From 1f463f9a887874a41e6e33da941333bc350c7301 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Wed, 5 Nov 2025 02:27:00 -0800 Subject: [PATCH 001/114] chore: update crate version to v0.13.0 --- CHANGELOG.md | 2 ++ Cargo.lock | 12 ++++++------ Cargo.toml | 14 +++++++------- README.md | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e3060ba83..8907c81c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.13.0 (TBD) + ## 0.12.0 (11-05-2025) ### Features diff --git a/Cargo.lock b/Cargo.lock index 4b4cc93993..b45d94475a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1457,7 +1457,7 @@ dependencies = [ [[package]] name = "miden-block-prover" -version = "0.12.0" +version = "0.13.0" dependencies = [ "miden-lib", "miden-objects", @@ -1553,7 +1553,7 @@ dependencies = [ [[package]] name = "miden-lib" -version = "0.12.0" +version = "0.13.0" dependencies = [ "Inflector", "anyhow", @@ -1626,7 +1626,7 @@ dependencies = [ [[package]] name = "miden-objects" -version = "0.12.0" +version = "0.13.0" dependencies = [ "anyhow", "assert_matches", @@ -1710,7 +1710,7 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.12.0" +version = "0.13.0" dependencies = [ "anyhow", "assert_matches", @@ -1732,7 +1732,7 @@ dependencies = [ [[package]] name = "miden-tx" -version = "0.12.0" +version = "0.13.0" dependencies = [ "anyhow", "assert_matches", @@ -1751,7 +1751,7 @@ dependencies = [ [[package]] name = "miden-tx-batch-prover" -version = "0.12.0" +version = "0.13.0" dependencies = [ "miden-objects", "miden-tx", diff --git a/Cargo.toml b/Cargo.toml index a4f5124b46..552339a08c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ homepage = "https://miden.xyz" license = "MIT" repository = "https://github.com/0xMiden/miden-base" rust-version = "1.90" -version = "0.12.0" +version = "0.13.0" [profile.release] codegen-units = 1 @@ -40,12 +40,12 @@ lto = true [workspace.dependencies] # Workspace crates -miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.12" } -miden-lib = { default-features = false, path = "crates/miden-lib", version = "0.12" } -miden-objects = { default-features = false, path = "crates/miden-objects", version = "0.12" } -miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.12" } -miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.12" } -miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.12" } +miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.13" } +miden-lib = { default-features = false, path = "crates/miden-lib", version = "0.13" } +miden-objects = { default-features = false, path = "crates/miden-objects", version = "0.13" } +miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.13" } +miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.13" } +miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.13" } # Miden dependencies miden-air = { default-features = false, version = "0.19" } diff --git a/README.md b/README.md index 394bf72716..55dd81ee88 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ If you want to join the technical discussion or learn more about the project, pl ## Status and features -Miden is currently on release v0.12. This is an early version of the protocol and its components. We expect to keep making changes (including breaking changes) to all components. +Miden is currently on release v0.13. This is an early version of the protocol and its components. We expect to keep making changes (including breaking changes) to all components. ### Feature highlights From 370c18427919f2e3a9969d5be36925036981be10 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 13:10:24 +0100 Subject: [PATCH 002/114] Add duplicate approver validation to multisig configuration (#2046) * Initial plan * Add duplicate approver validation to multisig configuration Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * Refactor duplicate check to use BTreeSet for conciseness Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * Add changelog entry for duplicate approver validation Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Update CHANGELOG.md * Simplify duplicate approver test using clone Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Remove unnecessary clone() call (Copy trait) Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Co-authored-by: Marti --- CHANGELOG.md | 2 +- .../account/auth/rpo_falcon_512_multisig.rs | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8907c81c47..e3c5eeb779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,7 @@ - [BREAKING] Remove `miden::account::get_native_nonce` procedure ([#2026](https://github.com/0xMiden/miden-base/pull/2026)). - [BREAKING] Refactor `Address` to make routing parameters optional ([#2032](https://github.com/0xMiden/miden-base/pull/2032), [#2047](https://github.com/0xMiden/miden-base/pull/2047)). - [BREAKING] Refactor `PartialVault`, `PartialStorageMap`, `PartialAccountTree` and `PartialNullifierTree` to allow construction from a root ([#2042](https://github.com/0xMiden/miden-base/pull/2042)). -- [BREAKING] Refactor `Address` to make routing parameters optional ([#2032](https://github.com/0xMiden/miden-base/pull/2032)). +- Added duplicate approver validation to `AuthRpoFalcon512MultisigConfig` ([#2046](https://github.com/0xMiden/miden-base/issues/2046)). - Added `encryption_key` to `RoutingParameters` ([#2050](https://github.com/0xMiden/miden-base/pull/2050)). - [BREAKING] Added `EcdsaK256Keccak` variant to auth enums ([#2052](https://github.com/0xMiden/miden-base/pull/2052)). - Implemented storage map templates, which can be initialized through key/value lists provided via `InitStorageData` TOML ([#2053](https://github.com/0xMiden/miden-base/pull/2053)). diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs index f364abc298..3163cf8b03 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs @@ -1,3 +1,4 @@ +use alloc::collections::BTreeSet; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; @@ -34,6 +35,13 @@ impl AuthRpoFalcon512MultisigConfig { )); } + // Check for duplicate approvers + if approvers.len() + != approvers.iter().map(|&pk| Word::from(pk)).collect::>().len() + { + return Err(AccountError::other("duplicate approver public keys are not allowed")); + } + Ok(Self { approvers, default_threshold, @@ -242,4 +250,21 @@ mod tests { .contains("threshold cannot be greater than number of approvers") ); } + + /// Test multisig component with duplicate approvers (should fail) + #[test] + fn test_multisig_component_duplicate_approvers() { + let pub_key_1 = PublicKeyCommitment::from(Word::from([1u32, 0, 0, 0])); + let pub_key_2 = PublicKeyCommitment::from(Word::from([2u32, 0, 0, 0])); + + // Test with duplicate approvers (should fail) + let approvers = vec![pub_key_1, pub_key_2, pub_key_1]; + let result = AuthRpoFalcon512MultisigConfig::new(approvers, 2); + assert!( + result + .unwrap_err() + .to_string() + .contains("duplicate approver public keys are not allowed") + ); + } } From d694ebb6db851aa8011730f21126d1468d8ba548 Mon Sep 17 00:00:00 2001 From: Marti Date: Wed, 5 Nov 2025 22:10:07 +0100 Subject: [PATCH 003/114] fix: map_entries should be public (#2055) --- .../src/account/component/template/storage/init_storage_data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs b/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs index 7bf195ad07..e5955d19e2 100644 --- a/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs +++ b/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs @@ -57,7 +57,7 @@ impl InitStorageData { } /// Returns the map entries associated with the given placeholder name, if any. - pub(crate) fn map_entries(&self, key: &StorageValueName) -> Option<&Vec<(Word, Word)>> { + pub fn map_entries(&self, key: &StorageValueName) -> Option<&Vec<(Word, Word)>> { self.map_entries.get(key) } } From d3f38c794c2b5c2275ae9579c59d1e96fe7f58d7 Mon Sep 17 00:00:00 2001 From: Marti Date: Wed, 5 Nov 2025 22:11:01 +0100 Subject: [PATCH 004/114] docs: update `address.md` docs to include the encryption key (#2057) --- docs/src/account/address.md | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/src/account/address.md b/docs/src/account/address.md index 79b457c3c3..1977212115 100644 --- a/docs/src/account/address.md +++ b/docs/src/account/address.md @@ -6,7 +6,7 @@ sidebar_position: 3 ## Purpose -An address is an extension to account IDs and other identifiers that facilitates sending and receiving of [notes](../note). It serves three main purposes explained in this section. +An address is an extension to account IDs and other identifiers that facilitates sending and receiving of [notes](../note). It serves four main purposes explained in this section. ### Communicating receiver information @@ -43,6 +43,10 @@ An address allows the sender of the note to easily discover the interface of the If a sender wants to create a note, it is up to them to check whether the receiver account has an interface that it compatible with that note. The notion of an address doesn't exist at protocol level and so it is up to wallets or clients to implement this interface compatibility check. +### Note encryption + +An address can include a public encryption key that enables senders to securely encrypt note payloads for the receiver. This ensures that only the intended recipient, who holds the corresponding private key, can decrypt and read the note contents. + ## Structure An address consists of two parts: @@ -61,13 +65,16 @@ mm1arp0azyk9jugtgpnnhle8daav58nczzr_qpgqqwcfx0p The routing parameters in an address can encode exactly one account interface, which is a deliberate limitation to keep the size of addresses small. Users can generate multiple addresses for the same identifier like account ID or public key, in order to communicate different interfaces to senders. In other words, there could be multiple different addresses that point to the same account, each encoding a different interface. So, the relationship from addresses to their underlying identifiers is n-to-1. -As an example, these two addresses contain the same account ID but different routing parameters: +As an example, these addresses contain the same account ID but different routing parameters: ```text mm1arp0azyk9jugtgpnnhle8daav58nczzr_qpgqqwcfx0p mm1arp0azyk9jugtgpnnhle8daav58nczzr_qzsqqd4avz7 +mm1arp0azyk9jugtgpnnhle8daav58nczzr_qruqqqgqjmsgjsh3687mt2w0qtqunxt3th442j48qwdnezl0fv6qm3x9c8zqsv7pku ``` +The third example above includes an encryption key in the routing parameters, which results in a longer encoded address string. + ### Address Types The supported **address types** are: @@ -82,10 +89,6 @@ Adding a public key-based address type is planned. The supported routing parameters are detailed in this section. -:::note -Adding an encryption key routing parameter is planned. -::: - #### Address Interface The address interface informs the sender of the capabilities of the [receiver account's interface](./code#interface). @@ -97,6 +100,19 @@ The supported **address interfaces** are: The note tag length routing parameter allows specifying the length of the [note tag](../note#note-discovery) that the sender should create. This parameter determines how many bits of the account ID are encoded into note tags of notes targeted to this address. This lets the owner of the account choose their level of privacy. A higher tag length makes the address ID more uniquely identifiable and reduces privacy, while a shorter length increases privacy at the cost of matching more notes published onchain. +#### Encryption Key + +The encryption key routing parameter enables secure note payload encryption by allowing the receiver to provide a public encryption key in their address. When present, senders can use this key to encrypt the note payload using sealed box encryption, ensuring that only the receiver can decrypt and read the note contents. + +The supported **encryption schemes** are: +- `X25519_XChaCha20Poly1305`: Curve25519-based key exchange with XChaCha20-Poly1305 authenticated encryption +- `K256_XChaCha20Poly1305`: secp256k1-based key exchange with XChaCha20-Poly1305 authenticated encryption +- `X25519_AeadRpo`: Curve25519-based key exchange with RPO-based authenticated encryption +- `K256_AeadRpo`: secp256k1-based key exchange with RPO-based authenticated encryption + +The encryption key is optional in an address. If not provided, senders may use alternative encryption mechanisms or send unencrypted notes. + +When an encryption key is included in the address, it is encoded in bech32 format alongside other routing parameters. The encoding consists of a 1-byte variant discriminant followed by the public key bytes (32 bytes for Curve25519 keys, 33 bytes for secp256k1 keys in their compressed format). ## Encoding The two parts of an address are encoded as follows: From 02968c9778843a2cbea67780169940bac63aba18 Mon Sep 17 00:00:00 2001 From: Marti Date: Wed, 5 Nov 2025 22:11:39 +0100 Subject: [PATCH 005/114] fix: handle empty map in templates (#2056) --- .../account/component/template/storage/mod.rs | 39 +++++++++++++++++++ .../component/template/storage/toml.rs | 12 +++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/crates/miden-objects/src/account/component/template/storage/mod.rs b/crates/miden-objects/src/account/component/template/storage/mod.rs index e75e4c5ebc..e55b4551de 100644 --- a/crates/miden-objects/src/account/component/template/storage/mod.rs +++ b/crates/miden-objects/src/account/component/template/storage/mod.rs @@ -799,6 +799,45 @@ mod tests { assert!(toml_roundtrip.contains("type = \"map\"")); } + #[test] + fn toml_map_with_empty_values_creates_value_map() { + // Test that when type = "map" and values = [] is specified, + // it creates a MapRepresentation::Value with empty entries, + // not a MapRepresentation::Template + let toml_text = r#" + name = "Test Component" + description = "Component with map having empty values" + version = "1.0.0" + supported-types = ["RegularAccountImmutableCode"] + + [[storage]] + name = "executed_transactions" + description = "Map which stores executed transactions" + slot = 0 + type = "map" + values = [] + "#; + + let metadata = AccountComponentMetadata::from_toml(toml_text).unwrap(); + assert_eq!(metadata.storage_entries().len(), 1); + match metadata.storage_entries().first().unwrap() { + StorageEntry::Map { map, .. } => match map { + MapRepresentation::Value { identifier, entries } => { + assert_eq!(identifier.name.as_str(), "executed_transactions"); + assert_eq!( + identifier.description.as_deref(), + Some("Map which stores executed transactions") + ); + assert!(entries.is_empty(), "Expected empty entries for map with values = []"); + }, + MapRepresentation::Template { .. } => { + panic!("expected value map with empty entries, not template map") + }, + }, + _ => panic!("expected map storage entry"), + } + } + #[test] fn map_placeholder_populated_via_toml_array() { let storage_entry = StorageEntry::new_map( diff --git a/crates/miden-objects/src/account/component/template/storage/toml.rs b/crates/miden-objects/src/account/component/template/storage/toml.rs index f1e1b1a49c..396c88f0c7 100644 --- a/crates/miden-objects/src/account/component/template/storage/toml.rs +++ b/crates/miden-objects/src/account/component/template/storage/toml.rs @@ -398,7 +398,17 @@ impl<'de> Deserialize<'de> for StorageEntry { raw.identifier.ok_or_else(|| missing_field_for("identifier", "map entry"))?; let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "map entry"))?; let FieldIdentifier { name, description } = identifier; - let mut map = MapRepresentation::new_template(name); + + // If values is specified (even if empty), create a value map. + // Due to #[serde(untagged)] on StorageValues, values = [] gets deserialized + // as StorageValues::Words(vec![]), so we need to treat it as an empty map. + // Otherwise, create a template map. + let mut map = if raw.values.is_some() { + MapRepresentation::new_value(Vec::new(), name) + } else { + MapRepresentation::new_template(name) + }; + if let Some(desc) = description { map = map.with_description(desc); } From 3bb99efcefbb266120267ce71f618ae38eddc2af Mon Sep 17 00:00:00 2001 From: Marti Date: Wed, 5 Nov 2025 22:45:26 +0100 Subject: [PATCH 006/114] fix: auth components should always increment the nonce if zero (#2060) --- CHANGELOG.md | 2 ++ crates/miden-lib/asm/account_components/no_auth.masm | 12 +++++++++++- .../asm/account_components/rpo_falcon_512_acl.masm | 11 +++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3c5eeb779..7407639aa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## 0.13.0 (TBD) +- [BREAKING] Changed auth components to increment nonce if it is zero ([#2060](https://github.com/0xMiden/miden-base/pull/2060)). + ## 0.12.0 (11-05-2025) ### Features diff --git a/crates/miden-lib/asm/account_components/no_auth.masm b/crates/miden-lib/asm/account_components/no_auth.masm index d651f68179..7726b6c183 100644 --- a/crates/miden-lib/asm/account_components/no_auth.masm +++ b/crates/miden-lib/asm/account_components/no_auth.masm @@ -24,7 +24,17 @@ pub proc auth_no_auth exec.word::eq not # => [has_account_state_changed, pad(16)] - # if the account has been updated, increment the nonce + # check if this is a new account (i.e., nonce == 0); this check is needed because new + # accounts are initialized with a non-empty state, and thus, unless the account was modified + # during the transaction, the initial and current state commitments will be the same + + exec.active_account::get_nonce eq.0 + # => [is_new_account, has_account_state_changed, pad(16)] + + or + # => [should_increment_nonce, pad(16)] + + # if the account has been updated or we are creating a new account, increment the nonce if.true exec.native_account::incr_nonce drop end diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm b/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm index f349738d84..72fd4916a0 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm +++ b/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm @@ -140,6 +140,17 @@ pub proc auth_tx_rpo_falcon512_acl.2(auth_args: BeWord) exec.word::eq not # => [has_account_state_changed, pad(16)] + # check if this is a new account (i.e., nonce == 0); this check is needed because new + # accounts are initialized with a non-empty state, and thus, unless the account was + # modified during the transaction, the initial and current state commitments will be + # the same + + exec.active_account::get_nonce eq.0 + # => [is_new_account, has_account_state_changed, pad(16)] + + or + # => [should_increment_nonce, pad(16)] + if.true exec.native_account::incr_nonce drop end From 9f6ce3297a859649efba7610ccd3d7fcfd31dde1 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 6 Nov 2025 17:00:29 +0800 Subject: [PATCH 007/114] fix: unused dependencies workflow (#2067) * fix: unuse dependencies workflow * chore: Remove unused dependencies * chore: Add cargo machete to `make lint` --- .github/workflows/lint.yml | 12 +++--------- Cargo.lock | 17 ----------------- Makefile | 1 + bin/bench-transaction/Cargo.toml | 12 ++++-------- crates/miden-lib/Cargo.toml | 1 - crates/miden-objects/Cargo.toml | 1 - crates/miden-testing/Cargo.toml | 1 - crates/miden-tx/Cargo.toml | 2 -- 8 files changed, 8 insertions(+), 39 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ca2e209143..273b129312 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -122,13 +122,7 @@ jobs: name: check for unused dependencies runs-on: ubuntu-latest steps: - - uses: actions/checkout@main - - name: Install cargo-machete - uses: clechasseur/rs-cargo@v2 - with: - command: install - args: cargo-machete@0.7.0 + - name: Checkout + uses: actions/checkout@v4 - name: Machete - uses: clechasseur/rs-cargo@v2 - with: - command: machete \ No newline at end of file + uses: bnjbvr/cargo-machete@main diff --git a/Cargo.lock b/Cargo.lock index b45d94475a..90a892ccf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "addr2line" version = "0.21.0" @@ -259,10 +249,8 @@ dependencies = [ "criterion 0.6.0", "miden-lib", "miden-objects", - "miden-processor", "miden-testing", "miden-tx", - "rand_chacha", "serde", "serde_json", "tokio", @@ -1555,7 +1543,6 @@ dependencies = [ name = "miden-lib" version = "0.13.0" dependencies = [ - "Inflector", "anyhow", "assert_matches", "fs-err", @@ -1634,7 +1621,6 @@ dependencies = [ "color-eyre", "criterion 0.5.1", "getrandom 0.3.4", - "log", "miden-air", "miden-assembly", "miden-assembly-syntax", @@ -1724,7 +1710,6 @@ dependencies = [ "rand", "rand_chacha", "rstest", - "thiserror", "tokio", "winter-rand-utils", "winterfell", @@ -1743,10 +1728,8 @@ dependencies = [ "miden-prover", "miden-tx", "miden-verifier", - "rand", "rstest", "thiserror", - "tokio", ] [[package]] diff --git a/Makefile b/Makefile index 2af8b9b5ab..c506650d8e 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,7 @@ lint: ## Runs all linting tasks at once (Clippy, fixing, formatting, typos) @$(BUILD_GENERATED_FILES_IN_SRC) $(MAKE) clippy-no-std @$(BUILD_GENERATED_FILES_IN_SRC) $(MAKE) typos-check @$(BUILD_GENERATED_FILES_IN_SRC) $(MAKE) toml + cargo machete # --- docs ---------------------------------------------------------------------------------------- diff --git a/bin/bench-transaction/Cargo.toml b/bin/bench-transaction/Cargo.toml index a269825112..c5ed2cdb9e 100644 --- a/bin/bench-transaction/Cargo.toml +++ b/bin/bench-transaction/Cargo.toml @@ -22,15 +22,11 @@ miden-objects = { features = ["testing"], workspace = true } miden-testing = { workspace = true } miden-tx = { workspace = true } -# Miden dependencies -miden-processor = { workspace = true } - # External dependencies -anyhow = { workspace = true } -rand_chacha = { default-features = false, version = "0.9" } -serde = { features = ["derive"], version = "1.0" } -serde_json = { features = ["preserve_order"], package = "serde_json", version = "1.0" } -tokio = { features = ["macros", "rt"], workspace = true } +anyhow = { workspace = true } +serde = { features = ["derive"], version = "1.0" } +serde_json = { features = ["preserve_order"], package = "serde_json", version = "1.0" } +tokio = { features = ["macros", "rt"], workspace = true } [dev-dependencies] criterion = { features = ["async_tokio", "html_reports"], version = "0.6" } diff --git a/crates/miden-lib/Cargo.toml b/crates/miden-lib/Cargo.toml index 288d2fb02b..fe845f123a 100644 --- a/crates/miden-lib/Cargo.toml +++ b/crates/miden-lib/Cargo.toml @@ -32,7 +32,6 @@ rand = { optional = true, workspace = true } thiserror = { workspace = true } [build-dependencies] -Inflector = { version = "0.11" } fs-err = { version = "3" } miden-assembly = { workspace = true } miden-core = { workspace = true } diff --git a/crates/miden-objects/Cargo.toml b/crates/miden-objects/Cargo.toml index 445dfea655..f6ecd1dea6 100644 --- a/crates/miden-objects/Cargo.toml +++ b/crates/miden-objects/Cargo.toml @@ -47,7 +47,6 @@ winter-rand-utils = { optional = true, version = "0.13" } # External dependencies bech32 = { default-features = false, features = ["alloc"], version = "0.11" } -log = { optional = true, version = "0.4" } rand = { workspace = true } rand_xoshiro = { default-features = false, optional = true, version = "0.7" } semver = { features = ["serde"], version = "1.0" } diff --git a/crates/miden-testing/Cargo.toml b/crates/miden-testing/Cargo.toml index 01e4fe2fc2..56b578d0fc 100644 --- a/crates/miden-testing/Cargo.toml +++ b/crates/miden-testing/Cargo.toml @@ -31,7 +31,6 @@ anyhow = { workspace = true } itertools = { default-features = false, features = ["use_alloc"], version = "0.14" } rand = { features = ["os_rng", "small_rng"], workspace = true } rand_chacha = { default-features = false, version = "0.9" } -thiserror = { workspace = true } winterfell = { version = "0.13" } [dev-dependencies] diff --git a/crates/miden-tx/Cargo.toml b/crates/miden-tx/Cargo.toml index 86c3c5006b..56187d7570 100644 --- a/crates/miden-tx/Cargo.toml +++ b/crates/miden-tx/Cargo.toml @@ -29,9 +29,7 @@ miden-prover = { workspace = true } miden-verifier = { workspace = true } # External dependencies -rand = { workspace = true } thiserror = { workspace = true } -tokio = { features = ["rt"], workspace = true } [dev-dependencies] anyhow = { features = ["backtrace", "std"], workspace = true } From 698363407d9aab564570647d49e87306df75cb52 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:44:13 +0300 Subject: [PATCH 008/114] Merge branch `main` into `next` (#2070) * feat: Add `create_mint_note` & `create_burn_note` helper functions (#2061) * chore: update crate version to v0.13.0 * Add duplicate approver validation to multisig configuration (#2046) * Initial plan * Add duplicate approver validation to multisig configuration Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * Refactor duplicate check to use BTreeSet for conciseness Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * Add changelog entry for duplicate approver validation Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Update CHANGELOG.md * Simplify duplicate approver test using clone Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Remove unnecessary clone() call (Copy trait) Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Co-authored-by: Marti * feat: add mint / burn note helper functions * (parital) Revert "chore: update crate version to v0.13.0" This reverts commit 1f463f9a887874a41e6e33da941333bc350c7301. * Move changelog entry to unreleased 0.12.2 section (#2068) * Initial plan * Move changelog entry to 0.12.2 unreleased section Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> --------- Co-authored-by: Bobbin Threadbare Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Co-authored-by: Marti * Update CHANGELOG.md Co-authored-by: Marti --------- Co-authored-by: Bobbin Threadbare Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Co-authored-by: Marti --- CHANGELOG.md | 4 + crates/miden-lib/src/note/mod.rs | 113 +++++++++++++++++++ crates/miden-testing/tests/scripts/faucet.rs | 61 ++++------ 3 files changed, 137 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f15d01578..374908b37f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.13.0 (TBD) +## 0.12.2 (unreleased) + +- Added `create_mint_note` and `create_burn_note` helper functions for creating standardized MINT and BURN notes ([#2061](https://github.com/0xMiden/miden-base/pull/2061)). + ## 0.12.1 (2025-11-06) - Made `InitStorageData::map_entries()` public ([#2055](https://github.com/0xMiden/miden-base/pull/2055)). diff --git a/crates/miden-lib/src/note/mod.rs b/crates/miden-lib/src/note/mod.rs index b02d417867..c5e1a4a5c8 100644 --- a/crates/miden-lib/src/note/mod.rs +++ b/crates/miden-lib/src/note/mod.rs @@ -158,3 +158,116 @@ pub fn create_swap_note( Ok((note, payback_note)) } + +/// Generates a MINT note - a note that instructs a network faucet to mint fungible assets. +/// +/// This script enables the creation of a PUBLIC note that, when consumed by a network faucet, +/// will mint the specified amount of fungible assets and create a PRIVATE note with the given +/// RECIPIENT. The MINT note uses note-based authentication, checking if the note sender equals +/// the faucet owner to authorize minting. +/// +/// MINT notes are always PUBLIC (for network execution) and output notes are always PRIVATE +/// (TODO: enable public output note creation from MINT note consumption). +/// +/// The passed-in `rng` is used to generate a serial number for the note. The note's tag +/// is automatically set to the faucet's account ID for proper routing. +/// +/// # Parameters +/// - `faucet_id`: The account ID of the network faucet that will mint the assets +/// - `sender`: The account ID of the note creator (must be the faucet owner) +/// - `target_recipient`: The recipient digest for the output P2ID note that will receive the minted +/// assets +/// - `output_note_tag`: The tag for the output P2ID note +/// - `amount`: The amount of fungible assets to mint +/// - `aux`: Auxiliary data for the MINT note +/// - `output_note_aux`: Auxiliary data for the output P2ID note +/// - `rng`: Random number generator for creating the serial number +/// +/// # Errors +/// Returns an error if note creation fails. +pub fn create_mint_note( + faucet_id: AccountId, + sender: AccountId, + target_recipient: Word, + output_note_tag: Felt, + amount: Felt, + aux: Felt, + output_note_aux: Felt, + rng: &mut R, +) -> Result { + let note_script = WellKnownNote::MINT.script(); + let serial_num = rng.draw_word(); + + // MINT notes are always public for network execution + let note_type = NoteType::Public; + // Output notes are always private (for now) + let output_note_type = NoteType::Private; + + let execution_hint = NoteExecutionHint::always(); + + let inputs = NoteInputs::new(vec![ + target_recipient[0], + target_recipient[1], + target_recipient[2], + target_recipient[3], + execution_hint.into(), + output_note_type.into(), + output_note_aux, + output_note_tag, + amount, + ])?; + + let tag = NoteTag::from_account_id(faucet_id); + + let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?; + let assets = NoteAssets::new(vec![])?; // MINT notes have no assets + let recipient = NoteRecipient::new(serial_num, note_script, inputs); + + Ok(Note::new(assets, metadata, recipient)) +} + +/// Generates a BURN note - a note that instructs a faucet to burn a fungible asset. +/// +/// This script enables the creation of a PUBLIC note that, when consumed by a faucet (either basic +/// or network), will burn the fungible assets contained in the note. Both basic and network +/// fungible faucets export the same `burn` procedure with identical MAST roots, allowing +/// a single BURN note script to work with either faucet type. +/// +/// BURN notes are always PUBLIC for network execution. +/// +/// The passed-in `rng` is used to generate a serial number for the note. The note's tag +/// is automatically set to the faucet's account ID for proper routing. +/// +/// # Parameters +/// - `sender`: The account ID of the note creator +/// - `faucet_id`: The account ID of the faucet that will burn the assets +/// - `fungible_asset`: The fungible asset to be burned +/// - `aux`: Auxiliary data for the note +/// - `rng`: Random number generator for creating the serial number +/// +/// # Errors +/// Returns an error if note creation fails. +pub fn create_burn_note( + sender: AccountId, + faucet_id: AccountId, + fungible_asset: Asset, + aux: Felt, + rng: &mut R, +) -> Result { + let note_script = WellKnownNote::BURN.script(); + let serial_num = rng.draw_word(); + + // BURN notes are always public + let note_type = NoteType::Public; + // Use always execution hint for BURN notes + let execution_hint = NoteExecutionHint::always(); + + let inputs = NoteInputs::new(vec![])?; + let tag = NoteTag::from_account_id(faucet_id); + + let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?; + let assets = NoteAssets::new(vec![fungible_asset])?; // BURN notes contain the asset to burn + let recipient = NoteRecipient::new(serial_num, note_script, inputs); + + Ok(Note::new(assets, metadata, recipient)) +} diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 1de23c6ea2..6cbf19857d 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use miden_lib::account::faucets::{BasicFungibleFaucet, FungibleFaucetExt, NetworkFungibleFaucet}; use miden_lib::errors::tx_kernel_errors::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; -use miden_lib::note::WellKnownNote; +use miden_lib::note::{create_burn_note, create_mint_note}; use miden_lib::testing::note::NoteBuilder; use miden_lib::utils::ScriptBuilder; use miden_objects::account::{ @@ -519,46 +519,33 @@ async fn network_faucet_mint() -> anyhow::Result<()> { let amount = Felt::new(75); let mint_asset: Asset = FungibleAsset::new(faucet.id(), amount.into()).unwrap().into(); - let tag = NoteTag::for_local_use_case(0, 0).unwrap(); let aux = Felt::new(27); - let note_execution_hint = NoteExecutionHint::on_block_slot(5, 6, 7); - let note_type = NoteType::Private; let serial_num = Word::default(); + let output_note_tag = NoteTag::from_account_id(target_account.id()); let p2id_mint_output_note = create_p2id_note_exact( faucet.id(), target_account.id(), vec![mint_asset], - note_type, + NoteType::Private, aux, serial_num, ) .unwrap(); let recipient = p2id_mint_output_note.recipient().digest(); - // Use the standard MINT note script - let note_script = WellKnownNote::MINT.script(); - - // Create the note inputs for MINT note (reversed order) - let inputs = NoteInputs::new(vec![ - recipient[0], - recipient[1], - recipient[2], - recipient[3], - note_execution_hint.into(), - note_type.into(), - aux, - tag.into(), + // Create the MINT note using the helper function + let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); + let mint_note = create_mint_note( + faucet.id(), + faucet_owner_account_id, + recipient, + output_note_tag.into(), amount, - ])?; - - // Create the MINT note using the standard script - let mint_note_metadata = - NoteMetadata::new(faucet_owner_account_id, note_type, tag, note_execution_hint, aux)?; - let mint_note_assets = NoteAssets::new(vec![])?; // Empty assets for mint note - let serial_num = Word::from([1, 2, 3, 4u32]); // Random serial number - let mint_note_recipient = NoteRecipient::new(serial_num, note_script, inputs); - let mint_note = Note::new(mint_note_assets, mint_note_metadata, mint_note_recipient); + aux, + aux, + &mut rng, + )?; // Add the MINT note to the mock chain builder.add_output_note(OutputNote::Full(mint_note.clone())); @@ -637,24 +624,16 @@ async fn network_faucet_burn() -> anyhow::Result<()> { let burn_amount = 100u64; let fungible_asset = FungibleAsset::new(faucet.id(), burn_amount).unwrap(); - // CREATE BURN NOTE USING STANDARD NOTE SCRIPT + // CREATE BURN NOTE // -------------------------------------------------------------------------------------------- - // Use the standard BURN note script - let note_script = WellKnownNote::BURN.script(); - - // Create the burn note using the standard script - let burn_note_metadata = NoteMetadata::new( + let mut rng = RpoRandomCoin::new([Felt::from(99u32); 4].into()); + let note = create_burn_note( faucet_owner_account_id, - NoteType::Public, - NoteTag::for_local_use_case(0, 0)?, - NoteExecutionHint::Always, + faucet.id(), + fungible_asset.into(), Felt::new(0), + &mut rng, )?; - let burn_note_assets = NoteAssets::new(vec![fungible_asset.into()])?; - let serial_num = Word::from([5, 6, 7, 8u32]); - let inputs = NoteInputs::new(vec![]).unwrap(); - let burn_note_recipient = NoteRecipient::new(serial_num, note_script, inputs); - let note = Note::new(burn_note_assets, burn_note_metadata, burn_note_recipient); builder.add_output_note(OutputNote::Full(note.clone())); let mut mock_chain = builder.build()?; From e6d333d3409165d95ba02e229898522cfa00ad0d Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:05:32 +0300 Subject: [PATCH 009/114] refactor: change `DataStore::get_note_script` to return `Option` (#2069) --- .../miden-testing/src/tx_context/context.rs | 25 ++++++++----------- crates/miden-tx/src/errors/mod.rs | 2 -- crates/miden-tx/src/executor/data_store.rs | 14 +++++------ crates/miden-tx/src/executor/exec_host.rs | 10 +++----- 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index d09ffc069c..b6abc0df72 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -331,13 +331,8 @@ impl DataStore for TransactionContext { fn get_note_script( &self, script_root: Word, - ) -> impl FutureMaybeSend> { - async move { - self.note_scripts - .get(&script_root) - .cloned() - .ok_or_else(|| DataStoreError::NoteScriptNotFound(script_root)) - } + ) -> impl FutureMaybeSend, DataStoreError>> { + async move { Ok(self.note_scripts.get(&script_root).cloned()) } } } @@ -366,7 +361,7 @@ mod tests { let script1_code = "begin push.1 end"; let program1 = assembler1 .assemble_program(script1_code) - .expect("Failed to assemble note script 1"); + .expect("failed to assemble note script 1"); let note_script1 = NoteScript::new(program1); let script_root1 = note_script1.root(); @@ -374,7 +369,7 @@ mod tests { let script2_code = "begin push.2 push.3 add end"; let program2 = assembler2 .assemble_program(script2_code) - .expect("Failed to assemble note script 2"); + .expect("failed to assemble note script 2"); let note_script2 = NoteScript::new(program2); let script_root2 = note_script2.root(); @@ -383,25 +378,27 @@ mod tests { .add_note_script(note_script1.clone()) .add_note_script(note_script2.clone()) .build() - .expect("Failed to build transaction context"); + .expect("failed to build transaction context"); // Assert that fetching both note scripts works let retrieved_script1 = tx_context .get_note_script(script_root1) .await - .expect("Failed to get note script 1"); + .expect("failed to get note script 1") + .expect("note script 1 should exist"); assert_eq!(retrieved_script1, note_script1); let retrieved_script2 = tx_context .get_note_script(script_root2) .await - .expect("Failed to get note script 2"); + .expect("failed to get note script 2") + .expect("note script 2 should exist"); assert_eq!(retrieved_script2, note_script2); - // Fetching a non-existent one fails + // Fetching a non-existent one returns None let non_existent_root = Word::from([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]); let result = tx_context.get_note_script(non_existent_root).await; - assert!(matches!(result, Err(DataStoreError::NoteScriptNotFound(_)))); + assert!(matches!(result, Ok(None))); } } diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 7bb66698bd..ae8f476d89 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -354,8 +354,6 @@ pub enum DataStoreError { AccountNotFound(AccountId), #[error("block with number {0} not found in data store")] BlockNotFound(BlockNumber), - #[error("note script with root {0} not found in data store")] - NoteScriptNotFound(Word), /// Custom error variant for implementors of the [`DataStore`](crate::executor::DataStore) /// trait. #[error("{error_msg}")] diff --git a/crates/miden-tx/src/executor/data_store.rs b/crates/miden-tx/src/executor/data_store.rs index c9b18f08f5..fa7e85091f 100644 --- a/crates/miden-tx/src/executor/data_store.rs +++ b/crates/miden-tx/src/executor/data_store.rs @@ -69,17 +69,17 @@ pub trait DataStore: MastForestStore { map_key: Word, ) -> impl FutureMaybeSend>; - /// Returns a note script with the specified root. + /// Returns a note script with the specified root, or `None` if not found. /// - /// This method will try to find a note script with the specified root in the data store, - /// and if not found, return an error. + /// This method will try to find a note script with the specified root in the data store. + /// If the script is not found, it returns `Ok(None)` rather than an error, as "not found" + /// is a valid, expected outcome. /// /// # Errors - /// Returns an error if: - /// - The note script with the specified root could not be found in the data store. - /// - The data store encountered some internal error. + /// Returns an error if the data store encountered an internal error while attempting to + /// retrieve the script. fn get_note_script( &self, script_root: Word, - ) -> impl FutureMaybeSend>; + ) -> impl FutureMaybeSend, DataStoreError>>; } diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 0c80bbb83b..18411e4fce 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -34,7 +34,7 @@ use crate::host::{ TransactionEventHandling, TransactionProgress, }; -use crate::{AccountProcedureIndexMap, DataStore, DataStoreError}; +use crate::{AccountProcedureIndexMap, DataStore}; // TRANSACTION EXECUTOR HOST // ================================================================================================ @@ -376,7 +376,7 @@ where let note_script_result = self.base_host.store().get_note_script(script_root).await; let (recipient, mutations) = match note_script_result { - Ok(note_script) => { + Ok(Some(note_script)) => { let script_felts: Vec = (¬e_script).into(); let recipient = NoteRecipient::new(serial_num, note_script, note_inputs); let mutations = vec![AdviceMutation::extend_map(AdviceMap::from_iter([( @@ -386,10 +386,8 @@ where (Some(recipient), mutations) }, - Err(DataStoreError::NoteScriptNotFound(_)) if metadata.is_private() => { - (None, Vec::new()) - }, - Err(DataStoreError::NoteScriptNotFound(_)) => { + Ok(None) if metadata.is_private() => (None, Vec::new()), + Ok(None) => { return Err(TransactionKernelError::other(format!( "note script with root {script_root} not found in data store for public note" ))); From d52c65e85949b0b85b38fef04b2e86ab5cbe281a Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 13 Nov 2025 18:04:08 +0800 Subject: [PATCH 010/114] feat: separate event handling into data extraction and handling (#2071) --- CHANGELOG.md | 6 + .../src/errors/transaction_errors.rs | 4 +- crates/miden-lib/src/transaction/mod.rs | 4 +- .../transaction/{events.rs => tx_event_id.rs} | 86 +- crates/miden-testing/src/mock_host.rs | 28 +- crates/miden-tx/src/errors/mod.rs | 2 +- crates/miden-tx/src/executor/exec_host.rs | 327 ++++-- .../miden-tx/src/host/account_procedures.rs | 45 +- crates/miden-tx/src/host/kernel_process.rs | 74 +- crates/miden-tx/src/host/link_map.rs | 16 +- crates/miden-tx/src/host/mod.rs | 949 +++--------------- crates/miden-tx/src/host/note_builder.rs | 34 +- crates/miden-tx/src/host/tx_event.rs | 730 ++++++++++++++ crates/miden-tx/src/prover/mod.rs | 2 +- crates/miden-tx/src/prover/prover_host.rs | 159 ++- 15 files changed, 1373 insertions(+), 1093 deletions(-) rename crates/miden-lib/src/transaction/{events.rs => tx_event_id.rs} (62%) create mode 100644 crates/miden-tx/src/host/tx_event.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d858f476d5..dfd7a82498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## 0.13.0 (TBD) +### Features + +### Changes + +- [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). + ## 0.12.2 (2025-11-12) - Added `create_mint_note` and `create_burn_note` helper functions for creating standardized MINT and BURN notes ([#2061](https://github.com/0xMiden/miden-base/pull/2061)). diff --git a/crates/miden-lib/src/errors/transaction_errors.rs b/crates/miden-lib/src/errors/transaction_errors.rs index 8c8013c8e4..d216c1342a 100644 --- a/crates/miden-lib/src/errors/transaction_errors.rs +++ b/crates/miden-lib/src/errors/transaction_errors.rs @@ -1,7 +1,7 @@ use miden_core::EventId; use thiserror::Error; -use crate::transaction::TransactionEvent; +use crate::transaction::TransactionEventId; // TRANSACTION EVENT PARSING ERROR // ================================================================================================ @@ -13,7 +13,7 @@ pub enum TransactionEventError { #[error("event id {0} is not a transaction kernel event")] NotTransactionEvent(EventId, Option<&'static str>), #[error("event id {0} can only be emitted from the root context")] - NotRootContext(TransactionEvent), + NotRootContext(TransactionEventId), } // TRANSACTION TRACE PARSING ERROR diff --git a/crates/miden-lib/src/transaction/mod.rs b/crates/miden-lib/src/transaction/mod.rs index 54c55744cc..d7dae98a10 100644 --- a/crates/miden-lib/src/transaction/mod.rs +++ b/crates/miden-lib/src/transaction/mod.rs @@ -21,8 +21,8 @@ use super::MidenLib; pub mod memory; -mod events; -pub use events::{EventId, TransactionEvent}; +mod tx_event_id; +pub use tx_event_id::{EventId, TransactionEventId}; mod inputs; pub use inputs::{TransactionAdviceInputs, TransactionAdviceMapMismatch}; diff --git a/crates/miden-lib/src/transaction/events.rs b/crates/miden-lib/src/transaction/tx_event_id.rs similarity index 62% rename from crates/miden-lib/src/transaction/events.rs rename to crates/miden-lib/src/transaction/tx_event_id.rs index 2b3c5edd67..1c7a3dcb58 100644 --- a/crates/miden-lib/src/transaction/events.rs +++ b/crates/miden-lib/src/transaction/tx_event_id.rs @@ -19,7 +19,7 @@ include!(concat!(env!("OUT_DIR"), "/assets/transaction_events.rs")); /// by the transaction kernel are in the `miden` namespace. #[repr(u64)] #[derive(Debug, Clone, Eq, PartialEq)] -pub enum TransactionEvent { +pub enum TransactionEventId { AccountBeforeForeignLoad = ACCOUNT_BEFORE_FOREIGN_LOAD, AccountVaultBeforeAddAsset = ACCOUNT_VAULT_BEFORE_ADD_ASSET, @@ -80,7 +80,7 @@ pub enum TransactionEvent { Unauthorized = AUTH_UNAUTHORIZED, } -impl TransactionEvent { +impl TransactionEventId { /// Returns `true` if the event is privileged, i.e. it is only allowed to be emitted from the /// root context of the VM, which is where the transaction kernel executes. pub fn is_privileged(&self) -> bool { @@ -94,13 +94,13 @@ impl TransactionEvent { } } -impl fmt::Display for TransactionEvent { +impl fmt::Display for TransactionEventId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self:?}") } } -impl TryFrom for TransactionEvent { +impl TryFrom for TransactionEventId { type Error = TransactionEventError; fn try_from(event_id: EventId) -> Result { @@ -109,76 +109,80 @@ impl TryFrom for TransactionEvent { let name = EVENT_NAME_LUT.get(&raw).copied(); match raw { - ACCOUNT_BEFORE_FOREIGN_LOAD => Ok(TransactionEvent::AccountBeforeForeignLoad), + ACCOUNT_BEFORE_FOREIGN_LOAD => Ok(TransactionEventId::AccountBeforeForeignLoad), - ACCOUNT_VAULT_BEFORE_ADD_ASSET => Ok(TransactionEvent::AccountVaultBeforeAddAsset), - ACCOUNT_VAULT_AFTER_ADD_ASSET => Ok(TransactionEvent::AccountVaultAfterAddAsset), + ACCOUNT_VAULT_BEFORE_ADD_ASSET => Ok(TransactionEventId::AccountVaultBeforeAddAsset), + ACCOUNT_VAULT_AFTER_ADD_ASSET => Ok(TransactionEventId::AccountVaultAfterAddAsset), ACCOUNT_VAULT_BEFORE_REMOVE_ASSET => { - Ok(TransactionEvent::AccountVaultBeforeRemoveAsset) + Ok(TransactionEventId::AccountVaultBeforeRemoveAsset) + }, + ACCOUNT_VAULT_AFTER_REMOVE_ASSET => { + Ok(TransactionEventId::AccountVaultAfterRemoveAsset) }, - ACCOUNT_VAULT_AFTER_REMOVE_ASSET => Ok(TransactionEvent::AccountVaultAfterRemoveAsset), - ACCOUNT_VAULT_BEFORE_GET_BALANCE => Ok(TransactionEvent::AccountVaultBeforeGetBalance), + ACCOUNT_VAULT_BEFORE_GET_BALANCE => { + Ok(TransactionEventId::AccountVaultBeforeGetBalance) + }, ACCOUNT_VAULT_BEFORE_HAS_NON_FUNGIBLE_ASSET => { - Ok(TransactionEvent::AccountVaultBeforeHasNonFungibleAsset) + Ok(TransactionEventId::AccountVaultBeforeHasNonFungibleAsset) }, - ACCOUNT_STORAGE_BEFORE_SET_ITEM => Ok(TransactionEvent::AccountStorageBeforeSetItem), - ACCOUNT_STORAGE_AFTER_SET_ITEM => Ok(TransactionEvent::AccountStorageAfterSetItem), + ACCOUNT_STORAGE_BEFORE_SET_ITEM => Ok(TransactionEventId::AccountStorageBeforeSetItem), + ACCOUNT_STORAGE_AFTER_SET_ITEM => Ok(TransactionEventId::AccountStorageAfterSetItem), ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM => { - Ok(TransactionEvent::AccountStorageBeforeGetMapItem) + Ok(TransactionEventId::AccountStorageBeforeGetMapItem) }, ACCOUNT_STORAGE_BEFORE_SET_MAP_ITEM => { - Ok(TransactionEvent::AccountStorageBeforeSetMapItem) + Ok(TransactionEventId::AccountStorageBeforeSetMapItem) }, ACCOUNT_STORAGE_AFTER_SET_MAP_ITEM => { - Ok(TransactionEvent::AccountStorageAfterSetMapItem) + Ok(TransactionEventId::AccountStorageAfterSetMapItem) }, - ACCOUNT_BEFORE_INCREMENT_NONCE => Ok(TransactionEvent::AccountBeforeIncrementNonce), - ACCOUNT_AFTER_INCREMENT_NONCE => Ok(TransactionEvent::AccountAfterIncrementNonce), + ACCOUNT_BEFORE_INCREMENT_NONCE => Ok(TransactionEventId::AccountBeforeIncrementNonce), + ACCOUNT_AFTER_INCREMENT_NONCE => Ok(TransactionEventId::AccountAfterIncrementNonce), - ACCOUNT_PUSH_PROCEDURE_INDEX => Ok(TransactionEvent::AccountPushProcedureIndex), + ACCOUNT_PUSH_PROCEDURE_INDEX => Ok(TransactionEventId::AccountPushProcedureIndex), - NOTE_BEFORE_CREATED => Ok(TransactionEvent::NoteBeforeCreated), - NOTE_AFTER_CREATED => Ok(TransactionEvent::NoteAfterCreated), + NOTE_BEFORE_CREATED => Ok(TransactionEventId::NoteBeforeCreated), + NOTE_AFTER_CREATED => Ok(TransactionEventId::NoteAfterCreated), - NOTE_BEFORE_ADD_ASSET => Ok(TransactionEvent::NoteBeforeAddAsset), - NOTE_AFTER_ADD_ASSET => Ok(TransactionEvent::NoteAfterAddAsset), + NOTE_BEFORE_ADD_ASSET => Ok(TransactionEventId::NoteBeforeAddAsset), + NOTE_AFTER_ADD_ASSET => Ok(TransactionEventId::NoteAfterAddAsset), - AUTH_REQUEST => Ok(TransactionEvent::AuthRequest), + AUTH_REQUEST => Ok(TransactionEventId::AuthRequest), - PROLOGUE_START => Ok(TransactionEvent::PrologueStart), - PROLOGUE_END => Ok(TransactionEvent::PrologueEnd), + PROLOGUE_START => Ok(TransactionEventId::PrologueStart), + PROLOGUE_END => Ok(TransactionEventId::PrologueEnd), - NOTES_PROCESSING_START => Ok(TransactionEvent::NotesProcessingStart), - NOTES_PROCESSING_END => Ok(TransactionEvent::NotesProcessingEnd), + NOTES_PROCESSING_START => Ok(TransactionEventId::NotesProcessingStart), + NOTES_PROCESSING_END => Ok(TransactionEventId::NotesProcessingEnd), - NOTE_EXECUTION_START => Ok(TransactionEvent::NoteExecutionStart), - NOTE_EXECUTION_END => Ok(TransactionEvent::NoteExecutionEnd), + NOTE_EXECUTION_START => Ok(TransactionEventId::NoteExecutionStart), + NOTE_EXECUTION_END => Ok(TransactionEventId::NoteExecutionEnd), - TX_SCRIPT_PROCESSING_START => Ok(TransactionEvent::TxScriptProcessingStart), - TX_SCRIPT_PROCESSING_END => Ok(TransactionEvent::TxScriptProcessingEnd), + TX_SCRIPT_PROCESSING_START => Ok(TransactionEventId::TxScriptProcessingStart), + TX_SCRIPT_PROCESSING_END => Ok(TransactionEventId::TxScriptProcessingEnd), - EPILOGUE_START => Ok(TransactionEvent::EpilogueStart), - EPILOGUE_AUTH_PROC_START => Ok(TransactionEvent::EpilogueAuthProcStart), - EPILOGUE_AUTH_PROC_END => Ok(TransactionEvent::EpilogueAuthProcEnd), + EPILOGUE_START => Ok(TransactionEventId::EpilogueStart), + EPILOGUE_AUTH_PROC_START => Ok(TransactionEventId::EpilogueAuthProcStart), + EPILOGUE_AUTH_PROC_END => Ok(TransactionEventId::EpilogueAuthProcEnd), EPILOGUE_AFTER_TX_CYCLES_OBTAINED => { - Ok(TransactionEvent::EpilogueAfterTxCyclesObtained) + Ok(TransactionEventId::EpilogueAfterTxCyclesObtained) }, EPILOGUE_BEFORE_TX_FEE_REMOVED_FROM_ACCOUNT => { - Ok(TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount) + Ok(TransactionEventId::EpilogueBeforeTxFeeRemovedFromAccount) }, - EPILOGUE_END => Ok(TransactionEvent::EpilogueEnd), + EPILOGUE_END => Ok(TransactionEventId::EpilogueEnd), - LINK_MAP_SET => Ok(TransactionEvent::LinkMapSet), - LINK_MAP_GET => Ok(TransactionEvent::LinkMapGet), + LINK_MAP_SET => Ok(TransactionEventId::LinkMapSet), + LINK_MAP_GET => Ok(TransactionEventId::LinkMapGet), - AUTH_UNAUTHORIZED => Ok(TransactionEvent::Unauthorized), + AUTH_UNAUTHORIZED => Ok(TransactionEventId::Unauthorized), _ => Err(TransactionEventError::InvalidTransactionEvent(event_id, name)), } diff --git a/crates/miden-testing/src/mock_host.rs b/crates/miden-testing/src/mock_host.rs index ded9a6e663..92b08ecc9b 100644 --- a/crates/miden-testing/src/mock_host.rs +++ b/crates/miden-testing/src/mock_host.rs @@ -3,7 +3,7 @@ use alloc::sync::Arc; use alloc::vec::Vec; use miden_lib::StdLibrary; -use miden_lib::transaction::{EventId, TransactionEvent}; +use miden_lib::transaction::{EventId, TransactionEventId}; use miden_objects::Word; use miden_processor::{ AdviceMutation, @@ -60,14 +60,14 @@ impl<'store> MockHost<'store> { // The default set of transaction events that are always handled. handled_events.extend( [ - &TransactionEvent::AccountPushProcedureIndex, - &TransactionEvent::LinkMapSet, - &TransactionEvent::LinkMapGet, + &TransactionEventId::AccountPushProcedureIndex, + &TransactionEventId::LinkMapSet, + &TransactionEventId::LinkMapGet, // TODO: It should be possible to remove this after implementing // https://github.com/0xMiden/miden-base/issues/1852. - &TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount, + &TransactionEventId::EpilogueBeforeTxFeeRemovedFromAccount, ] - .map(TransactionEvent::event_id), + .map(TransactionEventId::event_id), ); Self { exec_host, handled_events } @@ -77,15 +77,15 @@ impl<'store> MockHost<'store> { pub fn enable_lazy_loading(&mut self) { self.handled_events.extend( [ - &TransactionEvent::AccountBeforeForeignLoad, - &TransactionEvent::AccountVaultBeforeGetBalance, - &TransactionEvent::AccountVaultBeforeHasNonFungibleAsset, - &TransactionEvent::AccountVaultBeforeAddAsset, - &TransactionEvent::AccountVaultBeforeRemoveAsset, - &TransactionEvent::AccountStorageBeforeSetMapItem, - &TransactionEvent::AccountStorageBeforeGetMapItem, + &TransactionEventId::AccountBeforeForeignLoad, + &TransactionEventId::AccountVaultBeforeGetBalance, + &TransactionEventId::AccountVaultBeforeHasNonFungibleAsset, + &TransactionEventId::AccountVaultBeforeAddAsset, + &TransactionEventId::AccountVaultBeforeRemoveAsset, + &TransactionEventId::AccountStorageBeforeSetMapItem, + &TransactionEventId::AccountStorageBeforeGetMapItem, ] - .map(TransactionEvent::event_id), + .map(TransactionEventId::event_id), ); } } diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index ae8f476d89..9b069b5378 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -260,7 +260,7 @@ pub enum TransactionKernelError { #[error("recipient data `{0:?}` in the advice provider is not well formed")] MalformedRecipientData(Vec), #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")] - MissingNote(u64), + MissingNote(usize), #[error( "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider" )] diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 0d3283ac2a..81a23a49e5 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -1,8 +1,9 @@ +use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::{EventId, TransactionAdviceInputs}; +use miden_lib::transaction::TransactionAdviceInputs; use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::account::{AccountCode, AccountDelta, AccountId, PartialAccount}; use miden_objects::assembly::debuginfo::Location; @@ -11,7 +12,7 @@ use miden_objects::asset::{Asset, AssetVaultKey, AssetWitness, FungibleAsset}; use miden_objects::block::BlockNumber; use miden_objects::crypto::merkle::SmtProof; use miden_objects::note::{NoteInputs, NoteMetadata, NoteRecipient}; -use miden_objects::transaction::{InputNote, InputNotes, OutputNote}; +use miden_objects::transaction::{InputNote, InputNotes, OutputNote, TransactionSummary}; use miden_objects::vm::AdviceMap; use miden_objects::{Felt, Hasher, Word}; use miden_processor::{ @@ -26,12 +27,11 @@ use miden_processor::{ use crate::auth::{SigningInputs, TransactionAuthenticator}; use crate::errors::TransactionKernelError; -use crate::host::note_builder::OutputNoteBuilder; use crate::host::{ + RecipientData, ScriptMastForestStore, TransactionBaseHost, - TransactionEventData, - TransactionEventHandling, + TransactionEvent, TransactionProgress, }; use crate::{AccountProcedureIndexMap, DataStore}; @@ -54,6 +54,11 @@ where /// The underlying base transaction host. base_host: TransactionBaseHost<'store, STORE>, + /// Tracks the number of cycles for each of the transaction execution stages. + /// + /// The progress is updated event handlers. + tx_progress: TransactionProgress, + /// Serves signature generation requests from the transaction runtime for signatures which are /// not present in the `generated_signatures` field. authenticator: Option<&'auth AUTH>, @@ -107,6 +112,7 @@ where Self { base_host, + tx_progress: TransactionProgress::default(), authenticator, ref_block, accessed_foreign_account_code: Vec::new(), @@ -120,7 +126,7 @@ where /// Returns a reference to the `tx_progress` field of this transaction host. pub fn tx_progress(&self) -> &TransactionProgress { - self.base_host.tx_progress() + &self.tx_progress } // EVENT HANDLERS @@ -179,8 +185,10 @@ where pub async fn on_auth_requested( &mut self, pub_key_hash: Word, - signing_inputs: SigningInputs, + tx_summary: TransactionSummary, ) -> Result, TransactionKernelError> { + let signing_inputs = SigningInputs::TransactionSummary(Box::new(tx_summary)); + let authenticator = self.authenticator.ok_or(TransactionKernelError::MissingAuthenticator)?; @@ -281,14 +289,14 @@ where /// [`Self::on_account_vault_asset_witness_requested`] for more on this topic. async fn on_account_storage_map_witness_requested( &self, - current_account_id: AccountId, + active_account_id: AccountId, map_root: Word, map_key: Word, ) -> Result, TransactionKernelError> { let storage_map_witness = self .base_host .store() - .get_storage_map_witness(current_account_id, map_root, map_key) + .get_storage_map_witness(active_account_id, map_root, map_key) .await .map_err(|err| TransactionKernelError::GetStorageMapWitness { map_root, @@ -343,14 +351,14 @@ where /// witnesses for the initial vault root. async fn on_account_vault_asset_witness_requested( &self, - current_account_id: AccountId, + active_account_id: AccountId, vault_root: Word, asset_key: AssetVaultKey, ) -> Result, TransactionKernelError> { let asset_witness = self .base_host .store() - .get_vault_asset_witness(current_account_id, vault_root, asset_key) + .get_vault_asset_witness(active_account_id, vault_root, asset_key) .await .map_err(|err| TransactionKernelError::GetVaultAssetWitness { vault_root, @@ -368,44 +376,51 @@ where /// where the script is not already available in the advice provider. async fn on_note_script_requested( &mut self, + note_idx: usize, + recipient_digest: Word, script_root: Word, metadata: NoteMetadata, - recipient_digest: Word, - note_idx: usize, note_inputs: NoteInputs, serial_num: Word, ) -> Result, TransactionKernelError> { let note_script_result = self.base_host.store().get_note_script(script_root).await; - let (recipient, mutations) = match note_script_result { + match note_script_result { Ok(Some(note_script)) => { let script_felts: Vec = (¬e_script).into(); let recipient = NoteRecipient::new(serial_num, note_script, note_inputs); - let mutations = vec![AdviceMutation::extend_map(AdviceMap::from_iter([( + + if recipient.digest() != recipient_digest { + return Err(TransactionKernelError::other(format!( + "recipient digest is {recipient_digest}, but recipient constructed from raw inputs has digest {}", + recipient.digest() + ))); + } + + self.base_host.output_note_from_recipient(note_idx, metadata, recipient)?; + + Ok(vec![AdviceMutation::extend_map(AdviceMap::from_iter([( script_root, script_felts, - )]))]; - - (Some(recipient), mutations) + )]))]) }, - Ok(None) if metadata.is_private() => (None, Vec::new()), - Ok(None) => { - return Err(TransactionKernelError::other(format!( - "note script with root {script_root} not found in data store for public note" - ))); - }, - Err(err) => { - return Err(TransactionKernelError::other_with_source( - "failed to retrieve note script from data store", - err, - )); - }, - }; - - let note_builder = OutputNoteBuilder::new(metadata, recipient_digest, recipient)?; - self.base_host.insert_output_note_builder(note_idx, note_builder)?; + Ok(None) if metadata.is_private() => { + self.base_host.output_note_from_recipient_digest( + note_idx, + metadata, + recipient_digest, + )?; - Ok(mutations) + Ok(Vec::new()) + }, + Ok(None) => Err(TransactionKernelError::other(format!( + "note script with root {script_root} not found in data store for public note" + ))), + Err(err) => Err(TransactionKernelError::other_with_source( + "failed to retrieve note script from data store", + err, + )), + } } /// Consumes `self` and returns the account delta, output notes, generated signatures and @@ -421,7 +436,7 @@ where BTreeMap>, TransactionProgress, ) { - let (account_delta, input_notes, output_notes, tx_progress) = self.base_host.into_parts(); + let (account_delta, input_notes, output_notes) = self.base_host.into_parts(); ( account_delta, @@ -429,7 +444,7 @@ where output_notes, self.accessed_foreign_account_code, self.generated_signatures, - tx_progress, + self.tx_progress, ) } } @@ -467,72 +482,208 @@ where &mut self, process: &ProcessState, ) -> impl FutureMaybeSend, EventError>> { - let event_id = EventId::from_felt(process.get_stack_item(0)); - - // TODO: Eventually, refactor this to let TransactionEvent contain the data directly, which - // should be cleaner. - let event_handling_result = self.base_host.handle_event(process, event_id); + let stdlib_event_result = self.base_host.handle_stdlib_events(process); + + // If the event was handled by a stdlib handler (Ok(Some)), we will return the result from + // within the async block below. So, we only need to extract th tx event if the event was + // not yet handled (Ok(None)). + let tx_event_result = match stdlib_event_result { + Ok(None) => Some(TransactionEvent::extract(&self.base_host, process)), + _ => None, + }; async move { - let event_handling = event_handling_result?; - let event_data = match event_handling { - TransactionEventHandling::Unhandled(event) => event, - TransactionEventHandling::Handled(mutations) => { - return Ok(mutations); - }, + if let Some(mutations) = stdlib_event_result? { + return Ok(mutations); + } + + // The outer None means the event was handled by stdlib handlers. + let Some(tx_event_result) = tx_event_result else { + return Ok(Vec::new()); + }; + // The inner None means the transaction event ID does not need to be handled. + let Some(tx_event) = tx_event_result? else { + return Ok(Vec::new()); }; - match event_data { - TransactionEventData::AuthRequest { pub_key_hash, signing_inputs } => self - .on_auth_requested(pub_key_hash, signing_inputs) - .await - .map_err(EventError::from), - TransactionEventData::TransactionFeeComputed { fee_asset } => self - .on_before_tx_fee_removed_from_account(fee_asset) - .await - .map_err(EventError::from), - TransactionEventData::ForeignAccount { account_id } => { - self.on_foreign_account_requested(account_id).await.map_err(EventError::from) + let result = match tx_event { + TransactionEvent::AccountBeforeForeignLoad { foreign_account_id: account_id } => { + self.on_foreign_account_requested(account_id).await + }, + + TransactionEvent::AccountVaultAfterRemoveAsset { asset } => { + self.base_host.on_account_vault_after_remove_asset(asset) }, - TransactionEventData::AccountVaultAssetWitness { - current_account_id, + TransactionEvent::AccountVaultAfterAddAsset { asset } => { + self.base_host.on_account_vault_after_add_asset(asset) + }, + + TransactionEvent::AccountStorageAfterSetItem { slot_idx, new_value } => { + self.base_host.on_account_storage_after_set_item(slot_idx, new_value) + }, + + TransactionEvent::AccountStorageAfterSetMapItem { + slot_index, + key, + prev_map_value, + new_map_value, + } => self.base_host.on_account_storage_after_set_map_item( + slot_index, + key, + prev_map_value, + new_map_value, + ), + + TransactionEvent::AccountVaultBeforeAssetAccess { + active_account_id, vault_root, asset_key, - } => self - .on_account_vault_asset_witness_requested( - current_account_id, + } => { + self.on_account_vault_asset_witness_requested( + active_account_id, vault_root, asset_key, ) .await - .map_err(EventError::from), - TransactionEventData::AccountStorageMapWitness { - current_account_id, + }, + + TransactionEvent::AccountStorageBeforeMapItemAccess { + active_account_id, map_root, map_key, - } => self - .on_account_storage_map_witness_requested(current_account_id, map_root, map_key) - .await - .map_err(EventError::from), - TransactionEventData::NoteData { - note_idx, - metadata, - script_root, - recipient_digest, - note_inputs, - serial_num, - } => self - .on_note_script_requested( - script_root, - metadata, - recipient_digest, - note_idx, - note_inputs, - serial_num, + } => { + self.on_account_storage_map_witness_requested( + active_account_id, + map_root, + map_key, ) .await - .map_err(EventError::from), - } + }, + + TransactionEvent::AccountAfterIncrementNonce => { + self.base_host.on_account_after_increment_nonce() + }, + + TransactionEvent::AccountPushProcedureIndex { code_commitment, procedure_root } => { + self.base_host.on_account_push_procedure_index(code_commitment, procedure_root) + }, + + TransactionEvent::NoteAfterCreated { note_idx, metadata, recipient_data } => { + match recipient_data { + RecipientData::Digest(recipient_digest) => { + self.base_host.output_note_from_recipient_digest( + note_idx, + metadata, + recipient_digest, + ) + }, + RecipientData::Recipient(note_recipient) => self + .base_host + .output_note_from_recipient(note_idx, metadata, note_recipient), + RecipientData::ScriptMissing { + recipient_digest, + serial_num, + script_root, + note_inputs, + } => { + self.on_note_script_requested( + note_idx, + recipient_digest, + script_root, + metadata, + note_inputs, + serial_num, + ) + .await + }, + } + }, + + TransactionEvent::NoteBeforeAddAsset { note_idx, asset } => { + self.base_host.on_note_before_add_asset(note_idx, asset) + }, + + TransactionEvent::AuthRequest { pub_key_hash, tx_summary, signature } => { + if let Some(signature) = signature { + Ok(self.base_host.on_auth_requested(signature)) + } else { + self.on_auth_requested(pub_key_hash, tx_summary).await + } + }, + + // This always returns an error to abort the transaction. + TransactionEvent::Unauthorized { tx_summary } => { + Err(TransactionKernelError::Unauthorized(Box::new(tx_summary))) + }, + + TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount { fee_asset } => { + self.on_before_tx_fee_removed_from_account(fee_asset).await + }, + + TransactionEvent::LinkMapSet { advice_mutation } => Ok(advice_mutation), + TransactionEvent::LinkMapGet { advice_mutation } => Ok(advice_mutation), + + TransactionEvent::PrologueStart { clk } => { + self.tx_progress.start_prologue(clk); + Ok(Vec::new()) + }, + TransactionEvent::PrologueEnd { clk } => { + self.tx_progress.end_prologue(clk); + Ok(Vec::new()) + }, + + TransactionEvent::NotesProcessingStart { clk } => { + self.tx_progress.start_notes_processing(clk); + Ok(Vec::new()) + }, + TransactionEvent::NotesProcessingEnd { clk } => { + self.tx_progress.end_notes_processing(clk); + Ok(Vec::new()) + }, + + TransactionEvent::NoteExecutionStart { note_id, clk } => { + self.tx_progress.start_note_execution(clk, note_id); + Ok(Vec::new()) + }, + TransactionEvent::NoteExecutionEnd { clk } => { + self.tx_progress.end_note_execution(clk); + Ok(Vec::new()) + }, + + TransactionEvent::TxScriptProcessingStart { clk } => { + self.tx_progress.start_tx_script_processing(clk); + Ok(Vec::new()) + }, + TransactionEvent::TxScriptProcessingEnd { clk } => { + self.tx_progress.end_tx_script_processing(clk); + Ok(Vec::new()) + }, + + TransactionEvent::EpilogueStart { clk } => { + self.tx_progress.start_epilogue(clk); + Ok(Vec::new()) + }, + TransactionEvent::EpilogueEnd { clk } => { + self.tx_progress.end_epilogue(clk); + Ok(Vec::new()) + }, + + TransactionEvent::EpilogueAuthProcStart { clk } => { + self.tx_progress.start_auth_procedure(clk); + Ok(Vec::new()) + }, + TransactionEvent::EpilogueAuthProcEnd { clk } => { + self.tx_progress.end_auth_procedure(clk); + Ok(Vec::new()) + }, + + TransactionEvent::EpilogueAfterTxCyclesObtained { clk } => { + self.tx_progress.epilogue_after_tx_cycles_obtained(clk); + Ok(Vec::new()) + }, + }; + + result.map_err(EventError::from) } } } diff --git a/crates/miden-tx/src/host/account_procedures.rs b/crates/miden-tx/src/host/account_procedures.rs index c4c664977f..18440e8284 100644 --- a/crates/miden-tx/src/host/account_procedures.rs +++ b/crates/miden-tx/src/host/account_procedures.rs @@ -1,7 +1,6 @@ -use miden_lib::transaction::memory::{ACCOUNT_STACK_TOP_PTR, ACCT_CODE_COMMITMENT_OFFSET}; use miden_objects::account::AccountCode; -use super::{BTreeMap, ProcessState, Word}; +use super::{BTreeMap, Word}; use crate::errors::{TransactionHostError, TransactionKernelError}; // ACCOUNT PROCEDURE INDEX MAP @@ -54,41 +53,23 @@ impl AccountProcedureIndexMap { Ok(()) } - /// Returns index of the procedure whose root is currently at the top of the operand stack in - /// the provided process. + /// Returns the index of the requested procedure root in the account code identified by the + /// provided commitment. /// /// # Errors - /// Returns an error if the procedure at the top of the operand stack is not present in this - /// map. - pub fn get_proc_index(&self, process: &ProcessState) -> Result { - // get active account code commitment - let code_commitment = { - let account_stack_top_ptr = process - .get_mem_value(process.ctx(), ACCOUNT_STACK_TOP_PTR) - .expect("Account stack top pointer was not initialized") - .as_int(); - let curr_data_ptr = process - .get_mem_value( - process.ctx(), - account_stack_top_ptr - .try_into() - .expect("account stack top pointer should be less than u32::MAX"), - ) - .expect("active account pointer was not initialized") - .as_int(); - process - .get_mem_word(process.ctx(), curr_data_ptr as u32 + ACCT_CODE_COMMITMENT_OFFSET) - .expect("failed to read a word from memory") - .expect("active account code commitment was not initialized") - }; - - let proc_root = process.get_stack_word_be(1); - + /// + /// Returns an error if: + /// - the requested procedure is not present in this map. + pub fn get_proc_index( + &self, + code_commitment: Word, + procedure_root: Word, + ) -> Result { self.0 .get(&code_commitment) .ok_or(TransactionKernelError::UnknownCodeCommitment(code_commitment))? - .get(&proc_root) + .get(&procedure_root) .cloned() - .ok_or(TransactionKernelError::UnknownAccountProcedure(proc_root)) + .ok_or(TransactionKernelError::UnknownAccountProcedure(procedure_root)) } } diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 5280c811d3..289cc8fabc 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -1,12 +1,13 @@ use miden_lib::transaction::memory::{ ACCOUNT_STACK_TOP_PTR, + ACCT_CODE_COMMITMENT_OFFSET, ACTIVE_INPUT_NOTE_PTR, NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, }; use miden_objects::account::AccountId; use miden_objects::note::{NoteId, NoteInputs}; use miden_objects::{Hasher, Word}; -use miden_processor::{EventError, ExecutionError, Felt, ProcessState}; +use miden_processor::{ExecutionError, Felt, ProcessState}; use crate::errors::TransactionKernelError; @@ -14,13 +15,20 @@ use crate::errors::TransactionKernelError; // ================================================================================================ pub(super) trait TransactionKernelProcess { + /// Returns the pointer to the active account. + fn get_active_account_ptr(&self) -> Result; + + /// Returns the [`AccountId`] of the active account. fn get_active_account_id(&self) -> Result; + /// Returns the account code commitment of the active account. + fn get_active_account_code_commitment(&self) -> Result; + fn get_num_storage_slots(&self) -> Result; fn get_vault_root(&self, vault_root_ptr: Felt) -> Result; - fn get_active_note_id(&self) -> Result, EventError>; + fn get_active_note_id(&self) -> Result, TransactionKernelError>; fn read_note_recipient_info_from_adv_map( &self, @@ -33,11 +41,18 @@ pub(super) trait TransactionKernelProcess { ) -> Result; fn has_advice_map_entry(&self, key: Word) -> bool; + + /// Returns `true` if the advice provider has a merkle path for the provided root and leaf + /// index, `false` otherwise. + fn has_merkle_path( + &self, + root: Word, + leaf_index: Felt, + ) -> Result; } impl<'a> TransactionKernelProcess for ProcessState<'a> { - /// Returns the ID of the currently active account. - fn get_active_account_id(&self) -> Result { + fn get_active_account_ptr(&self) -> Result { let account_stack_top_ptr = self.get_mem_value(self.ctx(), ACCOUNT_STACK_TOP_PTR).ok_or_else(|| { TransactionKernelError::other("account stack top ptr should be initialized") @@ -49,10 +64,12 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { let active_account_ptr = self .get_mem_value(self.ctx(), account_stack_top_ptr) .ok_or_else(|| TransactionKernelError::other("account id should be initialized"))?; - let active_account_ptr = u32::try_from(active_account_ptr).map_err(|_| { - TransactionKernelError::other("active account ptr should fit into a u32") - })?; + u32::try_from(active_account_ptr) + .map_err(|_| TransactionKernelError::other("active account ptr should fit into a u32")) + } + fn get_active_account_id(&self) -> Result { + let active_account_ptr = self.get_active_account_ptr()?; let active_account_id_and_nonce = self .get_mem_word(self.ctx(), active_account_ptr) .map_err(|_| { @@ -70,6 +87,23 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { }) } + fn get_active_account_code_commitment(&self) -> Result { + let active_account_ptr = self.get_active_account_ptr()?; + let code_commitment = self + .get_mem_word(self.ctx(), active_account_ptr + ACCT_CODE_COMMITMENT_OFFSET) + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to read code commitment from memory", + err, + ) + })? + .ok_or_else(|| { + TransactionKernelError::other("active account code commitment was not initialized") + })?; + + Ok(code_commitment) + } + /// Returns the number of storage slots initialized for the active account. /// /// # Errors @@ -91,7 +125,7 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { /// # Errors /// Returns an error if the address of the active note is invalid (e.g., greater than /// `u32::MAX`). - fn get_active_note_id(&self) -> Result, EventError> { + fn get_active_note_id(&self) -> Result, TransactionKernelError> { // get the note address in `Felt` or return `None` if the address hasn't been accessed // previously. let note_address_felt = match self.get_mem_value(self.ctx(), ACTIVE_INPUT_NOTE_PTR) { @@ -100,7 +134,7 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { }; // convert note address into u32 let note_address = u32::try_from(note_address_felt).map_err(|_| { - EventError::from(format!( + TransactionKernelError::other(format!( "failed to convert {note_address_felt} into a memory address (u32)" )) })?; @@ -110,7 +144,12 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { } else { Ok(self .get_mem_word(self.ctx(), note_address) - .map_err(ExecutionError::MemoryError)? + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to read note address", + ExecutionError::MemoryError(err), + ) + })? .map(NoteId::from)) } } @@ -193,6 +232,21 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { fn has_advice_map_entry(&self, key: Word) -> bool { self.advice_provider().get_mapped_values(&key).is_some() } + + fn has_merkle_path( + &self, + root: Word, + leaf_index: Felt, + ) -> Result { + self.advice_provider() + .has_merkle_path(root, Felt::from(TREE_DEPTH), leaf_index) + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to check for merkle path presence in advice provider", + err, + ) + }) + } } // HELPER FUNCTIONS diff --git a/crates/miden-tx/src/host/link_map.rs b/crates/miden-tx/src/host/link_map.rs index de96421edf..5a6f15638c 100644 --- a/crates/miden-tx/src/host/link_map.rs +++ b/crates/miden-tx/src/host/link_map.rs @@ -3,7 +3,7 @@ use core::cmp::Ordering; use miden_objects::{Felt, LexicographicWord, Word, ZERO}; use miden_processor::fast::ExecutionOutput; -use miden_processor::{AdviceMutation, ContextId, EventError, ProcessState}; +use miden_processor::{AdviceMutation, ContextId, ProcessState}; // LINK MAP // ================================================================================================ @@ -42,7 +42,7 @@ impl<'process> LinkMap<'process> { /// /// Expected operand stack state before: [map_ptr, KEY, NEW_VALUE] /// Advice stack state after: [set_operation, entry_ptr] - pub fn handle_set_event(process: &ProcessState<'_>) -> Result, EventError> { + pub fn handle_set_event(process: &ProcessState<'_>) -> Vec { let map_ptr = process.get_stack_item(1); let map_key = process.get_stack_word_be(2); @@ -51,17 +51,14 @@ impl<'process> LinkMap<'process> { let (set_op, entry_ptr) = link_map.compute_set_operation(LexicographicWord::from(map_key)); - Ok(vec![AdviceMutation::extend_stack([ - Felt::from(set_op as u8), - Felt::from(entry_ptr), - ])]) + vec![AdviceMutation::extend_stack([Felt::from(set_op as u8), Felt::from(entry_ptr)])] } /// Handles a `LINK_MAP_GET_EVENT` emitted from a VM. /// /// Expected operand stack state before: [map_ptr, KEY] /// Advice stack state after: [get_operation, entry_ptr] - pub fn handle_get_event(process: &ProcessState<'_>) -> Result, EventError> { + pub fn handle_get_event(process: &ProcessState<'_>) -> Vec { let map_ptr = process.get_stack_item(1); let map_key = process.get_stack_word_be(2); @@ -69,10 +66,7 @@ impl<'process> LinkMap<'process> { let link_map = LinkMap::new(map_ptr, &mem_viewer); let (get_op, entry_ptr) = link_map.compute_get_operation(LexicographicWord::from(map_key)); - Ok(vec![AdviceMutation::extend_stack([ - Felt::from(get_op as u8), - Felt::from(entry_ptr), - ])]) + vec![AdviceMutation::extend_stack([Felt::from(get_op as u8), Felt::from(entry_ptr)])] } /// Returns `true` if the map is empty, `false` otherwise. diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index d31a8fb161..0ab53cc254 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -11,6 +11,7 @@ pub use account_procedures::AccountProcedureIndexMap; pub(crate) mod note_builder; use miden_lib::StdLibrary; +use miden_lib::transaction::EventId; use note_builder::OutputNoteBuilder; mod kernel_process; @@ -21,12 +22,13 @@ pub use script_mast_forest_store::ScriptMastForestStore; mod tx_progress; +mod tx_event; use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::{EventId, TransactionEvent, TransactionEventError}; +use miden_objects::Word; use miden_objects::account::{ AccountCode, AccountDelta, @@ -34,11 +36,9 @@ use miden_objects::account::{ AccountId, AccountStorageHeader, PartialAccount, - StorageMap, - StorageSlotType, }; -use miden_objects::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; -use miden_objects::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; +use miden_objects::asset::Asset; +use miden_objects::note::{NoteId, NoteMetadata, NoteRecipient}; use miden_objects::transaction::{ InputNote, InputNotes, @@ -48,11 +48,8 @@ use miden_objects::transaction::{ TransactionSummary, }; use miden_objects::vm::RowIndex; -use miden_objects::{Hasher, Word}; use miden_processor::{ - AdviceError, AdviceMutation, - ContextId, EventError, EventHandlerRegistry, Felt, @@ -60,9 +57,9 @@ use miden_processor::{ MastForestStore, ProcessState, }; +pub(crate) use tx_event::{RecipientData, TransactionEvent}; pub use tx_progress::TransactionProgress; -use crate::auth::SigningInputs; use crate::errors::{TransactionHostError, TransactionKernelError}; // TRANSACTION BASE HOST @@ -100,19 +97,11 @@ pub struct TransactionBaseHost<'store, STORE> { /// map. output_notes: BTreeMap, - /// Tracks the number of cycles for each of the transaction execution stages. - /// - /// The progress is updated event handlers. - tx_progress: TransactionProgress, - /// Handle the VM default events _before_ passing it to user defined ones. stdlib_handlers: EventHandlerRegistry, } -impl<'store, STORE> TransactionBaseHost<'store, STORE> -where - STORE: MastForestStore, -{ +impl<'store, STORE> TransactionBaseHost<'store, STORE> { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -144,7 +133,6 @@ where acct_procedure_index_map, output_notes: BTreeMap::default(), input_notes, - tx_progress: TransactionProgress::default(), stdlib_handlers, } } @@ -152,18 +140,9 @@ where // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the [`MastForest`] that contains the procedure with the given `procedure_root`. - pub fn get_mast_forest(&self, procedure_root: &Word) -> Option> { - // Search in the note MAST forest store, otherwise fall back to the user-provided store - match self.scripts_mast_store.get(procedure_root) { - Some(forest) => Some(forest), - None => self.mast_store.get(procedure_root), - } - } - - /// Returns a reference to the `tx_progress` field of this transaction host. - pub fn tx_progress(&self) -> &TransactionProgress { - &self.tx_progress + /// Returns the ID of the native account. + pub fn native_account_id(&self) -> AccountId { + self.initial_account_header().id() } /// Returns a reference to the initial account header of the native account, which represents @@ -199,18 +178,11 @@ where self.output_notes.values().cloned().map(|builder| builder.build()).collect() } - /// Consumes `self` and returns the account delta, output notes and transaction progress. - pub fn into_parts( - self, - ) -> (AccountDelta, InputNotes, Vec, TransactionProgress) { + /// Consumes `self` and returns the account delta, input and output notes. + pub fn into_parts(self) -> (AccountDelta, InputNotes, Vec) { let output_notes = self.output_notes.into_values().map(|builder| builder.build()).collect(); - ( - self.account_delta.into_delta(), - self.input_notes, - output_notes, - self.tx_progress, - ) + (self.account_delta.into_delta(), self.input_notes, output_notes) } // MUTATORS @@ -219,7 +191,9 @@ where /// Inserts an output note builder at the specified index. /// /// # Errors - /// Returns an error if a note builder already exists at the given index. + /// + /// Returns an error if: + /// - a note builder already exists at the given index. pub(super) fn insert_output_note_builder( &mut self, note_idx: usize, @@ -235,7 +209,35 @@ where Ok(()) } - /// Returns a mutable reference to the [`AccountProcedureIndexMap`]. + /// Inserts an [`OutputNoteBuilder`] into the output notes created from only the recipient + /// digest. + pub(super) fn output_note_from_recipient_digest( + &mut self, + note_idx: usize, + metadata: NoteMetadata, + recipient_digest: Word, + ) -> Result, TransactionKernelError> { + let note_builder = OutputNoteBuilder::from_recipient_digest(metadata, recipient_digest)?; + self.insert_output_note_builder(note_idx, note_builder)?; + + Ok(Vec::new()) + } + + /// Inserts an [`OutputNoteBuilder`] into the output notes created from the full + /// [`NoteRecipient`] object. + pub(super) fn output_note_from_recipient( + &mut self, + note_idx: usize, + metadata: NoteMetadata, + recipient: NoteRecipient, + ) -> Result, TransactionKernelError> { + let note_builder = OutputNoteBuilder::from_recipient(metadata, recipient); + self.insert_output_note_builder(note_idx, note_builder)?; + + Ok(Vec::new()) + } + + /// Loads the provided [`AccountCode`] into the host's [`AccountProcedureIndexMap`]. pub fn load_foreign_account_code( &mut self, account_code: &AccountCode, @@ -246,756 +248,138 @@ where // EVENT HANDLERS // -------------------------------------------------------------------------------------------- - /// Handles the given [`TransactionEvent`], for example by updating the account delta or pushing - /// requested advice to the advice stack. - pub(super) fn handle_event( - &mut self, - process: &ProcessState, - event_id: EventId, - ) -> Result { - if let Some(mutations) = self.stdlib_handlers.handle_event(event_id, process)? { - return Ok(TransactionEventHandling::Handled(mutations)); - } - - let transaction_event = TransactionEvent::try_from(event_id).map_err(EventError::from)?; - - // Privileged events can only be emitted from the root context. - if process.ctx() != ContextId::root() && transaction_event.is_privileged() { - return Err(Box::new(TransactionEventError::NotRootContext(transaction_event))); - } - - let advice_mutations = match transaction_event { - TransactionEvent::AccountBeforeForeignLoad => { - self.on_account_before_foreign_load(process) - } - - TransactionEvent::AccountVaultBeforeAddAsset => { - self.on_account_vault_before_add_or_remove_asset(process) - }, - TransactionEvent::AccountVaultAfterAddAsset => { - self.on_account_vault_after_add_asset(process).map(|_| TransactionEventHandling::Handled(Vec::new())) - }, - - TransactionEvent::AccountVaultBeforeRemoveAsset => { - self.on_account_vault_before_add_or_remove_asset(process) - }, - TransactionEvent::AccountVaultAfterRemoveAsset => { - self.on_account_vault_after_remove_asset(process).map(|_| TransactionEventHandling::Handled(Vec::new())) - }, - - TransactionEvent::AccountVaultBeforeGetBalance => { - self.on_account_vault_before_get_balance(process) - }, - - TransactionEvent::AccountVaultBeforeHasNonFungibleAsset => { - self.on_account_vault_before_has_non_fungible_asset(process) - } - - TransactionEvent::AccountStorageBeforeGetMapItem => { - self.on_account_storage_before_get_map_item(process) - } - - TransactionEvent::AccountStorageBeforeSetItem => Ok(TransactionEventHandling::Handled(Vec::new())), - TransactionEvent::AccountStorageAfterSetItem => { - self.on_account_storage_after_set_item(process).map(|_| TransactionEventHandling::Handled(Vec::new())) - }, - - TransactionEvent::AccountStorageBeforeSetMapItem => { - self.on_account_storage_before_set_map_item(process) - }, - TransactionEvent::AccountStorageAfterSetMapItem => { - self.on_account_storage_after_set_map_item(process).map(|_| TransactionEventHandling::Handled(Vec::new())) - }, - - TransactionEvent::AccountBeforeIncrementNonce => { - Ok(TransactionEventHandling::Handled(Vec::new())) - }, - TransactionEvent::AccountAfterIncrementNonce => { - self.on_account_after_increment_nonce().map(|_| TransactionEventHandling::Handled(Vec::new())) - }, - - TransactionEvent::AccountPushProcedureIndex => { - self.on_account_push_procedure_index(process).map(TransactionEventHandling::Handled) - }, - - TransactionEvent::NoteBeforeCreated => Ok(TransactionEventHandling::Handled(Vec::new())), - TransactionEvent::NoteAfterCreated => self.on_note_after_created(process), - - TransactionEvent::NoteBeforeAddAsset => self.on_note_before_add_asset(process).map(|_| TransactionEventHandling::Handled(Vec::new())), - TransactionEvent::NoteAfterAddAsset => Ok(TransactionEventHandling::Handled(Vec::new())), - - TransactionEvent::AuthRequest => self.on_auth_requested(process), - - TransactionEvent::PrologueStart => { - self.tx_progress.start_prologue(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - }, - TransactionEvent::PrologueEnd => { - self.tx_progress.end_prologue(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - }, - - TransactionEvent::NotesProcessingStart => { - self.tx_progress.start_notes_processing(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - }, - TransactionEvent::NotesProcessingEnd => { - self.tx_progress.end_notes_processing(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - }, - - TransactionEvent::NoteExecutionStart => { - let note_id = process.get_active_note_id()?.expect( - "Note execution interval measurement is incorrect: check the placement of the start and the end of the interval", - ); - self.tx_progress.start_note_execution(process.clk(), note_id); - Ok(TransactionEventHandling::Handled(Vec::new())) - }, - TransactionEvent::NoteExecutionEnd => { - self.tx_progress.end_note_execution(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - }, - - TransactionEvent::TxScriptProcessingStart => { - self.tx_progress.start_tx_script_processing(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - } - TransactionEvent::TxScriptProcessingEnd => { - self.tx_progress.end_tx_script_processing(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - } - - TransactionEvent::EpilogueStart => { - self.tx_progress.start_epilogue(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - } - TransactionEvent::EpilogueAuthProcStart => { - self.tx_progress.start_auth_procedure(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - } - TransactionEvent::EpilogueAuthProcEnd => { - self.tx_progress.end_auth_procedure(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - } - TransactionEvent::EpilogueAfterTxCyclesObtained => { - self.tx_progress.epilogue_after_tx_cycles_obtained(process.clk()); - Ok(TransactionEventHandling::Handled(vec![])) - } - TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount => self.on_before_tx_fee_removed_from_account(process), - TransactionEvent::EpilogueEnd => { - self.tx_progress.end_epilogue(process.clk()); - Ok(TransactionEventHandling::Handled(Vec::new())) - } - TransactionEvent::LinkMapSet => { - return LinkMap::handle_set_event(process).map(TransactionEventHandling::Handled); - }, - TransactionEvent::LinkMapGet => { - return LinkMap::handle_get_event(process).map(TransactionEventHandling::Handled); - }, - TransactionEvent::Unauthorized => { - // Note: This always returns an error to abort the transaction. - Err(self.on_unauthorized(process)) - } - } - .map_err(EventError::from)?; - - Ok(advice_mutations) - } - - /// Extract all necessary data for requesting the data to access the foreign account that is - /// being loaded. + /// Handles the event if the stdlib event handler registry contains a handler with the emitted + /// event ID. /// - /// Expected stack state: `[event, account_id_prefix, account_id_suffix]` - pub fn on_account_before_foreign_load( + /// Returns `Some` if the event was handled, `None` otherwise. + pub fn handle_stdlib_events( &self, process: &ProcessState, - ) -> Result { - let account_id_word = process.get_stack_word_be(1); - let account_id = - AccountId::try_from([account_id_word[3], account_id_word[2]]).map_err(|err| { - TransactionKernelError::other_with_source( - "failed to convert account ID word into account ID", - err, - ) - })?; - - Ok(TransactionEventHandling::Unhandled(TransactionEventData::ForeignAccount { - account_id, - })) - } - - /// Pushes a signature to the advice stack as a response to the `AuthRequest` event. - /// - /// Expected stack state: `[event, MESSAGE, PUB_KEY]` - /// - /// The signature is fetched from the advice map using `hash(PUB_KEY, MESSAGE)` as the key. If - /// not present in the advice map [`TransactionEventHandling::Unhandled`] is returned with the - /// data required to request a signature from a - /// [`TransactionAuthenticator`](crate::auth::TransactionAuthenticator). - fn on_auth_requested( - &self, - process: &ProcessState, - ) -> Result { - let message = process.get_stack_word_be(1); - let pub_key_hash = process.get_stack_word_be(5); - let signature_key = Hasher::merge(&[pub_key_hash, message]); - - let tx_summary = self.build_tx_summary(process, message)?; - - if message != tx_summary.to_commitment() { - return Err(TransactionKernelError::TransactionSummaryConstructionFailed( - "transaction summary doesn't commit to the expected message".into(), - )); - } - - let signing_inputs = SigningInputs::TransactionSummary(Box::new(tx_summary)); - - if let Some(signature) = process - .advice_provider() - .get_mapped_values(&signature_key) - .map(|slice| slice.to_vec()) - { - return Ok(TransactionEventHandling::Handled(vec![AdviceMutation::extend_stack( - signature, - )])); + ) -> Result>, EventError> { + let event_id = EventId::from_felt(process.get_stack_item(0)); + if let Some(mutations) = self.stdlib_handlers.handle_event(event_id, process)? { + Ok(Some(mutations)) + } else { + Ok(None) } - - // If the signature is not present in the advice map, extract the necessary data to handle - // the event at a later point. - Ok(TransactionEventHandling::Unhandled(TransactionEventData::AuthRequest { - pub_key_hash, - signing_inputs, - })) } - /// Aborts the transaction by building the - /// [`TransactionSummary`](miden_objects::transaction::TransactionSummary) based on elements on - /// the operand stack and advice map. - /// - /// Expected stack state: - /// - /// `[event, MESSAGE]` - /// - /// Expected advice map state: - /// - /// ```text - /// MESSAGE -> [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT] - /// ``` - fn on_unauthorized(&self, process: &ProcessState) -> TransactionKernelError { - let msg = process.get_stack_word_be(1); - - let tx_summary = match self.build_tx_summary(process, msg) { - Ok(s) => s, - Err(err) => return err, - }; - - if msg != tx_summary.to_commitment() { - return TransactionKernelError::TransactionSummaryConstructionFailed( - "transaction summary doesn't commit to the expected message".into(), - ); - } - - TransactionKernelError::Unauthorized(Box::new(tx_summary)) + /// Converts the provided signature into an advice mutation that pushes it onto the advice stack + /// as a response to an `AuthRequest` event. + pub fn on_auth_requested(&self, signature: Vec) -> Vec { + vec![AdviceMutation::extend_stack(signature)] } - /// Extracts all necessary data to handle - /// [`TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount`]. - /// - /// Expected stack state: - /// - /// ```text - /// `[event, FEE_ASSET]` - /// ``` - fn on_before_tx_fee_removed_from_account( - &self, - process: &ProcessState, - ) -> Result { - let fee_asset = process.get_stack_word_be(1); - let fee_asset = FungibleAsset::try_from(fee_asset) - .map_err(TransactionKernelError::FailedToConvertFeeAsset)?; - - Ok(TransactionEventHandling::Unhandled( - TransactionEventData::TransactionFeeComputed { fee_asset }, - )) - } - - /// Handles the note creation event by extracting note data from the stack and advice provider. - /// - /// If the recipient data and note script are present in the advice provider, creates a new - /// [`OutputNoteBuilder`] and stores it in the `output_notes` field of this - /// [`TransactionBaseHost`]. Otherwise, returns [`TransactionEventHandling::Unhandled`] to - /// request the missing note script from the data store. - /// - /// Expected stack state: `[event, NOTE_METADATA, note_ptr, RECIPIENT, note_idx]` - fn on_note_after_created( + /// Adds an asset to the output note identified by the note index. + pub fn on_note_before_add_asset( &mut self, - process: &ProcessState, - ) -> Result { - let metadata_word = process.get_stack_word_be(1); - let metadata = NoteMetadata::try_from(metadata_word) - .map_err(TransactionKernelError::MalformedNoteMetadata)?; - - let recipient_digest = process.get_stack_word_be(6); - let note_idx = process.get_stack_item(10).as_int() as usize; - - // try to read the full recipient from the advice provider - let recipient = if process.has_advice_map_entry(recipient_digest) { - let (inputs, script_root, serial_num) = - process.read_note_recipient_info_from_adv_map(recipient_digest)?; - - if let Some(script_data) = process.advice_provider().get_mapped_values(&script_root) { - let script = NoteScript::try_from(script_data).map_err(|source| { - TransactionKernelError::MalformedNoteScript { - data: script_data.to_vec(), - source, - } - })?; - - Some(NoteRecipient::new(serial_num, script, inputs)) - } else { - // we couldn't build the full recipient because script root was missing; return the - // info that we did read so that we could request the script from the data store - return Ok(TransactionEventHandling::Unhandled(TransactionEventData::NoteData { - note_idx, - metadata, - script_root, - recipient_digest, - note_inputs: inputs, - serial_num, - })); - } - } else { - None - }; - - let note_builder = OutputNoteBuilder::new(metadata, recipient_digest, recipient)?; - self.insert_output_note_builder(note_idx, note_builder)?; - - Ok(TransactionEventHandling::Handled(Vec::new())) - } - - /// Adds an asset at the top of the [OutputNoteBuilder] identified by the note pointer. - /// - /// Expected stack state: `[event, ASSET, note_ptr, num_of_assets, note_idx]` - fn on_note_before_add_asset( - &mut self, - process: &ProcessState, - ) -> Result<(), TransactionKernelError> { - let stack = process.get_stack_state(); - //# => [ASSET, note_ptr, num_of_assets, note_idx] - - let note_idx = stack[7].as_int(); - assert!(note_idx < self.output_notes.len() as u64); - let node_idx = note_idx as usize; - - let asset_word = process.get_stack_word_be(1); - let asset = Asset::try_from(asset_word).map_err(|source| { - TransactionKernelError::MalformedAssetInEventHandler { - handler: "on_note_before_add_asset", - source, - } + note_idx: usize, + asset: Asset, + ) -> Result, TransactionKernelError> { + let note_builder = self.output_notes.get_mut(¬e_idx).ok_or_else(|| { + TransactionKernelError::other(format!("failed to find output note {note_idx}")) })?; - let note_builder = self - .output_notes - .get_mut(&node_idx) - .ok_or_else(|| TransactionKernelError::MissingNote(note_idx))?; - note_builder.add_asset(asset)?; - Ok(()) + Ok(Vec::new()) } - /// Loads the index of the procedure root onto the advice stack. - /// - /// Expected stack state: `[event, PROC_ROOT, ...]` - fn on_account_push_procedure_index( + /// Pushes the index of the procedure root in the code identified by the commitment onto the + /// advice stack. + pub fn on_account_push_procedure_index( &mut self, - process: &ProcessState, + code_commitment: Word, + procedure_root: Word, ) -> Result, TransactionKernelError> { - let proc_idx = self.acct_procedure_index_map.get_proc_index(process)?; + let proc_idx = + self.acct_procedure_index_map.get_proc_index(code_commitment, procedure_root)?; Ok(vec![AdviceMutation::extend_stack([Felt::from(proc_idx)])]) } /// Handles the increment nonce event by incrementing the nonce delta by one. - pub fn on_account_after_increment_nonce(&mut self) -> Result<(), TransactionKernelError> { + pub fn on_account_after_increment_nonce( + &mut self, + ) -> Result, TransactionKernelError> { if self.account_delta.was_nonce_incremented() { return Err(TransactionKernelError::NonceCanOnlyIncrementOnce); } self.account_delta.increment_nonce(); - Ok(()) + + Ok(Vec::new()) } // ACCOUNT STORAGE UPDATE HANDLERS // -------------------------------------------------------------------------------------------- - /// Extracts information from the process state about the storage slot being updated and - /// records the latest value of this storage slot. - /// - /// Expected stack state: `[event, slot_index, NEW_SLOT_VALUE]` + /// Tracks the insertion of an item in the account delta. pub fn on_account_storage_after_set_item( &mut self, - process: &ProcessState, - ) -> Result<(), TransactionKernelError> { - // get slot index from the stack and make sure it is valid - let slot_index = process.get_stack_item(1); - - // get number of storage slots initialized by the account - let num_storage_slot = process.get_num_storage_slots()?; - - if slot_index.as_int() >= num_storage_slot { - return Err(TransactionKernelError::InvalidStorageSlotIndex { - max: num_storage_slot, - actual: slot_index.as_int(), - }); - } - - // get the value to which the slot is being updated - let new_slot_value = process.get_stack_word_be(2); - - self.account_delta.storage().set_item(slot_index.as_int() as u8, new_slot_value); - - Ok(()) - } - - /// Checks if the necessary witness for accessing the map item is already in the merkle store, - /// and if not, extracts all necessary data for requesting it. - /// - /// Expected stack state: `[event, KEY, ROOT, index]` - pub fn on_account_storage_before_get_map_item( - &self, - process: &ProcessState, - ) -> Result { - let map_key = process.get_stack_word_be(1); - let current_map_root = process.get_stack_word_be(5); - let slot_index = process.get_stack_item(9); - - self.on_account_storage_before_get_or_set_map_item( - slot_index, - current_map_root, - map_key, - process, - ) - } + slot_index: u8, + new_slot_value: Word, + ) -> Result, TransactionKernelError> { + self.account_delta.storage().set_item(slot_index, new_slot_value); - /// Checks if the necessary witness for accessing the map item is already in the merkle store, - /// and if not, extracts all necessary data for requesting it. - /// - /// Expected stack state: `[event, index, KEY, NEW_VALUE, OLD_ROOT]` - pub fn on_account_storage_before_set_map_item( - &self, - process: &ProcessState, - ) -> Result { - let slot_index = process.get_stack_item(1); - let map_key = process.get_stack_word_be(2); - let current_map_root = process.get_stack_word_be(10); - - self.on_account_storage_before_get_or_set_map_item( - slot_index, - current_map_root, - map_key, - process, - ) + Ok(Vec::new()) } - /// Checks if the necessary witness for accessing the map item is already in the merkle store, - /// and if not, extracts all necessary data for requesting it. - fn on_account_storage_before_get_or_set_map_item( - &self, - slot_index: Felt, - current_map_root: Word, - map_key: Word, - process: &ProcessState, - ) -> Result { - let current_account_id = process.get_active_account_id()?; - let hashed_map_key = StorageMap::hash_key(map_key); - let leaf_index = StorageMap::hashed_map_key_to_leaf_index(hashed_map_key); - - if advice_provider_has_merkle_path::<{ StorageMap::DEPTH }>( - process, - current_map_root, - leaf_index, - )? { - // If the merkle path is already in the store there is nothing to do. - Ok(TransactionEventHandling::Handled(Vec::new())) - } else { - // For the native account we need to explicitly request the initial map root, while for - // foreign accounts the current map root is always the initial one. - let map_root = if current_account_id == self.initial_account_header().id() { - // For native accounts, we have to request witnesses against the initial root - // instead of the _current_ one, since the data store only has - // witnesses for initial one. - let (slot_type, slot_value) = self - .initial_account_storage_header() - // Slot index should always fit into a usize. - .slot(slot_index.as_int() as usize) - .map_err(|err| { - TransactionKernelError::other_with_source( - "failed to access storage map in storage header", - err, - ) - })?; - if *slot_type != StorageSlotType::Map { - return Err(TransactionKernelError::other(format!( - "expected map slot type at slot index {slot_index}" - ))); - } - *slot_value - } else { - current_map_root - }; - - // If the merkle path is not in the store return the data to request it. - Ok(TransactionEventHandling::Unhandled( - TransactionEventData::AccountStorageMapWitness { - current_account_id, - map_root, - map_key, - }, - )) - } - } - - /// Extracts information from the process state about the storage map being updated and - /// records the latest values of this storage map. - /// - /// Expected stack state: `[event, slot_index, KEY, PREV_MAP_VALUE, NEW_MAP_VALUE]` + /// Tracks the insertion of a storage map item in the account delta. pub fn on_account_storage_after_set_map_item( &mut self, - process: &ProcessState, - ) -> Result<(), TransactionKernelError> { - // get slot index from the stack and make sure it is valid - let slot_index = process.get_stack_item(1); - - // get number of storage slots initialized by the account - let num_storage_slot = process.get_num_storage_slots()?; - - if slot_index.as_int() >= num_storage_slot { - return Err(TransactionKernelError::InvalidStorageSlotIndex { - max: num_storage_slot, - actual: slot_index.as_int(), - }); - } - - // get the KEY to which the slot is being updated - let key = process.get_stack_word_be(2); - - // get the previous VALUE of the slot - let prev_map_value = process.get_stack_word_be(6); - - // get the VALUE to which the slot is being updated - let new_map_value = process.get_stack_word_be(10); - - self.account_delta.storage().set_map_item( - slot_index.as_int() as u8, - key, - prev_map_value, - new_map_value, - ); + slot_index: u8, + key: Word, + prev_map_value: Word, + new_map_value: Word, + ) -> Result, TransactionKernelError> { + self.account_delta + .storage() + .set_map_item(slot_index, key, prev_map_value, new_map_value); - Ok(()) + Ok(Vec::new()) } // ACCOUNT VAULT UPDATE HANDLERS // -------------------------------------------------------------------------------------------- - /// Extracts the asset that is being added to the account's vault from the process state and - /// updates the appropriate fungible or non-fungible asset map. - /// - /// Expected stack state: `[event, ASSET, ...]` + /// Tracks the addition of an asset to the account vault in the account delta. pub fn on_account_vault_after_add_asset( &mut self, - process: &ProcessState, - ) -> Result<(), TransactionKernelError> { - let asset: Asset = process.get_stack_word_be(1).try_into().map_err(|source| { - TransactionKernelError::MalformedAssetInEventHandler { - handler: "on_account_vault_after_add_asset", - source, - } - })?; - + asset: Asset, + ) -> Result, TransactionKernelError> { self.account_delta .vault_delta_mut() .add_asset(asset) .map_err(TransactionKernelError::AccountDeltaAddAssetFailed)?; - Ok(()) - } - - /// Checks if the necessary witness for accessing the asset is already in the merkle store, - /// and if not, extracts all necessary data for requesting it. - /// - /// Expected stack state: `[event, ASSET, account_vault_root_ptr]` - pub fn on_account_vault_before_add_or_remove_asset( - &self, - process: &ProcessState, - ) -> Result { - let asset_word = process.get_stack_word_be(1); - let asset = Asset::try_from(asset_word).map_err(|source| { - TransactionKernelError::MalformedAssetInEventHandler { - handler: "on_account_vault_before_add_or_remove_asset", - source, - } - })?; - let vault_root_ptr = process.get_stack_item(5); - let vault_root_ptr = u32::try_from(vault_root_ptr).map_err(|_err| { - TransactionKernelError::other(format!( - "vault root ptr should fit into a u32, but was {vault_root_ptr}" - )) - })?; - let current_vault_root = process - .get_mem_word(process.ctx(), vault_root_ptr) - .map_err(|_err| { - TransactionKernelError::other(format!( - "vault root ptr {vault_root_ptr} is not word-aligned" - )) - })? - .ok_or_else(|| { - TransactionKernelError::other(format!( - "vault root ptr {vault_root_ptr} was not initialized" - )) - })?; - - self.on_account_vault_asset_accessed(process, asset.vault_key(), current_vault_root) + Ok(Vec::new()) } - /// Extracts the asset that is being removed from the account's vault from the process state - /// and updates the appropriate fungible or non-fungible asset map. - /// - /// Expected stack state: `[event, ASSET, ...]` + /// Tracks the removal of an asset from the account vault in the account delta. pub fn on_account_vault_after_remove_asset( &mut self, - process: &ProcessState, - ) -> Result<(), TransactionKernelError> { - let asset: Asset = process.get_stack_word_be(1).try_into().map_err(|source| { - TransactionKernelError::MalformedAssetInEventHandler { - handler: "on_account_vault_after_remove_asset", - source, - } - })?; - + asset: Asset, + ) -> Result, TransactionKernelError> { self.account_delta .vault_delta_mut() .remove_asset(asset) .map_err(TransactionKernelError::AccountDeltaRemoveAssetFailed)?; - Ok(()) - } - - /// Checks if the necessary witness for accessing the asset is already in the merkle store, - /// and if not, extracts all necessary data for requesting it. - /// - /// Expected stack state: `[event, faucet_id_prefix, faucet_id_suffix, vault_root_ptr]` - pub fn on_account_vault_before_get_balance( - &self, - process: &ProcessState, - ) -> Result { - let stack_top = process.get_stack_word_be(1); - let faucet_id = AccountId::try_from([stack_top[3], stack_top[2]]).map_err(|err| { - TransactionKernelError::other_with_source( - "failed to convert faucet ID word into faucet ID", - err, - ) - })?; - let vault_root_ptr = stack_top[1]; - let vault_root = process.get_vault_root(vault_root_ptr)?; - - let vault_key = AssetVaultKey::from_account_id(faucet_id).ok_or_else(|| { - TransactionKernelError::other(format!( - "provided faucet ID {faucet_id} is not valid for fungible assets" - )) - })?; - self.on_account_vault_asset_accessed(process, vault_key, vault_root) - } - - /// Checks if the necessary witness for accessing the asset is already in the merkle store, - /// and if not, extracts all necessary data for requesting it. - /// - /// Expected stack state: `[event, ASSET, vault_root_ptr]` - pub fn on_account_vault_before_has_non_fungible_asset( - &self, - process: &ProcessState, - ) -> Result { - let asset_word = process.get_stack_word_be(1); - let asset = Asset::try_from(asset_word).map_err(|err| { - TransactionKernelError::other_with_source("provided asset is not a valid asset", err) - })?; - - let vault_root_ptr = process.get_stack_item(5); - let vault_root = process.get_vault_root(vault_root_ptr)?; - self.on_account_vault_asset_accessed(process, asset.vault_key(), vault_root) - } - - /// Checks if the necessary witness for accessing the provided asset is already in the merkle - /// store, and if not, extracts all necessary data for requesting it. - fn on_account_vault_asset_accessed( - &self, - process: &ProcessState, - vault_key: AssetVaultKey, - current_vault_root: Word, - ) -> Result { - let leaf_index = Felt::new(vault_key.to_leaf_index().value()); - let active_account_id = process.get_active_account_id()?; - - // Note that we check whether a merkle path for the current vault root is present, not - // necessarily for the root we are going to request. This is because the end goal is to - // enable access to an asset against the current vault root, and so if this - // condition is already satisfied, there is nothing to request. - if advice_provider_has_merkle_path::<{ AssetVault::DEPTH }>( - process, - current_vault_root, - leaf_index, - )? { - // If the merkle path is already in the store there is nothing to do. - Ok(TransactionEventHandling::Handled(Vec::new())) - } else { - // For the native account we need to explicitly request the initial vault root, while - // for foreign accounts the current vault root is always the initial one. - let vault_root = if active_account_id == self.initial_account_header().id() { - self.initial_account_header().vault_root() - } else { - current_vault_root - }; - - // If the merkle path is not in the store return the data to request it. - Ok(TransactionEventHandling::Unhandled( - TransactionEventData::AccountVaultAssetWitness { - current_account_id: active_account_id, - vault_root, - asset_key: vault_key, - }, - )) - } + Ok(Vec::new()) } // HELPER FUNCTIONS // -------------------------------------------------------------------------------------------- - /// Builds a [TransactionSummary] by extracting data from the advice provider and validating - /// commitments against the host's state. + /// Builds a [`TransactionSummary`] from the current host's state and validates it against the + /// provided commitments. pub(crate) fn build_tx_summary( &self, - process: &ProcessState, - msg: Word, + salt: Word, + output_notes_commitment: Word, + input_notes_commitment: Word, + account_delta_commitment: Word, ) -> Result { - let Some(commitments) = process.advice_provider().get_mapped_values(&msg) else { - return Err(TransactionKernelError::TransactionSummaryConstructionFailed( - "Expected message to exist in advice provider".into(), - )); - }; - - if commitments.len() != 16 { - return Err(TransactionKernelError::TransactionSummaryConstructionFailed( - "Expected 4 words for transaction summary commitments".into(), - )); - } - - let salt = extract_word(commitments, 0); - let output_notes_commitment = extract_word(commitments, 4); - let input_notes_commitment = extract_word(commitments, 8); - let account_delta_commitment = extract_word(commitments, 12); - let account_delta = self.build_account_delta(); let input_notes = self.input_notes(); let output_notes_vec = self.build_output_notes(); @@ -1036,116 +420,23 @@ where Ok(TransactionSummary::new(account_delta, input_notes, output_notes, salt)) } -} -impl<'store, STORE> TransactionBaseHost<'store, STORE> { /// Returns the underlying store of the base host. pub fn store(&self) -> &'store STORE { self.mast_store } } -// TRANSACTION EVENT HANDLING -// ================================================================================================ - -/// Indicates whether a [`TransactionEvent`] was handled or not. -/// -/// If it is unhandled, the necessary data to handle it is returned. -#[derive(Debug)] -pub(super) enum TransactionEventHandling { - Unhandled(TransactionEventData), - Handled(Vec), -} - -/// The data necessary to handle an [`TransactionEvent`]. -#[derive(Debug, Clone)] -pub(super) enum TransactionEventData { - /// The data necessary to handle an auth request. - AuthRequest { - /// The hash of the public key for which a signature was requested. - pub_key_hash: Word, - /// The signing inputs that summarize what is being signed. The commitment to these inputs - /// is the message that is being signed. - signing_inputs: SigningInputs, - }, - /// The data necessary to handle a transaction fee computed event. - TransactionFeeComputed { - /// The fee asset extracted from the stack. - fee_asset: FungibleAsset, - }, - /// The data necessary to request a foreign account's data from the data store. - ForeignAccount { - /// The foreign account's ID. - account_id: AccountId, - }, - /// The data necessary to request an asset witness from the data store. - AccountVaultAssetWitness { - /// The account ID for whose vault a witness is requested. - current_account_id: AccountId, - /// The vault root identifying the asset vault from which a witness is requested. - vault_root: Word, - /// The asset for which a witness is requested. - asset_key: AssetVaultKey, - }, - /// The data necessary to request a storage map witness from the data store. - AccountStorageMapWitness { - /// The account ID for whose storage a witness is requested. - current_account_id: AccountId, - /// The root of the storage map in the account at the beginning of the transaction. - map_root: Word, - /// The raw map key for which a witness is requested. - map_key: Word, - }, - /// The data necessary to request a note script from the data store. - NoteData { - /// The note index extracted from the stack. - note_idx: usize, - /// The note metadata extracted from the stack. - metadata: NoteMetadata, - /// The root of the note script being requested. - script_root: Word, - /// The recipient digest extracted from the stack. - recipient_digest: Word, - /// The note inputs extracted from the advice provider. - note_inputs: NoteInputs, - /// The serial number extracted from the advice provider. - serial_num: Word, - }, -} - -// HELPER FUNCTIONS -// ================================================================================================ - -/// Returns `true` if the advice provider has a merkle path for the provided root and leaf -/// index, `false` otherwise. -fn advice_provider_has_merkle_path( - process: &ProcessState, - root: Word, - leaf_index: Felt, -) -> Result { - match process - .advice_provider() - .get_merkle_path(root, Felt::from(TREE_DEPTH), leaf_index) - { - // Merkle path is already in the store; consider the event handled. - Ok(_) => Ok(true), - // This means the merkle path is missing in the advice provider. - Err(AdviceError::MerkleStoreLookupFailed(_)) => Ok(false), - // We should never encounter this as long as our inputs to get_merkle_path are correct. - Err(err) => Err(TransactionKernelError::other_with_source( - "unexpected get_merkle_path error", - err, - )), +impl<'store, STORE> TransactionBaseHost<'store, STORE> +where + STORE: MastForestStore, +{ + /// Returns the [`MastForest`] that contains the procedure with the given `procedure_root`. + pub fn get_mast_forest(&self, procedure_root: &Word) -> Option> { + // Search in the note MAST forest store, otherwise fall back to the user-provided store + match self.scripts_mast_store.get(procedure_root) { + Some(forest) => Some(forest), + None => self.mast_store.get(procedure_root), + } } } - -/// Extracts a word from a slice of field elements. -#[inline(always)] -fn extract_word(commitments: &[Felt], start: usize) -> Word { - Word::from([ - commitments[start], - commitments[start + 1], - commitments[start + 2], - commitments[start + 3], - ]) -} diff --git a/crates/miden-tx/src/host/note_builder.rs b/crates/miden-tx/src/host/note_builder.rs index d4016f6e65..e3642fd217 100644 --- a/crates/miden-tx/src/host/note_builder.rs +++ b/crates/miden-tx/src/host/note_builder.rs @@ -24,39 +24,39 @@ impl OutputNoteBuilder { /// recipient. /// /// # Errors - /// Returns an error if the note is public but no recipient is provided. - pub fn new( + /// + /// Returns an error if: + /// - the note is public. + pub fn from_recipient_digest( metadata: NoteMetadata, recipient_digest: Word, - recipient: Option, ) -> Result { - // For public notes, we must have a recipient - if !metadata.is_private() && recipient.is_none() { + // For public notes, we must have a recipient. + if !metadata.is_private() { return Err(TransactionKernelError::PublicNoteMissingDetails( metadata, recipient_digest, )); } - // If recipient is present, verify its digest matches the provided recipient_digest - if let Some(ref recipient) = recipient - && recipient.digest() != recipient_digest - { - return Err(TransactionKernelError::other(format!( - "recipient digest mismatch: expected {}, but recipient has digest {}", - recipient_digest, - recipient.digest() - ))); - } - Ok(Self { metadata, recipient_digest, - recipient, + recipient: None, assets: NoteAssets::default(), }) } + /// Returns a new [`OutputNoteBuilder`] from the provided metadata and recipient. + pub fn from_recipient(metadata: NoteMetadata, recipient: NoteRecipient) -> Self { + Self { + metadata, + recipient_digest: recipient.digest(), + recipient: Some(recipient), + assets: NoteAssets::default(), + } + } + // STATE MUTATORS // -------------------------------------------------------------------------------------------- diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs new file mode 100644 index 0000000000..43c7e83746 --- /dev/null +++ b/crates/miden-tx/src/host/tx_event.rs @@ -0,0 +1,730 @@ +use alloc::vec::Vec; + +use miden_lib::transaction::{EventId, TransactionEventId}; +use miden_objects::account::{AccountId, StorageMap, StorageSlotType}; +use miden_objects::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; +use miden_objects::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; +use miden_objects::transaction::TransactionSummary; +use miden_objects::{Felt, Hasher, Word}; +use miden_processor::{AdviceMutation, ProcessState, RowIndex}; + +use crate::host::{TransactionBaseHost, TransactionKernelProcess}; +use crate::{LinkMap, TransactionKernelError}; + +// TRANSACTION EVENT +// ================================================================================================ + +/// The data necessary to handle a [`TransactionEventId`]. +#[derive(Debug)] +pub(crate) enum TransactionEvent { + /// The data necessary to request a foreign account's data from the data store. + AccountBeforeForeignLoad { + /// The foreign account's ID. + foreign_account_id: AccountId, + }, + + AccountVaultAfterRemoveAsset { + asset: Asset, + }, + + AccountVaultAfterAddAsset { + asset: Asset, + }, + + AccountStorageAfterSetItem { + slot_idx: u8, + new_value: Word, + }, + + AccountStorageAfterSetMapItem { + slot_index: u8, + key: Word, + prev_map_value: Word, + new_map_value: Word, + }, + + /// The data necessary to request a storage map witness from the data store. + AccountStorageBeforeMapItemAccess { + /// The account ID for whose storage a witness is requested. + active_account_id: AccountId, + /// The root of the storage map for which a witness is requested. + map_root: Word, + /// The raw map key for which a witness is requested. + map_key: Word, + }, + + /// The data necessary to request an asset witness from the data store. + AccountVaultBeforeAssetAccess { + /// The account ID for whose vault a witness is requested. + active_account_id: AccountId, + /// The vault root identifying the asset vault from which a witness is requested. + vault_root: Word, + /// The asset for which a witness is requested. + asset_key: AssetVaultKey, + }, + + AccountAfterIncrementNonce, + + AccountPushProcedureIndex { + /// The code commitment of the active account. + code_commitment: Word, + /// The procedure root whose index is requested. + procedure_root: Word, + }, + + NoteAfterCreated { + /// The note index extracted from the stack. + note_idx: usize, + /// The note metadata extracted from the stack. + metadata: NoteMetadata, + /// The recipient data extracted from the advice inputs. + recipient_data: RecipientData, + }, + + NoteBeforeAddAsset { + /// The note index to which the asset is added. + note_idx: usize, + /// The asset that is added to the output note. + asset: Asset, + }, + + /// The data necessary to handle an auth request. + AuthRequest { + pub_key_hash: Word, + tx_summary: TransactionSummary, + signature: Option>, + }, + + Unauthorized { + tx_summary: TransactionSummary, + }, + + EpilogueBeforeTxFeeRemovedFromAccount { + fee_asset: FungibleAsset, + }, + + LinkMapSet { + advice_mutation: Vec, + }, + LinkMapGet { + advice_mutation: Vec, + }, + + PrologueStart { + clk: RowIndex, + }, + PrologueEnd { + clk: RowIndex, + }, + + NotesProcessingStart { + clk: RowIndex, + }, + NotesProcessingEnd { + clk: RowIndex, + }, + + NoteExecutionStart { + note_id: NoteId, + clk: RowIndex, + }, + NoteExecutionEnd { + clk: RowIndex, + }, + + TxScriptProcessingStart { + clk: RowIndex, + }, + TxScriptProcessingEnd { + clk: RowIndex, + }, + + EpilogueStart { + clk: RowIndex, + }, + EpilogueEnd { + clk: RowIndex, + }, + + EpilogueAuthProcStart { + clk: RowIndex, + }, + EpilogueAuthProcEnd { + clk: RowIndex, + }, + + EpilogueAfterTxCyclesObtained { + clk: RowIndex, + }, +} + +impl TransactionEvent { + /// Extracts the [`TransactionEventId`] from the stack as well as the data necessary to handle + /// it. + /// + /// Returns `Some` if the extracted [`TransactionEventId`] resulted in an event that needs to be + /// handled, `None` otherwise. + pub fn extract<'store, STORE>( + base_host: &TransactionBaseHost<'store, STORE>, + process: &ProcessState, + ) -> Result, TransactionKernelError> { + let event_id = EventId::from_felt(process.get_stack_item(0)); + let tx_event_id = TransactionEventId::try_from(event_id).map_err(|err| { + TransactionKernelError::other_with_source( + "failed to convert event ID into transaction event ID", + err, + ) + })?; + + let tx_event = match tx_event_id { + TransactionEventId::AccountBeforeForeignLoad => { + // Expected stack state: [event, account_id_prefix, account_id_suffix] + let account_id_word = process.get_stack_word_be(1); + let account_id = AccountId::try_from([account_id_word[3], account_id_word[2]]) + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to convert account ID word into account ID", + err, + ) + })?; + + Some(TransactionEvent::AccountBeforeForeignLoad { foreign_account_id: account_id }) + }, + TransactionEventId::AccountVaultBeforeAddAsset + | TransactionEventId::AccountVaultBeforeRemoveAsset => { + // Expected stack state: [event, ASSET, account_vault_root_ptr] + let asset_word = process.get_stack_word_be(1); + let asset = Asset::try_from(asset_word).map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "on_account_vault_before_add_or_remove_asset", + source, + } + })?; + + let vault_root_ptr = process.get_stack_item(5); + let current_vault_root = process.get_vault_root(vault_root_ptr)?; + + on_account_vault_asset_accessed( + base_host, + process, + asset.vault_key(), + current_vault_root, + )? + }, + TransactionEventId::AccountVaultAfterRemoveAsset => { + // Expected stack state: [event, ASSET] + let asset: Asset = process.get_stack_word_be(1).try_into().map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "on_account_vault_after_remove_asset", + source, + } + })?; + + Some(TransactionEvent::AccountVaultAfterRemoveAsset { asset }) + }, + TransactionEventId::AccountVaultAfterAddAsset => { + // Expected stack state: [event, ASSET] + let asset: Asset = process.get_stack_word_be(1).try_into().map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "on_account_vault_after_add_asset", + source, + } + })?; + + Some(TransactionEvent::AccountVaultAfterAddAsset { asset }) + }, + TransactionEventId::AccountVaultBeforeGetBalance => { + // Expected stack state: + // [event, faucet_id_prefix, faucet_id_suffix, vault_root_ptr] + let stack_top = process.get_stack_word_be(1); + let faucet_id = + AccountId::try_from([stack_top[3], stack_top[2]]).map_err(|err| { + TransactionKernelError::other_with_source( + "failed to convert faucet ID word into faucet ID", + err, + ) + })?; + let vault_root_ptr = stack_top[1]; + let vault_root = process.get_vault_root(vault_root_ptr)?; + + let vault_key = AssetVaultKey::from_account_id(faucet_id).ok_or_else(|| { + TransactionKernelError::other(format!( + "provided faucet ID {faucet_id} is not valid for fungible assets" + )) + })?; + + on_account_vault_asset_accessed(base_host, process, vault_key, vault_root)? + }, + TransactionEventId::AccountVaultBeforeHasNonFungibleAsset => { + // Expected stack state: [event, ASSET, vault_root_ptr] + let asset_word = process.get_stack_word_be(1); + let asset = Asset::try_from(asset_word).map_err(|err| { + TransactionKernelError::other_with_source( + "provided asset is not a valid asset", + err, + ) + })?; + + let vault_root_ptr = process.get_stack_item(5); + let vault_root = process.get_vault_root(vault_root_ptr)?; + + on_account_vault_asset_accessed(base_host, process, asset.vault_key(), vault_root)? + }, + + TransactionEventId::AccountStorageBeforeSetItem => None, + + TransactionEventId::AccountStorageAfterSetItem => { + // Expected stack state: + // [event, slot_index, NEW_SLOT_VALUE] + // get slot index from the stack and make sure it is valid + let slot_index = process.get_stack_item(1); + let slot_index = u8::try_from(slot_index).map_err(|err| { + TransactionKernelError::other(format!( + "failed to convert slot index into u8: {err}" + )) + })?; + + // get number of storage slots initialized by the account + let num_storage_slot = process.get_num_storage_slots()?; + if slot_index as u64 >= num_storage_slot { + return Err(TransactionKernelError::InvalidStorageSlotIndex { + max: num_storage_slot, + actual: slot_index as u64, + }); + } + + // get the value to which the slot is being updated + let new_slot_value = process.get_stack_word_be(2); + + Some(TransactionEvent::AccountStorageAfterSetItem { + slot_idx: slot_index, + new_value: new_slot_value, + }) + }, + + TransactionEventId::AccountStorageBeforeGetMapItem => { + // Expected stack state: [event, KEY, ROOT, index] + let map_key = process.get_stack_word_be(1); + let current_map_root = process.get_stack_word_be(5); + let slot_index = process.get_stack_item(9); + + on_account_storage_map_item_accessed( + base_host, + process, + slot_index, + current_map_root, + map_key, + )? + }, + + TransactionEventId::AccountStorageBeforeSetMapItem => { + // Expected stack state: [event, index, KEY, NEW_VALUE, OLD_ROOT] + let slot_index = process.get_stack_item(1); + let map_key = process.get_stack_word_be(2); + let current_map_root = process.get_stack_word_be(10); + + on_account_storage_map_item_accessed( + base_host, + process, + slot_index, + current_map_root, + map_key, + )? + }, + + TransactionEventId::AccountStorageAfterSetMapItem => { + // Expected stack state: [event, slot_index, KEY, PREV_MAP_VALUE, NEW_MAP_VALUE] + // get slot index from the stack and make sure it is valid + let slot_index = process.get_stack_item(1); + let slot_index = u8::try_from(slot_index).map_err(|err| { + TransactionKernelError::other(format!( + "failed to convert slot index into u8: {err}" + )) + })?; + + // get number of storage slots initialized by the account + let num_storage_slot = process.get_num_storage_slots()?; + if slot_index as u64 >= num_storage_slot { + return Err(TransactionKernelError::InvalidStorageSlotIndex { + max: num_storage_slot, + actual: slot_index as u64, + }); + } + + // get the KEY to which the slot is being updated + let key = process.get_stack_word_be(2); + + // get the previous VALUE of the slot + let prev_map_value = process.get_stack_word_be(6); + + // get the VALUE to which the slot is being updated + let new_map_value = process.get_stack_word_be(10); + + Some(TransactionEvent::AccountStorageAfterSetMapItem { + slot_index, + key, + prev_map_value, + new_map_value, + }) + }, + + TransactionEventId::AccountBeforeIncrementNonce => None, + + TransactionEventId::AccountAfterIncrementNonce => { + Some(TransactionEvent::AccountAfterIncrementNonce) + }, + + TransactionEventId::AccountPushProcedureIndex => { + // Expected stack state: [event, PROC_ROOT] + let procedure_root = process.get_stack_word_be(1); + let code_commitment = process.get_active_account_code_commitment()?; + + Some(TransactionEvent::AccountPushProcedureIndex { + code_commitment, + procedure_root, + }) + }, + + TransactionEventId::NoteBeforeCreated => None, + + TransactionEventId::NoteAfterCreated => { + // Expected stack state: [event, NOTE_METADATA, note_ptr, RECIPIENT, note_idx] + let metadata_word = process.get_stack_word_be(1); + let metadata = NoteMetadata::try_from(metadata_word) + .map_err(TransactionKernelError::MalformedNoteMetadata)?; + + let recipient_digest = process.get_stack_word_be(6); + let note_idx = process.get_stack_item(10).as_int() as usize; + + // try to read the full recipient from the advice provider + let recipient_data = if process.has_advice_map_entry(recipient_digest) { + let (note_inputs, script_root, serial_num) = + process.read_note_recipient_info_from_adv_map(recipient_digest)?; + + let note_script = process + .advice_provider() + .get_mapped_values(&script_root) + .map(|script_data| { + NoteScript::try_from(script_data).map_err(|source| { + TransactionKernelError::MalformedNoteScript { + data: script_data.to_vec(), + source, + } + }) + }) + .transpose()?; + + match note_script { + Some(note_script) => { + let recipient = + NoteRecipient::new(serial_num, note_script, note_inputs); + + if recipient.digest() != recipient_digest { + return Err(TransactionKernelError::other(format!( + "recipient digest is {recipient_digest}, but recipient constructed from raw inputs has digest {}", + recipient.digest() + ))); + } + + RecipientData::Recipient(recipient) + }, + None => RecipientData::ScriptMissing { + recipient_digest, + serial_num, + script_root, + note_inputs, + }, + } + } else { + RecipientData::Digest(recipient_digest) + }; + + Some(TransactionEvent::NoteAfterCreated { note_idx, metadata, recipient_data }) + }, + + TransactionEventId::NoteBeforeAddAsset => { + // Expected stack state: [event, ASSET, note_ptr, num_of_assets, note_idx] + let note_idx = process.get_stack_item(7).as_int() as usize; + + let asset_word = process.get_stack_word_be(1); + let asset = Asset::try_from(asset_word).map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "on_note_before_add_asset", + source, + } + })?; + + Some(TransactionEvent::NoteBeforeAddAsset { note_idx, asset }) + }, + + TransactionEventId::NoteAfterAddAsset => None, + + TransactionEventId::AuthRequest => { + // Expected stack state: [event, MESSAGE, PUB_KEY] + let message = process.get_stack_word_be(1); + let pub_key_hash = process.get_stack_word_be(5); + let signature_key = Hasher::merge(&[pub_key_hash, message]); + + let signature = process + .advice_provider() + .get_mapped_values(&signature_key) + .map(|slice| slice.to_vec()); + + let tx_summary = extract_tx_summary(base_host, process, message)?; + + Some(TransactionEvent::AuthRequest { pub_key_hash, tx_summary, signature }) + }, + + TransactionEventId::Unauthorized => { + // Expected stack state: [event, MESSAGE] + let message = process.get_stack_word_be(1); + let tx_summary = extract_tx_summary(base_host, process, message)?; + + Some(TransactionEvent::Unauthorized { tx_summary }) + }, + + TransactionEventId::EpilogueBeforeTxFeeRemovedFromAccount => { + // Expected stack state: [event, FEE_ASSET] + let fee_asset = process.get_stack_word_be(1); + let fee_asset = FungibleAsset::try_from(fee_asset) + .map_err(TransactionKernelError::FailedToConvertFeeAsset)?; + + Some(TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount { fee_asset }) + }, + + TransactionEventId::LinkMapSet => Some(TransactionEvent::LinkMapSet { + advice_mutation: LinkMap::handle_set_event(process), + }), + TransactionEventId::LinkMapGet => Some(TransactionEvent::LinkMapGet { + advice_mutation: LinkMap::handle_get_event(process), + }), + + TransactionEventId::PrologueStart => { + Some(TransactionEvent::PrologueStart { clk: process.clk() }) + }, + TransactionEventId::PrologueEnd => { + Some(TransactionEvent::PrologueEnd { clk: process.clk() }) + }, + + TransactionEventId::NotesProcessingStart => { + Some(TransactionEvent::NotesProcessingStart { clk: process.clk() }) + }, + TransactionEventId::NotesProcessingEnd => { + Some(TransactionEvent::NotesProcessingEnd { clk: process.clk() }) + }, + + TransactionEventId::NoteExecutionStart => { + let note_id = process.get_active_note_id()?.ok_or_else(|| TransactionKernelError::other( + "note execution interval measurement is incorrect: check the placement of the start and the end of the interval", + ))?; + + Some(TransactionEvent::NoteExecutionStart { note_id, clk: process.clk() }) + }, + TransactionEventId::NoteExecutionEnd => { + Some(TransactionEvent::NoteExecutionEnd { clk: process.clk() }) + }, + + TransactionEventId::TxScriptProcessingStart => { + Some(TransactionEvent::TxScriptProcessingStart { clk: process.clk() }) + }, + TransactionEventId::TxScriptProcessingEnd => { + Some(TransactionEvent::TxScriptProcessingEnd { clk: process.clk() }) + }, + + TransactionEventId::EpilogueStart => { + Some(TransactionEvent::EpilogueStart { clk: process.clk() }) + }, + TransactionEventId::EpilogueEnd => { + Some(TransactionEvent::EpilogueEnd { clk: process.clk() }) + }, + + TransactionEventId::EpilogueAuthProcStart => { + Some(TransactionEvent::EpilogueAuthProcStart { clk: process.clk() }) + }, + TransactionEventId::EpilogueAuthProcEnd => { + Some(TransactionEvent::EpilogueAuthProcEnd { clk: process.clk() }) + }, + + TransactionEventId::EpilogueAfterTxCyclesObtained => { + Some(TransactionEvent::EpilogueAfterTxCyclesObtained { clk: process.clk() }) + }, + }; + + Ok(tx_event) + } +} + +// RECIPIENT DATA +// ================================================================================================ + +/// The partial data to construct a note recipient. +#[derive(Debug)] +pub(crate) enum RecipientData { + /// Only the recipient digest is available. + Digest(Word), + /// The full [`NoteRecipient`] is available. + Recipient(NoteRecipient), + /// Everything but the note script is available. + ScriptMissing { + recipient_digest: Word, + serial_num: Word, + script_root: Word, + note_inputs: NoteInputs, + }, +} + +/// Checks if the necessary witness for accessing the asset identified by the vault key is already +/// in the merkle store, and: +/// - If so, returns `None`. +/// - If not, returns `Some` with all necessary data for requesting it. +fn on_account_vault_asset_accessed<'store, STORE>( + base_host: &TransactionBaseHost<'store, STORE>, + process: &ProcessState, + vault_key: AssetVaultKey, + vault_root: Word, +) -> Result, TransactionKernelError> { + let leaf_index = Felt::new(vault_key.to_leaf_index().value()); + let active_account_id = process.get_active_account_id()?; + + // For the native account we need to explicitly request the initial vault root, while for + // foreign accounts the current vault root is always the initial one. + let vault_root = if active_account_id == base_host.native_account_id() { + base_host.initial_account_header().vault_root() + } else { + vault_root + }; + + // Note that we check whether a merkle path for the current vault root is present, not + // necessarily for the root we are going to request. This is because the end goal is to + // enable access to an asset against the current vault root, and so if this + // condition is already satisfied, there is nothing to request. + if process.has_merkle_path::<{ AssetVault::DEPTH }>(vault_root, leaf_index)? { + // If the witness already exists, the event does not need to be handled. + Ok(None) + } else { + Ok(Some(TransactionEvent::AccountVaultBeforeAssetAccess { + active_account_id, + vault_root, + asset_key: vault_key, + })) + } +} + +/// Checks if the necessary witness for accessing the map item identified by the map key is already +/// in the merkle store, and: +/// - If so, returns `None`. +/// - If not, returns `Some` with all necessary data for requesting it. +fn on_account_storage_map_item_accessed<'store, STORE>( + base_host: &TransactionBaseHost<'store, STORE>, + process: &ProcessState, + slot_index: Felt, + current_map_root: Word, + map_key: Word, +) -> Result, TransactionKernelError> { + let active_account_id = process.get_active_account_id()?; + let hashed_map_key = StorageMap::hash_key(map_key); + let leaf_index = StorageMap::hashed_map_key_to_leaf_index(hashed_map_key); + + let slot_index = u8::try_from(slot_index).map_err(|err| { + TransactionKernelError::other(format!("failed to convert slot index into u8: {err}")) + })?; + + // For the native account we need to explicitly request the initial map root, + // while for foreign accounts the current map root is always the initial one. + let map_root = if active_account_id == base_host.native_account_id() { + // For native accounts, we have to request witnesses against the initial + // root instead of the _current_ one, since the data + // store only has witnesses for initial one. + let (slot_type, slot_value) = base_host + .initial_account_storage_header() + // Slot index should always fit into a usize. + .slot(slot_index as usize) + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to access storage map in storage header", + err, + ) + })?; + if *slot_type != StorageSlotType::Map { + return Err(TransactionKernelError::other(format!( + "expected map slot type at slot index {slot_index}" + ))); + } + *slot_value + } else { + current_map_root + }; + + if process.has_merkle_path::<{ StorageMap::DEPTH }>(current_map_root, leaf_index)? { + // If the witness already exists, the event does not need to be handled. + Ok(None) + } else { + Ok(Some(TransactionEvent::AccountStorageBeforeMapItemAccess { + active_account_id, + map_root, + map_key, + })) + } +} + +/// Extracts the transaction summary from the advice map using the provided `message` as the +/// key. +/// +/// ```text +/// Expected advice map state: { +/// MESSAGE: [ +/// SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT +/// ] +/// } +/// ``` +fn extract_tx_summary<'store, STORE>( + base_host: &TransactionBaseHost<'store, STORE>, + process: &ProcessState, + message: Word, +) -> Result { + let Some(commitments) = process.advice_provider().get_mapped_values(&message) else { + return Err(TransactionKernelError::TransactionSummaryConstructionFailed( + "expected message to exist in advice provider".into(), + )); + }; + + if commitments.len() != 16 { + return Err(TransactionKernelError::TransactionSummaryConstructionFailed( + "expected 4 words for transaction summary commitments".into(), + )); + } + + let salt = extract_word(commitments, 0); + let output_notes_commitment = extract_word(commitments, 4); + let input_notes_commitment = extract_word(commitments, 8); + let account_delta_commitment = extract_word(commitments, 12); + + let tx_summary = base_host.build_tx_summary( + salt, + output_notes_commitment, + input_notes_commitment, + account_delta_commitment, + )?; + + if tx_summary.to_commitment() != message { + return Err(TransactionKernelError::TransactionSummaryConstructionFailed( + "transaction summary doesn't commit to the expected message".into(), + )); + } + + Ok(tx_summary) +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Extracts a word from a slice of field elements. +#[inline(always)] +fn extract_word(commitments: &[Felt], start: usize) -> Word { + Word::from([ + commitments[start], + commitments[start + 1], + commitments[start + 2], + commitments[start + 3], + ]) +} diff --git a/crates/miden-tx/src/prover/mod.rs b/crates/miden-tx/src/prover/mod.rs index 29c3d5f576..c20728257d 100644 --- a/crates/miden-tx/src/prover/mod.rs +++ b/crates/miden-tx/src/prover/mod.rs @@ -140,7 +140,7 @@ impl LocalTransactionProver { // Extract transaction outputs and process transaction data. // Note that the account delta does not contain the removed transaction fee, so it is the // "pre-fee" delta of the transaction. - let (pre_fee_account_delta, input_notes, output_notes, _tx_progress) = host.into_parts(); + let (pre_fee_account_delta, input_notes, output_notes) = host.into_parts(); let tx_outputs = TransactionKernel::from_transaction_parts(&stack_outputs, &advice_inputs, output_notes) .map_err(TransactionProverError::TransactionOutputConstructionFailed)?; diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index 6f948217eb..d254624d96 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -1,7 +1,6 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::EventId; use miden_objects::Word; use miden_objects::account::{AccountDelta, PartialAccount}; use miden_objects::assembly::debuginfo::Location; @@ -17,14 +16,8 @@ use miden_processor::{ SyncHost, }; -use crate::AccountProcedureIndexMap; -use crate::host::{ - ScriptMastForestStore, - TransactionBaseHost, - TransactionEventData, - TransactionEventHandling, - TransactionProgress, -}; +use crate::host::{RecipientData, ScriptMastForestStore, TransactionBaseHost, TransactionEvent}; +use crate::{AccountProcedureIndexMap, TransactionKernelError}; /// The transaction prover host is responsible for handling [`SyncHost`] requests made by the /// transaction kernel during proving. @@ -65,15 +58,8 @@ where // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns a reference to the `tx_progress` field of this transaction host. - pub fn tx_progress(&self) -> &TransactionProgress { - self.base_host.tx_progress() - } - - /// Consumes `self` and returns the account delta, output notes and transaction progress. - pub fn into_parts( - self, - ) -> (AccountDelta, InputNotes, Vec, TransactionProgress) { + /// Consumes `self` and returns the account delta, input and output notes. + pub fn into_parts(self) -> (AccountDelta, InputNotes, Vec) { self.base_host.into_parts() } } @@ -105,34 +91,117 @@ where } fn on_event(&mut self, process: &ProcessState) -> Result, EventError> { - let event_id = EventId::from_felt(process.get_stack_item(0)); - - match self.base_host.handle_event(process, event_id)? { - TransactionEventHandling::Unhandled(event_data) => { - // We match on the event_data here so that if a new - // variant is added to the enum, this fails compilation and we can adapt - // accordingly. - match event_data { - // The base host should have handled this event since the signature should be - // present in the advice map. - TransactionEventData::AuthRequest { .. } => { - Err(EventError::from("base host should have handled auth request event")) - }, - // Foreign account data and witnesses should be in the advice provider at - // proving time, so there is nothing to do. - TransactionEventData::ForeignAccount { .. } => Ok(Vec::new()), - TransactionEventData::AccountVaultAssetWitness { .. } => Ok(Vec::new()), - TransactionEventData::AccountStorageMapWitness { .. } => Ok(Vec::new()), - // Note scripts should be in the advice provider at proving time, so there is - // nothing to do. - TransactionEventData::NoteData { .. } => Ok(Vec::new()), - // We don't track enough information to handle this event. Since this just - // improves error messages for users and the error should not be relevant during - // proving, we ignore it. - TransactionEventData::TransactionFeeComputed { .. } => Ok(Vec::new()), + if let Some(advice_mutations) = self.base_host.handle_stdlib_events(process)? { + return Ok(advice_mutations); + } + + let tx_event = + TransactionEvent::extract(&self.base_host, process).map_err(EventError::from)?; + + // None means the event ID does not need to be handled. + let Some(tx_event) = tx_event else { + return Ok(Vec::new()); + }; + + let result = match tx_event { + // Foreign account data and witnesses should be in the advice provider at + // proving time, so there is nothing to do. + TransactionEvent::AccountBeforeForeignLoad { .. } => Ok(Vec::new()), + + TransactionEvent::AccountVaultAfterRemoveAsset { asset } => { + self.base_host.on_account_vault_after_remove_asset(asset) + }, + TransactionEvent::AccountVaultAfterAddAsset { asset } => { + self.base_host.on_account_vault_after_add_asset(asset) + }, + + TransactionEvent::AccountStorageAfterSetItem { slot_idx, new_value } => { + self.base_host.on_account_storage_after_set_item(slot_idx, new_value) + }, + + TransactionEvent::AccountStorageAfterSetMapItem { + slot_index, + key, + prev_map_value, + new_map_value, + } => self.base_host.on_account_storage_after_set_map_item( + slot_index, + key, + prev_map_value, + new_map_value, + ), + + // Access witnesses should be in the advice provider at proving time. + TransactionEvent::AccountVaultBeforeAssetAccess { .. } => Ok(Vec::new()), + TransactionEvent::AccountStorageBeforeMapItemAccess { .. } => Ok(Vec::new()), + + TransactionEvent::AccountAfterIncrementNonce => { + self.base_host.on_account_after_increment_nonce() + }, + + TransactionEvent::AccountPushProcedureIndex { code_commitment, procedure_root } => { + self.base_host.on_account_push_procedure_index(code_commitment, procedure_root) + }, + + TransactionEvent::NoteAfterCreated { note_idx, metadata, recipient_data } => { + match recipient_data { + RecipientData::Digest(recipient_digest) => self + .base_host + .output_note_from_recipient_digest(note_idx, metadata, recipient_digest), + RecipientData::Recipient(note_recipient) => self + .base_host + .output_note_from_recipient(note_idx, metadata, note_recipient), + RecipientData::ScriptMissing { .. } => Err(TransactionKernelError::other( + "note script should be in the advice provider at proving time", + )), } }, - TransactionEventHandling::Handled(mutations) => Ok(mutations), - } + + TransactionEvent::NoteBeforeAddAsset { note_idx, asset } => { + self.base_host.on_note_before_add_asset(note_idx, asset).map(|_| Vec::new()) + }, + + TransactionEvent::AuthRequest { signature, .. } => { + if let Some(signature) = signature { + Ok(self.base_host.on_auth_requested(signature)) + } else { + Err(TransactionKernelError::other( + "signatures should be in the advice provider at proving time", + )) + } + }, + + TransactionEvent::Unauthorized { tx_summary } => { + Err(TransactionKernelError::other(format!( + "unexpected unauthorized event during proving with tx summary commitment {}", + tx_summary.to_commitment() + ))) + }, + + // We don't track enough information to handle this event. Since this just improves + // error messages for users and the error should not be relevant during proving, we + // ignore it. + TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount { .. } => Ok(Vec::new()), + + TransactionEvent::LinkMapSet { advice_mutation } => Ok(advice_mutation), + TransactionEvent::LinkMapGet { advice_mutation } => Ok(advice_mutation), + + // We do not track tx progress during proving. + TransactionEvent::PrologueStart { .. } + | TransactionEvent::PrologueEnd { .. } + | TransactionEvent::NotesProcessingStart { .. } + | TransactionEvent::NotesProcessingEnd { .. } + | TransactionEvent::NoteExecutionStart { .. } + | TransactionEvent::NoteExecutionEnd { .. } + | TransactionEvent::TxScriptProcessingStart { .. } + | TransactionEvent::TxScriptProcessingEnd { .. } + | TransactionEvent::EpilogueStart { .. } + | TransactionEvent::EpilogueEnd { .. } + | TransactionEvent::EpilogueAuthProcStart { .. } + | TransactionEvent::EpilogueAuthProcEnd { .. } + | TransactionEvent::EpilogueAfterTxCyclesObtained { .. } => Ok(Vec::new()), + }; + + result.map_err(EventError::from) } } From faaac2ac4e0f028a0cf8abfb75849a273be0654b Mon Sep 17 00:00:00 2001 From: juan518munoz <62400508+juan518munoz@users.noreply.github.com> Date: Sat, 15 Nov 2025 01:13:11 -0300 Subject: [PATCH 011/114] feat: expose error (#2093) --- crates/miden-objects/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/miden-objects/src/lib.rs b/crates/miden-objects/src/lib.rs index 34f9219efb..9da7702fd4 100644 --- a/crates/miden-objects/src/lib.rs +++ b/crates/miden-objects/src/lib.rs @@ -38,6 +38,7 @@ pub use errors::{ NetworkIdError, NoteError, NullifierTreeError, + PartialAssetVaultError, PartialBlockchainError, ProposedBatchError, ProposedBlockError, From 8136935f7459616a2ef4680904572a5d1377c3ef Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Mon, 24 Nov 2025 05:42:42 +1300 Subject: [PATCH 012/114] feat: Refactor block types for signatures and deferred proving (#2012) --- CHANGELOG.md | 1 + Cargo.lock | 1 - crates/miden-block-prover/Cargo.toml | 1 - crates/miden-block-prover/src/errors.rs | 38 +-- crates/miden-block-prover/src/lib.rs | 6 +- .../src/local_block_prover.rs | 314 ++---------------- crates/miden-lib/src/block/mod.rs | 101 ++++++ crates/miden-lib/src/lib.rs | 1 + .../src/batch/ordered_batches.rs | 2 +- crates/miden-objects/src/block/block_body.rs | 202 +++++++++++ crates/miden-objects/src/block/block_proof.rs | 33 ++ crates/miden-objects/src/block/header.rs | 6 +- crates/miden-objects/src/block/mod.rs | 6 + .../miden-objects/src/block/proposed_block.rs | 167 ++++++++-- .../miden-objects/src/block/proven_block.rs | 171 ++-------- crates/miden-objects/src/errors.rs | 25 ++ .../src/transaction/partial_blockchain.rs | 20 +- .../src/kernel_tests/batch/proposed_batch.rs | 18 +- ...proven_block_error.rs => header_errors.rs} | 48 +-- .../src/kernel_tests/block/mod.rs | 2 +- .../block/proposed_block_errors.rs | 2 +- .../block/proven_block_success.rs | 54 ++- .../src/kernel_tests/tx/test_prologue.rs | 2 +- crates/miden-testing/src/mock_chain/chain.rs | 40 ++- .../src/mock_chain/chain_builder.rs | 8 +- 25 files changed, 681 insertions(+), 588 deletions(-) create mode 100644 crates/miden-lib/src/block/mod.rs create mode 100644 crates/miden-objects/src/block/block_body.rs create mode 100644 crates/miden-objects/src/block/block_proof.rs rename crates/miden-testing/src/kernel_tests/block/{proven_block_error.rs => header_errors.rs} (89%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c933f3598..6557208288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Changes +- [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). ## 0.12.3 (2025-11-15) diff --git a/Cargo.lock b/Cargo.lock index c9f2320f22..20d5acd3d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1447,7 +1447,6 @@ dependencies = [ name = "miden-block-prover" version = "0.13.0" dependencies = [ - "miden-lib", "miden-objects", "thiserror", ] diff --git a/crates/miden-block-prover/Cargo.toml b/crates/miden-block-prover/Cargo.toml index d774870d03..ed9bb534ff 100644 --- a/crates/miden-block-prover/Cargo.toml +++ b/crates/miden-block-prover/Cargo.toml @@ -19,6 +19,5 @@ bench = false testing = [] [dependencies] -miden-lib = { workspace = true } miden-objects = { workspace = true } thiserror = { workspace = true } diff --git a/crates/miden-block-prover/src/errors.rs b/crates/miden-block-prover/src/errors.rs index b1c99cac18..4ae35efc30 100644 --- a/crates/miden-block-prover/src/errors.rs +++ b/crates/miden-block-prover/src/errors.rs @@ -1,30 +1,8 @@ -use miden_objects::{AccountTreeError, NullifierTreeError, Word}; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum ProvenBlockError { - #[error("nullifier witness has a different root than the current nullifier tree root")] - NullifierWitnessRootMismatch(#[source] NullifierTreeError), - - #[error("failed to track account witness")] - AccountWitnessTracking { source: AccountTreeError }, - - #[error("account ID prefix already exists in the tree")] - AccountIdPrefixDuplicate { source: AccountTreeError }, - - #[error( - "account tree root of the previous block header is {prev_block_account_root} but the root of the partial tree computed from account witnesses is {stale_account_root}, indicating that the witnesses are stale" - )] - StaleAccountTreeRoot { - prev_block_account_root: Word, - stale_account_root: Word, - }, - - #[error( - "nullifier tree root of the previous block header is {prev_block_nullifier_root} but the root of the partial tree computed from nullifier witnesses is {stale_nullifier_root}, indicating that the witnesses are stale" - )] - StaleNullifierTreeRoot { - prev_block_nullifier_root: Word, - stale_nullifier_root: Word, - }, -} +// BLOCK PROVER ERROR +// ================================================================================================ + +/// Represents errors that can occur during block proving. +/// +/// NOTE: Block proving is not yet implemented. This is a placeholder enum. +#[derive(Debug, thiserror::Error)] +pub enum BlockProverError {} diff --git a/crates/miden-block-prover/src/lib.rs b/crates/miden-block-prover/src/lib.rs index 5497535f0c..a4e18d92c2 100644 --- a/crates/miden-block-prover/src/lib.rs +++ b/crates/miden-block-prover/src/lib.rs @@ -1,5 +1,5 @@ -mod errors; -pub use errors::ProvenBlockError; - mod local_block_prover; pub use local_block_prover::LocalBlockProver; + +mod errors; +pub use errors::BlockProverError; diff --git a/crates/miden-block-prover/src/local_block_prover.rs b/crates/miden-block-prover/src/local_block_prover.rs index 5637009e29..6de931fa2d 100644 --- a/crates/miden-block-prover/src/local_block_prover.rs +++ b/crates/miden-block-prover/src/local_block_prover.rs @@ -1,32 +1,12 @@ -use std::collections::BTreeMap; -use std::vec::Vec; +use miden_objects::batch::OrderedBatches; +use miden_objects::block::{BlockHeader, BlockInputs, BlockProof}; -use miden_lib::transaction::TransactionKernel; -use miden_objects::Word; -use miden_objects::account::AccountId; -use miden_objects::block::{ - AccountUpdateWitness, - BlockAccountUpdate, - BlockHeader, - BlockNoteIndex, - BlockNoteTree, - BlockNumber, - NullifierWitness, - OutputNoteBatch, - PartialAccountTree, - PartialNullifierTree, - ProposedBlock, - ProvenBlock, -}; -use miden_objects::note::Nullifier; -use miden_objects::transaction::PartialBlockchain; - -use crate::errors::ProvenBlockError; +use crate::BlockProverError; // LOCAL BLOCK PROVER // ================================================================================================ -/// A local prover for blocks, proving a [`ProposedBlock`] and returning a [`ProvenBlock`]. +/// A local prover for blocks in the chain. #[derive(Clone)] pub struct LocalBlockProver {} @@ -38,283 +18,29 @@ impl LocalBlockProver { Self {} } - /// Proves the provided [`ProposedBlock`] into a [`ProvenBlock`]. - /// - /// For now this does not actually verify the batches or create a block proof, but will be added - /// in the future. + /// Generates a proof of a block in the chain based on the given header and inputs. /// - /// # Errors - /// - /// Returns an error if: - /// - the account witnesses provided in the proposed block result in a different account tree - /// root than the contained previous block header commits to. - /// - the nullifier witnesses provided in the proposed block result in a different nullifier - /// tree root than the contained previous block header commits to. - /// - the account tree root in the previous block header does not match the root of the tree - /// computed from the account witnesses. - /// - the nullifier tree root in the previous block header does not match the root of the tree - /// computed from the nullifier witnesses. - pub fn prove(&self, proposed_block: ProposedBlock) -> Result { - self.prove_without_batch_verification_inner(proposed_block) + /// NOTE: Block proving is not yet implemented. This is a placeholder struct. + pub fn prove( + &self, + _tx_batches: OrderedBatches, + _block_header: BlockHeader, + _block_inputs: BlockInputs, + ) -> Result { + Ok(BlockProof {}) } - /// Proves the provided [`ProposedBlock`] into a [`ProvenBlock`], **without verifying batches - /// and proving the block**. + /// A mock implementation of the execution of a proof of a block in the chain based on the given + /// header and inputs. /// /// This is exposed for testing purposes. #[cfg(any(feature = "testing", test))] pub fn prove_dummy( &self, - proposed_block: ProposedBlock, - ) -> Result { - self.prove_without_batch_verification_inner(proposed_block) + _tx_batches: OrderedBatches, + _block_header: BlockHeader, + _block_inputs: BlockInputs, + ) -> Result { + Ok(BlockProof {}) } - - /// Proves the provided [`ProposedBlock`] into a [`ProvenBlock`]. - /// - /// The assumptions of this method are that the checks made by construction of a - /// [`ProposedBlock`] are enforced. - /// - /// See [`Self::prove`] for more details. - fn prove_without_batch_verification_inner( - &self, - proposed_block: ProposedBlock, - ) -> Result { - // Get the block number and timestamp of the new block and compute the tx commitment. - // -------------------------------------------------------------------------------------------- - - let block_num = proposed_block.block_num(); - let timestamp = proposed_block.timestamp(); - - // Split the proposed block into its parts. - // -------------------------------------------------------------------------------------------- - - let ( - batches, - account_updated_witnesses, - output_note_batches, - created_nullifiers, - partial_blockchain, - prev_block_header, - ) = proposed_block.into_parts(); - - let prev_block_commitment = prev_block_header.commitment(); - // For now we copy the parameters of the previous header, which means the parameters set on - // the genesis block will be passed through. Eventually, the contained base fees will be - // updated based on the demand in the currently proposed block. - let fee_parameters = prev_block_header.fee_parameters().clone(); - - // Compute the root of the block note tree. - // -------------------------------------------------------------------------------------------- - - let note_tree = compute_block_note_tree(&output_note_batches); - let note_root = note_tree.root(); - - // Insert the created nullifiers into the nullifier tree to compute its new root. - // -------------------------------------------------------------------------------------------- - - let (created_nullifiers, new_nullifier_root) = - compute_nullifiers(created_nullifiers, &prev_block_header, block_num)?; - - // Insert the state commitments of updated accounts into the account tree to compute its new - // root. - // -------------------------------------------------------------------------------------------- - - let new_account_root = - compute_account_root(&account_updated_witnesses, &prev_block_header)?; - - // Insert the previous block header into the block partial blockchain to get the new chain - // commitment. - // -------------------------------------------------------------------------------------------- - - let new_chain_commitment = compute_chain_commitment(partial_blockchain, prev_block_header); - - // Transform the account update witnesses into block account updates. - // -------------------------------------------------------------------------------------------- - - let updated_accounts = account_updated_witnesses - .into_iter() - .map(|(account_id, update_witness)| { - let ( - _initial_state_commitment, - final_state_commitment, - // Note that compute_account_root took out this value so it should not be used. - _initial_state_proof, - details, - ) = update_witness.into_parts(); - BlockAccountUpdate::new(account_id, final_state_commitment, details) - }) - .collect(); - - // Aggregate the verified transactions of all batches. - // -------------------------------------------------------------------------------------------- - - let txs = batches.into_transactions(); - let tx_commitment = txs.commitment(); - - // Construct the new block header. - // -------------------------------------------------------------------------------------------- - - // Currently undefined and reserved for future use. - // See miden-base/1155. - let version = 0; - let tx_kernel_commitment = TransactionKernel.to_commitment(); - - // For now, we're not actually proving the block. - let proof_commitment = Word::empty(); - - let header = BlockHeader::new( - version, - prev_block_commitment, - block_num, - new_chain_commitment, - new_account_root, - new_nullifier_root, - note_root, - tx_commitment, - tx_kernel_commitment, - proof_commitment, - fee_parameters, - timestamp, - ); - - // Construct the new proven block. - // -------------------------------------------------------------------------------------------- - - let proven_block = ProvenBlock::new_unchecked( - header, - updated_accounts, - output_note_batches, - created_nullifiers, - txs, - ); - - Ok(proven_block) - } -} - -/// Computes the new nullifier root by inserting the nullifier witnesses into a partial nullifier -/// tree and marking each nullifier as spent in the given block number. Returns the list of -/// nullifiers and the new nullifier tree root. -fn compute_nullifiers( - created_nullifiers: BTreeMap, - prev_block_header: &BlockHeader, - block_num: BlockNumber, -) -> Result<(Vec, Word), ProvenBlockError> { - // If no nullifiers were created, the nullifier tree root is unchanged. - if created_nullifiers.is_empty() { - return Ok((Vec::new(), prev_block_header.nullifier_root())); - } - - let nullifiers: Vec = created_nullifiers.keys().copied().collect(); - - // First, reconstruct the current nullifier tree with the merkle paths of the nullifiers we want - // to update. - // Due to the guarantees of ProposedBlock we can safely assume that each nullifier is mapped to - // its corresponding nullifier witness, so we don't have to check again whether they match. - let mut partial_nullifier_tree = - PartialNullifierTree::with_witnesses(created_nullifiers.into_values()) - .map_err(ProvenBlockError::NullifierWitnessRootMismatch)?; - - // Check the nullifier tree root in the previous block header matches the reconstructed tree's - // root. - if prev_block_header.nullifier_root() != partial_nullifier_tree.root() { - return Err(ProvenBlockError::StaleNullifierTreeRoot { - prev_block_nullifier_root: prev_block_header.nullifier_root(), - stale_nullifier_root: partial_nullifier_tree.root(), - }); - } - - // Second, mark each nullifier as spent in the tree. Note that checking whether each nullifier - // is unspent is checked as part of the proposed block. - - // SAFETY: As mentioned above, we can safely assume that each nullifier's witness was - // added and every nullifier should be tracked by the partial tree and - // therefore updatable. - partial_nullifier_tree.mark_spent(nullifiers.iter().copied(), block_num).expect( - "nullifiers' merkle path should have been added to the partial tree and the nullifiers should be unspent", - ); - - Ok((nullifiers, partial_nullifier_tree.root())) -} - -/// Adds the commitment of the previous block header to the partial blockchain to compute the new -/// chain commitment. -fn compute_chain_commitment( - mut partial_blockchain: PartialBlockchain, - prev_block_header: BlockHeader, -) -> Word { - // SAFETY: This does not panic as long as the block header we're adding is the next one in the - // chain which is validated as part of constructing a `ProposedBlock`. - partial_blockchain.add_block(prev_block_header, true); - partial_blockchain.peaks().hash_peaks() -} - -/// Computes the new account tree root after the given updates. -/// -/// It uses a PartialMerkleTree for now while we use a SimpleSmt for the account tree. Once that is -/// updated to an Smt, we can use a PartialSmt instead. -fn compute_account_root( - updated_accounts: &[(AccountId, AccountUpdateWitness)], - prev_block_header: &BlockHeader, -) -> Result { - // If no accounts were updated, the account tree root is unchanged. - if updated_accounts.is_empty() { - return Ok(prev_block_header.account_root()); - } - - // First reconstruct the current account tree from the provided merkle paths. - // If a witness points to a leaf where multiple account IDs share the same prefix, this will - // return an error. - let mut partial_account_tree = PartialAccountTree::with_witnesses( - updated_accounts.iter().map(|(_, update_witness)| update_witness.to_witness()), - ) - .map_err(|source| ProvenBlockError::AccountWitnessTracking { source })?; - - // Check the account tree root in the previous block header matches the reconstructed tree's - // root. - if prev_block_header.account_root() != partial_account_tree.root() { - return Err(ProvenBlockError::StaleAccountTreeRoot { - prev_block_account_root: prev_block_header.account_root(), - stale_account_root: partial_account_tree.root(), - }); - } - - // Second, update the account tree by inserting the new final account state commitments to - // compute the new root of the account tree. - // If an account ID's prefix already exists in the tree, this will return an error. - // Note that we have inserted all witnesses that we want to update into the partial account - // tree, so we should not run into the untracked key error. - partial_account_tree - .upsert_state_commitments(updated_accounts.iter().map(|(account_id, update_witness)| { - (*account_id, update_witness.final_state_commitment()) - })) - .map_err(|source| ProvenBlockError::AccountIdPrefixDuplicate { source })?; - - Ok(partial_account_tree.root()) -} - -/// Compute the block note tree from the output note batches. -fn compute_block_note_tree(output_note_batches: &[OutputNoteBatch]) -> BlockNoteTree { - let output_notes_iter = - output_note_batches.iter().enumerate().flat_map(|(batch_idx, notes)| { - notes.iter().map(move |(note_idx_in_batch, note)| { - ( - // SAFETY: The proposed block contains at most the max allowed number of - // batches and each batch is guaranteed to contain at most - // the max allowed number of output notes. - BlockNoteIndex::new(batch_idx, *note_idx_in_batch) - .expect("max batches in block and max notes in batches should be enforced"), - note.id(), - *note.metadata(), - ) - }) - }); - - // SAFETY: We only construct proposed blocks that: - // - do not contain duplicates - // - contain at most the max allowed number of batches and each batch is guaranteed to contain - // at most the max allowed number of output notes. - BlockNoteTree::with_entries(output_notes_iter) - .expect("the output notes of the block should not contain duplicates and contain at most the allowed maximum") } diff --git a/crates/miden-lib/src/block/mod.rs b/crates/miden-lib/src/block/mod.rs new file mode 100644 index 0000000000..ffdb6f4bc3 --- /dev/null +++ b/crates/miden-lib/src/block/mod.rs @@ -0,0 +1,101 @@ +use miden_core::Word; +use miden_objects::ProposedBlockError; +use miden_objects::block::{BlockBody, BlockHeader, BlockNumber, ProposedBlock}; + +use crate::transaction::TransactionKernel; + +/// Builds a [`BlockHeader`] and [`BlockBody`] by computing the following from the state updates +/// encapsulated by the provided [`ProposedBlock`]: +/// - the account root; +/// - the nullifier root; +/// - the note root; +/// - the transaction commitment; and +/// - the chain commitment. +/// +/// This functionality is handled here because the block header requires [`TransactionKernel`] for +/// its various commitment fields. +pub fn build_block( + proposed_block: ProposedBlock, +) -> Result<(BlockHeader, BlockBody), ProposedBlockError> { + // Get fields from the proposed block before it is consumed. + let block_num = proposed_block.block_num(); + let timestamp = proposed_block.timestamp(); + let prev_block_header = proposed_block.prev_block_header().clone(); + + // Insert the state commitments of updated accounts into the account tree to compute its new + // root. + let new_account_root = proposed_block.compute_account_root()?; + + // Insert the created nullifiers into the nullifier tree to compute its new root. + let new_nullifier_root = proposed_block.compute_nullifier_root()?; + + // Compute the root of the block note tree. + let note_tree = proposed_block.compute_block_note_tree(); + let note_root = note_tree.root(); + + // Insert the previous block header into the block partial blockchain to get the new chain + // commitment. + let new_chain_commitment = proposed_block.compute_chain_commitment(); + + // Construct the block body from the proposed block. + let body = BlockBody::from(proposed_block); + + // Construct the header. + let tx_commitment = body.transaction_commitment(); + let header = construct_block_header( + block_num, + timestamp, + prev_block_header, + tx_commitment, + new_chain_commitment, + new_account_root, + new_nullifier_root, + note_root, + ); + + Ok((header, body)) +} + +// HELPERS +// ================================================================================================ + +fn construct_block_header( + block_num: BlockNumber, + timestamp: u32, + prev_block_header: BlockHeader, + tx_commitment: Word, + new_chain_commitment: Word, + new_account_root: Word, + new_nullifier_root: Word, + note_root: Word, +) -> BlockHeader { + let prev_block_commitment = prev_block_header.commitment(); + + // For now we copy the parameters of the previous header, which means the parameters set on + // the genesis block will be passed through. Eventually, the contained base fees will be + // updated based on the demand in the currently proposed block. + let fee_parameters = prev_block_header.fee_parameters().clone(); + + // Currently undefined and reserved for future use. + // See miden-base/1155. + let version = 0; + let tx_kernel_commitment = TransactionKernel.to_commitment(); + + // TODO(serge): remove proof commitment when block header is updated to no longer have it. + let proof_commitment = Word::empty(); + + BlockHeader::new( + version, + prev_block_commitment, + block_num, + new_chain_commitment, + new_account_root, + new_nullifier_root, + note_root, + tx_commitment, + tx_kernel_commitment, + proof_commitment, + fee_parameters, + timestamp, + ) +} diff --git a/crates/miden-lib/src/lib.rs b/crates/miden-lib/src/lib.rs index 85e2797b9a..126df397a1 100644 --- a/crates/miden-lib/src/lib.rs +++ b/crates/miden-lib/src/lib.rs @@ -17,6 +17,7 @@ mod auth; pub use auth::AuthScheme; pub mod account; +pub mod block; pub mod errors; pub mod note; pub mod transaction; diff --git a/crates/miden-objects/src/batch/ordered_batches.rs b/crates/miden-objects/src/batch/ordered_batches.rs index f1d80593dc..00b04fcf9a 100644 --- a/crates/miden-objects/src/batch/ordered_batches.rs +++ b/crates/miden-objects/src/batch/ordered_batches.rs @@ -17,7 +17,7 @@ pub struct OrderedBatches(Vec); impl OrderedBatches { /// Creates a new set of ordered batches from the provided vector. - pub(crate) fn new(batches: Vec) -> Self { + pub fn new(batches: Vec) -> Self { Self(batches) } diff --git a/crates/miden-objects/src/block/block_body.rs b/crates/miden-objects/src/block/block_body.rs new file mode 100644 index 0000000000..8392a5ba7f --- /dev/null +++ b/crates/miden-objects/src/block/block_body.rs @@ -0,0 +1,202 @@ +use alloc::vec::Vec; + +use miden_core::Word; +use miden_core::utils::{ + ByteReader, + ByteWriter, + Deserializable, + DeserializationError, + Serializable, +}; + +use crate::block::{ + BlockAccountUpdate, + BlockNoteIndex, + BlockNoteTree, + OutputNoteBatch, + ProposedBlock, +}; +use crate::note::Nullifier; +use crate::transaction::{OrderedTransactionHeaders, OutputNote}; + +// BLOCK BODY +// ================================================================================================ + +/// Body of a block in the chain which contains data pertaining to all relevant state changes. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlockBody { + /// Account updates for the block. + updated_accounts: Vec, + + /// Note batches created by the transactions in this block. + output_note_batches: Vec, + + /// Nullifiers created by the transactions in this block through the consumption of notes. + created_nullifiers: Vec, + + /// The aggregated and flattened transaction headers of all batches in the order in which they + /// appeared in the proposed block. + transactions: OrderedTransactionHeaders, +} + +impl BlockBody { + // CONSTRUCTOR + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`BlockBody`] without performing any validation. + /// + /// # Warning + /// + /// This does not validate any of the guarantees of this type. It should only be used internally + /// (in miden-lib) or in tests. + pub fn new_unchecked( + updated_accounts: Vec, + output_note_batches: Vec, + created_nullifiers: Vec, + transactions: OrderedTransactionHeaders, + ) -> Self { + Self { + updated_accounts, + output_note_batches, + created_nullifiers, + transactions, + } + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the slice of [`BlockAccountUpdate`]s for all accounts updated in the block. + pub fn updated_accounts(&self) -> &[BlockAccountUpdate] { + &self.updated_accounts + } + + /// Returns the slice of [`OutputNoteBatch`]es for all output notes created in the block. + pub fn output_note_batches(&self) -> &[OutputNoteBatch] { + &self.output_note_batches + } + + /// Returns a reference to the slice of nullifiers for all notes consumed in the block. + pub fn created_nullifiers(&self) -> &[Nullifier] { + &self.created_nullifiers + } + + /// Returns the [`OrderedTransactionHeaders`] of all transactions included in this block. + pub fn transactions(&self) -> &OrderedTransactionHeaders { + &self.transactions + } + + /// Returns the commitment of all transactions included in this block. + pub fn transaction_commitment(&self) -> Word { + self.transactions.commitment() + } + + /// Returns an iterator over all [`OutputNote`]s created in this block. + /// + /// Each note is accompanied by a corresponding index specifying where the note is located + /// in the block's [`BlockNoteTree`]. + pub fn output_notes(&self) -> impl Iterator { + self.output_note_batches.iter().enumerate().flat_map(|(batch_idx, notes)| { + notes.iter().map(move |(note_idx_in_batch, note)| { + ( + // SAFETY: The block body contains at most the max allowed number of + // batches and each batch is guaranteed to contain + // at most the max allowed number of output notes. + BlockNoteIndex::new(batch_idx, *note_idx_in_batch) + .expect("max batches in block and max notes in batches should be enforced"), + note, + ) + }) + }) + } + + /// Computes the [`BlockNoteTree`] containing all [`OutputNote`]s created in this block. + pub fn compute_block_note_tree(&self) -> BlockNoteTree { + let entries = self + .output_notes() + .map(|(note_index, note)| (note_index, note.id(), *note.metadata())); + + // SAFETY: We only construct block bodies that: + // - do not contain duplicates + // - contain at most the max allowed number of batches and each batch is guaranteed to + // contain at most the max allowed number of output notes. + BlockNoteTree::with_entries(entries) + .expect("the output notes of the block should not contain duplicates and contain at most the allowed maximum") + } + + // DESTRUCTURING + // -------------------------------------------------------------------------------------------- + + /// Consumes the block body and returns its parts. + pub fn into_parts( + self, + ) -> ( + Vec, + Vec, + Vec, + OrderedTransactionHeaders, + ) { + ( + self.updated_accounts, + self.output_note_batches, + self.created_nullifiers, + self.transactions, + ) + } +} + +impl From for BlockBody { + fn from(block: ProposedBlock) -> Self { + // Split the proposed block into its constituent parts. + let (batches, account_updated_witnesses, output_note_batches, created_nullifiers, ..) = + block.into_parts(); + + // Transform the account update witnesses into block account updates. + let updated_accounts = account_updated_witnesses + .into_iter() + .map(|(account_id, update_witness)| { + let ( + _initial_state_commitment, + final_state_commitment, + // Note that compute_account_root took out this value so it should not be used. + _initial_state_proof, + details, + ) = update_witness.into_parts(); + BlockAccountUpdate::new(account_id, final_state_commitment, details) + }) + .collect(); + let created_nullifiers = created_nullifiers.keys().copied().collect::>(); + // Aggregate the verified transactions of all batches. + let transactions = batches.into_transactions(); + Self { + updated_accounts, + output_note_batches, + created_nullifiers, + transactions, + } + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for BlockBody { + fn write_into(&self, target: &mut W) { + self.updated_accounts.write_into(target); + self.output_note_batches.write_into(target); + self.created_nullifiers.write_into(target); + self.transactions.write_into(target); + } +} + +impl Deserializable for BlockBody { + fn read_from(source: &mut R) -> Result { + let block = Self { + updated_accounts: Vec::read_from(source)?, + output_note_batches: Vec::read_from(source)?, + created_nullifiers: Vec::read_from(source)?, + transactions: OrderedTransactionHeaders::read_from(source)?, + }; + Ok(block) + } +} diff --git a/crates/miden-objects/src/block/block_proof.rs b/crates/miden-objects/src/block/block_proof.rs new file mode 100644 index 0000000000..710a77cb03 --- /dev/null +++ b/crates/miden-objects/src/block/block_proof.rs @@ -0,0 +1,33 @@ +use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; + +/// Represents a proof of a block in the chain. +/// +/// NOTE: Block proving is not yet implemented. This is a placeholder struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlockProof {} + +impl BlockProof { + /// Creates a dummy `BlockProof` for testing purposes only. + #[cfg(any(test, feature = "testing"))] + pub fn new_dummy() -> Self { + Self {} + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for BlockProof { + fn write_into(&self, _target: &mut W) { + // TODO: Implement serialization for BlockProof when fields exist. + } +} + +impl Deserializable for BlockProof { + fn read_from(_source: &mut R) -> Result { + // TODO: Implement deserialization for BlockProof when fields exist. + let block = Self {}; + + Ok(block) + } +} diff --git a/crates/miden-objects/src/block/header.rs b/crates/miden-objects/src/block/header.rs index 9c8f28b15d..22e8f91f34 100644 --- a/crates/miden-objects/src/block/header.rs +++ b/crates/miden-objects/src/block/header.rs @@ -12,6 +12,9 @@ use crate::utils::serde::{ }; use crate::{FeeError, Felt, Hasher, Word, ZERO}; +// BLOCK HEADER +// ================================================================================================ + /// The header of a block. It contains metadata about the block, commitments to the current /// state of the chain and the hash of the proof that attests to the integrity of the chain. /// @@ -46,7 +49,7 @@ pub struct BlockHeader { note_root: Word, tx_commitment: Word, tx_kernel_commitment: Word, - proof_commitment: Word, + proof_commitment: Word, // TODO(serge): remove this field as proofs are constructed later. fee_parameters: FeeParameters, timestamp: u32, sub_commitment: Word, @@ -370,6 +373,7 @@ impl Deserializable for FeeParameters { .map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } + // TESTS // ================================================================================================ diff --git a/crates/miden-objects/src/block/mod.rs b/crates/miden-objects/src/block/mod.rs index 083551363f..0489bd2062 100644 --- a/crates/miden-objects/src/block/mod.rs +++ b/crates/miden-objects/src/block/mod.rs @@ -1,9 +1,15 @@ mod header; pub use header::{BlockHeader, FeeParameters}; +mod block_body; +pub use block_body::BlockBody; + mod block_number; pub use block_number::BlockNumber; +mod block_proof; +pub use block_proof::BlockProof; + mod proposed_block; pub use proposed_block::ProposedBlock; diff --git a/crates/miden-objects/src/block/proposed_block.rs b/crates/miden-objects/src/block/proposed_block.rs index 529a91dfa5..747247c848 100644 --- a/crates/miden-objects/src/block/proposed_block.rs +++ b/crates/miden-objects/src/block/proposed_block.rs @@ -16,9 +16,13 @@ use crate::block::{ AccountUpdateWitness, AccountWitness, BlockHeader, + BlockNoteIndex, + BlockNoteTree, BlockNumber, NullifierWitness, OutputNoteBatch, + PartialAccountTree, + PartialNullifierTree, }; use crate::errors::ProposedBlockError; use crate::note::{NoteId, Nullifier}; @@ -267,14 +271,6 @@ impl ProposedBlock { // ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns an iterator over all transactions in the block. - pub fn transactions(&self) -> impl Iterator { - self.batches - .as_slice() - .iter() - .flat_map(|batch| batch.transactions().as_slice().iter()) - } - /// Returns the block number of this proposed block. pub fn block_num(&self) -> BlockNumber { // The chain length is the length at the state of the previous block header, so we have to @@ -282,16 +278,6 @@ impl ProposedBlock { self.partial_blockchain().chain_length() + 1 } - /// Returns a reference to the slice of batches in this block. - pub fn batches(&self) -> &OrderedBatches { - &self.batches - } - - /// Returns the map of nullifiers to their proofs from the proposed block. - pub fn created_nullifiers(&self) -> &BTreeMap { - &self.created_nullifiers - } - /// Returns a reference to the previous block header that this block builds on top of. pub fn prev_block_header(&self) -> &BlockHeader { &self.prev_block_header @@ -302,19 +288,157 @@ impl ProposedBlock { &self.partial_blockchain } + /// Returns a reference to the slice of transaction batches in this block. + pub fn batches(&self) -> &OrderedBatches { + &self.batches + } + + /// Returns an iterator over all transactions in the block. + pub fn transactions(&self) -> impl Iterator { + self.batches + .as_slice() + .iter() + .flat_map(|batch| batch.transactions().as_slice().iter()) + } + + /// Returns the map of nullifiers to their proofs from the proposed block. + pub fn created_nullifiers(&self) -> &BTreeMap { + &self.created_nullifiers + } + /// Returns a reference to the slice of accounts updated in this block. pub fn updated_accounts(&self) -> &[(AccountId, AccountUpdateWitness)] { &self.account_updated_witnesses } + /// Returns a slice of the [`OutputNoteBatch`] of each batch in this block. + pub fn output_note_batches(&self) -> &[OutputNoteBatch] { + &self.output_note_batches + } + /// Returns the timestamp of this block. pub fn timestamp(&self) -> u32 { self.timestamp } - /// Returns a slice of the [`OutputNoteBatch`] of each batch in this block. - pub fn output_note_batches(&self) -> &[OutputNoteBatch] { - &self.output_note_batches + // COMMITMENT COMPUTATIONS + // -------------------------------------------------------------------------------------------- + + /// Computes the new account tree root after the given updates. + pub fn compute_account_root(&self) -> Result { + // If no accounts were updated, the account tree root is unchanged. + if self.account_updated_witnesses.is_empty() { + return Ok(self.prev_block_header.account_root()); + } + + // First reconstruct the current account tree from the provided merkle paths. + // If a witness points to a leaf where multiple account IDs share the same prefix, this will + // return an error. + let mut partial_account_tree = PartialAccountTree::with_witnesses( + self.account_updated_witnesses + .iter() + .map(|(_, update_witness)| update_witness.to_witness()), + ) + .map_err(|source| ProposedBlockError::AccountWitnessTracking { source })?; + + // Check the account tree root in the previous block header matches the reconstructed tree's + // root. + if self.prev_block_header.account_root() != partial_account_tree.root() { + return Err(ProposedBlockError::StaleAccountTreeRoot { + prev_block_account_root: self.prev_block_header.account_root(), + stale_account_root: partial_account_tree.root(), + }); + } + + // Second, update the account tree by inserting the new final account state commitments to + // compute the new root of the account tree. + // If an account ID's prefix already exists in the tree, this will return an error. + // Note that we have inserted all witnesses that we want to update into the partial account + // tree, so we should not run into the untracked key error. + partial_account_tree + .upsert_state_commitments(self.account_updated_witnesses.iter().map( + |(account_id, update_witness)| { + (*account_id, update_witness.final_state_commitment()) + }, + )) + .map_err(|source| ProposedBlockError::AccountIdPrefixDuplicate { source })?; + + Ok(partial_account_tree.root()) + } + + /// Computes the new nullifier root by inserting the nullifier witnesses into a partial + /// nullifier tree and marking each nullifier as spent in the given block number. + pub fn compute_nullifier_root(&self) -> Result { + // If no nullifiers were created, the nullifier tree root is unchanged. + if self.created_nullifiers.is_empty() { + return Ok(self.prev_block_header.nullifier_root()); + } + + // First, reconstruct the current nullifier tree with the merkle paths of the nullifiers we + // want to update. + // Due to the guarantees of ProposedBlock we can safely assume that each nullifier is mapped + // to its corresponding nullifier witness, so we don't have to check again whether + // they match. + let mut partial_nullifier_tree = + PartialNullifierTree::with_witnesses(self.created_nullifiers().values().cloned()) + .map_err(ProposedBlockError::NullifierWitnessRootMismatch)?; + + // Check the nullifier tree root in the previous block header matches the reconstructed + // tree's root. + if self.prev_block_header.nullifier_root() != partial_nullifier_tree.root() { + return Err(ProposedBlockError::StaleNullifierTreeRoot { + prev_block_nullifier_root: self.prev_block_header.nullifier_root(), + stale_nullifier_root: partial_nullifier_tree.root(), + }); + } + + // Second, mark each nullifier as spent in the tree. Note that checking whether each + // nullifier is unspent is checked as part of constructing the proposed block. + + // SAFETY: As mentioned above, we can safely assume that each nullifier's witness was + // added and every nullifier should be tracked by the partial tree and + // therefore updatable. + partial_nullifier_tree.mark_spent(self.created_nullifiers.keys().copied(), self.block_num()).expect( + "nullifiers' merkle path should have been added to the partial tree and the nullifiers should be unspent", + ); + + Ok(partial_nullifier_tree.root()) + } + + /// Compute the block note tree from the output note batches. + pub fn compute_block_note_tree(&self) -> BlockNoteTree { + let output_notes_iter = + self.output_note_batches.iter().enumerate().flat_map(|(batch_idx, notes)| { + notes.iter().map(move |(note_idx_in_batch, note)| { + ( + // SAFETY: The proposed block contains at most the max allowed number of + // batches and each batch is guaranteed to contain at most + // the max allowed number of output notes. + BlockNoteIndex::new(batch_idx, *note_idx_in_batch).expect( + "max batches in block and max notes in batches should be enforced", + ), + note.id(), + *note.metadata(), + ) + }) + }); + + // SAFETY: We only construct proposed blocks that: + // - do not contain duplicates + // - contain at most the max allowed number of batches and each batch is guaranteed to + // contain at most the max allowed number of output notes. + BlockNoteTree::with_entries(output_notes_iter) + .expect("the output notes of the block should not contain duplicates and contain at most the allowed maximum") + } + + /// Adds the commitment of the previous block header to the partial blockchain to compute the + /// new chain commitment. + pub fn compute_chain_commitment(&self) -> Word { + let mut partial_blockchain = self.partial_blockchain.clone(); + // SAFETY: This does not panic as long as the block header we're adding is the next one in + // the chain which is validated as part of constructing a `ProposedBlock`. + partial_blockchain.add_block(&self.prev_block_header, true); + partial_blockchain.peaks().hash_peaks() } // STATE MUTATORS @@ -373,6 +497,7 @@ impl Deserializable for ProposedBlock { Ok(block) } } + // HELPER FUNCTIONS // ================================================================================================ diff --git a/crates/miden-objects/src/block/proven_block.rs b/crates/miden-objects/src/block/proven_block.rs index 736a64cd85..dfa2bc60bf 100644 --- a/crates/miden-objects/src/block/proven_block.rs +++ b/crates/miden-objects/src/block/proven_block.rs @@ -1,55 +1,26 @@ -use alloc::vec::Vec; - -use crate::block::{ - BlockAccountUpdate, - BlockHeader, - BlockNoteIndex, - BlockNoteTree, - OutputNoteBatch, -}; -use crate::note::Nullifier; -use crate::transaction::{OrderedTransactionHeaders, OutputNote}; +use crate::MIN_PROOF_SECURITY_LEVEL; +use crate::block::{BlockBody, BlockHeader, BlockProof}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; -use crate::{MIN_PROOF_SECURITY_LEVEL, Word}; // PROVEN BLOCK // ================================================================================================ -/// A block in the Miden chain. +/// Represents a block in the Miden blockchain that has been signed and proven. /// -/// A block is built from batches of transactions, i.e. multiple -/// [`ProvenBatch`](crate::batch::ProvenBatch)es, and each batch contains multiple -/// [`ProvenTransaction`](crate::transaction::ProvenTransaction)s. +/// Blocks transition through proposed, signed, and proven states. This struct represents the final, +/// proven state of a block. /// -/// It consists of the following components: -/// - A [`BlockHeader`] committing to the current state of the chain and against which account, note -/// or nullifier inclusion or absence can be proven. See its documentation for details on what it -/// commits to. Eventually, it will also contain a ZK proof of the validity of the block. -/// - A list of account updates for all accounts updated in this block. For private accounts, the -/// update contains only the new account state commitments while for public accounts, the update -/// also includes the delta which can be applied to the previous account state to get the new -/// account state. -/// - A list of new notes created in this block. For private notes, the block contains only note IDs -/// and note metadata while for public notes the full note details are included. -/// - A list of new nullifiers created for all notes that were consumed in the block. -/// - A list of transaction headers that were included in the block. +/// Proven blocks are the final, canonical blocks in the chain. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProvenBlock { - /// The header of the block, committing to the current state of the chain. + /// The header of the proven block. header: BlockHeader, - /// Account updates for the block. - updated_accounts: Vec, - - /// Note batches created by the transactions in this block. - output_note_batches: Vec, + /// The body of the proven block. + body: BlockBody, - /// Nullifiers created by the transactions in this block through the consumption of notes. - created_nullifiers: Vec, - - /// The aggregated and flattened transaction headers of all batches in the order in which they - /// appeared in the proposed block. - transactions: OrderedTransactionHeaders, + /// The proof of the block. + proof: BlockProof, } impl ProvenBlock { @@ -59,40 +30,8 @@ impl ProvenBlock { /// /// This constructor does not do any validation, so passing incorrect values may lead to later /// panics. - pub fn new_unchecked( - header: BlockHeader, - updated_accounts: Vec, - output_note_batches: Vec, - created_nullifiers: Vec, - transactions: OrderedTransactionHeaders, - ) -> Self { - Self { - header, - updated_accounts, - output_note_batches, - created_nullifiers, - transactions, - } - } - - /// Returns the commitment to this block. - pub fn commitment(&self) -> Word { - self.header.commitment() - } - - /// Returns the header of this block. - pub fn header(&self) -> &BlockHeader { - &self.header - } - - /// Returns the slice of [`BlockAccountUpdate`]s for all accounts updated in this block. - pub fn updated_accounts(&self) -> &[BlockAccountUpdate] { - &self.updated_accounts - } - - /// Returns the slice of [`OutputNoteBatch`]es for all output notes created in this block. - pub fn output_note_batches(&self) -> &[OutputNoteBatch] { - &self.output_note_batches + pub fn new_unchecked(header: BlockHeader, body: BlockBody, proof: BlockProof) -> Self { + Self { header, body, proof } } /// Returns the proof security level of the block. @@ -100,47 +39,19 @@ impl ProvenBlock { MIN_PROOF_SECURITY_LEVEL } - /// Returns an iterator over all [`OutputNote`]s created in this block. - /// - /// Each note is accompanied by a corresponding index specifying where the note is located - /// in the block's [`BlockNoteTree`]. - pub fn output_notes(&self) -> impl Iterator { - self.output_note_batches.iter().enumerate().flat_map(|(batch_idx, notes)| { - notes.iter().map(move |(note_idx_in_batch, note)| { - ( - // SAFETY: The proven block contains at most the max allowed number of batches - // and each batch is guaranteed to contain at most the - // max allowed number of output notes. - BlockNoteIndex::new(batch_idx, *note_idx_in_batch) - .expect("max batches in block and max notes in batches should be enforced"), - note, - ) - }) - }) - } - - /// Returns the [`BlockNoteTree`] containing all [`OutputNote`]s created in this block. - pub fn build_output_note_tree(&self) -> BlockNoteTree { - let entries = self - .output_notes() - .map(|(note_index, note)| (note_index, note.id(), *note.metadata())); - - // SAFETY: We only construct proven blocks that: - // - do not contain duplicates - // - contain at most the max allowed number of batches and each batch is guaranteed to - // contain at most the max allowed number of output notes. - BlockNoteTree::with_entries(entries) - .expect("the output notes of the block should not contain duplicates and contain at most the allowed maximum") + /// Returns the header of the block. + pub fn header(&self) -> &BlockHeader { + &self.header } - /// Returns a reference to the slice of nullifiers for all notes consumed in the block. - pub fn created_nullifiers(&self) -> &[Nullifier] { - &self.created_nullifiers + /// Returns the body of the block. + pub fn body(&self) -> &BlockBody { + &self.body } - /// Returns the [`OrderedTransactionHeaders`] of all transactions included in this block. - pub fn transactions(&self) -> &OrderedTransactionHeaders { - &self.transactions + /// Returns the proof of the block. + pub fn proof(&self) -> &BlockProof { + &self.proof } } @@ -150,10 +61,8 @@ impl ProvenBlock { impl Serializable for ProvenBlock { fn write_into(&self, target: &mut W) { self.header.write_into(target); - self.updated_accounts.write_into(target); - self.output_note_batches.write_into(target); - self.created_nullifiers.write_into(target); - self.transactions.write_into(target); + self.body.write_into(target); + self.proof.write_into(target); } } @@ -161,38 +70,10 @@ impl Deserializable for ProvenBlock { fn read_from(source: &mut R) -> Result { let block = Self { header: BlockHeader::read_from(source)?, - updated_accounts: >::read_from(source)?, - output_note_batches: >::read_from(source)?, - created_nullifiers: >::read_from(source)?, - transactions: OrderedTransactionHeaders::read_from(source)?, + body: BlockBody::read_from(source)?, + proof: BlockProof::read_from(source)?, }; Ok(block) } } - -// TESTING -// ================================================================================================ - -#[cfg(any(feature = "testing", test))] -impl ProvenBlock { - /// Returns a mutable reference to the block's account updates for testing purposes. - pub fn updated_accounts_mut(&mut self) -> &mut Vec { - &mut self.updated_accounts - } - - /// Returns a mutable reference to the block's nullifiers for testing purposes. - pub fn created_nullifiers_mut(&mut self) -> &mut Vec { - &mut self.created_nullifiers - } - - /// Returns a mutable reference to the block's output note batches for testing purposes. - pub fn output_note_batches_mut(&mut self) -> &mut Vec { - &mut self.output_note_batches - } - - /// Sets the block's header for testing purposes. - pub fn set_block_header(&mut self, header: BlockHeader) { - self.header = header; - } -} diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 3a806326a7..a11d45d421 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -1022,6 +1022,31 @@ pub enum ProposedBlockError { account_id: AccountId, source: Box, }, + + #[error("failed to track account witness")] + AccountWitnessTracking { source: AccountTreeError }, + + #[error( + "account tree root of the previous block header is {prev_block_account_root} but the root of the partial tree computed from account witnesses is {stale_account_root}, indicating that the witnesses are stale" + )] + StaleAccountTreeRoot { + prev_block_account_root: Word, + stale_account_root: Word, + }, + + #[error("account ID prefix already exists in the tree")] + AccountIdPrefixDuplicate { source: AccountTreeError }, + + #[error( + "nullifier tree root of the previous block header is {prev_block_nullifier_root} but the root of the partial tree computed from nullifier witnesses is {stale_nullifier_root}, indicating that the witnesses are stale" + )] + StaleNullifierTreeRoot { + prev_block_nullifier_root: Word, + stale_nullifier_root: Word, + }, + + #[error("nullifier witness has a different root than the current nullifier tree root")] + NullifierWitnessRootMismatch(NullifierTreeError), } // FEE ERROR diff --git a/crates/miden-objects/src/transaction/partial_blockchain.rs b/crates/miden-objects/src/transaction/partial_blockchain.rs index 2142ad69dd..c91b98174a 100644 --- a/crates/miden-objects/src/transaction/partial_blockchain.rs +++ b/crates/miden-objects/src/transaction/partial_blockchain.rs @@ -182,7 +182,7 @@ impl PartialBlockchain { /// # Panics /// Panics if the `block_header.block_num` is not equal to the current chain length (i.e., the /// provided block header is not the next block in the chain). - pub fn add_block(&mut self, block_header: BlockHeader, track: bool) { + pub fn add_block(&mut self, block_header: &BlockHeader, track: bool) { assert_eq!(block_header.block_num(), self.chain_length()); self.mmr.add(block_header.commitment(), track); } @@ -298,9 +298,9 @@ mod tests { // add a new block to the partial blockchain, this reduces the number of peaks to 1 let block_num = 3; - let bock_header = int_to_block_header(block_num); - mmr.add(bock_header.commitment()); - partial_blockchain.add_block(bock_header, true); + let block_header = int_to_block_header(block_num); + mmr.add(block_header.commitment()); + partial_blockchain.add_block(&block_header, true); assert_eq!( mmr.open(block_num as usize).unwrap(), @@ -309,9 +309,9 @@ mod tests { // add one more block to the partial blockchain, the number of peaks is again 2 let block_num = 4; - let bock_header = int_to_block_header(block_num); - mmr.add(bock_header.commitment()); - partial_blockchain.add_block(bock_header, true); + let block_header = int_to_block_header(block_num); + mmr.add(block_header.commitment()); + partial_blockchain.add_block(&block_header, true); assert_eq!( mmr.open(block_num as usize).unwrap(), @@ -320,9 +320,9 @@ mod tests { // add one more block to the partial blockchain, the number of peaks is still 2 let block_num = 5; - let bock_header = int_to_block_header(block_num); - mmr.add(bock_header.commitment()); - partial_blockchain.add_block(bock_header, true); + let block_header = int_to_block_header(block_num); + mmr.add(block_header.commitment()); + partial_blockchain.add_block(&block_header, true); assert_eq!( mmr.open(block_num as usize).unwrap(), diff --git a/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs b/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs index 058d6a7f80..78b1d61eb3 100644 --- a/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs +++ b/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs @@ -293,20 +293,30 @@ async fn unauthenticated_note_converted_to_authenticated() -> anyhow::Result<()> let block2 = chain.prove_next_block()?; let block3 = chain.prove_next_block()?; - assert_eq!(block1.output_notes().count(), 2, "block 1 should contain note1 and note2"); + assert_eq!( + block1.body().output_notes().count(), + 2, + "block 1 should contain note1 and note2" + ); assert!( - block1.output_notes().any(|(_, note)| note.commitment() == note1.commitment()), + block1 + .body() + .output_notes() + .any(|(_, note)| note.commitment() == note1.commitment()), "block 1 should contain note1" ); assert!( - block1.output_notes().any(|(_, note)| note.commitment() == note2.commitment()), + block1 + .body() + .output_notes() + .any(|(_, note)| note.commitment() == note2.commitment()), "block 1 should contain note2" ); // Consume the authenticated note as an unauthenticated one in the transaction. let tx1 = MockProvenTxBuilder::with_account(account1.id(), Word::empty(), account1.commitment()) - .ref_block_commitment(block2.commitment()) + .ref_block_commitment(block2.header().commitment()) .unauthenticated_notes(vec![note2.clone()]) .build()?; diff --git a/crates/miden-testing/src/kernel_tests/block/proven_block_error.rs b/crates/miden-testing/src/kernel_tests/block/header_errors.rs similarity index 89% rename from crates/miden-testing/src/kernel_tests/block/proven_block_error.rs rename to crates/miden-testing/src/kernel_tests/block/header_errors.rs index f50937fdbb..e9125333f0 100644 --- a/crates/miden-testing/src/kernel_tests/block/proven_block_error.rs +++ b/crates/miden-testing/src/kernel_tests/block/header_errors.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use anyhow::Context; use assert_matches::assert_matches; -use miden_block_prover::{LocalBlockProver, ProvenBlockError}; +use miden_lib::block::build_block; use miden_lib::testing::account_component::{IncrNonceAuthComponent, MockAccountComponent}; use miden_lib::testing::mock_account::MockAccountExt; use miden_objects::account::delta::AccountUpdateDetails; @@ -13,7 +13,7 @@ use miden_objects::block::{BlockInputs, BlockNumber, ProposedBlock}; use miden_objects::note::NoteType; use miden_objects::transaction::ProvenTransactionBuilder; use miden_objects::vm::ExecutionProof; -use miden_objects::{AccountTreeError, NullifierTreeError, Word}; +use miden_objects::{AccountTreeError, NullifierTreeError, ProposedBlockError, Word}; use miden_tx::LocalTransactionProver; use crate::kernel_tests::block::utils::MockChainBlockExt; @@ -73,10 +73,10 @@ async fn witness_test_setup() -> anyhow::Result { }) } -/// Tests that a proven block cannot be built if witnesses from a stale account tree are used +/// Tests that a block cannot be built if witnesses from a stale account tree are used /// (i.e. an account tree whose root is not in the previous block header). #[tokio::test] -async fn proven_block_fails_on_stale_account_witnesses() -> anyhow::Result<()> { +async fn block_building_fails_on_stale_account_witnesses() -> anyhow::Result<()> { // Setup test with stale and valid block inputs. // -------------------------------------------------------------------------------------------- @@ -97,11 +97,11 @@ async fn proven_block_fails_on_stale_account_witnesses() -> anyhow::Result<()> { let proposed_block0 = ProposedBlock::new(invalid_account_tree_block_inputs, batches.clone()) .context("failed to propose block 0")?; - let error = LocalBlockProver::new(0).prove_dummy(proposed_block0).unwrap_err(); + let error = build_block(proposed_block0).unwrap_err(); assert_matches!( error, - ProvenBlockError::StaleAccountTreeRoot { + ProposedBlockError::StaleAccountTreeRoot { prev_block_account_root, .. } if prev_block_account_root == valid_block_inputs.prev_block_header().account_root() @@ -110,10 +110,10 @@ async fn proven_block_fails_on_stale_account_witnesses() -> anyhow::Result<()> { Ok(()) } -/// Tests that a proven block cannot be built if witnesses from a stale nullifier tree are used +/// Tests that a block cannot be built if witnesses from a stale nullifier tree are used /// (i.e. a nullifier tree whose root is not in the previous block header). #[tokio::test] -async fn proven_block_fails_on_stale_nullifier_witnesses() -> anyhow::Result<()> { +async fn block_building_fails_on_stale_nullifier_witnesses() -> anyhow::Result<()> { // Setup test with stale and valid block inputs. // -------------------------------------------------------------------------------------------- @@ -134,11 +134,11 @@ async fn proven_block_fails_on_stale_nullifier_witnesses() -> anyhow::Result<()> let proposed_block2 = ProposedBlock::new(invalid_nullifier_tree_block_inputs, batches.clone()) .context("failed to propose block 2")?; - let error = LocalBlockProver::new(0).prove_dummy(proposed_block2).unwrap_err(); + let error = build_block(proposed_block2).unwrap_err(); assert_matches!( error, - ProvenBlockError::StaleNullifierTreeRoot { + ProposedBlockError::StaleNullifierTreeRoot { prev_block_nullifier_root, .. } if prev_block_nullifier_root == valid_block_inputs.prev_block_header().nullifier_root() @@ -147,10 +147,10 @@ async fn proven_block_fails_on_stale_nullifier_witnesses() -> anyhow::Result<()> Ok(()) } -/// Tests that a proven block cannot be built if both witnesses from a stale account tree and from +/// Tests that a block cannot be built if both witnesses from a stale account tree and from /// the current account tree are used which results in different account tree roots. #[tokio::test] -async fn proven_block_fails_on_account_tree_root_mismatch() -> anyhow::Result<()> { +async fn block_building_fails_on_account_tree_root_mismatch() -> anyhow::Result<()> { // Setup test with stale and valid block inputs. // -------------------------------------------------------------------------------------------- @@ -180,11 +180,11 @@ async fn proven_block_fails_on_account_tree_root_mismatch() -> anyhow::Result<() let proposed_block1 = ProposedBlock::new(stale_account_witness_block_inputs, batches.clone()) .context("failed to propose block 1")?; - let error = LocalBlockProver::new(0).prove_dummy(proposed_block1).unwrap_err(); + let error = build_block(proposed_block1).unwrap_err(); assert_matches!( error, - ProvenBlockError::AccountWitnessTracking { + ProposedBlockError::AccountWitnessTracking { source: AccountTreeError::TreeRootConflict { .. }, .. } @@ -193,10 +193,10 @@ async fn proven_block_fails_on_account_tree_root_mismatch() -> anyhow::Result<() Ok(()) } -/// Tests that a proven block cannot be built if both witnesses from a stale nullifier tree and from +/// Tests that a block cannot be built if both witnesses from a stale nullifier tree and from /// the current nullifier tree are used which results in different nullifier tree roots. #[tokio::test] -async fn proven_block_fails_on_nullifier_tree_root_mismatch() -> anyhow::Result<()> { +async fn block_building_fails_on_nullifier_tree_root_mismatch() -> anyhow::Result<()> { // Setup test with stale and valid block inputs. // -------------------------------------------------------------------------------------------- @@ -228,11 +228,11 @@ async fn proven_block_fails_on_nullifier_tree_root_mismatch() -> anyhow::Result< let proposed_block3 = ProposedBlock::new(invalid_nullifier_witness_block_inputs, batches) .context("failed to propose block 3")?; - let error = LocalBlockProver::new(0).prove_dummy(proposed_block3).unwrap_err(); + let error = build_block(proposed_block3).unwrap_err(); assert_matches!( error, - ProvenBlockError::NullifierWitnessRootMismatch(NullifierTreeError::TreeRootConflict(_)) + ProposedBlockError::NullifierWitnessRootMismatch(NullifierTreeError::TreeRootConflict(_)) ); Ok(()) @@ -241,7 +241,7 @@ async fn proven_block_fails_on_nullifier_tree_root_mismatch() -> anyhow::Result< /// Tests that creating an account when an existing account with the same account ID prefix exists, /// results in an error. #[tokio::test] -async fn proven_block_fails_on_creating_account_with_existing_account_id_prefix() +async fn block_building_fails_on_creating_account_with_existing_account_id_prefix() -> anyhow::Result<()> { // Construct a new account. // -------------------------------------------------------------------------------------------- @@ -321,12 +321,12 @@ async fn proven_block_fails_on_creating_account_with_existing_account_id_prefix( let block = mock_chain.propose_block(batches).context("failed to propose block")?; - let err = LocalBlockProver::new(0).prove_dummy(block).unwrap_err(); + let err = build_block(block).unwrap_err(); // This should fail when we try to _insert_ the same two prefixes into the partial tree. assert_matches!( err, - ProvenBlockError::AccountIdPrefixDuplicate { + ProposedBlockError::AccountIdPrefixDuplicate { source: AccountTreeError::DuplicateIdPrefix { duplicate_prefix } } if duplicate_prefix == new_id.prefix() ); @@ -336,7 +336,7 @@ async fn proven_block_fails_on_creating_account_with_existing_account_id_prefix( /// Tests that creating two accounts in the same block whose ID prefixes match, results in an error. #[tokio::test] -async fn proven_block_fails_on_creating_account_with_duplicate_account_id_prefix() +async fn block_building_fails_on_creating_account_with_duplicate_account_id_prefix() -> anyhow::Result<()> { // Construct a new account. // -------------------------------------------------------------------------------------------- @@ -417,12 +417,12 @@ async fn proven_block_fails_on_creating_account_with_duplicate_account_id_prefix let block = mock_chain.propose_block(batches).context("failed to propose block")?; - let err = LocalBlockProver::new(0).prove_dummy(block).unwrap_err(); + let err = build_block(block).unwrap_err(); // This should fail when we try to _track_ the same two prefixes in the partial tree. assert_matches!( err, - ProvenBlockError::AccountWitnessTracking { + ProposedBlockError::AccountWitnessTracking { source: AccountTreeError::DuplicateIdPrefix { duplicate_prefix } } if duplicate_prefix == id0.prefix() ); diff --git a/crates/miden-testing/src/kernel_tests/block/mod.rs b/crates/miden-testing/src/kernel_tests/block/mod.rs index ecd426ab64..b55f773b60 100644 --- a/crates/miden-testing/src/kernel_tests/block/mod.rs +++ b/crates/miden-testing/src/kernel_tests/block/mod.rs @@ -1,6 +1,6 @@ mod proposed_block_errors; mod proposed_block_success; -mod proven_block_error; +mod header_errors; mod proven_block_success; pub(crate) mod utils; diff --git a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs index cc7aabf71f..b2b0b879fa 100644 --- a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs @@ -418,7 +418,7 @@ async fn proposed_block_fails_on_invalid_proof_or_missing_note_inclusion_referen .expect("note proof should have been fetched") .clone(); let mut original_merkle_path = MerklePath::from(original_note_proof.note_path().clone()); - original_merkle_path.push(block2.commitment()); + original_merkle_path.push(block2.header().commitment()); // Add a random hash to the path to make it invalid. let invalid_note_path = SparseMerklePath::try_from(original_merkle_path).unwrap(); let invalid_note_proof = NoteInclusionProof::new( diff --git a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs index ab324b942c..cf5d5718ee 100644 --- a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs @@ -3,8 +3,8 @@ use std::collections::BTreeMap; use std::vec::Vec; use anyhow::Context; -use miden_block_prover::LocalBlockProver; use miden_lib::note::create_p2id_note; +use miden_objects::ZERO; use miden_objects::asset::FungibleAsset; use miden_objects::batch::BatchNoteTree; use miden_objects::block::account_tree::AccountTree; @@ -12,7 +12,6 @@ use miden_objects::block::{BlockInputs, BlockNoteIndex, BlockNoteTree, ProposedB use miden_objects::crypto::merkle::Smt; use miden_objects::note::NoteType; use miden_objects::transaction::InputNoteCommitment; -use miden_objects::{MIN_PROOF_SECURITY_LEVEL, ZERO}; use crate::kernel_tests::block::utils::MockChainBlockExt; use crate::utils::create_p2any_note; @@ -94,10 +93,9 @@ async fn proven_block_success() -> anyhow::Result<()> { // Sanity check: Batches should have two output notes each. assert_eq!(batch0.output_notes().len(), 2); assert_eq!(batch1.output_notes().len(), 2); + let batches = vec![batch0.clone(), batch1.clone()]; - let proposed_block = chain - .propose_block([batch0.clone(), batch1.clone()]) - .context("failed to propose block")?; + let proposed_block = chain.propose_block(batches.clone()).context("failed to propose block")?; // Compute expected block note tree. // -------------------------------------------------------------------------------------------- @@ -147,9 +145,7 @@ async fn proven_block_success() -> anyhow::Result<()> { // Prove block. // -------------------------------------------------------------------------------------------- - let proven_block = LocalBlockProver::new(MIN_PROOF_SECURITY_LEVEL) - .prove_dummy(proposed_block) - .context("failed to prove proposed block")?; + let proven_block = chain.prove_block(proposed_block.clone())?; // Check tree/chain commitments against expected values. // -------------------------------------------------------------------------------------------- @@ -168,27 +164,27 @@ async fn proven_block_success() -> anyhow::Result<()> { assert_eq!(proven_block.header().note_root(), expected_block_note_tree.root()); // Assert that the block note tree can be reconstructed. - assert_eq!(proven_block.build_output_note_tree(), expected_block_note_tree); + assert_eq!(proven_block.body().compute_block_note_tree(), expected_block_note_tree); // Check input notes / nullifiers. // -------------------------------------------------------------------------------------------- - assert_eq!(proven_block.created_nullifiers().len(), 4); - assert!(proven_block.created_nullifiers().contains(&input_note0.nullifier())); - assert!(proven_block.created_nullifiers().contains(&input_note1.nullifier())); - assert!(proven_block.created_nullifiers().contains(&input_note2.nullifier())); - assert!(proven_block.created_nullifiers().contains(&input_note3.nullifier())); + assert_eq!(proven_block.body().created_nullifiers().len(), 4); + assert!(proven_block.body().created_nullifiers().contains(&input_note0.nullifier())); + assert!(proven_block.body().created_nullifiers().contains(&input_note1.nullifier())); + assert!(proven_block.body().created_nullifiers().contains(&input_note2.nullifier())); + assert!(proven_block.body().created_nullifiers().contains(&input_note3.nullifier())); // Check output notes. // -------------------------------------------------------------------------------------------- - assert_eq!(proven_block.output_note_batches().len(), 2); + assert_eq!(proven_block.body().output_note_batches().len(), 2); assert_eq!( - proven_block.output_note_batches()[0], + proven_block.body().output_note_batches()[0], batch0.output_notes().iter().cloned().enumerate().collect::>() ); assert_eq!( - proven_block.output_note_batches()[1], + proven_block.body().output_note_batches()[1], batch1.output_notes().iter().cloned().enumerate().collect::>() ); @@ -199,6 +195,7 @@ async fn proven_block_success() -> anyhow::Result<()> { for (tx, batch) in [(&tx0, &batch0), (&tx1, &batch0), (&tx2, &batch1), (&tx3, &batch1)] { let updated_account = tx.account_id(); let block_account_update = proven_block + .body() .updated_accounts() .iter() .find(|update| update.account_id() == updated_account) @@ -342,10 +339,8 @@ async fn proven_block_erasing_unauthenticated_notes() -> anyhow::Result<()> { assert_eq!(output_notes_batch0.len(), 2); assert_eq!(output_notes_batch0, &expected_output_notes_batch0); - let proven_block = LocalBlockProver::new(0) - .prove_dummy(proposed_block) - .context("failed to prove block")?; - let actual_block_note_tree = proven_block.build_output_note_tree(); + let proven_block = chain.prove_block(proposed_block.clone())?; + let actual_block_note_tree = proven_block.body().compute_block_note_tree(); // Remove the erased note to get the expected batch note tree. let mut batch_tree = BatchNoteTree::with_contiguous_leaves( @@ -390,7 +385,7 @@ async fn proven_block_succeeds_with_empty_batches() -> anyhow::Result<()> { // -------------------------------------------------------------------------------------------- let latest_block_header = chain.latest_block_header(); - assert_eq!(latest_block_header.commitment(), blockx.commitment()); + assert_eq!(latest_block_header.commitment(), blockx.header().commitment()); // Sanity check: The account and nullifier tree roots should not be the empty tree roots. assert_ne!(latest_block_header.account_root(), AccountTree::::default().root()); @@ -407,18 +402,17 @@ async fn proven_block_succeeds_with_empty_batches() -> anyhow::Result<()> { BTreeMap::default(), ); + let batches = Vec::new(); let proposed_block = - ProposedBlock::new(block_inputs, Vec::new()).context("failed to propose block")?; + ProposedBlock::new(block_inputs, batches.clone()).context("failed to propose block")?; - let proven_block = LocalBlockProver::new(MIN_PROOF_SECURITY_LEVEL) - .prove_dummy(proposed_block) - .context("failed to prove proposed block")?; + let proven_block = chain.prove_block(proposed_block.clone())?; // Nothing should be created or updated. - assert_eq!(proven_block.updated_accounts().len(), 0); - assert_eq!(proven_block.output_note_batches().len(), 0); - assert_eq!(proven_block.created_nullifiers().len(), 0); - assert!(proven_block.build_output_note_tree().is_empty()); + assert_eq!(proven_block.body().updated_accounts().len(), 0); + assert_eq!(proven_block.body().output_note_batches().len(), 0); + assert_eq!(proven_block.body().created_nullifiers().len(), 0); + assert!(proven_block.body().compute_block_note_tree().is_empty()); // Account and nullifier root should match the previous block header's roots, since nothing has // changed. diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index b3d65aa2f3..6c0fd678a9 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -332,7 +332,7 @@ fn partial_blockchain_memory_assertions( // update the partial blockchain to point to the block against which this transaction is being // executed let mut partial_blockchain = prepared_tx.tx_inputs().blockchain().clone(); - partial_blockchain.add_block(prepared_tx.tx_inputs().block_header().clone(), true); + partial_blockchain.add_block(prepared_tx.tx_inputs().block_header(), true); assert_eq!( exec_output.get_kernel_mem_word(PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR)[0], diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index 39b5a7d688..11495f2d48 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -2,7 +2,9 @@ use alloc::collections::{BTreeMap, BTreeSet}; use alloc::vec::Vec; use anyhow::Context; -use miden_block_prover::{LocalBlockProver, ProvenBlockError}; +use miden_block_prover::LocalBlockProver; +use miden_lib::block::build_block; +use miden_objects::MIN_PROOF_SECURITY_LEVEL; use miden_objects::account::auth::AuthSecretKey; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{Account, AccountId, PartialAccount}; @@ -511,16 +513,6 @@ impl MockChain { self.propose_block_at(batches, timestamp) } - /// Mock-proves a proposed block into a proven block and returns it. - /// - /// This method does not modify the chain state. - pub fn prove_block( - &self, - proposed_block: ProposedBlock, - ) -> Result { - LocalBlockProver::new(0).prove_dummy(proposed_block) - } - // TRANSACTION APIS // ---------------------------------------------------------------------------------------- @@ -847,13 +839,13 @@ impl MockChain { /// - Consumed notes are removed from the committed notes. /// - The block is appended to the [`BlockChain`] and the list of proven blocks. fn apply_block(&mut self, proven_block: ProvenBlock) -> anyhow::Result<()> { - for account_update in proven_block.updated_accounts() { + for account_update in proven_block.body().updated_accounts() { self.account_tree .insert(account_update.account_id(), account_update.final_state_commitment()) .context("failed to insert account update into account tree")?; } - for nullifier in proven_block.created_nullifiers() { + for nullifier in proven_block.body().created_nullifiers() { self.nullifier_tree .mark_spent(*nullifier, proven_block.header().block_num()) .context("failed to mark block nullifier as spent")?; @@ -863,7 +855,7 @@ impl MockChain { // nullifiers, so we'll have to create a second index to do this. } - for account_update in proven_block.updated_accounts() { + for account_update in proven_block.body().updated_accounts() { match account_update.details() { AccountUpdateDetails::Delta(account_delta) => { if account_delta.is_full_state() { @@ -888,8 +880,8 @@ impl MockChain { } } - let notes_tree = proven_block.build_output_note_tree(); - for (block_note_index, created_note) in proven_block.output_notes() { + let notes_tree = proven_block.body().compute_block_note_tree(); + for (block_note_index, created_note) in proven_block.body().output_notes() { let note_path = notes_tree.open(block_note_index); let note_inclusion_proof = NoteInclusionProof::new( proven_block.header().block_num(), @@ -969,9 +961,9 @@ impl MockChain { timestamp.unwrap_or(self.latest_block_header().timestamp() + Self::TIMESTAMP_STEP_SECS); let proposed_block = self - .propose_block_at(batches, block_timestamp) + .propose_block_at(batches.clone(), block_timestamp) .context("failed to create proposed block")?; - let proven_block = self.prove_block(proposed_block).context("failed to prove block")?; + let proven_block = self.prove_block(proposed_block.clone())?; // Apply block. // ---------------------------------------------------------------------------------------- @@ -980,6 +972,18 @@ impl MockChain { Ok(proven_block) } + + /// Proves proposed block alongside a corresponding list of batches. + pub fn prove_block(&self, proposed_block: ProposedBlock) -> anyhow::Result { + let (header, body) = build_block(proposed_block.clone())?; + let inputs = self.get_block_inputs(proposed_block.batches().as_slice())?; + let block_proof = LocalBlockProver::new(MIN_PROOF_SECURITY_LEVEL).prove_dummy( + proposed_block.batches().clone(), + header.clone(), + inputs, + )?; + Ok(ProvenBlock::new_unchecked(header, body, block_proof)) + } } impl Default for MockChain { diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 93aeafa53a..5975c78035 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -32,9 +32,11 @@ use miden_objects::asset::{Asset, FungibleAsset, TokenSymbol}; use miden_objects::block::account_tree::AccountTree; use miden_objects::block::{ BlockAccountUpdate, + BlockBody, BlockHeader, BlockNoteTree, BlockNumber, + BlockProof, Blockchain, FeeParameters, NullifierTree, @@ -233,14 +235,16 @@ impl MockChainBuilder { timestamp, ); - let genesis_block = ProvenBlock::new_unchecked( - header, + let body = BlockBody::new_unchecked( block_account_updates, output_note_batches, created_nullifiers, transactions, ); + let block_proof = BlockProof::new_dummy(); + let genesis_block = ProvenBlock::new_unchecked(header, body, block_proof); + MockChain::from_genesis_block(genesis_block, account_tree, self.account_authenticators) } From fe16823c8aac8b56a8abc40ffa467738b85bcb8d Mon Sep 17 00:00:00 2001 From: igamigo Date: Sun, 23 Nov 2025 17:16:43 -0300 Subject: [PATCH 013/114] refactor: remove advice map merging (#2088) --- crates/miden-lib/src/transaction/inputs.rs | 64 +++---------------- crates/miden-lib/src/transaction/mod.rs | 10 ++- .../src/kernel_tests/tx/test_tx.rs | 4 +- .../miden-testing/src/tx_context/context.rs | 3 +- crates/miden-tx/src/errors/mod.rs | 5 -- crates/miden-tx/src/executor/exec_host.rs | 12 +--- crates/miden-tx/src/executor/mod.rs | 3 +- crates/miden-tx/src/prover/mod.rs | 3 +- 8 files changed, 19 insertions(+), 85 deletions(-) diff --git a/crates/miden-lib/src/transaction/inputs.rs b/crates/miden-lib/src/transaction/inputs.rs index 84140838e8..770ac7e09b 100644 --- a/crates/miden-lib/src/transaction/inputs.rs +++ b/crates/miden-lib/src/transaction/inputs.rs @@ -8,7 +8,6 @@ use miden_objects::transaction::{AccountInputs, InputNote, PartialBlockchain, Tr use miden_objects::vm::AdviceInputs; use miden_objects::{EMPTY_WORD, Felt, FieldElement, Word, ZERO}; use miden_processor::AdviceMutation; -use thiserror::Error; use super::TransactionKernel; @@ -25,13 +24,13 @@ impl TransactionAdviceInputs { /// /// The created advice inputs will be populated with the data required for executing a /// transaction with the specified transaction inputs. - pub fn new(tx_inputs: &TransactionInputs) -> Result { + pub fn new(tx_inputs: &TransactionInputs) -> Self { let mut inputs = TransactionAdviceInputs(tx_inputs.advice_inputs().clone()); inputs.build_stack(tx_inputs); inputs.add_kernel_commitment(); inputs.add_partial_blockchain(tx_inputs.blockchain()); - inputs.add_input_notes(tx_inputs)?; + inputs.add_input_notes(tx_inputs); // Add the script's MAST forest's advice inputs. if let Some(tx_script) = tx_inputs.tx_args().tx_script() { @@ -46,7 +45,7 @@ impl TransactionAdviceInputs { // Inject native account. let partial_native_acc = tx_inputs.account(); - inputs.add_account(partial_native_acc)?; + inputs.add_account(partial_native_acc); // If a seed was provided, extend the map appropriately. if let Some(seed) = tx_inputs.account().seed() { @@ -71,7 +70,7 @@ impl TransactionAdviceInputs { // Extend with extra user-supplied advice. inputs.extend(tx_inputs.tx_args().advice_inputs().clone()); - Ok(inputs) + inputs } /// Returns a reference to the underlying advice inputs. @@ -108,9 +107,9 @@ impl TransactionAdviceInputs { pub fn add_foreign_accounts<'inputs>( &mut self, foreign_account_inputs: impl IntoIterator, - ) -> Result<(), TransactionAdviceMapMismatch> { + ) { for foreign_acc in foreign_account_inputs { - self.add_account(foreign_acc.account())?; + self.add_account(foreign_acc.account()); self.add_account_witness(foreign_acc.witness()); // for foreign accounts, we need to insert the id to state mapping @@ -121,8 +120,6 @@ impl TransactionAdviceInputs { // ACCOUNT_ID |-> [ID_AND_NONCE, VAULT_ROOT, STORAGE_COMMITMENT, CODE_COMMITMENT] self.add_map_entry(account_id_key, header.as_elements()); } - - Ok(()) } /// Extend the advice stack with the transaction inputs. @@ -253,28 +250,13 @@ impl TransactionAdviceInputs { /// - The account code commitment |-> procedures vector. /// - The leaf hash |-> (key, value), for all leaves of the partial vault. /// - If present, the Merkle leaves associated with the account storage maps. - fn add_account( - &mut self, - account: &PartialAccount, - ) -> Result<(), TransactionAdviceMapMismatch> { + fn add_account(&mut self, account: &PartialAccount) { // --- account code ------------------------------------------------------- // CODE_COMMITMENT -> [[ACCOUNT_PROCEDURE_DATA]] let code = account.code(); self.add_map_entry(code.commitment(), code.as_elements()); - // Extend the advice map with the account code's advice inputs. - // This ensures that the advice map is available during the note script execution when it - // calls the account's code that relies on the it's advice map data (data segments) loaded - // into the advice provider - self.0.map.merge(account.code().mast().advice_map()).map_err( - |((key, existing_val), incoming_val)| TransactionAdviceMapMismatch { - key, - existing_val: existing_val.to_vec(), - incoming_val: incoming_val.to_vec(), - }, - )?; - // --- account storage ---------------------------------------------------- // STORAGE_COMMITMENT |-> [[STORAGE_SLOT_DATA]] @@ -290,8 +272,6 @@ impl TransactionAdviceInputs { // populate Merkle store and advice map with nodes info needed to access vault assets self.extend_merkle_store(account.vault().inner_nodes()); self.extend_map(account.vault().leaves().map(|leaf| (leaf.hash(), leaf.to_elements()))); - - Ok(()) } /// Adds an account witness to the advice inputs. @@ -328,12 +308,9 @@ impl TransactionAdviceInputs { /// - The note's position in the note tree /// /// The data above is processed by `prologue::process_input_notes_data`. - fn add_input_notes( - &mut self, - tx_inputs: &TransactionInputs, - ) -> Result<(), TransactionAdviceMapMismatch> { + fn add_input_notes(&mut self, tx_inputs: &TransactionInputs) { if tx_inputs.input_notes().is_empty() { - return Ok(()); + return; } let mut note_data = Vec::new(); @@ -387,19 +364,9 @@ impl TransactionAdviceInputs { note_data.push(Felt::ZERO) }, } - - self.0.map.merge(note.script().mast().advice_map()).map_err( - |((key, existing_val), incoming_val)| TransactionAdviceMapMismatch { - key, - existing_val: existing_val.to_vec(), - incoming_val: incoming_val.to_vec(), - }, - )?; } self.add_map_entry(tx_inputs.input_notes().commitment(), note_data); - - Ok(()) } // HELPER METHODS @@ -447,16 +414,3 @@ impl From for TransactionAdviceInputs { Self(inner) } } - -// CONFLICT ERROR -// ================================================================================================ - -#[derive(Debug, Error)] -#[error( - "conflicting map entry for key {key}: existing={existing_val:?}, incoming={incoming_val:?}" -)] -pub struct TransactionAdviceMapMismatch { - pub key: Word, - pub existing_val: Vec, - pub incoming_val: Vec, -} diff --git a/crates/miden-lib/src/transaction/mod.rs b/crates/miden-lib/src/transaction/mod.rs index d7dae98a10..a81c44adcb 100644 --- a/crates/miden-lib/src/transaction/mod.rs +++ b/crates/miden-lib/src/transaction/mod.rs @@ -25,7 +25,7 @@ mod tx_event_id; pub use tx_event_id::{EventId, TransactionEventId}; mod inputs; -pub use inputs::{TransactionAdviceInputs, TransactionAdviceMapMismatch}; +pub use inputs::TransactionAdviceInputs; mod outputs; pub use outputs::{ @@ -121,9 +121,7 @@ impl TransactionKernel { /// Transforms the provided [`TransactionInputs`] into stack and advice /// inputs needed to execute a transaction kernel for a specific transaction. - pub fn prepare_inputs( - tx_inputs: &TransactionInputs, - ) -> Result<(StackInputs, TransactionAdviceInputs), TransactionAdviceMapMismatch> { + pub fn prepare_inputs(tx_inputs: &TransactionInputs) -> (StackInputs, TransactionAdviceInputs) { let account = tx_inputs.account(); let stack_inputs = TransactionKernel::build_input_stack( @@ -134,9 +132,9 @@ impl TransactionKernel { tx_inputs.block_header().block_num(), ); - let tx_advice_inputs = TransactionAdviceInputs::new(tx_inputs)?; + let tx_advice_inputs = TransactionAdviceInputs::new(tx_inputs); - Ok((stack_inputs, tx_advice_inputs)) + (stack_inputs, tx_advice_inputs) } // ASSEMBLER CONSTRUCTOR diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index dfee592916..7c15358a82 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -786,13 +786,13 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { adv_map.A([1,2,3,4])=[5,6,7,8] begin + call.{assert_adv_map_proc_root} + # test account code advice map push.[6,7,8,9] adv.push_mapval adv_loadw push.[10,11,12,13] assert_eqw.err="account code adv map not found" - - call.{assert_adv_map_proc_root} end "#, assert_adv_map_proc_root = diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index b6abc0df72..9289a9e598 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -77,8 +77,7 @@ impl TransactionContext { /// /// - If the provided `code` is not a valid program. pub async fn execute_code(&self, code: &str) -> Result { - let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&self.tx_inputs) - .expect("error initializing transaction inputs"); + let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&self.tx_inputs); // Virtual file name should be unique. let virtual_source_file = self.source_manager.load( diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 9b069b5378..242a3e6915 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -3,7 +3,6 @@ use alloc::string::String; use alloc::vec::Vec; use core::error::Error; -use miden_lib::transaction::TransactionAdviceMapMismatch; use miden_objects::account::AccountId; use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic; @@ -74,8 +73,6 @@ impl From for TransactionExecutorError { #[derive(Debug, Error)] pub enum TransactionExecutorError { - #[error("the advice map contains conflicting map entries")] - ConflictingAdviceMapEntry(#[source] TransactionAdviceMapMismatch), #[error("failed to fetch transaction inputs from the data store")] FetchTransactionInputsFailed(#[source] DataStoreError), #[error("foreign account inputs for ID {0} are not anchored on reference block")] @@ -149,8 +146,6 @@ pub enum TransactionProverError { TransactionOutputConstructionFailed(#[source] TransactionOutputError), #[error("failed to build proven transaction")] ProvenTransactionBuildFailed(#[source] ProvenTransactionError), - #[error("the advice map contains conflicting map entries")] - ConflictingAdviceMapEntry(#[source] TransactionAdviceMapMismatch), // Print the diagnostic directly instead of returning the source error. In the source error // case, the diagnostic is lost if the execution error is not explicitly unwrapped. #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))] diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 81a23a49e5..8db215cef0 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -149,17 +149,7 @@ where })?; let mut tx_advice_inputs = TransactionAdviceInputs::default(); - tx_advice_inputs - .add_foreign_accounts([&foreign_account_inputs]) - .map_err(|err| { - TransactionKernelError::other_with_source( - format!( - "failed to construct advice inputs for foreign account {}", - foreign_account_inputs.id() - ), - err, - ) - })?; + tx_advice_inputs.add_foreign_accounts([&foreign_account_inputs]); self.base_host .load_foreign_account_code(foreign_account_inputs.code()) diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index c85b8037da..2101970a0d 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -281,8 +281,7 @@ where (TransactionExecutorHost<'store, 'auth, STORE, AUTH>, StackInputs, AdviceInputs), TransactionExecutorError, > { - let (stack_inputs, tx_advice_inputs) = TransactionKernel::prepare_inputs(tx_inputs) - .map_err(TransactionExecutorError::ConflictingAdviceMapEntry)?; + let (stack_inputs, tx_advice_inputs) = TransactionKernel::prepare_inputs(tx_inputs); // This reverses the stack inputs (even though it doesn't look like it does) because the // fast processor expects the reverse order. diff --git a/crates/miden-tx/src/prover/mod.rs b/crates/miden-tx/src/prover/mod.rs index c20728257d..ae7a9e65d0 100644 --- a/crates/miden-tx/src/prover/mod.rs +++ b/crates/miden-tx/src/prover/mod.rs @@ -99,8 +99,7 @@ impl LocalTransactionProver { tx_inputs: impl Into, ) -> Result { let tx_inputs = tx_inputs.into(); - let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&tx_inputs) - .map_err(TransactionProverError::ConflictingAdviceMapEntry)?; + let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&tx_inputs); self.mast_store.load_account_code(tx_inputs.account().code()); for account_code in tx_inputs.foreign_account_code() { From 71403e6be6ac62f8707727f57f2bd26072e6c262 Mon Sep 17 00:00:00 2001 From: Varun Doshi Date: Tue, 25 Nov 2025 05:34:30 +0530 Subject: [PATCH 014/114] refactor: split tx progress events out into a separate enum (#2103) --- CHANGELOG.md | 1 + crates/miden-tx/src/executor/exec_host.rs | 112 ++++++++------- crates/miden-tx/src/host/mod.rs | 2 +- crates/miden-tx/src/host/tx_event.rs | 157 ++++++++++------------ crates/miden-tx/src/prover/prover_host.rs | 14 +- 5 files changed, 127 insertions(+), 159 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6557208288..6af3f16e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). +- Split tx progress events out into a separate enum ([#2103](https://github.com/0xMiden/miden-base/pull/2103)). ## 0.12.3 (2025-11-15) diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 8db215cef0..0a7eafbf41 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -33,6 +33,7 @@ use crate::host::{ TransactionBaseHost, TransactionEvent, TransactionProgress, + TransactionProgressEvent, }; use crate::{AccountProcedureIndexMap, DataStore}; @@ -612,64 +613,59 @@ where TransactionEvent::LinkMapSet { advice_mutation } => Ok(advice_mutation), TransactionEvent::LinkMapGet { advice_mutation } => Ok(advice_mutation), - - TransactionEvent::PrologueStart { clk } => { - self.tx_progress.start_prologue(clk); - Ok(Vec::new()) - }, - TransactionEvent::PrologueEnd { clk } => { - self.tx_progress.end_prologue(clk); - Ok(Vec::new()) - }, - - TransactionEvent::NotesProcessingStart { clk } => { - self.tx_progress.start_notes_processing(clk); - Ok(Vec::new()) - }, - TransactionEvent::NotesProcessingEnd { clk } => { - self.tx_progress.end_notes_processing(clk); - Ok(Vec::new()) - }, - - TransactionEvent::NoteExecutionStart { note_id, clk } => { - self.tx_progress.start_note_execution(clk, note_id); - Ok(Vec::new()) - }, - TransactionEvent::NoteExecutionEnd { clk } => { - self.tx_progress.end_note_execution(clk); - Ok(Vec::new()) - }, - - TransactionEvent::TxScriptProcessingStart { clk } => { - self.tx_progress.start_tx_script_processing(clk); - Ok(Vec::new()) - }, - TransactionEvent::TxScriptProcessingEnd { clk } => { - self.tx_progress.end_tx_script_processing(clk); - Ok(Vec::new()) - }, - - TransactionEvent::EpilogueStart { clk } => { - self.tx_progress.start_epilogue(clk); - Ok(Vec::new()) - }, - TransactionEvent::EpilogueEnd { clk } => { - self.tx_progress.end_epilogue(clk); - Ok(Vec::new()) - }, - - TransactionEvent::EpilogueAuthProcStart { clk } => { - self.tx_progress.start_auth_procedure(clk); - Ok(Vec::new()) - }, - TransactionEvent::EpilogueAuthProcEnd { clk } => { - self.tx_progress.end_auth_procedure(clk); - Ok(Vec::new()) - }, - - TransactionEvent::EpilogueAfterTxCyclesObtained { clk } => { - self.tx_progress.epilogue_after_tx_cycles_obtained(clk); - Ok(Vec::new()) + TransactionEvent::Progress(tx_progress) => match tx_progress { + TransactionProgressEvent::PrologueStart(clk) => { + self.tx_progress.start_prologue(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::PrologueEnd(clk) => { + self.tx_progress.end_prologue(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::NotesProcessingStart(clk) => { + self.tx_progress.start_notes_processing(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::NotesProcessingEnd(clk) => { + self.tx_progress.end_notes_processing(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::NoteExecutionStart { note_id, clk } => { + self.tx_progress.start_note_execution(clk, note_id); + Ok(Vec::new()) + }, + TransactionProgressEvent::NoteExecutionEnd(clk) => { + self.tx_progress.end_note_execution(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::TxScriptProcessingStart(clk) => { + self.tx_progress.start_tx_script_processing(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::TxScriptProcessingEnd(clk) => { + self.tx_progress.end_tx_script_processing(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueStart(clk) => { + self.tx_progress.start_epilogue(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueEnd(clk) => { + self.tx_progress.end_epilogue(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueAuthProcStart(clk) => { + self.tx_progress.start_auth_procedure(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueAuthProcEnd(clk) => { + self.tx_progress.end_auth_procedure(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueAfterTxCyclesObtained(clk) => { + self.tx_progress.epilogue_after_tx_cycles_obtained(clk); + Ok(Vec::new()) + }, }, }; diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index 0ab53cc254..b22c8245f3 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -57,7 +57,7 @@ use miden_processor::{ MastForestStore, ProcessState, }; -pub(crate) use tx_event::{RecipientData, TransactionEvent}; +pub(crate) use tx_event::{RecipientData, TransactionEvent, TransactionProgressEvent}; pub use tx_progress::TransactionProgress; use crate::errors::{TransactionHostError, TransactionKernelError}; diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index 43c7e83746..d233965c45 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -11,6 +11,31 @@ use miden_processor::{AdviceMutation, ProcessState, RowIndex}; use crate::host::{TransactionBaseHost, TransactionKernelProcess}; use crate::{LinkMap, TransactionKernelError}; +// TRANSACTION PROGRESS EVENT +// ================================================================================================ +#[derive(Debug)] +pub(crate) enum TransactionProgressEvent { + PrologueStart(RowIndex), + PrologueEnd(RowIndex), + + NotesProcessingStart(RowIndex), + NotesProcessingEnd(RowIndex), + + NoteExecutionStart { note_id: NoteId, clk: RowIndex }, + NoteExecutionEnd(RowIndex), + + TxScriptProcessingStart(RowIndex), + TxScriptProcessingEnd(RowIndex), + + EpilogueStart(RowIndex), + EpilogueEnd(RowIndex), + + EpilogueAuthProcStart(RowIndex), + EpilogueAuthProcEnd(RowIndex), + + EpilogueAfterTxCyclesObtained(RowIndex), +} + // TRANSACTION EVENT // ================================================================================================ @@ -110,52 +135,7 @@ pub(crate) enum TransactionEvent { advice_mutation: Vec, }, - PrologueStart { - clk: RowIndex, - }, - PrologueEnd { - clk: RowIndex, - }, - - NotesProcessingStart { - clk: RowIndex, - }, - NotesProcessingEnd { - clk: RowIndex, - }, - - NoteExecutionStart { - note_id: NoteId, - clk: RowIndex, - }, - NoteExecutionEnd { - clk: RowIndex, - }, - - TxScriptProcessingStart { - clk: RowIndex, - }, - TxScriptProcessingEnd { - clk: RowIndex, - }, - - EpilogueStart { - clk: RowIndex, - }, - EpilogueEnd { - clk: RowIndex, - }, - - EpilogueAuthProcStart { - clk: RowIndex, - }, - EpilogueAuthProcEnd { - clk: RowIndex, - }, - - EpilogueAfterTxCyclesObtained { - clk: RowIndex, - }, + Progress(TransactionProgressEvent), } impl TransactionEvent { @@ -499,55 +479,58 @@ impl TransactionEvent { advice_mutation: LinkMap::handle_get_event(process), }), - TransactionEventId::PrologueStart => { - Some(TransactionEvent::PrologueStart { clk: process.clk() }) - }, - TransactionEventId::PrologueEnd => { - Some(TransactionEvent::PrologueEnd { clk: process.clk() }) - }, + TransactionEventId::PrologueStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::PrologueStart(process.clk()), + )), + TransactionEventId::PrologueEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::PrologueEnd(process.clk()), + )), - TransactionEventId::NotesProcessingStart => { - Some(TransactionEvent::NotesProcessingStart { clk: process.clk() }) - }, - TransactionEventId::NotesProcessingEnd => { - Some(TransactionEvent::NotesProcessingEnd { clk: process.clk() }) - }, + TransactionEventId::NotesProcessingStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::NotesProcessingStart(process.clk()), + )), + TransactionEventId::NotesProcessingEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::NotesProcessingEnd(process.clk()), + )), TransactionEventId::NoteExecutionStart => { let note_id = process.get_active_note_id()?.ok_or_else(|| TransactionKernelError::other( "note execution interval measurement is incorrect: check the placement of the start and the end of the interval", ))?; - Some(TransactionEvent::NoteExecutionStart { note_id, clk: process.clk() }) - }, - TransactionEventId::NoteExecutionEnd => { - Some(TransactionEvent::NoteExecutionEnd { clk: process.clk() }) - }, - - TransactionEventId::TxScriptProcessingStart => { - Some(TransactionEvent::TxScriptProcessingStart { clk: process.clk() }) - }, - TransactionEventId::TxScriptProcessingEnd => { - Some(TransactionEvent::TxScriptProcessingEnd { clk: process.clk() }) - }, - - TransactionEventId::EpilogueStart => { - Some(TransactionEvent::EpilogueStart { clk: process.clk() }) - }, - TransactionEventId::EpilogueEnd => { - Some(TransactionEvent::EpilogueEnd { clk: process.clk() }) - }, - - TransactionEventId::EpilogueAuthProcStart => { - Some(TransactionEvent::EpilogueAuthProcStart { clk: process.clk() }) - }, - TransactionEventId::EpilogueAuthProcEnd => { - Some(TransactionEvent::EpilogueAuthProcEnd { clk: process.clk() }) - }, - - TransactionEventId::EpilogueAfterTxCyclesObtained => { - Some(TransactionEvent::EpilogueAfterTxCyclesObtained { clk: process.clk() }) + Some(TransactionEvent::Progress(TransactionProgressEvent::NoteExecutionStart { + note_id, + clk: process.clk(), + })) }, + TransactionEventId::NoteExecutionEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::NoteExecutionEnd(process.clk()), + )), + + TransactionEventId::TxScriptProcessingStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::TxScriptProcessingStart(process.clk()), + )), + TransactionEventId::TxScriptProcessingEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::TxScriptProcessingEnd(process.clk()), + )), + + TransactionEventId::EpilogueStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueStart(process.clk()), + )), + TransactionEventId::EpilogueEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueEnd(process.clk()), + )), + + TransactionEventId::EpilogueAuthProcStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueAuthProcStart(process.clk()), + )), + TransactionEventId::EpilogueAuthProcEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueAuthProcEnd(process.clk()), + )), + + TransactionEventId::EpilogueAfterTxCyclesObtained => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueAfterTxCyclesObtained(process.clk()), + )), }; Ok(tx_event) diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index d254624d96..cf70ffa568 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -187,19 +187,7 @@ where TransactionEvent::LinkMapGet { advice_mutation } => Ok(advice_mutation), // We do not track tx progress during proving. - TransactionEvent::PrologueStart { .. } - | TransactionEvent::PrologueEnd { .. } - | TransactionEvent::NotesProcessingStart { .. } - | TransactionEvent::NotesProcessingEnd { .. } - | TransactionEvent::NoteExecutionStart { .. } - | TransactionEvent::NoteExecutionEnd { .. } - | TransactionEvent::TxScriptProcessingStart { .. } - | TransactionEvent::TxScriptProcessingEnd { .. } - | TransactionEvent::EpilogueStart { .. } - | TransactionEvent::EpilogueEnd { .. } - | TransactionEvent::EpilogueAuthProcStart { .. } - | TransactionEvent::EpilogueAuthProcEnd { .. } - | TransactionEvent::EpilogueAfterTxCyclesObtained { .. } => Ok(Vec::new()), + TransactionEvent::Progress(_) => Ok(Vec::new()), }; result.map_err(EventError::from) From d129f34f1a338473fe58dc81cc3cb4598e1f1914 Mon Sep 17 00:00:00 2001 From: radik878 Date: Tue, 25 Nov 2025 04:24:21 +0200 Subject: [PATCH 015/114] Add FPI proving test with two foreign accounts (#2086) * Add FPI proving test with two foreign accounts * corrections --- .../src/kernel_tests/tx/test_fpi.rs | 161 +++++++++++++++++- 1 file changed, 159 insertions(+), 2 deletions(-) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 915c1dd01c..aabe11c7a6 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -1077,7 +1077,7 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { .with_dynamically_linked_library(first_foreign_account_component.library())? .compile_tx_script(code)?; - let executed_transaction = mock_chain + mock_chain .build_tx_context(native_account.id(), &[], &[]) .expect("failed to build tx context") .foreign_accounts(foreign_account_inputs) @@ -1088,7 +1088,164 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { .execute() .await?; - // TODO: Remove later and add a integration test using FPI. + Ok(()) +} + +/// Proves a transaction that uses FPI with two foreign accounts. +/// +/// Call chain: +/// `Native -> First FA -> Second FA` +/// +/// Each foreign account has unique code. The first foreign account calls a procedure on the second +/// foreign account via FPI. We then prove the executed transaction to ensure code for multiple +/// foreign accounts is correctly loaded into the prover host's MAST store. +#[tokio::test] +async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { + // ------ SECOND FOREIGN ACCOUNT --------------------------------------------------------------- + // unique procedure which just leaves a constant on the stack + let second_foreign_account_code_source = r#" + use.std::sys + + export.second_account_foreign_proc + # leave a constant result on the stack + push.3 + + # truncate any padding + exec.sys::truncate_stack + end + "#; + + let source_manager = Arc::new(DefaultSourceManager::default()); + let second_foreign_account_component = AccountComponent::compile( + NamedSource::new("foreign_account", second_foreign_account_code_source), + TransactionKernel::with_kernel_library(source_manager.clone()), + vec![], + )? + .with_supports_all_types(); + + let second_foreign_account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) + .with_auth_component(Auth::IncrNonce) + .with_component(second_foreign_account_component.clone()) + .build_existing()?; + + // ------ FIRST FOREIGN ACCOUNT --------------------------------------------------------------- + // unique procedure which calls the second foreign account via FPI and then returns + let first_foreign_account_code_source = format!( + r#" + use.miden::tx + use.std::sys + + export.first_account_foreign_proc + # pad the stack for the `execute_foreign_procedure` execution + padw padw padw push.0.0.0 + # => [pad(15)] + + # get the hash of the `second_account_foreign_proc` using procref + procref.::foreign_account::second_account_foreign_proc + + # push the ID of the second foreign account + push.{second_foreign_suffix} push.{second_foreign_prefix} + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, pad(15)] + + # call the second foreign account + exec.tx::execute_foreign_procedure + # => [result_from_second] + + # keep the result and drop any padding if present + exec.sys::truncate_stack + end + "#, + second_foreign_prefix = second_foreign_account.id().prefix().as_felt(), + second_foreign_suffix = second_foreign_account.id().suffix(), + ); + + // Link against the second foreign account. + let first_foreign_account_component = AccountComponent::compile( + NamedSource::new("first_foreign_account", first_foreign_account_code_source), + TransactionKernel::with_kernel_library(source_manager.clone()) + .with_dynamic_library(second_foreign_account_component.library()) + .map_err(|e| anyhow::anyhow!("Failed to link dynamic library: {}", e))?, + vec![], + )? + .with_supports_all_types(); + + let first_foreign_account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) + .with_auth_component(Auth::IncrNonce) + .with_component(first_foreign_account_component.clone()) + .build_existing()?; + + // ------ NATIVE ACCOUNT --------------------------------------------------------------- + let native_account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) + .with_auth_component(Auth::IncrNonce) + .with_component(MockAccountComponent::with_empty_slots()) + .storage_mode(AccountStorageMode::Public) + .build_existing()?; + + let mut mock_chain = MockChainBuilder::with_accounts([ + native_account.clone(), + first_foreign_account.clone(), + second_foreign_account.clone(), + ])? + .build()?; + mock_chain.prove_next_block()?; + + let foreign_account_inputs = vec![ + mock_chain + .get_foreign_account_inputs(first_foreign_account.id()) + .expect("failed to get foreign account inputs"), + mock_chain + .get_foreign_account_inputs(second_foreign_account.id()) + .expect("failed to get foreign account inputs"), + ]; + + // ------ TRANSACTION SCRIPT (Native) ---------------------------------------------------------- + // Call the first foreign account's procedure. It will call into the second FA via FPI. + let code = format!( + r#" + use.std::sys + use.miden::tx + + begin + # pad the stack for the `execute_foreign_procedure` execution + padw padw padw push.0.0.0 + # => [pad(15)] + + # get the hash of the `first_account_foreign_proc` procedure + procref.::first_foreign_account::first_account_foreign_proc + + # push the first foreign account ID + push.{foreign_suffix} push.{foreign_prefix} + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, pad(15)] + + exec.tx::execute_foreign_procedure + # => [result_from_second] + + # assert the result returned from the second FA is 3 + dup push.3 assert_eq.err="result from second foreign account should be 3" + + # truncate any remaining stack items + exec.sys::truncate_stack + end + "#, + foreign_prefix = first_foreign_account.id().prefix().as_felt(), + foreign_suffix = first_foreign_account.id().suffix(), + ); + + let tx_script = ScriptBuilder::with_source_manager(source_manager.clone()) + .with_dynamically_linked_library(first_foreign_account_component.library())? + .compile_tx_script(code)?; + + let executed_transaction = mock_chain + .build_tx_context(native_account.id(), &[], &[]) + .expect("failed to build tx context") + .foreign_accounts(foreign_account_inputs) + .tx_script(tx_script) + .with_source_manager(source_manager) + .build()? + .execute() + .await?; + + // Prove the executed transaction which uses FPI across two foreign accounts. LocalTransactionProver::default().prove(executed_transaction)?; Ok(()) From 5a0c6f8ee487d4f17142016e626df0a0c327a30f Mon Sep 17 00:00:00 2001 From: Bernhard Schuster Date: Tue, 25 Nov 2025 13:31:08 +0100 Subject: [PATCH 016/114] feat: add generic `` to `NullifierTree` enabling `LargeSmt` support (#2091) --- CHANGELOG.md | 1 + .../src/account/account_id/id_prefix.rs | 6 +- crates/miden-objects/src/block/mod.rs | 3 +- .../miden-objects/src/block/nullifier_tree.rs | 440 +++++++++++++++--- .../src/block/partial_nullifier_tree.rs | 10 +- crates/miden-testing/src/mock_chain/chain.rs | 2 +- .../src/mock_chain/chain_builder.rs | 5 +- 7 files changed, 399 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af3f16e12..ae61e908a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Added `ecdsa_k256_keccak::PublicKey` as a valid template type ([#2097](https://github.com/0xMiden/miden-base/pull/2097)). - [BREAKING] Fix advice inputs in transaction inputs not being propagated through ([#2099](https://github.com/0xMiden/miden-base/pull/2099)). +- Add `S` generic to `NullifierTree` to allow usage with `LargeSmt`s ([#1353](https://github.com/0xMiden/miden-node/issues/1353)). ## 0.12.2 (2025-11-12) diff --git a/crates/miden-objects/src/account/account_id/id_prefix.rs b/crates/miden-objects/src/account/account_id/id_prefix.rs index 97fa2552b4..46207bcc85 100644 --- a/crates/miden-objects/src/account/account_id/id_prefix.rs +++ b/crates/miden-objects/src/account/account_id/id_prefix.rs @@ -51,9 +51,9 @@ impl AccountIdPrefix { /// /// Panics if the prefix does not contain a known account ID version. /// - /// If debug_assertions are enabled (e.g. in debug mode), this function panics if the given - /// felt is invalid according to the constraints in the - /// [`AccountId`](crate::account::AccountId) documentation. + /// If `debug_assertions` are enabled (e.g. in debug mode), this function panics if the given + /// felt is invalid according to the constraints in the [`AccountId`](crate::account::AccountId) + /// documentation. pub fn new_unchecked(prefix: Felt) -> Self { // The prefix contains the metadata. // If we add more versions in the future, we may need to generalize this. diff --git a/crates/miden-objects/src/block/mod.rs b/crates/miden-objects/src/block/mod.rs index 0489bd2062..63a0c19a01 100644 --- a/crates/miden-objects/src/block/mod.rs +++ b/crates/miden-objects/src/block/mod.rs @@ -24,8 +24,7 @@ pub use partial_account_tree::PartialAccountTree; pub mod account_tree; -mod nullifier_tree; -pub use nullifier_tree::NullifierTree; +pub mod nullifier_tree; mod blockchain; pub use blockchain::Blockchain; diff --git a/crates/miden-objects/src/block/nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree.rs index ce99372208..9c7bd70e9d 100644 --- a/crates/miden-objects/src/block/nullifier_tree.rs +++ b/crates/miden-objects/src/block/nullifier_tree.rs @@ -1,16 +1,199 @@ +use alloc::boxed::Box; use alloc::string::ToString; use alloc::vec::Vec; use miden_core::EMPTY_WORD; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_processor::DeserializationError; +#[cfg(feature = "std")] +use miden_crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; +use miden_crypto::merkle::{MerkleError, MutationSet, Smt, SmtProof}; +use miden_processor::{DeserializationError, SMT_DEPTH}; use crate::Word; use crate::block::{BlockNumber, NullifierWitness}; -use crate::crypto::merkle::{MutationSet, SMT_DEPTH, Smt}; use crate::errors::NullifierTreeError; use crate::note::Nullifier; +// FREE HELPER FUNCTIONS AND CONSTANTS +// ================================================================================================ + +/// The value of an unspent nullifier in the tree. +pub(super) const UNSPENT_NULLIFIER: Word = EMPTY_WORD; + +/// Returns the nullifier's leaf value in the SMT by its block number. +pub(super) fn block_num_to_nullifier_leaf_value(block: BlockNumber) -> Word { + Word::from([block.as_u32(), 0, 0, 0]) +} + +/// Given the leaf value of the nullifier SMT, returns the nullifier's block number. +/// +/// There are no nullifiers in the genesis block. The value zero is instead used to signal +/// absence of a value. +pub(super) fn nullifier_leaf_value_to_block_num(value: Word) -> BlockNumber { + let block_num: u32 = value[0].as_int().try_into().expect("invalid block number found in store"); + + block_num.into() +} + +// NULLIFIER TREE BACKEND TRAIT +// ================================================================================================ + +/// This trait abstracts over different SMT backends (e.g., `Smt` and `LargeSmt`) to allow +/// the `NullifierTree` to work with either implementation transparently. +/// +/// Users should instantiate the backend directly (potentially with entries) and then +/// pass it to [`NullifierTree::new_unchecked`]. +/// +/// # Invariants +/// +/// Assumes the provided SMT upholds the guarantees of the [`NullifierTree`]. Specifically: +/// - Nullifiers are only spent once and their block numbers do not change. +/// - Nullifier entries must follow the format `Word([block_num, 0, 0, 0])` with `block_num` a valid +/// block number. +pub trait NullifierTreeBackend: Sized { + type Error: core::error::Error + Send + 'static; + + /// Returns the number of entries in the SMT. + fn num_entries(&self) -> usize; + + /// Returns all entries in the SMT as an iterator over key-value pairs. + fn entries(&self) -> Box + '_>; + + /// Opens the leaf at the given key, returning a Merkle proof. + fn open(&self, key: &Word) -> SmtProof; + + /// Applies the given mutation set to the SMT. + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error>; + + /// Computes the mutation set required to apply the given updates to the SMT. + fn compute_mutations( + &self, + updates: impl IntoIterator, + ) -> Result, Self::Error>; + + /// Inserts a key-value pair into the SMT, returning the previous value at that key. + fn insert(&mut self, key: Word, value: Word) -> Result; + + /// Returns the value associated with the given key. + fn get_value(&self, key: &Word) -> Word; + + /// Returns the root of the SMT. + fn root(&self) -> Word; +} + +impl NullifierTreeBackend for Smt { + type Error = MerkleError; + + fn num_entries(&self) -> usize { + Smt::num_entries(self) + } + + fn entries(&self) -> Box + '_> { + Box::new(Smt::entries(self).map(|(k, v)| (*k, *v))) + } + + fn open(&self, key: &Word) -> SmtProof { + Smt::open(self, key) + } + + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error> { + Smt::apply_mutations(self, set) + } + + fn compute_mutations( + &self, + updates: impl IntoIterator, + ) -> Result, Self::Error> { + Smt::compute_mutations(self, updates) + } + + fn insert(&mut self, key: Word, value: Word) -> Result { + Smt::insert(self, key, value) + } + + fn get_value(&self, key: &Word) -> Word { + Smt::get_value(self, key) + } + + fn root(&self) -> Word { + Smt::root(self) + } +} + +#[cfg(feature = "std")] +fn large_smt_error_to_merkle_error(err: LargeSmtError) -> MerkleError { + match err { + LargeSmtError::Storage(storage_err) => { + panic!("Storage error encountered: {:?}", storage_err) + }, + LargeSmtError::Merkle(merkle_err) => merkle_err, + } +} + +#[cfg(feature = "std")] +impl NullifierTreeBackend for LargeSmt +where + Backend: SmtStorage, +{ + type Error = MerkleError; + + fn num_entries(&self) -> usize { + // SAFETY: We panic on storage errors here as they represent unrecoverable I/O failures. + // This maintains API compatibility with the non-fallible Smt::num_entries(). + // See issue #2010 for future improvements to error handling. + LargeSmt::num_entries(self) + .map_err(large_smt_error_to_merkle_error) + .expect("Storage I/O error accessing num_entries") + } + + fn entries(&self) -> Box + '_> { + // SAFETY: We expect here as only I/O errors can occur. Storage failures are considered + // unrecoverable at this layer. See issue #2010 for future error handling improvements. + Box::new(LargeSmt::entries(self).expect("Storage I/O error accessing entries")) + } + + fn open(&self, key: &Word) -> SmtProof { + LargeSmt::open(self, key) + } + + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error> { + LargeSmt::apply_mutations(self, set).map_err(large_smt_error_to_merkle_error) + } + + fn compute_mutations( + &self, + updates: impl IntoIterator, + ) -> Result, Self::Error> { + LargeSmt::compute_mutations(self, updates).map_err(large_smt_error_to_merkle_error) + } + + fn insert(&mut self, key: Word, value: Word) -> Result { + LargeSmt::insert(self, key, value) + } + + fn get_value(&self, key: &Word) -> Word { + LargeSmt::get_value(self, key) + } + + fn root(&self) -> Word { + // SAFETY: We expect here as storage errors are considered unrecoverable. This maintains + // API compatibility with the non-fallible Smt::root(). + // See issue #2010 for future improvements to error handling. + LargeSmt::root(self) + .map_err(large_smt_error_to_merkle_error) + .expect("Storage I/O error accessing root") + } +} + /// The sparse merkle tree of all nullifiers in the blockchain. /// /// A nullifier can only ever be spent once and its value in the tree is the block number at which @@ -20,11 +203,23 @@ use crate::note::Nullifier; /// not change. Note that inserting the nullifier multiple times with the same block number is /// valid. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct NullifierTree { - smt: Smt, +pub struct NullifierTree { + smt: Backend, } -impl NullifierTree { +impl Default for NullifierTree +where + Backend: Default, +{ + fn default() -> Self { + Self { smt: Default::default() } + } +} + +impl NullifierTree +where + Backend: NullifierTreeBackend, +{ // CONSTANTS // -------------------------------------------------------------------------------------------- @@ -37,28 +232,13 @@ impl NullifierTree { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new, empty nullifier tree. - pub fn new() -> Self { - Self { smt: Smt::new() } - } - - /// Construct a new nullifier tree from the provided entries. + /// Creates a new `NullifierTree` from its inner representation. /// - /// # Errors + /// # Invariants /// - /// Returns an error if: - /// - the provided entries contain multiple block numbers for the same nullifier. - pub fn with_entries( - entries: impl IntoIterator, - ) -> Result { - let leaves = entries.into_iter().map(|(nullifier, block_num)| { - (nullifier.as_word(), Self::block_num_to_leaf_value(block_num)) - }); - - let smt = Smt::with_entries(leaves) - .map_err(NullifierTreeError::DuplicateNullifierBlockNumbers)?; - - Ok(Self { smt }) + /// See the documentation on [`NullifierTreeBackend`] trait documentation. + pub fn new_unchecked(backend: Backend) -> Self { + NullifierTree { smt: backend } } // PUBLIC ACCESSORS @@ -77,7 +257,7 @@ impl NullifierTree { /// Returns an iterator over the nullifiers and their block numbers in the tree. pub fn entries(&self) -> impl Iterator { self.smt.entries().map(|(nullifier, block_num)| { - (Nullifier::from(*nullifier), Self::leaf_value_to_block_num(*block_num)) + (Nullifier::from(nullifier), nullifier_leaf_value_to_block_num(block_num)) }) } @@ -99,7 +279,7 @@ impl NullifierTree { return None; } - Some(Self::leaf_value_to_block_num(value)) + Some(nullifier_leaf_value_to_block_num(value)) } /// Computes a mutation set resulting from inserting the provided nullifiers into this nullifier @@ -125,9 +305,14 @@ impl NullifierTree { let mutation_set = self .smt - .compute_mutations(nullifiers.into_iter().map(|(nullifier, block_num)| { - (nullifier.as_word(), Self::block_num_to_leaf_value(block_num)) - })) + .compute_mutations( + nullifiers + .into_iter() + .map(|(nullifier, block_num)| { + (nullifier.as_word(), block_num_to_nullifier_leaf_value(block_num)) + }) + .collect::>(), + ) .map_err(NullifierTreeError::ComputeMutations)?; Ok(NullifierMutationSet::new(mutation_set)) @@ -149,7 +334,7 @@ impl NullifierTree { ) -> Result<(), NullifierTreeError> { let prev_nullifier_value = self .smt - .insert(nullifier.as_word(), Self::block_num_to_leaf_value(block_num)) + .insert(nullifier.as_word(), block_num_to_nullifier_leaf_value(block_num)) .map_err(NullifierTreeError::MaxLeafEntriesExceeded)?; if prev_nullifier_value != Self::UNSPENT_NULLIFIER { @@ -173,30 +358,32 @@ impl NullifierTree { .apply_mutations(mutations.into_mutation_set()) .map_err(NullifierTreeError::TreeRootConflict) } +} - // HELPER FUNCTIONS - // -------------------------------------------------------------------------------------------- - - /// Returns the nullifier's leaf value in the SMT by its block number. - pub(super) fn block_num_to_leaf_value(block: BlockNumber) -> Word { - Word::from([block.as_u32(), 0, 0, 0]) - } +// CONVENIENCE METHODS +// ================================================================================================ - /// Given the leaf value of the nullifier SMT, returns the nullifier's block number. +impl NullifierTree { + /// Creates a new nullifier tree from the provided entries. + /// + /// This is a convenience method that creates an SMT backend with the provided entries and + /// wraps it in a NullifierTree. /// - /// There are no nullifiers in the genesis block. The value zero is instead used to signal - /// absence of a value. - fn leaf_value_to_block_num(value: Word) -> BlockNumber { - let block_num: u32 = - value[0].as_int().try_into().expect("invalid block number found in store"); + /// # Errors + /// + /// Returns an error if: + /// - the provided entries contain multiple block numbers for the same nullifier. + pub fn with_entries( + entries: impl IntoIterator, + ) -> Result { + let leaves = entries.into_iter().map(|(nullifier, block_num)| { + (nullifier.as_word(), block_num_to_nullifier_leaf_value(block_num)) + }); - block_num.into() - } -} + let smt = Smt::with_entries(leaves) + .map_err(NullifierTreeError::DuplicateNullifierBlockNumbers)?; -impl Default for NullifierTree { - fn default() -> Self { - Self::new() + Ok(Self::new_unchecked(smt)) } } @@ -229,15 +416,15 @@ impl Deserializable for NullifierTree { /// It is returned by and used in methods on the [`NullifierTree`]. #[derive(Debug, Clone, PartialEq, Eq)] pub struct NullifierMutationSet { - mutation_set: MutationSet<{ NullifierTree::DEPTH }, Word, Word>, + mutation_set: MutationSet, } impl NullifierMutationSet { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new [`AccountMutationSet`] from the provided raw mutation set. - fn new(mutation_set: MutationSet<{ NullifierTree::DEPTH }, Word, Word>) -> Self { + /// Creates a new [`NullifierMutationSet`] from the provided raw mutation set. + fn new(mutation_set: MutationSet) -> Self { Self { mutation_set } } @@ -245,7 +432,7 @@ impl NullifierMutationSet { // -------------------------------------------------------------------------------------------- /// Returns a reference to the underlying [`MutationSet`]. - pub fn as_mutation_set(&self) -> &MutationSet<{ NullifierTree::DEPTH }, Word, Word> { + pub fn as_mutation_set(&self) -> &MutationSet { &self.mutation_set } @@ -253,7 +440,7 @@ impl NullifierMutationSet { // -------------------------------------------------------------------------------------------- /// Consumes self and returns the underlying [`MutationSet`]. - pub fn into_mutation_set(self) -> MutationSet<{ NullifierTree::DEPTH }, Word, Word> { + pub fn into_mutation_set(self) -> MutationSet { self.mutation_set } } @@ -270,10 +457,18 @@ mod tests { use crate::note::Nullifier; use crate::{NullifierTreeError, Word}; + #[test] + fn leaf_value_encode_decode() { + let block_num = BlockNumber::from(0xffff_ffff_u32); + let leaf = super::block_num_to_nullifier_leaf_value(block_num); + let block_num_recovered = super::nullifier_leaf_value_to_block_num(leaf); + assert_eq!(block_num, block_num_recovered); + } + #[test] fn leaf_value_encoding() { let block_num = 123; - let nullifier_value = NullifierTree::block_num_to_leaf_value(block_num.into()); + let nullifier_value = super::block_num_to_nullifier_leaf_value(block_num.into()); assert_eq!(nullifier_value, Word::from([block_num, 0, 0, 0u32])); } @@ -281,7 +476,7 @@ mod tests { fn leaf_value_decoding() { let block_num = 123; let nullifier_value = Word::from([block_num, 0, 0, 0u32]); - let decoded_block_num = NullifierTree::leaf_value_to_block_num(nullifier_value); + let decoded_block_num = super::nullifier_leaf_value_to_block_num(nullifier_value); assert_eq!(decoded_block_num, block_num.into()); } @@ -334,4 +529,135 @@ mod tests { let err = tree.mark_spent(nullifier1, block2).unwrap_err(); assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1); } + + #[cfg(feature = "std")] + #[test] + fn large_smt_backend_basic_operations() { + use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + + // Create test data + let nullifier1 = Nullifier::dummy(1); + let nullifier2 = Nullifier::dummy(2); + let nullifier3 = Nullifier::dummy(3); + + let block1 = BlockNumber::from(1); + let block2 = BlockNumber::from(2); + let block3 = BlockNumber::from(3); + + // Create NullifierTree with LargeSmt backend + let mut tree = NullifierTree::new_unchecked( + LargeSmt::with_entries( + MemoryStorage::default(), + [ + (nullifier1.as_word(), super::block_num_to_nullifier_leaf_value(block1)), + (nullifier2.as_word(), super::block_num_to_nullifier_leaf_value(block2)), + ], + ) + .unwrap(), + ); + + // Test basic operations + assert_eq!(tree.num_nullifiers(), 2); + assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1); + assert_eq!(tree.get_block_num(&nullifier2).unwrap(), block2); + + // Test opening + let _witness1 = tree.open(&nullifier1); + + // Test mutations + tree.mark_spent(nullifier3, block3).unwrap(); + assert_eq!(tree.num_nullifiers(), 3); + assert_eq!(tree.get_block_num(&nullifier3).unwrap(), block3); + } + + #[cfg(feature = "std")] + #[test] + fn large_smt_backend_nullifier_already_spent() { + use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + + let nullifier1 = Nullifier::dummy(1); + + let block1 = BlockNumber::from(1); + let block2 = BlockNumber::from(2); + + let mut tree = NullifierTree::new_unchecked( + LargeSmt::with_entries( + MemoryStorage::default(), + [(nullifier1.as_word(), super::block_num_to_nullifier_leaf_value(block1))], + ) + .unwrap(), + ); + + assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1); + + let err = tree.mark_spent(nullifier1, block2).unwrap_err(); + assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1); + } + + #[cfg(feature = "std")] + #[test] + fn large_smt_backend_apply_mutations() { + use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + + let nullifier1 = Nullifier::dummy(1); + let nullifier2 = Nullifier::dummy(2); + let nullifier3 = Nullifier::dummy(3); + + let block1 = BlockNumber::from(1); + let block2 = BlockNumber::from(2); + let block3 = BlockNumber::from(3); + + let mut tree = LargeSmt::with_entries( + MemoryStorage::default(), + [(nullifier1.as_word(), super::block_num_to_nullifier_leaf_value(block1))], + ) + .map(NullifierTree::new_unchecked) + .unwrap(); + + let mutations = + tree.compute_mutations([(nullifier2, block2), (nullifier3, block3)]).unwrap(); + + tree.apply_mutations(mutations).unwrap(); + + assert_eq!(tree.num_nullifiers(), 3); + assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1); + assert_eq!(tree.get_block_num(&nullifier2).unwrap(), block2); + assert_eq!(tree.get_block_num(&nullifier3).unwrap(), block3); + } + + #[cfg(feature = "std")] + #[test] + fn large_smt_backend_same_root_as_regular_smt() { + use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + + let nullifier1 = Nullifier::dummy(1); + let nullifier2 = Nullifier::dummy(2); + + let block1 = BlockNumber::from(1); + let block2 = BlockNumber::from(2); + + // Create tree with LargeSmt backend + let large_tree = LargeSmt::with_entries( + MemoryStorage::default(), + [ + (nullifier1.as_word(), super::block_num_to_nullifier_leaf_value(block1)), + (nullifier2.as_word(), super::block_num_to_nullifier_leaf_value(block2)), + ], + ) + .map(NullifierTree::new_unchecked) + .unwrap(); + + // Create tree with regular Smt backend + let regular_tree = + NullifierTree::with_entries([(nullifier1, block1), (nullifier2, block2)]).unwrap(); + + // Both should have the same root + assert_eq!(large_tree.root(), regular_tree.root()); + + // Both should have the same nullifier entries + let large_entries: std::collections::BTreeMap<_, _> = large_tree.entries().collect(); + let regular_entries: std::collections::BTreeMap<_, _> = regular_tree.entries().collect(); + + assert_eq!(large_entries, regular_entries); + } } diff --git a/crates/miden-objects/src/block/partial_nullifier_tree.rs b/crates/miden-objects/src/block/partial_nullifier_tree.rs index d32d425976..c7b71b4878 100644 --- a/crates/miden-objects/src/block/partial_nullifier_tree.rs +++ b/crates/miden-objects/src/block/partial_nullifier_tree.rs @@ -1,5 +1,5 @@ use crate::Word; -use crate::block::{BlockNumber, NullifierTree, NullifierWitness}; +use crate::block::{BlockNumber, NullifierWitness, nullifier_tree}; use crate::crypto::merkle::PartialSmt; use crate::errors::NullifierTreeError; use crate::note::Nullifier; @@ -86,10 +86,13 @@ impl PartialNullifierTree { ) -> Result<(), NullifierTreeError> { let prev_nullifier_value = self .0 - .insert(nullifier.as_word(), NullifierTree::block_num_to_leaf_value(block_num)) + .insert( + nullifier.as_word(), + nullifier_tree::block_num_to_nullifier_leaf_value(block_num), + ) .map_err(|source| NullifierTreeError::UntrackedNullifier { nullifier, source })?; - if prev_nullifier_value != NullifierTree::UNSPENT_NULLIFIER { + if prev_nullifier_value != nullifier_tree::UNSPENT_NULLIFIER { Err(NullifierTreeError::NullifierAlreadySpent(nullifier)) } else { Ok(()) @@ -104,6 +107,7 @@ mod tests { use winter_rand_utils::rand_value; use super::*; + use crate::block::nullifier_tree::NullifierTree; use crate::{EMPTY_WORD, Word}; /// Test that using a stale nullifier witness together with a current one results in a different diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index 11495f2d48..47dc10c0eb 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -10,13 +10,13 @@ use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{Account, AccountId, PartialAccount}; use miden_objects::batch::{ProposedBatch, ProvenBatch}; use miden_objects::block::account_tree::AccountTree; +use miden_objects::block::nullifier_tree::NullifierTree; use miden_objects::block::{ AccountWitness, BlockHeader, BlockInputs, BlockNumber, Blockchain, - NullifierTree, NullifierWitness, ProposedBlock, ProvenBlock, diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 5975c78035..5223295f3e 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -30,6 +30,7 @@ use miden_objects::account::{ }; use miden_objects::asset::{Asset, FungibleAsset, TokenSymbol}; use miden_objects::block::account_tree::AccountTree; +use miden_objects::block::nullifier_tree::NullifierTree; use miden_objects::block::{ BlockAccountUpdate, BlockBody, @@ -39,10 +40,10 @@ use miden_objects::block::{ BlockProof, Blockchain, FeeParameters, - NullifierTree, OutputNoteBatch, ProvenBlock, }; +use miden_objects::crypto::merkle::Smt; use miden_objects::note::{Note, NoteDetails, NoteType}; use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; use miden_objects::transaction::{OrderedTransactionHeaders, OutputNote}; @@ -211,7 +212,7 @@ impl MockChainBuilder { let block_num = BlockNumber::from(0u32); let chain_commitment = Blockchain::new().commitment(); let account_root = account_tree.root(); - let nullifier_root = NullifierTree::new().root(); + let nullifier_root = NullifierTree::::default().root(); let note_root = note_tree.root(); let tx_commitment = transactions.commitment(); let tx_kernel_commitment = TransactionKernel.to_commitment(); From cc968e9929a533bd52898a732ac059f05408b33d Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:16:38 +1300 Subject: [PATCH 017/114] feat: add serialization for BlockInputs (#2112) --- .../miden-objects/src/block/block_inputs.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/crates/miden-objects/src/block/block_inputs.rs b/crates/miden-objects/src/block/block_inputs.rs index f21bd95006..15049dfa80 100644 --- a/crates/miden-objects/src/block/block_inputs.rs +++ b/crates/miden-objects/src/block/block_inputs.rs @@ -1,9 +1,12 @@ use alloc::collections::BTreeMap; +use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; + use crate::account::AccountId; use crate::block::{AccountWitness, BlockHeader, NullifierWitness}; use crate::note::{NoteId, NoteInclusionProof, Nullifier}; use crate::transaction::PartialBlockchain; +use crate::utils::serde::DeserializationError; // BLOCK INPUTS // ================================================================================================ @@ -128,3 +131,35 @@ impl BlockInputs { &mut self.account_witnesses } } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for BlockInputs { + fn write_into(&self, target: &mut W) { + self.prev_block_header.write_into(target); + self.partial_blockchain.write_into(target); + self.account_witnesses.write_into(target); + self.nullifier_witnesses.write_into(target); + self.unauthenticated_note_proofs.write_into(target); + } +} + +impl Deserializable for BlockInputs { + fn read_from(source: &mut R) -> Result { + let prev_block_header = BlockHeader::read_from(source)?; + let partial_blockchain = PartialBlockchain::read_from(source)?; + let account_witnesses = BTreeMap::::read_from(source)?; + let nullifier_witnesses = BTreeMap::::read_from(source)?; + let unauthenticated_note_proofs = + BTreeMap::::read_from(source)?; + + Ok(Self::new( + prev_block_header, + partial_blockchain, + account_witnesses, + nullifier_witnesses, + unauthenticated_note_proofs, + )) + } +} From e5ae2329735d2a89b6589375ca7fd91e4f104422 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:11:16 -0300 Subject: [PATCH 018/114] feat: add `note::build_note_tag_for_network_account` procedure (#2120) --- CHANGELOG.md | 1 + crates/miden-lib/asm/miden/note.masm | 32 +++++++++++++++ .../src/kernel_tests/tx/test_note.rs | 41 +++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4666ad506f..937d7fa4a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). - Split tx progress events out into a separate enum ([#2103](https://github.com/0xMiden/miden-base/pull/2103)). +- Added `note::get_network_account_tag` procedure ([#2120](https://github.com/0xMiden/miden-base/pull/2120)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-lib/asm/miden/note.masm b/crates/miden-lib/asm/miden/note.masm index c4e05c4c67..bf2cdc6f00 100644 --- a/crates/miden-lib/asm/miden/note.masm +++ b/crates/miden-lib/asm/miden/note.masm @@ -1,5 +1,6 @@ use.miden::account_id use.std::crypto::hashes::rpo +use.std::math::u64 use.std::mem # ERRORS @@ -225,3 +226,34 @@ export.extract_sender_from_metadata swap # => [sender_id_prefix, sender_id_suffix] end + +#! Computes the tag for a network note for a given network account such that it is +#! picked up by the network transaction builder. +#! +#! This procedure implements the same logic as in Rust in NoteTag::from_network_account_id(). +#! Note: This procedure does not check if the account id is a network account id. +#! +#! Inputs: [account_id_prefix, account_id_suffix] +#! Outputs: [network_account_tag] +#! +#! Where: +#! - account_id_prefix, account_id_suffix is the account id to compute the note tag for. +#! - network_account_tag is the computed network note tag. +#! +#! Invocation: exec +export.build_note_tag_for_network_account + swap drop + # => [account_id_prefix] + + u32split + # => [a_hi, a_lo] + + push.34 + # => [b, a_hi, a_lo] + + exec.u64::shr + # => [c_hi, c_lo] + + drop + # => [network_account_tag] +end diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 1c37c1591e..76f1bc8590 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -27,6 +27,7 @@ use miden_objects::note::{ NoteType, }; use miden_objects::testing::account_id::{ + ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; @@ -554,3 +555,43 @@ async fn test_public_key_as_note_input() -> anyhow::Result<()> { tx_context.execute().await?; Ok(()) } + +#[tokio::test] +async fn test_build_note_tag_for_network_account() -> anyhow::Result<()> { + let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; + + let account_id = AccountId::try_from(ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET)?; + let expected_tag = NoteTag::from_account_id(account_id).as_u32(); + + let prefix: u64 = account_id.prefix().into(); + let suffix: u64 = account_id.suffix().into(); + + let code = format!( + " + use.std::sys + use.miden::note + + begin + push.{suffix}.{prefix} + + exec.note::build_note_tag_for_network_account + # => [network_account_tag] + + exec.sys::truncate_stack + end + ", + suffix = suffix, + prefix = prefix, + ); + + let exec_output = tx_context.execute_code(&code).await?; + let actual_tag = exec_output.stack[0].as_int(); + + assert_eq!( + actual_tag, expected_tag as u64, + "Expected tag {:#010x}, got {:#010x}", + expected_tag, actual_tag + ); + + Ok(()) +} From 271de2fa484300e625d1a275d03f153244e6b874 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 1 Dec 2025 10:56:00 +0700 Subject: [PATCH 019/114] feat: fetch note asset and fee asset witnesses before transaction execution (#2113) * feat: Fetch asset vault keys from data store * feat: Fetch fee asset witness in data store * feat: Add asset witnesses to `TransactionInputs` * feat: Remove asset witness loading during tx fee removal event * chore: add changelog * chore: handle fee asset error * fix: changelog entry * fix: remove unused import * chore: Revert to simpler `get_transaction_inputs` interface * feat: Make `get_vault_asset_witness` take multiple keys * chore: improve asset_vault::add_asset comment --- CHANGELOG.md | 1 + .../asm/kernels/transaction/lib/prologue.masm | 12 +-- crates/miden-lib/src/transaction/inputs.rs | 15 +++- .../src/asset/vault/asset_witness.rs | 46 ++++++++++- .../src/transaction/inputs/mod.rs | 18 +++++ .../miden-testing/src/tx_context/context.rs | 76 +++++++++++++++---- crates/miden-tx/src/errors/mod.rs | 4 + crates/miden-tx/src/executor/data_store.rs | 15 ++-- crates/miden-tx/src/executor/exec_host.rs | 56 ++++++-------- crates/miden-tx/src/executor/mod.rs | 67 +++++++++++++--- 10 files changed, 232 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 937d7fa4a9..78edb303c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Added `ecdsa_k256_keccak::PublicKey` as a valid template type ([#2097](https://github.com/0xMiden/miden-base/pull/2097)). - [BREAKING] Fix advice inputs in transaction inputs not being propagated through ([#2099](https://github.com/0xMiden/miden-base/pull/2099)). - Add `S` generic to `NullifierTree` to allow usage with `LargeSmt`s ([#1353](https://github.com/0xMiden/miden-node/issues/1353)). +- [BREAKING] Pre-fetch note and fee asset witnesses before transaction execution ([#2113](https://github.com/0xMiden/miden-base/pull/2113)). ## 0.12.2 (2025-11-12) diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index 0f4848deb0..13e95c22b0 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -785,16 +785,8 @@ proc.add_input_note_assets_to_vault padw dup.5 mem_loadw_be # => [ASSET, input_vault_root_ptr, assets_start_ptr, assets_end_ptr, input_vault_root_ptr] - # TODO: Because the input vault is a copy of the account vault, to mutate the input vault, - # asset witnesses for the account vault must be requested. - # This is a temporary solution. We should avoid loading assets one by one here and instead - # pre-load all relevant merkle paths for the note assets before tx execution. - # - # This emitted event is equivalent to ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT. - - emit.ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT - # => [ASSET, input_vault_root_ptr, assets_start_ptr, assets_end_ptr, input_vault_root_ptr] - + # the witnesses for the note assets should be added prior to transaction execution and so + # there should be no need to fetch them lazily via an event. exec.asset_vault::add_asset dropw # => [assets_start_ptr, assets_end_ptr, input_vault_root_ptr] diff --git a/crates/miden-lib/src/transaction/inputs.rs b/crates/miden-lib/src/transaction/inputs.rs index 770ac7e09b..dd99af643a 100644 --- a/crates/miden-lib/src/transaction/inputs.rs +++ b/crates/miden-lib/src/transaction/inputs.rs @@ -1,9 +1,10 @@ use alloc::vec::Vec; use miden_objects::account::{AccountHeader, AccountId, PartialAccount}; +use miden_objects::asset::AssetWitness; use miden_objects::block::AccountWitness; use miden_objects::crypto::SequentialCommit; -use miden_objects::crypto::merkle::InnerNodeInfo; +use miden_objects::crypto::merkle::{InnerNodeInfo, SmtProof}; use miden_objects::transaction::{AccountInputs, InputNote, PartialBlockchain, TransactionInputs}; use miden_objects::vm::AdviceInputs; use miden_objects::{EMPTY_WORD, Felt, FieldElement, Word, ZERO}; @@ -67,6 +68,10 @@ impl TransactionAdviceInputs { } } + tx_inputs.asset_witnesses().iter().for_each(|asset_witness| { + inputs.add_asset_witness(asset_witness.clone()); + }); + // Extend with extra user-supplied advice. inputs.extend(tx_inputs.tx_args().advice_inputs().clone()); @@ -287,6 +292,14 @@ impl TransactionAdviceInputs { self.extend_merkle_store(witness.authenticated_nodes()); } + /// Adds an asset witness to the advice inputs. + fn add_asset_witness(&mut self, witness: AssetWitness) { + self.extend_merkle_store(witness.authenticated_nodes()); + + let smt_proof = SmtProof::from(witness); + self.extend_map([(smt_proof.leaf().hash(), smt_proof.leaf().to_elements())]); + } + // NOTE INJECTION // -------------------------------------------------------------------------------------------- diff --git a/crates/miden-objects/src/asset/vault/asset_witness.rs b/crates/miden-objects/src/asset/vault/asset_witness.rs index 41154d23c8..a1f79c4342 100644 --- a/crates/miden-objects/src/asset/vault/asset_witness.rs +++ b/crates/miden-objects/src/asset/vault/asset_witness.rs @@ -1,8 +1,11 @@ +use alloc::string::ToString; + use miden_crypto::merkle::{InnerNodeInfo, SmtLeaf, SmtProof}; use super::vault_key::AssetVaultKey; use crate::AssetError; use crate::asset::Asset; +use crate::utils::serde::{Deserializable, DeserializationError, Serializable}; /// A witness of an asset in an [`AssetVault`](super::AssetVault). /// @@ -46,6 +49,12 @@ impl AssetWitness { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- + /// Returns `true` if this [`AssetWitness`] authenticates the provided [`AssetVaultKey`], i.e. + /// if its leaf index matches, `false` otherwise. + pub fn authenticates_asset_vault_key(&self, vault_key: AssetVaultKey) -> bool { + self.0.leaf().index() == vault_key.to_leaf_index() + } + /// Searches for an [`Asset`] in the witness with the given `vault_key`. pub fn find(&self, vault_key: AssetVaultKey) -> Option { self.assets().find(|asset| asset.vault_key() == vault_key) @@ -82,6 +91,21 @@ impl From for SmtProof { } } +impl Serializable for AssetWitness { + fn write_into(&self, target: &mut W) { + self.0.write_into(target); + } +} + +impl Deserializable for AssetWitness { + fn read_from( + source: &mut R, + ) -> Result { + let proof = SmtProof::read_from(source)?; + Self::new(proof).map_err(|err| DeserializationError::InvalidValue(err.to_string())) + } +} + // TESTS // ================================================================================================ @@ -92,7 +116,11 @@ mod tests { use super::*; use crate::Word; - use crate::asset::{FungibleAsset, NonFungibleAsset}; + use crate::asset::{AssetVault, FungibleAsset, NonFungibleAsset}; + use crate::testing::account_id::{ + ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET, + ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, + }; /// Tests that constructing an asset witness fails if any asset in the smt proof is invalid. #[test] @@ -128,4 +156,20 @@ mod tests { Ok(()) } + + #[test] + fn asset_witness_authenticates_asset_vault_key() -> anyhow::Result<()> { + let fungible_asset0 = + FungibleAsset::new(ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET.try_into()?, 200)?; + let fungible_asset1 = + FungibleAsset::new(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET.try_into()?, 100)?; + + let vault = AssetVault::new(&[fungible_asset0.into()])?; + let witness0 = vault.open(fungible_asset0.vault_key()); + + assert!(witness0.authenticates_asset_vault_key(fungible_asset0.vault_key())); + assert!(!witness0.authenticates_asset_vault_key(fungible_asset1.vault_key())); + + Ok(()) + } } diff --git a/crates/miden-objects/src/transaction/inputs/mod.rs b/crates/miden-objects/src/transaction/inputs/mod.rs index aaabfd9081..7e131e360a 100644 --- a/crates/miden-objects/src/transaction/inputs/mod.rs +++ b/crates/miden-objects/src/transaction/inputs/mod.rs @@ -6,6 +6,7 @@ use miden_core::utils::{Deserializable, Serializable}; use super::PartialBlockchain; use crate::TransactionInputError; use crate::account::{AccountCode, PartialAccount}; +use crate::asset::AssetWitness; use crate::block::{BlockHeader, BlockNumber}; use crate::note::{Note, NoteInclusionProof}; use crate::transaction::{TransactionArgs, TransactionScript}; @@ -30,6 +31,8 @@ pub struct TransactionInputs { tx_args: TransactionArgs, advice_inputs: AdviceInputs, foreign_account_code: Vec, + /// Pre-fetched asset witnesses for note assets and the fee asset. + asset_witnesses: Vec, } impl TransactionInputs { @@ -85,9 +88,16 @@ impl TransactionInputs { tx_args: TransactionArgs::default(), advice_inputs: AdviceInputs::default(), foreign_account_code: Vec::new(), + asset_witnesses: Vec::new(), }) } + /// Replaces the transaction inputs and assigns the given asset witnesses. + pub fn with_asset_witnesses(mut self, witnesses: Vec) -> Self { + self.asset_witnesses = witnesses; + self + } + /// Replaces the transaction inputs and assigns the given foreign account code. pub fn with_foreign_account_code(mut self, foreign_account_code: Vec) -> Self { self.foreign_account_code = foreign_account_code; @@ -168,6 +178,11 @@ impl TransactionInputs { &self.foreign_account_code } + /// Returns the pre-fetched witnesses for note and fee assets. + pub fn asset_witnesses(&self) -> &[AssetWitness] { + &self.asset_witnesses + } + /// Returns the advice inputs to be consumed in the transaction. pub fn advice_inputs(&self) -> &AdviceInputs { &self.advice_inputs @@ -216,6 +231,7 @@ impl Serializable for TransactionInputs { self.tx_args.write_into(target); self.advice_inputs.write_into(target); self.foreign_account_code.write_into(target); + self.asset_witnesses.write_into(target); } } @@ -230,6 +246,7 @@ impl Deserializable for TransactionInputs { let tx_args = TransactionArgs::read_from(source)?; let advice_inputs = AdviceInputs::read_from(source)?; let foreign_account_code = Vec::::read_from(source)?; + let asset_witnesses = Vec::::read_from(source)?; Ok(TransactionInputs { account, @@ -239,6 +256,7 @@ impl Deserializable for TransactionInputs { tx_args, advice_inputs, foreign_account_code, + asset_witnesses, }) } } diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index 9289a9e598..e3ccf6b1db 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -7,7 +7,7 @@ use miden_lib::transaction::TransactionKernel; use miden_objects::account::{Account, AccountId, PartialAccount, StorageMapWitness, StorageSlot}; use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; use miden_objects::assembly::{SourceManager, SourceManagerSync}; -use miden_objects::asset::{AssetVaultKey, AssetWitness}; +use miden_objects::asset::{Asset, AssetVaultKey, AssetWitness}; use miden_objects::block::{AccountWitness, BlockHeader, BlockNumber}; use miden_objects::note::{Note, NoteScript}; use miden_objects::transaction::{ @@ -77,7 +77,42 @@ impl TransactionContext { /// /// - If the provided `code` is not a valid program. pub async fn execute_code(&self, code: &str) -> Result { - let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&self.tx_inputs); + // Fetch all witnesses for note assets and the fee asset. + let mut asset_vault_keys = self + .tx_inputs + .input_notes() + .iter() + .flat_map(|note| note.note().assets().iter().map(Asset::vault_key)) + .collect::>(); + let fee_asset_vault_key = AssetVaultKey::from_account_id( + self.tx_inputs().block_header().fee_parameters().native_asset_id(), + ) + .expect("fee asset should be a fungible asset"); + asset_vault_keys.extend([fee_asset_vault_key]); + + let (account, block_header, _blockchain) = self + .get_transaction_inputs( + self.tx_inputs.account().id(), + BTreeSet::from_iter([self.tx_inputs.block_header().block_num()]), + ) + .await + .expect("failed to fetch transaction inputs"); + + // Add the vault key for the fee asset to the list of asset vault keys which may need to be + // accessed at the end of the transaction. + let fee_asset_vault_key = + AssetVaultKey::from_account_id(block_header.fee_parameters().native_asset_id()) + .expect("fee asset should be a fungible asset"); + asset_vault_keys.insert(fee_asset_vault_key); + + // Fetch the witnesses for all asset vault keys. + let asset_witnesses = self + .get_vault_asset_witnesses(account.id(), account.vault().root(), asset_vault_keys) + .await + .expect("failed to fetch asset witnesses"); + + let tx_inputs = self.tx_inputs.clone().with_asset_witnesses(asset_witnesses); + let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&tx_inputs); // Virtual file name should be unique. let virtual_source_file = self.source_manager.load( @@ -100,23 +135,26 @@ impl TransactionContext { self.mast_store.insert(program.mast_forest().clone()); let account_procedure_idx_map = AccountProcedureIndexMap::new( - [self.tx_inputs().account().code()] + [tx_inputs.account().code()] .into_iter() .chain(self.foreign_account_inputs.values().map(|(account, _)| account.code())), ) .expect("constructing account procedure index map should work"); // The ref block is unimportant when using execute_code so we can set it to any value. - let ref_block = self.tx_inputs().block_header().block_num(); + let ref_block = tx_inputs.block_header().block_num(); let exec_host = TransactionExecutorHost::<'_, '_, _, UnreachableAuth>::new( &PartialAccount::from(self.account()), - self.tx_inputs().input_notes().clone(), + tx_inputs.input_notes().clone(), self, ScriptMastForestStore::default(), account_procedure_idx_map, None, ref_block, + // We don't need to set the initial balance in this context under the assumption that + // fees are zero. + 0u64, self.source_manager(), ); @@ -189,15 +227,25 @@ impl DataStore for TransactionContext { fn get_transaction_inputs( &self, account_id: AccountId, - _ref_blocks: BTreeSet, + ref_blocks: BTreeSet, ) -> impl FutureMaybeSend> { + // Sanity checks assert_eq!(account_id, self.account().id()); assert_eq!(account_id, self.tx_inputs.account().id()); + assert_eq!( + ref_blocks + .last() + .copied() + .expect("at least the tx ref block should be provided"), + self.tx_inputs().blockchain().chain_length(), + "tx reference block should match partial blockchain length" + ); let account = self.tx_inputs.account().clone(); let block_header = self.tx_inputs.block_header().clone(); let blockchain = self.tx_inputs.blockchain().clone(); + async move { Ok((account, block_header, blockchain)) } } @@ -223,22 +271,21 @@ impl DataStore for TransactionContext { } } - fn get_vault_asset_witness( + fn get_vault_asset_witnesses( &self, account_id: AccountId, vault_root: Word, - asset_key: AssetVaultKey, - ) -> impl FutureMaybeSend> { + vault_keys: BTreeSet, + ) -> impl FutureMaybeSend, DataStoreError>> { async move { - if account_id == self.account().id() { + let asset_vault = if account_id == self.account().id() { if self.account().vault().root() != vault_root { return Err(DataStoreError::other(format!( "native account {account_id} has vault root {} but {vault_root} was requested", self.account().vault().root() ))); } - - Ok(self.account().vault().open(asset_key)) + self.account().vault() } else { let (foreign_account, _witness) = self .foreign_account_inputs @@ -260,9 +307,10 @@ impl DataStore for TransactionContext { foreign_account.vault().root() ))); } + foreign_account.vault() + }; - Ok(foreign_account.vault().open(asset_key)) - } + Ok(vault_keys.into_iter().map(|vault_key| asset_vault.open(vault_key)).collect()) } } diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 242a3e6915..e253ea5988 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -75,6 +75,10 @@ impl From for TransactionExecutorError { pub enum TransactionExecutorError { #[error("failed to fetch transaction inputs from the data store")] FetchTransactionInputsFailed(#[source] DataStoreError), + #[error("failed to fetch asset witnesses from the data store")] + FetchAssetWitnessFailed(#[source] DataStoreError), + #[error("fee asset must be fungible but was non-fungible")] + FeeAssetMustBeFungible, #[error("foreign account inputs for ID {0} are not anchored on reference block")] ForeignAccountNotAnchoredInReference(AccountId), #[error( diff --git a/crates/miden-tx/src/executor/data_store.rs b/crates/miden-tx/src/executor/data_store.rs index fa7e85091f..19fe34ba10 100644 --- a/crates/miden-tx/src/executor/data_store.rs +++ b/crates/miden-tx/src/executor/data_store.rs @@ -1,4 +1,5 @@ use alloc::collections::BTreeSet; +use alloc::vec::Vec; use miden_objects::account::{AccountId, PartialAccount, StorageMapWitness}; use miden_objects::asset::{AssetVaultKey, AssetWitness}; @@ -42,17 +43,17 @@ pub trait DataStore: MastForestStore { ref_block: BlockNumber, ) -> impl FutureMaybeSend>; - /// Returns a witness for an asset in the requested account's vault with the requested vault - /// root. + /// Returns witnesses for the asset vault keys in the requested account's vault with the + /// requested vault root. /// - /// This is the witness that needs to be added to the advice provider's merkle store and advice - /// map to make access to the specified asset possible. - fn get_vault_asset_witness( + /// These are the witnesses that need to be added to the advice provider's merkle store and + /// advice map to make access to the corresponding assets possible. + fn get_vault_asset_witnesses( &self, account_id: AccountId, vault_root: Word, - vault_key: AssetVaultKey, - ) -> impl FutureMaybeSend>; + vault_keys: BTreeSet, + ) -> impl FutureMaybeSend, DataStoreError>>; /// Returns a witness for a storage map item identified by `map_key` in the requested account's /// storage with the requested storage `map_root`. diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 0a7eafbf41..c68f7c83ad 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -1,5 +1,5 @@ use alloc::boxed::Box; -use alloc::collections::BTreeMap; +use alloc::collections::{BTreeMap, BTreeSet}; use alloc::sync::Arc; use alloc::vec::Vec; @@ -8,7 +8,7 @@ use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::account::{AccountCode, AccountDelta, AccountId, PartialAccount}; use miden_objects::assembly::debuginfo::Location; use miden_objects::assembly::{SourceFile, SourceManagerSync, SourceSpan}; -use miden_objects::asset::{Asset, AssetVaultKey, AssetWitness, FungibleAsset}; +use miden_objects::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; use miden_objects::block::BlockNumber; use miden_objects::crypto::merkle::SmtProof; use miden_objects::note::{NoteInputs, NoteMetadata, NoteRecipient}; @@ -79,6 +79,9 @@ where /// authenticator that produced it. generated_signatures: BTreeMap>, + /// The initial balance of the fee asset in the native account's vault. + initial_fee_asset_balance: u64, + /// The source manager to track source code file span information, improving any MASM related /// error messages. source_manager: Arc, @@ -93,6 +96,7 @@ where // -------------------------------------------------------------------------------------------- /// Creates a new [`TransactionExecutorHost`] instance from the provided inputs. + #[allow(clippy::too_many_arguments)] pub fn new( account: &PartialAccount, input_notes: InputNotes, @@ -101,6 +105,7 @@ where acct_procedure_index_map: AccountProcedureIndexMap, authenticator: Option<&'auth AUTH>, ref_block: BlockNumber, + initial_fee_asset_balance: u64, source_manager: Arc, ) -> Self { let base_host = TransactionBaseHost::new( @@ -118,6 +123,7 @@ where ref_block, accessed_foreign_account_code: Vec::new(), generated_signatures: BTreeMap::new(), + initial_fee_asset_balance, source_manager, } } @@ -204,32 +210,10 @@ where &self, fee_asset: FungibleAsset, ) -> Result, TransactionKernelError> { - let asset_witness = self - .base_host - .store() - .get_vault_asset_witness( - self.base_host.initial_account_header().id(), - self.base_host.initial_account_header().vault_root(), - fee_asset.vault_key(), - ) - .await - .map_err(|err| TransactionKernelError::GetVaultAssetWitness { - vault_root: self.base_host.initial_account_header().vault_root(), - asset_key: fee_asset.vault_key(), - source: err, - })?; - - // Find fee asset in the witness or default to 0 if it isn't present. - let initial_fee_asset = asset_witness - .find(fee_asset.vault_key()) - .and_then(|asset| match asset { - Asset::Fungible(fungible_asset) => Some(fungible_asset), - _ => None, - }) - .unwrap_or( - FungibleAsset::new(fee_asset.faucet_id(), 0) - .expect("fungible asset created from fee asset should be valid"), - ); + // Construct initial fee asset. + let initial_fee_asset = + FungibleAsset::new(fee_asset.faucet_id(), self.initial_fee_asset_balance) + .expect("fungible asset created from fee asset should be valid"); // Compute the current balance of the native asset in the account based on the initial value // and the delta. @@ -271,7 +255,7 @@ where }); } - Ok(asset_witness_to_advice_mutation(asset_witness)) + Ok(Vec::new()) } /// Handles a request for a storage map witness by querying the data store for a merkle path. @@ -346,10 +330,14 @@ where vault_root: Word, asset_key: AssetVaultKey, ) -> Result, TransactionKernelError> { - let asset_witness = self + let asset_witnesses = self .base_host .store() - .get_vault_asset_witness(active_account_id, vault_root, asset_key) + .get_vault_asset_witnesses( + active_account_id, + vault_root, + BTreeSet::from_iter([asset_key]), + ) .await .map_err(|err| TransactionKernelError::GetVaultAssetWitness { vault_root, @@ -357,7 +345,7 @@ where source: err, })?; - Ok(asset_witness_to_advice_mutation(asset_witness)) + Ok(asset_witnesses.into_iter().flat_map(asset_witness_to_advice_mutation).collect()) } /// Handles a request for a [`NoteScript`] by querying the [`DataStore`]. @@ -679,7 +667,7 @@ where /// Converts an [`AssetWitness`] into the set of advice mutations that need to be inserted in order /// to access the asset. -fn asset_witness_to_advice_mutation(asset_witness: AssetWitness) -> Vec { +fn asset_witness_to_advice_mutation(asset_witness: AssetWitness) -> [AdviceMutation; 2] { // Get the nodes in the proof and insert them into the merkle store. let merkle_store_ext = AdviceMutation::extend_merkle_store(asset_witness.authenticated_nodes()); @@ -689,5 +677,5 @@ fn asset_witness_to_advice_mutation(asset_witness: AssetWitness) -> Vec, tx_args: TransactionArgs, ) -> Result { - let mut ref_blocks = validate_input_notes(&input_notes, block_ref)?; + let (mut asset_vault_keys, mut ref_blocks) = validate_input_notes(&input_notes, block_ref)?; ref_blocks.insert(block_ref); let (account, block_header, blockchain) = self @@ -263,9 +263,24 @@ where .await .map_err(TransactionExecutorError::FetchTransactionInputsFailed)?; + // Add the vault key for the fee asset to the list of asset vault keys which will need to be + // accessed at the end of the transaction. + let fee_asset_vault_key = + AssetVaultKey::from_account_id(block_header.fee_parameters().native_asset_id()) + .expect("fee asset should be a fungible asset"); + asset_vault_keys.insert(fee_asset_vault_key); + + // Fetch the witnesses for all asset vault keys. + let asset_witnesses = self + .data_store + .get_vault_asset_witnesses(account_id, account.vault().root(), asset_vault_keys) + .await + .map_err(TransactionExecutorError::FetchAssetWitnessFailed)?; + let tx_inputs = TransactionInputs::new(account, block_header, blockchain, input_notes) .map_err(TransactionExecutorError::InvalidTransactionInputs)? - .with_tx_args(tx_args); + .with_tx_args(tx_args) + .with_asset_witnesses(asset_witnesses); Ok(tx_inputs) } @@ -303,6 +318,26 @@ where AccountProcedureIndexMap::new([tx_inputs.account().code()]) .map_err(TransactionExecutorError::TransactionHostCreationFailed)?; + let initial_fee_asset_balance = { + let native_asset_id = tx_inputs.block_header().fee_parameters().native_asset_id(); + let fee_asset_vault_key = AssetVaultKey::from_account_id(native_asset_id) + .expect("fee asset should be a fungible asset"); + + let fee_asset_witness = tx_inputs + .asset_witnesses() + .iter() + .find_map(|witness| witness.find(fee_asset_vault_key)); + + match fee_asset_witness { + Some(Asset::Fungible(fee_asset)) => fee_asset.amount(), + Some(Asset::NonFungible(_)) => { + return Err(TransactionExecutorError::FeeAssetMustBeFungible); + }, + // If the witness does not contain the asset, its balance is zero. + None => 0, + } + }; + let host = TransactionExecutorHost::new( tx_inputs.account(), input_notes.clone(), @@ -311,6 +346,7 @@ where account_procedure_index_map, self.authenticator, tx_inputs.block_header().block_num(), + initial_fee_asset_balance, self.source_manager.clone(), ); @@ -398,27 +434,36 @@ fn build_executed_transaction, block_ref: BlockNumber, -) -> Result, TransactionExecutorError> { - // Validate that notes were not created after the reference, and build the set of required - // block numbers +) -> Result<(BTreeSet, BTreeSet), TransactionExecutorError> { let mut ref_blocks: BTreeSet = BTreeSet::new(); - for note in notes.iter() { - if let Some(location) = note.location() { + let mut asset_vault_keys: BTreeSet = BTreeSet::new(); + + for input_note in notes.iter() { + // Validate that notes were not created after the reference, and build the set of required + // block numbers + if let Some(location) = input_note.location() { if location.block_num() > block_ref { return Err(TransactionExecutorError::NoteBlockPastReferenceBlock( - note.id(), + input_note.id(), block_ref, )); } ref_blocks.insert(location.block_num()); } + + asset_vault_keys.extend(input_note.note().assets().iter().map(Asset::vault_key)); } - Ok(ref_blocks) + Ok((asset_vault_keys, ref_blocks)) } /// Validates that the number of cycles specified is within the allowed range. From 0cd80afdbd9b9d26e27341fad895063ac53eacdc Mon Sep 17 00:00:00 2001 From: Bernhard Schuster Date: Mon, 1 Dec 2025 09:54:29 +0100 Subject: [PATCH 020/114] fix: expose `with_backend_from_entries` for `LargeSmt` backed `NullifierTree`s (#2110) --- .../miden-objects/src/block/nullifier_tree.rs | 31 +++++++++++++++++++ crates/miden-objects/src/errors.rs | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/crates/miden-objects/src/block/nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree.rs index 9c7bd70e9d..9464231263 100644 --- a/crates/miden-objects/src/block/nullifier_tree.rs +++ b/crates/miden-objects/src/block/nullifier_tree.rs @@ -387,6 +387,37 @@ impl NullifierTree { } } +#[cfg(feature = "std")] +impl NullifierTree> +where + Backend: SmtStorage, +{ + /// Creates a new nullifier tree from the provided entries using the given storage backend + /// + /// This is a convenience method that creates an SMT on the provided storage backend using the + /// provided entries and wraps it in a NullifierTree. + /// + /// # Errors + /// + /// Returns an error if: + /// - the provided entries contain multiple block numbers for the same nullifier. + /// - a storage error is encountered. + pub fn with_storage_from_entries( + storage: Backend, + entries: impl IntoIterator, + ) -> Result { + let leaves = entries.into_iter().map(|(nullifier, block_num)| { + (nullifier.as_word(), block_num_to_nullifier_leaf_value(block_num)) + }); + + let smt = LargeSmt::::with_entries(storage, leaves) + .map_err(large_smt_error_to_merkle_error) + .map_err(NullifierTreeError::DuplicateNullifierBlockNumbers)?; + + Ok(Self::new_unchecked(smt)) + } +} + // SERIALIZATION // ================================================================================================ diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index a11d45d421..077aac0bc2 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -1083,7 +1083,7 @@ pub enum NullifierTreeError { #[error("new tree root after nullifier witness insertion does not match previous tree root")] TreeRootConflict(#[source] MerkleError), - #[error("failed to compute nulifier tree mutations")] + #[error("failed to compute nullifier tree mutations")] ComputeMutations(#[source] MerkleError), } From 41c14bef00f8206af7c2c0df5b8d774793d9ba01 Mon Sep 17 00:00:00 2001 From: Bernhard Schuster Date: Tue, 2 Dec 2025 17:16:45 +0100 Subject: [PATCH 021/114] feat: add proc-macro `WordWrapper` (#2108) --- CHANGELOG.md | 2 + Cargo.lock | 11 ++ Cargo.toml | 2 + crates/miden-objects/Cargo.toml | 1 + crates/miden-objects/src/batch/batch_id.rs | 19 +- .../miden-objects/src/block/nullifier_tree.rs | 5 +- crates/miden-objects/src/note/note_id.rs | 59 +----- crates/miden-objects/src/note/nullifier.rs | 53 +----- .../src/transaction/transaction_id.rs | 57 +----- crates/miden-protocol-macros/Cargo.toml | 27 +++ crates/miden-protocol-macros/README.md | 69 +++++++ crates/miden-protocol-macros/src/lib.rs | 171 ++++++++++++++++++ .../tests/integration_test.rs | 42 +++++ .../src/kernel_tests/tx/test_epilogue.rs | 2 +- crates/miden-tx/src/host/kernel_process.rs | 2 +- 15 files changed, 344 insertions(+), 178 deletions(-) create mode 100644 crates/miden-protocol-macros/Cargo.toml create mode 100644 crates/miden-protocol-macros/README.md create mode 100644 crates/miden-protocol-macros/src/lib.rs create mode 100644 crates/miden-protocol-macros/tests/integration_test.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 78edb303c2..6645c85258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Features +- Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). + ### Changes - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). diff --git a/Cargo.lock b/Cargo.lock index 20d5acd3d2..dde15ee20d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1628,6 +1628,7 @@ dependencies = [ "miden-mast-package", "miden-objects", "miden-processor", + "miden-protocol-macros", "miden-stdlib", "miden-utils-sync", "miden-verifier", @@ -1664,6 +1665,16 @@ dependencies = [ "winter-prover", ] +[[package]] +name = "miden-protocol-macros" +version = "0.13.0" +dependencies = [ + "miden-objects", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "miden-prover" version = "0.19.1" diff --git a/Cargo.toml b/Cargo.toml index 552339a08c..fc5e3dca8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "crates/miden-block-prover", "crates/miden-lib", "crates/miden-objects", + "crates/miden-protocol-macros", "crates/miden-testing", "crates/miden-tx", "crates/miden-tx-batch-prover", @@ -43,6 +44,7 @@ lto = true miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.13" } miden-lib = { default-features = false, path = "crates/miden-lib", version = "0.13" } miden-objects = { default-features = false, path = "crates/miden-objects", version = "0.13" } +miden-protocol-macros = { default-features = false, path = "crates/miden-protocol-macros", version = "0.13" } miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.13" } miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.13" } miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.13" } diff --git a/crates/miden-objects/Cargo.toml b/crates/miden-objects/Cargo.toml index cb4e8a3bad..d3abe589dc 100644 --- a/crates/miden-objects/Cargo.toml +++ b/crates/miden-objects/Cargo.toml @@ -41,6 +41,7 @@ miden-core = { workspace = true } miden-crypto = { workspace = true } miden-mast-package = { workspace = true } miden-processor = { workspace = true } +miden-protocol-macros = { workspace = true } miden-stdlib = { workspace = true } miden-utils-sync = { workspace = true } miden-verifier = { workspace = true } diff --git a/crates/miden-objects/src/batch/batch_id.rs b/crates/miden-objects/src/batch/batch_id.rs index 45d3cbc97a..bcbb50e16a 100644 --- a/crates/miden-objects/src/batch/batch_id.rs +++ b/crates/miden-objects/src/batch/batch_id.rs @@ -1,6 +1,8 @@ use alloc::string::String; use alloc::vec::Vec; +use miden_protocol_macros::WordWrapper; + use crate::account::AccountId; use crate::transaction::{ProvenTransaction, TransactionId}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -15,7 +17,7 @@ use crate::{Felt, Hasher, Word, ZERO}; /// This is a sequential hash of the tuple `(TRANSACTION_ID || [account_id_prefix, /// account_id_suffix, 0, 0])` of all transactions and the accounts their executed against in the /// batch. -#[derive(Debug, Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)] +#[derive(Debug, Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Hash, WordWrapper)] pub struct BatchId(Word); impl BatchId { @@ -38,21 +40,6 @@ impl BatchId { Self(Hasher::hash_elements(&elements)) } - - /// Returns the elements representation of this batch ID. - pub fn as_elements(&self) -> &[Felt] { - self.0.as_elements() - } - - /// Returns the byte representation of this batch ID. - pub fn as_bytes(&self) -> [u8; 32] { - self.0.as_bytes() - } - - /// Returns a big-endian, hex-encoded string. - pub fn to_hex(&self) -> String { - self.0.to_hex() - } } impl core::fmt::Display for BatchId { diff --git a/crates/miden-objects/src/block/nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree.rs index 9464231263..bf12264eb8 100644 --- a/crates/miden-objects/src/block/nullifier_tree.rs +++ b/crates/miden-objects/src/block/nullifier_tree.rs @@ -257,7 +257,10 @@ where /// Returns an iterator over the nullifiers and their block numbers in the tree. pub fn entries(&self) -> impl Iterator { self.smt.entries().map(|(nullifier, block_num)| { - (Nullifier::from(nullifier), nullifier_leaf_value_to_block_num(block_num)) + ( + Nullifier::new_unchecked(nullifier), + nullifier_leaf_value_to_block_num(block_num), + ) }) } diff --git a/crates/miden-objects/src/note/note_id.rs b/crates/miden-objects/src/note/note_id.rs index 96e5fab8fc..1a1e2caa45 100644 --- a/crates/miden-objects/src/note/note_id.rs +++ b/crates/miden-objects/src/note/note_id.rs @@ -1,6 +1,8 @@ use alloc::string::String; use core::fmt::Display; +use miden_protocol_macros::WordWrapper; + use super::{Felt, Hasher, NoteDetails, Word}; use crate::WordError; use crate::utils::serde::{ @@ -28,7 +30,7 @@ use crate::utils::serde::{ /// - Every note can be reduced to a single unique ID. /// - To compute a note ID, we do not need to know the note's serial_num. Knowing the hash of the /// serial_num (as well as script root, input commitment, and note assets) is sufficient. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, WordWrapper)] pub struct NoteId(Word); impl NoteId { @@ -36,26 +38,6 @@ impl NoteId { pub fn new(recipient: Word, asset_commitment: Word) -> Self { Self(Hasher::merge(&[recipient, asset_commitment])) } - - /// Returns the elements representation of this note ID. - pub fn as_elements(&self) -> &[Felt] { - self.0.as_elements() - } - - /// Returns the byte representation of this note ID. - pub fn as_bytes(&self) -> [u8; 32] { - self.0.as_bytes() - } - - /// Returns a big-endian, hex-encoded string. - pub fn to_hex(&self) -> String { - self.0.to_hex() - } - - /// Returns the digest defining this note ID. - pub fn as_word(&self) -> Word { - self.0 - } } impl Display for NoteId { @@ -73,43 +55,10 @@ impl From<&NoteDetails> for NoteId { } } -impl From for NoteId { - fn from(digest: Word) -> Self { - Self(digest) - } -} - impl NoteId { /// Attempts to convert from a hexadecimal string to [NoteId]. pub fn try_from_hex(hex_value: &str) -> Result { - Word::try_from(hex_value).map(NoteId::from) - } -} - -// CONVERSIONS FROM NOTE ID -// ================================================================================================ - -impl From for Word { - fn from(id: NoteId) -> Self { - id.as_word() - } -} - -impl From for [u8; 32] { - fn from(id: NoteId) -> Self { - id.0.into() - } -} - -impl From<&NoteId> for Word { - fn from(id: &NoteId) -> Self { - id.0 - } -} - -impl From<&NoteId> for [u8; 32] { - fn from(id: &NoteId) -> Self { - id.0.into() + Word::try_from(hex_value).map(NoteId::new_unchecked) } } diff --git a/crates/miden-objects/src/note/nullifier.rs b/crates/miden-objects/src/note/nullifier.rs index 8f4f3704e7..05a54fc8c7 100644 --- a/crates/miden-objects/src/note/nullifier.rs +++ b/crates/miden-objects/src/note/nullifier.rs @@ -2,6 +2,7 @@ use alloc::string::String; use core::fmt::{Debug, Display, Formatter}; use miden_crypto::WordError; +use miden_protocol_macros::WordWrapper; use super::{ ByteReader, @@ -36,7 +37,7 @@ const NULLIFIER_PREFIX_SHIFT: u8 = 48; /// - We cannot derive a note's commitment from its nullifier, or a note's nullifier from its hash. /// - To compute the nullifier we must know all components of the note: serial_num, script_root, /// input_commitment and asset_commitment. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, WordWrapper)] pub struct Nullifier(Word); impl Nullifier { @@ -55,21 +56,11 @@ impl Nullifier { Self(Hasher::hash_elements(&elements)) } - /// Returns the elements of this nullifier. - pub fn as_elements(&self) -> &[Felt] { - self.0.as_elements() - } - /// Returns the most significant felt (the last element in array) pub fn most_significant_felt(&self) -> Felt { self.as_elements()[3] } - /// Returns the digest defining this nullifier. - pub fn as_word(&self) -> Word { - self.0 - } - /// Returns the prefix of this nullifier. /// /// Nullifier prefix is defined as the 16 most significant bits of the nullifier value. @@ -80,12 +71,7 @@ impl Nullifier { /// Creates a Nullifier from a hex string. Assumes that the string starts with "0x" and /// that the hexadecimal characters are big-endian encoded. pub fn from_hex(hex_value: &str) -> Result { - Word::try_from(hex_value).map(Self::from) - } - - /// Returns a big-endian, hex-encoded string. - pub fn to_hex(&self) -> String { - self.0.to_hex() + Word::try_from(hex_value).map(Self::new_unchecked) } #[cfg(any(feature = "testing", test))] @@ -122,39 +108,6 @@ impl From<&NoteDetails> for Nullifier { } } -impl From for Nullifier { - fn from(digest: Word) -> Self { - Self(digest) - } -} - -// CONVERSIONS FROM NULLIFIER -// ================================================================================================ - -impl From for Word { - fn from(nullifier: Nullifier) -> Self { - nullifier.0 - } -} - -impl From for [u8; 32] { - fn from(nullifier: Nullifier) -> Self { - nullifier.0.into() - } -} - -impl From<&Nullifier> for Word { - fn from(nullifier: &Nullifier) -> Self { - nullifier.0 - } -} - -impl From<&Nullifier> for [u8; 32] { - fn from(nullifier: &Nullifier) -> Self { - nullifier.0.into() - } -} - // SERIALIZATION // ================================================================================================ diff --git a/crates/miden-objects/src/transaction/transaction_id.rs b/crates/miden-objects/src/transaction/transaction_id.rs index 8a8648af61..e75f7662f7 100644 --- a/crates/miden-objects/src/transaction/transaction_id.rs +++ b/crates/miden-objects/src/transaction/transaction_id.rs @@ -1,6 +1,8 @@ use alloc::string::String; use core::fmt::{Debug, Display}; +use miden_protocol_macros::WordWrapper; + use super::{Felt, Hasher, ProvenTransaction, WORD_SIZE, Word, ZERO}; use crate::utils::serde::{ ByteReader, @@ -23,7 +25,7 @@ use crate::utils::serde::{ /// This achieves the following properties: /// - Transactions are identical if and only if they have the same ID. /// - Computing transaction ID can be done solely from public transaction data. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, WordWrapper)] pub struct TransactionId(Word); impl TransactionId { @@ -41,26 +43,6 @@ impl TransactionId { elements[12..].copy_from_slice(output_notes_commitment.as_elements()); Self(Hasher::hash_elements(&elements)) } - - /// Returns the elements representation of this transaction ID. - pub fn as_elements(&self) -> &[Felt] { - self.0.as_elements() - } - - /// Returns the byte representation of this transaction ID. - pub fn as_bytes(&self) -> [u8; 32] { - self.0.as_bytes() - } - - /// Returns a big-endian, hex-encoded string. - pub fn to_hex(&self) -> String { - self.0.to_hex() - } - - /// Returns the digest defining this transaction ID. - pub fn as_word(&self) -> Word { - self.0 - } } impl Debug for TransactionId { @@ -89,39 +71,6 @@ impl From<&ProvenTransaction> for TransactionId { } } -impl From for TransactionId { - fn from(digest: Word) -> Self { - Self(digest) - } -} - -// CONVERSIONS FROM TRANSACTION ID -// ================================================================================================ - -impl From for Word { - fn from(id: TransactionId) -> Self { - id.0 - } -} - -impl From for [u8; 32] { - fn from(id: TransactionId) -> Self { - id.0.into() - } -} - -impl From<&TransactionId> for Word { - fn from(id: &TransactionId) -> Self { - id.0 - } -} - -impl From<&TransactionId> for [u8; 32] { - fn from(id: &TransactionId) -> Self { - id.0.into() - } -} - // SERIALIZATION // ================================================================================================ diff --git a/crates/miden-protocol-macros/Cargo.toml b/crates/miden-protocol-macros/Cargo.toml new file mode 100644 index 0000000000..030b01a60a --- /dev/null +++ b/crates/miden-protocol-macros/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors.workspace = true +categories = ["development-tools::procedural-macro-helpers"] +description = "Procedural macros for Miden protocol" +edition.workspace = true +homepage.workspace = true +keywords = ["macros", "miden", "protocol"] +license.workspace = true +name = "miden-protocol-macros" +readme = "README.md" +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { features = ["extra-traits", "full"], version = "2.0" } + +[dev-dependencies] +miden-objects = { path = "../miden-objects" } + +[package.metadata.cargo-machete] +ignored = ["proc-macro2"] diff --git a/crates/miden-protocol-macros/README.md b/crates/miden-protocol-macros/README.md new file mode 100644 index 0000000000..e0d2257d11 --- /dev/null +++ b/crates/miden-protocol-macros/README.md @@ -0,0 +1,69 @@ +# miden-protocol-macros + +A collection of procedural macros for the Miden protocol. + +## WordWrapper + +The `WordWrapper` derive macro automatically implements helpful accessor methods and conversions for tuple structs that wrap a `Word` type. + +### Usage + +Add the derive macro to any tuple struct with a single `Word` field: + +```rust +use miden_protocol_macros::WordWrapper; +use miden_crypto::word::Word; + +#[derive(WordWrapper)] +pub struct NoteId(Word); +``` + +### Generated Methods + +The macro automatically generates the following methods: + +#### Accessor Methods + +- **`new_unchecked(Word) -> Self`** - Construct without any checks +- **`as_elements(&self) -> &[Felt]`** - Returns the elements representation of the wrapped Word +- **`as_bytes(&self) -> [u8; 32]`** - Returns the byte representation +- **`to_hex(&self) -> String`** - Returns a big-endian, hex-encoded string +- **`as_word(&self) -> Word`** - Returns the underlying Word value + +### Example + +```rust +use miden_protocol_macros::WordWrapper; +use miden_crypto::word::Word; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, WordWrapper)] +pub struct NoteId(Word); + +// Create using new_unchecked (generated by the macro) +let word = Word::from([Felt::ONE, Felt::ZERO, Felt::ONE, Felt::ZERO]); +let note_id = NoteId::new_unchecked(word); + +// Use accessor methods +let elements = note_id.as_elements(); +let bytes = note_id.as_bytes(); +let hex = note_id.to_hex(); +let word_back = note_id.as_word(); +``` + +### Requirements + +The macro can only be applied to: +- Tuple structs (e.g., `struct Foo(Word)`) +- With exactly one field +- Where that field is of type `Word` + +### Benefits + +Using this macro eliminates boilerplate code. Instead of manually writing ~50 lines of implementation code for each Word wrapper type, you can simply add `#[derive(WordWrapper)]` to your struct definition. + +This is particularly useful in the Miden codebase where many types like `NoteId`, `TransactionId`, `Nullifier`, `BatchId`, etc. all follow the same pattern of wrapping a `Word` and providing similar accessor methods. + +### Important Notes + +- The macro generates the `new_unchecked` constructor. You should not manually implement this method. +- Previously, the macro also generated `From` and `From<&T>` trait implementations for `Word` and `[u8; 32]`. These have been **removed** to give types more control over their conversions. If you need these conversions, implement them manually for your specific type. diff --git a/crates/miden-protocol-macros/src/lib.rs b/crates/miden-protocol-macros/src/lib.rs new file mode 100644 index 0000000000..3431d38791 --- /dev/null +++ b/crates/miden-protocol-macros/src/lib.rs @@ -0,0 +1,171 @@ +//! Procedural macros for the Miden project. +//! +//! Provides derive macros and other procedural macros to reduce boilerplate +//! and ensure consistency across the Miden codebase. +//! +//! ## Available Macros +//! +//! ### `WordWrapper` +//! +//! A derive macro for tuple structs wrapping a `Word` type. Automatically generates +//! accessor methods and `From` trait implementations. + +use proc_macro::TokenStream; +use quote::quote; +use syn::{Data, DeriveInput, Fields, Type, parse_macro_input}; + +/// Generates accessor methods for tuple structs wrapping a `Word` type. +/// +/// Automatically implements: +/// - `new_unchecked(Word) -> Self` - Construct without further checks +/// - `as_elements(&self) -> &[Felt]` - Returns the elements representation +/// - `as_bytes(&self) -> [u8; 32]` - Returns the byte representation +/// - `to_hex(&self) -> String` - Returns a big-endian, hex-encoded string +/// - `as_word(&self) -> Word` - Returns the underlying Word +/// +/// Note: This macro does NOT generate `From` trait implementations. If you need conversions +/// to/from `Word` or `[u8; 32]`, implement them manually for your type. +/// +/// # Example +/// +/// ```ignore +/// use miden_protocol_macros::WordWrapper; +/// use miden_crypto::word::Word; +/// +/// #[derive(WordWrapper)] +/// pub struct NoteId(Word); +/// ``` +/// +/// This will generate implementations equivalent to: +/// +/// ```ignore +/// impl NoteId { +/// /// Construct without further checks from a given `Word` +/// /// +/// /// # Warning +/// /// +/// /// This requires the caller to uphold the guarantees/invariants of this type (if any). +/// /// Check the type-level documentation for guarantees/invariants. +/// pub fn new_unchecked(word: Word) -> Self { +/// Self(word) +/// } +/// +/// pub fn as_elements(&self) -> &[Felt] { +/// self.0.as_elements() +/// } +/// +/// pub fn as_bytes(&self) -> [u8; 32] { +/// self.0.as_bytes() +/// } +/// +/// pub fn to_hex(&self) -> String { +/// self.0.to_hex() +/// } +/// +/// pub fn as_word(&self) -> Word { +/// self.0 +/// } +/// } +/// ``` +#[proc_macro_derive(WordWrapper)] +pub fn word_wrapper_derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + // Validate that this is a tuple struct with a single field + let field_type = match &input.data { + Data::Struct(data_struct) => match &data_struct.fields { + Fields::Unnamed(fields) if fields.unnamed.len() == 1 => match fields.unnamed.first() { + Some(field) => &field.ty, + None => { + return syn::Error::new_spanned( + &input, + "WordWrapper requires exactly one field", + ) + .to_compile_error() + .into(); + }, + }, + _ => { + return syn::Error::new_spanned( + &input, + "WordWrapper can only be derived for tuple structs with exactly one field", + ) + .to_compile_error() + .into(); + }, + }, + _ => { + return syn::Error::new_spanned(&input, "WordWrapper can only be derived for structs") + .to_compile_error() + .into(); + }, + }; + + // Verify that the field type is 'Word' (or a path ending in 'Word') + if let Type::Path(type_path) = field_type { + let last_segment = type_path.path.segments.last(); + if let Some(segment) = last_segment { + if segment.ident != "Word" { + return syn::Error::new_spanned( + field_type, + "WordWrapper can only be derived for types wrapping a 'Word' field", + ) + .to_compile_error() + .into(); + } + } else { + return syn::Error::new_spanned( + field_type, + "WordWrapper can only be derived for types wrapping a 'Word' field", + ) + .to_compile_error() + .into(); + } + } else { + return syn::Error::new_spanned( + field_type, + "WordWrapper can only be derived for types wrapping a 'Word' field", + ) + .to_compile_error() + .into(); + } + + let expanded = quote! { + impl #impl_generics #name #ty_generics #where_clause { + /// Construct without further checks from a given `Word` + /// + /// # Warning + /// + /// This requires the caller to uphold the guarantees/invariants of this type (if any). + /// Check the type-level documentation for guarantees/invariants. + pub fn new_unchecked(word: Word) -> Self { + Self(word) + } + + /// Returns the elements representation of this value. + pub fn as_elements(&self) -> &[Felt] { + self.0.as_elements() + } + + /// Returns the byte representation of this value. + pub fn as_bytes(&self) -> [u8; 32] { + self.0.as_bytes() + } + + /// Returns a big-endian, hex-encoded string. + pub fn to_hex(&self) -> String { + self.0.to_hex() + } + + /// Returns the underlying word of this value. + pub fn as_word(&self) -> Word { + self.0 + } + } + }; + + TokenStream::from(expanded) +} diff --git a/crates/miden-protocol-macros/tests/integration_test.rs b/crates/miden-protocol-macros/tests/integration_test.rs new file mode 100644 index 0000000000..bcc9f3869b --- /dev/null +++ b/crates/miden-protocol-macros/tests/integration_test.rs @@ -0,0 +1,42 @@ +#[cfg(test)] +mod tests { + use miden_objects::{Felt, FieldElement, Word}; + use miden_protocol_macros::WordWrapper; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, WordWrapper)] + pub struct TestId(Word); + + #[test] + fn test_word_wrapper_accessors() { + // Create a test Word + let word = Word::from([Felt::ONE, Felt::ONE, Felt::ZERO, Felt::ZERO]); + // Use the new_unchecked method generated by the macro + let test_id = TestId::new_unchecked(word); + + // Test as_elements + let elements = test_id.as_elements(); + assert_eq!(elements.len(), 4); + assert_eq!(elements[0], Felt::ONE); + assert_eq!(elements[1], Felt::ONE); + + // Test as_bytes + let bytes = test_id.as_bytes(); + assert_eq!(bytes.len(), 32); + + // Test to_hex + let hex = test_id.to_hex(); + assert!(!hex.is_empty()); + + // Test as_word + let retrieved_word = test_id.as_word(); + assert_eq!(retrieved_word, word); + } + + #[test] + fn test_new_unchecked_is_generated() { + // This test verifies that new_unchecked is generated by the macro + let word = Word::from([Felt::ONE, Felt::ONE, Felt::ZERO, Felt::ZERO]); + let test_id = TestId::new_unchecked(word); + assert_eq!(test_id.as_word(), word); + } +} diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 9b778fd556..e34f058e7d 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -197,7 +197,7 @@ async fn test_compute_output_note_id() -> anyhow::Result<()> { ); assert_eq!( - Word::from(note.id()), + note.id().as_word(), exec_output.get_kernel_mem_word(OUTPUT_NOTE_SECTION_OFFSET + i * NOTE_MEM_SIZE), "NOTE_ID didn't match expected value", ); diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 289cc8fabc..3762254e8b 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -150,7 +150,7 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { ExecutionError::MemoryError(err), ) })? - .map(NoteId::from)) + .map(NoteId::new_unchecked)) } } From 15e787d2c9ee4744be2fedbc5cdd10abf639a5cb Mon Sep 17 00:00:00 2001 From: Developer Uche <69772615+developeruche@users.noreply.github.com> Date: Tue, 2 Dec 2025 19:27:55 +0100 Subject: [PATCH 022/114] chore: introduced `cargo binstall` for installing `cargo-msrv` (#2089) --- .github/workflows/msrv.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index 2f39de1937..60b81bfad6 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -24,8 +24,11 @@ jobs: run: sudo apt-get update && sudo apt-get install -y jq - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: cargo-binstall - name: Install cargo-msrv - run: cargo install cargo-msrv + run: cargo binstall --no-confirm cargo-msrv - name: Check MSRV for each workspace member run: | ./scripts/check-msrv.sh From 80b8dfa5cec56861749f1012c16e57be53885978 Mon Sep 17 00:00:00 2001 From: igamigo Date: Wed, 3 Dec 2025 22:07:43 -0300 Subject: [PATCH 023/114] refactor: remove component templates in favor of package (#2127) --- CHANGELOG.md | 1 + .../src/account/component/mod.rs | 136 +++++++++--------- .../src/account/component/template/mod.rs | 135 +++++++---------- .../template/storage/init_storage_data.rs | 4 +- .../account/component/template/storage/mod.rs | 25 ++-- .../component/template/storage/placeholder.rs | 2 +- crates/miden-objects/src/account/mod.rs | 1 - 7 files changed, 134 insertions(+), 170 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6645c85258..0ddaeb46da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). - Split tx progress events out into a separate enum ([#2103](https://github.com/0xMiden/miden-base/pull/2103)). - Added `note::get_network_account_tag` procedure ([#2120](https://github.com/0xMiden/miden-base/pull/2120)). +- [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-objects/src/account/component/mod.rs b/crates/miden-objects/src/account/component/mod.rs index 22874bc06e..3c607cbaaf 100644 --- a/crates/miden-objects/src/account/component/mod.rs +++ b/crates/miden-objects/src/account/component/mod.rs @@ -4,7 +4,7 @@ use alloc::vec::Vec; use miden_assembly::ast::QualifiedProcedureName; use miden_assembly::{Assembler, Library, Parse}; use miden_core::utils::Deserializable; -use miden_mast_package::{Package, SectionId}; +use miden_mast_package::{MastArtifact, Package, SectionId}; use miden_processor::MastForest; mod template; @@ -16,25 +16,21 @@ use crate::{AccountError, Word}; // IMPLEMENTATIONS // ================================================================================================ -impl TryFrom for AccountComponentTemplate { +impl TryFrom<&Package> for AccountComponentMetadata { type Error = AccountError; - fn try_from(package: Package) -> Result { - let library = package.unwrap_library().as_ref().clone(); - - // Look for account component metadata in sections - let metadata = package + fn try_from(package: &Package) -> Result { + package .sections .iter() .find_map(|section| { (section.id == SectionId::ACCOUNT_COMPONENT_METADATA).then(|| { - AccountComponentMetadata::read_from_bytes(§ion.data) - .map_err(|err| { - AccountError::other_with_source( - "failed to deserialize account component metadata", - err, - ) - }) + AccountComponentMetadata::read_from_bytes(§ion.data).map_err(|err| { + AccountError::other_with_source( + "failed to deserialize account component metadata", + err, + ) + }) }) }) .transpose()? @@ -42,9 +38,7 @@ impl TryFrom for AccountComponentTemplate { AccountError::other( "package does not contain account component metadata section - packages without explicit metadata may be intended for other purposes (e.g., note scripts, transaction scripts)", ) - })?; - - Ok(AccountComponentTemplate::new(metadata, library)) + }) } } @@ -123,55 +117,76 @@ impl AccountComponent { Self::new(library, storage_slots) } - /// Instantiates an [AccountComponent] from the [AccountComponentTemplate]. + /// Creates an [`AccountComponent`] from a [`Package`] using [`InitStorageData`]. /// - /// The template's component metadata might contain placeholders, which can be replaced by - /// mapping storage placeholders to values through the `init_storage_data` parameter. + /// This method provides type safety by leveraging the component's metadata to validate + /// storage initialization data. The package must contain explicit account component metadata. + /// + /// # Arguments + /// + /// * `package` - The package containing the library and account component metadata + /// * `init_storage_data` - The initialization data for storage slots /// /// # Errors /// - /// - If any of the component's storage entries cannot be transformed into a valid storage slot. - /// This could be because the metadata is invalid, or storage values were not provided (or - /// they are not of a valid type) - pub fn from_template( - template: &AccountComponentTemplate, + /// Returns an error if: + /// - The package does not contain a library artifact + /// - The package does not contain account component metadata + /// - The metadata cannot be deserialized from the package + /// - The storage initialization fails due to invalid or missing data + /// - The component creation fails + pub fn from_package( + package: &Package, init_storage_data: &InitStorageData, - ) -> Result { - let mut storage_slots = vec![]; - for storage_entry in template.metadata().storage_entries() { - let entry_storage_slots = storage_entry - .try_build_storage_slots(init_storage_data) - .map_err(AccountError::AccountComponentTemplateInstantiationError)?; - storage_slots.extend(entry_storage_slots); - } + ) -> Result { + let metadata = AccountComponentMetadata::try_from(package)?; + let library = match &package.mast { + MastArtifact::Library(library) => library.as_ref().clone(), + MastArtifact::Executable(_) => { + return Err(AccountError::other( + "expected Package to contain a library, but got an executable", + )); + }, + }; - Ok(AccountComponent::new(template.library().clone(), storage_slots)? - .with_supported_types(template.metadata().supported_types().clone())) + AccountComponent::from_library(&library, &metadata, init_storage_data) } - /// Creates an [`AccountComponent`] from a [`Package`] using [`InitStorageData`]. + /// Creates an [`AccountComponent`] from a [`Library`] and [`AccountComponentMetadata`]. /// /// This method provides type safety by leveraging the component's metadata to validate - /// storage initialization data. The package must contain explicit account component metadata. + /// the passed storage initialization data ([`InitStorageData`]). /// /// # Arguments /// - /// * `package` - The package containing the library and account component metadata + /// * `library` - The component's assembled code + /// * `account_component_metadata` - The component's metadata, which describes the storage + /// layout /// * `init_storage_data` - The initialization data for storage slots /// /// # Errors /// /// Returns an error if: + /// - The package does not contain a library artifact /// - The package does not contain account component metadata - /// - The package cannot be converted to an [`AccountComponentTemplate`] + /// - The metadata cannot be deserialized from the package /// - The storage initialization fails due to invalid or missing data /// - The component creation fails - pub fn from_package_with_init_data( - package: &Package, + pub fn from_library( + library: &Library, + account_component_metadata: &AccountComponentMetadata, init_storage_data: &InitStorageData, ) -> Result { - let template = AccountComponentTemplate::try_from(package.clone())?; - Self::from_template(&template, init_storage_data) + let mut storage_slots = vec![]; + for storage_entry in account_component_metadata.storage_entries() { + let entry_storage_slots = storage_entry + .try_build_storage_slots(init_storage_data) + .map_err(AccountError::AccountComponentTemplateInstantiationError)?; + storage_slots.extend(entry_storage_slots); + } + + Ok(AccountComponent::new(library.clone(), storage_slots)? + .with_supported_types(account_component_metadata.supported_types().clone())) } // ACCESSORS @@ -283,7 +298,7 @@ mod tests { use crate::testing::account_code::CODE; #[test] - fn test_try_from_package_for_template() { + fn test_extract_metadata_from_package() { // Create a simple library for testing let library = Assembler::default().assemble_library([CODE]).unwrap(); @@ -311,11 +326,11 @@ mod tests { description: None, }; - let template = AccountComponentTemplate::try_from(package_with_metadata).unwrap(); - assert_eq!(template.metadata().name(), "test_component"); + let extracted_metadata = + AccountComponentMetadata::try_from(&package_with_metadata).unwrap(); + assert_eq!(extracted_metadata.name(), "test_component"); assert!( - template - .metadata() + extracted_metadata .supported_types() .contains(&AccountType::RegularAccountImmutableCode) ); @@ -330,14 +345,14 @@ mod tests { description: None, }; - let result = AccountComponentTemplate::try_from(package_without_metadata); + let result = AccountComponentMetadata::try_from(&package_without_metadata); assert!(result.is_err()); let error_msg = result.unwrap_err().to_string(); assert!(error_msg.contains("package does not contain account component metadata")); } #[test] - fn test_from_package_with_init_data() { + fn test_from_library_with_init_data() { // Create a simple library for testing let library = Assembler::default().assemble_library([CODE]).unwrap(); @@ -354,24 +369,10 @@ mod tests { ) .unwrap(); - // Create a package with metadata - let package = Package { - name: "test_package_init_data".to_string(), - mast: MastArtifact::Library(Arc::new(library.clone())), - manifest: PackageManifest::new(None), - sections: vec![Section::new( - SectionId::ACCOUNT_COMPONENT_METADATA, - metadata.to_bytes(), - )], - version: Default::default(), - description: None, - }; - // Test with empty init data - this tests the complete workflow: - // Package -> AccountComponentTemplate -> AccountComponent + // Library + Metadata -> AccountComponent let init_data = InitStorageData::default(); - let component = - AccountComponent::from_package_with_init_data(&package, &init_data).unwrap(); + let component = AccountComponent::from_library(&library, &metadata, &init_data).unwrap(); // Verify the component was created correctly assert_eq!(component.storage_size(), 0); @@ -389,8 +390,7 @@ mod tests { description: None, }; - let result = - AccountComponent::from_package_with_init_data(&package_without_metadata, &init_data); + let result = AccountComponent::from_package(&package_without_metadata, &init_data); assert!(result.is_err()); let error_msg = result.unwrap_err().to_string(); assert!(error_msg.contains("package does not contain account component metadata")); diff --git a/crates/miden-objects/src/account/component/template/mod.rs b/crates/miden-objects/src/account/component/template/mod.rs index c646fa50a1..a2b046b4a1 100644 --- a/crates/miden-objects/src/account/component/template/mod.rs +++ b/crates/miden-objects/src/account/component/template/mod.rs @@ -3,7 +3,6 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::str::FromStr; -use miden_assembly::Library; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; use miden_processor::DeserializationError; use semver::Version; @@ -14,74 +13,10 @@ use crate::errors::AccountComponentTemplateError; mod storage; pub use storage::*; -// ACCOUNT COMPONENT TEMPLATE -// ================================================================================================ - -/// Represents a template containing a component's metadata and its associated library. -/// -/// The [AccountComponentTemplate] encapsulates all necessary information to initialize and manage -/// an account component within the system. It includes the configuration details and the compiled -/// library code required for the component's operation. -/// -/// A template can be instantiated into [AccountComponent](super::AccountComponent) objects. -/// The component metadata can be defined with placeholders that can be replaced at instantiation -/// time. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct AccountComponentTemplate { - /// The component's metadata. This describes the component and how the storage is laid out, - /// alongside how storage values are initialized. - metadata: AccountComponentMetadata, - /// The account component's assembled code. This defines all functionality related to the - /// component. - library: Library, -} - -impl AccountComponentTemplate { - /// Creates a new [AccountComponentTemplate]. - /// - /// This template holds everything needed to describe and implement a component, including the - /// compiled procedures (via the [Library]) and the metadata that defines the component’s - /// storage layout ([AccountComponentMetadata]). The metadata can include storage placeholders - /// that get filled in at the time of the [AccountComponent](super::AccountComponent) - /// instantiation. - pub fn new(metadata: AccountComponentMetadata, library: Library) -> Self { - Self { metadata, library } - } - - /// Returns a reference to the template's [AccountComponentMetadata]. - pub fn metadata(&self) -> &AccountComponentMetadata { - &self.metadata - } - - /// Returns a reference to the underlying [Library] of this component. - pub fn library(&self) -> &Library { - &self.library - } -} - -impl Serializable for AccountComponentTemplate { - fn write_into(&self, target: &mut W) { - target.write(&self.metadata); - target.write(&self.library); - } -} - -impl Deserializable for AccountComponentTemplate { - fn read_from( - source: &mut R, - ) -> Result { - // Read and deserialize the configuration from a TOML string. - let metadata: AccountComponentMetadata = source.read()?; - let library = Library::read_from(source)?; - - Ok(AccountComponentTemplate::new(metadata, library)) - } -} - // ACCOUNT COMPONENT METADATA // ================================================================================================ -/// Represents the full component template configuration. +/// Represents the full component metadata configuration. /// /// An account component metadata describes the component alongside its storage layout. /// On the storage layout, placeholders can be utilized to identify values that should be provided @@ -105,12 +40,14 @@ impl Deserializable for AccountComponentTemplate { /// ``` /// # use semver::Version; /// # use std::collections::{BTreeMap, BTreeSet}; +/// # use std::sync::Arc; +/// # use miden_mast_package::{MastArtifact, Package, PackageManifest, Section, SectionId}; /// # use miden_objects::{testing::account_code::CODE, account::{ /// # AccountComponent, AccountComponentMetadata, StorageEntry, -/// # StorageValueName, -/// # AccountComponentTemplate, FeltRepresentation, WordRepresentation, TemplateType}, +/// # StorageValueName, FeltRepresentation, WordRepresentation, TemplateType}, /// # assembly::Assembler, Felt}; /// # use miden_objects::account::InitStorageData; +/// # use miden_objects::utils::Serializable; /// # fn main() -> Result<(), Box> { /// let first_felt = FeltRepresentation::from(Felt::new(0u64)); /// let second_felt = FeltRepresentation::from(Felt::new(1u64)); @@ -140,9 +77,18 @@ impl Deserializable for AccountComponentTemplate { /// )?; /// /// let library = Assembler::default().assemble_library([CODE]).unwrap(); -/// let template = AccountComponentTemplate::new(component_template, library); -/// -/// let component = AccountComponent::from_template(&template, &init_storage_data)?; +/// let package = Package { +/// name: "test".into(), +/// mast: MastArtifact::Library(Arc::new(library)), +/// manifest: PackageManifest::new(None), +/// sections: vec![Section::new( +/// SectionId::ACCOUNT_COMPONENT_METADATA, +/// component_template.to_bytes(), +/// )], +/// version: Default::default(), +/// description: None, +/// }; +/// let component = AccountComponent::from_package(&package, &init_storage_data)?; /// # Ok(()) /// # } /// ``` @@ -324,6 +270,26 @@ impl Deserializable for AccountComponentMetadata { } } +#[cfg(test)] +pub(crate) fn test_package_with_metadata( + name: impl Into, + library: &miden_assembly::Library, + metadata: &AccountComponentMetadata, +) -> miden_mast_package::Package { + use std::sync::Arc; + + use miden_mast_package::{MastArtifact, Package, PackageManifest, Section, SectionId}; + + Package { + name: name.into(), + mast: MastArtifact::Library(Arc::new(library.clone())), + manifest: PackageManifest::new(None), + sections: vec![Section::new(SectionId::ACCOUNT_COMPONENT_METADATA, metadata.to_bytes())], + version: Default::default(), + description: None, + } +} + // TESTS // ================================================================================================ @@ -338,15 +304,11 @@ mod tests { use miden_core::{Felt, FieldElement}; use semver::Version; - use super::FeltRepresentation; + use super::{FeltRepresentation, test_package_with_metadata}; use crate::AccountError; use crate::account::component::FieldIdentifier; use crate::account::component::template::storage::StorageEntry; - use crate::account::component::template::{ - AccountComponentMetadata, - AccountComponentTemplate, - InitStorageData, - }; + use crate::account::component::template::{AccountComponentMetadata, InitStorageData}; use crate::account::{AccountComponent, StorageValueName}; use crate::errors::AccountComponentTemplateError; use crate::testing::account_code::CODE; @@ -402,7 +364,7 @@ mod tests { } #[test] - fn binary_serde_roundtrip() { + fn metadata_binary_serde_roundtrip() { let storage = vec![ StorageEntry::new_multislot( FieldIdentifier::with_name(StorageValueName::new("slot1").unwrap()), @@ -421,13 +383,13 @@ mod tests { }; let library = Assembler::default().assemble_library([CODE]).unwrap(); - let template = AccountComponentTemplate::new(component_metadata, library); - let _ = AccountComponent::from_template(&template, &InitStorageData::default()).unwrap(); + let package = test_package_with_metadata("test_package", &library, &component_metadata); + let _ = AccountComponent::from_package(&package, &InitStorageData::default()).unwrap(); - let serialized = template.to_bytes(); - let deserialized = AccountComponentTemplate::read_from_bytes(&serialized).unwrap(); + let serialized = component_metadata.to_bytes(); + let deserialized = AccountComponentMetadata::read_from_bytes(&serialized).unwrap(); - assert_eq!(deserialized, template); + assert_eq!(deserialized, component_metadata); } #[test] @@ -498,7 +460,8 @@ mod tests { let metadata = AccountComponentMetadata::from_toml(toml_text).unwrap(); let library = Assembler::default().assemble_library([CODE]).unwrap(); - let template = AccountComponentTemplate::new(metadata, library); + + let package = test_package_with_metadata("test_package", &library, &metadata); // Fail to instantiate on a duplicate key @@ -509,7 +472,7 @@ mod tests { )], BTreeMap::new(), ); - let account_component = AccountComponent::from_template(&template, &init_storage_data); + let account_component = AccountComponent::from_package(&package, &init_storage_data); assert_matches!( account_component, Err(AccountError::AccountComponentTemplateInstantiationError( @@ -522,6 +485,6 @@ mod tests { [(StorageValueName::new("map.duplicate_key").unwrap(), "0x30".to_string())], BTreeMap::new(), ); - AccountComponent::from_template(&template, &valid_init_storage_data).unwrap(); + AccountComponent::from_package(&package, &valid_init_storage_data).unwrap(); } } diff --git a/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs b/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs index e5955d19e2..e1cf1b856b 100644 --- a/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs +++ b/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs @@ -6,8 +6,8 @@ use super::StorageValueName; use crate::Word; /// Represents the data required to initialize storage entries when instantiating an -/// [AccountComponent](crate::account::AccountComponent) from a -/// [template](crate::account::AccountComponentTemplate). +/// [AccountComponent](crate::account::AccountComponent) from component metadata (either provided +/// directly or extracted from a package). /// /// An [`InitStorageData`] can be created from a TOML string when the `std` feature flag is set. #[derive(Clone, Debug, Default)] diff --git a/crates/miden-objects/src/account/component/template/storage/mod.rs b/crates/miden-objects/src/account/component/template/storage/mod.rs index e1380e025f..f90a6ce976 100644 --- a/crates/miden-objects/src/account/component/template/storage/mod.rs +++ b/crates/miden-objects/src/account/component/template/storage/mod.rs @@ -390,10 +390,10 @@ mod tests { MapEntry, MapRepresentation, StorageValueName, + test_package_with_metadata, }; use crate::account::{ AccountComponent, - AccountComponentTemplate, AccountType, FeltRepresentation, StorageEntry, @@ -562,12 +562,10 @@ mod tests { assert_eq!(map_key_template.r#type.as_str(), "word"); let library = Assembler::default().assemble_library([CODE]).unwrap(); - let template = AccountComponentTemplate::new(component_metadata, library); - - let template_bytes = template.to_bytes(); - let template_deserialized = - AccountComponentTemplate::read_from_bytes(&template_bytes).unwrap(); - assert_eq!(template, template_deserialized); + let metadata_bytes = component_metadata.to_bytes(); + let metadata_deserialized = + AccountComponentMetadata::read_from_bytes(&metadata_bytes).unwrap(); + assert_eq!(component_metadata, metadata_deserialized); // Fail to parse because 2800 > u8 let storage_placeholders = InitStorageData::new( @@ -586,7 +584,10 @@ mod tests { BTreeMap::new(), ); - let component = AccountComponent::from_template(&template, &storage_placeholders); + let package = + test_package_with_metadata("component_with_templates", &library, &component_metadata); + + let component = AccountComponent::from_package(&package, &storage_placeholders); assert_matches::assert_matches!( component, Err(AccountError::AccountComponentTemplateInstantiationError( @@ -613,7 +614,7 @@ mod tests { BTreeMap::new(), ); - let component = AccountComponent::from_template(&template, &storage_placeholders).unwrap(); + let component = AccountComponent::from_package(&package, &storage_placeholders).unwrap(); assert_eq!( component.supported_types(), &[AccountType::FungibleFaucet, AccountType::RegularAccountImmutableCode] @@ -636,7 +637,7 @@ mod tests { } let failed_instantiation = - AccountComponent::from_template(&template, &InitStorageData::default()); + AccountComponent::from_package(&package, &InitStorageData::default()); assert_matches::assert_matches!( failed_instantiation, @@ -684,14 +685,14 @@ mod tests { assert_eq!(metadata.storage_entries()[0].name().unwrap().as_str(), "ecdsa_pubkey"); let library = Assembler::default().assemble_library([CODE]).unwrap(); - let template = AccountComponentTemplate::new(metadata, library); + let package = test_package_with_metadata("ecdsa_auth_package", &library, &metadata); let init_storage = InitStorageData::new( [(StorageValueName::new("ecdsa_pubkey").unwrap(), "0x1234".into())], BTreeMap::new(), ); - let component = AccountComponent::from_template(&template, &init_storage).unwrap(); + let component = AccountComponent::from_package(&package, &init_storage).unwrap(); let slot = component.storage_slots().first().expect("missing storage slot"); match slot { StorageSlot::Value(word) => { diff --git a/crates/miden-objects/src/account/component/template/storage/placeholder.rs b/crates/miden-objects/src/account/component/template/storage/placeholder.rs index a4079f36d1..8acbbfeeda 100644 --- a/crates/miden-objects/src/account/component/template/storage/placeholder.rs +++ b/crates/miden-objects/src/account/component/template/storage/placeholder.rs @@ -54,7 +54,7 @@ impl StorageValueName { /// Creates a new [`StorageValueName`] from the provided string. /// /// A [`StorageValueName`] serves as an identifier for storage values that are determined at - /// instantiation time of an [AccountComponentTemplate](super::super::AccountComponentTemplate). + /// instantiation time of an [`AccountComponent`](crate::account::AccountComponent). /// /// The key can consist of one or more segments separated by dots (`.`). /// Each segment must be non-empty and may contain only alphanumeric characters, underscores diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 0918e43ce8..d75e05b045 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -37,7 +37,6 @@ pub mod component; pub use component::{ AccountComponent, AccountComponentMetadata, - AccountComponentTemplate, FeltRepresentation, InitStorageData, MapEntry, From 1f9e7a4a5dd0844e107419bcb939b2241b928468 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 4 Dec 2025 12:22:42 +0700 Subject: [PATCH 024/114] feat: add fee to `TransactionHeader` (#2131) * feat: Add `fee` to `TransactionHeader` * chore: add changelog --- CHANGELOG.md | 1 + .../src/transaction/tx_header.rs | 39 +++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ddaeb46da..182ce3ae6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). +- [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). ### Changes diff --git a/crates/miden-objects/src/transaction/tx_header.rs b/crates/miden-objects/src/transaction/tx_header.rs index bdec27be5e..2b4b0cef1f 100644 --- a/crates/miden-objects/src/transaction/tx_header.rs +++ b/crates/miden-objects/src/transaction/tx_header.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use miden_processor::DeserializationError; use crate::Word; +use crate::asset::FungibleAsset; use crate::note::NoteHeader; use crate::transaction::{ AccountId, @@ -29,6 +30,7 @@ pub struct TransactionHeader { final_state_commitment: Word, input_notes: InputNotes, output_notes: Vec, + fee: FungibleAsset, } impl TransactionHeader { @@ -42,14 +44,15 @@ impl TransactionHeader { /// The input notes and output notes must be in the same order as they appeared in the /// transaction that this header represents, otherwise an incorrect ID will be computed. /// - /// Note that this cannot validate that the [`AccountId`] is valid with respect to the other - /// data. This must be validated outside of this type. + /// Note that this cannot validate that the [`AccountId`] or the fee asset is valid with respect + /// to the other data. This must be validated outside of this type. pub fn new( account_id: AccountId, initial_state_commitment: Word, final_state_commitment: Word, input_notes: InputNotes, output_notes: Vec, + fee: FungibleAsset, ) -> Self { let input_notes_commitment = input_notes.commitment(); let output_notes_commitment = OutputNotes::compute_commitment(output_notes.iter().copied()); @@ -68,6 +71,7 @@ impl TransactionHeader { final_state_commitment, input_notes, output_notes, + fee, } } @@ -84,6 +88,7 @@ impl TransactionHeader { final_state_commitment: Word, input_notes: InputNotes, output_notes: Vec, + fee: FungibleAsset, ) -> Self { Self { id, @@ -92,6 +97,7 @@ impl TransactionHeader { final_state_commitment, input_notes, output_notes, + fee, } } @@ -141,6 +147,11 @@ impl TransactionHeader { pub fn output_notes(&self) -> &[NoteHeader] { &self.output_notes } + + /// Returns the fee paid by this transaction. + pub fn fee(&self) -> FungibleAsset { + self.fee + } } impl From<&ProvenTransaction> for TransactionHeader { @@ -155,6 +166,7 @@ impl From<&ProvenTransaction> for TransactionHeader { tx.account_update().final_state_commitment(), tx.input_notes().clone(), tx.output_notes().iter().map(NoteHeader::from).collect(), + tx.fee(), ) } } @@ -164,11 +176,22 @@ impl From<&ProvenTransaction> for TransactionHeader { impl Serializable for TransactionHeader { fn write_into(&self, target: &mut W) { - self.account_id.write_into(target); - self.initial_state_commitment.write_into(target); - self.final_state_commitment.write_into(target); - self.input_notes.write_into(target); - self.output_notes.write_into(target); + let Self { + id: _, + account_id, + initial_state_commitment, + final_state_commitment, + input_notes, + output_notes, + fee, + } = self; + + account_id.write_into(target); + initial_state_commitment.write_into(target); + final_state_commitment.write_into(target); + input_notes.write_into(target); + output_notes.write_into(target); + fee.write_into(target); } } @@ -179,6 +202,7 @@ impl Deserializable for TransactionHeader { let final_state_commitment = ::read_from(source)?; let input_notes = >::read_from(source)?; let output_notes = >::read_from(source)?; + let fee = FungibleAsset::read_from(source)?; let tx_header = Self::new( account_id, @@ -186,6 +210,7 @@ impl Deserializable for TransactionHeader { final_state_commitment, input_notes, output_notes, + fee, ); Ok(tx_header) From db6e31fa1ef7bb9d5cdf64a7ab227733733836c1 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 4 Dec 2025 15:14:06 +0700 Subject: [PATCH 025/114] chore: rename `new_unchecked` to `from_raw` in `WordWrapper` (#2134) * feat: Implement `From for Nullifier` * feat: Implement `From` for `NoteId` and `TransactionId` * chore: Rename `new_unchecked` to `from_raw` * fix: build and test --- crates/miden-objects/src/block/nullifier_tree.rs | 5 +---- crates/miden-objects/src/note/note_id.rs | 4 +++- crates/miden-objects/src/note/nullifier.rs | 4 +++- crates/miden-protocol-macros/README.md | 2 +- crates/miden-protocol-macros/src/lib.rs | 2 +- crates/miden-protocol-macros/tests/integration_test.rs | 4 ++-- crates/miden-tx/src/host/kernel_process.rs | 2 +- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/miden-objects/src/block/nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree.rs index bf12264eb8..db63c76571 100644 --- a/crates/miden-objects/src/block/nullifier_tree.rs +++ b/crates/miden-objects/src/block/nullifier_tree.rs @@ -257,10 +257,7 @@ where /// Returns an iterator over the nullifiers and their block numbers in the tree. pub fn entries(&self) -> impl Iterator { self.smt.entries().map(|(nullifier, block_num)| { - ( - Nullifier::new_unchecked(nullifier), - nullifier_leaf_value_to_block_num(block_num), - ) + (Nullifier::from_raw(nullifier), nullifier_leaf_value_to_block_num(block_num)) }) } diff --git a/crates/miden-objects/src/note/note_id.rs b/crates/miden-objects/src/note/note_id.rs index 1a1e2caa45..054ca5a564 100644 --- a/crates/miden-objects/src/note/note_id.rs +++ b/crates/miden-objects/src/note/note_id.rs @@ -57,8 +57,10 @@ impl From<&NoteDetails> for NoteId { impl NoteId { /// Attempts to convert from a hexadecimal string to [NoteId]. + /// + /// Callers must ensure the provided value is an actual [`NoteId`]. pub fn try_from_hex(hex_value: &str) -> Result { - Word::try_from(hex_value).map(NoteId::new_unchecked) + Word::try_from(hex_value).map(NoteId::from_raw) } } diff --git a/crates/miden-objects/src/note/nullifier.rs b/crates/miden-objects/src/note/nullifier.rs index 05a54fc8c7..26377caa8a 100644 --- a/crates/miden-objects/src/note/nullifier.rs +++ b/crates/miden-objects/src/note/nullifier.rs @@ -70,8 +70,10 @@ impl Nullifier { /// Creates a Nullifier from a hex string. Assumes that the string starts with "0x" and /// that the hexadecimal characters are big-endian encoded. + /// + /// Callers must ensure the provided value is an actual [`Nullifier`]. pub fn from_hex(hex_value: &str) -> Result { - Word::try_from(hex_value).map(Self::new_unchecked) + Word::try_from(hex_value).map(Self::from_raw) } #[cfg(any(feature = "testing", test))] diff --git a/crates/miden-protocol-macros/README.md b/crates/miden-protocol-macros/README.md index e0d2257d11..1b7676ec70 100644 --- a/crates/miden-protocol-macros/README.md +++ b/crates/miden-protocol-macros/README.md @@ -41,7 +41,7 @@ pub struct NoteId(Word); // Create using new_unchecked (generated by the macro) let word = Word::from([Felt::ONE, Felt::ZERO, Felt::ONE, Felt::ZERO]); -let note_id = NoteId::new_unchecked(word); +let note_id = NoteId::from_raw(word); // Use accessor methods let elements = note_id.as_elements(); diff --git a/crates/miden-protocol-macros/src/lib.rs b/crates/miden-protocol-macros/src/lib.rs index 3431d38791..100ccac410 100644 --- a/crates/miden-protocol-macros/src/lib.rs +++ b/crates/miden-protocol-macros/src/lib.rs @@ -141,7 +141,7 @@ pub fn word_wrapper_derive(input: TokenStream) -> TokenStream { /// /// This requires the caller to uphold the guarantees/invariants of this type (if any). /// Check the type-level documentation for guarantees/invariants. - pub fn new_unchecked(word: Word) -> Self { + pub fn from_raw(word: Word) -> Self { Self(word) } diff --git a/crates/miden-protocol-macros/tests/integration_test.rs b/crates/miden-protocol-macros/tests/integration_test.rs index bcc9f3869b..c6c95165ce 100644 --- a/crates/miden-protocol-macros/tests/integration_test.rs +++ b/crates/miden-protocol-macros/tests/integration_test.rs @@ -11,7 +11,7 @@ mod tests { // Create a test Word let word = Word::from([Felt::ONE, Felt::ONE, Felt::ZERO, Felt::ZERO]); // Use the new_unchecked method generated by the macro - let test_id = TestId::new_unchecked(word); + let test_id = TestId::from_raw(word); // Test as_elements let elements = test_id.as_elements(); @@ -36,7 +36,7 @@ mod tests { fn test_new_unchecked_is_generated() { // This test verifies that new_unchecked is generated by the macro let word = Word::from([Felt::ONE, Felt::ONE, Felt::ZERO, Felt::ZERO]); - let test_id = TestId::new_unchecked(word); + let test_id = TestId::from_raw(word); assert_eq!(test_id.as_word(), word); } } diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 3762254e8b..7e8a62b6b9 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -150,7 +150,7 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { ExecutionError::MemoryError(err), ) })? - .map(NoteId::new_unchecked)) + .map(NoteId::from_raw)) } } From 33aa51f0491ba42e7633ca883153059501d7c004 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:36:29 -0800 Subject: [PATCH 026/114] feat: increase `MAX_INPUTS_PER_NOTE` to 1024 (#2139) --- CHANGELOG.md | 1 + .../kernels/transaction/lib/constants.masm | 2 +- .../asm/kernels/transaction/lib/prologue.masm | 2 +- crates/miden-lib/asm/miden/note.masm | 10 +++++----- .../miden-lib/asm/shared_utils/util/note.masm | 2 +- .../miden-lib/src/errors/tx_kernel_errors.rs | 8 ++++---- crates/miden-objects/src/constants.rs | 5 ++--- crates/miden-objects/src/note/inputs.rs | 20 +++++++++---------- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 182ce3ae6b..5743fadd1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Changes +- [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)). - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). - Split tx progress events out into a separate enum ([#2103](https://github.com/0xMiden/miden-base/pull/2103)). diff --git a/crates/miden-lib/asm/kernels/transaction/lib/constants.masm b/crates/miden-lib/asm/kernels/transaction/lib/constants.masm index 2a5e76a9f3..7c1da10d20 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/constants.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/constants.masm @@ -5,7 +5,7 @@ const.WORD_SIZE=4 # The maximum number of input values associated with a single note. -const.MAX_INPUTS_PER_NOTE=128 +const.MAX_INPUTS_PER_NOTE=1024 # The maximum number of assets that can be stored in a single note. const.MAX_ASSETS_PER_NOTE=256 diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index 13e95c22b0..7117dc96bd 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -61,7 +61,7 @@ const.ERR_PROLOGUE_INPUT_NOTES_COMMITMENT_MISMATCH="note commitment computed fro const.ERR_PROLOGUE_NEW_ACCOUNT_NONCE_MUST_BE_ZERO="new account must have a zero nonce" -const.ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 128" +const.ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" const.ERR_PROLOGUE_NOTE_AUTHENTICATION_FAILED="failed to authenticate note inclusion in block" diff --git a/crates/miden-lib/asm/miden/note.masm b/crates/miden-lib/asm/miden/note.masm index bf2cdc6f00..a8097a70f2 100644 --- a/crates/miden-lib/asm/miden/note.masm +++ b/crates/miden-lib/asm/miden/note.masm @@ -6,7 +6,7 @@ use.std::mem # ERRORS # ================================================================================================= -const.ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 128" +const.ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" # NOTE UTILITY PROCEDURES # ================================================================================================= @@ -29,12 +29,12 @@ const.ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceede #! #! Panics if: #! - inputs_ptr is not word-aligned (i.e., is not a multiple of 4). -#! - num_inputs is greater than 128. +#! - num_inputs is greater than 1024. #! #! Invocation: exec export.compute_inputs_commitment - # check that number of inputs is less than 128 - dup.1 push.128 u32assert2.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT + # check that number of inputs is less than or equal to 1024 + dup.1 push.1024 u32assert2.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT u32lte assert.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT # => [inputs_ptr, num_inputs] @@ -120,7 +120,7 @@ end #! #! Panics if: #! - inputs_ptr is not word-aligned (i.e., is not a multiple of 4). -#! - num_inputs is greater than 128. +#! - num_inputs is greater than 1024. #! #! Invocation: exec export.build_recipient.1 diff --git a/crates/miden-lib/asm/shared_utils/util/note.masm b/crates/miden-lib/asm/shared_utils/util/note.masm index dc1bc21855..1a425ce13b 100644 --- a/crates/miden-lib/asm/shared_utils/util/note.masm +++ b/crates/miden-lib/asm/shared_utils/util/note.masm @@ -2,7 +2,7 @@ # ================================================================================================= # The maximum number of input values associated with a single note. -const.MAX_INPUTS_PER_NOTE=128 +const.MAX_INPUTS_PER_NOTE=1024 # PROCEDURES # ================================================================================================= diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index 3022640235..40183a5c1e 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -209,14 +209,14 @@ pub const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE: MasmE pub const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT: MasmError = MasmError::from_static_str("reserved slot for non-fungible faucet is not a valid empty SMT"); /// Error Message: "failed to authenticate note inclusion in block" pub const ERR_PROLOGUE_NOTE_AUTHENTICATION_FAILED: MasmError = MasmError::from_static_str("failed to authenticate note inclusion in block"); -/// Error Message: "number of note inputs exceeded the maximum limit of 128" -pub const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 128"); +/// Error Message: "number of note inputs exceeded the maximum limit of 1024" +pub const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 1024"); /// Error Message: "number of input notes exceeds the kernel's maximum limit of 1024" pub const ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT: MasmError = MasmError::from_static_str("number of input notes exceeds the kernel's maximum limit of 1024"); /// Error Message: "number of note assets exceeds the maximum limit of 256" pub const ERR_PROLOGUE_NUMBER_OF_NOTE_ASSETS_EXCEEDS_LIMIT: MasmError = MasmError::from_static_str("number of note assets exceeds the maximum limit of 256"); -/// Error Message: "number of note inputs exceeded the maximum limit of 128" -pub const ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 128"); +/// Error Message: "number of note inputs exceeded the maximum limit of 1024" +pub const ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 1024"); /// Error Message: "account data provided does not match the commitment recorded on-chain" pub const ERR_PROLOGUE_PROVIDED_ACCOUNT_DATA_DOES_NOT_MATCH_ON_CHAIN_COMMITMENT: MasmError = MasmError::from_static_str("account data provided does not match the commitment recorded on-chain"); /// Error Message: "provided info about assets of an input does not match its commitment" diff --git a/crates/miden-objects/src/constants.rs b/crates/miden-objects/src/constants.rs index 4d57250a40..bda8e339cc 100644 --- a/crates/miden-objects/src/constants.rs +++ b/crates/miden-objects/src/constants.rs @@ -11,9 +11,8 @@ pub const MAX_ASSETS_PER_NOTE: usize = 255; /// The maximum number of inputs that can accompany a single note. /// -/// The value is set to 128 so that it can be represented using as a single byte while being -/// evenly divisible by 8. -pub const MAX_INPUTS_PER_NOTE: usize = 128; +/// The value is set to 1024 so that it is evenly divisible by 8. +pub const MAX_INPUTS_PER_NOTE: usize = 1024; /// The maximum number of notes that can be consumed by a single transaction. pub const MAX_INPUT_NOTES_PER_TX: usize = 1024; diff --git a/crates/miden-objects/src/note/inputs.rs b/crates/miden-objects/src/note/inputs.rs index 5d0366eeaf..57b3b9365d 100644 --- a/crates/miden-objects/src/note/inputs.rs +++ b/crates/miden-objects/src/note/inputs.rs @@ -15,8 +15,8 @@ use crate::{Felt, Hasher, MAX_INPUTS_PER_NOTE, WORD_SIZE, Word, ZERO}; /// A container for note inputs. /// -/// A note can be associated with up to 128 input values. Each value is represented by a single -/// field element. Thus, note input values can contain up to ~1 KB of data. +/// A note can be associated with up to 1024 input values. Each value is represented by a single +/// field element. Thus, note input values can contain up to ~8 KB of data. /// /// All inputs associated with a note can be reduced to a single commitment which is computed by /// first padding the inputs with ZEROs to the next multiple of 8, and then by computing a @@ -34,7 +34,7 @@ impl NoteInputs { /// Returns [NoteInputs] instantiated from the provided values. /// /// # Errors - /// Returns an error if the number of provided inputs is greater than 128. + /// Returns an error if the number of provided inputs is greater than 1024. pub fn new(values: Vec) -> Result { if values.len() > MAX_INPUTS_PER_NOTE { return Err(NoteError::TooManyInputs(values.len())); @@ -53,14 +53,14 @@ impl NoteInputs { /// Returns the number of input values. /// - /// The returned value is guaranteed to be smaller than or equal to 128. - pub fn num_values(&self) -> u8 { - const _: () = assert!(MAX_INPUTS_PER_NOTE <= u8::MAX as usize); + /// The returned value is guaranteed to be smaller than or equal to 1024. + pub fn num_values(&self) -> u16 { + const _: () = assert!(MAX_INPUTS_PER_NOTE <= u16::MAX as usize); debug_assert!( - self.values.len() < MAX_INPUTS_PER_NOTE, + self.values.len() <= MAX_INPUTS_PER_NOTE, "The constructor should have checked the number of inputs" ); - self.values.len() as u8 + self.values.len() as u16 } /// Returns a reference to the input values. @@ -142,14 +142,14 @@ fn pad_and_build(values: Vec) -> NoteInputs { impl Serializable for NoteInputs { fn write_into(&self, target: &mut W) { let NoteInputs { values, commitment: _commitment } = self; - target.write_u8(values.len().try_into().expect("inputs len is not a u8 value")); + target.write_u16(values.len().try_into().expect("inputs len is not a u16 value")); target.write_many(values); } } impl Deserializable for NoteInputs { fn read_from(source: &mut R) -> Result { - let num_values = source.read_u8()? as usize; + let num_values = source.read_u16()? as usize; let values = source.read_many::(num_values)?; Self::new(values).map_err(|v| DeserializationError::InvalidValue(format!("{v}"))) } From 463c116812a7e008a8333ca5e7ec982190fae13c Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Sun, 7 Dec 2025 05:11:20 +0700 Subject: [PATCH 027/114] chore: remove unnecessary `cdropw` logic (#2087) --- .../transaction/lib/account_delta.masm | 30 +++++-------------- .../asm/kernels/transaction/lib/memory.masm | 11 +++++++ .../src/transaction/kernel_procedures.rs | 2 +- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm index d207ecaa22..3457e01d94 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm @@ -176,31 +176,15 @@ proc.update_value_slot_delta dup.4 exec.account::get_initial_item # => [INIT_VALUE, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] - # if account is new, replace INIT_VALUE with EMPTY_WORD - # we need to do this specifically for new accounts because for those, get_initial_item returns - # the same as get_item but we want the delta for value slots to be from an empty value to the - # final value - # use get_init_nonce so the delta is still correctly computed when the nonce has already been - # incremented - padw exec.memory::get_init_nonce eq.0 - # => [is_account_new, EMPTY_WORD, INIT_VALUE, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] - - # If is_account_new EMPTY_WORD remains. - # If !is_account_new INIT_VALUE remains. - cdropw - # => [INIT_VALUE', CURRENT_VALUE, slot_idx, RATE, RATE, PERM] - exec.word::test_eq not - # => [was_changed, INIT_VALUE', CURRENT_VALUE, slot_idx, RATE, RATE, PERM] + # => [was_changed, INIT_VALUE, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] # set was_changed to true if the account is new - # this means a storage slot initialized with an empty word is included in the commitment for - # new accounts - # more generally, we want the delta for a new account to include all its newly added values, - # regardless of the exact value, because the initial delta for an account must represent its - # full state - exec.memory::get_init_nonce eq.0 or - # => [was_changed, INIT_VALUE', CURRENT_VALUE, slot_idx, RATE, RATE, PERM] + # generally, the delta for a new account must include all its storage slots, regardless of the + # initial value and even if it is an empty word, because the initial delta for an account must + # represent its full state + exec.memory::is_new_account or + # => [was_changed, INIT_VALUE, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] # only include in delta if the slot's value has changed or the account is new if.true @@ -320,7 +304,7 @@ proc.update_map_slot_delta.4 # if the account is new (nonce == 0) include the map header even if it is an empty map # in order to have the delta commit to this initial storage slot. - exec.memory::get_init_nonce eq.0 or + exec.memory::is_new_account or # => [should_include_map_header, RATE, RATE, PERM] if.true diff --git a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm index 5223ca6e48..2dd45460bd 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm @@ -987,6 +987,17 @@ export.is_native_account # => [is_native_account] end +#! Returns 1 if the native account is new, i.e. its nonce is zero, 0 otherwise. +#! +#! Inputs: [] +#! Outputs: [is_new_account] +export.is_new_account + # the account is new if its nonce is zero + # use get_init_nonce so this procedure works correctly even after the account's nonce was + # incremented + exec.get_init_nonce eq.0 +end + #! Returns a pointer to the end of the core account data section. #! #! Inputs: [] diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-lib/src/transaction/kernel_procedures.rs index eaad10e432..e17fc4a79a 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-lib/src/transaction/kernel_procedures.rs @@ -50,7 +50,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_non_fungible_asset word!("0xfaad11de0c026551df15231790c2364cc598e891444bf826da01b524b1a8ca8f"), // account_compute_delta_commitment - word!("0xbb3ff91c5791cf0062df00954ebea8d97a79084d053e7c3a4555391faebdc819"), + word!("0xa19790f217a166fa3918118f75eb234436fe5dd513274b625757eab83d67a37c"), // account_get_num_procedures word!("0x53b5ec38b7841948762c258010e6e07ad93963bcaac2d83813f8edb6710dc720"), // account_get_procedure_root From 004e99cf9c02ebe190f35ee4087b638caca99438 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 9 Dec 2025 13:16:14 +0700 Subject: [PATCH 028/114] feat: Implement named storage slots in the transaction kernel (#1987) * feat: Implement `NamedStorageSlot` * feat: Use `find_half_key_value` * feat: Implement `AccountStorage::get` * feat: Reimplement account storage APIs * chore: Add compat APIs * feat: Reinstantiate apply_delta and reimpl set_item_raw * fix: get_storage_slot_type * fix: name layout in memory * feat: Implement get_size_hint for named slot and slot name * chore: Reinstantiate commented tests * feat: Implement `SequentialCommit` and make `StorageSlotHeader` private * chore: Various cleanup * fix: toml fmt and `typ` "typo" * chore: Add changelog entry * feat: Enforce max length on `SlotName` * chore: Emit event earlier in `get_map_item_raw` * feat: Update docs of storage slot header and account storage header * chore: Document `NamedStorageSlot` fields * chore: Rename `get_storage_slot_ptr` -> `find_storage_slot` * chore: use branch from VM PR * chore: Bump miden-vm to 0.18.3 * chore: Address post-merge changes * chore: move changelog entry to 0.13 and remove duplicate entries * fix: make format * chore: Use `get_storage_slot_type` instead * chore: Use BTreeSet to check for name uniqueness * chore: Rename `name_id` to `id` * chore: Use `project_name` in slot name example * chore: Remove `SlotName::is_empty` * chore: regengerate kernel procedure files --------- Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> --- CHANGELOG.md | 3 + .../asm/kernels/transaction/lib/account.masm | 213 +++++++++++--- .../transaction/lib/account_delta.masm | 6 +- .../asm/kernels/transaction/lib/memory.masm | 14 - .../asm/kernels/transaction/lib/prologue.masm | 4 +- .../miden-lib/src/errors/tx_kernel_errors.rs | 2 + crates/miden-lib/src/transaction/inputs.rs | 2 +- .../src/transaction/kernel_procedures.rs | 22 +- .../miden-objects/src/account/builder/mod.rs | 6 +- crates/miden-objects/src/account/header.rs | 2 +- crates/miden-objects/src/account/mod.rs | 9 +- .../src/account/storage/header.rs | 137 +++++---- .../miden-objects/src/account/storage/mod.rs | 271 +++++++++++------- .../src/account/storage/partial.rs | 15 +- .../src/account/storage/slot/mod.rs | 6 + .../src/account/storage/slot/named_slot.rs | 79 +++++ .../src/account/storage/slot/slot_name.rs | 132 ++++++++- .../src/account/storage/slot/slot_name_id.rs | 39 +++ .../src/account/storage/slot/type.rs | 40 ++- crates/miden-objects/src/errors.rs | 4 + crates/miden-objects/src/testing/storage.rs | 1 + .../src/transaction/inputs/account.rs | 2 +- .../src/kernel_tests/tx/test_account.rs | 23 +- .../src/kernel_tests/tx/test_faucet.rs | 4 +- .../src/kernel_tests/tx/test_fpi.rs | 4 +- .../src/kernel_tests/tx/test_prologue.rs | 14 +- .../miden-testing/src/tx_context/context.rs | 4 +- .../src/host/storage_delta_tracker.rs | 17 +- crates/miden-tx/src/host/tx_event.rs | 4 +- 29 files changed, 788 insertions(+), 291 deletions(-) create mode 100644 crates/miden-objects/src/account/storage/slot/named_slot.rs create mode 100644 crates/miden-objects/src/account/storage/slot/slot_name_id.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5743fadd1f..554b8ab53b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### Features +- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987)). + +## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). - [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index d4f6b69e42..0b390f8235 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -3,8 +3,8 @@ use.$kernel::account_id use.$kernel::asset_vault use.$kernel::constants use.$kernel::memory - use.std::collections::smt +use.std::collections::sorted_array use.std::crypto::hashes::rpo use.std::mem use.std::word @@ -30,8 +30,11 @@ const.ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS="provided procedure index is out of b const.ERR_ACCOUNT_PROC_NOT_AUTH_PROC="account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure" +# TODO(named_slots): Remove along with index APIs. const.ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS="provided storage slot index is out of bounds" +const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" + const.ERR_FAUCET_INVALID_STORAGE_OFFSET="storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets)" const.ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH="computed account code commitment does not match recorded account code commitment" @@ -120,6 +123,9 @@ const.ACCOUNT_STORAGE_SLOT_DATA_LENGTH=8 # The number of field elements it takes to store one account procedure. const.ACCOUNT_PROCEDURE_DATA_LENGTH=8 +# The offset of the slot type in the storage slot. +const.ACCOUNT_SLOT_TYPE_OFFSET=1 + # EVENTS # ================================================================================================= @@ -476,7 +482,7 @@ export.set_item # => [index, VALUE] # get storage slot type - dup exec.get_storage_slot_type + dup exec.get_storage_slot_type_by_index # => [storage_slot_type, index, VALUE] # check if type == slot @@ -583,7 +589,7 @@ export.set_map_item.12 # => [index, KEY, NEW_VALUE, OLD_ROOT, ...] # check if storage type is map - exec.get_storage_slot_type + exec.get_storage_slot_type_by_index # => [slot_type, KEY, NEW_VALUE, OLD_ROOT] # check if slot_type == map @@ -671,7 +677,7 @@ end #! #! Panics if: #! - the slot index is out of bounds. -export.get_storage_slot_type +export.get_storage_slot_type_by_index # check that index is in bounds dup exec.memory::get_num_storage_slots lt assert.err=ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS # => [index] @@ -679,7 +685,25 @@ export.get_storage_slot_type exec.memory::get_account_storage_slots_section_ptr # => [curr_account_storage_slots_section_ptr, index] - exec.memory::get_storage_slot_type + exec.get_storage_slot_type + # => [slot_type] +end + +#! Returns the type of the requested storage slot. +#! +#! Inputs: [account_storage_slots_section_ptr, index] +#! Outputs: [slot_type] +#! +#! Where: +#! - index is the location in memory of the storage slot. +#! - slot_type is the type of the storage slot. +export.get_storage_slot_type + # compute storage slot ptr + swap mul.8 add + # => [storage_slot_ptr] + + # load the slot type + add.ACCOUNT_SLOT_TYPE_OFFSET mem_load # => [slot_type] end @@ -1350,7 +1374,7 @@ export.insert_new_storage sub.1 # => [slot_idx] - dup exec.get_storage_slot_type + dup exec.get_storage_slot_type_by_index # => [slot_type, slot_idx] exec.constants::get_storage_slot_type_map eq @@ -1482,8 +1506,81 @@ end #! - index is the index of the item to get. #! - VALUE is the value of the item. export.get_item_raw + # TODO(named_slots): pass in name + exec.find_indexed_storage_slot + # => [slot_ptr] + + # offset the pointer to point to the VALUE in the slot + add.4 + # => [slot_value_ptr] + + # load the item from storage + padw movup.4 mem_loadw_be + # => [VALUE] +end + +#! Gets an item from the storage slot at the provided index. +#! +#! WARNING: The index must be in bounds. +#! +#! Inputs: [index] +#! Outputs: [VALUE] +#! +#! Where: +#! - storage_slots_ptr is the pointer to the storage slots section. +#! - index is the index of the item to get. +#! - VALUE is the value of the item. +export.get_item_by_index + # get account storage slots section offset + exec.memory::get_account_storage_slots_section_ptr + # => [acct_storage_slots_section_offset, index] + + exec.get_item_by_index_raw + # => [VALUE] +end + +#! Gets the initial item from the storage slot at the provided index. +#! +#! WARNING: The index must be in bounds. +#! +#! Inputs: [index] +#! Outputs: [VALUE] +#! +#! Where: +#! - storage_slots_ptr is the pointer to the storage slots section. +#! - index is the index of the item to get. +#! - VALUE is the value of the item. +export.get_initial_item_by_index + # get account initial storage slots section offset + exec.memory::get_account_initial_storage_slots_ptr + # => [account_initial_storage_slots_ptr, index] + + exec.get_item_by_index_raw + # => [VALUE] +end + +#! Gets an item from storage using the provided storage slots pointer and index. +#! +#! WARNING: The index must be in bounds. +#! +#! Inputs: [storage_slots_ptr, index] +#! Outputs: [VALUE] +#! +#! Where: +#! - storage_slots_ptr is the pointer to the storage slots section. +#! - index is the index of the item to get. +#! - VALUE is the value of the item. +proc.get_item_by_index_raw # get the item from storage - swap mul.8 add padw movup.4 mem_loadw_be + swap mul.8 add + # => [storage_slot_ptr] + + # offset the pointer to point to the VALUE in the slot + add.4 + # => [slot_value_ptr] + + # load the item from storage + padw movup.4 mem_loadw_be # => [VALUE] end @@ -1503,29 +1600,29 @@ end #! Panics if: #! - the requested storage slot type is not map. proc.get_map_item_raw + emit.ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM_EVENT + # => [KEY, ROOT, index] + # check storage slot type - dup.8 exec.get_storage_slot_type - # => [slot_type, KEY, ROOT, index] + movup.8 exec.get_storage_slot_type_by_index + # => [slot_type, KEY, ROOT] # check if storage slot type is map exec.constants::get_storage_slot_type_map eq assert.err=ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT - # => [KEY, ROOT, index] - - emit.ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM_EVENT - # => [KEY, ROOT, index] + # => [KEY, ROOT] # see hash_map_key's docs for why this is done exec.hash_map_key - # => [HASHED_KEY, ROOT, index] + # => [HASHED_KEY, ROOT] # fetch the VALUE located under HASHED_KEY in the tree exec.smt::get - # => [VALUE, ROOT, index] + # => [VALUE, ROOT] # remove the ROOT from the stack swapw dropw - # => [VALUE, index] + # => [VALUE] movup.4 drop # => [VALUE] @@ -1541,32 +1638,84 @@ end #! - NEW_VALUE is the value to set. #! - OLD_VALUE is the previous value of the item. proc.set_item_raw - # get old value from storage - dup movdn.5 exec.get_item - # => [OLD_VALUE, NEW_VALUE, index] - - # arrange stack for storage update - swapw movup.8 - # => [index, NEW_VALUE, OLD_VALUE] + # TODO(named_slots): pass in name - # get account storage slots section offset exec.memory::get_account_storage_slots_section_ptr - # => [acct_storage_slots_section_offset, index, NEW_VALUE, OLD_VALUE] + # => [storage_slots_ptr, index, NEW_VALUE] + + exec.find_indexed_storage_slot + # => [slot_ptr, NEW_VALUE] + + # offset the pointer to point to the VALUE in the slot + add.4 movdn.4 + # => [NEW_VALUE, slot_value_ptr] - # update storage - swap mul.8 add mem_storew_be - # => [NEW_VALUE, OLD_VALUE] + # load the item from storage + padw dup.8 mem_loadw_be + # => [OLD_VALUE, NEW_VALUE, slot_value_ptr] + + swapw movup.8 + # => [slot_value_ptr, NEW_VALUE, OLD_VALUE] + + mem_storew_be dropw + # => [OLD_VALUE] # update the storage commitment dirty flag, indicating that the commitment is outdated push.1 exec.memory::set_native_account_storage_commitment_dirty_flag - # => [NEW_VALUE, OLD_VALUE] - - # drop value - dropw # => [OLD_VALUE] end +#! TODO(named_slots): Remove this API. +#! +#! Inputs: [storage_slots_ptr, index] +#! Outputs: [slot_ptr] +proc.find_indexed_storage_slot + # set the suffix to 0 + push.0 movup.2 + # => [name_id_prefix = index, name_id_suffix = 0, storage_slots_ptr] + + movup.2 + # => [storage_slots_ptr, name_id_prefix = index, name_id_suffix = 0] + + exec.find_storage_slot + # => [slot_ptr] +end + +#! Finds the slot identified by the key constructed from the name ID and slot type and returns the +#! pointer to that slot. +#! +#! Inputs: [storage_slots_ptr, name_id_prefix, name_id_suffix] +#! Outputs: [slot_ptr] +#! +#! Where: +#! - storage_slots_ptr is the pointer to the storage slots section. +#! - slot_ptr is the pointer to the resolved storage slot. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +proc.find_storage_slot + # construct the start and end pointers of the storage slot section in which we will search + dup exec.memory::get_num_storage_slots mul.8 add + # => [storage_slots_end_ptr, storage_slots_start_ptr, name_id_prefix, name_id_suffix] + + # TODO: Check if we can get here if num_storage_slots == 0. If so, return a better error than + # what find_key_value returns. + + movdn.3 movdn.2 + # => [name_id_prefix, name_id_suffix, storage_slots_start_ptr, storage_slots_end_ptr] + + # find the slot whose slot key matches [name_id_prefix, name_id_suffix, 0, 0] + # if the slot key does not exist, this procedure will validate its absence + exec.sorted_array::find_half_key_value + # => [is_slot_found, slot_ptr, storage_slots_start_ptr, storage_slots_end_ptr] + + assert.err=ERR_ACCOUNT_ID_UNKNOWN_STORAGE_SLOT_NAME + # => [slot_ptr, storage_slots_start_ptr, storage_slots_end_ptr] + + swap.2 drop drop + # => [slot_ptr] +end + #! Returns the procedure root. #! #! Inputs: [index] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm index 3457e01d94..68ed259a1a 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm @@ -150,7 +150,7 @@ proc.update_slot_delta dup exec.memory::get_native_account_storage_slots_ptr # => [native_account_storage_slots_section_ptr, slot_idx, slot_idx, RATE, RATE, PERM] - exec.memory::get_storage_slot_type + exec.account::get_storage_slot_type # => [storage_slot_type, slot_idx, RATE, RATE, PERM] # check if type == slot @@ -170,10 +170,10 @@ end #! Inputs: [slot_idx, RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] proc.update_value_slot_delta - dup exec.account::get_item + dup exec.account::get_item_by_index # => [CURRENT_VALUE, slot_idx, RATE, RATE, PERM] - dup.4 exec.account::get_initial_item + dup.4 exec.account::get_initial_item_by_index # => [INIT_VALUE, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] exec.word::test_eq not diff --git a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm index 2dd45460bd..aee6869ec9 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm @@ -1358,20 +1358,6 @@ export.set_num_storage_slots mem_store end -#! Returns the type of the requested storage slot. -#! -#! Inputs: [account_storage_slots_section_ptr, index] -#! Outputs: [slot_type] -#! -#! Where: -#! - index is the location in memory of the storage slot. -#! - slot_type is the type of the storage slot. -export.get_storage_slot_type - # get storage slot type - swap mul.8 add add.4 mem_load - # => [slot_type] -end - #! Returns the memory pointer to the account storage slots section. #! #! Inputs: [] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index 7117dc96bd..557d7d9d70 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -332,7 +332,7 @@ proc.validate_new_account # => [] # get the faucet reserved storage data slot type - exec.account::get_faucet_storage_data_slot exec.account::get_storage_slot_type + exec.account::get_faucet_storage_data_slot exec.account::get_storage_slot_type_by_index # => [slot_type] # assert the fungible faucet reserved slot type == value @@ -347,7 +347,7 @@ proc.validate_new_account # => [] # get the faucet reserved storage data slot type - exec.account::get_faucet_storage_data_slot exec.account::get_storage_slot_type + exec.account::get_faucet_storage_data_slot exec.account::get_storage_slot_type_by_index # => [slot_type] # assert the non-fungible faucet reserved slot type == map diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index 40183a5c1e..19809b1fc4 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -24,6 +24,8 @@ pub const ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO: MasmError = pub const ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO: MasmError = MasmError::from_static_str("most significant bit of the account ID suffix must be zero"); /// Error Message: "unknown account storage mode in account ID" pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE: MasmError = MasmError::from_static_str("unknown account storage mode in account ID"); +/// Error Message: "storage slot with the provided name does not exist" +pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_SLOT_NAME: MasmError = MasmError::from_static_str("storage slot with the provided name does not exist"); /// Error Message: "unknown version in account ID" pub const ERR_ACCOUNT_ID_UNKNOWN_VERSION: MasmError = MasmError::from_static_str("unknown version in account ID"); /// Error Message: "storage size can only be zero if storage offset is also zero" diff --git a/crates/miden-lib/src/transaction/inputs.rs b/crates/miden-lib/src/transaction/inputs.rs index dd99af643a..1705ffd3fe 100644 --- a/crates/miden-lib/src/transaction/inputs.rs +++ b/crates/miden-lib/src/transaction/inputs.rs @@ -266,7 +266,7 @@ impl TransactionAdviceInputs { // STORAGE_COMMITMENT |-> [[STORAGE_SLOT_DATA]] let storage_header = account.storage().header(); - self.add_map_entry(storage_header.compute_commitment(), storage_header.as_elements()); + self.add_map_entry(storage_header.to_commitment(), storage_header.to_elements()); // populate Merkle store and advice map with nodes info needed to access storage map entries self.extend_merkle_store(account.storage().inner_nodes()); diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-lib/src/transaction/kernel_procedures.rs index e17fc4a79a..92e8bea452 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-lib/src/transaction/kernel_procedures.rs @@ -24,17 +24,17 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_compute_storage_commitment word!("0xa87008550383e1a88dde5d0adefc68ee3bf477aec07e4700f9101241aa1e868f"), // account_get_item - word!("0xe1e6843fb47f24476a12ef8cd19dd5de2dd74b90433051b26720dce5ab223bf0"), + word!("0x045ba56f6e0f788fc4cd5b41a9ba9635bad9cf5e735d06c74372a208fd7ca1b1"), // account_get_initial_item - word!("0x5e956c876cd6eaaa15f5800a5232c6b4e3e50e0335a31ba2e4a9e5f2401aece4"), + word!("0x0662d55068a44158e8061bfdfe753fd57d53102e8861c71f36ec9481b14d7e8b"), // account_set_item - word!("0x84b5206c5a0dccf56568bc0157b8322e8a506332bc212f1ad35bab4fe9f6bfed"), + word!("0xe2ac03d4fb2ed9d0e756a774476c97e4b7197cdcf4a0d8de302a47344f7ce211"), // account_get_map_item - word!("0xeb40115d6ca9bed0ac817b246e3d247bb3386e56ee17692d30a3be799f98f2f6"), + word!("0xd8449285da87d27e78eec95466cd9ac05506eab757c8561de21362cc1a2e9e1a"), // account_get_initial_map_item - word!("0x054f48624a30f260269cc9d780e9d84b77091e29f1d44c6450ab73e0eb91cc39"), + word!("0x6a8ca612d462a2ef1e3bf778de57827cbd66eb83dbd596a4746a716da15d8c51"), // account_set_map_item - word!("0x33ab43462ceb87f00c5fbebd47cdf948246db0e82b8e85c98edc1b568b784ba3"), + word!("0x2d6684f7ad6b051b8c5d3476f01dfac55f4d27ff0b3f4d359238acb1bdfc400b"), // account_get_initial_vault_root word!("0x46297d9ac95afd60c7ef1a065e024ad49aa4c019f6b3924191905449b244d4ec"), // account_get_vault_root @@ -50,7 +50,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_non_fungible_asset word!("0xfaad11de0c026551df15231790c2364cc598e891444bf826da01b524b1a8ca8f"), // account_compute_delta_commitment - word!("0xa19790f217a166fa3918118f75eb234436fe5dd513274b625757eab83d67a37c"), + word!("0x274e90e4c5fc54ed530c32aae966cbeebd4bb97e2ebd93f76f4cd943902eb851"), // account_get_num_procedures word!("0x53b5ec38b7841948762c258010e6e07ad93963bcaac2d83813f8edb6710dc720"), // account_get_procedure_root @@ -60,13 +60,13 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_procedure word!("0x667d5ce1b7a54c3b8965666ce90e59085c97775b82eba25dddfe218db5fe137d"), // faucet_mint_asset - word!("0xfc1923fc651f8122a2f32fce96711203bd8309180ce6f484ea6fd4ceb175e3f0"), + word!("0xa0c038cba851e70f1d08141006fbffebe7283aa8f5af7b27dd5328c89309bde4"), // faucet_burn_asset - word!("0x2633226e8831cfb1e9970ae2ee79b83678538f2c6656a755c707ba85cd462562"), + word!("0x9c22302e6505e55b805d0a0ec1328653065e31980368a09f40e0528e3a2fdb54"), // faucet_get_total_fungible_asset_issuance - word!("0x7d32952d4dc0edd0311e3424b8128df2d48cf949f800c28218fbc851a8db42b5"), + word!("0xd361b7554245e8a414771e95ef53b427d53fb4990846d8c376c7e0f744d15b87"), // faucet_is_non_fungible_asset_issued - word!("0x0323d18fe6bd7bbada87d265d2ffb27f2a7828b1b6865d6d1a3b4a64924a9f7f"), + word!("0x64aae9fa0550d5224b4bd765a42d7e1b9ec4e66dad636f11ba54b00ff03a2af0"), // input_note_get_metadata word!("0x7ad3e94585e7a397ee27443c98b376ed8d4ba762122af6413fde9314c00a6219"), // input_note_get_assets_info diff --git a/crates/miden-objects/src/account/builder/mod.rs b/crates/miden-objects/src/account/builder/mod.rs index e5d19206be..6aa7665b6b 100644 --- a/crates/miden-objects/src/account/builder/mod.rs +++ b/crates/miden-objects/src/account/builder/mod.rs @@ -209,14 +209,14 @@ impl AccountBuilder { self.init_seed, self.id_version, code.commitment(), - storage.commitment(), + storage.to_commitment(), )?; let account_id = AccountId::new( seed, AccountIdVersion::Version0, code.commitment(), - storage.commitment(), + storage.to_commitment(), ) .expect("get_account_seed should provide a suitable seed"); @@ -373,7 +373,7 @@ mod tests { account.seed().unwrap(), AccountIdVersion::Version0, account.code.commitment(), - account.storage.commitment(), + account.storage.to_commitment(), ) .unwrap(); assert_eq!(account.id(), computed_id); diff --git a/crates/miden-objects/src/account/header.rs b/crates/miden-objects/src/account/header.rs index b46e6f2209..19d7d98c9a 100644 --- a/crates/miden-objects/src/account/header.rs +++ b/crates/miden-objects/src/account/header.rs @@ -141,7 +141,7 @@ impl From<&Account> for AccountHeader { id: account.id(), nonce: account.nonce(), vault_root: account.vault().root(), - storage_commitment: account.storage().commitment(), + storage_commitment: account.storage().to_commitment(), code_commitment: account.code().commitment(), } } diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index d75e05b045..ef1779285d 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -65,9 +65,11 @@ mod storage; pub use storage::{ AccountStorage, AccountStorageHeader, + NamedStorageSlot, PartialStorage, PartialStorageMap, SlotName, + SlotNameId, StorageMap, StorageMapWitness, StorageSlot, @@ -135,7 +137,7 @@ impl Account { nonce: Felt, seed: Option, ) -> Result { - validate_account_seed(id, code.commitment(), storage.commitment(), seed, nonce)?; + validate_account_seed(id, code.commitment(), storage.to_commitment(), seed, nonce)?; Ok(Self::new_unchecked(id, vault, storage, code, nonce, seed)) } @@ -231,7 +233,7 @@ impl Account { self.id, self.nonce, self.vault.root(), - self.storage.commitment(), + self.storage.to_commitment(), self.code.commitment(), ) } @@ -439,6 +441,7 @@ impl TryFrom for AccountDelta { let mut map_slots = BTreeMap::new(); for (slot_idx, slot) in (0..u8::MAX).zip(storage.into_slots().into_iter()) { + let (_, _, slot) = slot.into_parts(); match slot { StorageSlot::Value(word) => { value_slots.insert(slot_idx, word); @@ -923,7 +926,7 @@ mod tests { Network, AccountIdVersion::Version0, code.commitment(), - storage.commitment(), + storage.to_commitment(), )?; // Set nonce to 1 so the account is considered existing and provide the seed. diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-objects/src/account/storage/header.rs index 5690e180f4..eb1bf50ab3 100644 --- a/crates/miden-objects/src/account/storage/header.rs +++ b/crates/miden-objects/src/account/storage/header.rs @@ -1,6 +1,8 @@ use alloc::vec::Vec; -use super::{AccountStorage, Felt, Hasher, StorageSlot, StorageSlotType, Word}; +use super::{AccountStorage, Felt, StorageSlot, StorageSlotType, Word}; +use crate::account::{SlotName, SlotNameId}; +use crate::crypto::SequentialCommit; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -8,53 +10,57 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{AccountError, ZERO}; +use crate::{AccountError, FieldElement, ZERO}; // ACCOUNT STORAGE HEADER // ================================================================================================ -/// Storage slot header is a lighter version of the [StorageSlot] storing only the type and the -/// top-level value for the slot, and being, in fact, just a thin wrapper around a tuple. +/// The header of a [`StorageSlot`], storing only the name ID, slot type and value of the slot. /// -/// That is, for complex storage slot (e.g., storage map), the header contains only the commitment -/// to the underlying data. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct StorageSlotHeader(StorageSlotType, Word); +/// The stored value differs based on the slot type: +/// - [`StorageSlotType::Value`]: The value of the slot itself. +/// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct StorageSlotHeader { + id: SlotNameId, + r#type: StorageSlotType, + value: Word, +} impl StorageSlotHeader { /// Returns a new instance of storage slot header from the provided storage slot type and value. - pub fn new(value: &(StorageSlotType, Word)) -> Self { - Self(value.0, value.1) + pub(crate) fn new(id: SlotNameId, r#type: StorageSlotType, value: Word) -> Self { + Self { id, r#type, value } } /// Returns this storage slot header as field elements. /// /// This is done by converting this storage slot into 8 field elements as follows: /// ```text - /// [SLOT_VALUE, slot_type, 0, 0, 0] + /// [[0, slot_type, name_id_suffix, name_id_prefix], SLOT_VALUE] /// ``` - pub fn as_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT] { + pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT] { let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT]; - elements[0..4].copy_from_slice(self.1.as_elements()); - elements[4..8].copy_from_slice(self.0.as_word().as_elements()); + elements[0..4].copy_from_slice(&[ + Felt::ZERO, + self.r#type.as_felt(), + self.id.suffix(), + self.id.prefix(), + ]); + elements[4..8].copy_from_slice(self.value.as_elements()); elements } } -impl From<&StorageSlot> for StorageSlotHeader { - fn from(value: &StorageSlot) -> Self { - Self(value.slot_type(), value.value()) - } -} - -/// Account storage header is a lighter version of the [AccountStorage] storing only the type and -/// the top-level value for each storage slot. +/// The header of an [`AccountStorage`], storing only the slot name, slot type and value of each +/// storage slot. /// -/// That is, for complex storage slots (e.g., storage maps), the header contains only the commitment -/// to the underlying data. +/// The stored value differs based on the slot type: +/// - [`StorageSlotType::Value`]: The value of the slot itself. +/// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AccountStorageHeader { - slots: Vec<(StorageSlotType, Word)>, + slots: Vec<(SlotName, StorageSlotType, Word)>, } impl AccountStorageHeader { @@ -65,7 +71,7 @@ impl AccountStorageHeader { /// /// # Panics /// - If the number of provided slots is greater than [AccountStorage::MAX_NUM_STORAGE_SLOTS]. - pub fn new(slots: Vec<(StorageSlotType, Word)>) -> Self { + pub fn new(slots: Vec<(SlotName, StorageSlotType, Word)>) -> Self { assert!(slots.len() <= AccountStorage::MAX_NUM_STORAGE_SLOTS); Self { slots } } @@ -74,16 +80,16 @@ impl AccountStorageHeader { // -------------------------------------------------------------------------------------------- /// Returns an iterator over the storage header slots. - pub fn slots(&self) -> impl Iterator { - self.slots.iter() + pub fn slots(&self) -> impl Iterator { + self.slots.iter().map(|(name, r#type, value)| (name, r#type, value)) } /// Returns an iterator over the storage header map slots. pub fn map_slot_roots(&self) -> impl Iterator { - self.slots - .iter() - .filter(|(slot_type, _)| matches!(slot_type, StorageSlotType::Map)) - .map(|x| x.1) + self.slots.iter().filter_map(|(_, slot_type, value)| match slot_type { + StorageSlotType::Value => None, + StorageSlotType::Map => Some(*value), + }) } /// Returns the number of slots contained in the storage header. @@ -96,17 +102,23 @@ impl AccountStorageHeader { /// /// # Errors /// - If the index is out of bounds. - pub fn slot(&self, index: usize) -> Result<&(StorageSlotType, Word), AccountError> { - self.slots.get(index).ok_or(AccountError::StorageIndexOutOfBounds { - slots_len: self.slots.len() as u8, - index: index as u8, - }) - } + pub fn slot_header( + &self, + index: usize, + ) -> Result<(&SlotName, &StorageSlotType, &Word), AccountError> { + let slot_name = SlotName::new_index(index); - // NOTE: The way of computing the commitment should be kept in sync with `AccountStorage` - /// Computes the account storage header commitment. - pub fn compute_commitment(&self) -> Word { - Hasher::hash_elements(&self.as_elements()) + self.slots + .binary_search_by_key(&slot_name.compute_id(), |(name, ..)| name.compute_id()) + .map(|slot_idx| { + let (name, r#type, value) = &self.slots[slot_idx]; + (name, r#type, value) + }) + .ok() + .ok_or(AccountError::StorageIndexOutOfBounds { + slots_len: self.slots.len() as u8, + index: index as u8, + }) } /// Indicates whether the slot at `index` is a map slot. @@ -114,7 +126,7 @@ impl AccountStorageHeader { /// # Errors /// - If `index` exceeds the slot count. pub fn is_map_slot(&self, index: usize) -> Result { - match self.slot(index)?.0 { + match self.slot_header(index)?.1 { StorageSlotType::Map => Ok(true), StorageSlotType::Value => Ok(false), } @@ -123,15 +135,19 @@ impl AccountStorageHeader { /// Converts storage slots of this account storage header into a vector of field elements. /// /// This is done by first converting each storage slot into exactly 8 elements as follows: + /// /// ```text - /// [STORAGE_SLOT_VALUE, storage_slot_type, 0, 0, 0] + /// [[0, slot_type, name_id_suffix, name_id_prefix], SLOT_VALUE] /// ``` + /// /// And then concatenating the resulting elements into a single vector. - pub fn as_elements(&self) -> Vec { - self.slots - .iter() - .flat_map(|slot| StorageSlotHeader::new(slot).as_elements()) - .collect() + pub fn to_elements(&self) -> Vec { + ::to_elements(self) + } + + /// Returns the commitment to the [`AccountStorage`] this header represents. + pub fn to_commitment(&self) -> Word { + ::to_commitment(self) } } @@ -141,6 +157,22 @@ impl From<&AccountStorage> for AccountStorageHeader { } } +// SEQUENTIAL COMMIT +// ================================================================================================ + +impl SequentialCommit for AccountStorageHeader { + type Commitment = Word; + + fn to_elements(&self) -> Vec { + self.slots() + .flat_map(|(slot_name, slot_type, slot_value)| { + StorageSlotHeader::new(slot_name.compute_id(), *slot_type, *slot_value) + .to_elements() + }) + .collect() + } +} + // SERIALIZATION // ================================================================================================ @@ -171,7 +203,7 @@ mod tests { use super::AccountStorageHeader; use crate::Word; - use crate::account::{AccountStorage, StorageSlotType}; + use crate::account::{AccountStorage, SlotName, StorageSlotType}; #[test] fn test_from_account_storage() { @@ -179,12 +211,13 @@ mod tests { // create new storage header from AccountStorage let slots = vec![ - (StorageSlotType::Value, Word::from([1, 2, 3, 4u32])), + (SlotName::new_index(0), StorageSlotType::Value, Word::from([1, 2, 3, 4u32])), ( + SlotName::new_index(1), StorageSlotType::Value, Word::from([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]), ), - (StorageSlotType::Map, storage_map.root()), + (SlotName::new_index(2), StorageSlotType::Map, storage_map.root()), ]; let expected_header = AccountStorageHeader { slots }; diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index 168f12ee85..c1f932bbd3 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -1,3 +1,4 @@ +use alloc::collections::BTreeSet; use alloc::string::ToString; use alloc::vec::Vec; @@ -9,20 +10,21 @@ use super::{ Deserializable, DeserializationError, Felt, - Hasher, Serializable, Word, }; +use crate::account::storage::header::StorageSlotHeader; use crate::account::{AccountComponent, AccountType}; +use crate::crypto::SequentialCommit; mod slot; -pub use slot::{SlotName, StorageSlot, StorageSlotType}; +pub use slot::{NamedStorageSlot, SlotName, SlotNameId, StorageSlot, StorageSlotType}; mod map; pub use map::{PartialStorageMap, StorageMap, StorageMapWitness}; mod header; -pub use header::{AccountStorageHeader, StorageSlotHeader}; +pub use header::AccountStorageHeader; mod partial; pub use partial::PartialStorage; @@ -41,7 +43,7 @@ pub use partial::PartialStorage; /// underlying map. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct AccountStorage { - slots: Vec, + slots: Vec, } impl AccountStorage { @@ -51,6 +53,8 @@ impl AccountStorage { // CONSTRUCTOR // -------------------------------------------------------------------------------------------- + /// TODO(named_slots): Remove this temporary API. + /// /// Returns a new instance of account storage initialized with the provided items. /// /// # Errors @@ -58,12 +62,35 @@ impl AccountStorage { /// Returns an error if: /// - The number of [`StorageSlot`]s exceeds 255. pub fn new(slots: Vec) -> Result { + let slots = slots + .into_iter() + .enumerate() + .map(|(idx, slot)| NamedStorageSlot::new(SlotName::new_index(idx), slot)) + .collect(); + + Self::new_named(slots) + } + + /// TODO(named_slots): Rename to new. + pub fn new_named(mut slots: Vec) -> Result { let num_slots = slots.len(); if num_slots > Self::MAX_NUM_STORAGE_SLOTS { return Err(AccountError::StorageTooManySlots(num_slots as u64)); } + let mut names = BTreeSet::new(); + for slot in &slots { + if !names.insert(slot.name()) { + // TODO(named_slots): Return error. + // TODO(named_slots): Add test for this new error. + todo!("error: storage slot name {} is assigned to more than one slot", slot.name()) + } + } + + // Unstable sort is fine because we require all names to be unique. + slots.sort_unstable(); + Ok(Self { slots }) } @@ -85,23 +112,47 @@ impl AccountStorage { account_type: AccountType, ) -> Result { let mut storage_slots = match account_type { - AccountType::FungibleFaucet => vec![StorageSlot::empty_value()], - AccountType::NonFungibleFaucet => vec![StorageSlot::empty_map()], + AccountType::FungibleFaucet => { + vec![NamedStorageSlot::new(SlotName::new_index(0), StorageSlot::empty_value())] + }, + AccountType::NonFungibleFaucet => { + vec![NamedStorageSlot::new(SlotName::new_index(0), StorageSlot::empty_map())] + }, _ => vec![], }; - storage_slots - .extend(components.iter().flat_map(|component| component.storage_slots()).cloned()); + let offset = storage_slots.len(); + + for (slot_idx, slot) in components + .iter() + .flat_map(|component| component.storage_slots()) + .cloned() + .enumerate() + { + let name = SlotName::new_index(slot_idx + offset); + storage_slots.push(NamedStorageSlot::new(name, slot)); + } - Self::new(storage_slots) + Self::new_named(storage_slots) } // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns a commitment to this storage. - pub fn commitment(&self) -> Word { - build_slots_commitment(&self.slots) + /// Converts storage slots of this account storage into a vector of field elements. + /// + /// Each storage slot is represented by exactly 8 elements: + /// + /// ```text + /// [[0, slot_type, name_id_suffix, name_id_prefix], SLOT_VALUE] + /// ``` + pub fn to_elements(&self) -> Vec { + ::to_elements(self) + } + + /// Returns the commitment to the [`AccountStorage`]. + pub fn to_commitment(&self) -> Word { + ::to_commitment(self) } /// Returns the number of slots in the account's storage. @@ -112,34 +163,58 @@ impl AccountStorage { } /// Returns a reference to the storage slots. - pub fn slots(&self) -> &[StorageSlot] { + pub fn slots(&self) -> &[NamedStorageSlot] { &self.slots } /// Consumes self and returns the storage slots of the account storage. - pub fn into_slots(self) -> Vec { + pub fn into_slots(self) -> Vec { self.slots } /// Returns an [AccountStorageHeader] for this account storage. pub fn to_header(&self) -> AccountStorageHeader { AccountStorageHeader::new( - self.slots.iter().map(|slot| (slot.slot_type(), slot.value())).collect(), + self.slots + .iter() + .map(|slot| { + ( + slot.name().clone(), + slot.storage_slot().slot_type(), + slot.storage_slot().value(), + ) + }) + .collect(), ) } + pub fn get(&self, slot_name: &SlotName) -> Option<&NamedStorageSlot> { + debug_assert!(self.slots.is_sorted()); + + let name_id = slot_name.compute_id(); + self.slots + .binary_search_by_key(&name_id, |named_slot| named_slot.name_id()) + .map(|idx| &self.slots[idx]) + .ok() + } + + fn get_mut(&mut self, slot_name: &SlotName) -> Option<&mut NamedStorageSlot> { + let name_id = slot_name.compute_id(); + self.slots + .binary_search_by_key(&name_id, |named_slot| named_slot.name_id()) + .map(|idx| &mut self.slots[idx]) + .ok() + } + /// Returns an item from the storage at the specified index. /// /// # Errors: /// - If the index is out of bounds pub fn get_item(&self, index: u8) -> Result { - self.slots - .get(index as usize) - .ok_or(AccountError::StorageIndexOutOfBounds { - slots_len: self.slots.len() as u8, - index, - }) - .map(|slot| slot.value()) + let slot_name = SlotName::new_index(index as usize); + self.get(&slot_name) + .map(|named_slot| named_slot.storage_slot().value()) + .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }) } /// Returns a map item from a map located in storage at the specified index. @@ -148,24 +223,13 @@ impl AccountStorage { /// - If the index is out of bounds /// - If the [StorageSlot] is not [StorageSlotType::Map] pub fn get_map_item(&self, index: u8, key: Word) -> Result { - match self.slots.get(index as usize).ok_or(AccountError::StorageIndexOutOfBounds { - slots_len: self.slots.len() as u8, - index, - })? { - StorageSlot::Map(map) => Ok(map.get(&key)), - _ => Err(AccountError::StorageSlotNotMap(index)), - } - } - - /// Converts storage slots of this account storage into a vector of field elements. - /// - /// This is done by first converting each storage slot into exactly 8 elements as follows: - /// ```text - /// [STORAGE_SLOT_VALUE, storage_slot_type, 0, 0, 0] - /// ``` - /// And then concatenating the resulting elements into a single vector. - pub fn as_elements(&self) -> Vec { - slots_as_elements(self.slots()) + let slot_name = SlotName::new_index(index as usize); + self.get(&slot_name) + .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }) + .and_then(|named_slot| match named_slot.storage_slot() { + StorageSlot::Map(map) => Ok(map.get(&key)), + _ => Err(AccountError::StorageSlotNotMap(index)), + }) } // STATE MUTATORS @@ -180,12 +244,11 @@ impl AccountStorage { // update storage maps for (&idx, map) in delta.maps().iter() { - let storage_slot = self - .slots - .get_mut(idx as usize) + let named_slot = self + .get_mut(&SlotName::new_index(idx as usize)) .ok_or(AccountError::StorageIndexOutOfBounds { slots_len: len, index: idx })?; - let storage_map = match storage_slot { + let storage_map = match named_slot.storage_slot_mut() { StorageSlot::Map(map) => map, _ => return Err(AccountError::StorageSlotNotMap(idx)), }; @@ -210,24 +273,18 @@ impl AccountStorage { /// - If the index is out of bounds /// - If the [StorageSlot] is not [StorageSlotType::Value] pub fn set_item(&mut self, index: u8, value: Word) -> Result { - // check if index is in bounds - let num_slots = self.slots.len(); - - if index as usize >= num_slots { - return Err(AccountError::StorageIndexOutOfBounds { - slots_len: self.slots.len() as u8, - index, - }); - } + let slot_name = SlotName::new_index(index as usize); + let slot = self.get_mut(&slot_name).ok_or_else(|| { + AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() } + })?; - let old_value = match self.slots[index as usize] { - StorageSlot::Value(value) => value, - // return an error if the type != Value - _ => return Err(AccountError::StorageSlotNotValue(index)), + let StorageSlot::Value(old_value) = slot.storage_slot() else { + return Err(AccountError::StorageSlotNotValue(index)); }; + let old_value = *old_value; - // update the value of the storage slot - self.slots[index as usize] = StorageSlot::Value(value); + let mut new_slot = StorageSlot::Value(value); + core::mem::swap(slot.storage_slot_mut(), &mut new_slot); Ok(old_value) } @@ -243,29 +300,21 @@ impl AccountStorage { pub fn set_map_item( &mut self, index: u8, - key: Word, + raw_key: Word, value: Word, ) -> Result<(Word, Word), AccountError> { - // check if index is in bounds - let num_slots = self.slots.len(); - - if index as usize >= num_slots { - return Err(AccountError::StorageIndexOutOfBounds { - slots_len: self.slots.len() as u8, - index, - }); - } + let slot_name = SlotName::new_index(index as usize); + let slot = self.get_mut(&slot_name).ok_or_else(|| { + AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() } + })?; - let storage_map = match self.slots[index as usize] { - StorageSlot::Map(ref mut map) => map, - _ => return Err(AccountError::StorageSlotNotMap(index)), + let StorageSlot::Map(storage_map) = slot.storage_slot_mut() else { + return Err(AccountError::StorageSlotNotMap(index)); }; - // get old map root to return let old_root = storage_map.root(); - // update the key-value pair in the map - let old_value = storage_map.insert(key, value)?; + let old_value = storage_map.insert(raw_key, value)?; Ok((old_root, old_value)) } @@ -275,29 +324,33 @@ impl AccountStorage { // ================================================================================================ impl IntoIterator for AccountStorage { - type Item = StorageSlot; - type IntoIter = alloc::vec::IntoIter; + type Item = NamedStorageSlot; + type IntoIter = alloc::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.slots.into_iter() } } -// HELPER FUNCTIONS -// ------------------------------------------------------------------------------------------------ - -/// Converts given slots into field elements -fn slots_as_elements(slots: &[StorageSlot]) -> Vec { - slots - .iter() - .flat_map(|slot| StorageSlotHeader::from(slot).as_elements()) - .collect() -} +// SEQUENTIAL COMMIT +// ================================================================================================ -/// Computes the commitment to the given slots -pub fn build_slots_commitment(slots: &[StorageSlot]) -> Word { - let elements = slots_as_elements(slots); - Hasher::hash_elements(&elements) +impl SequentialCommit for AccountStorage { + type Commitment = Word; + + fn to_elements(&self) -> Vec { + self.slots() + .iter() + .flat_map(|named_slot| { + StorageSlotHeader::new( + named_slot.name_id(), + named_slot.storage_slot().slot_type(), + named_slot.storage_slot().value(), + ) + .to_elements() + }) + .collect() + } } // SERIALIZATION @@ -325,9 +378,9 @@ impl Serializable for AccountStorage { impl Deserializable for AccountStorage { fn read_from(source: &mut R) -> Result { let num_slots = source.read_u8()? as usize; - let slots = source.read_many::(num_slots)?; + let slots = source.read_many::(num_slots)?; - Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string())) + Self::new_named(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } @@ -336,15 +389,8 @@ impl Deserializable for AccountStorage { #[cfg(test)] mod tests { - use super::{ - AccountStorage, - Deserializable, - Serializable, - StorageMap, - Word, - build_slots_commitment, - }; - use crate::account::StorageSlot; + use super::{AccountStorage, Deserializable, Serializable, StorageMap, Word}; + use crate::account::{NamedStorageSlot, SlotName, StorageSlot}; #[test] fn test_serde_account_storage() { @@ -364,9 +410,22 @@ mod tests { } #[test] - fn test_account_storage_slots_commitment() { - let storage = AccountStorage::mock(); - let storage_slots_commitment = build_slots_commitment(storage.slots()); - assert_eq!(storage_slots_commitment, storage.commitment()) + fn test_get_slot_by_name() -> anyhow::Result<()> { + // TODO(named_slots): Use proper names. + // const COUNTER_SLOT: SlotName = SlotName::from_static_str("miden::test::counter"); + // const MAP_SLOT: SlotName = SlotName::from_static_str("miden::test::map"); + const COUNTER_SLOT: SlotName = SlotName::from_static_str("miden::0"); + const MAP_SLOT: SlotName = SlotName::from_static_str("miden::4"); + + let slots = vec![ + NamedStorageSlot::new(COUNTER_SLOT, StorageSlot::empty_value()), + NamedStorageSlot::new(MAP_SLOT, StorageSlot::empty_map()), + ]; + let storage = AccountStorage::new_named(slots.clone())?; + + assert_eq!(storage.get(&COUNTER_SLOT).unwrap(), &slots[0]); + assert_eq!(storage.get(&MAP_SLOT).unwrap(), &slots[1]); + + Ok(()) } } diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-objects/src/account/storage/partial.rs index 53c90828a7..f7f268ab95 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-objects/src/account/storage/partial.rs @@ -47,7 +47,7 @@ impl PartialStorage { maps.insert(smt.root(), smt); } - let commitment = storage_header.compute_commitment(); + let commitment = storage_header.to_commitment(); Ok(Self { commitment, header: storage_header, maps }) } @@ -57,11 +57,11 @@ impl PartialStorage { /// in all map slots of the account storage. pub fn new_full(account_storage: AccountStorage) -> Self { let header: AccountStorageHeader = account_storage.to_header(); - let commitment = header.compute_commitment(); + let commitment = header.to_commitment(); let mut maps = BTreeMap::new(); for slot in account_storage { - if let StorageSlot::Map(storage_map) = slot { + if let StorageSlot::Map(storage_map) = slot.into_parts().2 { let partial_map = PartialStorageMap::new_full(storage_map); maps.insert(partial_map.root(), partial_map); } @@ -76,11 +76,11 @@ impl PartialStorage { /// [`PartialStorageMap`] represents the correct root. pub fn new_minimal(account_storage: &AccountStorage) -> Self { let header: AccountStorageHeader = account_storage.to_header(); - let commitment = header.compute_commitment(); + let commitment = header.to_commitment(); let mut maps = BTreeMap::new(); for slot in account_storage.slots() { - if let StorageSlot::Map(storage_map) = slot { + if let StorageSlot::Map(storage_map) = slot.storage_slot() { let partial_map = PartialStorageMap::new_minimal(storage_map); maps.insert(partial_map.root(), partial_map); } @@ -145,7 +145,7 @@ impl Deserializable for PartialStorage { let header: AccountStorageHeader = source.read()?; let map_smts: BTreeMap = source.read()?; - let commitment = header.compute_commitment(); + let commitment = header.to_commitment(); Ok(PartialStorage { header, maps: map_smts, commitment }) } @@ -185,7 +185,8 @@ mod tests { PartialStorage::new(storage_header, [PartialStorageMap::with_witnesses([witness])?]) .context("creating partial storage")?; - let retrieved_map = partial_storage.maps.get(&partial_storage.header.slot(0)?.1).unwrap(); + let retrieved_map = + partial_storage.maps.get(partial_storage.header.slot_header(0)?.2).unwrap(); assert!(retrieved_map.open(&map_key_absent).is_err()); assert!(retrieved_map.open(&map_key_present).is_ok()); Ok(()) diff --git a/crates/miden-objects/src/account/storage/slot/mod.rs b/crates/miden-objects/src/account/storage/slot/mod.rs index 9f6389c38b..8f27fa0550 100644 --- a/crates/miden-objects/src/account/storage/slot/mod.rs +++ b/crates/miden-objects/src/account/storage/slot/mod.rs @@ -8,9 +8,15 @@ use super::{StorageMap, Word}; mod slot_name; pub use slot_name::SlotName; +mod slot_name_id; +pub use slot_name_id::SlotNameId; + mod r#type; pub use r#type::StorageSlotType; +mod named_slot; +pub use named_slot::NamedStorageSlot; + // STORAGE SLOT // ================================================================================================ diff --git a/crates/miden-objects/src/account/storage/slot/named_slot.rs b/crates/miden-objects/src/account/storage/slot/named_slot.rs new file mode 100644 index 0000000000..975c4ed2a3 --- /dev/null +++ b/crates/miden-objects/src/account/storage/slot/named_slot.rs @@ -0,0 +1,79 @@ +use crate::account::storage::slot::SlotNameId; +use crate::account::{SlotName, StorageSlot}; + +// TODO(named_slots): Docs + separators for the entire module. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NamedStorageSlot { + /// The name of the storage slot. + name: SlotName, + /// The cached [`SlotNameId`] of the slot name. These must always be consistent with each + /// other. + /// + /// This is cached so that the `Ord` implementation can use the computed name ID instead of + /// having to hash the slot name on every comparison operation. + name_id: SlotNameId, + /// The underlying storage slot. + slot: StorageSlot, +} + +impl NamedStorageSlot { + pub fn new(name: SlotName, slot: StorageSlot) -> Self { + let name_id = name.compute_id(); + + Self { name, name_id, slot } + } + + pub fn name(&self) -> &SlotName { + &self.name + } + + pub fn name_id(&self) -> SlotNameId { + self.name_id + } + + pub fn storage_slot(&self) -> &StorageSlot { + &self.slot + } + + pub fn storage_slot_mut(&mut self) -> &mut StorageSlot { + &mut self.slot + } + + pub fn into_parts(self) -> (SlotName, SlotNameId, StorageSlot) { + (self.name, self.name_id, self.slot) + } +} + +impl Ord for NamedStorageSlot { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.name_id.cmp(&other.name_id) + } +} + +impl PartialOrd for NamedStorageSlot { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl crate::utils::serde::Serializable for NamedStorageSlot { + fn write_into(&self, target: &mut W) { + target.write(&self.name); + target.write(&self.slot); + } + + fn get_size_hint(&self) -> usize { + self.name.get_size_hint() + self.storage_slot().get_size_hint() + } +} + +impl crate::utils::serde::Deserializable for NamedStorageSlot { + fn read_from( + source: &mut R, + ) -> Result { + let name: SlotName = source.read()?; + let slot: StorageSlot = source.read()?; + + Ok(Self::new(name, slot)) + } +} diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-objects/src/account/storage/slot/slot_name.rs index 0efbb96a97..7cf7673115 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_name.rs @@ -1,7 +1,11 @@ use alloc::borrow::Cow; -use alloc::string::String; +use alloc::string::{String, ToString}; +use core::fmt::Display; +use crate::account::storage::slot::SlotNameId; use crate::errors::SlotNameError; +use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Serializable}; +use crate::{Felt, FieldElement}; /// The name of an account storage slot. /// @@ -18,12 +22,13 @@ use crate::errors::SlotNameError; /// structure: /// /// ```text -/// organization::component::slot_name +/// project_name::component_name::slot_name /// ``` /// /// ## Requirements /// /// For a string to be a valid slot name it needs to satisfy the following criteria: +/// - Its length must be less than 255. /// - It needs to have at least 2 components. /// - Each component must consist of at least one character. /// - Each component must only consist of the characters `a` to `z`, `A` to `Z`, `0` to `9` or `_` @@ -38,9 +43,12 @@ impl SlotName { // CONSTANTS // -------------------------------------------------------------------------------------------- - // The minimum number of components that a slot name must contain. + /// The minimum number of components that a slot name must contain. pub(crate) const MIN_NUM_COMPONENTS: usize = 2; + /// The maximum number of characters in a slot name. + pub(crate) const MAX_LENGTH: usize = u8::MAX as usize; + // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -79,6 +87,11 @@ impl SlotName { Ok(Self { name: Cow::Owned(name) }) } + // TODO(named_slots): Temporary. Remove later. + pub fn new_index(slot_idx: usize) -> Self { + SlotName::new(format!("miden::{slot_idx}")).expect("slot name should be valid") + } + // ACCESSORS // -------------------------------------------------------------------------------------------- @@ -87,6 +100,37 @@ impl SlotName { &self.name } + /// Returns the slot name as a string slice. + // allow is_empty to be missing because it would always return false since slot names are + // enforced to have a length greater than zero, so it does not have much use. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> u8 { + // SAFETY: Slot name validation should enforce length fits into a u8. + debug_assert!(self.name.len() <= Self::MAX_LENGTH); + self.name.len() as u8 + } + + // TODO(named_slots): Docs. + pub fn compute_id(&self) -> SlotNameId { + // let hashed_word = hash_string_to_word(self.as_str()); + // let prefix = hashed_word[0]; + // let suffix = hashed_word[1]; + // SlotNameId::new(prefix, suffix) + + // TODO: Temporary, replace later with the above. + let mut split = self.as_str().split("::"); + + let namespace = split.next().unwrap(); + assert_eq!(namespace, "miden"); + + let slot_idx = split.next().unwrap(); + let slot_index: u32 = slot_idx.parse().expect( + "named storage slots should for now have the slot index as the second component", + ); + + SlotNameId::new(Felt::from(slot_index), Felt::ZERO) + } + // HELPERS // -------------------------------------------------------------------------------------------- @@ -114,6 +158,10 @@ impl SlotName { return Err(SlotNameError::TooShort); } + if bytes.len() > Self::MAX_LENGTH { + return Err(SlotNameError::TooLong); + } + // Slot names must not start with a colon or underscore. // SAFETY: We just checked that we're not dealing with an empty slice. if bytes[0] == b':' { @@ -178,11 +226,45 @@ impl SlotName { } } +impl Display for SlotName { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl Serializable for SlotName { + fn write_into(&self, target: &mut W) { + target.write_u8(self.len()); + target.write_many(self.as_str().as_bytes()) + } + + fn get_size_hint(&self) -> usize { + // Slot name length + slot name bytes + 1 + self.as_str().len() + } +} + +impl Deserializable for SlotName { + fn read_from( + source: &mut R, + ) -> Result { + let len = source.read_u8()?; + let name = source.read_many(len as usize)?; + String::from_utf8(name) + .map_err(|err| DeserializationError::InvalidValue(err.to_string())) + .and_then(|name| { + Self::new(name).map_err(|err| DeserializationError::InvalidValue(err.to_string())) + }) + } +} + // TESTS // ================================================================================================ #[cfg(test)] mod tests { + use std::borrow::ToOwned; + use assert_matches::assert_matches; use super::*; @@ -256,7 +338,7 @@ mod tests { ); } - // Num components tests + // Length validation tests // -------------------------------------------------------------------------------------------- #[test] @@ -269,6 +351,13 @@ mod tests { assert_matches!(SlotName::new("single_component").unwrap_err(), SlotNameError::TooShort); } + #[test] + fn slot_name_fails_on_string_whose_length_exceeds_max_length() { + let mut string = get_max_length_slot_name(); + string.push('a'); + assert_matches!(SlotName::new(string).unwrap_err(), SlotNameError::TooLong); + } + // Alphabet validation tests // -------------------------------------------------------------------------------------------- @@ -311,4 +400,39 @@ mod tests { SlotName::new("miden::faucet0::fungible_1::b4sic::metadata")?; Ok(()) } + + #[test] + fn slot_name_with_max_length_is_valid() -> anyhow::Result<()> { + SlotName::new(get_max_length_slot_name())?; + Ok(()) + } + + // Serialization tests + // -------------------------------------------------------------------------------------------- + + #[test] + fn serde_slot_name() -> anyhow::Result<()> { + let slot_name = SlotName::new("miden::faucet0::fungible_1::b4sic::metadata")?; + assert_eq!(slot_name, SlotName::read_from_bytes(&slot_name.to_bytes())?); + Ok(()) + } + + #[test] + fn serde_max_length_slot_name() -> anyhow::Result<()> { + let slot_name = SlotName::new(get_max_length_slot_name())?; + assert_eq!(slot_name, SlotName::read_from_bytes(&slot_name.to_bytes())?); + Ok(()) + } + + // Test helpers + // -------------------------------------------------------------------------------------------- + + fn get_max_length_slot_name() -> String { + const MIDEN_STR: &str = "miden::"; + let remainder = ['a'; SlotName::MAX_LENGTH - MIDEN_STR.len()]; + let mut string = MIDEN_STR.to_owned(); + string.extend(remainder); + assert_eq!(string.len(), SlotName::MAX_LENGTH); + string + } } diff --git a/crates/miden-objects/src/account/storage/slot/slot_name_id.rs b/crates/miden-objects/src/account/storage/slot/slot_name_id.rs new file mode 100644 index 0000000000..2dbd3e29a2 --- /dev/null +++ b/crates/miden-objects/src/account/storage/slot/slot_name_id.rs @@ -0,0 +1,39 @@ +use core::cmp::Ordering; + +use crate::Felt; + +// TODO(named_slots): Docs + separators for the entire module. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SlotNameId { + prefix: Felt, + suffix: Felt, +} + +impl SlotNameId { + pub fn new(prefix: Felt, suffix: Felt) -> Self { + Self { prefix, suffix } + } + + pub fn prefix(&self) -> Felt { + self.prefix + } + + pub fn suffix(&self) -> Felt { + self.suffix + } +} + +impl Ord for SlotNameId { + fn cmp(&self, other: &Self) -> Ordering { + match self.prefix.as_int().cmp(&other.prefix.as_int()) { + ord @ Ordering::Less | ord @ Ordering::Greater => ord, + Ordering::Equal => self.suffix.as_int().cmp(&other.suffix.as_int()), + } + } +} + +impl PartialOrd for SlotNameId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/crates/miden-objects/src/account/storage/slot/type.rs b/crates/miden-objects/src/account/storage/slot/type.rs index bb5d9f2645..a02b830154 100644 --- a/crates/miden-objects/src/account/storage/slot/type.rs +++ b/crates/miden-objects/src/account/storage/slot/type.rs @@ -1,4 +1,4 @@ -use alloc::string::{String, ToString}; +use alloc::string::ToString; use crate::utils::serde::{ ByteReader, @@ -7,27 +7,27 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{Felt, Word}; +use crate::{AccountError, Felt}; // STORAGE SLOT TYPE // ================================================================================================ -/// An object that represents the type of a storage slot. +/// The type of a storage slot. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] pub enum StorageSlotType { /// Represents a slot that contains a value. - Value, + Value = Self::VALUE_TYPE, /// Represents a slot that contains a commitment to a map with key-value pairs. - Map, + Map = Self::MAP_TYPE, } impl StorageSlotType { - /// Returns storage slot type as a [Word] - pub fn as_word(&self) -> Word { - match self { - StorageSlotType::Value => Word::empty(), - StorageSlotType::Map => Word::from([1, 0, 0, 0u32]), - } + const VALUE_TYPE: u8 = 0; + const MAP_TYPE: u8 = 1; + + pub fn as_felt(&self) -> Felt { + Felt::from(*self as u8) } /// Returns `true` if the slot is a value slot, `false` otherwise. @@ -41,16 +41,14 @@ impl StorageSlotType { } } -impl TryFrom for StorageSlotType { - type Error = String; - - fn try_from(value: Felt) -> Result { - let value = value.as_int(); +impl TryFrom for StorageSlotType { + type Error = AccountError; + fn try_from(value: u8) -> Result { match value { - 0 => Ok(StorageSlotType::Value), - 1 => Ok(StorageSlotType::Map), - _ => Err("No storage slot type exists for this field element.".to_string()), + Self::VALUE_TYPE => Ok(StorageSlotType::Value), + Self::MAP_TYPE => Ok(StorageSlotType::Map), + _ => Err(AccountError::other(format!("unsupported storage slot type {value}"))), } } } @@ -77,8 +75,8 @@ impl Deserializable for StorageSlotType { let storage_slot_type = source.read_u8()?; match storage_slot_type { - 0 => Ok(Self::Value), - 1 => Ok(Self::Map), + Self::VALUE_TYPE => Ok(Self::Value), + Self::MAP_TYPE => Ok(Self::Map), _ => Err(DeserializationError::InvalidValue(storage_slot_type.to_string())), } } diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 077aac0bc2..2cecab8277 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -150,6 +150,8 @@ pub enum AccountError { StorageSlotNotValue(u8), #[error("storage slot index is {index} but the slots length is {slots_len}")] StorageIndexOutOfBounds { slots_len: u8, index: u8 }, + #[error("storage does not contain a slot with name {slot_name}")] + StorageSlotNameNotFound { slot_name: SlotName }, #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)] StorageTooManySlots(u64), #[error("procedure storage offset + size is {0} which exceeds the maximum value of {max}", @@ -247,6 +249,8 @@ pub enum SlotNameError { SlotName::MIN_NUM_COMPONENTS )] TooShort, + #[error("slot names must contain at most {} characters", SlotName::MAX_LENGTH)] + TooLong, } // ACCOUNT TREE ERROR diff --git a/crates/miden-objects/src/testing/storage.rs b/crates/miden-objects/src/testing/storage.rs index 85f8f673f5..33422fd54c 100644 --- a/crates/miden-objects/src/testing/storage.rs +++ b/crates/miden-objects/src/testing/storage.rs @@ -67,6 +67,7 @@ impl AccountStorageDeltaBuilder { // ACCOUNT STORAGE UTILS // ================================================================================================ +#[derive(Debug)] pub struct SlotWithIndex { pub slot: StorageSlot, pub index: u8, diff --git a/crates/miden-objects/src/transaction/inputs/account.rs b/crates/miden-objects/src/transaction/inputs/account.rs index 4d7dd2faf9..83d0cb7488 100644 --- a/crates/miden-objects/src/transaction/inputs/account.rs +++ b/crates/miden-objects/src/transaction/inputs/account.rs @@ -112,7 +112,7 @@ mod tests { let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); let code = AccountCode::mock(); let vault = AssetVault::new(&[]).unwrap(); - let storage = AccountStorage::new(vec![]).unwrap(); + let storage = AccountStorage::new_named(vec![]).unwrap(); let account = Account::new_existing(id, vault, storage, code, Felt::new(10)); let commitment = account.commitment(); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index a4f5a4398a..210d2f4f92 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -31,6 +31,7 @@ use miden_objects::account::{ AccountType, StorageMap, StorageSlot, + StorageSlotType, }; use miden_objects::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette}; use miden_objects::assembly::{DefaultSourceManager, Library}; @@ -482,7 +483,7 @@ async fn test_get_storage_slot_type() -> miette::Result<()> { push.{item_index} # get the type of the respective storage slot - exec.account::get_storage_slot_type + exec.account::get_storage_slot_type_by_index # truncate the stack swap drop @@ -495,7 +496,13 @@ async fn test_get_storage_slot_type() -> miette::Result<()> { let storage_slot_type = storage_item.slot.slot_type(); - assert_eq!(storage_slot_type, exec_output.get_stack_element(0).try_into().unwrap()); + assert_eq!( + storage_slot_type, + StorageSlotType::try_from( + u8::try_from(exec_output.get_stack_element(0).as_int()).unwrap() + ) + .unwrap() + ); assert_eq!(exec_output.get_stack_element(1), ZERO, "the rest of the stack is empty"); assert_eq!(exec_output.get_stack_element(2), ZERO, "the rest of the stack is empty"); assert_eq!(exec_output.get_stack_element(3), ZERO, "the rest of the stack is empty"); @@ -818,10 +825,10 @@ async fn create_procedure_metadata_test_account( AccountStorageMode::Private, version, code.commitment(), - storage.commitment(), + storage.to_commitment(), ) .context("failed to compute seed")?; - let id = AccountId::new(seed, version, code.commitment(), storage.commitment()) + let id = AccountId::new(seed, version, code.commitment(), storage.to_commitment()) .context("failed to compute ID")?; let account = @@ -918,7 +925,7 @@ async fn test_get_initial_storage_commitment() -> anyhow::Result<()> { assert_eqw.err="actual storage commitment is not equal to the expected one" end "#, - expected_storage_commitment = &tx_context.account().storage().commitment(), + expected_storage_commitment = &tx_context.account().storage().to_commitment(), ); tx_context.execute_code(&code).await?; @@ -940,17 +947,17 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { let mut account_clone = tx_context.account().clone(); let account_storage = account_clone.storage_mut(); - let init_storage_commitment = account_storage.commitment(); + let init_storage_commitment = account_storage.to_commitment(); account_storage.set_item(0, [9, 10, 11, 12].map(Felt::new).into())?; - let storage_commitment_0 = account_storage.commitment(); + let storage_commitment_0 = account_storage.to_commitment(); account_storage.set_map_item( 2, [101, 102, 103, 104].map(Felt::new).into(), [5, 6, 7, 8].map(Felt::new).into(), )?; - let storage_commitment_2 = account_storage.commitment(); + let storage_commitment_2 = account_storage.to_commitment(); let code = format!( r#" diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index 7eab71ff10..2280e6e318 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -88,7 +88,7 @@ async fn test_mint_fungible_asset_succeeds() -> anyhow::Result<()> { let expected_final_storage_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE + FUNGIBLE_ASSET_AMOUNT; let faucet_reserved_slot_storage_location = FAUCET_STORAGE_DATA_SLOT as u32 + NATIVE_ACCT_STORAGE_SLOTS_SECTION_PTR; - let faucet_storage_amount_location = faucet_reserved_slot_storage_location + 3; + let faucet_storage_amount_location = faucet_reserved_slot_storage_location + 7; let faucet_storage_amount = exec_output.get_kernel_mem_element(faucet_storage_amount_location).as_int(); @@ -381,7 +381,7 @@ async fn test_burn_fungible_asset_succeeds() -> anyhow::Result<()> { let expected_final_storage_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE - FUNGIBLE_ASSET_AMOUNT; let faucet_reserved_slot_storage_location = FAUCET_STORAGE_DATA_SLOT as u32 + NATIVE_ACCT_STORAGE_SLOTS_SECTION_PTR; - let faucet_storage_amount_location = faucet_reserved_slot_storage_location + 3; + let faucet_storage_amount_location = faucet_reserved_slot_storage_location + 7; let faucet_storage_amount = exec_output.get_kernel_mem_element(faucet_storage_amount_location).as_int(); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index aabe11c7a6..d7f55db52a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -1764,7 +1764,7 @@ fn foreign_account_data_memory_assertions( assert_eq!( exec_output.get_kernel_mem_word(foreign_account_data_ptr + ACCT_STORAGE_COMMITMENT_OFFSET), - foreign_account.storage().commitment(), + foreign_account.storage().to_commitment(), ); assert_eq!( @@ -1779,7 +1779,7 @@ fn foreign_account_data_memory_assertions( for (i, elements) in foreign_account .storage() - .as_elements() + .to_elements() .chunks(StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT / 2) .enumerate() { diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 6c0fd678a9..8cc034e102 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -199,7 +199,7 @@ fn global_input_memory_assertions(exec_output: &ExecutionOutput, inputs: &Transa assert_eq!( exec_output.get_kernel_mem_word(INIT_NATIVE_ACCT_STORAGE_COMMITMENT_PTR), - inputs.account().storage().commitment(), + inputs.account().storage().to_commitment(), "The initial native account storage commitment should be stored at the INIT_ACCT_STORAGE_COMMITMENT_PTR" ); @@ -393,7 +393,7 @@ fn account_data_memory_assertions(exec_output: &ExecutionOutput, inputs: &Transa assert_eq!( exec_output.get_kernel_mem_word(NATIVE_ACCT_STORAGE_COMMITMENT_PTR), - inputs.account().storage().commitment(), + inputs.account().storage().to_commitment(), "The account storage commitment should be stored at NATIVE_ACCT_STORAGE_COMMITMENT_PTR" ); @@ -412,7 +412,7 @@ fn account_data_memory_assertions(exec_output: &ExecutionOutput, inputs: &Transa for (i, elements) in inputs .account() .storage() - .as_elements() + .to_elements() .chunks(StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT / 2) .enumerate() { @@ -592,7 +592,9 @@ pub async fn create_multiple_accounts_test(storage_mode: AccountStorageMode) -> [255u32; WORD_SIZE], ))])) .build() - .context("account build failed")?; + .with_context(|| { + format!("account build for {account_type} and {storage_mode} failed") + })?; accounts.push(account); } @@ -627,7 +629,7 @@ fn compute_valid_account_id(account: Account) -> Account { AccountStorageMode::Public, AccountIdVersion::Version0, account.code().commitment(), - account.storage().commitment(), + account.storage().to_commitment(), ) .unwrap(); @@ -635,7 +637,7 @@ fn compute_valid_account_id(account: Account) -> Account { seed, AccountIdVersion::Version0, account.code().commitment(), - account.storage().commitment(), + account.storage().to_commitment(), ) .unwrap(); diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index e3ccf6b1db..bea15c6a2f 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -328,7 +328,7 @@ impl DataStore for TransactionContext { .storage() .slots() .iter() - .find_map(|slot| match slot { + .find_map(|named_slot| match named_slot.storage_slot() { StorageSlot::Map(storage_map) if storage_map.root() == map_root => { Some(storage_map) }, @@ -360,7 +360,7 @@ impl DataStore for TransactionContext { .storage() .slots() .iter() - .find_map(|slot| match slot { + .find_map(|named_slot| match named_slot.storage_slot() { StorageSlot::Map(storage_map) if storage_map.root() == map_root => {Some(storage_map)}, _ => None, }) diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index 81da62182b..55e142ee6d 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -59,17 +59,17 @@ impl StorageDeltaTracker { // Insert account storage into delta if it is new to match the kernel behavior. if account.is_new() { (0..u8::MAX).zip(account.storage().header().slots()).for_each( - |(slot_idx, (slot_type, value))| match slot_type { + |(slot_idx, (_, slot_type, slot_value))| match slot_type { StorageSlotType::Value => { // For new accounts, all values should be added to the delta, even empty // words, so that the final delta includes the storage slot. - storage_delta_tracker.set_item(slot_idx, *value); + storage_delta_tracker.set_item(slot_idx, *slot_value); }, StorageSlotType::Map => { let storage_map = account .storage() .maps() - .find(|map| map.root() == *value) + .find(|map| map.root() == *slot_value) .expect("storage map should be present in partial storage"); // Make sure each map is represented by at least an empty storage map delta. @@ -147,8 +147,9 @@ impl StorageDeltaTracker { // SAFETY: The header in the initial storage is the one from the account against // which the transaction is executed, so accessing that slot index // should be fine. - let (_, initial_value) = - storage_header.slot(*slot_idx as usize).expect("index should be in bounds"); + let (_, _, initial_value) = storage_header + .slot_header(*slot_idx as usize) + .expect("index should be in bounds"); new_value != initial_value }); } @@ -185,9 +186,9 @@ fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorage .storage() .header() .slots() - .map(|(slot_type, _)| match slot_type { - StorageSlotType::Value => (*slot_type, Word::empty()), - StorageSlotType::Map => (*slot_type, StorageMap::new().root()), + .map(|(slot_name, slot_type, _)| match slot_type { + StorageSlotType::Value => (slot_name.clone(), *slot_type, Word::empty()), + StorageSlotType::Map => (slot_name.clone(), *slot_type, StorageMap::new().root()), }) .collect(); AccountStorageHeader::new(slots) diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index d233965c45..4b371141ba 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -618,10 +618,10 @@ fn on_account_storage_map_item_accessed<'store, STORE>( // For native accounts, we have to request witnesses against the initial // root instead of the _current_ one, since the data // store only has witnesses for initial one. - let (slot_type, slot_value) = base_host + let (_slot_name, slot_type, slot_value) = base_host .initial_account_storage_header() // Slot index should always fit into a usize. - .slot(slot_index as usize) + .slot_header(slot_index as usize) .map_err(|err| { TransactionKernelError::other_with_source( "failed to access storage map in storage header", From 150a8066c5a4b4011c4f3e55f9435921ad3835f3 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 9 Dec 2025 13:35:37 +0700 Subject: [PATCH 029/114] feat: implement API changes for named storage slots (#2025) * feat: Implement `NamedStorageSlot` * feat: Use `find_half_key_value` * feat: Implement `AccountStorage::get` * feat: Reimplement account storage APIs * chore: Add compat APIs * feat: Reinstantiate apply_delta and reimpl set_item_raw * fix: get_storage_slot_type * fix: name layout in memory * feat: Implement get_size_hint for named slot and slot name * chore: Reinstantiate commented tests * feat: Implement `SequentialCommit` and make `StorageSlotHeader` private * chore: Various cleanup * fix: toml fmt and `typ` "typo" * chore: Add changelog entry * feat: Enforce max length on `SlotName` * chore: Emit event earlier in `get_map_item_raw` * feat: Update docs of storage slot header and account storage header * chore: Document `NamedStorageSlot` fields * chore: Rename `get_storage_slot_ptr` -> `find_storage_slot` * chore: use branch from VM PR * chore: Bump miden-vm to 0.18.3 * feat: Convert `account::get_item` to use names * feat: Convert `account::get_initial_item` to use names * feat: Convert `account::set_map_item` to use names * feat: Impl `Display` for `SlotNameId` * feat: Convert on_account_storage_before_set_map_item to use names * feat: Convert `account::get_(initial_)map_item` to use names * feat: Convert `account::set_item` to use names * feat: Update `set_(map_)item` event handlers * feat: Convert miden-{objects, lib, tx} to use names * feat: Update storage and storage delta to use names * feat: Update miden-objects tests to use names * fix: miden-air import in miden-objects * chore: Deactivate `AccountComponentTemplate` code * feat: Convert `miden-lib` to use names * feat: Convert `miden-tx` to use names * fixup! feat: Convert `miden-lib` to use names * feat: Update account delta commit. computation and set_map_item * feat: Convert MockAccountCode to use names * chore: Update faucet data storage slot in masm * fixup! feat: Convert `account::get_(initial_)map_item` to use names * chore: Add todo for protocol library docs * fix: set_map_item and get_item * feat: Update prologue new faucet validation * chore: Add `NamedStorageSlot::slot_type` * chore: Update storage API tests * chore: Remove storage offset related tests * fix: reinsert missing `and` * fix: add storage commitment dirty flag * fix: get_item_delta impl * chore: Update storage commitment and proven TX test * chore: Use miden-air from crates.io * chore: Update MASM auth components to use names * feat: Convert `test_account` to use names * chore: Compile mock account code in debug mode if enabled * fixup! feat: Convert `test_account` to use names * feat: Convert `test_account_delta` to use names * feat: Convert `test_{lazy_loading, tx, prologue}` to use names * chore: Convert chain builder to use names * chore: Convert proven block error test to use names * fix: Move dropw from account::set_map_item to api.masm * feat: Update `test_fee` to use names * feat: Convert `test_faucet` to use names * feat: Convert `test_epilogue` to use names * feat: Convert `test_fpi` to use names * feat: Convert wallet and auth tests to use names * feat: Update basic fungible faucet MASM to use named slot * chore: Remove unused function; move stack truncation to caller * feat: Refactor set_map_item to not store name ID * chore: Remove unused `$kernel::account` procedures * feat: Document slot name getters * chore: Update account delta ambiguity examples * feat: Make `AccountStorageHeader::new` fallible * feat: Return error on duplicate storage slot names * feat: Document [`NamedStorageSlot`] * feat: Document `SlotNameId` * chore: Document `SlotName::compute_id` * fix: ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME naming * chore: Remove `SlotName::random` due to no-std incompatibility * chore: Remove outdated TODO(kernel_stack_underflow) * chore: add changelog * chore: Deactivate templates differently to reduce diff * fix: storage map delta test, typos, rustfmt * fix: doc build and slot name import * chore: Use ACCOUNT_STORAGE_SLOT_DATA_LENGTH instead of magic constant * chore: add TODO for faucet data slot rename * fix: incorrect error in get_map_item and u32 validity * feat: Update storage layout docs of std components * chore: Replace `NamedStorageSlot::new` with better constructors * chore: remove outdated slot layout * chore: Add docs for memory constants * chore: Reorder slot name ID for consistency * chore: Expand slot name ID docs * chore: use more interesting test case * chore: Avoid reimplementing find logic in storage header * chore: Update `AccountStorage` docs * chore: Avoid using `SlotName::from_static_str` * chore: Unify named storage slot constructor names * chore: Comment out `AccountComponentTemplateError` * chore: Improve error names and messages * chore: Rename testing slot name constants * chore: Improve `AccountStorage::mock_*_slot` names * chore: Use mock slot names consistently * chore: Remove unnecessary named storage slot test helpers * chore: Use constant for slot value offset instead of magic num * chore: Remove development lints * chore: Remove proto library todo * chore: Use `get_item_raw` instead of `mem_loadw` * feat: Abstract over setting the slot item via `set_item_raw` * chore: Rename `SlotName::new_test` to `mock` * chore: Address post-merge changes * chore: move changelog entry to 0.13 and remove duplicate entries * fix: make format * fix: make toml * chore: remove leftover println * chore: Document `set_map_item_raw` * chore: Use `get_storage_slot_type` instead * chore: Use BTreeSet to check for name uniqueness * chore: Rename `name_id` to `id` * chore: Use `project_name` in slot name example * chore: Remove `SlotName::is_empty` * fix: remove dup changelog entries * chore: Revive `AccountStorageHeader::is_map_slot` * chore: Use slot content and just slot ID without name * chore: Describe storage slot layout in memory.rs * chore: update comments in account comp interface * chore: Encapsulate storage slot lookup into base host method * chore: Use `miden::standards::auth` prefix for auth comp slot names * chore: Add missing docs for `is_faucet_storage_data_slot` * chore: Update `AccountStorage` docs * fix: read owner from owner instead of metadata slot * chore: regengerate kernel procedure files * chore: Correct `find_storage_slot` docs * chore: Drop `.` from `proc.get_item_raw` * chore: Drop unnecessary `ProcessState` param from `get_storage_slot` * chore: Drop `.` from `proc.slot_ptr_to_index` * chore: Move local docs to proc signature * fix: correct stack comment `slot_ptr` -> `slot_type` * chore: Remove `active_account::{set_item,set_map_item}` * chore: Combine stack truncation into single line * chore: Rename to `get_faucet_storage_slot_id` * chore: Make `set_map_item_raw` private * chore: Relax requirements for `slot_ptr_to_index` * chore: improve `set_` procedure doc comments * fix: slot_ptr description * chore: Rename `get_slot_id` and warn on natve account usage * chore: Add note for storage delta setters * chore: improve named storage slot docs * chore: remove outdated comments --------- Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> --- CHANGELOG.md | 5 +- .../account_components/ecdsa_k256_keccak.masm | 5 +- .../ecdsa_k256_keccak_acl.masm | 14 +- .../multisig_ecdsa_k256_keccak.masm | 36 +- .../multisig_rpo_falcon_512.masm | 36 +- .../account_components/rpo_falcon_512.masm | 5 +- .../rpo_falcon_512_acl.masm | 14 +- .../asm/kernels/transaction/api.masm | 126 ++- .../asm/kernels/transaction/lib/account.masm | 732 ++++++++++-------- .../transaction/lib/account_delta.masm | 74 +- .../asm/kernels/transaction/lib/faucet.masm | 18 +- .../asm/kernels/transaction/lib/prologue.masm | 21 +- .../miden-lib/asm/miden/active_account.masm | 60 +- .../asm/miden/auth/ecdsa_k256_keccak.masm | 16 +- .../asm/miden/auth/rpo_falcon512.masm | 16 +- .../contracts/faucets/basic_fungible.masm | 3 - .../asm/miden/contracts/faucets/mod.masm | 8 +- .../contracts/faucets/network_fungible.masm | 7 +- .../miden-lib/asm/miden/native_account.masm | 39 +- .../src/account/auth/ecdsa_k256_keccak.rs | 18 +- .../src/account/auth/ecdsa_k256_keccak_acl.rs | 115 ++- .../auth/ecdsa_k256_keccak_multisig.rs | 110 ++- .../src/account/auth/rpo_falcon_512.rs | 22 +- .../src/account/auth/rpo_falcon_512_acl.rs | 123 ++- .../account/auth/rpo_falcon_512_multisig.rs | 122 ++- .../miden-lib/src/account/components/mod.rs | 34 +- .../src/account/faucets/basic_fungible.rs | 88 ++- crates/miden-lib/src/account/faucets/mod.rs | 24 +- .../src/account/faucets/network_fungible.rs | 47 +- .../src/account/interface/component.rs | 155 ++-- crates/miden-lib/src/account/interface/mod.rs | 20 +- .../miden-lib/src/account/interface/test.rs | 10 +- .../miden-lib/src/errors/tx_kernel_errors.rs | 4 +- .../mock_account_component.rs | 8 +- crates/miden-lib/src/testing/mock_account.rs | 14 +- .../src/testing/mock_account_code.rs | 72 +- .../src/transaction/kernel_procedures.rs | 22 +- crates/miden-lib/src/transaction/memory.rs | 24 +- .../miden-objects/src/account/builder/mod.rs | 44 +- crates/miden-objects/src/account/code/mod.rs | 40 +- .../src/account/component/mod.rs | 62 +- .../src/account/component/template/mod.rs | 28 + .../account/component/template/storage/mod.rs | 8 +- crates/miden-objects/src/account/delta/mod.rs | 45 +- .../src/account/delta/storage.rs | 252 +++--- crates/miden-objects/src/account/mod.rs | 72 +- .../src/account/storage/header.rs | 76 +- .../miden-objects/src/account/storage/mod.rs | 198 ++--- .../src/account/storage/partial.rs | 13 +- .../src/account/storage/slot/mod.rs | 4 +- .../src/account/storage/slot/named_slot.rs | 68 +- .../src/account/storage/slot/slot_name.rs | 44 +- .../src/account/storage/slot/slot_name_id.rs | 62 +- .../src/account/storage/slot/type.rs | 10 + crates/miden-objects/src/errors.rs | 40 +- crates/miden-objects/src/testing/mod.rs | 1 + crates/miden-objects/src/testing/slot_name.rs | 8 + crates/miden-objects/src/testing/storage.rs | 61 +- .../src/transaction/inputs/account.rs | 2 +- .../src/transaction/proven_tx.rs | 4 +- crates/miden-testing/Cargo.toml | 2 +- .../src/kernel_tests/block/header_errors.rs | 23 +- .../miden-testing/src/kernel_tests/tx/mod.rs | 2 + .../src/kernel_tests/tx/test_account.rs | 613 +++++---------- .../src/kernel_tests/tx/test_account_delta.rs | 233 +++--- .../src/kernel_tests/tx/test_epilogue.rs | 23 +- .../src/kernel_tests/tx/test_faucet.rs | 78 +- .../src/kernel_tests/tx/test_fee.rs | 16 +- .../src/kernel_tests/tx/test_fpi.rs | 303 +++++--- .../src/kernel_tests/tx/test_lazy_loading.rs | 33 +- .../src/kernel_tests/tx/test_prologue.rs | 20 +- .../src/kernel_tests/tx/test_tx.rs | 5 +- .../src/mock_chain/chain_builder.rs | 13 +- crates/miden-testing/tests/auth/ecdsa_acl.rs | 43 +- .../tests/auth/ecdsa_multisig.rs | 39 +- crates/miden-testing/tests/auth/multisig.rs | 31 +- .../tests/auth/rpo_falcon_acl.rs | 50 +- crates/miden-testing/tests/scripts/faucet.rs | 20 +- crates/miden-testing/tests/wallet/mod.rs | 10 +- crates/miden-tx/src/executor/exec_host.rs | 10 +- crates/miden-tx/src/host/kernel_process.rs | 67 +- crates/miden-tx/src/host/mod.rs | 30 +- .../src/host/storage_delta_tracker.rs | 57 +- crates/miden-tx/src/host/tx_event.rs | 135 ++-- crates/miden-tx/src/prover/prover_host.rs | 12 +- 85 files changed, 3011 insertions(+), 2266 deletions(-) create mode 100644 crates/miden-objects/src/testing/slot_name.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 554b8ab53b..1609f0aa6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987)). +- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025)). ## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). @@ -76,9 +77,9 @@ - Added `account::get_initial_balance` procedure to `miden` lib ([#1959](https://github.com/0xMiden/miden-base/pull/1959)). - [BREAKING] Changed `Account` to `PartialAccount` conversion to generally track only minimal data ([#1963](https://github.com/0xMiden/miden-base/pull/1963)). - Added `MastArtifact`, `PackageExport`, `PackageManifest`, `AttributeSet`, `QualifiedProcedureName`, `Section` and `SectionId` to re-export section ([#1984](https://github.com/0xMiden/miden-base/pull/1984) and [#2015](https://github.com/0xMiden/miden-base/pull/2015)). -- [BREAKING] Enable computing the transaction ID from the data in a `TransactionHeader` ([#1973](https://github.com/0xMiden/miden-base/pull/1973)). +- [BREAKING] Enable computing the transaction ID from the data in a `TransactionHeader` ([#1973]https://github.com/0xMiden/miden-base/pull/1973). +- [BREAKING] Introduce `VaultKey` newtype wrapper for asset vault keys ([#1978]https://github.com/0xMiden/miden-base/pull/1978). - [BREAKING] Introduce `AssetVaultKey` newtype wrapper for asset vault keys ([#1978](https://github.com/0xMiden/miden-base/pull/1978), [#2024](https://github.com/0xMiden/miden-base/pull/2024)). -- [BREAKING] Change `Account` to `PartialAccount` conversion to generally track only minimal data ([#1963](https://github.com/0xMiden/miden-base/pull/1963)). - Added `network_fungible_faucet` and `MINT` & `BURN` notes ([#1925](https://github.com/0xMiden/miden-base/pull/1925)) - Removed `create_p2id_note` and `create_p2any_note` methods from `MockChainBuilder`, users should use `add_p2id_note` and `add_p2any_note` instead ([#1990](https://github.com/0xMiden/miden-base/issues/1990)). - [BREAKING] Introduced `AuthScheme` and `PublicKey` enums in `miden-objects::account::auth` module ([#1994](https://github.com/0xMiden/miden-base/pull/1994)). diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm index 2f4a867d36..aca5d51f93 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm +++ b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm @@ -11,7 +11,7 @@ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } # ================================================================================================= # The slot in this component's storage layout where the public key is stored. -const PUBLIC_KEY_SLOT = 0 +const PUBLIC_KEY_SLOT = word("miden::standards::auth::ecdsa_k256_keccak::public_key") #! Authenticate a transaction using the ECDSA signature scheme. #! @@ -34,7 +34,8 @@ pub proc auth_tx_ecdsa_k256_keccak(auth_args: BeWord) # Fetch public key from storage. # --------------------------------------------------------------------------------------------- - push.PUBLIC_KEY_SLOT exec.active_account::get_item + + push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] exec.ecdsa_k256_keccak::authenticate_transaction diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm index 8f2929c417..978f24881a 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm +++ b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm @@ -13,13 +13,13 @@ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } # ================================================================================================ # The slot in this component's storage layout where the public key is stored. -const PUBLIC_KEY_SLOT = 0 +const PUBLIC_KEY_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_acl::public_key") # The slot where the authentication configuration is stored. -const AUTH_CONFIG_SLOT = 1 +const AUTH_CONFIG_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_acl::config") # The slot where the map of auth trigger procedure roots is stored. -const AUTH_TRIGGER_PROCS_MAP_SLOT = 2 +const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_acl::tracked_procedure_roots") #! Authenticate a transaction using the ECDSA signature scheme based on procedure calls and note usage. #! @@ -40,7 +40,7 @@ pub proc auth_tx_ecdsa_k256_keccak_acl.2(auth_args: BeWord) # => [pad(16)] # Get the authentication configuration - push.AUTH_CONFIG_SLOT exec.active_account::get_item + push.AUTH_CONFIG_SLOT[0..2] exec.active_account::get_item # => [0, allow_unauthorized_input_notes, allow_unauthorized_output_notes, num_auth_trigger_procs, pad(16)] drop @@ -62,8 +62,8 @@ pub proc auth_tx_ecdsa_k256_keccak_acl.2(auth_args: BeWord) # => [require_acl_auth, i, pad(16)] # Get the procedure root from storage - dup.1 sub.1 push.0.0.0 push.AUTH_TRIGGER_PROCS_MAP_SLOT - # => [AUTH_TRIGGER_PROCS_MAP_SLOT, [0, 0, 0, i-1], require_acl_auth, i, pad(16)] + dup.1 sub.1 push.0.0.0 push.AUTH_TRIGGER_PROCS_MAP_SLOT[0..2] + # => [trigger_proc_slot_prefix, trigger_proc_slot_suffix, [0, 0, 0, i-1], require_acl_auth, i, pad(16)] exec.active_account::get_map_item # => [AUTH_TRIGGER_PROC_ROOT, require_acl_auth, i, pad(16)] @@ -124,7 +124,7 @@ pub proc auth_tx_ecdsa_k256_keccak_acl.2(auth_args: BeWord) # If authentication is required, perform signature verification if.true # Fetch public key from storage. - push.PUBLIC_KEY_SLOT exec.active_account::get_item + push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] exec.::miden::auth::ecdsa_k256_keccak::authenticate_transaction diff --git a/crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm b/crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm index 86f30222ef..abd148ab1e 100644 --- a/crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm +++ b/crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm @@ -29,19 +29,19 @@ const AUTH_UNAUTHORIZED_EVENT = event("miden::auth::unauthorized") # number of approvers are stored as: # [default_threshold, num_approvers, 0, 0]. # The threshold is guaranteed to be less than or equal to num_approvers. -const THRESHOLD_CONFIG_SLOT = 0 +const THRESHOLD_CONFIG_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_multisig::threshold_config") # The slot in this component's storage layout where the public keys map is stored. # Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY -const PUBLIC_KEYS_MAP_SLOT = 1 +const APPROVER_PUBLIC_KEYS_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_multisig::approver_public_keys") # The slot in this component's storage layout where executed transactions are stored. # Map entries: transaction_message => [is_executed, 0, 0, 0] -const EXECUTED_TXS_SLOT = 2 +const EXECUTED_TXS_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_multisig::executed_transactions") # The slot in this component's storage layout where procedure thresholds are stored. # Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0] -const.PROC_THRESHOLD_ROOTS_SLOT=3 +const PROC_THRESHOLD_ROOTS_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_multisig::procedure_thresholds") # Executed Transaction Flag Constant const IS_EXECUTED_FLAG = [1, 0, 0, 0] @@ -69,8 +69,8 @@ proc assert_new_tx(msg: BeWord) swapw # => [MSG, IS_EXECUTED_FLAG] - push.EXECUTED_TXS_SLOT - # => [index, MSG, IS_EXECUTED_FLAG] + push.EXECUTED_TXS_SLOT[0..2] + # => [txs_slot_prefix, txs_slot_suffix, MSG, IS_EXECUTED_FLAG] # Set the key value pair in the map to mark transaction as executed exec.native_account::set_map_item @@ -115,8 +115,8 @@ proc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u3 padw swapw # => [[0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers] - push.PUBLIC_KEYS_MAP_SLOT - # => [pub_key_slot_idx, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers] + push.APPROVER_PUBLIC_KEYS_SLOT[0..2] + # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers] exec.native_account::set_map_item # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, new_num_of_approvers] @@ -178,8 +178,8 @@ pub proc update_signers_and_threshold.2(multisig_config_hash: BeWord) eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG # => [MULTISIG_CONFIG, pad(12)] - push.THRESHOLD_CONFIG_SLOT - # => [slot, MULTISIG_CONFIG, pad(12)] + push.THRESHOLD_CONFIG_SLOT[0..2] + # => [config_slot_prefix, config_slot_suffix, MULTISIG_CONFIG, pad(12)] exec.native_account::set_item # => [OLD_THRESHOLD_CONFIG, pad(12)] @@ -205,8 +205,8 @@ pub proc update_signers_and_threshold.2(multisig_config_hash: BeWord) swapw # => [[0, 0, 0, i-1], PUB_KEY, i-1, pad(12)] - push.PUBLIC_KEYS_MAP_SLOT - # => [pub_key_slot_idx, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)] + push.APPROVER_PUBLIC_KEYS_SLOT[0..2] + # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)] exec.native_account::set_map_item # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, pad(12)] @@ -277,8 +277,8 @@ proc compute_transaction_threshold.1(default_threshold: u32) -> u32 if.true # => [PROC_ROOT, num_procedures-1, transaction_threshold] - push.PROC_THRESHOLD_ROOTS_SLOT - # => [PROC_THRESHOLD_ROOTS_SLOT, PROC_ROOT, num_procedures-1, transaction_threshold] + push.PROC_THRESHOLD_ROOTS_SLOT[0..2] + # => [proc_roots_slot_prefix, proc_roots_slot_suffix, PROC_ROOT, num_procedures-1, transaction_threshold] # 2b. get the override proc_threshold of that procedure # if the procedure has no override threshold, the returned map item will be [0, 0, 0, 0] @@ -372,8 +372,8 @@ pub proc auth_tx_ecdsa_k256_keccak_multisig.1(salt: BeWord) # ------ Verifying approver signatures ------ - push.THRESHOLD_CONFIG_SLOT - # => [index, TX_SUMMARY_COMMITMENT] + push.THRESHOLD_CONFIG_SLOT[0..2] + # => [config_slot_prefix, config_slot_suffix, TX_SUMMARY_COMMITMENT] exec.active_account::get_initial_item # => [0, 0, num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT] @@ -384,8 +384,8 @@ pub proc auth_tx_ecdsa_k256_keccak_multisig.1(salt: BeWord) swap movdn.5 # => [num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] - push.PUBLIC_KEYS_MAP_SLOT - # => [pub_key_slot_idx, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] + push.APPROVER_PUBLIC_KEYS_SLOT[0..2] + # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] exec.::miden::auth::ecdsa_k256_keccak::verify_signatures # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold] diff --git a/crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm b/crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm index 92a0cf0a21..0b5f037d52 100644 --- a/crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm +++ b/crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm @@ -29,19 +29,19 @@ const AUTH_UNAUTHORIZED_EVENT = event("miden::auth::unauthorized") # number of approvers are stored as: # [default_threshold, num_approvers, 0, 0]. # The threshold is guaranteed to be less than or equal to num_approvers. -const THRESHOLD_CONFIG_SLOT = 0 +const THRESHOLD_CONFIG_SLOT = word("miden::standards::auth::rpo_falcon512_multisig::threshold_config") # The slot in this component's storage layout where the public keys map is stored. # Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY -const PUBLIC_KEYS_MAP_SLOT = 1 +const APPROVER_PUBLIC_KEYS_SLOT = word("miden::standards::auth::rpo_falcon512_multisig::approver_public_keys") # The slot in this component's storage layout where executed transactions are stored. # Map entries: transaction_message => [is_executed, 0, 0, 0] -const EXECUTED_TXS_SLOT = 2 +const EXECUTED_TXS_SLOT = word("miden::standards::auth::rpo_falcon512_multisig::executed_transactions") # The slot in this component's storage layout where procedure thresholds are stored. # Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0] -const.PROC_THRESHOLD_ROOTS_SLOT=3 +const PROC_THRESHOLD_ROOTS_SLOT = word("miden::standards::auth::rpo_falcon512_multisig::procedure_thresholds") # Executed Transaction Flag Constant const IS_EXECUTED_FLAG = [1, 0, 0, 0] @@ -69,8 +69,8 @@ proc assert_new_tx(msg: BeWord) swapw # => [MSG, IS_EXECUTED_FLAG] - push.EXECUTED_TXS_SLOT - # => [index, MSG, IS_EXECUTED_FLAG] + push.EXECUTED_TXS_SLOT[0..2] + # => [txs_slot_prefix, txs_slot_suffix, MSG, IS_EXECUTED_FLAG] # Set the key value pair in the map to mark transaction as executed exec.native_account::set_map_item @@ -115,8 +115,8 @@ proc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u3 padw swapw # => [[0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers] - push.PUBLIC_KEYS_MAP_SLOT - # => [pub_key_slot_idx, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers] + push.APPROVER_PUBLIC_KEYS_SLOT[0..2] + # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers] exec.native_account::set_map_item # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, new_num_of_approvers] @@ -178,8 +178,8 @@ pub proc update_signers_and_threshold.2(multisig_config_hash: BeWord) eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG # => [MULTISIG_CONFIG, pad(12)] - push.THRESHOLD_CONFIG_SLOT - # => [slot, MULTISIG_CONFIG, pad(12)] + push.THRESHOLD_CONFIG_SLOT[0..2] + # => [config_slot_prefix, config_slot_suffix, MULTISIG_CONFIG, pad(12)] exec.native_account::set_item # => [OLD_THRESHOLD_CONFIG, pad(12)] @@ -205,8 +205,8 @@ pub proc update_signers_and_threshold.2(multisig_config_hash: BeWord) swapw # => [[0, 0, 0, i-1], PUB_KEY, i-1, pad(12)] - push.PUBLIC_KEYS_MAP_SLOT - # => [pub_key_slot_idx, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)] + push.APPROVER_PUBLIC_KEYS_SLOT[0..2] + # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)] exec.native_account::set_map_item # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, pad(12)] @@ -277,8 +277,8 @@ proc compute_transaction_threshold.1(default_threshold: u32) -> u32 if.true # => [PROC_ROOT, num_procedures-1, transaction_threshold] - push.PROC_THRESHOLD_ROOTS_SLOT - # => [PROC_THRESHOLD_ROOTS_SLOT, PROC_ROOT, num_procedures-1, transaction_threshold] + push.PROC_THRESHOLD_ROOTS_SLOT[0..2] + # => [proc_roots_slot_prefix, proc_roots_slot_suffix, PROC_ROOT, num_procedures-1, transaction_threshold] # 2b. get the override proc_threshold of that procedure # if the procedure has no override threshold, the returned map item will be [0, 0, 0, 0] @@ -372,8 +372,8 @@ pub proc auth_tx_rpo_falcon512_multisig.1(salt: BeWord) # ------ Verifying approver signatures ------ - push.THRESHOLD_CONFIG_SLOT - # => [index, TX_SUMMARY_COMMITMENT] + push.THRESHOLD_CONFIG_SLOT[0..2] + # => [config_slot_prefix, config_slot_suffix, TX_SUMMARY_COMMITMENT] exec.active_account::get_initial_item # => [0, 0, num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT] @@ -384,8 +384,8 @@ pub proc auth_tx_rpo_falcon512_multisig.1(salt: BeWord) swap movdn.5 # => [num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] - push.PUBLIC_KEYS_MAP_SLOT - # => [pub_key_slot_idx, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] + push.APPROVER_PUBLIC_KEYS_SLOT[0..2] + # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] exec.::miden::auth::rpo_falcon512::verify_signatures # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold] diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512.masm b/crates/miden-lib/asm/account_components/rpo_falcon_512.masm index 7e68c230ee..914e80e48b 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512.masm +++ b/crates/miden-lib/asm/account_components/rpo_falcon_512.masm @@ -11,7 +11,7 @@ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } # ================================================================================================= # The slot in this component's storage layout where the public key is stored. -const PUBLIC_KEY_SLOT = 0 +const PUBLIC_KEY_SLOT = word("miden::standards::auth::rpo_falcon512::public_key") #! Authenticate a transaction using the Falcon signature scheme. #! @@ -34,7 +34,8 @@ pub proc auth_tx_rpo_falcon512(auth_args: BeWord) # Fetch public key from storage. # --------------------------------------------------------------------------------------------- - push.PUBLIC_KEY_SLOT exec.active_account::get_item + + push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] exec.rpo_falcon512::authenticate_transaction diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm b/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm index 72fd4916a0..2c50339084 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm +++ b/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm @@ -13,13 +13,13 @@ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } # ================================================================================================ # The slot in this component's storage layout where the public key is stored. -const PUBLIC_KEY_SLOT = 0 +const PUBLIC_KEY_SLOT = word("miden::standards::auth::rpo_falcon512_acl::public_key") # The slot where the authentication configuration is stored. -const AUTH_CONFIG_SLOT = 1 +const AUTH_CONFIG_SLOT = word("miden::standards::auth::rpo_falcon512_acl::config") # The slot where the map of auth trigger procedure roots is stored. -const AUTH_TRIGGER_PROCS_MAP_SLOT = 2 +const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::rpo_falcon512_acl::tracked_procedure_roots") #! Authenticate a transaction using the Falcon signature scheme based on procedure calls and note usage. #! @@ -40,7 +40,7 @@ pub proc auth_tx_rpo_falcon512_acl.2(auth_args: BeWord) # => [pad(16)] # Get the authentication configuration - push.AUTH_CONFIG_SLOT exec.active_account::get_item + push.AUTH_CONFIG_SLOT[0..2] exec.active_account::get_item # => [0, allow_unauthorized_input_notes, allow_unauthorized_output_notes, num_auth_trigger_procs, pad(16)] drop @@ -62,8 +62,8 @@ pub proc auth_tx_rpo_falcon512_acl.2(auth_args: BeWord) # => [require_acl_auth, i, pad(16)] # Get the procedure root from storage - dup.1 sub.1 push.0.0.0 push.AUTH_TRIGGER_PROCS_MAP_SLOT - # => [AUTH_TRIGGER_PROCS_MAP_SLOT, [0, 0, 0, i-1], require_acl_auth, i, pad(16)] + dup.1 sub.1 push.0.0.0 push.AUTH_TRIGGER_PROCS_MAP_SLOT[0..2] + # => [trigger_proc_slot_prefix, trigger_proc_slot_suffix, [0, 0, 0, i-1], require_acl_auth, i, pad(16)] exec.active_account::get_map_item # => [AUTH_TRIGGER_PROC_ROOT, require_acl_auth, i, pad(16)] @@ -124,7 +124,7 @@ pub proc auth_tx_rpo_falcon512_acl.2(auth_args: BeWord) # If authentication is required, perform signature verification if.true # Fetch public key from storage. - push.PUBLIC_KEY_SLOT exec.active_account::get_item + push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] exec.::miden::auth::rpo_falcon512::authenticate_transaction diff --git a/crates/miden-lib/asm/kernels/transaction/api.masm b/crates/miden-lib/asm/kernels/transaction/api.masm index 48b764e071..77dece1eb2 100644 --- a/crates/miden-lib/asm/kernels/transaction/api.masm +++ b/crates/miden-lib/asm/kernels/transaction/api.masm @@ -46,7 +46,7 @@ const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSE #! Authenticates that the invocation of a kernel procedure originates from the account context. #! #! Inputs: [] -#! Outputs: [storage_offset, storage_size] +#! Outputs: [] #! #! Panics if: #! - the invocation of the kernel procedure does not originate from the account context. @@ -60,6 +60,10 @@ proc.authenticate_account_origin # assert that the caller is from the user context exec.account::authenticate_and_track_procedure # => [storage_offset, storage_size] + + # TODO(named_slots): Remove storage offset and size from procedure info. + drop drop + # => [] end #! Asserts that the invocation of a kernel procedure originates from the authentication procedure of @@ -270,7 +274,7 @@ end #! Invocation: dynexec export.account_get_code_commitment # authenticate that the procedure invocation originates from the account context - exec.authenticate_account_origin drop drop + exec.authenticate_account_origin # => [pad(16)] # get the account code commitment @@ -312,7 +316,7 @@ end #! Invocation: dynexec export.account_compute_storage_commitment # authenticate that the procedure invocation originates from the account context - exec.authenticate_account_origin drop drop + exec.authenticate_account_origin # => [pad(16)] # compute the account storage commitment @@ -326,25 +330,22 @@ end #! Gets an item from the account storage. #! -#! Inputs: [index, pad(15)] +#! Inputs: [name_id_prefix, name_id_suffix, pad(14)] #! Outputs: [VALUE, pad(12)] #! #! Where: -#! - index is the index of the item to get. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - VALUE is the value of the item. #! #! Panics if: -#! - the index is out of bounds. +#! - a slot with the provided name ID does not exist in account storage. #! #! Invocation: dynexec export.account_get_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [storage_offset, storage_size, index, pad(15)] - - # apply offset to storage slot index - exec.account::apply_storage_offset - # => [index_with_offset, pad(15)] + # => [name_id_prefix, name_id_suffix, pad(14)] # fetch the account storage item exec.account::get_item @@ -357,67 +358,67 @@ end #! Sets an item in the account storage. #! -#! Inputs: [index, VALUE, pad(11)] +#! Inputs: [name_id_prefix, name_id_suffix, VALUE, pad(10)] #! Outputs: [OLD_VALUE, pad(12)] #! #! Where: -#! - index is the index of the item to set. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - VALUE is the value to set. #! - OLD_VALUE is the previous value of the item. #! #! Panics if: -#! - the index is out of bounds. +#! - a slot with the provided name ID does not exist in account storage. #! - the invocation of this procedure does not originate from the native account. +#! - the native account is a faucet and the provided name ID points to the reserved faucet storage slot. #! #! Invocation: dynexec export.account_set_item # check that this procedure was executed against the native account exec.memory::assert_native_account - # => [index, VALUE, pad(11)] + # => [name_id_prefix, name_id_suffix, VALUE, pad(10)] # if the transaction is being executed against a faucet account then assert - # index != FAUCET_STORAGE_DATA_SLOT (reserved slot) - dup exec.account::get_faucet_storage_data_slot eq + # the slot that is being written to is not the reserved faucet slot. + dup.1 dup.1 exec.account::is_faucet_storage_data_slot + # => [is_faucet_storage_data_slot, name_id_prefix, name_id_suffix, VALUE, pad(10)] + exec.account::get_id swap drop exec.account_id::is_faucet + # => [is_faucet_account, is_faucet_storage_data_slot, name_id_prefix, name_id_suffix, VALUE, pad(10)] + and assertz.err=ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED - # => [index, VALUE, pad(11)] + # => [name_id_prefix, name_id_suffix, VALUE, pad(10)] # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [storage_offset, storage_size, index, VALUE, pad(11)] - - # apply offset to storage slot index - exec.account::apply_storage_offset - # => [index_with_offset, VALUE, pad(11)] + # => [name_id_prefix, name_id_suffix, VALUE, pad(10)] # set the account storage item exec.account::set_item # => [OLD_VALUE, pad(12)] end -#! Returns the VALUE located under the specified KEY within the map contained in the given -#! account storage slot. +#! Returns the VALUE located under the specified KEY within the map contained in the account +#! storage slot identified by the name ID. #! -#! Inputs: [index, KEY, pad(11)] +#! Inputs: [name_id_prefix, name_id_suffix, KEY, pad(10)] #! Outputs: [VALUE, pad(12)] #! #! Where: -#! - index is the index of the storage slot that contains the map root. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - the slot must point to the root of the storage map. #! - VALUE is the value of the map item at KEY. #! #! Panics if: -#! - the index is out of bounds (>255). +#! - a slot with the provided name ID does not exist in account storage. #! - the requested storage slot type is not map. #! #! Invocation: dynexec export.account_get_map_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [storage_offset, storage_size, index, KEY, pad(11)] - - # apply offset to storage slot index - exec.account::apply_storage_offset - # => [index_with_offset, KEY, pad(11)] + # => [name_id_prefix, name_id_suffix, KEY, pad(10)] # fetch the map item from account storage exec.account::get_map_item @@ -426,25 +427,22 @@ end #! Gets an item from the account storage at its initial state (beginning of transaction). #! -#! Inputs: [index, pad(15)] +#! Inputs: [name_id_prefix, name_id_suffix, pad(14)] #! Outputs: [INIT_VALUE, pad(12)] #! #! Where: -#! - index is the index of the item to get. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - INIT_VALUE is the initial value of the item at the beginning of the transaction. #! #! Panics if: -#! - the index is out of bounds. +#! - a slot with the provided name ID does not exist in account storage. #! #! Invocation: dynexec export.account_get_initial_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [storage_offset, storage_size, index, pad(15)] - - # apply offset to storage slot index - exec.account::apply_storage_offset - # => [index_with_offset, pad(15)] + # => [name_id_prefix, name_id_suffix, pad(14)] # fetch the initial account storage item exec.account::get_initial_item @@ -455,29 +453,27 @@ export.account_get_initial_item # => [INIT_VALUE, pad(12)] end -#! Returns the initial VALUE located under the specified KEY within the map contained in the given -#! account storage slot at the beginning of the transaction. +#! Returns the initial VALUE located under the specified KEY within the map contained in the +#! account storage slot identified by the name ID at the beginning of the transaction. #! -#! Inputs: [index, KEY, pad(11)] +#! Inputs: [name_id_prefix, name_id_suffix, KEY, pad(10)] #! Outputs: [INIT_VALUE, pad(12)] #! #! Where: -#! - index is the index of the storage slot that contains the map root. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - the slot must point to the root of the storage map. #! - INIT_VALUE is the initial value of the map item at KEY at the beginning of the transaction. #! #! Panics if: -#! - the index is out of bounds (>255). +#! - a slot with the provided name ID does not exist in account storage. #! - the requested storage slot type is not map. #! #! Invocation: dynexec export.account_get_initial_map_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [storage_offset, storage_size, index, KEY, pad(11)] - - # apply offset to storage slot index - exec.account::apply_storage_offset - # => [index_with_offset, KEY, pad(11)] + # => [name_id_prefix, name_id_suffix, KEY, pad(10)] # fetch the initial map item from account storage exec.account::get_initial_map_item @@ -487,18 +483,20 @@ end #! Stores NEW_VALUE under the specified KEY within the map contained in the given account storage #! slot. #! -#! Inputs: [index, KEY, NEW_VALUE, pad(7)] +#! Inputs: [name_id_prefix, name_id_suffix, KEY, NEW_VALUE, pad(6)] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] #! #! Where: -#! - index is the index of the storage slot which contains the map root. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - the slot must point to the root of the storage map. #! - NEW_VALUE is the value of the new map item for the respective KEY. #! - OLD_VALUE is the value of the old map item for the respective KEY. #! - KEY is the key of the new item. #! - OLD_MAP_ROOT is the root of the old map before insertion #! #! Panics if: -#! - the index is out of bounds (>255). +#! - a slot with the provided name ID does not exist in account storage. #! - the requested storage slot type is not map. #! - the procedure is called from a non-account context. #! - the invocation of this procedure does not originate from the native account. @@ -507,18 +505,18 @@ end export.account_set_map_item # check that this procedure was executed against the native account exec.memory::assert_native_account - # => [index, KEY, NEW_VALUE, pad(7)] + # => [name_id_prefix, name_id_suffix, KEY, NEW_VALUE, pad(6)] # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [storage_offset, storage_size, index, KEY, NEW_VALUE, pad(7)] - - # apply offset to storage slot index - exec.account::apply_storage_offset - # => [index_with_offset, KEY, NEW_VALUE, pad(7)] + # => [name_id_prefix, name_id_suffix, KEY, NEW_VALUE, pad(6)] # set the new map item exec.account::set_map_item + # => [OLD_MAP_ROOT, OLD_VALUE, pad(16)] + + # truncate the stack + movupw.2 dropw movupw.2 dropw # => [OLD_MAP_ROOT, OLD_VALUE, pad(8)] end @@ -586,7 +584,7 @@ export.account_add_asset # => [ASSET, pad(12)] # authenticate that the procedure invocation originates from the account context - exec.authenticate_account_origin drop drop + exec.authenticate_account_origin # => [ASSET, pad(12)] # add the specified asset to the account vault, emitting the corresponding events @@ -615,7 +613,7 @@ export.account_remove_asset # => [ASSET, pad(12)] # authenticate that the procedure invocation originates from the account context - exec.authenticate_account_origin drop drop + exec.authenticate_account_origin # => [ASSET, pad(12)] # remove the specified asset from the account vault, emitting the corresponding events @@ -798,7 +796,7 @@ export.faucet_mint_asset # => [ASSET, pad(12)] # authenticate that the procedure invocation originates from the account context - exec.authenticate_account_origin drop drop + exec.authenticate_account_origin # => [ASSET, pad(12)] # mint the asset @@ -833,7 +831,7 @@ export.faucet_burn_asset # => [ASSET, pad(12)] # authenticate that the procedure invocation originates from the account context - exec.authenticate_account_origin drop drop + exec.authenticate_account_origin # => [ASSET, pad(12)] # burn the asset diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index 0b390f8235..c20c42963b 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -33,7 +33,7 @@ const.ERR_ACCOUNT_PROC_NOT_AUTH_PROC="account procedure is not the authenticatio # TODO(named_slots): Remove along with index APIs. const.ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS="provided storage slot index is out of bounds" -const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" +const.ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" const.ERR_FAUCET_INVALID_STORAGE_OFFSET="storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets)" @@ -64,10 +64,11 @@ const.ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account # CONSTANTS # ================================================================================================= -# The account storage slot at which faucet data is stored. +# The name of the account storage slot at which faucet data is stored. # Fungible faucet: The faucet data consists of [0, 0, 0, total_issuance] # Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. -const.FAUCET_STORAGE_DATA_SLOT=0 +# TODO(named_slots): Rename constant and procedure to FAUCET_METADATA_SLOT. +const.FAUCET_STORAGE_DATA_SLOT=word("miden::faucet::metadata") # The maximum storage slot index const.MAX_STORAGE_SLOT_INDEX=254 @@ -126,6 +127,15 @@ const.ACCOUNT_PROCEDURE_DATA_LENGTH=8 # The offset of the slot type in the storage slot. const.ACCOUNT_SLOT_TYPE_OFFSET=1 +# The offset of the slot's name ID suffix in the storage slot. +const.ACCOUNT_SLOT_NAME_ID_SUFFIX_OFFSET=2 + +# The offset of the slot's name ID prefix in the storage slot. +const.ACCOUNT_SLOT_NAME_ID_PREFIX_OFFSET=3 + +# The offset of the slot value in the storage slot. +const.ACCOUNT_SLOT_VALUE_OFFSET=4 + # EVENTS # ================================================================================================= @@ -178,12 +188,12 @@ const.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_i #! Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. #! #! Inputs: [] -#! Outputs: [faucet_storage_data_slot] +#! Outputs: [faucet_name_id_prefix, faucet_name_id_suffix] #! #! Where: #! - faucet_storage_data_slot is the account storage slot at which faucet data is stored. -export.get_faucet_storage_data_slot - push.FAUCET_STORAGE_DATA_SLOT +export.get_faucet_storage_slot_id + push.FAUCET_STORAGE_DATA_SLOT[0..2] end #! Returns the maximum number of account storage slots. @@ -422,40 +432,77 @@ end #! Gets an item from the account storage. #! -#! Note: -#! - We assume that index has been validated and is within bounds. -#! -#! Inputs: [index] +#! Inputs: [name_id_prefix, name_id_suffix] #! Outputs: [VALUE] #! #! Where: -#! - index is the index of the item to get. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - VALUE is the value of the item. +#! +#! Panics if: +#! - a slot with the provided name ID does not exist in account storage. export.get_item # get account storage slots section offset exec.memory::get_account_storage_slots_section_ptr - # => [acct_storage_slots_section_offset, index] + # => [acct_storage_slots_section_offset, name_id_prefix, name_id_suffix] + + exec.find_storage_slot + # => [slot_ptr] # get the item from storage exec.get_item_raw # => [VALUE] end -#! Gets an item from the account storage at its initial state (beginning of transaction). +#! Gets an item and its slot type from the account storage. #! -#! Note: -#! - We assume that index has been validated and is within bounds. +#! Inputs: [name_id_prefix, name_id_suffix] +#! Outputs: [VALUE, slot_type] #! -#! Inputs: [index] +#! Where: +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - VALUE is the value of the item. +#! - slot_type is the type of the slot. +#! +#! Panics if: +#! - a slot with the provided name ID does not exist in account storage. +export.get_typed_item + # get account storage slots section offset + exec.memory::get_account_storage_slots_section_ptr + # => [acct_storage_slots_section_offset, name_id_prefix, name_id_suffix] + + exec.find_storage_slot + # => [slot_ptr] + + dup add.ACCOUNT_SLOT_TYPE_OFFSET mem_load + # => [slot_type, slot_ptr] + + # load the value + swap exec.get_item_raw + # => [VALUE, slot_type] +end + +#! Gets an item from the account storage at its initial state (beginning of transaction). +#! +#! Inputs: [name_id_prefix, name_id_suffix] #! Outputs: [INIT_VALUE] #! #! Where: -#! - index is the index of the item to get. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - INIT_VALUE is the initial value of the item at the beginning of the transaction. +#! +#! Panics if: +#! - a slot with the provided name ID does not exist in account storage. export.get_initial_item # get account initial storage slots section offset exec.memory::get_account_initial_storage_slots_ptr - # => [account_initial_storage_slots_ptr, index] + # => [account_initial_storage_slots_ptr, name_id_prefix, name_id_suffix] + + exec.find_storage_slot + # => [slot_ptr] # get the item from initial storage exec.get_item_raw @@ -464,70 +511,72 @@ end #! Sets an item in the account storage. #! -#! Note: -#! - We assume that index has been validated and is within bounds. -#! -#! Inputs: [index, VALUE] +#! Inputs: [name_id_prefix, name_id_suffix, VALUE] #! Outputs: [OLD_VALUE] #! #! Where: -#! - index is the index of the item to set. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - VALUE is the value to set. #! - OLD_VALUE is the previous value of the item. #! #! Panics if: +#! - a slot with the provided name ID does not exist in account storage. #! - the storage slot type is not value. export.set_item emit.ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT - # => [index, VALUE] + # => [name_id_prefix, name_id_suffix, VALUE] - # get storage slot type - dup exec.get_storage_slot_type_by_index - # => [storage_slot_type, index, VALUE] + exec.memory::get_account_storage_slots_section_ptr + # => [storage_slots_ptr, name_id_prefix, name_id_suffix, VALUE] + + exec.find_storage_slot + # => [slot_ptr, VALUE] - # check if type == slot + # load the slot type + dup add.ACCOUNT_SLOT_TYPE_OFFSET mem_load + # => [slot_type, slot_ptr, VALUE] + + # assert slot_type is value exec.constants::get_storage_slot_type_value eq assert.err=ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT - # => [index, VALUE] + # => [slot_ptr, VALUE] - # duplicate the index and the VALUE enabling emission of an - # event after an account storage item is being updated - movdn.4 dupw dup.8 - # => [index, VALUE, VALUE, index] + movdn.4 + # => [VALUE, slot_ptr] - # set VALUE in the storage slot - exec.set_item_raw - # => [OLD_VALUE, VALUE, index] + # load the old value of the slot + dup.4 exec.get_item_raw + # => [OLD_VALUE, VALUE, slot_ptr] - # emit event to signal that an account storage item is being updated swapw movup.8 - emit.ACCOUNT_STORAGE_AFTER_SET_ITEM_EVENT drop dropw + # => [slot_ptr, VALUE, OLD_VALUE] + + emit.ACCOUNT_STORAGE_AFTER_SET_ITEM_EVENT + # => [slot_ptr, VALUE, OLD_VALUE] + + # store the new value into the slot + exec.set_item_raw # => [OLD_VALUE] end -#! Returns the VALUE located under the specified KEY within the map contained in the given -#! account storage slot. +#! Returns the VALUE located under the specified KEY within the map contained in the account +#! storage slot identified by the name ID. #! -#! Inputs: [index, KEY] +#! Inputs: [name_id_prefix, name_id_suffix, KEY] #! Outputs: [VALUE] #! -#! Note: -#! - We assume that index has been validated and is within bounds. -#! #! Where: -#! - index is the index of the storage slot that contains the map root. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - VALUE is the value of the map item at KEY. #! #! Panics if: +#! - a slot with the provided name ID does not exist in account storage. #! - the requested storage slot type is not map. export.get_map_item - # duplicate index for later use - dup movdn.5 - # => [index, KEY, index] - - # fetch the account storage item, which is ROOT of the map - exec.get_item swapw - # => [KEY, ROOT, index] + exec.memory::get_account_storage_slots_section_ptr + # => [storage_slots_ptr, name_id_prefix, name_id_suffix, KEY] exec.get_map_item_raw end @@ -535,138 +584,66 @@ end #! Returns the VALUE located under the specified KEY within the map contained in the given #! account storage slot at its initial state (beginning of transaction). #! -#! Inputs: [index, KEY] +#! Inputs: [name_id_prefix, name_id_suffix, KEY] #! Outputs: [INIT_VALUE] #! -#! Note: -#! - We assume that index has been validated and is within bounds. -#! #! Where: -#! - index is the index of the storage slot that contains the map root. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - INIT_VALUE is the initial value of the map item at KEY at the beginning of the transaction. #! #! Panics if: +#! - a slot with the provided name ID does not exist in account storage. #! - the requested storage slot type is not map. export.get_initial_map_item - # duplicate index for later use - dup movdn.5 - # => [index, KEY, index] - - # fetch the initial account storage item, which is ROOT of the map - exec.get_initial_item swapw - # => [KEY, INIT_ROOT, index] + exec.memory::get_account_initial_storage_slots_ptr + # => [initial_storage_slots_ptr, name_id_prefix, name_id_suffix, KEY] exec.get_map_item_raw end #! Stores NEW_VALUE under the specified KEY within the map contained in the given account storage slot. #! -#! Note: -#! - We assume that index has been validated and is within bounds. -#! -#! Inputs: [index, KEY, NEW_VALUE] +#! Inputs: [name_id_prefix, name_id_suffix, KEY, NEW_VALUE] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE] #! #! Where: -#! - index is the index of the storage slot which contains the map root. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - the slot must point to the root of the storage map. #! - NEW_VALUE is the value to set under KEY. #! - KEY is the key to set. #! - OLD_MAP_VALUE is the previous value of the item. #! - OLD_MAP_ROOT is the root of the old map before insertion #! #! Panics if: +#! - a slot with the provided name ID does not exist in account storage. #! - the storage slot type is not map. -#! - no map is found for ROOT. -export.set_map_item.12 - # store index for later - dup loc_store.0 exec.get_item - # => [OLD_ROOT, KEY, NEW_VALUE, ...] - - movdnw.2 loc_load.0 - # => [index, KEY, NEW_VALUE, OLD_ROOT, ...] +#! - no map with the root of the slot is found. +export.set_map_item + exec.memory::get_account_storage_slots_section_ptr + # => [storage_slots_ptr, name_id_prefix, name_id_suffix, KEY, NEW_VALUE] - emit.ACCOUNT_STORAGE_BEFORE_SET_MAP_ITEM_EVENT - # => [index, KEY, NEW_VALUE, OLD_ROOT, ...] + # resolve the slot name to its pointer + exec.find_storage_slot + # => [slot_ptr, KEY, NEW_VALUE] - # check if storage type is map - exec.get_storage_slot_type_by_index - # => [slot_type, KEY, NEW_VALUE, OLD_ROOT] + # load the slot type + dup add.ACCOUNT_SLOT_TYPE_OFFSET mem_load + # => [slot_type, slot_ptr, KEY, NEW_VALUE] - # check if slot_type == map + # assert slot_type is map exec.constants::get_storage_slot_type_map eq assert.err=ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT - # => [KEY, NEW_VALUE, OLD_ROOT] - - # duplicate the original KEY and the NEW_VALUE to be able to emit an event after the - # account storage item was updated - movupw.2 dupw.2 dupw.2 - # => [KEY, NEW_VALUE, OLD_ROOT, KEY, NEW_VALUE] - - # see hash_map_key's docs for why this is done - exec.hash_map_key - # => [HASHED_KEY, NEW_VALUE, OLD_ROOT, KEY, NEW_VALUE] - - # set the NEW_VALUE under HASHED_KEY in the tree - # note smt::set expects the stack to be [NEW_VALUE, HASHED_KEY, OLD_ROOT] - swapw exec.smt::set - # => [OLD_MAP_VALUE, NEW_ROOT, KEY, NEW_VALUE] + # => [slot_ptr, KEY, NEW_VALUE] - # store OLD_MAP_VALUE and NEW_ROOT until the end of the procedure - loc_storew_be.4 dropw loc_storew_be.8 dropw - # => [KEY, NEW_VALUE] - - dupw.1 - # => [NEW_VALUE, KEY, NEW_VALUE] - - padw loc_loadw_be.4 - # => [OLD_MAP_VALUE, NEW_VALUE, KEY, NEW_VALUE] - - dupw.2 - # => [KEY, OLD_MAP_VALUE, NEW_VALUE, KEY, NEW_VALUE] - - # emit event to signal that an account storage item is being updated - loc_load.0 - # => [index, KEY, OLD_MAP_VALUE, NEW_VALUE, KEY, NEW_VALUE] - - emit.ACCOUNT_STORAGE_AFTER_SET_MAP_ITEM_EVENT - # => [index, KEY, OLD_MAP_VALUE, NEW_VALUE, KEY, NEW_VALUE] - - exec.account_delta::set_map_item - # => [KEY, NEW_VALUE] - - # load OLD_MAP_VALUE and NEW_ROOT on the top of the stack - loc_loadw_be.8 swapw loc_loadw_be.4 swapw - # => [NEW_ROOT, OLD_MAP_VALUE, ...] - - # set the root of the map in the respective account storage slot - loc_load.0 exec.set_item_raw - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, ...] + exec.set_map_item_raw + # => [OLD_MAP_ROOT, OLD_MAP_VALUE] end -#! Applies storage offset to provided storage slot index for storage access. -#! -#! Inputs: [storage_offset, storage_size, slot_index] -#! Outputs: [offset_slot_index] +#! Returns the type of the storage slot at the provided index. #! -#! Where: -#! - storage_offset is the offset of the storage for this account component. -#! - storage_size is the number of storage slots accessible from this account component. -#! - slot_index is the index of the storage slot to be accessed. -#! - offset_slot_index is the final index of the storage slot with the storage offset applied to it. -#! -#! Panics if: -#! - the computed index is out of bounds -export.apply_storage_offset - # offset index - dup movup.3 add - # => [offset_slot_index, storage_offset, storage_size] - - # verify that slot_index is in bounds - movdn.2 add dup.1 gt assert.err=ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS - # => [offset_slot_index] -end - -#! Returns the type of the requested storage slot. +#! WARNING: The index must be in bounds. #! #! Inputs: [index] #! Outputs: [slot_type] @@ -674,33 +651,15 @@ end #! Where: #! - index is the location in memory of the storage slot. #! - slot_type is the type of the storage slot. -#! -#! Panics if: -#! - the slot index is out of bounds. -export.get_storage_slot_type_by_index - # check that index is in bounds - dup exec.memory::get_num_storage_slots lt assert.err=ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS - # => [index] - - exec.memory::get_account_storage_slots_section_ptr - # => [curr_account_storage_slots_section_ptr, index] - - exec.get_storage_slot_type - # => [slot_type] -end - -#! Returns the type of the requested storage slot. -#! -#! Inputs: [account_storage_slots_section_ptr, index] -#! Outputs: [slot_type] -#! -#! Where: -#! - index is the location in memory of the storage slot. -#! - slot_type is the type of the storage slot. export.get_storage_slot_type + # convert the index into a memory offset + mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH + # => [offset] + # compute storage slot ptr - swap mul.8 add - # => [storage_slot_ptr] + exec.memory::get_native_account_storage_slots_ptr + add + # => [slot_ptr] # load the slot type add.ACCOUNT_SLOT_TYPE_OFFSET mem_load @@ -1257,7 +1216,8 @@ export.save_account_storage_data # AS => [[STORAGE_SLOT_DATA]] # setup acct_storage_slots_ptr and end_ptr for reading from advice stack - mul.8 exec.memory::get_account_storage_slots_section_ptr dup movdn.2 add swap + mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH exec.memory::get_account_storage_slots_section_ptr + dup movdn.2 add swap # OS => [acct_storage_slots_ptr, end_ptr, STORAGE_COMMITMENT] # AS => [[STORAGE_SLOT_DATA]] @@ -1374,7 +1334,7 @@ export.insert_new_storage sub.1 # => [slot_idx] - dup exec.get_storage_slot_type_by_index + dup exec.get_storage_slot_type # => [slot_type, slot_idx] exec.constants::get_storage_slot_type_map eq @@ -1411,76 +1371,76 @@ end #! Advice map: { MAP_ROOT: [MAP_ENTRIES] } #! Outputs: [] proc.insert_and_validate_storage_map - dup exec.memory::get_account_storage_slots_section_ptr - # => [storage_slots_ptr, slot_idx, slot_idx] + mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH + exec.memory::get_account_storage_slots_section_ptr + add + # => [slot_ptr] - exec.get_item_raw - # => [MAP_ROOT, slot_idx] + dup exec.get_item_raw + # => [MAP_ROOT, slot_ptr] # overwrite the map root with the root of an empty SMT, so we can insert the entries of # the map into an empty map and then check whether the resulting root matches MAP_ROOT. exec.constants::get_empty_smt_root dup.8 - # => [slot_idx, EMPTY_SMT_ROOT, MAP_ROOT, slot_idx] + # => [slot_ptr, EMPTY_SMT_ROOT, MAP_ROOT, slot_ptr] - exec.set_item_raw dropw - # => [MAP_ROOT, slot_idx] + exec.set_item_raw + # => [MAP_ROOT, slot_ptr] adv.push_mapvaln - # OS => [MAP_ROOT, slot_idx] + # OS => [MAP_ROOT, slot_ptr] # AS => [num_elements, [MAP_ENTRIES]] movup.4 - # OS => [slot_idx, MAP_ROOT] + # OS => [slot_ptr, MAP_ROOT] # AS => [num_elements, [MAP_ENTRIES]] adv_push.1 - # OS => [num_elements, slot_idx, MAP_ROOT] + # OS => [num_elements, slot_ptr, MAP_ROOT] # AS => [[MAP_ENTRIES]] push.8 u32assert2.err="number of storage map elements should fit into a u32" - # OS => [8, num_elements, slot_idx, MAP_ROOT] + # OS => [8, num_elements, slot_ptr, MAP_ROOT] # AS => [[MAP_ENTRIES]] # check that num_elements % 8 = 0 so we can use an equality check for the loop condition # this also computes number_entries which is num_elements / 8. u32divmod eq.0 assert.err="number of storage map elements must be a multiple of 8" - # OS => [num_entries, slot_idx, MAP_ROOT] + # OS => [num_entries, slot_ptr, MAP_ROOT] # AS => [[MAP_ENTRIES]] # loop if there are more than 0 storage map elements dup neq.0 - # OS => [should_loop, num_entries, slot_idx, MAP_ROOT] + # OS => [should_loop, num_entries, slot_ptr, MAP_ROOT] # AS => [[MAP_ENTRIES]] while.true sub.1 - # => [remaining_entries, slot_idx, MAP_ROOT] + # => [remaining_entries, slot_ptr, MAP_ROOT] # push a key-value pair (8 felts) to the operand stack adv_push.8 - # => [KEY, VALUE, remaining_entries, slot_idx, MAP_ROOT] + # => [KEY, VALUE, remaining_entries, slot_ptr, MAP_ROOT] dup.9 - # => [slot_idx, KEY, VALUE, remaining_entries, slot_idx, MAP_ROOT] + # => [slot_ptr, KEY, VALUE, remaining_entries, slot_ptr, MAP_ROOT] # insert the key-value pair into account storage - # this could be optimized to avoid the map slot type assertion and reading and writing the - # root on every call - exec.set_map_item dropw dropw - # => [remaining_entries, slot_idx, MAP_ROOT] + # this could be optimized to avoid reading and writing the root on every call + exec.set_map_item_raw dropw dropw + # => [remaining_entries, slot_ptr, MAP_ROOT] dup neq.0 - # => [should_continue, remaining_entries, slot_idx, MAP_ROOT] + # => [should_continue, remaining_entries, slot_ptr, MAP_ROOT] end - # OS => [remaining_entries, slot_idx, MAP_ROOT] + # OS => [remaining_entries, slot_ptr, MAP_ROOT] # AS => [] drop - # => [slot_idx, MAP_ROOT] + # => [slot_ptr, MAP_ROOT] # load the root after all entries have been inserted - exec.memory::get_account_storage_slots_section_ptr exec.get_item_raw # => [CURRENT_MAP_ROOT, MAP_ROOT] @@ -1493,127 +1453,268 @@ end # HELPER PROCEDURES # ================================================================================================= -#! Gets an item from storage using the provided storage slots pointer and index. +#! Returns 1 if the provided slot ID is equal to the reserved faucet storage data slot, 0 +#! otherwise. #! -#! Note: -#! - We assume that index has been validated and is within bounds. -#! -#! Inputs: [storage_slots_ptr, index] -#! Outputs: [VALUE] +#! Inputs: [name_id_prefix, name_id_suffix] +#! Outputs: [is_faucet_storage_data_slot] #! #! Where: -#! - storage_slots_ptr is the pointer to the storage slots section. -#! - index is the index of the item to get. -#! - VALUE is the value of the item. -export.get_item_raw - # TODO(named_slots): pass in name - exec.find_indexed_storage_slot - # => [slot_ptr] +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - is_faucet_storage_data_slot is a boolean value indicating whether the provided slot is the +#! reserved faucet data slot. +export.is_faucet_storage_data_slot + exec.get_faucet_storage_slot_id + # => [faucet_name_id_prefix, faucet_name_id_suffix, name_id_prefix, name_id_suffix] - # offset the pointer to point to the VALUE in the slot - add.4 - # => [slot_value_ptr] + movup.2 eq + # => [prefix_eq, faucet_name_id_suffix, name_id_suffix] - # load the item from storage - padw movup.4 mem_loadw_be - # => [VALUE] + movdn.2 eq + # => [prefix_eq, suffix_eq] + + and + # => [is_faucet_storage_data_slot] end -#! Gets an item from the storage slot at the provided index. +#! Gets the initial and current value of an item from the storage slot at the provided index for +#! the native account. #! #! WARNING: The index must be in bounds. #! #! Inputs: [index] -#! Outputs: [VALUE] +#! Outputs: [INITIAL_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix] #! #! Where: -#! - storage_slots_ptr is the pointer to the storage slots section. -#! - index is the index of the item to get. -#! - VALUE is the value of the item. -export.get_item_by_index +#! - index is the index of the slot. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - INITIAL_VALUE is the initial value of the item at the beginning of the transaction. +#! - CURRENT_VALUE is the current value of the item. +export.get_item_delta + # convert the index into a memory offset + mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH + # => [offset] + # get account storage slots section offset - exec.memory::get_account_storage_slots_section_ptr - # => [acct_storage_slots_section_offset, index] + exec.memory::get_native_account_storage_slots_ptr + # => [storage_slots_ptr, offset] - exec.get_item_by_index_raw - # => [VALUE] + dup.1 add dup + # => [slot_ptr, slot_ptr, offset] + + # load the name ID + add.ACCOUNT_SLOT_NAME_ID_SUFFIX_OFFSET mem_load + # => [name_id_suffix, slot_ptr, offset] + + dup.1 add.ACCOUNT_SLOT_NAME_ID_PREFIX_OFFSET mem_load + # => [name_id_prefix, name_id_suffix, slot_ptr, offset] + + # load the current value + movup.2 exec.get_item_raw + # => [CURRENT_VALUE, name_id_prefix, name_id_suffix, offset] + + # get account initial storage slots section offset + exec.memory::get_account_initial_storage_slots_ptr + # => [init_storage_slots_ptr, CURRENT_VALUE, name_id_prefix, name_id_suffix, offset] + + movup.7 add + # => [init_slot_ptr, CURRENT_VALUE, name_id_prefix, name_id_suffix] + + exec.get_item_raw + # => [INITIAL_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix] end -#! Gets the initial item from the storage slot at the provided index. +#! Gets the slot ID of the storage slot at the provided index. #! #! WARNING: The index must be in bounds. #! #! Inputs: [index] -#! Outputs: [VALUE] +#! Outputs: [name_id_prefix, name_id_suffix] #! #! Where: -#! - storage_slots_ptr is the pointer to the storage slots section. -#! - index is the index of the item to get. -#! - VALUE is the value of the item. -export.get_initial_item_by_index - # get account initial storage slots section offset - exec.memory::get_account_initial_storage_slots_ptr - # => [account_initial_storage_slots_ptr, index] +#! - index is the index of the slot. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +export.get_slot_id + # convert the index into a memory offset + mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH + # => [offset] - exec.get_item_by_index_raw - # => [VALUE] + exec.memory::get_account_storage_slots_section_ptr + add + # => [slot_ptr] + + dup add.ACCOUNT_SLOT_NAME_ID_SUFFIX_OFFSET mem_load + # => [name_id_suffix, slot_ptr] + + swap add.ACCOUNT_SLOT_NAME_ID_PREFIX_OFFSET mem_load + # => [name_id_prefix, name_id_suffix] end -#! Gets an item from storage using the provided storage slots pointer and index. +#! Sets the value of the storage slot located at the memory address specified by the provided +#! pointer. #! -#! WARNING: The index must be in bounds. +#! The provided pointer must be a valid pointer into the current storage slot section. +#! +#! WARNING: This must only be used on the native account. +#! +#! Inputs: [slot_ptr, VALUE] +#! Outputs: [] #! -#! Inputs: [storage_slots_ptr, index] +#! Where: +#! - slot_ptr is the pointer to a slot. +#! - VALUE is the new value of the item. +proc.set_item_raw + add.ACCOUNT_SLOT_VALUE_OFFSET mem_storew_be dropw + # => [] + + # set the storage commitment dirty flag to indicate that the commitment is outdated + push.1 + exec.memory::set_native_account_storage_commitment_dirty_flag + # => [] +end + +#! Stores NEW_VALUE under the specified KEY within the map contained in the given account storage slot. +#! +#! WARNING: The slot ptr must point to a map slot. +#! WARNING: This must only be used on the native account. +#! +#! Inputs: [slot_ptr, KEY, NEW_VALUE] +#! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE] +#! +#! Where: +#! - slot_ptr is the pointer to a slot. +#! - KEY is the key to set. +#! - NEW_VALUE is the value to set under KEY. +#! - OLD_MAP_VALUE is the previous value of the item. +#! - OLD_MAP_ROOT is the root of the old map before insertion +#! +#! Panics if: +#! - a slot with the provided name ID does not exist in account storage. +#! - no map with the root of the slot is found. +#! +#! Locals: +#! - 0: slot_ptr +#! - 4..8: OLD_MAP_VALUE +#! - 8..12: OLD_MAP_ROOT +proc set_map_item_raw.12 + # store slot_ptr until the end of the procedure + dup loc_store.0 + # => [slot_ptr, KEY, NEW_VALUE] + + emit.ACCOUNT_STORAGE_BEFORE_SET_MAP_ITEM_EVENT + # => [slot_ptr, KEY, NEW_VALUE] + + # load the current map root from memory + exec.get_item_raw + # => [OLD_MAP_ROOT, KEY, NEW_VALUE] + + # store OLD_MAP_ROOT until the end of the procedure + loc_storew_be.8 + # => [OLD_MAP_ROOT, KEY, NEW_VALUE] + + # duplicate the KEY and the NEW_VALUE for account delta insertion and event emission + dupw.2 dupw.2 + # => [KEY, NEW_VALUE, OLD_MAP_ROOT, KEY, NEW_VALUE] + + # see hash_map_key's docs for why this is done + exec.hash_map_key swapw + # => [NEW_VALUE, HASHED_KEY, OLD_MAP_ROOT, KEY, NEW_VALUE] + + # set the NEW_VALUE under HASHED_KEY in the tree + exec.smt::set + # => [OLD_MAP_VALUE, NEW_ROOT, KEY, NEW_VALUE] + + # store OLD_MAP_VALUE until the end of the procedure + loc_storew_be.4 swapw + # => [NEW_ROOT, OLD_MAP_VALUE, KEY, NEW_VALUE] + + # store NEW_ROOT into the map slot's VALUE + loc_load.0 exec.set_item_raw + # => [OLD_MAP_VALUE, KEY, NEW_VALUE] + + swapw + # => [KEY, OLD_MAP_VALUE, NEW_VALUE] + + loc_load.0 + # => [slot_ptr, KEY, OLD_MAP_VALUE, NEW_VALUE] + + # emit event to signal that an account storage map item is being updated + emit.ACCOUNT_STORAGE_AFTER_SET_MAP_ITEM_EVENT + # => [slot_ptr, KEY, OLD_MAP_VALUE, NEW_VALUE] + + # convert the slot ptr to a slot index for the delta API + exec.slot_ptr_to_index + # => [slot_idx, KEY, OLD_MAP_VALUE, NEW_VALUE] + + exec.account_delta::set_map_item + # => [] + + # load OLD_MAP_ROOT and OLD_MAP_VALUE as return values on the stack + padw loc_loadw_be.4 padw loc_loadw_be.8 + # => [OLD_MAP_ROOT, OLD_MAP_VALUE] +end + +#! Gets the value of the storage slot located at the memory address specified by the provided +#! pointer. +#! +#! The provided pointer must be a valid pointer into the initial or current storage slot section. +#! +#! Inputs: [slot_ptr] #! Outputs: [VALUE] #! #! Where: -#! - storage_slots_ptr is the pointer to the storage slots section. -#! - index is the index of the item to get. +#! - slot_ptr is the pointer to a slot. #! - VALUE is the value of the item. -proc.get_item_by_index_raw - # get the item from storage - swap mul.8 add - # => [storage_slot_ptr] - +proc get_item_raw # offset the pointer to point to the VALUE in the slot - add.4 + add.ACCOUNT_SLOT_VALUE_OFFSET # => [slot_value_ptr] - # load the item from storage + # load the item from memory padw movup.4 mem_loadw_be # => [VALUE] end -#! Shared procedure for getting a map item from a storage slot without checking the index. +#! Finds the storage map root in the storage slot with the provided name in the provided storage +#! slots section and returns the VALUE associated with the KEY in the corresponding map. #! -#! WARNING: Must be called with an index that is in bounds. -#! -#! Inputs: [KEY, ROOT, index] +#! Inputs: [storage_slots_ptr, name_id_prefix, name_id_suffix, KEY] #! Outputs: [VALUE] #! #! Where: #! - KEY is the key to look up in the map. -#! - ROOT is the root of the map. -#! - index is the index of the storage slot that contains the map root. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - VALUE is the value of the map item at KEY. #! #! Panics if: +#! - a slot with the provided name ID does not exist in account storage. #! - the requested storage slot type is not map. proc.get_map_item_raw + exec.find_storage_slot + # => [slot_ptr, KEY] + emit.ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM_EVENT - # => [KEY, ROOT, index] + # => [slot_ptr, KEY] - # check storage slot type - movup.8 exec.get_storage_slot_type_by_index - # => [slot_type, KEY, ROOT] + # load the slot type + dup add.ACCOUNT_SLOT_TYPE_OFFSET mem_load + # => [slot_type, slot_ptr, KEY] - # check if storage slot type is map + # assert slot_type is map exec.constants::get_storage_slot_type_map eq assert.err=ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT - # => [KEY, ROOT] + # => [slot_ptr, KEY] + + # load the storage map root from the slot + exec.get_item_raw + # => [ROOT, KEY] # see hash_map_key's docs for why this is done - exec.hash_map_key + swapw exec.hash_map_key # => [HASHED_KEY, ROOT] # fetch the VALUE located under HASHED_KEY in the tree @@ -1623,67 +1724,10 @@ proc.get_map_item_raw # remove the ROOT from the stack swapw dropw # => [VALUE] - - movup.4 drop - # => [VALUE] -end - -#! Sets an item in the account storage. Doesn't emit any events. -#! -#! Inputs: [index, NEW_VALUE] -#! Outputs: [OLD_VALUE] -#! -#! Where: -#! - index is the index of the item to set. -#! - NEW_VALUE is the value to set. -#! - OLD_VALUE is the previous value of the item. -proc.set_item_raw - # TODO(named_slots): pass in name - - exec.memory::get_account_storage_slots_section_ptr - # => [storage_slots_ptr, index, NEW_VALUE] - - exec.find_indexed_storage_slot - # => [slot_ptr, NEW_VALUE] - - # offset the pointer to point to the VALUE in the slot - add.4 movdn.4 - # => [NEW_VALUE, slot_value_ptr] - - # load the item from storage - padw dup.8 mem_loadw_be - # => [OLD_VALUE, NEW_VALUE, slot_value_ptr] - - swapw movup.8 - # => [slot_value_ptr, NEW_VALUE, OLD_VALUE] - - mem_storew_be dropw - # => [OLD_VALUE] - - # update the storage commitment dirty flag, indicating that the commitment is outdated - push.1 - exec.memory::set_native_account_storage_commitment_dirty_flag - # => [OLD_VALUE] end -#! TODO(named_slots): Remove this API. -#! -#! Inputs: [storage_slots_ptr, index] -#! Outputs: [slot_ptr] -proc.find_indexed_storage_slot - # set the suffix to 0 - push.0 movup.2 - # => [name_id_prefix = index, name_id_suffix = 0, storage_slots_ptr] - - movup.2 - # => [storage_slots_ptr, name_id_prefix = index, name_id_suffix = 0] - - exec.find_storage_slot - # => [slot_ptr] -end - -#! Finds the slot identified by the key constructed from the name ID and slot type and returns the -#! pointer to that slot. +#! Finds the slot identified by the key [name_id_prefix, name_id_suffix, 0, 0] (stack order) and +#! returns the pointer to that slot. #! #! Inputs: [storage_slots_ptr, name_id_prefix, name_id_suffix] #! Outputs: [slot_ptr] @@ -1693,13 +1737,16 @@ end #! - slot_ptr is the pointer to the resolved storage slot. #! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are #! the first two felts of the hashed slot name. +#! +#! Panics if: +#! - a slot with the provided name ID does not exist in account storage. proc.find_storage_slot # construct the start and end pointers of the storage slot section in which we will search - dup exec.memory::get_num_storage_slots mul.8 add + dup exec.memory::get_num_storage_slots mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH add # => [storage_slots_end_ptr, storage_slots_start_ptr, name_id_prefix, name_id_suffix] - # TODO: Check if we can get here if num_storage_slots == 0. If so, return a better error than - # what find_key_value returns. + # TODO(named_slots): Check if we can get here if num_storage_slots == 0. If so, return a better + # error than what find_key_value returns. movdn.3 movdn.2 # => [name_id_prefix, name_id_suffix, storage_slots_start_ptr, storage_slots_end_ptr] @@ -1709,13 +1756,35 @@ proc.find_storage_slot exec.sorted_array::find_half_key_value # => [is_slot_found, slot_ptr, storage_slots_start_ptr, storage_slots_end_ptr] - assert.err=ERR_ACCOUNT_ID_UNKNOWN_STORAGE_SLOT_NAME + assert.err=ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME # => [slot_ptr, storage_slots_start_ptr, storage_slots_end_ptr] swap.2 drop drop # => [slot_ptr] end +#! Computes the slot index from a given slot ptr. +#! +#! WARNING: The slot ptr is assumed to be a valid pointer to a slot in an account's storage slots +#! section (**not** the initial one). In particular, this means the slot_ptr must be a valid u32. +#! +#! Inputs: [slot_ptr] +#! Outputs: [slot_idx] +#! +#! Where: +#! - slot_ptr is the pointer to a "current" storage slot in the native account's memory section. +#! - slot_idx is the index of the storage slot. +proc slot_ptr_to_index + exec.memory::get_account_storage_slots_section_ptr + sub + # => [slot_offset] + + # compute slot_offset / ACCOUNT_STORAGE_SLOT_DATA_LENGTH to get the index + # we can assume the slot_ptr is a valid u32. + u32div.ACCOUNT_STORAGE_SLOT_DATA_LENGTH + # => [slot_idx] +end + #! Returns the procedure root. #! #! Inputs: [index] @@ -1900,7 +1969,8 @@ proc.refresh_storage_commitment # => [num_storage_slots] # setup start and end ptr - mul.8 exec.memory::get_account_storage_slots_section_ptr dup movdn.2 add swap + mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH exec.memory::get_account_storage_slots_section_ptr + dup movdn.2 add swap # => [start_ptr, end_ptr] # pad stack to read and hash from memory diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm index 68ed259a1a..7aad58b79f 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm @@ -145,15 +145,10 @@ end #! Inputs: [slot_idx, RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] proc.update_slot_delta - # we use memory::get_storage_slot_type instead of the procedure in account to - # avoid the assertion overhead - dup exec.memory::get_native_account_storage_slots_ptr - # => [native_account_storage_slots_section_ptr, slot_idx, slot_idx, RATE, RATE, PERM] - - exec.account::get_storage_slot_type + dup exec.account::get_storage_slot_type # => [storage_slot_type, slot_idx, RATE, RATE, PERM] - # check if type == slot + # check if slot is of type value exec.constants::get_storage_slot_type_value eq # => [is_value_slot_type, slot_idx, RATE, RATE, PERM] @@ -170,48 +165,45 @@ end #! Inputs: [slot_idx, RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] proc.update_value_slot_delta - dup exec.account::get_item_by_index - # => [CURRENT_VALUE, slot_idx, RATE, RATE, PERM] - - dup.4 exec.account::get_initial_item_by_index - # => [INIT_VALUE, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] + exec.account::get_item_delta + # => [INIT_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] exec.word::test_eq not - # => [was_changed, INIT_VALUE, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] + # => [was_changed, INIT_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] # set was_changed to true if the account is new # generally, the delta for a new account must include all its storage slots, regardless of the # initial value and even if it is an empty word, because the initial delta for an account must # represent its full state exec.memory::is_new_account or - # => [was_changed, INIT_VALUE, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] + # => [was_changed, INIT_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] # only include in delta if the slot's value has changed or the account is new if.true # drop init value dropw - # => [CURRENT_VALUE, slot_idx, RATE, RATE, PERM] + # => [CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] # build value slot metadata - push.DOMAIN_VALUE - # => [domain, CURRENT_VALUE, slot_idx, RATE, RATE, PERM] + push.DOMAIN_VALUE push.0 + # => [0, domain, CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] - movup.5 push.0.0 - # => [0, 0, slot_idx, domain, CURRENT_VALUE, RATE, RATE, PERM] + movup.7 movup.7 + # => [name_id_prefix, name_id_suffix, 0, domain, CURRENT_VALUE, RATE, RATE, PERM] # clear rate elements swapdw dropw dropw - # => [0, 0, slot_idx, domain, CURRENT_VALUE, PERM] + # => [name_id_prefix, name_id_suffix, 0, domain, CURRENT_VALUE, PERM] # arrange rate words in correct order swapw - # => [CURRENT_VALUE, 0, 0, slot_idx, domain, PERM] + # => [CURRENT_VALUE, name_id_prefix, name_id_suffix, 0, domain, PERM] hperm # => [RATE, RATE, PERM] else - # drop init value, current value and slot idx - dropw dropw drop + # drop init value, current value and slot name + dropw dropw drop drop # => [RATE, RATE, PERM] end # => [RATE, RATE, PERM] @@ -223,18 +215,22 @@ end #! Outputs: [RATE, RATE, PERM] #! #! Locals: -#! 0: slot_idx -#! 1: has_next -#! 2: iter -#! 3: num_changed_entries -proc.update_map_slot_delta.4 +#! - 0: name_id_suffix +#! - 1: name_id_prefix +#! - 2: has_next +#! - 3: iter +#! - 4: num_changed_entries +proc.update_map_slot_delta.5 # initialize num_changed_entries = 0 # this is necessary because this procedure can be called multiple times and the second # invocation shouldn't reuse the first invocation's value - push.0 loc_store.3 + push.0 loc_store.4 # => [slot_idx, RATE, RATE, PERM] - dup loc_store.0 + dup exec.account::get_slot_id + # => [name_id_prefix, name_id_suffix, slot_idx, RATE, RATE, PERM] + + loc_store.1 loc_store.0 # => [slot_idx, RATE, RATE, PERM] exec.memory::get_account_delta_storage_map_ptr @@ -249,11 +245,11 @@ proc.update_map_slot_delta.4 # => [KEY, INIT_VALUE, NEW_VALUE, has_next, iter, ...] # store has_next - movup.12 loc_store.1 + movup.12 loc_store.2 # => [KEY, INIT_VALUE, NEW_VALUE, iter, ...] # store iter - movup.12 loc_store.2 + movup.12 loc_store.3 # => [KEY, INIT_VALUE, NEW_VALUE, ...] swapw.2 @@ -268,8 +264,8 @@ proc.update_map_slot_delta.4 swapw dropw # => [NEW_VALUE, KEY, RATE, RATE, PERM] - # increment number of changed entries in local 3 - loc_load.3 add.1 loc_store.3 + # increment number of changed entries in local + loc_load.4 add.1 loc_store.4 # => [NEW_VALUE, KEY, RATE, RATE, PERM] # drop previous RATE elements @@ -286,10 +282,10 @@ proc.update_map_slot_delta.4 # => [RATE, RATE, PERM] # load iter and has_next - loc_load.2 + loc_load.3 # => [iter, RATE, RATE, PERM] - loc_load.1 + loc_load.2 # => [has_next, iter, RATE, RATE, PERM] end @@ -299,7 +295,7 @@ proc.update_map_slot_delta.4 # only include the map slot metadata if there were entries in the map that resulted in an # update to the hasher state - loc_load.3 neq.0 + loc_load.4 neq.0 # => [is_num_changed_entries_non_zero, RATE, RATE, PERM] # if the account is new (nonce == 0) include the map header even if it is an empty map @@ -312,8 +308,8 @@ proc.update_map_slot_delta.4 dropw dropw # => [PERM] - push.DOMAIN_MAP loc_load.0 loc_load.3 push.0 padw - # => [EMPTY_WORD, [0, num_changed_entries, slot_idx, domain], PERM] + push.DOMAIN_MAP loc_load.4 loc_load.0 loc_load.1 padw + # => [EMPTY_WORD, [name_id_prefix, name_id_suffix, num_changed_entries, domain], PERM] hperm # => [RATE, RATE, PERM] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm b/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm index 7cd5f4e515..e2148bb2c7 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm @@ -43,7 +43,7 @@ export.mint_fungible_asset # => [ASSET] # get the current total issuance - exec.account::get_faucet_storage_data_slot exec.account::get_item + exec.account::get_faucet_storage_slot_id exec.account::get_item # => [TOTAL_ISSUANCE, ASSET] # prepare stack to ensure that minting the asset will not exceed the maximum @@ -55,7 +55,7 @@ export.mint_fungible_asset # => [amount, TOTAL_ISSUANCE, ASSET] # update the total issuance - add exec.account::get_faucet_storage_data_slot exec.account::set_item dropw + add exec.account::get_faucet_storage_slot_id exec.account::set_item dropw # => [ASSET] # add the asset to the input vault for asset preservation checks @@ -85,7 +85,7 @@ proc.burn_fungible_asset # => [ASSET] # fetch TOTAL_ISSUANCE such that we can compute the new total issuance - exec.account::get_faucet_storage_data_slot exec.account::get_item + exec.account::get_faucet_storage_slot_id exec.account::get_item # => [TOTAL_ISSUANCE, ASSET] # assert that the asset amount being burned is less or equal to the total issuance @@ -93,7 +93,7 @@ proc.burn_fungible_asset # => [amount, TOTAL_ISSUANCE, ASSET] # compute new total issuance - sub exec.account::get_faucet_storage_data_slot exec.account::set_item dropw + sub exec.account::get_faucet_storage_slot_id exec.account::set_item dropw # => [ASSET] # remove the asset from the input vault @@ -111,7 +111,7 @@ end #! against. export.get_total_issuance # fetch the TOTAL_ISSUANCE from storage - exec.account::get_faucet_storage_data_slot exec.account::get_item + exec.account::get_faucet_storage_slot_id exec.account::get_item # => [TOTAL_ISSUANCE] # extract the total_issuance and purge the padding @@ -147,8 +147,8 @@ proc.mint_non_fungible_asset # => [ASSET_KEY, ASSET, ASSET] # get the faucet storage data slot - exec.account::get_faucet_storage_data_slot - # => [faucet_storage_data_slot, ASSET_KEY, ASSET, ASSET] + exec.account::get_faucet_storage_slot_id + # => [faucet_slot_name_prefix, faucet_slot_name_suffix, ASSET_KEY, ASSET, ASSET] # insert the non-fungible asset into the tracking SMT exec.account::set_map_item dropw @@ -195,7 +195,7 @@ proc.burn_non_fungible_asset # => [ASSET_KEY, EMPTY_WORD, ASSET] # get the faucet storage data slot - exec.account::get_faucet_storage_data_slot + exec.account::get_faucet_storage_slot_id # => [faucet_storage_data_slot, ASSET_KEY, EMPTY_WORD, ASSET] # remove the non-fungible asset from the tracking SMT @@ -236,7 +236,7 @@ export.is_non_fungible_asset_issued # => [ASSET_KEY] # get the storage index where faucet's assets map is stored - exec.account::get_faucet_storage_data_slot + exec.account::get_faucet_storage_slot_id # => [map_slot_index, ASSET_KEY] # get the non-fungible asset stored by the computed account key diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index 557d7d9d70..28401469f7 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -276,6 +276,11 @@ end # ACCOUNT DATA # ================================================================================================= +#! TODO(named_slots): Validate that slot names are unique. +#! This should only be necessary whenever the slots of an account "change", which is essentially +#! the case when it is created, or when slots are added or removed (currently unimplemented). +#! So, it should be sufficient to do this here instead of in save_account_storage_data. +#! #! Validates that the account the transaction is being executed against satisfies the criteria #! for a new account. #! @@ -318,21 +323,17 @@ proc.validate_new_account # process conditional logic depending on whether the account is a faucet if.true # get the faucet reserved slot - exec.account::get_faucet_storage_data_slot exec.account::get_item - # => [FAUCET_RESERVED_SLOT, acct_id_prefix] + exec.account::get_faucet_storage_slot_id exec.account::get_typed_item + # => [FAUCET_RESERVED_SLOT, slot_type, acct_id_prefix] # check if the account is a fungible faucet - movup.4 exec.account_id::is_fungible_faucet - # => [is_fungible_faucet, FAUCET_RESERVED_SLOT] + movup.5 exec.account_id::is_fungible_faucet + # => [is_fungible_faucet, FAUCET_RESERVED_SLOT, slot_type] if.true # assert the fungible faucet reserved slot is initialized correctly (EMPTY_WORD) exec.word::eqz not assertz.err=ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY - # => [] - - # get the faucet reserved storage data slot type - exec.account::get_faucet_storage_data_slot exec.account::get_storage_slot_type_by_index # => [slot_type] # assert the fungible faucet reserved slot type == value @@ -344,10 +345,6 @@ proc.validate_new_account # empty SMT) exec.constants::get_empty_smt_root assert_eqw.err=ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT - # => [] - - # get the faucet reserved storage data slot type - exec.account::get_faucet_storage_data_slot exec.account::get_storage_slot_type_by_index # => [slot_type] # assert the non-fungible faucet reserved slot type == map diff --git a/crates/miden-lib/asm/miden/active_account.masm b/crates/miden-lib/asm/miden/active_account.masm index 576e6b672c..e6266c0a05 100644 --- a/crates/miden-lib/asm/miden/active_account.masm +++ b/crates/miden-lib/asm/miden/active_account.masm @@ -272,29 +272,30 @@ end # STORAGE # ------------------------------------------------------------------------------------------------- -#! Gets an item from the active account storage. Panics if the index is out of bounds. +#! Gets an item from the active account storage. #! -#! Inputs: [index] +#! Inputs: [name_id_prefix, name_id_suffix] #! Outputs: [VALUE] #! #! Where: -#! - index is the index of the item to get. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - VALUE is the value of the item. #! #! Panics if: -#! - the index of the requested item is out of bounds. +#! - a slot with the provided name ID does not exist in account storage. #! #! Invocation: exec export.get_item - push.0.0 movup.2 - # => [index, 0, 0] + push.0 movdn.2 + # => [name_id_prefix, name_id_suffix, 0] exec.kernel_proc_offsets::account_get_item_offset - # => [offset, index, 0, 0] + # => [offset, name_id_prefix, name_id_suffix, 0] # pad the stack padw swapw padw padw swapdw - # => [offset, index, pad(14)] + # => [offset, name_id_prefix, name_id_suffix, pad(13)] syscall.exec_kernel_proc # => [VALUE, pad(12)] @@ -307,27 +308,28 @@ end #! Gets the initial item from the active account storage slot as it was at the beginning of the #! transaction. #! -#! Inputs: [index] +#! Inputs: [name_id_prefix, name_id_suffix] #! Outputs: [INIT_VALUE] #! #! Where: -#! - index is the index of the item to get. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - INIT_VALUE is the initial value of the item at the beginning of the transaction. #! #! Panics if: -#! - the index of the requested item is out of bounds. +#! - a slot with the provided name ID does not exist in account storage. #! #! Invocation: exec export.get_initial_item - push.0.0 movup.2 - # => [index, 0, 0] + push.0 movdn.2 + # => [name_id_prefix, name_id_suffix, 0] exec.kernel_proc_offsets::account_get_initial_item_offset - # => [offset, index, 0, 0] + # => [offset, name_id_prefix, name_id_suffix, 0] # pad the stack padw swapw padw padw swapdw - # => [offset, index, pad(14)] + # => [offset, name_id_prefix, name_id_suffix, pad(13)] syscall.exec_kernel_proc # => [INIT_VALUE, pad(12)] @@ -339,26 +341,28 @@ end #! Gets a map item from the active account storage. #! -#! Inputs: [index, KEY] +#! Inputs: [name_id_prefix, name_id_suffix, KEY] #! Outputs: [VALUE] #! #! Where: -#! - index is the index of the map where the KEY VALUE should be read. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - the slot must point to the root of the storage map. #! - KEY is the key of the item to get. #! - VALUE is the value of the item. #! #! Panics if: -#! - the index for the map is out of bounds, meaning > 255. +#! - a slot with the provided name ID does not exist in account storage. #! - the slot item at index is not a map. #! #! Invocation: exec export.get_map_item exec.kernel_proc_offsets::account_get_map_item_offset - # => [offset, index, KEY] + # => [offset, name_id_prefix, name_id_suffix, KEY] # pad the stack - push.0.0 movdn.7 movdn.7 padw padw swapdw - # => [offset, index, KEY, pad(10)] + push.0 movdn.7 padw padw swapdw + # => [0, offset, name_id_prefix, name_id_suffix, KEY, pad(9)] syscall.exec_kernel_proc # => [VALUE, pad(12)] @@ -371,26 +375,26 @@ end #! Gets the initial VALUE from the active account storage map as it was at the beginning of the #! transaction. #! -#! Inputs: [index, KEY] +#! Inputs: [name_id_prefix, name_id_suffix, KEY] #! Outputs: [INIT_VALUE] #! #! Where: -#! - index is the index of the map where the KEY VALUE should be read. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - KEY is the key of the item to get. #! - INIT_VALUE is the initial value of the item at the beginning of the transaction. #! #! Panics if: -#! - the index for the map is out of bounds, meaning > 255. +#! - a slot with the provided name ID does not exist in account storage. #! - the slot item at index is not a map. #! #! Invocation: exec export.get_initial_map_item exec.kernel_proc_offsets::account_get_initial_map_item_offset - # => [offset, index, KEY] + # => [offset, name_id_prefix, name_id_suffix, KEY] - # pad the stack - push.0.0 movdn.7 movdn.7 padw padw swapdw - # => [offset, index, KEY, pad(10)] + push.0 movdn.7 padw padw swapdw + # => [0, offset, name_id_prefix, name_id_suffix, KEY, pad(9)] syscall.exec_kernel_proc # => [INIT_VALUE, pad(12)] diff --git a/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm b/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm index c2b04142df..3505028a21 100644 --- a/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm +++ b/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm @@ -10,12 +10,10 @@ use.std::crypto::dsa::ecdsa::secp256k1 # The event to request an authentication signature. const.AUTH_REQUEST_EVENT=event("miden::auth::request") -# The slot in this component's storage layout where the public key is stored. -const.PUBLIC_KEY_SLOT=0 - # Local Memory Addresses for multisig operations const.NUM_OF_APPROVERS_LOC=0 -const.PUB_KEY_MAP_IDX_LOC=4 +const.PUB_KEY_SLOT_SUFFIX_LOC=4 +const.PUB_KEY_SLOT_PREFIX_LOC=5 const.CURRENT_PK_LOC=8 const.SUCCESSFUL_VERIFICATIONS_LOC=12 @@ -81,10 +79,11 @@ end #! the owner public key mapping - the previous signers must authorize the change to the new signers, #! not the new signers authorizing themselves. #! -#! Inputs: [pub_key_slot_idx, num_of_approvers, MSG] +#! Inputs: [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, MSG] #! Outputs: [num_verified_signatures, MSG] export.verify_signatures.16 - loc_store.PUB_KEY_MAP_IDX_LOC + loc_store.PUB_KEY_SLOT_PREFIX_LOC + loc_store.PUB_KEY_SLOT_SUFFIX_LOC # => [num_of_approvers, MSG] # Initializing SUCCESSFUL_VERIFICATIONS local memory address to 0 @@ -104,8 +103,9 @@ export.verify_signatures.16 # Fetch public key from storage map. # ----------------------------------------------------------------------------------------- - sub.1 dup push.0.0.0 loc_load.PUB_KEY_MAP_IDX_LOC - # => [owner_key_slot, [0, 0, 0, i-1], i-1, MSG] + sub.1 dup push.0.0.0 + loc_load.PUB_KEY_SLOT_SUFFIX_LOC loc_load.PUB_KEY_SLOT_PREFIX_LOC + # => [owner_key_slot_prefix, owner_key_slot_suffix, [0, 0, 0, i-1], i-1, MSG] # Get public key from initial storage state exec.active_account::get_initial_map_item diff --git a/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm b/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm index 81da11a100..16f4ef165b 100644 --- a/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm +++ b/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm @@ -10,12 +10,10 @@ use.std::crypto::dsa::rpo_falcon512 # The event to request an authentication signature. const.AUTH_REQUEST_EVENT=event("miden::auth::request") -# The slot in this component's storage layout where the public key is stored. -const.PUBLIC_KEY_SLOT=0 - # Local Memory Addresses for multisig operations const.NUM_OF_APPROVERS_LOC=0 -const.PUB_KEY_MAP_IDX_LOC=4 +const.PUB_KEY_SLOT_SUFFIX_LOC=4 +const.PUB_KEY_SLOT_PREFIX_LOC=5 const.CURRENT_PK_LOC=8 const.SUCCESSFUL_VERIFICATIONS_LOC=12 @@ -81,10 +79,11 @@ end #! the owner public key mapping - the previous signers must authorize the change to the new signers, #! not the new signers authorizing themselves. #! -#! Inputs: [pub_key_slot_idx, num_of_approvers, MSG] +#! Inputs: [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, MSG] #! Outputs: [num_verified_signatures, MSG] export.verify_signatures.16 - loc_store.PUB_KEY_MAP_IDX_LOC + loc_store.PUB_KEY_SLOT_PREFIX_LOC + loc_store.PUB_KEY_SLOT_SUFFIX_LOC # => [num_of_approvers, MSG] # Initializing SUCCESSFUL_VERIFICATIONS local memory address to 0 @@ -104,8 +103,9 @@ export.verify_signatures.16 # Fetch public key from storage map. # ----------------------------------------------------------------------------------------- - sub.1 dup push.0.0.0 loc_load.PUB_KEY_MAP_IDX_LOC - # => [owner_key_slot, [0, 0, 0, i-1], i-1, MSG] + sub.1 dup push.0.0.0 + loc_load.PUB_KEY_SLOT_SUFFIX_LOC loc_load.PUB_KEY_SLOT_PREFIX_LOC + # => [owner_key_slot_prefix, owner_key_slot_suffix, [0, 0, 0, i-1], i-1, MSG] # Get public key from initial storage state exec.active_account::get_initial_map_item diff --git a/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm b/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm index 130df4d663..9930eda19c 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm +++ b/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm @@ -24,9 +24,6 @@ const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no # CONSTANTS # ================================================================================================= -# The slot in this component's storage layout where the metadata is stored. -const.METADATA_SLOT=0 - #! Distributes freshly minted fungible assets to the provided recipient by creating a note. #! #! Inputs: [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] diff --git a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm b/crates/miden-lib/asm/miden/contracts/faucets/mod.masm index b16d3ad808..0cc2bb067b 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm +++ b/crates/miden-lib/asm/miden/contracts/faucets/mod.masm @@ -17,8 +17,10 @@ const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no # CONSTANTS # ================================================================================================= -# The slot in this component's storage layout where the metadata is stored. -const.METADATA_SLOT=0 +# The slot in this component's storage where the metadata is stored. +# TODO(named_slots): Unify slot name or make distribute take name ID as a parameter to allow for +# different slot names per fungible faucet? +const.METADATA_SLOT=word("miden::basic_fungible_faucet::metadata") #! Distributes freshly minted fungible assets to the provided recipient by creating a note. #! @@ -41,7 +43,7 @@ const.METADATA_SLOT=0 #! Invocation: exec export.distribute # get max supply of this faucet. We assume it is stored at pos 3 of slot 0 - push.METADATA_SLOT exec.active_account::get_item drop drop drop + push.METADATA_SLOT[0..2] exec.active_account::get_item drop drop drop # => [max_supply, amount, tag, aux, note_type, execution_hint, RECIPIENT] # get total issuance of this faucet so far and add amount to be minted diff --git a/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm b/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm index a983169cfa..41e0e90db1 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm +++ b/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm @@ -8,7 +8,7 @@ use.miden::contracts::faucets::basic_fungible # ================================================================================================ # The slot in this component's storage layout where the owner config is stored. -const.OWNER_CONFIG_SLOT=1 +const.OWNER_CONFIG_SLOT=word("miden::network_fungible_faucet::owner_config") # ERRORS const.ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who can mint assets" @@ -21,10 +21,7 @@ const.ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who ca #! Where: #! - is_owner is 1 if the sender is the owner, 0 otherwise. proc.is_owner - push.OWNER_CONFIG_SLOT - # => [owner_config_slot] - - exec.active_account::get_item + push.OWNER_CONFIG_SLOT[0..2] exec.active_account::get_item # => [owner_prefix, owner_suffix, 0, 0] exec.active_note::get_sender diff --git a/crates/miden-lib/asm/miden/native_account.masm b/crates/miden-lib/asm/miden/native_account.masm index f09f638c0d..a3ab85b772 100644 --- a/crates/miden-lib/asm/miden/native_account.masm +++ b/crates/miden-lib/asm/miden/native_account.masm @@ -114,25 +114,28 @@ end #! Sets an item in the native account storage. #! -#! Inputs: [index, VALUE] +#! Inputs: [name_id_prefix, name_id_suffix, VALUE] #! Outputs: [OLD_VALUE] #! #! Where: -#! - index is the index of the item to set. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. #! - VALUE is the value to set. #! - OLD_VALUE is the previous value of the item. #! #! Panics if: -#! - the index of the item is out of bounds. +#! - a slot with the provided name ID does not exist in account storage. +#! - the invocation of this procedure does not originate from the native account. +#! - the native account is a faucet and the provided name ID points to the reserved faucet storage slot. #! #! Invocation: exec export.set_item exec.kernel_proc_offsets::account_set_item_offset - # => [offset, index, VALUE] + # => [offset, name_id_prefix, name_id_suffix, VALUE] # pad the stack - push.0.0 movdn.7 movdn.7 padw padw swapdw - # => [offset, index, VALUE, pad(10)] + push.0 movdn.7 padw padw swapdw + # => [offset, name_id_prefix, name_id_suffix, VALUE, pad(9)] syscall.exec_kernel_proc # => [OLD_VALUE, pad(12)] @@ -144,28 +147,38 @@ end #! Sets a map item in the native account storage. #! -#! Inputs: [index, KEY, VALUE] +#! Inputs: [name_id_prefix, name_id_suffix, KEY, VALUE] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE] #! #! Where: -#! - index is the index of the map where the KEY VALUE should be set. +#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! the first two felts of the hashed slot name. +#! - the slot must point to the root of the storage map. #! - KEY is the key to set at VALUE. #! - VALUE is the value to set at KEY. #! - OLD_MAP_ROOT is the old map root. #! - OLD_MAP_VALUE is the old value at KEY. #! #! Panics if: -#! - the index for the map is out of bounds, meaning > 255. -#! - the slot item at index is not a map. +#! - a slot with the provided name ID does not exist in account storage. +#! - the requested storage slot type is not map. +#! - the procedure is called from a non-account context. +#! - the invocation of this procedure does not originate from the native account. #! #! Invocation: exec export.set_map_item exec.kernel_proc_offsets::account_set_map_item_offset - # => [offset, index, KEY, VALUE] + # => [offset, name_id_prefix, name_id_suffix, KEY, VALUE] # pad the stack - push.0.0 movdn.11 movdn.11 padw movdnw.3 - # => [offset, index, KEY, VALUE, pad(6)] + push.0 padw + # => [pad(4), 0, offset, name_id_prefix, name_id_suffix, KEY, VALUE] + + movdnw.3 + # => [0, offset, name_id_prefix, name_id_suffix, KEY, VALUE, pad(4)] + + movdn.11 + # => [offset, name_id_prefix, name_id_suffix, KEY, VALUE, pad(5)] syscall.exec_kernel_proc # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs index d7005a1028..ccb588f5b3 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs @@ -1,8 +1,14 @@ use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageSlot}; +use miden_objects::account::{AccountComponent, NamedStorageSlot, SlotName}; +use miden_objects::utils::sync::LazyLock; use crate::account::components::ecdsa_k256_keccak_library; +static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::ecdsa_k256_keccak::public_key") + .expect("slot name should be valid") +}); + /// An [`AccountComponent`] implementing the ECDSA K256 Keccak signature scheme for authentication /// of transactions. /// @@ -25,13 +31,21 @@ impl AuthEcdsaK256Keccak { pub fn new(pub_key: PublicKeyCommitment) -> Self { Self { pub_key } } + + /// Returns the [`SlotName`] where the public key is stored. + pub fn public_key_slot() -> &'static SlotName { + &ECDSA_PUBKEY_SLOT_NAME + } } impl From for AccountComponent { fn from(ecdsa: AuthEcdsaK256Keccak) -> Self { AccountComponent::new( ecdsa_k256_keccak_library(), - vec![StorageSlot::Value(ecdsa.pub_key.into())], + vec![NamedStorageSlot::with_value( + AuthEcdsaK256Keccak::public_key_slot().clone(), + ecdsa.pub_key.into(), + )], ) .expect("ecdsa component should satisfy the requirements of a valid account component") .with_supports_all_types() diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs index 186ebd026f..8214cca372 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs @@ -1,11 +1,33 @@ use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountCode, AccountComponent, StorageMap, StorageSlot}; +use miden_objects::account::{ + AccountCode, + AccountComponent, + NamedStorageSlot, + SlotName, + StorageMap, +}; +use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_acl_library; +static PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::public_key") + .expect("slot name should be valid") +}); + +static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::config") + .expect("slot name should be valid") +}); + +static TRACKED_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::tracked_procedure_roots") + .expect("slot name should be valid") +}); + /// Configuration for [`AuthEcdsaK256KeccakAcl`] component. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AuthEcdsaK256KeccakAclConfig { @@ -133,25 +155,46 @@ impl AuthEcdsaK256KeccakAcl { Ok(Self { pub_key, config }) } + + /// Returns the [`SlotName`] where the public key is stored. + pub fn public_key_slot() -> &'static SlotName { + &PUBKEY_SLOT_NAME + } + + /// Returns the [`SlotName`] where the component's configuration is stored. + pub fn config_slot() -> &'static SlotName { + &CONFIG_SLOT_NAME + } + + /// Returns the [`SlotName`] where the tracked procedure roots are stored. + pub fn tracked_procedure_roots_slot() -> &'static SlotName { + &TRACKED_PROCEDURE_ROOT_SLOT_NAME + } } impl From for AccountComponent { fn from(ecdsa: AuthEcdsaK256KeccakAcl) -> Self { let mut storage_slots = Vec::with_capacity(3); - // Slot 0: Public key - storage_slots.push(StorageSlot::Value(ecdsa.pub_key.into())); - // Slot 1: [num_tracked_procs, allow_unauthorized_output_notes, - // allow_unauthorized_input_notes, 0] + // Public key slot + storage_slots.push(NamedStorageSlot::with_value( + AuthEcdsaK256KeccakAcl::public_key_slot().clone(), + ecdsa.pub_key.into(), + )); + + // Config slot let num_procs = ecdsa.config.auth_trigger_procedures.len() as u32; - storage_slots.push(StorageSlot::Value(Word::from([ - num_procs, - u32::from(ecdsa.config.allow_unauthorized_output_notes), - u32::from(ecdsa.config.allow_unauthorized_input_notes), - 0, - ]))); - - // Slot 2: A map with tracked procedure roots + storage_slots.push(NamedStorageSlot::with_value( + AuthEcdsaK256KeccakAcl::config_slot().clone(), + Word::from([ + num_procs, + u32::from(ecdsa.config.allow_unauthorized_output_notes), + u32::from(ecdsa.config.allow_unauthorized_input_notes), + 0, + ]), + )); + + // Tracked procedure roots slot // We add the map even if there are no trigger procedures, to always maintain the same // storage layout. let map_entries = ecdsa @@ -162,7 +205,10 @@ impl From for AccountComponent { .map(|(i, proc_root)| (Word::from([i as u32, 0, 0, 0]), *proc_root)); // Safe to unwrap because we know that the map keys are unique. - storage_slots.push(StorageSlot::Map(StorageMap::with_entries(map_entries).unwrap())); + storage_slots.push(NamedStorageSlot::with_map( + AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot().clone(), + StorageMap::with_entries(map_entries).unwrap(), + )); AccountComponent::new(ecdsa_k256_keccak_acl_library(), storage_slots) .expect( @@ -189,8 +235,8 @@ mod tests { allow_unauthorized_output_notes: bool, /// Allow unauthorized input notes flag allow_unauthorized_input_notes: bool, - /// Expected slot 1 value [num_procs, allow_output, allow_input, 0] - expected_slot_1: Word, + /// Expected config slot value [num_procs, allow_output, allow_input, 0] + expected_config_slot: Word, } /// Helper function to get the basic wallet procedures for testing @@ -229,20 +275,29 @@ mod tests { .build() .expect("account building failed"); - // Assert public key in slot 0 - let public_key_slot = account.storage().get_item(0).expect("storage slot 0 access failed"); + // Check public key storage + let public_key_slot = account + .storage() + .get_item(AuthEcdsaK256KeccakAcl::public_key_slot()) + .expect("public key storage slot access failed"); assert_eq!(public_key_slot, public_key.into()); - // Assert configuration in slot 1 - let slot_1 = account.storage().get_item(1).expect("storage slot 1 access failed"); - assert_eq!(slot_1, config.expected_slot_1); + // Check configuration storage + let config_slot = account + .storage() + .get_item(AuthEcdsaK256KeccakAcl::config_slot()) + .expect("config storage slot access failed"); + assert_eq!(config_slot, config.expected_config_slot); - // Assert procedure roots in map (slot 2) + // Check procedure roots if config.with_procedures { for (i, expected_proc_root) in auth_trigger_procedures.iter().enumerate() { let proc_root = account .storage() - .get_map_item(2, Word::from([i as u32, 0, 0, 0])) + .get_map_item( + AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot(), + Word::from([i as u32, 0, 0, 0]), + ) .expect("storage map access failed"); assert_eq!(proc_root, *expected_proc_root); } @@ -250,7 +305,7 @@ mod tests { // When no procedures, the map should return empty for key [0,0,0,0] let proc_root = account .storage() - .get_map_item(2, Word::empty()) + .get_map_item(AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot(), Word::empty()) .expect("storage map access failed"); assert_eq!(proc_root, Word::empty()); } @@ -263,7 +318,7 @@ mod tests { with_procedures: false, allow_unauthorized_output_notes: false, allow_unauthorized_input_notes: false, - expected_slot_1: Word::empty(), // [0, 0, 0, 0] + expected_config_slot: Word::empty(), // [0, 0, 0, 0] }); } @@ -274,7 +329,7 @@ mod tests { with_procedures: true, allow_unauthorized_output_notes: false, allow_unauthorized_input_notes: false, - expected_slot_1: Word::from([2u32, 0, 0, 0]), + expected_config_slot: Word::from([2u32, 0, 0, 0]), }); } @@ -285,7 +340,7 @@ mod tests { with_procedures: false, allow_unauthorized_output_notes: true, allow_unauthorized_input_notes: false, - expected_slot_1: Word::from([0u32, 1, 0, 0]), + expected_config_slot: Word::from([0u32, 1, 0, 0]), }); } @@ -296,7 +351,7 @@ mod tests { with_procedures: true, allow_unauthorized_output_notes: true, allow_unauthorized_input_notes: false, - expected_slot_1: Word::from([2u32, 1, 0, 0]), + expected_config_slot: Word::from([2u32, 1, 0, 0]), }); } @@ -307,7 +362,7 @@ mod tests { with_procedures: false, allow_unauthorized_output_notes: false, allow_unauthorized_input_notes: true, - expected_slot_1: Word::from([0u32, 0, 1, 0]), + expected_config_slot: Word::from([0u32, 0, 1, 0]), }); } @@ -318,7 +373,7 @@ mod tests { with_procedures: true, allow_unauthorized_output_notes: true, allow_unauthorized_input_notes: true, - expected_slot_1: Word::from([2u32, 1, 1, 0]), + expected_config_slot: Word::from([2u32, 1, 1, 0]), }); } } diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs index 7bb653cb62..c24898bdaf 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs @@ -2,11 +2,32 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageMap, StorageSlot}; +use miden_objects::account::{AccountComponent, NamedStorageSlot, SlotName, StorageMap}; +use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_multisig_library; +static THRESHOLD_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::threshold_config") + .expect("slot name should be valid") +}); + +static APPROVER_PUBKEYS_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::approver_public_keys") + .expect("slot name should be valid") +}); + +static EXECUTED_TRANSACTIONS_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::executed_transactions") + .expect("slot name should be valid") +}); + +static PROCEDURE_THRESHOLDS_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::procedure_thresholds") + .expect("slot name should be valid") +}); + // MULTISIG AUTHENTICATION COMPONENT // ================================================================================================ @@ -104,22 +125,40 @@ impl AuthEcdsaK256KeccakMultisig { pub fn new(config: AuthEcdsaK256KeccakMultisigConfig) -> Result { Ok(Self { config }) } + + /// Returns the [`SlotName`] where the threshold configuration is stored. + pub fn threshold_config_slot() -> &'static SlotName { + &THRESHOLD_CONFIG_SLOT_NAME + } + + /// Returns the [`SlotName`] where the approver public keys are stored. + pub fn approver_public_keys_slot() -> &'static SlotName { + &APPROVER_PUBKEYS_SLOT_NAME + } + + /// Returns the [`SlotName`] where the executed transactions are stored. + pub fn executed_transactions_slot() -> &'static SlotName { + &EXECUTED_TRANSACTIONS_SLOT_NAME + } + + /// Returns the [`SlotName`] where the procedure thresholds are stored. + pub fn procedure_thresholds_slot() -> &'static SlotName { + &PROCEDURE_THRESHOLDS_SLOT_NAME + } } impl From for AccountComponent { fn from(multisig: AuthEcdsaK256KeccakMultisig) -> Self { let mut storage_slots = Vec::with_capacity(3); - // Slot 0: [threshold, num_approvers, 0, 0] + // Threshold config slot (value: [threshold, num_approvers, 0, 0]) let num_approvers = multisig.config.approvers().len() as u32; - storage_slots.push(StorageSlot::Value(Word::from([ - multisig.config.default_threshold(), - num_approvers, - 0, - 0, - ]))); - - // Slot 1: A map with approver public keys + storage_slots.push(NamedStorageSlot::with_value( + AuthEcdsaK256KeccakMultisig::threshold_config_slot().clone(), + Word::from([multisig.config.default_threshold(), num_approvers, 0, 0]), + )); + + // Approver public keys slot (map) let map_entries = multisig .config .approvers() @@ -128,13 +167,19 @@ impl From for AccountComponent { .map(|(i, pub_key)| (Word::from([i as u32, 0, 0, 0]), (*pub_key).into())); // Safe to unwrap because we know that the map keys are unique. - storage_slots.push(StorageSlot::Map(StorageMap::with_entries(map_entries).unwrap())); + storage_slots.push(NamedStorageSlot::with_map( + AuthEcdsaK256KeccakMultisig::approver_public_keys_slot().clone(), + StorageMap::with_entries(map_entries).unwrap(), + )); - // Slot 2: A map which stores executed transactions + // Executed transactions slot (map) let executed_transactions = StorageMap::default(); - storage_slots.push(StorageSlot::Map(executed_transactions)); + storage_slots.push(NamedStorageSlot::with_map( + AuthEcdsaK256KeccakMultisig::executed_transactions_slot().clone(), + executed_transactions, + )); - // Slot 3: A map which stores procedure thresholds (PROC_ROOT -> threshold) + // Procedure thresholds slot (map: PROC_ROOT -> threshold) let proc_threshold_roots = StorageMap::with_entries( multisig .config @@ -143,7 +188,10 @@ impl From for AccountComponent { .map(|(proc_root, threshold)| (*proc_root, Word::from([*threshold, 0, 0, 0]))), ) .unwrap(); - storage_slots.push(StorageSlot::Map(proc_threshold_roots)); + storage_slots.push(NamedStorageSlot::with_map( + AuthEcdsaK256KeccakMultisig::procedure_thresholds_slot().clone(), + proc_threshold_roots, + )); AccountComponent::new(ecdsa_k256_keccak_multisig_library(), storage_slots) .expect("Multisig auth component should satisfy the requirements of a valid account component") @@ -185,16 +233,22 @@ mod tests { .build() .expect("account building failed"); - // Verify slot 0: [threshold, num_approvers, 0, 0] - let threshold_slot = account.storage().get_item(0).expect("storage slot 0 access failed"); - assert_eq!(threshold_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); + // Verify config slot: [threshold, num_approvers, 0, 0] + let config_slot = account + .storage() + .get_item(AuthEcdsaK256KeccakMultisig::threshold_config_slot()) + .expect("config storage slot access failed"); + assert_eq!(config_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); - // Verify slot 1: Approver public keys in map + // Verify approver pub keys slot for (i, expected_pub_key) in approvers.iter().enumerate() { let stored_pub_key = account .storage() - .get_map_item(1, Word::from([i as u32, 0, 0, 0])) - .expect("storage map access failed"); + .get_map_item( + AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(), + Word::from([i as u32, 0, 0, 0]), + ) + .expect("approver public key storage map access failed"); assert_eq!(stored_pub_key, Word::from(*expected_pub_key)); } } @@ -219,13 +273,19 @@ mod tests { .expect("account building failed"); // Verify storage layout - let threshold_slot = account.storage().get_item(0).expect("storage slot 0 access failed"); - assert_eq!(threshold_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); + let config_slot = account + .storage() + .get_item(AuthEcdsaK256KeccakMultisig::threshold_config_slot()) + .expect("config storage slot access failed"); + assert_eq!(config_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); let stored_pub_key = account .storage() - .get_map_item(1, Word::from([0u32, 0, 0, 0])) - .expect("storage map access failed"); + .get_map_item( + AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(), + Word::from([0u32, 0, 0, 0]), + ) + .expect("approver pub keys storage map access failed"); assert_eq!(stored_pub_key, Word::from(pub_key)); } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs index a8e3e2ada8..dec8f6d7d4 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs @@ -1,8 +1,14 @@ use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageSlot}; +use miden_objects::account::{AccountComponent, NamedStorageSlot, SlotName}; +use miden_objects::utils::sync::LazyLock; use crate::account::components::rpo_falcon_512_library; +static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::rpo_falcon512::public_key") + .expect("slot name should be valid") +}); + /// An [`AccountComponent`] implementing the RpoFalcon512 signature scheme for authentication of /// transactions. /// @@ -15,6 +21,10 @@ use crate::account::components::rpo_falcon_512_library; /// /// This component supports all account types. /// +/// ## Storage Layout +/// +/// - [`Self::public_key_slot`]: Public key +/// /// [kasm]: crate::transaction::TransactionKernel::assembler pub struct AuthRpoFalcon512 { pub_key: PublicKeyCommitment, @@ -25,13 +35,21 @@ impl AuthRpoFalcon512 { pub fn new(pub_key: PublicKeyCommitment) -> Self { Self { pub_key } } + + /// Returns the [`SlotName`] where the public key is stored. + pub fn public_key_slot() -> &'static SlotName { + &FALCON_PUBKEY_SLOT_NAME + } } impl From for AccountComponent { fn from(falcon: AuthRpoFalcon512) -> Self { AccountComponent::new( rpo_falcon_512_library(), - vec![StorageSlot::Value(falcon.pub_key.into())], + vec![NamedStorageSlot::with_value( + AuthRpoFalcon512::public_key_slot().clone(), + falcon.pub_key.into(), + )], ) .expect("falcon component should satisfy the requirements of a valid account component") .with_supports_all_types() diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs index f9982d5456..8d11fa3c52 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs @@ -1,11 +1,33 @@ use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountCode, AccountComponent, StorageMap, StorageSlot}; +use miden_objects::account::{ + AccountCode, + AccountComponent, + NamedStorageSlot, + SlotName, + StorageMap, +}; +use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; use crate::account::components::rpo_falcon_512_acl_library; +static PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::rpo_falcon512_acl::public_key") + .expect("slot name should be valid") +}); + +static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::rpo_falcon512_acl::config") + .expect("slot name should be valid") +}); + +static TRACKED_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::rpo_falcon512_acl::tracked_procedure_roots") + .expect("slot name should be valid") +}); + /// Configuration for [`AuthRpoFalcon512Acl`] component. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AuthRpoFalcon512AclConfig { @@ -95,10 +117,11 @@ impl Default for AuthRpoFalcon512AclConfig { /// allowing free note processing. /// /// ## Storage Layout -/// - Slot 0(value): Public key (same as RpoFalcon512) -/// - Slot 1(value): [num_tracked_procs, allow_unauthorized_output_notes, -/// allow_unauthorized_input_notes, 0] -/// - Slot 2(map): A map with trigger procedure roots +/// +/// - [`Self::public_key_slot`]: Public key +/// - [`Self::config_slot`]: `[num_tracked_procs, allow_unauthorized_output_notes, +/// allow_unauthorized_input_notes, 0]` +/// - [`Self::tracked_procedure_roots_slot`]: A map with trigger procedure roots /// /// ## Important Note on Procedure Detection /// The procedure-based authentication relies on the `was_procedure_called` kernel function, @@ -133,26 +156,46 @@ impl AuthRpoFalcon512Acl { Ok(Self { pub_key, config }) } + + /// Returns the [`SlotName`] where the public key is stored. + pub fn public_key_slot() -> &'static SlotName { + &PUBKEY_SLOT_NAME + } + + /// Returns the [`SlotName`] where the component's configuration is stored. + pub fn config_slot() -> &'static SlotName { + &CONFIG_SLOT_NAME + } + + /// Returns the [`SlotName`] where the tracked procedure roots are stored. + pub fn tracked_procedure_roots_slot() -> &'static SlotName { + &TRACKED_PROCEDURE_ROOT_SLOT_NAME + } } impl From for AccountComponent { fn from(falcon: AuthRpoFalcon512Acl) -> Self { let mut storage_slots = Vec::with_capacity(3); - // Slot 0: Public key - storage_slots.push(StorageSlot::Value(falcon.pub_key.into())); + // Public key slot + storage_slots.push(NamedStorageSlot::with_value( + AuthRpoFalcon512Acl::public_key_slot().clone(), + falcon.pub_key.into(), + )); - // Slot 1: [num_tracked_procs, allow_unauthorized_output_notes, - // allow_unauthorized_input_notes, 0] + // Config slot let num_procs = falcon.config.auth_trigger_procedures.len() as u32; - storage_slots.push(StorageSlot::Value(Word::from([ - num_procs, - u32::from(falcon.config.allow_unauthorized_output_notes), - u32::from(falcon.config.allow_unauthorized_input_notes), - 0, - ]))); - - // Slot 2: A map with tracked procedure roots + storage_slots.push(NamedStorageSlot::with_value( + AuthRpoFalcon512Acl::config_slot().clone(), + Word::from([ + num_procs, + u32::from(falcon.config.allow_unauthorized_output_notes), + u32::from(falcon.config.allow_unauthorized_input_notes), + 0, + ]), + )); + + // Tracked procedure roots slot // We add the map even if there are no trigger procedures, to always maintain the same // storage layout. let map_entries = falcon @@ -163,7 +206,10 @@ impl From for AccountComponent { .map(|(i, proc_root)| (Word::from([i as u32, 0, 0, 0]), *proc_root)); // Safe to unwrap because we know that the map keys are unique. - storage_slots.push(StorageSlot::Map(StorageMap::with_entries(map_entries).unwrap())); + storage_slots.push(NamedStorageSlot::with_map( + AuthRpoFalcon512Acl::tracked_procedure_roots_slot().clone(), + StorageMap::with_entries(map_entries).unwrap(), + )); AccountComponent::new(rpo_falcon_512_acl_library(), storage_slots) .expect( @@ -190,8 +236,8 @@ mod tests { allow_unauthorized_output_notes: bool, /// Allow unauthorized input notes flag allow_unauthorized_input_notes: bool, - /// Expected slot 1 value [num_procs, allow_output, allow_input, 0] - expected_slot_1: Word, + /// Expected config slot value [num_procs, allow_output, allow_input, 0] + expected_config_slot: Word, } /// Helper function to get the basic wallet procedures for testing @@ -230,20 +276,29 @@ mod tests { .build() .expect("account building failed"); - // Assert public key in slot 0 - let public_key_slot = account.storage().get_item(0).expect("storage slot 0 access failed"); + // Check public key storage + let public_key_slot = account + .storage() + .get_item(AuthRpoFalcon512Acl::public_key_slot()) + .expect("public key storage slot access failed"); assert_eq!(public_key_slot, public_key.into()); - // Assert configuration in slot 1 - let slot_1 = account.storage().get_item(1).expect("storage slot 1 access failed"); - assert_eq!(slot_1, config.expected_slot_1); + // Check configuration storage + let config_slot = account + .storage() + .get_item(AuthRpoFalcon512Acl::config_slot()) + .expect("config storage slot access failed"); + assert_eq!(config_slot, config.expected_config_slot); - // Assert procedure roots in map (slot 2) + // Check procedure roots if config.with_procedures { for (i, expected_proc_root) in auth_trigger_procedures.iter().enumerate() { let proc_root = account .storage() - .get_map_item(2, Word::from([i as u32, 0, 0, 0])) + .get_map_item( + AuthRpoFalcon512Acl::tracked_procedure_roots_slot(), + Word::from([i as u32, 0, 0, 0]), + ) .expect("storage map access failed"); assert_eq!(proc_root, *expected_proc_root); } @@ -251,7 +306,7 @@ mod tests { // When no procedures, the map should return empty for key [0,0,0,0] let proc_root = account .storage() - .get_map_item(2, Word::empty()) + .get_map_item(AuthRpoFalcon512Acl::tracked_procedure_roots_slot(), Word::empty()) .expect("storage map access failed"); assert_eq!(proc_root, Word::empty()); } @@ -264,7 +319,7 @@ mod tests { with_procedures: false, allow_unauthorized_output_notes: false, allow_unauthorized_input_notes: false, - expected_slot_1: Word::empty(), // [0, 0, 0, 0] + expected_config_slot: Word::empty(), // [0, 0, 0, 0] }); } @@ -275,7 +330,7 @@ mod tests { with_procedures: true, allow_unauthorized_output_notes: false, allow_unauthorized_input_notes: false, - expected_slot_1: Word::from([2u32, 0, 0, 0]), + expected_config_slot: Word::from([2u32, 0, 0, 0]), }); } @@ -286,7 +341,7 @@ mod tests { with_procedures: false, allow_unauthorized_output_notes: true, allow_unauthorized_input_notes: false, - expected_slot_1: Word::from([0u32, 1, 0, 0]), + expected_config_slot: Word::from([0u32, 1, 0, 0]), }); } @@ -297,7 +352,7 @@ mod tests { with_procedures: true, allow_unauthorized_output_notes: true, allow_unauthorized_input_notes: false, - expected_slot_1: Word::from([2u32, 1, 0, 0]), + expected_config_slot: Word::from([2u32, 1, 0, 0]), }); } @@ -308,7 +363,7 @@ mod tests { with_procedures: false, allow_unauthorized_output_notes: false, allow_unauthorized_input_notes: true, - expected_slot_1: Word::from([0u32, 0, 1, 0]), + expected_config_slot: Word::from([0u32, 0, 1, 0]), }); } @@ -319,7 +374,7 @@ mod tests { with_procedures: true, allow_unauthorized_output_notes: true, allow_unauthorized_input_notes: true, - expected_slot_1: Word::from([2u32, 1, 1, 0]), + expected_config_slot: Word::from([2u32, 1, 1, 0]), }); } } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs index 2d933d990a..1ec2775f24 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs @@ -2,11 +2,32 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageMap, StorageSlot}; +use miden_objects::account::{AccountComponent, NamedStorageSlot, SlotName, StorageMap}; +use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; use crate::account::components::rpo_falcon_512_multisig_library; +static THRESHOLD_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::rpo_falcon512_multisig::threshold_config") + .expect("slot name should be valid") +}); + +static APPROVER_PUBKEYS_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::rpo_falcon512_multisig::approver_public_keys") + .expect("slot name should be valid") +}); + +static EXECUTED_TRANSACTIONS_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::rpo_falcon512_multisig::executed_transactions") + .expect("slot name should be valid") +}); + +static PROCEDURE_THRESHOLDS_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::standards::auth::rpo_falcon512_multisig::procedure_thresholds") + .expect("slot name should be valid") +}); + // MULTISIG AUTHENTICATION COMPONENT // ================================================================================================ @@ -87,11 +108,13 @@ impl AuthRpoFalcon512MultisigConfig { /// should be used with caution for private multisig accounts, as a single approver could withhold /// the new state from other approvers, effectively locking them out. /// -/// The storage layout is: -/// - Slot 0(value): [threshold, num_approvers, 0, 0] -/// - Slot 1(map): A map with approver public keys (index -> pubkey) -/// - Slot 2(map): A map which stores executed transactions -/// - Slot 3(map): A map which stores procedure thresholds (PROC_ROOT -> threshold) +/// ## Storage Layout +/// +/// - [`Self::threshold_config_slot`]: `[threshold, num_approvers, 0, 0]` +/// - [`Self::approver_public_keys_slot`]: A map with approver public keys (index -> pubkey) +/// - [`Self::executed_transactions_slot`]: A map which stores executed transactions +/// - [`Self::procedure_thresholds_slot`]: A map which stores procedure thresholds (PROC_ROOT -> +/// threshold) /// /// This component supports all account types. #[derive(Debug)] @@ -104,22 +127,40 @@ impl AuthRpoFalcon512Multisig { pub fn new(config: AuthRpoFalcon512MultisigConfig) -> Result { Ok(Self { config }) } + + /// Returns the [`SlotName`] where the threshold configuration is stored. + pub fn threshold_config_slot() -> &'static SlotName { + &THRESHOLD_CONFIG_SLOT_NAME + } + + /// Returns the [`SlotName`] where the approver public keys are stored. + pub fn approver_public_keys_slot() -> &'static SlotName { + &APPROVER_PUBKEYS_SLOT_NAME + } + + /// Returns the [`SlotName`] where the executed transactions are stored. + pub fn executed_transactions_slot() -> &'static SlotName { + &EXECUTED_TRANSACTIONS_SLOT_NAME + } + + /// Returns the [`SlotName`] where the procedure thresholds are stored. + pub fn procedure_thresholds_slot() -> &'static SlotName { + &PROCEDURE_THRESHOLDS_SLOT_NAME + } } impl From for AccountComponent { fn from(multisig: AuthRpoFalcon512Multisig) -> Self { let mut storage_slots = Vec::with_capacity(3); - // Slot 0: [threshold, num_approvers, 0, 0] + // Threshold config slot (value: [threshold, num_approvers, 0, 0]) let num_approvers = multisig.config.approvers().len() as u32; - storage_slots.push(StorageSlot::Value(Word::from([ - multisig.config.default_threshold(), - num_approvers, - 0, - 0, - ]))); - - // Slot 1: A map with approver public keys + storage_slots.push(NamedStorageSlot::with_value( + AuthRpoFalcon512Multisig::threshold_config_slot().clone(), + Word::from([multisig.config.default_threshold(), num_approvers, 0, 0]), + )); + + // Approver public keys slot (map) let map_entries = multisig .config .approvers() @@ -128,13 +169,19 @@ impl From for AccountComponent { .map(|(i, pub_key)| (Word::from([i as u32, 0, 0, 0]), (*pub_key).into())); // Safe to unwrap because we know that the map keys are unique. - storage_slots.push(StorageSlot::Map(StorageMap::with_entries(map_entries).unwrap())); + storage_slots.push(NamedStorageSlot::with_map( + AuthRpoFalcon512Multisig::approver_public_keys_slot().clone(), + StorageMap::with_entries(map_entries).unwrap(), + )); - // Slot 2: A map which stores executed transactions + // Executed transactions slot (map) let executed_transactions = StorageMap::default(); - storage_slots.push(StorageSlot::Map(executed_transactions)); + storage_slots.push(NamedStorageSlot::with_map( + AuthRpoFalcon512Multisig::executed_transactions_slot().clone(), + executed_transactions, + )); - // Slot 3: A map which stores procedure thresholds (PROC_ROOT -> threshold) + // Procedure thresholds slot (map: PROC_ROOT -> threshold) let proc_threshold_roots = StorageMap::with_entries( multisig .config @@ -143,7 +190,10 @@ impl From for AccountComponent { .map(|(proc_root, threshold)| (*proc_root, Word::from([*threshold, 0, 0, 0]))), ) .unwrap(); - storage_slots.push(StorageSlot::Map(proc_threshold_roots)); + storage_slots.push(NamedStorageSlot::with_map( + AuthRpoFalcon512Multisig::procedure_thresholds_slot().clone(), + proc_threshold_roots, + )); AccountComponent::new(rpo_falcon_512_multisig_library(), storage_slots) .expect("Multisig auth component should satisfy the requirements of a valid account component") @@ -185,16 +235,22 @@ mod tests { .build() .expect("account building failed"); - // Verify slot 0: [threshold, num_approvers, 0, 0] - let threshold_slot = account.storage().get_item(0).expect("storage slot 0 access failed"); - assert_eq!(threshold_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); + // Verify config slot: [threshold, num_approvers, 0, 0] + let config_slot = account + .storage() + .get_item(AuthRpoFalcon512Multisig::threshold_config_slot()) + .expect("config storage slot access failed"); + assert_eq!(config_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); - // Verify slot 1: Approver public keys in map + // Verify approver pub keys slot for (i, expected_pub_key) in approvers.iter().enumerate() { let stored_pub_key = account .storage() - .get_map_item(1, Word::from([i as u32, 0, 0, 0])) - .expect("storage map access failed"); + .get_map_item( + AuthRpoFalcon512Multisig::approver_public_keys_slot(), + Word::from([i as u32, 0, 0, 0]), + ) + .expect("approver public key storage map access failed"); assert_eq!(stored_pub_key, Word::from(*expected_pub_key)); } } @@ -219,13 +275,19 @@ mod tests { .expect("account building failed"); // Verify storage layout - let threshold_slot = account.storage().get_item(0).expect("storage slot 0 access failed"); - assert_eq!(threshold_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); + let config_slot = account + .storage() + .get_item(AuthRpoFalcon512Multisig::threshold_config_slot()) + .expect("config storage slot access failed"); + assert_eq!(config_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); let stored_pub_key = account .storage() - .get_map_item(1, Word::from([0u32, 0, 0, 0])) - .expect("storage map access failed"); + .get_map_item( + AuthRpoFalcon512Multisig::approver_public_keys_slot(), + Word::from([0u32, 0, 0, 0]), + ) + .expect("approver pub keys storage map access failed"); assert_eq!(stored_pub_key, Word::from(pub_key)); } diff --git a/crates/miden-lib/src/account/components/mod.rs b/crates/miden-lib/src/account/components/mod.rs index 8e6f257dcd..c1946b28bd 100644 --- a/crates/miden-lib/src/account/components/mod.rs +++ b/crates/miden-lib/src/account/components/mod.rs @@ -212,22 +212,28 @@ impl WellKnownComponent { Self::BasicWallet => { component_interface_vec.push(AccountComponentInterface::BasicWallet) }, - Self::BasicFungibleFaucet => component_interface_vec - .push(AccountComponentInterface::BasicFungibleFaucet(storage_offset)), - Self::NetworkFungibleFaucet => component_interface_vec - .push(AccountComponentInterface::NetworkFungibleFaucet(storage_offset)), - Self::AuthEcdsaK256Keccak => component_interface_vec - .push(AccountComponentInterface::AuthEcdsaK256Keccak(storage_offset)), - Self::AuthEcdsaK256KeccakAcl => component_interface_vec - .push(AccountComponentInterface::AuthEcdsaK256KeccakAcl(storage_offset)), + Self::BasicFungibleFaucet => { + component_interface_vec.push(AccountComponentInterface::BasicFungibleFaucet) + }, + Self::NetworkFungibleFaucet => { + component_interface_vec.push(AccountComponentInterface::NetworkFungibleFaucet) + }, + Self::AuthEcdsaK256Keccak => { + component_interface_vec.push(AccountComponentInterface::AuthEcdsaK256Keccak) + }, + Self::AuthEcdsaK256KeccakAcl => { + component_interface_vec.push(AccountComponentInterface::AuthEcdsaK256KeccakAcl) + }, Self::AuthEcdsaK256KeccakMultisig => component_interface_vec - .push(AccountComponentInterface::AuthEcdsaK256KeccakMultisig(storage_offset)), - Self::AuthRpoFalcon512 => component_interface_vec - .push(AccountComponentInterface::AuthRpoFalcon512(storage_offset)), - Self::AuthRpoFalcon512Acl => component_interface_vec - .push(AccountComponentInterface::AuthRpoFalcon512Acl(storage_offset)), + .push(AccountComponentInterface::AuthEcdsaK256KeccakMultisig), + Self::AuthRpoFalcon512 => { + component_interface_vec.push(AccountComponentInterface::AuthRpoFalcon512) + }, + Self::AuthRpoFalcon512Acl => { + component_interface_vec.push(AccountComponentInterface::AuthRpoFalcon512Acl) + }, Self::AuthRpoFalcon512Multisig => component_interface_vec - .push(AccountComponentInterface::AuthRpoFalcon512Multisig(storage_offset)), + .push(AccountComponentInterface::AuthRpoFalcon512Multisig), Self::AuthNoAuth => { component_interface_vec.push(AccountComponentInterface::AuthNoAuth) }, diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 9d86cadf92..5e7ee5e75a 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -5,9 +5,11 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - StorageSlot, + NamedStorageSlot, + SlotName, }; use miden_objects::asset::{FungibleAsset, TokenSymbol}; +use miden_objects::utils::sync::LazyLock; use miden_objects::{Felt, FieldElement, Word}; use super::FungibleFaucetError; @@ -39,6 +41,10 @@ procedure_digest!( basic_fungible_faucet_library ); +static METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::basic_fungible_faucet::metadata").expect("slot name should be valid") +}); + /// An [`AccountComponent`] implementing a basic fungible faucet. /// /// It reexports the procedures from `miden::contracts::faucets::basic_fungible`. When linking @@ -55,6 +61,10 @@ procedure_digest!( /// /// This component supports accounts of type [`AccountType::FungibleFaucet`]. /// +/// ## Storage Layout +/// +/// - [`Self::metadata_slot_name`]: Basic fungible faucet's metadata +/// /// [kasm]: crate::transaction::TransactionKernel::assembler pub struct BasicFungibleFaucet { symbol: TokenSymbol, @@ -120,12 +130,13 @@ impl BasicFungibleFaucet { storage: &AccountStorage, ) -> Result { for component in interface.components().iter() { - if let AccountComponentInterface::BasicFungibleFaucet(offset) = component { - // obtain metadata from storage using offset provided by BasicFungibleFaucet - // interface + if let AccountComponentInterface::BasicFungibleFaucet = component { let faucet_metadata = storage - .get_item(*offset) - .map_err(|_| FungibleFaucetError::InvalidStorageOffset(*offset))?; + .get_item(BasicFungibleFaucet::metadata_slot_name()) + .map_err(|err| FungibleFaucetError::StorageLookupFailed { + slot_name: BasicFungibleFaucet::metadata_slot_name().clone(), + source: err, + })?; let [max_supply, decimals, token_symbol, _] = *faucet_metadata; // verify metadata values @@ -148,6 +159,11 @@ impl BasicFungibleFaucet { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- + /// Returns the [`SlotName`] where the [`BasicFungibleFaucet`]'s metadata is stored. + pub fn metadata_slot_name() -> &'static SlotName { + &METADATA_SLOT_NAME + } + /// Returns the symbol of the faucet. pub fn symbol(&self) -> TokenSymbol { self.symbol @@ -184,8 +200,12 @@ impl From for AccountComponent { faucet.symbol.into(), Felt::ZERO, ]); + let storage_slot = NamedStorageSlot::with_value( + BasicFungibleFaucet::metadata_slot_name().clone(), + metadata, + ); - AccountComponent::new(basic_fungible_faucet_library(), vec![StorageSlot::Value(metadata)]) + AccountComponent::new(basic_fungible_faucet_library(), vec![storage_slot]) .expect("basic fungible faucet component should satisfy the requirements of a valid account component") .with_supported_type(AccountType::FungibleFaucet) } @@ -298,6 +318,7 @@ pub fn create_basic_fungible_faucet( #[cfg(test)] mod tests { use assert_matches::assert_matches; + use miden_objects::account::AccountStorage; use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::{FieldElement, ONE, Word}; @@ -312,7 +333,7 @@ mod tests { TokenSymbol, create_basic_fungible_faucet, }; - use crate::account::auth::AuthRpoFalcon512; + use crate::account::auth::{AuthRpoFalcon512, AuthRpoFalcon512Acl}; use crate::account::wallets::BasicWallet; #[test] @@ -343,35 +364,52 @@ mod tests { .unwrap(); // The reserved faucet slot should be initialized to an empty word. - assert_eq!(faucet_account.storage().get_item(0).unwrap(), Word::empty()); + assert_eq!( + faucet_account + .storage() + .get_item(AccountStorage::faucet_metadata_slot()) + .unwrap(), + Word::empty() + ); - // The falcon auth component is added first so its assigned storage slot for the public key - // will be 1. - assert_eq!(faucet_account.storage().get_item(1).unwrap(), pub_key_word); + // The falcon auth component's public key should be present. + assert_eq!( + faucet_account + .storage() + .get_item(AuthRpoFalcon512Acl::public_key_slot()) + .unwrap(), + pub_key_word + ); - // Slot 2 stores [num_tracked_procs, allow_unauthorized_output_notes, - // allow_unauthorized_input_notes, 0]. With 1 tracked procedure (distribute), - // allow_unauthorized_output_notes=false, and allow_unauthorized_input_notes=true, - // this should be [1, 0, 1, 0]. + // The config slot of the auth component stores: + // [num_tracked_procs, allow_unauthorized_output_notes, allow_unauthorized_input_notes, 0]. + // + // With 1 tracked procedure (distribute), allow_unauthorized_output_notes=false, and + // allow_unauthorized_input_notes=true, this should be [1, 0, 1, 0]. assert_eq!( - faucet_account.storage().get_item(2).unwrap(), + faucet_account.storage().get_item(AuthRpoFalcon512Acl::config_slot()).unwrap(), [Felt::ONE, Felt::ZERO, Felt::ONE, Felt::ZERO].into() ); - // The procedure root map in slot 3 should contain the distribute procedure root. + // The procedure root map should contain the distribute procedure root. let distribute_root = BasicFungibleFaucet::distribute_digest(); assert_eq!( faucet_account .storage() - .get_map_item(3, [Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO].into()) + .get_map_item( + AuthRpoFalcon512Acl::tracked_procedure_roots_slot(), + [Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO].into() + ) .unwrap(), distribute_root ); - // Check that faucet metadata was initialized to the given values. The faucet component is - // added second, so its assigned storage slot for the metadata will be 2. + // Check that faucet metadata was initialized to the given values. assert_eq!( - faucet_account.storage().get_item(4).unwrap(), + faucet_account + .storage() + .get_item(BasicFungibleFaucet::metadata_slot_name()) + .unwrap(), [Felt::new(123), Felt::new(2), token_symbol.into(), Felt::ZERO].into() ); @@ -407,9 +445,9 @@ mod tests { let basic_ff = BasicFungibleFaucet::try_from(faucet_account) .expect("basic fungible faucet creation failed"); - assert_eq!(basic_ff.symbol(), token_symbol); - assert_eq!(basic_ff.decimals(), 10); - assert_eq!(basic_ff.max_supply(), Felt::new(100)); + assert_eq!(basic_ff.symbol, token_symbol); + assert_eq!(basic_ff.decimals, 10); + assert_eq!(basic_ff.max_supply, Felt::new(100)); // invalid account: basic fungible faucet component is missing let invalid_faucet_account = AccountBuilder::new(mock_seed) diff --git a/crates/miden-lib/src/account/faucets/mod.rs b/crates/miden-lib/src/account/faucets/mod.rs index f09f361044..0b98d61103 100644 --- a/crates/miden-lib/src/account/faucets/mod.rs +++ b/crates/miden-lib/src/account/faucets/mod.rs @@ -1,11 +1,9 @@ use alloc::string::String; -use miden_objects::account::{Account, AccountType}; +use miden_objects::account::{Account, AccountStorage, AccountType, SlotName}; use miden_objects::{AccountError, Felt, TokenSymbolError}; use thiserror::Error; -use crate::transaction::memory::FAUCET_STORAGE_DATA_SLOT; - mod basic_fungible; mod network_fungible; @@ -19,7 +17,6 @@ pub use network_fungible::{NetworkFungibleFaucet, create_network_fungible_faucet /// account's reserved storage slot. pub trait FungibleFaucetExt { const ISSUANCE_ELEMENT_INDEX: usize; - const ISSUANCE_STORAGE_SLOT: u8; /// Returns the amount of tokens (in base units) issued from this fungible faucet. /// @@ -30,17 +27,19 @@ pub trait FungibleFaucetExt { impl FungibleFaucetExt for Account { const ISSUANCE_ELEMENT_INDEX: usize = 3; - const ISSUANCE_STORAGE_SLOT: u8 = FAUCET_STORAGE_DATA_SLOT; fn get_token_issuance(&self) -> Result { if self.account_type() != AccountType::FungibleFaucet { return Err(FungibleFaucetError::NotAFungibleFaucetAccount); } - let slot = self - .storage() - .get_item(Self::ISSUANCE_STORAGE_SLOT) - .map_err(|_| FungibleFaucetError::InvalidStorageOffset(Self::ISSUANCE_STORAGE_SLOT))?; + let slot = + self.storage().get_item(AccountStorage::faucet_metadata_slot()).map_err(|err| { + FungibleFaucetError::StorageLookupFailed { + slot_name: AccountStorage::faucet_metadata_slot().clone(), + source: err, + } + })?; Ok(slot[Self::ISSUANCE_ELEMENT_INDEX]) } } @@ -59,8 +58,11 @@ pub enum FungibleFaucetError { "account interface provided for faucet creation does not have basic fungible faucet component" )] NoAvailableInterface, - #[error("storage offset `{0}` is invalid")] - InvalidStorageOffset(u8), + #[error("failed to retrieve slot with name {slot_name}")] + StorageLookupFailed { + slot_name: SlotName, + source: AccountError, + }, #[error("invalid token symbol")] InvalidTokenSymbol(#[source] TokenSymbolError), #[error("unsupported authentication scheme: {0}")] diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 3407fd71fc..3a02e9b660 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -6,9 +6,11 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - StorageSlot, + NamedStorageSlot, + SlotName, }; use miden_objects::asset::TokenSymbol; +use miden_objects::utils::sync::LazyLock; use miden_objects::{Felt, FieldElement, Word}; use super::{BasicFungibleFaucet, FungibleFaucetError}; @@ -34,6 +36,11 @@ procedure_digest!( network_fungible_faucet_library ); +static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("miden::network_fungible_faucet::owner_config") + .expect("slot name should be valid") +}); + /// An [`AccountComponent`] implementing a network fungible faucet. /// /// It reexports the procedures from `miden::contracts::faucets::network_fungible`. When linking @@ -108,18 +115,24 @@ impl NetworkFungibleFaucet { storage: &AccountStorage, ) -> Result { for component in interface.components().iter() { - if let AccountComponentInterface::NetworkFungibleFaucet(offset) = component { + if let AccountComponentInterface::NetworkFungibleFaucet = component { // obtain metadata from storage using offset provided by NetworkFungibleFaucet // interface let faucet_metadata = storage - .get_item(*offset) - .map_err(|_| FungibleFaucetError::InvalidStorageOffset(*offset))?; + .get_item(NetworkFungibleFaucet::metadata_slot()) + .map_err(|err| FungibleFaucetError::StorageLookupFailed { + slot_name: NetworkFungibleFaucet::metadata_slot().clone(), + source: err, + })?; let [max_supply, decimals, token_symbol, _] = *faucet_metadata; // obtain owner account ID from the next storage slot let owner_account_id_word: Word = storage - .get_item(*offset + 1) - .map_err(|_| FungibleFaucetError::InvalidStorageOffset(*offset + 1))?; + .get_item(NetworkFungibleFaucet::owner_config_slot()) + .map_err(|err| FungibleFaucetError::StorageLookupFailed { + slot_name: NetworkFungibleFaucet::owner_config_slot().clone(), + source: err, + })?; // Convert Word back to AccountId // Storage format: [0, 0, suffix, prefix] @@ -149,6 +162,18 @@ impl NetworkFungibleFaucet { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- + /// Returns the [`SlotName`] where the [`NetworkFungibleFaucet`]'s metadata is stored. + pub fn metadata_slot() -> &'static SlotName { + // TODO(named_slots): Rename to metadata_slot. + BasicFungibleFaucet::metadata_slot_name() + } + + /// Returns the [`SlotName`] where the [`NetworkFungibleFaucet`]'s owner configuration is + /// stored. + pub fn owner_config_slot() -> &'static SlotName { + &OWNER_CONFIG_SLOT_NAME + } + /// Returns the symbol of the faucet. pub fn symbol(&self) -> TokenSymbol { self.faucet.symbol() @@ -200,12 +225,16 @@ impl From for AccountComponent { ] .into(); - // Second storage slot stores the owner account ID - let owner_slot = StorageSlot::Value(owner_account_id_word); + let metadata_slot = + NamedStorageSlot::with_value(NetworkFungibleFaucet::metadata_slot().clone(), metadata); + let owner_slot = NamedStorageSlot::with_value( + NetworkFungibleFaucet::owner_config_slot().clone(), + owner_account_id_word, + ); AccountComponent::new( network_fungible_faucet_library(), - vec![StorageSlot::Value(metadata), owner_slot] + vec![metadata_slot, owner_slot] ) .expect("network fungible faucet component should satisfy the requirements of a valid account component") .with_supported_type(AccountType::FungibleFaucet) diff --git a/crates/miden-lib/src/account/interface/component.rs b/crates/miden-lib/src/account/interface/component.rs index bff5b88b7a..06ac07a6c0 100644 --- a/crates/miden-lib/src/account/interface/component.rs +++ b/crates/miden-lib/src/account/interface/component.rs @@ -3,11 +3,19 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountId, AccountProcedureInfo, AccountStorage}; +use miden_objects::account::{AccountId, AccountProcedureInfo, AccountStorage, SlotName}; use miden_objects::note::PartialNote; use miden_objects::{Felt, FieldElement, Word}; use crate::AuthScheme; +use crate::account::auth::{ + AuthEcdsaK256Keccak, + AuthEcdsaK256KeccakAcl, + AuthEcdsaK256KeccakMultisig, + AuthRpoFalcon512, + AuthRpoFalcon512Acl, + AuthRpoFalcon512Multisig, +}; use crate::account::components::WellKnownComponent; use crate::account::interface::AccountInterfaceError; @@ -21,42 +29,28 @@ pub enum AccountComponentInterface { BasicWallet, /// Exposes procedures from the /// [`BasicFungibleFaucet`][crate::account::faucets::BasicFungibleFaucet] module. - /// - /// Internal value holds the storage slot index where faucet metadata is stored. This metadata - /// slot has a format of `[max_supply, faucet_decimals, token_symbol, 0]`. - BasicFungibleFaucet(u8), + BasicFungibleFaucet, /// Exposes procedures from the /// [`NetworkFungibleFaucet`][crate::account::faucets::NetworkFungibleFaucet] module. - /// - /// Internal value holds the storage slot index where faucet metadata is stored. This metadata - /// slot has a format of `[max_supply, faucet_decimals, token_symbol, 0]`. - NetworkFungibleFaucet(u8), + NetworkFungibleFaucet, + /// Exposes procedures from the + /// [`AuthEcdsaK256Keccak`][crate::account::auth::AuthEcdsaK256Keccak] module. + AuthEcdsaK256Keccak, + /// Exposes procedures from the + /// [`AuthEcdsaK256KeccakAcl`][crate::account::auth::AuthEcdsaK256KeccakAcl] module. + AuthEcdsaK256KeccakAcl, + /// Exposes procedures from the + /// [`AuthEcdsaK256KeccakMultisig`][crate::account::auth::AuthEcdsaK256KeccakMultisig] module. + AuthEcdsaK256KeccakMultisig, /// Exposes procedures from the /// [`AuthRpoFalcon512`][crate::account::auth::AuthRpoFalcon512] module. - /// - /// Internal value holds the storage slot index where the public key for the EcdsaK256Keccak - /// authentication scheme is stored. - AuthEcdsaK256Keccak(u8), - /// Internal value holds the storage slot index where the public key for the EcdsaK256Keccak - /// authentication scheme is stored. - AuthEcdsaK256KeccakAcl(u8), - /// Internal value holds the storage slot index where the multisig for EcdsaK256Keccak - /// configuration is stored. - AuthEcdsaK256KeccakMultisig(u8), - /// - /// Internal value holds the storage slot index where the public key for the RpoFalcon512 - /// authentication scheme is stored. - AuthRpoFalcon512(u8), + AuthRpoFalcon512, /// Exposes procedures from the /// [`AuthRpoFalcon512Acl`][crate::account::auth::AuthRpoFalcon512Acl] module. - /// - /// Internal value holds the storage slot index where the public key for the RpoFalcon512 - /// authentication scheme is stored. - AuthRpoFalcon512Acl(u8), - /// Exposes procedures from the multisig RpoFalcon512 authentication module. - /// - /// Internal value holds the storage slot index where the multisig configuration is stored. - AuthRpoFalcon512Multisig(u8), + AuthRpoFalcon512Acl, + /// Exposes procedures from the + /// [`AuthRpoFalcon512Multisig`][crate::account::auth::AuthRpoFalcon512Multisig] module. + AuthRpoFalcon512Multisig, /// Exposes procedures from the [`NoAuth`][crate::account::auth::NoAuth] module. /// /// This authentication scheme provides no cryptographic authentication and only increments @@ -78,22 +72,20 @@ impl AccountComponentInterface { pub fn name(&self) -> String { match self { AccountComponentInterface::BasicWallet => "Basic Wallet".to_string(), - AccountComponentInterface::BasicFungibleFaucet(_) => { - "Basic Fungible Faucet".to_string() - }, - AccountComponentInterface::NetworkFungibleFaucet(_) => { + AccountComponentInterface::BasicFungibleFaucet => "Basic Fungible Faucet".to_string(), + AccountComponentInterface::NetworkFungibleFaucet => { "Network Fungible Faucet".to_string() }, - AccountComponentInterface::AuthEcdsaK256Keccak(_) => "ECDSA K256 Keccak".to_string(), - AccountComponentInterface::AuthEcdsaK256KeccakAcl(_) => { + AccountComponentInterface::AuthEcdsaK256Keccak => "ECDSA K256 Keccak".to_string(), + AccountComponentInterface::AuthEcdsaK256KeccakAcl => { "ECDSA K256 Keccak ACL".to_string() }, - AccountComponentInterface::AuthEcdsaK256KeccakMultisig(_) => { + AccountComponentInterface::AuthEcdsaK256KeccakMultisig => { "ECDSA K256 Keccak Multisig".to_string() }, - AccountComponentInterface::AuthRpoFalcon512(_) => "RPO Falcon512".to_string(), - AccountComponentInterface::AuthRpoFalcon512Acl(_) => "RPO Falcon512 ACL".to_string(), - AccountComponentInterface::AuthRpoFalcon512Multisig(_) => { + AccountComponentInterface::AuthRpoFalcon512 => "RPO Falcon512".to_string(), + AccountComponentInterface::AuthRpoFalcon512Acl => "RPO Falcon512 ACL".to_string(), + AccountComponentInterface::AuthRpoFalcon512Multisig => { "RPO Falcon512 Multisig".to_string() }, @@ -115,12 +107,12 @@ impl AccountComponentInterface { pub fn is_auth_component(&self) -> bool { matches!( self, - AccountComponentInterface::AuthEcdsaK256Keccak(_) - | AccountComponentInterface::AuthEcdsaK256KeccakAcl(_) - | AccountComponentInterface::AuthEcdsaK256KeccakMultisig(_) - | AccountComponentInterface::AuthRpoFalcon512(_) - | AccountComponentInterface::AuthRpoFalcon512Acl(_) - | AccountComponentInterface::AuthRpoFalcon512Multisig(_) + AccountComponentInterface::AuthEcdsaK256Keccak + | AccountComponentInterface::AuthEcdsaK256KeccakAcl + | AccountComponentInterface::AuthEcdsaK256KeccakMultisig + | AccountComponentInterface::AuthRpoFalcon512 + | AccountComponentInterface::AuthRpoFalcon512Acl + | AccountComponentInterface::AuthRpoFalcon512Multisig | AccountComponentInterface::AuthNoAuth ) } @@ -128,31 +120,55 @@ impl AccountComponentInterface { /// Returns the authentication schemes associated with this component interface. pub fn get_auth_schemes(&self, storage: &AccountStorage) -> Vec { match self { - AccountComponentInterface::AuthEcdsaK256Keccak(storage_index) - | AccountComponentInterface::AuthEcdsaK256KeccakAcl(storage_index) => { + AccountComponentInterface::AuthEcdsaK256Keccak => { + vec![AuthScheme::EcdsaK256Keccak { + pub_key: PublicKeyCommitment::from( + storage + .get_item(AuthEcdsaK256Keccak::public_key_slot()) + .expect("invalid storage index of the public key"), + ), + }] + }, + AccountComponentInterface::AuthEcdsaK256KeccakAcl => { vec![AuthScheme::EcdsaK256Keccak { pub_key: PublicKeyCommitment::from( storage - .get_item(*storage_index) + .get_item(AuthEcdsaK256KeccakAcl::public_key_slot()) .expect("invalid storage index of the public key"), ), }] }, - AccountComponentInterface::AuthEcdsaK256KeccakMultisig(storage_index) => { - vec![extract_multisig_auth_scheme(storage, *storage_index)] + AccountComponentInterface::AuthEcdsaK256KeccakMultisig => { + vec![extract_multisig_auth_scheme( + storage, + AuthEcdsaK256KeccakMultisig::threshold_config_slot(), + AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(), + )] }, - AccountComponentInterface::AuthRpoFalcon512(storage_index) - | AccountComponentInterface::AuthRpoFalcon512Acl(storage_index) => { + AccountComponentInterface::AuthRpoFalcon512 => { vec![AuthScheme::RpoFalcon512 { pub_key: PublicKeyCommitment::from( storage - .get_item(*storage_index) - .expect("invalid storage index of the public key"), + .get_item(AuthRpoFalcon512::public_key_slot()) + .expect("invalid slot name of the AuthRpoFalcon512 public key"), ), }] }, - AccountComponentInterface::AuthRpoFalcon512Multisig(storage_index) => { - vec![extract_multisig_auth_scheme(storage, *storage_index)] + AccountComponentInterface::AuthRpoFalcon512Acl => { + vec![AuthScheme::RpoFalcon512 { + pub_key: PublicKeyCommitment::from( + storage + .get_item(AuthRpoFalcon512Acl::public_key_slot()) + .expect("invalid slot name of the AuthRpoFalcon512Acl public key"), + ), + }] + }, + AccountComponentInterface::AuthRpoFalcon512Multisig => { + vec![extract_multisig_auth_scheme( + storage, + AuthRpoFalcon512Multisig::threshold_config_slot(), + AuthRpoFalcon512Multisig::approver_public_keys_slot(), + )] }, AccountComponentInterface::AuthNoAuth => vec![AuthScheme::NoAuth], _ => vec![], // Non-auth components return empty vector @@ -268,7 +284,7 @@ impl AccountComponentInterface { // stack => [tag, aux, note_type, execution_hint, RECIPIENT] match self { - AccountComponentInterface::BasicFungibleFaucet(_) => { + AccountComponentInterface::BasicFungibleFaucet => { if partial_note.assets().num_assets() != 1 { return Err(AccountInterfaceError::FaucetNoteWithoutAsset); } @@ -322,23 +338,20 @@ impl AccountComponentInterface { // ================================================================================================ /// Extracts authentication scheme from a multisig component. -fn extract_multisig_auth_scheme(storage: &AccountStorage, storage_index: u8) -> AuthScheme { +fn extract_multisig_auth_scheme( + storage: &AccountStorage, + config_slot: &SlotName, + approver_public_keys_slot: &SlotName, +) -> AuthScheme { // Read the multisig configuration from the config slot // Format: [threshold, num_approvers, 0, 0] let config = storage - .get_item(storage_index) - .expect("invalid storage index of the multisig configuration"); + .get_item(config_slot) + .expect("invalid slot name of the multisig configuration"); let threshold = config[0].as_int() as u32; let num_approvers = config[1].as_int() as u8; - // The multisig component has a fixed storage layout: - // - Slot 0: [threshold, num_approvers, 0, 0] - // - Slot 1: Map with public keys - // - Slot 2: Map with executed transactions - // The public keys are always stored in slot 1, regardless of storage_index - let pub_keys_map_slot = storage_index + 1; - let mut pub_keys = Vec::new(); // Read each public key from the map @@ -346,7 +359,7 @@ fn extract_multisig_auth_scheme(storage: &AccountStorage, storage_index: u8) -> // The multisig component stores keys using pattern [index, 0, 0, 0] let map_key = [Felt::new(key_index as u64), Felt::ZERO, Felt::ZERO, Felt::ZERO]; - match storage.get_map_item(pub_keys_map_slot, map_key.into()) { + match storage.get_map_item(approver_public_keys_slot, map_key.into()) { Ok(pub_key) => { pub_keys.push(PublicKeyCommitment::from(pub_key)); }, @@ -356,7 +369,7 @@ fn extract_multisig_auth_scheme(storage: &AccountStorage, storage_index: u8) -> "Failed to read public key {} from multisig configuration at storage slot {}. \ Expected key pattern [index, 0, 0, 0]. \ This indicates corrupted multisig storage or incorrect storage layout.", - key_index, pub_keys_map_slot + key_index, approver_public_keys_slot ); }, } diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-lib/src/account/interface/mod.rs index 48193fcef9..f60e8881d4 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-lib/src/account/interface/mod.rs @@ -140,37 +140,37 @@ impl AccountInterface { component_proc_digests .extend(basic_wallet_library().mast_forest().procedure_digests()); }, - AccountComponentInterface::BasicFungibleFaucet(_) => { + AccountComponentInterface::BasicFungibleFaucet => { component_proc_digests .extend(basic_fungible_faucet_library().mast_forest().procedure_digests()); }, - AccountComponentInterface::NetworkFungibleFaucet(_) => { + AccountComponentInterface::NetworkFungibleFaucet => { component_proc_digests.extend( network_fungible_faucet_library().mast_forest().procedure_digests(), ); }, - AccountComponentInterface::AuthEcdsaK256Keccak(_) => { + AccountComponentInterface::AuthEcdsaK256Keccak => { component_proc_digests .extend(ecdsa_k256_keccak_library().mast_forest().procedure_digests()); }, - AccountComponentInterface::AuthEcdsaK256KeccakAcl(_) => { + AccountComponentInterface::AuthEcdsaK256KeccakAcl => { component_proc_digests .extend(ecdsa_k256_keccak_acl_library().mast_forest().procedure_digests()); }, - AccountComponentInterface::AuthEcdsaK256KeccakMultisig(_) => { + AccountComponentInterface::AuthEcdsaK256KeccakMultisig => { component_proc_digests.extend( ecdsa_k256_keccak_multisig_library().mast_forest().procedure_digests(), ); }, - AccountComponentInterface::AuthRpoFalcon512(_) => { + AccountComponentInterface::AuthRpoFalcon512 => { component_proc_digests .extend(rpo_falcon_512_library().mast_forest().procedure_digests()); }, - AccountComponentInterface::AuthRpoFalcon512Acl(_) => { + AccountComponentInterface::AuthRpoFalcon512Acl => { component_proc_digests .extend(rpo_falcon_512_acl_library().mast_forest().procedure_digests()); }, - AccountComponentInterface::AuthRpoFalcon512Multisig(_) => { + AccountComponentInterface::AuthRpoFalcon512Multisig => { component_proc_digests.extend( rpo_falcon_512_multisig_library().mast_forest().procedure_digests(), ); @@ -271,12 +271,12 @@ impl AccountInterface { output_notes: &[PartialNote], ) -> Result { if let Some(basic_fungible_faucet) = self.components().iter().find(|component_interface| { - matches!(component_interface, AccountComponentInterface::BasicFungibleFaucet(_)) + matches!(component_interface, AccountComponentInterface::BasicFungibleFaucet) }) { basic_fungible_faucet.send_note_body(*self.id(), output_notes) } else if let Some(_network_fungible_faucet) = self.components().iter().find(|component_interface| { - matches!(component_interface, AccountComponentInterface::NetworkFungibleFaucet(_)) + matches!(component_interface, AccountComponentInterface::NetworkFungibleFaucet) }) { // Network fungible faucet doesn't support send_note_body, because minting diff --git a/crates/miden-lib/src/account/interface/test.rs b/crates/miden-lib/src/account/interface/test.rs index 1f89b366d4..c9d808fde5 100644 --- a/crates/miden-lib/src/account/interface/test.rs +++ b/crates/miden-lib/src/account/interface/test.rs @@ -4,7 +4,7 @@ use alloc::vec::Vec; use assert_matches::assert_matches; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountBuilder, AccountComponent, AccountType, StorageSlot}; +use miden_objects::account::{AccountBuilder, AccountComponent, AccountType, NamedStorageSlot}; use miden_objects::assembly::diagnostics::NamedSource; use miden_objects::assembly::{Assembler, DefaultSourceManager}; use miden_objects::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; @@ -676,7 +676,7 @@ trait AccountComponentExt { fn compile_with_path( source_code: impl ToString, assembler: Assembler, - storage_slots: Vec, + storage_slots: Vec, library_path: impl AsRef, ) -> Result; } @@ -697,7 +697,7 @@ impl AccountComponentExt for AccountComponent { fn compile_with_path( source_code: impl ToString, assembler: Assembler, - storage_slots: Vec, + storage_slots: Vec, library_path: impl AsRef, ) -> Result { let source = NamedSource::new(library_path, source_code.to_string()); @@ -741,7 +741,7 @@ fn test_get_auth_scheme_ecdsa_k256_keccak() { let ecdsa_k256_keccak_component = wallet_account_interface .components() .iter() - .find(|component| matches!(component, AccountComponentInterface::AuthEcdsaK256Keccak(_))) + .find(|component| matches!(component, AccountComponentInterface::AuthEcdsaK256Keccak)) .expect("should have EcdsaK256Keccak component"); // Test get_auth_schemes method @@ -771,7 +771,7 @@ fn test_get_auth_scheme_rpo_falcon512() { let rpo_falcon_component = wallet_account_interface .components() .iter() - .find(|component| matches!(component, AccountComponentInterface::AuthRpoFalcon512(_))) + .find(|component| matches!(component, AccountComponentInterface::AuthRpoFalcon512)) .expect("should have RpoFalcon512 component"); // Test get_auth_schemes method diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index 19809b1fc4..44443d4fd4 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -24,8 +24,6 @@ pub const ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO: MasmError = pub const ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO: MasmError = MasmError::from_static_str("most significant bit of the account ID suffix must be zero"); /// Error Message: "unknown account storage mode in account ID" pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE: MasmError = MasmError::from_static_str("unknown account storage mode in account ID"); -/// Error Message: "storage slot with the provided name does not exist" -pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_SLOT_NAME: MasmError = MasmError::from_static_str("storage slot with the provided name does not exist"); /// Error Message: "unknown version in account ID" pub const ERR_ACCOUNT_ID_UNKNOWN_VERSION: MasmError = MasmError::from_static_str("unknown version in account ID"); /// Error Message: "storage size can only be zero if storage offset is also zero" @@ -64,6 +62,8 @@ pub const ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::f pub const ERR_ACCOUNT_TOO_MANY_PROCEDURES: MasmError = MasmError::from_static_str("number of account procedures exceeds the maximum limit of 256"); /// Error Message: "number of account storage slots exceeds the maximum limit of 255" pub const ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS: MasmError = MasmError::from_static_str("number of account storage slots exceeds the maximum limit of 255"); +/// Error Message: "storage slot with the provided name does not exist" +pub const ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME: MasmError = MasmError::from_static_str("storage slot with the provided name does not exist"); /// Error Message: "executed transaction neither changed the account state, nor consumed any notes" pub const ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY: MasmError = MasmError::from_static_str("executed transaction neither changed the account state, nor consumed any notes"); diff --git a/crates/miden-lib/src/testing/account_component/mock_account_component.rs b/crates/miden-lib/src/testing/account_component/mock_account_component.rs index 91e51c6166..41ed676605 100644 --- a/crates/miden-lib/src/testing/account_component/mock_account_component.rs +++ b/crates/miden-lib/src/testing/account_component/mock_account_component.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use miden_objects::account::{AccountCode, AccountComponent, AccountStorage, StorageSlot}; +use miden_objects::account::{AccountCode, AccountComponent, AccountStorage, NamedStorageSlot}; use crate::testing::mock_account_code::MockAccountCodeExt; @@ -18,7 +18,7 @@ use crate::testing::mock_account_code::MockAccountCodeExt; /// /// [account_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_account_library pub struct MockAccountComponent { - storage_slots: Vec, + storage_slots: Vec, } impl MockAccountComponent { @@ -35,14 +35,14 @@ impl MockAccountComponent { /// # Panics /// /// Panics if the number of slots exceeds [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. - pub fn with_slots(storage_slots: Vec) -> Self { + pub fn with_slots(storage_slots: Vec) -> Self { Self::new(storage_slots) } // HELPERS // -------------------------------------------------------------------------------------------- - fn new(storage_slots: Vec) -> Self { + fn new(storage_slots: Vec) -> Self { debug_assert!( storage_slots.len() <= AccountStorage::MAX_NUM_STORAGE_SLOTS, "too many storage slots passed to MockAccountComponent" diff --git a/crates/miden-lib/src/testing/mock_account.rs b/crates/miden-lib/src/testing/mock_account.rs index 35fd96e137..e9a3dc83f4 100644 --- a/crates/miden-lib/src/testing/mock_account.rs +++ b/crates/miden-lib/src/testing/mock_account.rs @@ -5,13 +5,12 @@ use miden_objects::account::{ AccountId, AccountStorage, AccountType, + NamedStorageSlot, StorageMap, - StorageSlot, }; use miden_objects::asset::{AssetVault, NonFungibleAsset}; use miden_objects::testing::constants::{self}; use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::testing::storage::FAUCET_STORAGE_DATA_SLOT; use miden_objects::{Felt, Word, ZERO}; use crate::testing::account_component::{MockAccountComponent, MockFaucetComponent}; @@ -50,7 +49,9 @@ pub trait MockAccountExt { let (_id, vault, mut storage, code, nonce, _seed) = account.into_parts(); let faucet_data_slot = Word::from([ZERO, ZERO, ZERO, initial_balance]); - storage.set_item(FAUCET_STORAGE_DATA_SLOT, faucet_data_slot).unwrap(); + storage + .set_item(AccountStorage::faucet_metadata_slot(), faucet_data_slot) + .unwrap(); Account::new_existing(account_id, vault, storage, code, nonce) } @@ -71,8 +72,11 @@ pub trait MockAccountExt { let asset = NonFungibleAsset::mock(&constants::NON_FUNGIBLE_ASSET_DATA_2); let non_fungible_storage_map = StorageMap::with_entries([(asset.vault_key().into(), asset.into())]).unwrap(); - let storage = - AccountStorage::new(vec![StorageSlot::Map(non_fungible_storage_map)]).unwrap(); + let storage = AccountStorage::new(vec![NamedStorageSlot::with_map( + AccountStorage::faucet_metadata_slot().clone(), + non_fungible_storage_map, + )]) + .unwrap(); Account::new_existing(account_id, vault, storage, code, nonce) } diff --git a/crates/miden-lib/src/testing/mock_account_code.rs b/crates/miden-lib/src/testing/mock_account_code.rs index 50c757ba81..42f8db2b11 100644 --- a/crates/miden-lib/src/testing/mock_account_code.rs +++ b/crates/miden-lib/src/testing/mock_account_code.rs @@ -8,15 +8,15 @@ use crate::transaction::TransactionKernel; const MOCK_FAUCET_CODE: &str = " use.miden::faucet - # Stack: [ASSET, pad(12)] - # Output: [ASSET, pad(12)] + #! Inputs: [ASSET, pad(12)] + #! Outputs: [ASSET, pad(12)] export.mint exec.faucet::mint # => [ASSET, pad(12)] end - # Stack: [ASSET, pad(12)] - # Output: [ASSET, pad(12)] + #! Inputs: [ASSET, pad(12)] + #! Outputs: [ASSET, pad(12)] export.burn exec.faucet::burn # => [ASSET, pad(12)] @@ -35,56 +35,58 @@ const MOCK_ACCOUNT_CODE: &str = " # is assumed that the operand stack at the beginning of their execution is pad'ed and # does not have any other valuable information. - # Stack: [index, VALUE_TO_SET, pad(11)] - # Output: [PREVIOUS_STORAGE_VALUE, pad(12)] + #! Inputs: [name_id_prefix, name_id_suffix, VALUE, pad(10)] + #! Outputs: [OLD_VALUE, pad(12)] export.set_item exec.native_account::set_item - # => [V, pad(12)] + # => [OLD_VALUE, pad(12)] end - # Stack: [index, pad(15)] - # Output: [VALUE, pad(12)] + #! Inputs: [name_id_prefix, name_id_suffix, pad(14)] + #! Outputs: [VALUE, pad(12)] export.get_item exec.active_account::get_item - # => [VALUE, pad(15)] + # => [VALUE, pad(14)] # truncate the stack - movup.8 drop movup.8 drop movup.8 drop + movup.4 drop movup.4 drop # => [VALUE, pad(12)] end - # Stack: [index, pad(15)] - # Output: [VALUE, pad(12)] + #! Inputs: [name_id_prefix, name_id_suffix, pad(14)] + #! Outputs: [VALUE, pad(12)] export.get_initial_item exec.active_account::get_initial_item - # => [VALUE, pad(15)] + # => [VALUE, pad(14)] # truncate the stack - movup.8 drop movup.8 drop movup.8 drop + movup.4 drop movup.4 drop # => [VALUE, pad(12)] end - # Stack: [index, KEY, VALUE, pad(7)] - # Output: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] + #! Inputs: [name_id_prefix, name_id_suffix, KEY, NEW_VALUE, pad(6)] + #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] export.set_map_item exec.native_account::set_map_item - # => [R', V, pad(8)] + # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] end - # Stack: [index, KEY, pad(11)] - # Output: [VALUE, pad(12)] + #! Inputs: [name_id_prefix, name_id_suffix, KEY, pad(10)] + #! Outputs: [VALUE, pad(12)] export.get_map_item exec.active_account::get_map_item + # => [VALUE, pad(12)] end - # Stack: [index, KEY, pad(11)] - # Output: [VALUE, pad(12)] + #! Inputs: [name_id_prefix, name_id_suffix, KEY, pad(10)] + #! Outputs: [INIT_VALUE, pad(12)] export.get_initial_map_item exec.active_account::get_initial_map_item + # => [INIT_VALUE, pad(12)] end - # Stack: [pad(16)] - # Output: [CODE_COMMITMENT, pad(12)] + #! Inputs: [pad(16)] + #! Outputs: [CODE_COMMITMENT, pad(12)] export.get_code_commitment exec.active_account::get_code_commitment # => [CODE_COMMITMENT, pad(16)] @@ -94,8 +96,8 @@ const MOCK_ACCOUNT_CODE: &str = " # => [CODE_COMMITMENT, pad(12)] end - # Stack: [pad(16)] - # Output: [CODE_COMMITMENT, pad(12)] + #! Inputs: [pad(16)] + #! Outputs: [CODE_COMMITMENT, pad(12)] export.compute_storage_commitment exec.active_account::compute_storage_commitment # => [STORAGE_COMMITMENT, pad(16)] @@ -104,22 +106,22 @@ const MOCK_ACCOUNT_CODE: &str = " # => [STORAGE_COMMITMENT, pad(12)] end - # Stack: [ASSET, pad(12)] - # Output: [ASSET', pad(12)] + #! Inputs: [ASSET, pad(12)] + #! Outputs: [ASSET', pad(12)] export.add_asset exec.native_account::add_asset # => [ASSET', pad(12)] end - # Stack: [ASSET, pad(12)] - # Output: [ASSET, pad(12)] + #! Inputs: [ASSET, pad(12)] + #! Outputs: [ASSET, pad(12)] export.remove_asset exec.native_account::remove_asset # => [ASSET, pad(12)] end - # Stack: [pad(16)] - # Output: [3, pad(12)] + #! Inputs: [pad(16)] + #! Outputs: [3, pad(12)] export.account_procedure_1 push.1.2 add @@ -127,8 +129,8 @@ const MOCK_ACCOUNT_CODE: &str = " swap drop end - # Stack: [pad(16)] - # Output: [1, pad(12)] + #! Inputs: [pad(16)] + #! Outputs: [1, pad(12)] export.account_procedure_2 push.2.1 sub @@ -140,6 +142,7 @@ const MOCK_ACCOUNT_CODE: &str = " static MOCK_FAUCET_LIBRARY: LazyLock = LazyLock::new(|| { let source = NamedSource::new("mock::faucet", MOCK_FAUCET_CODE); TransactionKernel::assembler() + .with_debug_mode(cfg!(feature = "with-debug-info")) .assemble_library([source]) .expect("mock faucet code should be valid") }); @@ -147,6 +150,7 @@ static MOCK_FAUCET_LIBRARY: LazyLock = LazyLock::new(|| { static MOCK_ACCOUNT_LIBRARY: LazyLock = LazyLock::new(|| { let source = NamedSource::new("mock::account", MOCK_ACCOUNT_CODE); TransactionKernel::assembler() + .with_debug_mode(cfg!(feature = "with-debug-info")) .assemble_library([source]) .expect("mock account code should be valid") }); diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-lib/src/transaction/kernel_procedures.rs index 92e8bea452..a1ef820640 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-lib/src/transaction/kernel_procedures.rs @@ -24,17 +24,17 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_compute_storage_commitment word!("0xa87008550383e1a88dde5d0adefc68ee3bf477aec07e4700f9101241aa1e868f"), // account_get_item - word!("0x045ba56f6e0f788fc4cd5b41a9ba9635bad9cf5e735d06c74372a208fd7ca1b1"), + word!("0xb1952306b30686d1e1736702bd04ddc980a8110192277d69648f6fdff843ea16"), // account_get_initial_item - word!("0x0662d55068a44158e8061bfdfe753fd57d53102e8861c71f36ec9481b14d7e8b"), + word!("0x77118a4356781e36469bbfa34f45654f13627eb9a9846ee888ebd0bdb58172ef"), // account_set_item - word!("0xe2ac03d4fb2ed9d0e756a774476c97e4b7197cdcf4a0d8de302a47344f7ce211"), + word!("0x7bc2b8365092e8b98aa5d8b880424b6230a218437bea5e9fb19cd21ee9743dcb"), // account_get_map_item - word!("0xd8449285da87d27e78eec95466cd9ac05506eab757c8561de21362cc1a2e9e1a"), + word!("0x8c9f227c3c6992a4374df70bec4970c3fdf152baa8db7f8483f3b5619c312683"), // account_get_initial_map_item - word!("0x6a8ca612d462a2ef1e3bf778de57827cbd66eb83dbd596a4746a716da15d8c51"), + word!("0x013cfa17c70872230771d0c174a929a801ee9507689fa169d085ca3d443bf439"), // account_set_map_item - word!("0x2d6684f7ad6b051b8c5d3476f01dfac55f4d27ff0b3f4d359238acb1bdfc400b"), + word!("0x7e82c23b18727f1ac451fb96fec3ec2ef1a93c9a7efb7e16f667c8c3d9dba478"), // account_get_initial_vault_root word!("0x46297d9ac95afd60c7ef1a065e024ad49aa4c019f6b3924191905449b244d4ec"), // account_get_vault_root @@ -50,7 +50,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_non_fungible_asset word!("0xfaad11de0c026551df15231790c2364cc598e891444bf826da01b524b1a8ca8f"), // account_compute_delta_commitment - word!("0x274e90e4c5fc54ed530c32aae966cbeebd4bb97e2ebd93f76f4cd943902eb851"), + word!("0xd7ced8e16079d9c775e7dfc6b9d4b7a946ccd973fcd0ccaa84095bf8e10561a2"), // account_get_num_procedures word!("0x53b5ec38b7841948762c258010e6e07ad93963bcaac2d83813f8edb6710dc720"), // account_get_procedure_root @@ -60,13 +60,13 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_procedure word!("0x667d5ce1b7a54c3b8965666ce90e59085c97775b82eba25dddfe218db5fe137d"), // faucet_mint_asset - word!("0xa0c038cba851e70f1d08141006fbffebe7283aa8f5af7b27dd5328c89309bde4"), + word!("0x5af68530690f79bc8daa623c388d300e640adb6c123e16751b47e8739f3032f6"), // faucet_burn_asset - word!("0x9c22302e6505e55b805d0a0ec1328653065e31980368a09f40e0528e3a2fdb54"), + word!("0xea1bfbe5a0228b5ab4fb8968d22fa5df471cce284a171bb99e7863e5a842c392"), // faucet_get_total_fungible_asset_issuance - word!("0xd361b7554245e8a414771e95ef53b427d53fb4990846d8c376c7e0f744d15b87"), + word!("0xedbc4fa4fba063dc750576c2ad7b00ed92f1c7bf744ed096b4bc7923f92ef682"), // faucet_is_non_fungible_asset_issued - word!("0x64aae9fa0550d5224b4bd765a42d7e1b9ec4e66dad636f11ba54b00ff03a2af0"), + word!("0x94f574852d517f8e547a0b298a6760d76f770dab3cafc46556935c094f272792"), // input_note_get_metadata word!("0x7ad3e94585e7a397ee27443c98b376ed8d4ba762122af6413fde9314c00a6219"), // input_note_get_assets_info diff --git a/crates/miden-lib/src/transaction/memory.rs b/crates/miden-lib/src/transaction/memory.rs index 4fbb83178b..f29e1a564d 100644 --- a/crates/miden-lib/src/transaction/memory.rs +++ b/crates/miden-lib/src/transaction/memory.rs @@ -47,6 +47,8 @@ pub type StorageSlot = u8; // | Storage slot info | 2_344 (586) | 4_383 (1095) | 255 slots max, 8 elements each | // | Initial slot info | 4_384 (1096) | 6_423 (1545) | Only present on the native account | // | Padding | 6_424 (1545) | 8_191 (2047) | | +// +// Storage slot info is laid out as [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE]. // Relative layout of the native account's delta. // @@ -60,16 +62,6 @@ pub type StorageSlot = u8; // | Non-Fungible Asset Delta Ptr | 4 (1) | 7 (1) | | // | Storage Map Delta Ptrs | 8 (2) | 263 (65) | Max 255 storage map deltas | -// RESERVED ACCOUNT STORAGE SLOTS -// ------------------------------------------------------------------------------------------------ - -/// The account storage slot at which faucet data is stored. -/// -/// - Fungible faucet: The faucet data consists of [0, 0, 0, total_issuance]. -/// - Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible -/// assets. -pub const FAUCET_STORAGE_DATA_SLOT: StorageSlot = 0; - // BOOKKEEPING // ------------------------------------------------------------------------------------------------ @@ -315,6 +307,18 @@ pub const ACCT_STORAGE_SLOTS_SECTION_OFFSET: MemoryAddress = 2344; /// The number of elements that each storage slot takes up in memory. pub const ACCT_STORAGE_SLOT_NUM_ELEMENTS: u8 = 8; +/// The offset of the slot type in the storage slot. +pub const ACCT_STORAGE_SLOT_TYPE_OFFSET: u8 = 1; + +/// The offset of the slot's name ID suffix in the storage slot. +pub const ACCT_STORAGE_SLOT_NAME_ID_SUFFIX_OFFSET: u8 = 2; + +/// The offset of the slot's name ID prefix in the storage slot. +pub const ACCT_STORAGE_SLOT_NAME_ID_PREFIX_OFFSET: u8 = 3; + +/// The offset of the slot value in the storage slot. +pub const ACCT_STORAGE_SLOT_VALUE_OFFSET: u8 = 4; + /// The memory address at which the account storage slots section begins in the native account. pub const NATIVE_ACCT_STORAGE_SLOTS_SECTION_PTR: MemoryAddress = NATIVE_ACCOUNT_DATA_PTR + ACCT_STORAGE_SLOTS_SECTION_OFFSET; diff --git a/crates/miden-objects/src/account/builder/mod.rs b/crates/miden-objects/src/account/builder/mod.rs index 6aa7665b6b..d93b564ffe 100644 --- a/crates/miden-objects/src/account/builder/mod.rs +++ b/crates/miden-objects/src/account/builder/mod.rs @@ -144,13 +144,13 @@ impl AccountBuilder { let mut components = vec![auth_component]; components.append(&mut self.components); - let (code, storage) = Account::initialize_from_components(self.account_type, &components) + let (code, storage) = Account::initialize_from_components(self.account_type, components) .map_err(|err| { - AccountError::BuildError( - "account components failed to build".into(), - Some(Box::new(err)), - ) - })?; + AccountError::BuildError( + "account components failed to build".into(), + Some(Box::new(err)), + ) + })?; Ok((vault, code, storage)) } @@ -291,7 +291,7 @@ mod tests { use miden_processor::MastNodeExt; use super::*; - use crate::account::StorageSlot; + use crate::account::{NamedStorageSlot, SlotName}; use crate::testing::noop_auth_component::NoopAuthComponent; const CUSTOM_CODE1: &str = " @@ -316,6 +316,16 @@ mod tests { .expect("code should be valid") }); + static CUSTOM_COMPONENT1_SLOT_NAME: LazyLock = LazyLock::new(|| { + SlotName::new("custom::component1::slot0").expect("slot name should be valid") + }); + static CUSTOM_COMPONENT2_SLOT_NAME0: LazyLock = LazyLock::new(|| { + SlotName::new("custom::component2::slot0").expect("slot name should be valid") + }); + static CUSTOM_COMPONENT2_SLOT_NAME1: LazyLock = LazyLock::new(|| { + SlotName::new("custom::component2::slot1").expect("slot name should be valid") + }); + struct CustomComponent1 { slot0: u64, } @@ -324,9 +334,12 @@ mod tests { let mut value = Word::empty(); value[0] = Felt::new(custom.slot0); - AccountComponent::new(CUSTOM_LIBRARY1.clone(), vec![StorageSlot::Value(value)]) - .expect("component should be valid") - .with_supports_all_types() + AccountComponent::new( + CUSTOM_LIBRARY1.clone(), + vec![NamedStorageSlot::with_value(CUSTOM_COMPONENT1_SLOT_NAME.clone(), value)], + ) + .expect("component should be valid") + .with_supports_all_types() } } @@ -343,7 +356,10 @@ mod tests { AccountComponent::new( CUSTOM_LIBRARY2.clone(), - vec![StorageSlot::Value(value0), StorageSlot::Value(value1)], + vec![ + NamedStorageSlot::with_value(CUSTOM_COMPONENT2_SLOT_NAME0.clone(), value0), + NamedStorageSlot::with_value(CUSTOM_COMPONENT2_SLOT_NAME1.clone(), value1), + ], ) .expect("component should be valid") .with_supports_all_types() @@ -407,15 +423,15 @@ mod tests { assert_eq!(bar_procedure_info.storage_size(), 2); assert_eq!( - account.storage().get_item(0).unwrap(), + account.storage().get_item(&CUSTOM_COMPONENT1_SLOT_NAME).unwrap(), [Felt::new(storage_slot0), Felt::new(0), Felt::new(0), Felt::new(0)].into() ); assert_eq!( - account.storage().get_item(1).unwrap(), + account.storage().get_item(&CUSTOM_COMPONENT2_SLOT_NAME0).unwrap(), [Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(storage_slot1)].into() ); assert_eq!( - account.storage().get_item(2).unwrap(), + account.storage().get_item(&CUSTOM_COMPONENT2_SLOT_NAME1).unwrap(), [Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(storage_slot2)].into() ); } diff --git a/crates/miden-objects/src/account/code/mod.rs b/crates/miden-objects/src/account/code/mod.rs index d72690d689..e8f471631c 100644 --- a/crates/miden-objects/src/account/code/mod.rs +++ b/crates/miden-objects/src/account/code/mod.rs @@ -450,12 +450,11 @@ mod tests { use assert_matches::assert_matches; use miden_assembly::Assembler; - use miden_core::Word; use super::{AccountCode, Deserializable, Serializable}; use crate::AccountError; use crate::account::code::build_procedure_commitment; - use crate::account::{AccountComponent, AccountType, StorageSlot}; + use crate::account::{AccountComponent, AccountType}; use crate::testing::account_code::CODE; use crate::testing::noop_auth_component::NoopAuthComponent; @@ -474,43 +473,6 @@ mod tests { assert_eq!(procedure_root, code.commitment()) } - #[test] - fn test_account_code_procedure_offset_out_of_bounds() { - let code1 = "export.foo add end"; - let library1 = Assembler::default().assemble_library([code1]).unwrap(); - let code2 = "export.bar sub end"; - let library2 = Assembler::default().assemble_library([code2]).unwrap(); - - let auth_component: AccountComponent = NoopAuthComponent.into(); - - let component1 = - AccountComponent::new(library1, vec![StorageSlot::Value(Word::empty()); 250]) - .unwrap() - .with_supports_all_types(); - let mut component2 = - AccountComponent::new(library2, vec![StorageSlot::Value(Word::empty()); 5]) - .unwrap() - .with_supports_all_types(); - - // This is fine as the offset+size for component 2 is <= 255. - AccountCode::from_components( - &[auth_component.clone(), component1.clone(), component2.clone()], - AccountType::RegularAccountUpdatableCode, - ) - .unwrap(); - - // Push one more slot so offset+size exceeds 255. - component2.storage_slots.push(StorageSlot::Value(Word::empty())); - - let err = AccountCode::from_components( - &[auth_component, component1, component2], - AccountType::RegularAccountUpdatableCode, - ) - .unwrap_err(); - - assert_matches!(err, AccountError::StorageOffsetPlusSizeOutOfBounds(256)) - } - #[test] fn test_account_code_only_auth_component() { let err = AccountCode::from_components( diff --git a/crates/miden-objects/src/account/component/mod.rs b/crates/miden-objects/src/account/component/mod.rs index 3c607cbaaf..563e8bd26a 100644 --- a/crates/miden-objects/src/account/component/mod.rs +++ b/crates/miden-objects/src/account/component/mod.rs @@ -1,49 +1,20 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_assembly::ast::QualifiedProcedureName; use miden_assembly::{Assembler, Library, Parse}; -use miden_core::utils::Deserializable; -use miden_mast_package::{MastArtifact, Package, SectionId}; -use miden_processor::MastForest; -mod template; -pub use template::*; +// TODO(named_slots): Refactor templates. +// mod template; +// pub use template::*; +use crate::account::{AccountType, NamedStorageSlot}; +use crate::assembly::QualifiedProcedureName; +use crate::{AccountError, MastForest, Word}; -use crate::account::{AccountType, StorageSlot}; -use crate::{AccountError, Word}; - -// IMPLEMENTATIONS +// ACCOUNT COMPONENT // ================================================================================================ -impl TryFrom<&Package> for AccountComponentMetadata { - type Error = AccountError; - - fn try_from(package: &Package) -> Result { - package - .sections - .iter() - .find_map(|section| { - (section.id == SectionId::ACCOUNT_COMPONENT_METADATA).then(|| { - AccountComponentMetadata::read_from_bytes(§ion.data).map_err(|err| { - AccountError::other_with_source( - "failed to deserialize account component metadata", - err, - ) - }) - }) - }) - .transpose()? - .ok_or_else(|| { - AccountError::other( - "package does not contain account component metadata section - packages without explicit metadata may be intended for other purposes (e.g., note scripts, transaction scripts)", - ) - }) - } -} - /// An [`AccountComponent`] defines a [`Library`] of code and the initial value and types of -/// the [`StorageSlot`]s it accesses. +/// the [`NamedStorageSlot`]s it accesses. /// /// One or more components can be used to built [`AccountCode`](crate::account::AccountCode) and /// [`AccountStorage`](crate::account::AccountStorage). @@ -60,7 +31,7 @@ impl TryFrom<&Package> for AccountComponentMetadata { #[derive(Debug, Clone, PartialEq, Eq)] pub struct AccountComponent { pub(super) library: Library, - pub(super) storage_slots: Vec, + pub(super) storage_slots: Vec, pub(super) supported_types: BTreeSet, } @@ -81,8 +52,8 @@ impl AccountComponent { /// or in their fallible constructors. /// /// Returns an error if: - /// - The number of given [`StorageSlot`]s exceeds 255. - pub fn new(code: Library, storage_slots: Vec) -> Result { + /// - The number of given [`NamedStorageSlot`]s exceeds 255. + pub fn new(code: Library, storage_slots: Vec) -> Result { // Check that we have less than 256 storage slots. u8::try_from(storage_slots.len()) .map_err(|_| AccountError::StorageTooManySlots(storage_slots.len() as u64))?; @@ -108,7 +79,7 @@ impl AccountComponent { pub fn compile( source_code: impl Parse, assembler: Assembler, - storage_slots: Vec, + storage_slots: Vec, ) -> Result { let library = assembler .assemble_library([source_code]) @@ -117,6 +88,7 @@ impl AccountComponent { Self::new(library, storage_slots) } + /* /// Creates an [`AccountComponent`] from a [`Package`] using [`InitStorageData`]. /// /// This method provides type safety by leveraging the component's metadata to validate @@ -188,6 +160,7 @@ impl AccountComponent { Ok(AccountComponent::new(library.clone(), storage_slots)? .with_supported_types(account_component_metadata.supported_types().clone())) } + */ // ACCESSORS // -------------------------------------------------------------------------------------------- @@ -208,8 +181,8 @@ impl AccountComponent { self.library.mast_forest().as_ref() } - /// Returns a slice of the underlying [`StorageSlot`]s of this component. - pub fn storage_slots(&self) -> &[StorageSlot] { + /// Returns a slice of the underlying [`NamedStorageSlot`]s of this component. + pub fn storage_slots(&self) -> &[NamedStorageSlot] { self.storage_slots.as_slice() } @@ -283,6 +256,8 @@ impl From for Library { } } +// TODO(named_slots): Reactivate tests once template is refactored. +/* #[cfg(test)] mod tests { use alloc::collections::BTreeSet; @@ -396,3 +371,4 @@ mod tests { assert!(error_msg.contains("package does not contain account component metadata")); } } +*/ diff --git a/crates/miden-objects/src/account/component/template/mod.rs b/crates/miden-objects/src/account/component/template/mod.rs index a2b046b4a1..a17eac36fb 100644 --- a/crates/miden-objects/src/account/component/template/mod.rs +++ b/crates/miden-objects/src/account/component/template/mod.rs @@ -4,10 +4,12 @@ use alloc::vec::Vec; use core::str::FromStr; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use miden_mast_package::{Package, SectionId}; use miden_processor::DeserializationError; use semver::Version; use super::AccountType; +use crate::AccountError; use crate::errors::AccountComponentTemplateError; mod storage; @@ -243,6 +245,32 @@ impl AccountComponentMetadata { } } +impl TryFrom<&Package> for AccountComponentMetadata { + type Error = AccountError; + + fn try_from(package: &Package) -> Result { + package + .sections + .iter() + .find_map(|section| { + (section.id == SectionId::ACCOUNT_COMPONENT_METADATA).then(|| { + AccountComponentMetadata::read_from_bytes(§ion.data).map_err(|err| { + AccountError::other_with_source( + "failed to deserialize account component metadata", + err, + ) + }) + }) + }) + .transpose()? + .ok_or_else(|| { + AccountError::other( + "package does not contain account component metadata section - packages without explicit metadata may be intended for other purposes (e.g., note scripts, transaction scripts)", + ) + }) + } +} + // SERIALIZATION // ================================================================================================ diff --git a/crates/miden-objects/src/account/component/template/storage/mod.rs b/crates/miden-objects/src/account/component/template/storage/mod.rs index f90a6ce976..4b4fb1d0e7 100644 --- a/crates/miden-objects/src/account/component/template/storage/mod.rs +++ b/crates/miden-objects/src/account/component/template/storage/mod.rs @@ -622,14 +622,14 @@ mod tests { .collect() ); - let storage_map = component.storage_slots.first().unwrap(); - match storage_map { + let named_map_slot = component.storage_slots.first().unwrap(); + match named_map_slot.storage_slot() { StorageSlot::Map(storage_map) => assert_eq!(storage_map.entries().count(), 3), _ => panic!("should be map"), } - let value_entry = component.storage_slots().get(2).unwrap(); - match value_entry { + let named_value_slot = component.storage_slots().get(2).unwrap(); + match named_value_slot.storage_slot() { StorageSlot::Value(v) => { assert_eq!(v, &EMPTY_WORD) }, diff --git a/crates/miden-objects/src/account/delta/mod.rs b/crates/miden-objects/src/account/delta/mod.rs index a51c58edd6..21cee5eedf 100644 --- a/crates/miden-objects/src/account/delta/mod.rs +++ b/crates/miden-objects/src/account/delta/mod.rs @@ -6,7 +6,7 @@ use crate::account::{ AccountCode, AccountId, AccountStorage, - StorageSlot, + NamedStorageSlot, StorageSlotType, }; use crate::asset::AssetVault; @@ -205,15 +205,19 @@ impl AccountDelta { /// assets since `faucet_id_prefix` is at the same position in the layout for both assets, /// and, by design, it is never the same for fungible and non-fungible assets. /// - Append `[hash0, hash1, hash2, faucet_id_prefix]`, i.e. the non-fungible asset. - /// - Storage Slots - for each slot **whose value has changed**, depending on the slot type: + /// - Storage Slots are sorted by slot name ID and are iterated in this order. For each slot + /// **whose value has changed**, depending on the slot type: /// - Value Slot - /// - Append `[[domain = 2, slot_idx, 0, 0], NEW_VALUE]` where NEW_VALUE is the new value of - /// the slot and slot_idx is the index of the slot. + /// - Append `[[domain = 2, 0, name_id_suffix, name_id_prefix], NEW_VALUE]` where + /// `NEW_VALUE` is the new value of the slot and `name_id_{suffix, prefix}` are the slot + /// name identifiers of the slot. /// - Map Slot /// - For each key-value pair, sorted by key, whose new value is different from the previous /// value in the map: /// - Append `[KEY, NEW_VALUE]`. - /// - Append `[[domain = 3, slot_idx, num_changed_entries, 0], 0, 0, 0, 0]`. + /// - Append `[[domain = 3, num_changed_entries, name_id_suffix, name_id_prefix], 0, 0, 0, + /// 0]`, where `name_id_{suffix, prefix}` are the slot name identifiers and + /// `num_changed_entries` is the number of changed key-value pairs in the map. /// - For partial state deltas, the map header must only be included if /// `num_changed_entries` is not zero. /// - For full state deltas, the map header must always be included. @@ -265,7 +269,7 @@ impl AccountDelta { /// [ /// ID_AND_NONCE, EMPTY_WORD, /// [/* no fungible asset delta */], - /// [[domain = 1, was_added = 1, 0, 0], NON_FUNGIBLE_ASSET], + /// [[domain = 1, was_added = 0, 0, 0], NON_FUNGIBLE_ASSET], /// [/* no storage delta */] /// ] /// ``` @@ -275,7 +279,7 @@ impl AccountDelta { /// ID_AND_NONCE, EMPTY_WORD, /// [/* no fungible asset delta */], /// [/* no non-fungible asset delta */], - /// [[domain = 2, slot_idx = 1, 0, 0], NEW_VALUE] + /// [[domain = 2, 0, name_id_suffix = 0, name_id_prefix = 0], NEW_VALUE] /// ] /// ``` /// @@ -293,8 +297,8 @@ impl AccountDelta { /// ID_AND_NONCE, EMPTY_WORD, /// [/* no fungible asset delta */], /// [/* no non-fungible asset delta */], - /// [domain = 3, slot_idx = 0, num_changed_entries = 0, 0, 0, 0, 0, 0] - /// [domain = 3, slot_idx = 1, num_changed_entries = 0, 0, 0, 0, 0, 0] + /// [domain = 3, num_changed_entries = 0, name_id_suffix = 20, name_id_prefix = 21, 0, 0, 0, 0] + /// [domain = 3, num_changed_entries = 0, name_id_suffix = 42, name_id_prefix = 43, 0, 0, 0, 0] /// ] /// ``` /// @@ -304,7 +308,7 @@ impl AccountDelta { /// [/* no fungible asset delta */], /// [/* no non-fungible asset delta */], /// [KEY0, VALUE0], - /// [domain = 3, slot_idx = 1, num_changed_entries = 1, 0, 0, 0, 0, 0] + /// [domain = 3, num_changed_entries = 1, name_id_suffix = 42, name_id_prefix = 43, 0, 0, 0, 0] /// ] /// ``` /// @@ -356,11 +360,10 @@ impl TryFrom<&AccountDelta> for Account { // this to create an empty account and use `Account::apply_delta` instead. // For now, we need to create the initial storage of the account with the same slot types. let mut empty_storage_slots = Vec::new(); - for slot_idx in 0..u8::MAX { - let slot = match delta.storage().slot_type(slot_idx) { - Some(StorageSlotType::Value) => StorageSlot::empty_value(), - Some(StorageSlotType::Map) => StorageSlot::empty_map(), - None => break, + for (slot_name, slot_type) in delta.storage().slots() { + let slot = match slot_type { + StorageSlotType::Value => NamedStorageSlot::with_empty_value(slot_name.clone()), + StorageSlotType::Map => NamedStorageSlot::with_empty_map(slot_name.clone()), }; empty_storage_slots.push(slot); } @@ -596,6 +599,7 @@ mod tests { AccountStorage, AccountStorageMode, AccountType, + SlotName, StorageMapDelta, }; use crate::asset::{ @@ -623,7 +627,7 @@ mod tests { AccountDelta::new(account_id, storage_delta.clone(), vault_delta.clone(), ONE).unwrap(); // non-empty delta - let storage_delta = AccountStorageDelta::from_iters([1], [], []); + let storage_delta = AccountStorageDelta::from_iters([SlotName::mock(1)], [], []); assert_matches!( AccountDelta::new(account_id, storage_delta.clone(), vault_delta.clone(), ZERO) @@ -671,10 +675,13 @@ mod tests { assert_eq!(account_delta.to_bytes().len(), account_delta.get_size_hint()); let storage_delta = AccountStorageDelta::from_iters( - [1], - [(2, Word::from([1, 1, 1, 1u32])), (3, Word::from([1, 1, 0, 1u32]))], + [SlotName::mock(1)], + [ + (SlotName::mock(2), Word::from([1, 1, 1, 1u32])), + (SlotName::mock(3), Word::from([1, 1, 0, 1u32])), + ], [( - 4, + SlotName::mock(4), StorageMapDelta::from_iters( [Word::from([1, 1, 1, 0u32]), Word::from([0, 1, 1, 1u32])], [(Word::from([1, 1, 1, 1u32]), Word::from([1, 1, 1, 1u32]))], diff --git a/crates/miden-objects/src/account/delta/storage.rs b/crates/miden-objects/src/account/delta/storage.rs index baeda54931..f9c3ddb1b7 100644 --- a/crates/miden-objects/src/account/delta/storage.rs +++ b/crates/miden-objects/src/account/delta/storage.rs @@ -12,7 +12,7 @@ use super::{ Serializable, Word, }; -use crate::account::{StorageMap, StorageSlotType}; +use crate::account::{SlotName, StorageMap, StorageSlotType}; use crate::{EMPTY_WORD, Felt, LexicographicWord, ZERO}; // ACCOUNT STORAGE DELTA @@ -28,9 +28,9 @@ use crate::{EMPTY_WORD, Felt, LexicographicWord, ZERO}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct AccountStorageDelta { /// The updates to the value slots of the account. - values: BTreeMap, + values: BTreeMap, /// The updates to the map slots of the account. - maps: BTreeMap, + maps: BTreeMap, } impl AccountStorageDelta { @@ -50,8 +50,8 @@ impl AccountStorageDelta { /// - Any of the updated slot is referenced from both maps, which means a slot is treated as /// both a value and a map slot. pub fn from_parts( - values: BTreeMap, - maps: BTreeMap, + values: BTreeMap, + maps: BTreeMap, ) -> Result { let delta = Self { values, maps }; delta.validate()?; @@ -59,24 +59,21 @@ impl AccountStorageDelta { Ok(delta) } - /// Returns the slot type of the provided slot index or `None` if no such slot exists. - pub(crate) fn slot_type(&self, slot_index: u8) -> Option { - if self.values().contains_key(&slot_index) { - Some(StorageSlotType::Value) - } else if self.maps().contains_key(&slot_index) { - Some(StorageSlotType::Map) - } else { - None - } + /// Returns an iterator over the slot names and types tracked by this delta. + pub(crate) fn slots(&self) -> impl Iterator { + self.values() + .keys() + .map(|slot_name| (slot_name, StorageSlotType::Value)) + .chain(self.maps.keys().map(|slot_name| (slot_name, StorageSlotType::Map))) } /// Returns a reference to the updated values in this storage delta. - pub fn values(&self) -> &BTreeMap { + pub fn values(&self) -> &BTreeMap { &self.values } /// Returns a reference to the updated maps in this storage delta. - pub fn maps(&self) -> &BTreeMap { + pub fn maps(&self) -> &BTreeMap { &self.maps } @@ -85,21 +82,27 @@ impl AccountStorageDelta { self.values.is_empty() && self.maps.is_empty() } - /// Tracks a slot change - pub fn set_item(&mut self, slot_index: u8, new_slot_value: Word) { - self.values.insert(slot_index, new_slot_value); + /// Tracks a slot change. + /// + /// This does not (and cannot) validate that the slot name _exists_ or that it points to a + /// _value_ slot in the corresponding account. + pub fn set_item(&mut self, slot_name: SlotName, new_slot_value: Word) { + self.values.insert(slot_name, new_slot_value); } - /// Tracks a map item change - pub fn set_map_item(&mut self, slot_index: u8, key: Word, new_value: Word) { - self.maps.entry(slot_index).or_default().insert(key, new_value); + /// Tracks a map item change. + /// + /// This does not (and cannot) validate that the slot name _exists_ or that it points to a + /// _map_ slot in the corresponding account. + pub fn set_map_item(&mut self, slot_name: SlotName, key: Word, new_value: Word) { + self.maps.entry(slot_name).or_default().insert(key, new_value); } /// Inserts an empty storage map delta for the provided slot index. /// /// This is useful for full state deltas to represent an empty map in the delta. - pub fn insert_empty_map_delta(&mut self, slot_index: u8) { - self.maps.entry(slot_index).or_default(); + pub fn insert_empty_map_delta(&mut self, slot_name: SlotName) { + self.maps.entry(slot_name).or_default(); } /// Merges another delta into this one, overwriting any existing values. @@ -129,7 +132,7 @@ impl AccountStorageDelta { fn validate(&self) -> Result<(), AccountDeltaError> { for slot in self.maps.keys() { if self.values.contains_key(slot) { - return Err(AccountDeltaError::StorageSlotUsedAsDifferentTypes(*slot)); + return Err(AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot.clone())); } } @@ -137,12 +140,12 @@ impl AccountStorageDelta { } /// Returns an iterator of all the cleared storage slots. - fn cleared_slots(&self) -> impl Iterator + '_ { - self.values.iter().filter(|&(_, value)| value.is_empty()).map(|(slot, _)| *slot) + fn cleared_values(&self) -> impl Iterator { + self.values.iter().filter(|&(_, value)| value.is_empty()).map(|(slot, _)| slot) } /// Returns an iterator of all the updated storage slots. - fn updated_slots(&self) -> impl Iterator + '_ { + fn updated_values(&self) -> impl Iterator { self.values.iter().filter(|&(_, value)| !value.is_empty()) } @@ -152,47 +155,59 @@ impl AccountStorageDelta { const DOMAIN_VALUE: Felt = Felt::new(2); const DOMAIN_MAP: Felt = Felt::new(3); - let highest_value_slot_idx = self.values.last_key_value().map(|(slot_idx, _)| slot_idx); - let highest_map_slot_idx = self.maps.last_key_value().map(|(slot_idx, _)| slot_idx); - let highest_slot_idx = - highest_value_slot_idx.max(highest_map_slot_idx).copied().unwrap_or(0); - - for slot_idx in 0..=highest_slot_idx { - let slot_idx_felt = Felt::from(slot_idx); + // Merge slots into a single map to sort them by slot name. + let mut sorted_slots: BTreeMap<&SlotName, StorageSlotDelta> = self + .values() + .iter() + .map(|(name, value)| (name, StorageSlotDelta::Value(*value))) + .collect(); + sorted_slots.extend( + self.maps() + .iter() + .map(|(name, map_delta)| (name, StorageSlotDelta::Map(map_delta))), + ); - // The storage delta ensures that the value slots and map slots do not have overlapping - // slot indices, so at most one of them will return `Some` for a given slot index. - match self.values.get(&slot_idx) { - Some(new_value) => { - elements.extend_from_slice(&[DOMAIN_VALUE, slot_idx_felt, ZERO, ZERO]); + // The storage delta ensures that the value slots and map slots do not have overlapping + // slot names, so the number of entries should be identical after merging. + debug_assert_eq!(sorted_slots.len(), self.values.len() + self.maps.len()); + + for (slot_name, slot_delta) in sorted_slots { + let name_id = slot_name.compute_id(); + + match slot_delta { + StorageSlotDelta::Value(new_value) => { + elements.extend_from_slice(&[ + DOMAIN_VALUE, + ZERO, + name_id.suffix(), + name_id.prefix(), + ]); elements.extend_from_slice(new_value.as_elements()); }, - None => { - if let Some(map_delta) = self.maps().get(&slot_idx) { - for (key, value) in map_delta.entries() { - elements.extend_from_slice(key.inner().as_elements()); - elements.extend_from_slice(value.as_elements()); - } - - let num_changed_entries = Felt::try_from(map_delta.num_entries()).expect( - "number of changed entries should not exceed max representable felt", - ); - - elements.extend_from_slice(&[ - DOMAIN_MAP, - slot_idx_felt, - num_changed_entries, - ZERO, - ]); - elements.extend_from_slice(EMPTY_WORD.as_elements()); + StorageSlotDelta::Map(map_delta) => { + for (key, value) in map_delta.entries() { + elements.extend_from_slice(key.inner().as_elements()); + elements.extend_from_slice(value.as_elements()); } + + let num_changed_entries = Felt::try_from(map_delta.num_entries()).expect( + "number of changed entries should not exceed max representable felt", + ); + + elements.extend_from_slice(&[ + DOMAIN_MAP, + num_changed_entries, + name_id.suffix(), + name_id.prefix(), + ]); + elements.extend_from_slice(EMPTY_WORD.as_elements()); }, } } } /// Consumes self and returns the underlying parts of the storage delta. - pub fn into_parts(self) -> (BTreeMap, BTreeMap) { + pub fn into_parts(self) -> (BTreeMap, BTreeMap) { (self.values, self.maps) } } @@ -207,9 +222,9 @@ impl Default for AccountStorageDelta { impl AccountStorageDelta { /// Creates an [AccountStorageDelta] from the given iterators. pub fn from_iters( - cleared_values: impl IntoIterator, - updated_values: impl IntoIterator, - updated_maps: impl IntoIterator, + cleared_values: impl IntoIterator, + updated_values: impl IntoIterator, + updated_maps: impl IntoIterator, ) -> Self { Self { values: BTreeMap::from_iter( @@ -222,14 +237,19 @@ impl AccountStorageDelta { impl Serializable for AccountStorageDelta { fn write_into(&self, target: &mut W) { - let cleared: Vec = self.cleared_slots().collect(); - let updated: Vec<(&u8, &Word)> = self.updated_slots().collect(); + let num_cleared = self.cleared_values().count(); + let num_cleared = u8::try_from(num_cleared).expect("number of slots should fit in u8"); + let cleared = self.cleared_values(); - target.write_u8(cleared.len() as u8); - target.write_many(cleared.iter()); + let num_updated = self.updated_values().count(); + let num_updated = u8::try_from(num_updated).expect("number of slots should fit in u8"); + let updated = self.updated_values(); - target.write_u8(updated.len() as u8); - target.write_many(updated.iter()); + target.write_u8(num_cleared); + target.write_many(cleared); + + target.write_u8(num_updated); + target.write_many(updated); target.write_u8(self.maps.len() as u8); target.write_many(self.maps.iter()); @@ -237,7 +257,6 @@ impl Serializable for AccountStorageDelta { fn get_size_hint(&self) -> usize { let u8_size = 0u8.get_size_hint(); - let word_size = EMPTY_WORD.get_size_hint(); let mut storage_map_delta_size = 0; for (slot, storage_map_delta) in self.maps.iter() { @@ -248,10 +267,12 @@ impl Serializable for AccountStorageDelta { // Length Prefixes u8_size * 3 + - // Cleared Slots - self.cleared_slots().count() * u8_size + - // Updated Slots - self.updated_slots().count() * (u8_size + word_size) + + // Cleared Values + self.cleared_values().fold(0, |acc, slot_name| acc + slot_name.get_size_hint()) + + // Updated Values + self.updated_values().fold(0, |acc, (slot_name, slot_value)| { + acc + slot_name.get_size_hint() + slot_value.get_size_hint() + }) + // Storage Map Delta storage_map_delta_size } @@ -261,26 +282,36 @@ impl Deserializable for AccountStorageDelta { fn read_from(source: &mut R) -> Result { let mut values = BTreeMap::new(); - let num_cleared_items = source.read_u8()? as usize; + let num_cleared_items = source.read_u8()?; for _ in 0..num_cleared_items { - let cleared_slot = source.read_u8()?; - values.insert(cleared_slot, EMPTY_WORD); + let cleared_value: SlotName = source.read()?; + values.insert(cleared_value, EMPTY_WORD); } - let num_updated_items = source.read_u8()? as usize; + let num_updated_items = source.read_u8()?; for _ in 0..num_updated_items { let (updated_slot, updated_value) = source.read()?; values.insert(updated_slot, updated_value); } let num_maps = source.read_u8()? as usize; - let maps = source.read_many::<(u8, StorageMapDelta)>(num_maps)?.into_iter().collect(); + let maps = source.read_many::<(SlotName, StorageMapDelta)>(num_maps)?.into_iter().collect(); Self::from_parts(values, maps) .map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } +// STORAGE SLOT DELTA +// ================================================================================================ + +/// The delta of a single storage slot. +#[derive(Clone, Debug, PartialEq, Eq)] +enum StorageSlotDelta<'map> { + Value(Word), + Map(&'map StorageMapDelta), +} + // STORAGE MAP DELTA // ================================================================================================ @@ -443,15 +474,18 @@ mod tests { use anyhow::Context; use super::{AccountStorageDelta, Deserializable, Serializable}; - use crate::account::StorageMapDelta; + use crate::account::{SlotName, StorageMapDelta}; use crate::testing::storage::AccountStorageDeltaBuilder; use crate::{ONE, Word, ZERO}; #[test] fn account_storage_delta_validation() { let delta = AccountStorageDelta::from_iters( - [1, 2, 3], - [(4, Word::from([ONE, ONE, ONE, ONE])), (5, Word::from([ONE, ONE, ONE, ZERO]))], + [SlotName::mock(1), SlotName::mock(2), SlotName::mock(3)], + [ + (SlotName::mock(4), Word::from([ONE, ONE, ONE, ONE])), + (SlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), + ], [], ); assert!(delta.validate().is_ok()); @@ -461,9 +495,12 @@ mod tests { // duplicate across cleared items and maps let delta = AccountStorageDelta::from_iters( - [1, 2, 3], - [(2, Word::from([ONE, ONE, ONE, ONE])), (5, Word::from([ONE, ONE, ONE, ZERO]))], - [(1, StorageMapDelta::default())], + [SlotName::mock(1), SlotName::mock(2), SlotName::mock(3)], + [ + (SlotName::mock(2), Word::from([ONE, ONE, ONE, ONE])), + (SlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), + ], + [(SlotName::mock(1), StorageMapDelta::default())], ); assert!(delta.validate().is_err()); @@ -472,9 +509,12 @@ mod tests { // duplicate across updated items and maps let delta = AccountStorageDelta::from_iters( - [1, 3], - [(2, Word::from([ONE, ONE, ONE, ONE])), (5, Word::from([ONE, ONE, ONE, ZERO]))], - [(2, StorageMapDelta::default())], + [SlotName::mock(1), SlotName::mock(3)], + [ + (SlotName::mock(2), Word::from([ONE, ONE, ONE, ONE])), + (SlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), + ], + [(SlotName::mock(2), StorageMapDelta::default())], ); assert!(delta.validate().is_err()); @@ -487,15 +527,21 @@ mod tests { let storage_delta = AccountStorageDelta::new(); assert!(storage_delta.is_empty()); - let storage_delta = AccountStorageDelta::from_iters([1], [], []); + let storage_delta = AccountStorageDelta::from_iters([SlotName::mock(1)], [], []); assert!(!storage_delta.is_empty()); - let storage_delta = - AccountStorageDelta::from_iters([], [(2, Word::from([ONE, ONE, ONE, ONE]))], []); + let storage_delta = AccountStorageDelta::from_iters( + [], + [(SlotName::mock(2), Word::from([ONE, ONE, ONE, ONE]))], + [], + ); assert!(!storage_delta.is_empty()); - let storage_delta = - AccountStorageDelta::from_iters([], [], [(3, StorageMapDelta::default())]); + let storage_delta = AccountStorageDelta::from_iters( + [], + [], + [(SlotName::mock(3), StorageMapDelta::default())], + ); assert!(!storage_delta.is_empty()); } @@ -506,19 +552,25 @@ mod tests { let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap(); assert_eq!(deserialized, storage_delta); - let storage_delta = AccountStorageDelta::from_iters([1], [], []); + let storage_delta = AccountStorageDelta::from_iters([SlotName::mock(1)], [], []); let serialized = storage_delta.to_bytes(); let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap(); assert_eq!(deserialized, storage_delta); - let storage_delta = - AccountStorageDelta::from_iters([], [(2, Word::from([ONE, ONE, ONE, ONE]))], []); + let storage_delta = AccountStorageDelta::from_iters( + [], + [(SlotName::mock(2), Word::from([ONE, ONE, ONE, ONE]))], + [], + ); let serialized = storage_delta.to_bytes(); let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap(); assert_eq!(deserialized, storage_delta); - let storage_delta = - AccountStorageDelta::from_iters([], [], [(3, StorageMapDelta::default())]); + let storage_delta = AccountStorageDelta::from_iters( + [], + [], + [(SlotName::mock(3), StorageMapDelta::default())], + ); let serialized = storage_delta.to_bytes(); let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap(); assert_eq!(deserialized, storage_delta); @@ -555,11 +607,11 @@ mod tests { ) -> anyhow::Result<()> { /// Creates a delta containing the item as an update if Some, else with the item cleared. fn create_delta(item: Option) -> anyhow::Result { - const SLOT: u8 = 123; - let item = item.map(|x| (SLOT, Word::from([x, 0, 0, 0]))); + let slot_name = SlotName::mock(123); + let item = item.map(|x| (slot_name.clone(), Word::from([x, 0, 0, 0]))); AccountStorageDeltaBuilder::new() - .add_cleared_items(item.is_none().then_some(SLOT)) + .add_cleared_items(item.is_none().then_some(slot_name.clone())) .add_updated_values(item) .build() .context("failed to build storage delta") diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index ef1779285d..4a6644bc68 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -1,5 +1,6 @@ use alloc::collections::BTreeMap; use alloc::string::ToString; +use alloc::vec::Vec; use miden_core::LexicographicWord; @@ -36,18 +37,19 @@ pub use code::procedure::AccountProcedureInfo; pub mod component; pub use component::{ AccountComponent, - AccountComponentMetadata, - FeltRepresentation, - InitStorageData, - MapEntry, - MapRepresentation, - PlaceholderTypeRequirement, - StorageEntry, - StorageValueName, - StorageValueNameError, - TemplateType, - TemplateTypeError, - WordRepresentation, + // TODO(named_slots): Uncomment when refactored. + // AccountComponentMetadata, + // FeltRepresentation, + // InitStorageData, + // MapEntry, + // MapRepresentation, + // PlaceholderTypeRequirement, + // StorageEntry, + // StorageValueName, + // StorageValueNameError, + // TemplateType, + // TemplateTypeError, + // WordRepresentation, }; pub mod delta; @@ -202,11 +204,11 @@ impl Account { /// - [`MastForest::merge`](miden_processor::MastForest::merge) fails on all libraries. pub(super) fn initialize_from_components( account_type: AccountType, - components: &[AccountComponent], + components: Vec, ) -> Result<(AccountCode, AccountStorage), AccountError> { - validate_components_support_account_type(components, account_type)?; + validate_components_support_account_type(&components, account_type)?; - let code = AccountCode::from_components_unchecked(components, account_type)?; + let code = AccountCode::from_components_unchecked(&components, account_type)?; let storage = AccountStorage::from_components(components, account_type)?; Ok((code, storage)) @@ -440,11 +442,11 @@ impl TryFrom for AccountDelta { let mut value_slots = BTreeMap::new(); let mut map_slots = BTreeMap::new(); - for (slot_idx, slot) in (0..u8::MAX).zip(storage.into_slots().into_iter()) { - let (_, _, slot) = slot.into_parts(); - match slot { + for slot in storage.into_slots() { + let (slot_name, _, slot_content) = slot.into_parts(); + match slot_content { StorageSlot::Value(word) => { - value_slots.insert(slot_idx, word); + value_slots.insert(slot_name, word); }, StorageSlot::Map(storage_map) => { let map_delta = StorageMapDelta::new( @@ -454,7 +456,7 @@ impl TryFrom for AccountDelta { .map(|(key, value)| (LexicographicWord::from(key), value)) .collect(), ); - map_slots.insert(slot_idx, map_delta); + map_slots.insert(slot_name, map_delta); }, } } @@ -638,7 +640,9 @@ mod tests { AccountComponent, AccountIdVersion, AccountType, + NamedStorageSlot, PartialAccount, + SlotName, StorageMap, StorageMapDelta, StorageSlot, @@ -672,8 +676,8 @@ mod tests { let asset_0 = FungibleAsset::mock(15); let asset_1 = NonFungibleAsset::mock(&[5, 5, 5]); let storage_delta = AccountStorageDeltaBuilder::new() - .add_cleared_items([0]) - .add_updated_values([(1_u8, Word::from([1, 2, 3, 4u32]))]) + .add_cleared_items([SlotName::mock(0)]) + .add_updated_values([(SlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) .build() .unwrap(); let account_delta = build_account_delta( @@ -737,9 +741,9 @@ mod tests { // build account delta let final_nonce = Felt::new(2); let storage_delta = AccountStorageDeltaBuilder::new() - .add_cleared_items([0]) - .add_updated_values([(1, Word::from([1, 2, 3, 4u32]))]) - .add_updated_maps([(2, updated_map)]) + .add_cleared_items([SlotName::mock(0)]) + .add_updated_values([(SlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) + .add_updated_maps([(SlotName::mock(2), updated_map)]) .build() .unwrap(); let account_delta = build_account_delta( @@ -779,8 +783,8 @@ mod tests { // build account delta let storage_delta = AccountStorageDeltaBuilder::new() - .add_cleared_items([0]) - .add_updated_values([(1_u8, Word::from([1, 2, 3, 4u32]))]) + .add_cleared_items([SlotName::mock(0)]) + .add_updated_values([(SlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) .build() .unwrap(); let account_delta = @@ -803,8 +807,8 @@ mod tests { // build account delta let final_nonce = Felt::new(1); let storage_delta = AccountStorageDeltaBuilder::new() - .add_cleared_items([0]) - .add_updated_values([(1_u8, Word::from([1, 2, 3, 4u32]))]) + .add_cleared_items([SlotName::mock(0)]) + .add_updated_values([(SlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) .build() .unwrap(); let account_delta = @@ -854,6 +858,12 @@ mod tests { let vault = AssetVault::new(&assets).unwrap(); + let slots = slots + .into_iter() + .enumerate() + .map(|(idx, slot)| NamedStorageSlot::new(SlotName::mock(idx), slot)) + .collect(); + let storage = AccountStorage::new(slots).unwrap(); Account::new_existing(id, vault, storage, code, nonce) @@ -875,7 +885,7 @@ mod tests { let err = Account::initialize_from_components( AccountType::RegularAccountUpdatableCode, - &[component1], + vec![component1], ) .unwrap_err(); @@ -903,7 +913,7 @@ mod tests { let err = Account::initialize_from_components( AccountType::RegularAccountUpdatableCode, - &[NoopAuthComponent.into(), component1, component2], + vec![NoopAuthComponent.into(), component1, component2], ) .unwrap_err(); diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-objects/src/account/storage/header.rs index eb1bf50ab3..a197b919e8 100644 --- a/crates/miden-objects/src/account/storage/header.rs +++ b/crates/miden-objects/src/account/storage/header.rs @@ -1,3 +1,4 @@ +use alloc::string::ToString; use alloc::vec::Vec; use super::{AccountStorage, Felt, StorageSlot, StorageSlotType, Word}; @@ -69,11 +70,21 @@ impl AccountStorageHeader { /// Returns a new instance of account storage header initialized with the provided slots. /// - /// # Panics - /// - If the number of provided slots is greater than [AccountStorage::MAX_NUM_STORAGE_SLOTS]. - pub fn new(slots: Vec<(SlotName, StorageSlotType, Word)>) -> Self { - assert!(slots.len() <= AccountStorage::MAX_NUM_STORAGE_SLOTS); - Self { slots } + /// # Errors + /// + /// Returns an error if: + /// - The number of provided slots is greater than [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. + /// - The slots are not sorted by [`SlotNameId`]. + pub fn new(slots: Vec<(SlotName, StorageSlotType, Word)>) -> Result { + if slots.len() > AccountStorage::MAX_NUM_STORAGE_SLOTS { + return Err(AccountError::StorageTooManySlots(slots.len() as u64)); + } + + if !slots.is_sorted_by_key(|(slot_name, ..)| slot_name.compute_id()) { + return Err(AccountError::UnsortedStorageSlots); + } + + Ok(Self { slots }) } // PUBLIC ACCESSORS @@ -100,33 +111,43 @@ impl AccountStorageHeader { /// Returns a slot contained in the storage header at a given index. /// - /// # Errors - /// - If the index is out of bounds. - pub fn slot_header( + /// Returns `None` if a slot with the provided name ID does not exist. + pub fn find_slot_header_by_name( &self, - index: usize, - ) -> Result<(&SlotName, &StorageSlotType, &Word), AccountError> { - let slot_name = SlotName::new_index(index); + slot_name: &SlotName, + ) -> Option<(&StorageSlotType, &Word)> { + self.find_slot_header_by_id(slot_name.compute_id()) + .map(|(_slot_name, slot_type, slot_value)| (slot_type, slot_value)) + } + /// Returns a slot contained in the storage header at a given index. + /// + /// Returns `None` if a slot with the provided name ID does not exist. + pub fn find_slot_header_by_id( + &self, + name_id: SlotNameId, + ) -> Option<(&SlotName, &StorageSlotType, &Word)> { self.slots - .binary_search_by_key(&slot_name.compute_id(), |(name, ..)| name.compute_id()) + .binary_search_by_key(&name_id, |(name, ..)| name.compute_id()) .map(|slot_idx| { let (name, r#type, value) = &self.slots[slot_idx]; (name, r#type, value) }) .ok() - .ok_or(AccountError::StorageIndexOutOfBounds { - slots_len: self.slots.len() as u8, - index: index as u8, - }) } - /// Indicates whether the slot at `index` is a map slot. + /// Indicates whether the slot with the given `name` is a map slot. /// /// # Errors - /// - If `index` exceeds the slot count. - pub fn is_map_slot(&self, index: usize) -> Result { - match self.slot_header(index)?.1 { + /// + /// Returns an error if: + /// - a slot with the provided name does not exist. + pub fn is_map_slot(&self, name: &SlotName) -> Result { + match self + .find_slot_header_by_name(name) + .ok_or(AccountError::StorageSlotNameNotFound { slot_name: name.clone() })? + .0 + { StorageSlotType::Map => Ok(true), StorageSlotType::Value => Ok(false), } @@ -188,8 +209,7 @@ impl Deserializable for AccountStorageHeader { fn read_from(source: &mut R) -> Result { let len = source.read_u8()?; let slots = source.read_many(len as usize)?; - // number of storage slots is guaranteed to be smaller than or equal to 255 - Ok(Self::new(slots)) + Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } @@ -203,22 +223,24 @@ mod tests { use super::AccountStorageHeader; use crate::Word; - use crate::account::{AccountStorage, SlotName, StorageSlotType}; + use crate::account::{AccountStorage, StorageSlotType}; + use crate::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; #[test] fn test_from_account_storage() { let storage_map = AccountStorage::mock_map(); // create new storage header from AccountStorage - let slots = vec![ - (SlotName::new_index(0), StorageSlotType::Value, Word::from([1, 2, 3, 4u32])), + let mut slots = vec![ + (MOCK_VALUE_SLOT0.clone(), StorageSlotType::Value, Word::from([1, 2, 3, 4u32])), ( - SlotName::new_index(1), + MOCK_VALUE_SLOT1.clone(), StorageSlotType::Value, Word::from([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]), ), - (SlotName::new_index(2), StorageSlotType::Map, storage_map.root()), + (MOCK_MAP_SLOT.clone(), StorageSlotType::Map, storage_map.root()), ]; + slots.sort_unstable_by_key(|(slot_name, ..)| slot_name.compute_id()); let expected_header = AccountStorageHeader { slots }; let account_storage = AccountStorage::mock(); diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index c1f932bbd3..e7e1ae3138 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -16,6 +16,7 @@ use super::{ use crate::account::storage::header::StorageSlotHeader; use crate::account::{AccountComponent, AccountType}; use crate::crypto::SequentialCommit; +use crate::utils::sync::LazyLock; mod slot; pub use slot::{NamedStorageSlot, SlotName, SlotNameId, StorageSlot, StorageSlotType}; @@ -29,18 +30,28 @@ pub use header::AccountStorageHeader; mod partial; pub use partial::PartialStorage; +static FAUCET_METADATA_SLOT_NAME: LazyLock = + LazyLock::new(|| SlotName::new("miden::faucet::metadata").expect("slot name should be valid")); + // ACCOUNT STORAGE // ================================================================================================ -/// Account storage is composed of a variable number of index-addressable [StorageSlot]s up to +/// Account storage is composed of a variable number of name-addressable [`NamedStorageSlot`]s up to /// 255 slots in total. /// /// Each slot has a type which defines its size and structure. Currently, the following types are /// supported: -/// - [StorageSlot::Value]: contains a single [Word] of data (i.e., 32 bytes). -/// - [StorageSlot::Map]: contains a [StorageMap] which is a key-value map where both keys and +/// - [`StorageSlot::Value`]: contains a single [`Word`] of data (i.e., 32 bytes). +/// - [`StorageSlot::Map`]: contains a [`StorageMap`] which is a key-value map where both keys and /// values are [Word]s. The value of a storage slot containing a map is the commitment to the /// underlying map. +/// +/// Slots are sorted by [`SlotName`] (or [`SlotNameId`] equivalently). This order is necessary to: +/// - Simplify lookups of slots in the transaction kernel (using `std::collections::sorted_array` +/// from the miden core library) +/// - Allow the [`AccountStorageDelta`] to work only with slot names instead of slot indices. +/// - Make it simple to check for duplicates by iterating the slots and checking that no two +/// adjacent items have the same slot name. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct AccountStorage { slots: Vec, @@ -53,38 +64,29 @@ impl AccountStorage { // CONSTRUCTOR // -------------------------------------------------------------------------------------------- - /// TODO(named_slots): Remove this temporary API. + /// Returns a new instance of account storage initialized with the provided storage slots. /// - /// Returns a new instance of account storage initialized with the provided items. + /// This function sorts the slots by [`SlotName`]. /// /// # Errors /// /// Returns an error if: /// - The number of [`StorageSlot`]s exceeds 255. - pub fn new(slots: Vec) -> Result { - let slots = slots - .into_iter() - .enumerate() - .map(|(idx, slot)| NamedStorageSlot::new(SlotName::new_index(idx), slot)) - .collect(); - - Self::new_named(slots) - } - - /// TODO(named_slots): Rename to new. - pub fn new_named(mut slots: Vec) -> Result { + /// - There are multiple storage slots with the same [`SlotName`]. + pub fn new(mut slots: Vec) -> Result { let num_slots = slots.len(); if num_slots > Self::MAX_NUM_STORAGE_SLOTS { return Err(AccountError::StorageTooManySlots(num_slots as u64)); } + // TODO(named_slots): Optimization: If we keep slots sorted, iterate slots instead and + // compare adjacent elements. let mut names = BTreeSet::new(); for slot in &slots { if !names.insert(slot.name()) { - // TODO(named_slots): Return error. // TODO(named_slots): Add test for this new error. - todo!("error: storage slot name {} is assigned to more than one slot", slot.name()) + return Err(AccountError::DuplicateStorageSlotName(slot.name().clone())); } } @@ -108,37 +110,41 @@ impl AccountStorage { /// Returns an error if: /// - The number of [`StorageSlot`]s of all components exceeds 255. pub(super) fn from_components( - components: &[AccountComponent], + components: Vec, account_type: AccountType, ) -> Result { let mut storage_slots = match account_type { AccountType::FungibleFaucet => { - vec![NamedStorageSlot::new(SlotName::new_index(0), StorageSlot::empty_value())] + vec![NamedStorageSlot::with_empty_value(Self::faucet_metadata_slot().clone())] }, AccountType::NonFungibleFaucet => { - vec![NamedStorageSlot::new(SlotName::new_index(0), StorageSlot::empty_map())] + vec![NamedStorageSlot::with_empty_map(Self::faucet_metadata_slot().clone())] }, _ => vec![], }; - let offset = storage_slots.len(); + for component_slot in components.into_iter().flat_map(|component| { + let AccountComponent { storage_slots, .. } = component; + storage_slots.into_iter() + }) { + if component_slot.name() == Self::faucet_metadata_slot() { + return Err(AccountError::StorageSlotNameMustNotBeFaucetMetadata); + } - for (slot_idx, slot) in components - .iter() - .flat_map(|component| component.storage_slots()) - .cloned() - .enumerate() - { - let name = SlotName::new_index(slot_idx + offset); - storage_slots.push(NamedStorageSlot::new(name, slot)); + storage_slots.push(component_slot); } - Self::new_named(storage_slots) + Self::new(storage_slots) } // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- + /// Returns the [`SlotName`] of the faucet's protocol metadata. + pub fn faucet_metadata_slot() -> &'static SlotName { + &FAUCET_METADATA_SLOT_NAME + } + /// Converts storage slots of this account storage into a vector of field elements. /// /// Each storage slot is represented by exactly 8 elements: @@ -186,8 +192,11 @@ impl AccountStorage { }) .collect(), ) + .expect("slots should be valid as ensured by AccountStorage") } + /// Returns a reference to the storage slot with the provided name, if it exists, `None` + /// otherwise. pub fn get(&self, slot_name: &SlotName) -> Option<&NamedStorageSlot> { debug_assert!(self.slots.is_sorted()); @@ -206,29 +215,31 @@ impl AccountStorage { .ok() } - /// Returns an item from the storage at the specified index. + /// Returns an item from the storage slot with the given name. + /// + /// # Errors /// - /// # Errors: - /// - If the index is out of bounds - pub fn get_item(&self, index: u8) -> Result { - let slot_name = SlotName::new_index(index as usize); - self.get(&slot_name) + /// Returns an error if: + /// - A slot with the provided name does not exist. + pub fn get_item(&self, slot_name: &SlotName) -> Result { + self.get(slot_name) .map(|named_slot| named_slot.storage_slot().value()) .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }) } - /// Returns a map item from a map located in storage at the specified index. + /// Returns a map item from the map in the storage slot with the given name. /// - /// # Errors: - /// - If the index is out of bounds - /// - If the [StorageSlot] is not [StorageSlotType::Map] - pub fn get_map_item(&self, index: u8, key: Word) -> Result { - let slot_name = SlotName::new_index(index as usize); - self.get(&slot_name) + /// # Errors + /// + /// Returns an error if: + /// - A slot with the provided name does not exist. + /// - If the [`StorageSlot`] is not [`StorageSlotType::Map`]. + pub fn get_map_item(&self, slot_name: &SlotName, key: Word) -> Result { + self.get(slot_name) .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }) .and_then(|named_slot| match named_slot.storage_slot() { StorageSlot::Map(map) => Ok(map.get(&key)), - _ => Err(AccountError::StorageSlotNotMap(index)), + _ => Err(AccountError::StorageSlotNotMap(slot_name.clone())), }) } @@ -237,49 +248,50 @@ impl AccountStorage { /// Applies the provided delta to this account storage. /// - /// # Errors: - /// - If the updates violate storage constraints. + /// # Errors + /// + /// Returns an error if: + /// - The updates violate storage constraints. pub(super) fn apply_delta(&mut self, delta: &AccountStorageDelta) -> Result<(), AccountError> { - let len = self.slots.len() as u8; + // Update storage values + for (slot_name, &value) in delta.values().iter() { + self.set_item(slot_name, value)?; + } - // update storage maps - for (&idx, map) in delta.maps().iter() { + // Update storage maps + for (slot_name, map_delta) in delta.maps().iter() { let named_slot = self - .get_mut(&SlotName::new_index(idx as usize)) - .ok_or(AccountError::StorageIndexOutOfBounds { slots_len: len, index: idx })?; + .get_mut(slot_name) + .ok_or(AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })?; let storage_map = match named_slot.storage_slot_mut() { StorageSlot::Map(map) => map, - _ => return Err(AccountError::StorageSlotNotMap(idx)), + _ => return Err(AccountError::StorageSlotNotMap(slot_name.clone())), }; - storage_map.apply_delta(map)?; - } - - // update storage values - for (&idx, &value) in delta.values().iter() { - self.set_item(idx, value)?; + storage_map.apply_delta(map_delta)?; } Ok(()) } - /// Updates the value of the storage slot at the specified index. + /// Updates the value of the storage slot with the given name. /// /// This method should be used only to update value slots. For updating values - /// in storage maps, please see [AccountStorage::set_map_item()]. + /// in storage maps, please see [`AccountStorage::set_map_item`]. + /// + /// # Errors /// - /// # Errors: - /// - If the index is out of bounds - /// - If the [StorageSlot] is not [StorageSlotType::Value] - pub fn set_item(&mut self, index: u8, value: Word) -> Result { - let slot_name = SlotName::new_index(index as usize); - let slot = self.get_mut(&slot_name).ok_or_else(|| { + /// Returns an error if: + /// - A slot with the provided name does not exist. + /// - The [`StorageSlot`] is not [`StorageSlotType::Value`]. + pub fn set_item(&mut self, slot_name: &SlotName, value: Word) -> Result { + let slot = self.get_mut(slot_name).ok_or_else(|| { AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() } })?; let StorageSlot::Value(old_value) = slot.storage_slot() else { - return Err(AccountError::StorageSlotNotValue(index)); + return Err(AccountError::StorageSlotNotValue(slot_name.clone())); }; let old_value = *old_value; @@ -289,27 +301,28 @@ impl AccountStorage { Ok(old_value) } - /// Updates the value of a key-value pair of a storage map at the specified index. + /// Updates the value of a key-value pair of a storage map with the given name. /// /// This method should be used only to update storage maps. For updating values /// in storage slots, please see [AccountStorage::set_item()]. /// - /// # Errors: - /// - If the index is out of bounds - /// - If the [StorageSlot] is not [StorageSlotType::Map] + /// # Errors + /// + /// Returns an error if: + /// - A slot with the provided name does not exist. + /// - If the [`StorageSlot`] is not [`StorageSlotType::Map`]. pub fn set_map_item( &mut self, - index: u8, + slot_name: &SlotName, raw_key: Word, value: Word, ) -> Result<(Word, Word), AccountError> { - let slot_name = SlotName::new_index(index as usize); - let slot = self.get_mut(&slot_name).ok_or_else(|| { + let slot = self.get_mut(slot_name).ok_or_else(|| { AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() } })?; let StorageSlot::Map(storage_map) = slot.storage_slot_mut() else { - return Err(AccountError::StorageSlotNotMap(index)); + return Err(AccountError::StorageSlotNotMap(slot_name.clone())); }; let old_root = storage_map.root(); @@ -380,7 +393,7 @@ impl Deserializable for AccountStorage { let num_slots = source.read_u8()? as usize; let slots = source.read_many::(num_slots)?; - Self::new_named(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string())) + Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } @@ -389,11 +402,11 @@ impl Deserializable for AccountStorage { #[cfg(test)] mod tests { - use super::{AccountStorage, Deserializable, Serializable, StorageMap, Word}; - use crate::account::{NamedStorageSlot, SlotName, StorageSlot}; + use super::{AccountStorage, Deserializable, Serializable}; + use crate::account::{NamedStorageSlot, SlotName}; #[test] - fn test_serde_account_storage() { + fn test_serde_account_storage() -> anyhow::Result<()> { // empty storage let storage = AccountStorage::new(vec![]).unwrap(); let bytes = storage.to_bytes(); @@ -401,30 +414,29 @@ mod tests { // storage with values for default types let storage = AccountStorage::new(vec![ - StorageSlot::Value(Word::empty()), - StorageSlot::Map(StorageMap::default()), + NamedStorageSlot::with_empty_value(SlotName::new("miden::test::value")?), + NamedStorageSlot::with_empty_map(SlotName::new("miden::test::map")?), ]) .unwrap(); let bytes = storage.to_bytes(); assert_eq!(storage, AccountStorage::read_from_bytes(&bytes).unwrap()); + + Ok(()) } #[test] fn test_get_slot_by_name() -> anyhow::Result<()> { - // TODO(named_slots): Use proper names. - // const COUNTER_SLOT: SlotName = SlotName::from_static_str("miden::test::counter"); - // const MAP_SLOT: SlotName = SlotName::from_static_str("miden::test::map"); - const COUNTER_SLOT: SlotName = SlotName::from_static_str("miden::0"); - const MAP_SLOT: SlotName = SlotName::from_static_str("miden::4"); + let counter_slot = SlotName::new("miden::test::counter")?; + let map_slot = SlotName::new("miden::test::map")?; let slots = vec![ - NamedStorageSlot::new(COUNTER_SLOT, StorageSlot::empty_value()), - NamedStorageSlot::new(MAP_SLOT, StorageSlot::empty_map()), + NamedStorageSlot::with_empty_value(counter_slot.clone()), + NamedStorageSlot::with_empty_map(map_slot.clone()), ]; - let storage = AccountStorage::new_named(slots.clone())?; + let storage = AccountStorage::new(slots.clone())?; - assert_eq!(storage.get(&COUNTER_SLOT).unwrap(), &slots[0]); - assert_eq!(storage.get(&MAP_SLOT).unwrap(), &slots[1]); + assert_eq!(storage.get(&counter_slot).unwrap(), &slots[0]); + assert_eq!(storage.get(&map_slot).unwrap(), &slots[1]); Ok(()) } diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-objects/src/account/storage/partial.rs index f7f268ab95..c196a07ac8 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-objects/src/account/storage/partial.rs @@ -159,10 +159,11 @@ mod tests { use crate::account::{ AccountStorage, AccountStorageHeader, + NamedStorageSlot, PartialStorage, PartialStorageMap, + SlotName, StorageMap, - StorageSlot, }; #[test] @@ -175,7 +176,11 @@ mod tests { map_1.insert(map_key_present, Word::try_from([5u64, 4, 3, 2])?).unwrap(); assert_eq!(map_1.get(&map_key_present), [5u64, 4, 3, 2].try_into()?); - let storage = AccountStorage::new(vec![StorageSlot::Map(map_1.clone())]).unwrap(); + let slot_name = SlotName::new("miden::test_map")?; + + let storage = + AccountStorage::new(vec![NamedStorageSlot::with_map(slot_name.clone(), map_1.clone())]) + .unwrap(); // Create partial storage with validation of one map key let storage_header = AccountStorageHeader::from(&storage); @@ -185,8 +190,8 @@ mod tests { PartialStorage::new(storage_header, [PartialStorageMap::with_witnesses([witness])?]) .context("creating partial storage")?; - let retrieved_map = - partial_storage.maps.get(partial_storage.header.slot_header(0)?.2).unwrap(); + let (_, map_root) = partial_storage.header.find_slot_header_by_name(&slot_name).unwrap(); + let retrieved_map = partial_storage.maps.get(map_root).unwrap(); assert!(retrieved_map.open(&map_key_absent).is_err()); assert!(retrieved_map.open(&map_key_present).is_ok()); Ok(()) diff --git a/crates/miden-objects/src/account/storage/slot/mod.rs b/crates/miden-objects/src/account/storage/slot/mod.rs index 8f27fa0550..25454d46f5 100644 --- a/crates/miden-objects/src/account/storage/slot/mod.rs +++ b/crates/miden-objects/src/account/storage/slot/mod.rs @@ -65,8 +65,8 @@ impl StorageSlot { /// Returns this storage slot value as a [Word] /// /// Returns: - /// - For [StorageSlot::Value] the value - /// - For [StorageSlot::Map] the root of the [StorageMap] + /// - For [`StorageSlot::Value`] the value. + /// - For [`StorageSlot::Map`] the root of the [StorageMap]. pub fn value(&self) -> Word { match self { Self::Value(value) => *value, diff --git a/crates/miden-objects/src/account/storage/slot/named_slot.rs b/crates/miden-objects/src/account/storage/slot/named_slot.rs index 975c4ed2a3..da51f9017a 100644 --- a/crates/miden-objects/src/account/storage/slot/named_slot.rs +++ b/crates/miden-objects/src/account/storage/slot/named_slot.rs @@ -1,13 +1,17 @@ +use crate::Word; use crate::account::storage::slot::SlotNameId; -use crate::account::{SlotName, StorageSlot}; +use crate::account::{SlotName, StorageMap, StorageSlot, StorageSlotType}; -// TODO(named_slots): Docs + separators for the entire module. +/// An individual storage slot in [`AccountStorage`](crate::account::AccountStorage). +/// +/// This consists of a [`SlotName`] that uniquely identifies the slot and its [`StorageSlot`] +/// content. #[derive(Debug, Clone, PartialEq, Eq)] pub struct NamedStorageSlot { /// The name of the storage slot. name: SlotName, - /// The cached [`SlotNameId`] of the slot name. These must always be consistent with each - /// other. + /// The cached [`SlotNameId`] of the slot name. This field must always be consistent with the + /// slot name. /// /// This is cached so that the `Ord` implementation can use the computed name ID instead of /// having to hash the slot name on every comparison operation. @@ -17,28 +21,81 @@ pub struct NamedStorageSlot { } impl NamedStorageSlot { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and [`StorageSlot`]. pub fn new(name: SlotName, slot: StorageSlot) -> Self { let name_id = name.compute_id(); Self { name, name_id, slot } } + /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and the `value` wrapped into + /// a [`StorageSlot::Value`]. + pub fn with_value(name: SlotName, value: Word) -> Self { + Self::new(name, StorageSlot::Value(value)) + } + + /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and + /// [`StorageSlot::empty_value`]. + pub fn with_empty_value(name: SlotName) -> Self { + Self::new(name, StorageSlot::empty_value()) + } + + /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and the `map` wrapped into a + /// [`StorageSlot::Map`] + pub fn with_map(name: SlotName, map: StorageMap) -> Self { + Self::new(name, StorageSlot::Map(map)) + } + + /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and + /// [`StorageSlot::empty_map`]. + pub fn with_empty_map(name: SlotName) -> Self { + Self::new(name, StorageSlot::empty_map()) + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the [`SlotName`] by which the [`NamedStorageSlot`] is identified. pub fn name(&self) -> &SlotName { &self.name } + /// Returns the [`SlotNameId`] by which the [`NamedStorageSlot`] is identified. pub fn name_id(&self) -> SlotNameId { self.name_id } + /// Returns this storage slot value as a [Word] + /// + /// Returns: + /// - For [`StorageSlot::Value`] the value. + /// - For [`StorageSlot::Map`] the root of the [StorageMap]. + pub fn value(&self) -> Word { + self.storage_slot().value() + } + + /// Returns a reference to the [`StorageSlot`] contained in this [`NamedStorageSlot`]. pub fn storage_slot(&self) -> &StorageSlot { &self.slot } + /// Returns the [`StorageSlotType`] of this [`NamedStorageSlot`]. + pub fn slot_type(&self) -> StorageSlotType { + self.slot.slot_type() + } + + // MUTATORS + // -------------------------------------------------------------------------------------------- + + /// Returns a mutable reference to the [`StorageSlot`] contained in this [`NamedStorageSlot`]. pub fn storage_slot_mut(&mut self) -> &mut StorageSlot { &mut self.slot } + /// Consumes self and returns the underlying parts. pub fn into_parts(self) -> (SlotName, SlotNameId, StorageSlot) { (self.name, self.name_id, self.slot) } @@ -56,6 +113,9 @@ impl PartialOrd for NamedStorageSlot { } } +// SERIALIZATION +// ================================================================================================ + impl crate::utils::serde::Serializable for NamedStorageSlot { fn write_into(&self, target: &mut W) { target.write(&self.name); diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-objects/src/account/storage/slot/slot_name.rs index 7cf7673115..b3dc9c4390 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_name.rs @@ -2,10 +2,11 @@ use alloc::borrow::Cow; use alloc::string::{String, ToString}; use core::fmt::Display; +use miden_core::utils::hash_string_to_word; + use crate::account::storage::slot::SlotNameId; use crate::errors::SlotNameError; use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Serializable}; -use crate::{Felt, FieldElement}; /// The name of an account storage slot. /// @@ -34,7 +35,7 @@ use crate::{Felt, FieldElement}; /// - Each component must only consist of the characters `a` to `z`, `A` to `Z`, `0` to `9` or `_` /// (underscore). /// - Each component must not start with an underscore. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct SlotName { name: Cow<'static, str>, } @@ -87,11 +88,6 @@ impl SlotName { Ok(Self { name: Cow::Owned(name) }) } - // TODO(named_slots): Temporary. Remove later. - pub fn new_index(slot_idx: usize) -> Self { - SlotName::new(format!("miden::{slot_idx}")).expect("slot name should be valid") - } - // ACCESSORS // -------------------------------------------------------------------------------------------- @@ -112,23 +108,10 @@ impl SlotName { // TODO(named_slots): Docs. pub fn compute_id(&self) -> SlotNameId { - // let hashed_word = hash_string_to_word(self.as_str()); - // let prefix = hashed_word[0]; - // let suffix = hashed_word[1]; - // SlotNameId::new(prefix, suffix) - - // TODO: Temporary, replace later with the above. - let mut split = self.as_str().split("::"); - - let namespace = split.next().unwrap(); - assert_eq!(namespace, "miden"); - - let slot_idx = split.next().unwrap(); - let slot_index: u32 = slot_idx.parse().expect( - "named storage slots should for now have the slot index as the second component", - ); - - SlotNameId::new(Felt::from(slot_index), Felt::ZERO) + let hashed_word = hash_string_to_word(self.as_str()); + let suffix = hashed_word[0]; + let prefix = hashed_word[1]; + SlotNameId::new(suffix, prefix) } // HELPERS @@ -226,6 +209,19 @@ impl SlotName { } } +impl Ord for SlotName { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + // TODO(named_slots): Cache ID in SlotName for efficiency. + self.compute_id().cmp(&other.compute_id()) + } +} + +impl PartialOrd for SlotName { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl Display for SlotName { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_str(self.as_str()) diff --git a/crates/miden-objects/src/account/storage/slot/slot_name_id.rs b/crates/miden-objects/src/account/storage/slot/slot_name_id.rs index 2dbd3e29a2..87a6d8b7b2 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name_id.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_name_id.rs @@ -1,25 +1,49 @@ use core::cmp::Ordering; +use core::fmt::Display; use crate::Felt; -// TODO(named_slots): Docs + separators for the entire module. +/// The partial hash of a [`SlotName`](super::SlotName). +/// +/// The ID of a slot name are the first (`suffix`) and second (`prefix`) field elements of the +/// blake3-hashed slot name. +/// +/// The slot name ID is used to uniquely identify a storage slot and is used to sort slots in +/// account storage. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SlotNameId { - prefix: Felt, suffix: Felt, + prefix: Felt, } impl SlotNameId { - pub fn new(prefix: Felt, suffix: Felt) -> Self { - Self { prefix, suffix } + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`SlotNameId`] from the provided felts. + pub fn new(suffix: Felt, prefix: Felt) -> Self { + Self { suffix, prefix } } + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the suffix of the [`SlotNameId`]. + pub fn suffix(&self) -> Felt { + self.suffix + } + + /// Returns the prefix of the [`SlotNameId`]. pub fn prefix(&self) -> Felt { self.prefix } - pub fn suffix(&self) -> Felt { - self.suffix + /// Returns the [`SlotNameId`]'s felts encoded into a u128. + fn as_u128(&self) -> u128 { + let mut le_bytes = [0_u8; 16]; + le_bytes[..8].copy_from_slice(&self.suffix().as_int().to_le_bytes()); + le_bytes[8..].copy_from_slice(&self.prefix().as_int().to_le_bytes()); + u128::from_le_bytes(le_bytes) } } @@ -37,3 +61,29 @@ impl PartialOrd for SlotNameId { Some(self.cmp(other)) } } + +impl Display for SlotNameId { + /// Returns a big-endian, hex-encoded string of length 34, including the `0x` prefix. + /// + /// This means it encodes 16 bytes. + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_fmt(format_args!("0x{:032x}", self.as_u128())) + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_name_id_as_u128() { + let suffix = 5; + let prefix = 3; + let name_id = SlotNameId::new(Felt::from(suffix as u32), Felt::from(prefix as u32)); + assert_eq!(name_id.as_u128(), (prefix << 64) + suffix); + assert_eq!(format!("{name_id}"), "0x00000000000000030000000000000005"); + } +} diff --git a/crates/miden-objects/src/account/storage/slot/type.rs b/crates/miden-objects/src/account/storage/slot/type.rs index a02b830154..9fac8cada1 100644 --- a/crates/miden-objects/src/account/storage/slot/type.rs +++ b/crates/miden-objects/src/account/storage/slot/type.rs @@ -1,4 +1,5 @@ use alloc::string::ToString; +use core::fmt::Display; use crate::utils::serde::{ ByteReader, @@ -53,6 +54,15 @@ impl TryFrom for StorageSlotType { } } +impl Display for StorageSlotType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + StorageSlotType::Value => f.write_str("value"), + StorageSlotType::Map => f.write_str("map"), + } + } +} + // SERIALIZATION // ================================================================================================ diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 2cecab8277..680065b25c 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -23,9 +23,10 @@ use crate::account::{ AccountStorage, AccountType, SlotName, - StorageValueName, - StorageValueNameError, - TemplateTypeError, + SlotNameId, + // StorageValueName, + // StorageValueNameError, + // TemplateTypeError, }; use crate::address::AddressType; use crate::asset::AssetVaultKey; @@ -45,6 +46,7 @@ use crate::{ // ACCOUNT COMPONENT TEMPLATE ERROR // ================================================================================================ +/* #[derive(Debug, Error)] pub enum AccountComponentTemplateError { #[error("storage slot name `{0}` is duplicate")] @@ -80,6 +82,7 @@ pub enum AccountComponentTemplateError { #[error("error trying to deserialize from toml")] TomlSerializationError(#[source] toml::ser::Error), } +*/ // ACCOUNT ERROR // ================================================================================================ @@ -108,8 +111,8 @@ pub enum AccountError { AccountComponentMastForestMergeError(#[source] MastForestError), #[error("procedure with MAST root {0} is present in multiple account components")] AccountComponentDuplicateProcedureRoot(Word), - #[error("failed to create account component")] - AccountComponentTemplateInstantiationError(#[source] AccountComponentTemplateError), + // #[error("failed to create account component")] + // AccountComponentTemplateInstantiationError(#[source] AccountComponentTemplateError), #[error("account component contains multiple authentication procedures")] AccountComponentMultipleAuthProcedures, #[error("failed to update asset vault")] @@ -144,14 +147,23 @@ pub enum AccountError { SeedConvertsToInvalidAccountId(#[source] AccountIdError), #[error("storage map root {0} not found in the account storage")] StorageMapRootNotFound(Word), - #[error("storage slot at index {0} is not of type map")] - StorageSlotNotMap(u8), - #[error("storage slot at index {0} is not of type value")] - StorageSlotNotValue(u8), - #[error("storage slot index is {index} but the slots length is {slots_len}")] - StorageIndexOutOfBounds { slots_len: u8, index: u8 }, + #[error("storage slot {0} is not of type map")] + StorageSlotNotMap(SlotName), + #[error("storage slot {0} is not of type value")] + StorageSlotNotValue(SlotName), + #[error("storage slot name {0} is assigned to more than one slot")] + DuplicateStorageSlotName(SlotName), + #[error( + "account storage cannot contain a user-provided slot with name {} as it is reserved by the protocol", + AccountStorage::faucet_metadata_slot() + )] + StorageSlotNameMustNotBeFaucetMetadata, #[error("storage does not contain a slot with name {slot_name}")] StorageSlotNameNotFound { slot_name: SlotName }, + #[error("storage does not contain a slot with ID {slot_name_id}")] + StorageSlotNameIdNotFound { slot_name_id: SlotNameId }, + #[error("storage slots must be sorted by slot name ID")] + UnsortedStorageSlots, #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)] StorageTooManySlots(u64), #[error("procedure storage offset + size is {0} which exceeds the maximum value of {max}", @@ -361,12 +373,8 @@ pub enum NetworkIdError { #[derive(Debug, Error)] pub enum AccountDeltaError { - #[error( - "storage slot index {slot_index} is greater than or equal to the number of slots {num_slots}" - )] - StorageSlotIndexOutOfBounds { slot_index: u8, num_slots: u8 }, #[error("storage slot {0} was updated as a value and as a map")] - StorageSlotUsedAsDifferentTypes(u8), + StorageSlotUsedAsDifferentTypes(SlotName), #[error("non fungible vault can neither be added nor removed twice")] DuplicateNonFungibleVaultUpdate(NonFungibleAsset), #[error( diff --git a/crates/miden-objects/src/testing/mod.rs b/crates/miden-objects/src/testing/mod.rs index 7bf6c65286..c58d4ba930 100644 --- a/crates/miden-objects/src/testing/mod.rs +++ b/crates/miden-objects/src/testing/mod.rs @@ -9,5 +9,6 @@ pub mod constants; pub mod noop_auth_component; pub mod note; pub mod partial_blockchain; +pub mod slot_name; pub mod storage; pub mod tx; diff --git a/crates/miden-objects/src/testing/slot_name.rs b/crates/miden-objects/src/testing/slot_name.rs new file mode 100644 index 0000000000..76174f225e --- /dev/null +++ b/crates/miden-objects/src/testing/slot_name.rs @@ -0,0 +1,8 @@ +use crate::account::SlotName; + +impl SlotName { + /// Returns a new slot name with the format `"miden::test::slot::{index}"`. + pub fn mock(index: usize) -> Self { + Self::new(format!("miden::test::slot::{index}")).expect("slot name should be valid") + } +} diff --git a/crates/miden-objects/src/testing/storage.rs b/crates/miden-objects/src/testing/storage.rs index 33422fd54c..d74a57d010 100644 --- a/crates/miden-objects/src/testing/storage.rs +++ b/crates/miden-objects/src/testing/storage.rs @@ -9,19 +9,21 @@ use crate::AccountDeltaError; use crate::account::{ AccountStorage, AccountStorageDelta, + NamedStorageSlot, + SlotName, StorageMap, StorageMapDelta, - StorageSlot, }; use crate::note::NoteAssets; +use crate::utils::sync::LazyLock; // ACCOUNT STORAGE DELTA BUILDER // ================================================================================================ #[derive(Clone, Debug, Default)] pub struct AccountStorageDeltaBuilder { - values: BTreeMap, - maps: BTreeMap, + values: BTreeMap, + maps: BTreeMap, } impl AccountStorageDeltaBuilder { @@ -38,19 +40,19 @@ impl AccountStorageDeltaBuilder { // MODIFIERS // ------------------------------------------------------------------------------------------- - pub fn add_cleared_items(mut self, items: impl IntoIterator) -> Self { + pub fn add_cleared_items(mut self, items: impl IntoIterator) -> Self { self.values.extend(items.into_iter().map(|slot| (slot, EMPTY_WORD))); self } - pub fn add_updated_values(mut self, items: impl IntoIterator) -> Self { + pub fn add_updated_values(mut self, items: impl IntoIterator) -> Self { self.values.extend(items); self } pub fn add_updated_maps( mut self, - items: impl IntoIterator, + items: impl IntoIterator, ) -> Self { self.maps.extend(items); self @@ -64,23 +66,15 @@ impl AccountStorageDeltaBuilder { } } -// ACCOUNT STORAGE UTILS -// ================================================================================================ - -#[derive(Debug)] -pub struct SlotWithIndex { - pub slot: StorageSlot, - pub index: u8, -} - // CONSTANTS // ================================================================================================ -pub const FAUCET_STORAGE_DATA_SLOT: u8 = 0; - -pub const STORAGE_INDEX_0: u8 = 0; -pub const STORAGE_INDEX_1: u8 = 1; -pub const STORAGE_INDEX_2: u8 = 2; +pub static MOCK_VALUE_SLOT0: LazyLock = + LazyLock::new(|| SlotName::new("miden::test::value0").expect("slot name should be valid")); +pub static MOCK_VALUE_SLOT1: LazyLock = + LazyLock::new(|| SlotName::new("miden::test::value1").expect("slot name should be valid")); +pub static MOCK_MAP_SLOT: LazyLock = + LazyLock::new(|| SlotName::new("miden::test::map").expect("slot name should be valid")); pub const STORAGE_VALUE_0: Word = Word::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]); @@ -98,34 +92,25 @@ pub const STORAGE_LEAVES_2: [(Word, Word); 2] = [ ]; impl AccountStorage { - /// Create account storage: + /// Create account storage. pub fn mock() -> Self { AccountStorage::new(Self::mock_storage_slots()).unwrap() } - pub fn mock_storage_slots() -> Vec { - vec![Self::mock_item_0().slot, Self::mock_item_1().slot, Self::mock_item_2().slot] + pub fn mock_storage_slots() -> Vec { + vec![Self::mock_value_slot0(), Self::mock_value_slot1(), Self::mock_map_slot()] } - pub fn mock_item_0() -> SlotWithIndex { - SlotWithIndex { - slot: StorageSlot::Value(STORAGE_VALUE_0), - index: STORAGE_INDEX_0, - } + pub fn mock_value_slot0() -> NamedStorageSlot { + NamedStorageSlot::with_value(MOCK_VALUE_SLOT0.clone(), STORAGE_VALUE_0) } - pub fn mock_item_1() -> SlotWithIndex { - SlotWithIndex { - slot: StorageSlot::Value(STORAGE_VALUE_1), - index: STORAGE_INDEX_1, - } + pub fn mock_value_slot1() -> NamedStorageSlot { + NamedStorageSlot::with_value(MOCK_VALUE_SLOT1.clone(), STORAGE_VALUE_1) } - pub fn mock_item_2() -> SlotWithIndex { - SlotWithIndex { - slot: StorageSlot::Map(Self::mock_map()), - index: STORAGE_INDEX_2, - } + pub fn mock_map_slot() -> NamedStorageSlot { + NamedStorageSlot::with_map(MOCK_MAP_SLOT.clone(), Self::mock_map()) } pub fn mock_map() -> StorageMap { diff --git a/crates/miden-objects/src/transaction/inputs/account.rs b/crates/miden-objects/src/transaction/inputs/account.rs index 83d0cb7488..4d7dd2faf9 100644 --- a/crates/miden-objects/src/transaction/inputs/account.rs +++ b/crates/miden-objects/src/transaction/inputs/account.rs @@ -112,7 +112,7 @@ mod tests { let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); let code = AccountCode::mock(); let vault = AssetVault::new(&[]).unwrap(); - let storage = AccountStorage::new_named(vec![]).unwrap(); + let storage = AccountStorage::new(vec![]).unwrap(); let account = Account::new_existing(id, vault, storage, code, Felt::new(10)); let commitment = account.commitment(); diff --git a/crates/miden-objects/src/transaction/proven_tx.rs b/crates/miden-objects/src/transaction/proven_tx.rs index 3b002c80c9..0dc069251d 100644 --- a/crates/miden-objects/src/transaction/proven_tx.rs +++ b/crates/miden-objects/src/transaction/proven_tx.rs @@ -700,6 +700,7 @@ mod tests { AccountStorageMode, AccountType, AccountVaultDelta, + SlotName, StorageMapDelta, }; use crate::asset::FungibleAsset; @@ -776,7 +777,8 @@ mod tests { let storage_delta = StorageMapDelta::new(map); // A delta that exceeds the limit returns an error. - let storage_delta = AccountStorageDelta::from_iters([], [], [(4, storage_delta)]); + let storage_delta = + AccountStorageDelta::from_iters([], [], [(SlotName::mock(4), storage_delta)]); let delta = AccountDelta::new(account_id, storage_delta, AccountVaultDelta::default(), ONE) .unwrap(); let details = AccountUpdateDetails::Delta(delta); diff --git a/crates/miden-testing/Cargo.toml b/crates/miden-testing/Cargo.toml index 56b578d0fc..add802c7f4 100644 --- a/crates/miden-testing/Cargo.toml +++ b/crates/miden-testing/Cargo.toml @@ -13,7 +13,7 @@ rust-version.workspace = true version.workspace = true [features] -std = ["miden-lib/std"] +std = ["miden-lib/std", "miden-objects/std", "miden-tx/std"] [dependencies] # Workspace dependencies diff --git a/crates/miden-testing/src/kernel_tests/block/header_errors.rs b/crates/miden-testing/src/kernel_tests/block/header_errors.rs index e9125333f0..3d38d592b3 100644 --- a/crates/miden-testing/src/kernel_tests/block/header_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/header_errors.rs @@ -6,7 +6,14 @@ use miden_lib::block::build_block; use miden_lib::testing::account_component::{IncrNonceAuthComponent, MockAccountComponent}; use miden_lib::testing::mock_account::MockAccountExt; use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{Account, AccountBuilder, AccountComponent, AccountId, StorageSlot}; +use miden_objects::account::{ + Account, + AccountBuilder, + AccountComponent, + AccountId, + NamedStorageSlot, + SlotName, +}; use miden_objects::asset::FungibleAsset; use miden_objects::batch::ProvenBatch; use miden_objects::block::{BlockInputs, BlockNumber, ProposedBlock}; @@ -252,9 +259,10 @@ async fn block_building_fails_on_creating_account_with_existing_account_id_prefi let account = AccountBuilder::new([5; 32]) .with_auth_component(auth_component.clone()) - .with_component(MockAccountComponent::with_slots(vec![StorageSlot::Value(Word::from( - [5u32; 4], - ))])) + .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( + SlotName::new("miden::test_slot")?, + Word::from([5u32; 4]), + )])) .build() .context("failed to build account")?; @@ -343,9 +351,10 @@ async fn block_building_fails_on_creating_account_with_duplicate_account_id_pref let mock_chain = MockChain::new(); let account = AccountBuilder::new([5; 32]) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![StorageSlot::Value(Word::from( - [5u32; 4], - ))])) + .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( + SlotName::new("miden::test_slot")?, + Word::from([5u32; 4]), + )])) .build() .context("failed to build account")?; diff --git a/crates/miden-testing/src/kernel_tests/tx/mod.rs b/crates/miden-testing/src/kernel_tests/tx/mod.rs index 781bae46dc..3db274a4a6 100644 --- a/crates/miden-testing/src/kernel_tests/tx/mod.rs +++ b/crates/miden-testing/src/kernel_tests/tx/mod.rs @@ -59,6 +59,8 @@ pub trait ExecutionOutputExt { /// Reads an element from transaction kernel memory or returns [`ZERO`] if that location is not /// initialized. + // Unused for now, but may become useful in the future. + #[allow(dead_code)] fn get_kernel_mem_element(&self, addr: u32) -> Felt { // TODO: Use Memory::read_element once it no longer requires &mut self. // https://github.com/0xMiden/miden-vm/issues/2237 diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 210d2f4f92..30a7d85ea7 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -10,8 +10,6 @@ use miden_lib::errors::tx_kernel_errors::{ ERR_ACCOUNT_ID_UNKNOWN_VERSION, ERR_ACCOUNT_NONCE_AT_MAX, ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE, - ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS, - ERR_FAUCET_INVALID_STORAGE_OFFSET, }; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::mock_account::MockAccountExt; @@ -24,18 +22,18 @@ use miden_objects::account::{ AccountCode, AccountComponent, AccountId, - AccountIdVersion, - AccountProcedureInfo, AccountStorage, AccountStorageMode, AccountType, + NamedStorageSlot, + SlotName, StorageMap, StorageSlot, StorageSlotType, }; +use miden_objects::assembly::DefaultSourceManager; use miden_objects::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette}; -use miden_objects::assembly::{DefaultSourceManager, Library}; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset}; +use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::note::NoteType; use miden_objects::testing::account_id::{ ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET, @@ -45,11 +43,11 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::storage::STORAGE_LEAVES_2; -use miden_objects::transaction::{ExecutedTransaction, OutputNote, TransactionScript}; +use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; +use miden_objects::transaction::OutputNote; use miden_objects::{LexicographicWord, StarkField}; -use miden_processor::{EMPTY_WORD, ExecutionError, MastNodeExt, Word}; -use miden_tx::{LocalTransactionProver, TransactionExecutorError}; +use miden_processor::{ExecutionError, Word}; +use miden_tx::LocalTransactionProver; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; use winter_rand_utils::rand_value; @@ -63,7 +61,6 @@ use crate::{ MockChain, TransactionContextBuilder, TxContextInput, - assert_execution_error, assert_transaction_executor_error, }; @@ -78,7 +75,8 @@ pub async fn compute_commitment() -> miette::Result<()> { let mut account_clone = account.clone(); let key = Word::from([1, 2, 3, 4u32]); let value = Word::from([2, 3, 4, 5u32]); - account_clone.storage_mut().set_map_item(2, key, value).unwrap(); + let mock_map_slot = &*MOCK_MAP_SLOT; + account_clone.storage_mut().set_map_item(mock_map_slot, key, value).unwrap(); let expected_commitment = account_clone.commitment(); let tx_script = format!( @@ -89,6 +87,8 @@ pub async fn compute_commitment() -> miette::Result<()> { use.miden::active_account use.mock::account->mock_account + const MOCK_MAP_SLOT = word("{mock_map_slot}") + begin exec.active_account::get_initial_commitment # => [INITIAL_COMMITMENT] @@ -108,8 +108,8 @@ pub async fn compute_commitment() -> miette::Result<()> { padw push.0.0.0 push.{value} push.{key} - push.2 - # => [slot_idx = 2, KEY, VALUE, pad(7)] + push.MOCK_MAP_SLOT[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE, pad(7)] call.mock_account::set_map_item dropw dropw dropw dropw # => [STORAGE_COMMITMENT0] @@ -375,28 +375,31 @@ pub async fn test_compute_code_commitment() -> miette::Result<()> { #[tokio::test] async fn test_get_item() -> miette::Result<()> { - for storage_item in [AccountStorage::mock_item_0(), AccountStorage::mock_item_1()] { + for storage_item in [AccountStorage::mock_value_slot0(), AccountStorage::mock_value_slot1()] { let tx_context = TransactionContextBuilder::with_existing_mock_account().build().unwrap(); let code = format!( - " + r#" use.$kernel::account use.$kernel::prologue + const.SLOT_NAME = word("{slot_name}") + begin exec.prologue::prepare_transaction # push the account storage item index - push.{item_index} + push.SLOT_NAME[0..2] + # => [name_id_prefix, name_id_suffix] # assert the item value is correct exec.account::get_item push.{item_value} - assert_eqw + assert_eqw.err="expected item to have value {item_value}" end - ", - item_index = storage_item.index, - item_value = &storage_item.slot.value(), + "#, + slot_name = storage_item.name(), + item_value = &storage_item.storage_slot().value(), ); tx_context.execute_code(&code).await.unwrap(); @@ -407,56 +410,46 @@ async fn test_get_item() -> miette::Result<()> { #[tokio::test] async fn test_get_map_item() -> miette::Result<()> { + let named_slot = AccountStorage::mock_map_slot(); let account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![AccountStorage::mock_item_2().slot])) + .with_component(MockAccountComponent::with_slots(vec![named_slot.clone()])) .build_existing() .unwrap(); let tx_context = TransactionContextBuilder::new(account).build().unwrap(); - for (key, value) in STORAGE_LEAVES_2 { + let StorageSlot::Map(map) = named_slot.storage_slot() else { + panic!("expected map") + }; + + for (key, expected_value) in map.entries() { let code = format!( - " + r#" use.$kernel::prologue + use.mock::account + + const SLOT_NAME = word("{slot_name}") begin exec.prologue::prepare_transaction # get the map item - push.{map_key} - push.{item_index} - call.::mock::account::get_map_item + push.{key} + push.SLOT_NAME[0..2] + call.account::get_map_item + # => [VALUE] - # truncate the stack - swapw dropw movup.4 drop + push.{expected_value} + assert_eqw.err="value did not match {expected_value}" + + exec.::std::sys::truncate_stack end - ", - item_index = 0, - map_key = &key, + "#, + slot_name = named_slot.name(), ); - let exec_output = &mut tx_context.execute_code(&code).await?; - assert_eq!( - exec_output.get_stack_word_be(0), - value, - "get_map_item result doesn't match the expected value", - ); - assert_eq!( - exec_output.get_stack_word_be(4), - Word::empty(), - "The rest of the stack must be cleared", - ); - assert_eq!( - exec_output.get_stack_word_be(8), - Word::empty(), - "The rest of the stack must be cleared", - ); - assert_eq!( - exec_output.get_stack_word_be(12), - Word::empty(), - "The rest of the stack must be cleared", - ); + tx_context.execute_code(&code).await?; } Ok(()) @@ -464,12 +457,20 @@ async fn test_get_map_item() -> miette::Result<()> { #[tokio::test] async fn test_get_storage_slot_type() -> miette::Result<()> { - for storage_item in [ - AccountStorage::mock_item_0(), - AccountStorage::mock_item_1(), - AccountStorage::mock_item_2(), + for slot_name in [ + AccountStorage::mock_value_slot0().name(), + AccountStorage::mock_value_slot1().name(), + AccountStorage::mock_map_slot().name(), ] { let tx_context = TransactionContextBuilder::with_existing_mock_account().build().unwrap(); + let (slot_idx, slot) = tx_context + .account() + .storage() + .slots() + .iter() + .enumerate() + .find(|(_, slot)| slot.name() == slot_name) + .unwrap(); let code = format!( " @@ -479,25 +480,22 @@ async fn test_get_storage_slot_type() -> miette::Result<()> { begin exec.prologue::prepare_transaction - # push the account storage item index - push.{item_index} + # push the account storage slot index + push.{slot_idx} # get the type of the respective storage slot - exec.account::get_storage_slot_type_by_index + exec.account::get_storage_slot_type # truncate the stack swap drop end ", - item_index = storage_item.index, ); let exec_output = &tx_context.execute_code(&code).await.unwrap(); - let storage_slot_type = storage_item.slot.slot_type(); - assert_eq!( - storage_slot_type, + slot.slot_type(), StorageSlotType::try_from( u8::try_from(exec_output.get_stack_element(0).as_int()).unwrap() ) @@ -527,39 +525,46 @@ async fn test_get_storage_slot_type() -> miette::Result<()> { } #[tokio::test] -async fn test_set_item() -> miette::Result<()> { +async fn test_set_item() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build().unwrap(); - let new_storage_item = Word::from([91, 92, 93, 94u32]); + let slot_name = &*MOCK_VALUE_SLOT0; + let new_value = Word::from([91, 92, 93, 94u32]); + let old_value = tx_context.account().storage().get_item(slot_name)?; let code = format!( - " + r#" use.$kernel::account use.$kernel::prologue + const.MOCK_VALUE_SLOT0 = word("{slot_name}") + begin exec.prologue::prepare_transaction # set the storage item - push.{new_storage_item} - push.{new_storage_item_index} + push.{new_value} + push.MOCK_VALUE_SLOT0[0..2] + # => [name_id_prefix, name_id_suffix, NEW_VALUE] + exec.account::set_item # assert old value was correctly returned - push.1.2.3.4 assert_eqw + push.{old_value} + assert_eqw.err="old value did not match" # assert new value has been correctly set - push.{new_storage_item_index} + push.MOCK_VALUE_SLOT0[0..2] + # => [name_id_prefix, name_id_suffix] + exec.account::get_item - push.{new_storage_item} + push.{new_value} assert_eqw end - ", - new_storage_item = &new_storage_item, - new_storage_item_index = 0, + "#, ); - tx_context.execute_code(&code).await.unwrap(); + tx_context.execute_code(&code).await?; Ok(()) } @@ -569,45 +574,57 @@ async fn test_set_map_item() -> miette::Result<()> { let (new_key, new_value) = (Word::from([109, 110, 111, 112u32]), Word::from([9, 10, 11, 12u32])); + let named_slot = AccountStorage::mock_map_slot(); let account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![AccountStorage::mock_item_2().slot])) + .with_component(MockAccountComponent::with_slots(vec![named_slot.clone()])) .build_existing() .unwrap(); let tx_context = TransactionContextBuilder::new(account).build().unwrap(); - let storage_item = AccountStorage::mock_item_2(); let code = format!( - " + r#" use.std::sys use.$kernel::prologue use.mock::account->mock_account + const.SLOT_NAME=word("{slot_name}") + begin exec.prologue::prepare_transaction # set the map item push.{new_value} push.{new_key} - push.{item_index} + push.SLOT_NAME[0..2] call.mock_account::set_map_item - # double check that on storage slot is indeed the new map - push.{item_index} + # double check that the storage slot is indeed the new map + push.SLOT_NAME[0..2] + # => [name_id_prefix, name_id_suffix, OLD_VALUE, OLD_MAP_ROOT] + + # pad the stack + repeat.14 push.0 movdn.2 end + # => [name_id_prefix, name_id_suffix, pad(14), OLD_VALUE, OLD_MAP_ROOT] + call.mock_account::get_item + # => [MAP_ROOT, pad(12), OLD_VALUE, OLD_MAP_ROOT] # truncate the stack + repeat.3 swapw dropw end + # => [MAP_ROOT, OLD_VALUE, OLD_MAP_ROOT] + exec.sys::truncate_stack end - ", - item_index = 0, + "#, + slot_name = named_slot.name(), new_key = &new_key, new_value = &new_value, ); - let exec_output = &tx_context.execute_code(&code).await.unwrap(); + let exec_output = &tx_context.execute_code(&code).await?; let mut new_storage_map = AccountStorage::mock_map(); new_storage_map.insert(new_key, new_value).unwrap(); @@ -615,166 +632,13 @@ async fn test_set_map_item() -> miette::Result<()> { assert_eq!( new_storage_map.root(), exec_output.get_stack_word_be(0), - "get_item must return the new updated value", + "get_item should return the updated root", ); assert_eq!( - storage_item.slot.value(), + named_slot.storage_slot().value(), exec_output.get_stack_word_be(4), - "The original value stored in the map doesn't match the expected value", - ); - - Ok(()) -} - -#[tokio::test] -async fn test_account_component_storage_offset() -> miette::Result<()> { - // setup assembler - let assembler = - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())); - - // The following code will execute the following logic that will be asserted during the test: - // - // 1. foo_write will set word [1, 2, 3, 4] in storage at location 0 (0 offset by 0) - // 2. foo_read will read word [1, 2, 3, 4] in storage from location 0 (0 offset by 0) - // 3. bar_write will set word [5, 6, 7, 8] in storage at location 1 (0 offset by 1) - // 4. bar_read will read word [5, 6, 7, 8] in storage from location 1 (0 offset by 1) - // - // We will then assert that we are able to retrieve the correct elements from storage - // insuring consistent "set" and "get" using offsets. - let source_code_component1 = " - use.std::word - use.miden::active_account - use.miden::native_account - - export.foo_write - push.1.2.3.4.0 - exec.native_account::set_item - - dropw - end - - export.foo_read - push.0 - exec.active_account::get_item - push.1.2.3.4 - - exec.word::eq assert - end - "; - - let source_code_component2 = " - use.std::word - use.miden::active_account - use.miden::native_account - - export.bar_write - push.5.6.7.8.0 - exec.native_account::set_item - - dropw - end - - export.bar_read - push.0 - exec.active_account::get_item - push.5.6.7.8 - - exec.word::eq assert - end - "; - - // Compile source code to find MAST roots of procedures. - let code1 = assembler.clone().assemble_library([source_code_component1]).unwrap(); - let code2 = assembler.clone().assemble_library([source_code_component2]).unwrap(); - let find_procedure_digest_by_name = |name: &str, lib: &Library| { - lib.exports().find_map(|export| { - if export.name.name.as_str() == name { - Some(lib.mast_forest()[lib.get_export_node_id(&export.name)].digest()) - } else { - None - } - }) - }; - - let foo_write = find_procedure_digest_by_name("foo_write", &code1).unwrap(); - let foo_read = find_procedure_digest_by_name("foo_read", &code1).unwrap(); - let bar_write = find_procedure_digest_by_name("bar_write", &code2).unwrap(); - let bar_read = find_procedure_digest_by_name("bar_read", &code2).unwrap(); - - // Compile source code into components. - let component1 = AccountComponent::compile( - source_code_component1, - assembler.clone(), - vec![StorageSlot::Value(Word::empty())], - ) - .unwrap() - .with_supported_type(AccountType::RegularAccountUpdatableCode); - - let component2 = AccountComponent::compile( - source_code_component2, - assembler.clone(), - vec![StorageSlot::Value(Word::empty())], - ) - .unwrap() - .with_supported_type(AccountType::RegularAccountUpdatableCode); - - let mut account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) - .with_auth_component(Auth::IncrNonce) - .with_component(component1) - .with_component(component2) - .build_existing() - .unwrap(); - - // Assert that the storage offset and size have been set correctly. - for (procedure_digest, expected_offset, expected_size) in - [(foo_write, 0, 1), (foo_read, 0, 1), (bar_write, 1, 1), (bar_read, 1, 1)] - { - let procedure_info = account - .code() - .procedures() - .iter() - .find(|proc| proc.mast_root() == &procedure_digest) - .unwrap(); - assert_eq!( - procedure_info.storage_offset(), - expected_offset, - "failed for procedure {procedure_digest}" - ); - assert_eq!( - procedure_info.storage_size(), - expected_size, - "failed for procedure {procedure_digest}" - ); - } - - // setup transaction script - let tx_script_source_code = format!( - " - begin - call.{foo_write} - call.{foo_read} - call.{bar_write} - call.{bar_read} - end - " + "get_item must return the new updated value", ); - let tx_script_program = assembler.assemble_program(tx_script_source_code).unwrap(); - let tx_script = TransactionScript::new(tx_script_program); - - // setup transaction context - let tx_context = TransactionContextBuilder::new(account.clone()) - .tx_script(tx_script) - .build() - .unwrap(); - - // execute code in context - let tx = tx_context.execute().await.into_diagnostic()?; - account.apply_delta(tx.account_delta()).unwrap(); - - // assert that elements have been set at the correct locations in storage - assert_eq!(account.storage().get_item(0).unwrap(), Word::from([1, 2, 3, 4u32])); - - assert_eq!(account.storage().get_item(1).unwrap(), Word::from([5, 6, 7, 8u32])); Ok(()) } @@ -796,117 +660,6 @@ async fn create_account_with_empty_storage_slots() -> anyhow::Result<()> { Ok(()) } -async fn create_procedure_metadata_test_account( - account_type: AccountType, - storage_offset: u8, - storage_size: u8, -) -> anyhow::Result> { - let mock_chain = MockChain::new(); - - let version = AccountIdVersion::Version0; - - let mock_code = AccountCode::mock(); - let code = AccountCode::from_parts( - mock_code.mast(), - mock_code - .mast() - .procedure_digests() - .map(|mast_root| { - AccountProcedureInfo::new(mast_root, storage_offset, storage_size).unwrap() - }) - .collect(), - ); - - let storage = AccountStorage::new(vec![StorageSlot::Value(EMPTY_WORD)]).unwrap(); - - let seed = AccountId::compute_account_seed( - [9; 32], - account_type, - AccountStorageMode::Private, - version, - code.commitment(), - storage.to_commitment(), - ) - .context("failed to compute seed")?; - let id = AccountId::new(seed, version, code.commitment(), storage.to_commitment()) - .context("failed to compute ID")?; - - let account = - Account::new(id, AssetVault::default(), storage, code, Felt::from(0u32), Some(seed))?; - - let tx_inputs = mock_chain.get_transaction_inputs(&account, &[], &[])?; - let tx_context = TransactionContextBuilder::new(account).tx_inputs(tx_inputs).build()?; - - let result = tx_context.execute().await.map_err(|err| { - let TransactionExecutorError::TransactionProgramExecutionFailed(exec_err) = err else { - panic!("should have received an execution error"); - }; - - exec_err - }); - - Ok(result) -} - -/// Tests that creating an account whose procedure accesses the reserved faucet storage slot fails. -#[tokio::test] -async fn creating_faucet_account_with_procedure_accessing_reserved_slot_fails() -> anyhow::Result<()> -{ - // Set offset to 0 for a faucet which should be disallowed. - let execution_res = create_procedure_metadata_test_account(AccountType::FungibleFaucet, 0, 1) - .await - .context("failed to create test account")?; - - assert_execution_error!(execution_res, ERR_FAUCET_INVALID_STORAGE_OFFSET); - - Ok(()) -} - -/// Tests that creating a faucet whose procedure offset+size is out of bounds fails. -#[tokio::test] -async fn creating_faucet_with_procedure_offset_plus_size_out_of_bounds_fails() -> anyhow::Result<()> -{ - // Set offset to lowest allowed value 1 and size to 1 while number of slots is 1 which should - // result in an out of bounds error. - let execution_res = create_procedure_metadata_test_account(AccountType::FungibleFaucet, 1, 1) - .await - .context("failed to create test account")?; - - assert_execution_error!(execution_res, ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS); - - // Set offset to 2 while number of slots is 1 which should result in an out of bounds error. - let execution_res = create_procedure_metadata_test_account(AccountType::FungibleFaucet, 2, 1) - .await - .context("failed to create test account")?; - - assert_execution_error!(execution_res, ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS); - - Ok(()) -} - -/// Tests that creating an account whose procedure offset+size is out of bounds fails. -#[tokio::test] -async fn creating_account_with_procedure_offset_plus_size_out_of_bounds_fails() -> anyhow::Result<()> -{ - // Set size to 2 while number of slots is 1 which should result in an out of bounds error. - let execution_res = - create_procedure_metadata_test_account(AccountType::RegularAccountImmutableCode, 0, 2) - .await - .context("failed to create test account")?; - - assert_execution_error!(execution_res, ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS); - - // Set offset to 2 while number of slots is 1 which should result in an out of bounds error. - let execution_res = - create_procedure_metadata_test_account(AccountType::RegularAccountImmutableCode, 2, 1) - .await - .context("failed to create test account")?; - - assert_execution_error!(execution_res, ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS); - - Ok(()) -} - #[tokio::test] async fn test_get_initial_storage_commitment() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; @@ -949,15 +702,18 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { let init_storage_commitment = account_storage.to_commitment(); - account_storage.set_item(0, [9, 10, 11, 12].map(Felt::new).into())?; - let storage_commitment_0 = account_storage.to_commitment(); + let mock_value_slot0 = &*MOCK_VALUE_SLOT0; + let mock_map_slot = &*MOCK_MAP_SLOT; + + account_storage.set_item(mock_value_slot0, [9, 10, 11, 12].map(Felt::new).into())?; + let storage_commitment_value = account_storage.to_commitment(); account_storage.set_map_item( - 2, + mock_map_slot, [101, 102, 103, 104].map(Felt::new).into(), [5, 6, 7, 8].map(Felt::new).into(), )?; - let storage_commitment_2 = account_storage.to_commitment(); + let storage_commitment_map = account_storage.to_commitment(); let code = format!( r#" @@ -965,6 +721,9 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { use.$kernel::prologue use.mock::account->mock_account + const.MOCK_VALUE_SLOT0=word("{mock_value_slot0}") + const.MOCK_MAP_SLOT=word("{mock_map_slot}") + begin exec.prologue::prepare_transaction @@ -973,34 +732,39 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { push.{init_storage_commitment} assert_eqw.err="storage commitment at the beginning of the transaction is not equal to the expected one" - # update the 0th (value) storage slot - push.9.10.11.12.0 + # update the value storage slot + push.9.10.11.12 + push.MOCK_VALUE_SLOT0[0..2] call.mock_account::set_item dropw drop # => [] - # assert the correctness of the storage commitment after the 0th slot was updated + # assert the correctness of the storage commitment after the value slot was updated call.mock_account::compute_storage_commitment - push.{storage_commitment_0} - assert_eqw.err="storage commitment after the 0th slot was updated is not equal to the expected one" + push.{storage_commitment_value} + assert_eqw.err="storage commitment after the value slot was updated is not equal to the expected one" # get the storage commitment once more to get the cached data and assert that this data # didn't change call.mock_account::compute_storage_commitment - push.{storage_commitment_0} + push.{storage_commitment_value} assert_eqw.err="storage commitment should remain the same" - # update the 2nd (map) storage slot - push.5.6.7.8.101.102.103.104.2 # [idx, KEY, VALUE] + # update the map storage slot + push.5.6.7.8.101.102.103.104 + push.MOCK_MAP_SLOT[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] + call.mock_account::set_map_item dropw dropw # => [] - # assert the correctness of the storage commitment after the 2nd slot was updated + # assert the correctness of the storage commitment after the map slot was updated call.mock_account::compute_storage_commitment - push.{storage_commitment_2} - assert_eqw.err="storage commitment after the 2nd slot was updated is not equal to the expected one" + push.{storage_commitment_map} + assert_eqw.err="storage commitment after the map slot was updated is not equal to the expected one" end "#, ); + tx_context.execute_code(&code).await?; Ok(()) @@ -1012,13 +776,20 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { /// accounts. #[tokio::test] async fn prove_account_creation_with_non_empty_storage() -> anyhow::Result<()> { - let slot0 = StorageSlot::Value(Word::from([1, 2, 3, 4u32])); - let slot1 = StorageSlot::Value(Word::from([10, 20, 30, 40u32])); + let slot_name0 = SlotName::mock(0); + let slot_name1 = SlotName::mock(1); + let slot_name2 = SlotName::mock(2); + + let slot0 = NamedStorageSlot::with_value(slot_name0.clone(), Word::from([1, 2, 3, 4u32])); + let slot1 = NamedStorageSlot::with_value(slot_name1.clone(), Word::from([10, 20, 30, 40u32])); let mut map_entries = Vec::new(); for _ in 0..10 { map_entries.push((rand_value::(), rand_value::())); } - let map_slot = StorageSlot::Map(StorageMap::with_entries(map_entries.clone())?); + let map_slot = NamedStorageSlot::with_map( + slot_name2.clone(), + StorageMap::with_entries(map_entries.clone())?, + ); let account = AccountBuilder::new([6; 32]) .storage_mode(AccountStorageMode::Public) @@ -1038,11 +809,11 @@ async fn prove_account_creation_with_non_empty_storage() -> anyhow::Result<()> { assert_eq!(tx.account_delta().nonce_delta(), Felt::new(1)); - assert_eq!(tx.account_delta().storage().values().get(&0).unwrap(), &slot0.value()); - assert_eq!(tx.account_delta().storage().values().get(&1).unwrap(), &slot1.value()); + assert_eq!(tx.account_delta().storage().values().get(&slot_name0).unwrap(), &slot0.value()); + assert_eq!(tx.account_delta().storage().values().get(&slot_name1).unwrap(), &slot1.value()); assert_eq!( - tx.account_delta().storage().maps().get(&2).unwrap().entries(), + tx.account_delta().storage().maps().get(&slot_name2).unwrap().entries(), &BTreeMap::from_iter( map_entries .into_iter() @@ -1135,7 +906,7 @@ async fn test_get_vault_root() -> anyhow::Result<()> { /// in two cases: /// - when a note adds the asset which already exists in the account vault. /// - when a note adds the asset which doesn't exist in the account vault. -/// +/// /// As part of the test pipeline it also checks the correctness of the /// `miden::active_account::get_balance` procedure. #[tokio::test] @@ -1462,6 +1233,7 @@ async fn test_was_procedure_called() -> miette::Result<()> { .with_component(mock_component) .build_existing() .unwrap(); + let mock_value_slot1 = &*MOCK_VALUE_SLOT1; // Create a transaction script that: // 1. Checks that get_item hasn't been called yet @@ -1469,10 +1241,13 @@ async fn test_was_procedure_called() -> miette::Result<()> { // 3. Checks that get_item has been called // 4. Calls get_item **again** // 5. Checks that `was_procedure_called` returns `true` - let tx_script_code = r#" + let tx_script_code = format!( + r#" use.mock::account->mock_account use.miden::native_account + const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") + begin # First check that get_item procedure hasn't been called yet procref.mock_account::get_item @@ -1480,7 +1255,7 @@ async fn test_was_procedure_called() -> miette::Result<()> { assertz.err="procedure should not have been called" # Call the procedure first time - push.0 + push.MOCK_VALUE_SLOT1[0..2] call.mock_account::get_item dropw # => [] @@ -1489,14 +1264,15 @@ async fn test_was_procedure_called() -> miette::Result<()> { assert.err="procedure should have been called" # Call the procedure second time - push.0 + push.MOCK_VALUE_SLOT1[0..2] call.mock_account::get_item dropw procref.mock_account::get_item exec.native_account::was_procedure_called assert.err="2nd call should not change the was_called flag" end - "#; + "# + ); // Compile the transaction script using the testing assembler with mock account let tx_script = ScriptBuilder::with_mock_libraries() @@ -1523,15 +1299,20 @@ async fn test_was_procedure_called() -> miette::Result<()> { /// `tx script -> account code -> external library` #[tokio::test] async fn transaction_executor_account_code_using_custom_library() -> miette::Result<()> { - const EXTERNAL_LIBRARY_CODE: &str = r#" + let external_library_code = format!( + r#" use.miden::native_account + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + export.external_setter push.2.3.4.5 - push.0 + push.MOCK_VALUE_SLOT0[0..2] exec.native_account::set_item dropw dropw - end"#; + end"#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, + ); const ACCOUNT_COMPONENT_CODE: &str = " use.external_library::external_module @@ -1541,7 +1322,7 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res end"; let external_library_source = - NamedSource::new("external_library::external_module", EXTERNAL_LIBRARY_CODE); + NamedSource::new("external_library::external_module", external_library_code); let external_library = TransactionKernel::assembler().assemble_library([external_library_source])?; @@ -1592,7 +1373,7 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res // Make sure that account storage has been updated as per the tx script call. assert_eq!( *executed_tx.account_delta().storage().values(), - BTreeMap::from([(0, Word::from([2, 3, 4, 5u32]))]), + BTreeMap::from([(MOCK_VALUE_SLOT0.clone(), Word::from([2, 3, 4, 5u32]))]), ); Ok(()) } @@ -1688,106 +1469,118 @@ async fn test_get_initial_item() -> miette::Result<()> { // Test that get_initial_item returns the initial value before any changes let code = format!( - " + r#" use.$kernel::account use.$kernel::prologue use.mock::account->mock_account + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin exec.prologue::prepare_transaction - # get initial value of storage slot 0 - push.0 + # get initial value of the storage slot + push.MOCK_VALUE_SLOT0[0..2] exec.account::get_initial_item push.{expected_initial_value} - assert_eqw.err=\"initial value should match expected\" + assert_eqw.err="initial value should match expected" # modify the storage slot - push.9.10.11.12.0 - call.mock_account::set_item dropw drop + push.9.10.11.12 + push.MOCK_VALUE_SLOT0[0..2] + call.mock_account::set_item dropw drop drop # get_item should return the new value - push.0 + push.MOCK_VALUE_SLOT0[0..2] exec.account::get_item push.9.10.11.12 - assert_eqw.err=\"current value should be updated\" + assert_eqw.err="current value should be updated" # get_initial_item should still return the initial value - push.0 + push.MOCK_VALUE_SLOT0[0..2] exec.account::get_initial_item push.{expected_initial_value} - assert_eqw.err=\"initial value should remain unchanged\" + assert_eqw.err="initial value should remain unchanged" end - ", - expected_initial_value = &AccountStorage::mock_item_0().slot.value(), + "#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, + expected_initial_value = &AccountStorage::mock_value_slot0().storage_slot().value(), ); - tx_context.execute_code(&code).await.unwrap(); + tx_context.execute_code(&code).await?; Ok(()) } #[tokio::test] async fn test_get_initial_map_item() -> miette::Result<()> { + let map_slot = AccountStorage::mock_map_slot(); let account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![AccountStorage::mock_item_2().slot])) + .with_component(MockAccountComponent::with_slots(vec![map_slot.clone()])) .build_existing() .unwrap(); let tx_context = TransactionContextBuilder::new(account).build().unwrap(); // Use the first key-value pair from the mock storage - let (initial_key, initial_value) = STORAGE_LEAVES_2[0]; + let StorageSlot::Map(map) = map_slot.storage_slot() else { + panic!("expected map"); + }; + + let (initial_key, initial_value) = map.entries().next().unwrap(); let new_key = Word::from([201, 202, 203, 204u32]); let new_value = Word::from([301, 302, 303, 304u32]); + let mock_map_slot = map_slot.name(); let code = format!( - " + r#" use.$kernel::prologue use.mock::account->mock_account + const MOCK_MAP_SLOT = word("{mock_map_slot}") + begin exec.prologue::prepare_transaction # get initial value from map push.{initial_key} - push.0 + push.MOCK_MAP_SLOT[0..2] call.mock_account::get_initial_map_item push.{initial_value} - assert_eqw.err=\"initial map value should match expected\" + assert_eqw.err="initial map value should match expected" # add a new key-value pair to the map push.{new_value} push.{new_key} - push.0 + push.MOCK_MAP_SLOT[0..2] call.mock_account::set_map_item dropw dropw # get_map_item should return the new value push.{new_key} - push.0 + push.MOCK_MAP_SLOT[0..2] call.mock_account::get_map_item push.{new_value} - assert_eqw.err=\"current map value should be updated\" + assert_eqw.err="current map value should be updated" # get_initial_map_item should still return the initial value for the initial key push.{initial_key} - push.0 + push.MOCK_MAP_SLOT[0..2] call.mock_account::get_initial_map_item push.{initial_value} - assert_eqw.err=\"initial map value should remain unchanged\" + assert_eqw.err="initial map value should remain unchanged" # get_initial_map_item for the new key should return empty word (default) push.{new_key} - push.0 + push.MOCK_MAP_SLOT[0..2] call.mock_account::get_initial_map_item padw - assert_eqw.err=\"new key should have empty initial value\" + assert_eqw.err="new key should have empty initial value" - dropw dropw + dropw dropw dropw end - ", + "#, initial_key = &initial_key, initial_value = &initial_value, new_key = &new_key, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index 503d074266..58b30d27b2 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -14,8 +14,9 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, + NamedStorageSlot, + SlotName, StorageMap, - StorageSlot, }; use miden_objects::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; use miden_objects::note::{Note, NoteExecutionHint, NoteTag, NoteType}; @@ -35,7 +36,7 @@ use miden_objects::testing::constants::{ NON_FUNGIBLE_ASSET_DATA, NON_FUNGIBLE_ASSET_DATA_2, }; -use miden_objects::testing::storage::{STORAGE_INDEX_0, STORAGE_INDEX_2}; +use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0}; use miden_objects::transaction::TransactionScript; use miden_objects::{EMPTY_WORD, Felt, FieldElement, LexicographicWord, Word, ZERO}; use miden_tx::LocalTransactionProver; @@ -106,71 +107,80 @@ async fn delta_nonce() -> anyhow::Result<()> { /// - Slot 3: [1,3,5,7] -> [2,3,4,5] -> [1,3,5,7] -> Delta: None #[tokio::test] async fn storage_delta_for_value_slots() -> anyhow::Result<()> { + let slot_0_name = SlotName::mock(0); let slot_0_init_value = Word::from([2, 4, 6, 8u32]); let slot_0_tmp_value = Word::from([3, 4, 5, 6u32]); let slot_0_final_value = EMPTY_WORD; + let slot_1_name = SlotName::mock(1); let slot_1_init_value = EMPTY_WORD; let slot_1_final_value = Word::from([3, 4, 5, 6u32]); + let slot_2_name = SlotName::mock(2); let slot_2_init_value = Word::from([1, 3, 5, 7u32]); let slot_2_final_value = slot_2_init_value; + let slot_3_name = SlotName::mock(3); let slot_3_init_value = Word::from([1, 3, 5, 7u32]); let slot_3_tmp_value = Word::from([2, 3, 4, 5u32]); let slot_3_final_value = slot_3_init_value; let TestSetup { mock_chain, account_id, .. } = setup_test( vec![ - StorageSlot::Value(slot_0_init_value), - StorageSlot::Value(slot_1_init_value), - StorageSlot::Value(slot_2_init_value), - StorageSlot::Value(slot_3_init_value), + NamedStorageSlot::with_value(slot_0_name.clone(), slot_0_init_value), + NamedStorageSlot::with_value(slot_1_name.clone(), slot_1_init_value), + NamedStorageSlot::with_value(slot_2_name.clone(), slot_2_init_value), + NamedStorageSlot::with_value(slot_3_name.clone(), slot_3_init_value), ], [], [], )?; let tx_script = compile_tx_script(format!( - " + r#" + const SLOT_0_NAME = word("{slot_0_name}") + const SLOT_1_NAME = word("{slot_1_name}") + const SLOT_2_NAME = word("{slot_2_name}") + const SLOT_3_NAME = word("{slot_3_name}") + begin push.{slot_0_tmp_value} - push.0 - # => [index, VALUE] + push.SLOT_0_NAME[0..2] + # => [name_id_prefix, name_id_suffix, VALUE] exec.set_item # => [] push.{slot_0_final_value} - push.0 - # => [index, VALUE] + push.SLOT_0_NAME[0..2] + # => [name_id_prefix, name_id_suffix, VALUE] exec.set_item # => [] push.{slot_1_final_value} - push.1 - # => [index, VALUE] + push.SLOT_1_NAME[0..2] + # => [name_id_prefix, name_id_suffix, VALUE] exec.set_item # => [] push.{slot_2_final_value} - push.2 - # => [index, VALUE] + push.SLOT_2_NAME[0..2] + # => [name_id_prefix, name_id_suffix, VALUE] exec.set_item # => [] push.{slot_3_tmp_value} - push.3 - # => [index, VALUE] + push.SLOT_3_NAME[0..2] + # => [name_id_prefix, name_id_suffix, VALUE] exec.set_item # => [] push.{slot_3_final_value} - push.3 - # => [index, VALUE] + push.SLOT_3_NAME[0..2] + # => [name_id_prefix, name_id_suffix, VALUE] exec.set_item # => [] end - " + "# ))?; let executed_tx = mock_chain @@ -187,11 +197,14 @@ async fn storage_delta_for_value_slots() -> anyhow::Result<()> { .storage() .values() .iter() - .map(|(k, v)| (*k, *v)) - .collect::>(); + .map(|(slot_name, value)| (slot_name.clone(), *value)) + .collect::>(); // Note that slots 2 and 3 are absent because their values haven't effectively changed. - assert_eq!(storage_values_delta, &[(0u8, slot_0_final_value), (1u8, slot_1_final_value)]); + assert_eq!( + storage_values_delta, + BTreeMap::from_iter([(slot_0_name, slot_0_final_value), (slot_1_name, slot_1_final_value)]) + ); Ok(()) } @@ -234,81 +247,97 @@ async fn storage_delta_for_map_slots() -> anyhow::Result<()> { let key5_tmp_value = Word::from([2, 3, 4, 5u32]); let key5_final_value = Word::from([1, 2, 3, 4u32]); + let slot_0_name = SlotName::mock(0); let mut map0 = StorageMap::new(); map0.insert(key0, key0_init_value).unwrap(); map0.insert(key1, key1_init_value).unwrap(); + let slot_1_name = SlotName::mock(1); let mut map1 = StorageMap::new(); map1.insert(key2, key2_init_value).unwrap(); map1.insert(key3, key3_init_value).unwrap(); map1.insert(key4, key4_init_value).unwrap(); + let slot_2_name = SlotName::mock(2); let mut map2 = StorageMap::new(); map2.insert(key5, key5_init_value).unwrap(); let TestSetup { mock_chain, account_id, .. } = setup_test( vec![ - StorageSlot::Map(map0), - StorageSlot::Map(map1), - StorageSlot::Map(map2), + NamedStorageSlot::with_map(slot_0_name.clone(), map0), + NamedStorageSlot::with_map(slot_1_name.clone(), map1), + NamedStorageSlot::with_map(slot_2_name.clone(), map2), // Include an empty map which does not receive any updates, to test that the "metadata // header" in the delta commitment is not appended if there are no updates to a map // slot. - StorageSlot::Map(StorageMap::new()), + NamedStorageSlot::with_map(SlotName::mock(3), StorageMap::new()), ], [], [], )?; let tx_script = compile_tx_script(format!( - " + r#" + const SLOT_0_NAME = word("{slot_0_name}") + const SLOT_1_NAME = word("{slot_1_name}") + const SLOT_2_NAME = word("{slot_2_name}") + begin - push.{key0_final_value} push.{key0} push.0 - # => [index, KEY, VALUE] + push.{key0_final_value} push.{key0} + push.SLOT_0_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] - push.{key1_tmp_value} push.{key1} push.0 - # => [index, KEY, VALUE] + push.{key1_tmp_value} push.{key1} + push.SLOT_0_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] - push.{key1_final_value} push.{key1} push.0 - # => [index, KEY, VALUE] + push.{key1_final_value} push.{key1} + push.SLOT_0_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] - push.{key2_final_value} push.{key2} push.1 - # => [index, KEY, VALUE] + push.{key2_final_value} push.{key2} + push.SLOT_1_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] - push.{key3_final_value} push.{key3} push.1 - # => [index, KEY, VALUE] + push.{key3_final_value} push.{key3} + push.SLOT_1_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] - push.{key4_tmp_value} push.{key4} push.1 - # => [index, KEY, VALUE] + push.{key4_tmp_value} push.{key4} + push.SLOT_1_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] - push.{key4_final_value} push.{key4} push.1 - # => [index, KEY, VALUE] + push.{key4_final_value} push.{key4} + push.SLOT_1_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] - push.{key5_tmp_value} push.{key5} push.2 - # => [index, KEY, VALUE] + push.{key5_tmp_value} push.{key5} + push.SLOT_2_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] - push.{key5_final_value} push.{key5} push.2 - # => [index, KEY, VALUE] + push.{key5_final_value} push.{key5} + push.SLOT_2_NAME[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] exec.set_map_item # => [] end - " + "# ))?; let executed_tx = mock_chain @@ -323,12 +352,18 @@ async fn storage_delta_for_map_slots() -> anyhow::Result<()> { // Note that there should be no delta for map2 since it was normalized to an empty map which // should be removed. assert_eq!(maps_delta.len(), 2); - assert!(maps_delta.get(&2).is_none(), "map2 should not have a delta"); - - let mut map0_delta = - maps_delta.get(&0).expect("delta for map 0 should exist").clone().into_map(); - let mut map1_delta = - maps_delta.get(&1).expect("delta for map 1 should exist").clone().into_map(); + assert!(maps_delta.get(&slot_2_name).is_none(), "map2 should not have a delta"); + + let mut map0_delta = maps_delta + .get(&slot_0_name) + .expect("delta for map 0 should exist") + .clone() + .into_map(); + let mut map1_delta = maps_delta + .get(&slot_1_name) + .expect("delta for map 1 should exist") + .clone() + .into_map(); assert_eq!(map0_delta.len(), 2); assert_eq!(map0_delta.remove(&LexicographicWord::new(key0)).unwrap(), key0_final_value); @@ -621,10 +656,13 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { } let tx_script_src = format!( - "\ + r#" use.mock::account use.miden::output_note + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + const MOCK_MAP_SLOT = word("{mock_map_slot}") + ## TRANSACTION SCRIPT ## ======================================================================================== begin @@ -635,8 +673,8 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { # => [13, 11, 9, 7] # get the index of account storage slot - push.{STORAGE_INDEX_0} - # => [idx, 13, 11, 9, 7] + push.MOCK_VALUE_SLOT0[0..2] + # => [name_id_prefix, name_id_suffix, 13, 11, 9, 7] # update the storage value call.account::set_item dropw # => [] @@ -652,8 +690,8 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { # => [14, 15, 16, 17, 18, 19, 20, 21] # get the index of account storage slot - push.{STORAGE_INDEX_2} - # => [idx, 14, 15, 16, 17, 18, 19, 20, 21] + push.MOCK_MAP_SLOT[0..2] + # => [name_id_prefix, name_id_suffix, 14, 15, 16, 17, 18, 19, 20, 21] # update the storage value call.account::set_map_item dropw dropw dropw @@ -665,7 +703,9 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { dropw dropw dropw dropw end - " + "#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, + mock_map_slot = &*MOCK_MAP_SLOT, ); let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(tx_script_src)?; @@ -710,7 +750,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { // We expect one updated item and one updated map assert_eq!(executed_transaction.account_delta().storage().values().len(), 1); assert_eq!( - executed_transaction.account_delta().storage().values().get(&STORAGE_INDEX_0), + executed_transaction.account_delta().storage().values().get(&MOCK_VALUE_SLOT0), Some(&updated_slot_value) ); @@ -719,7 +759,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { .account_delta() .storage() .maps() - .get(&STORAGE_INDEX_2) + .get(&MOCK_MAP_SLOT) .context("failed to get expected value from storage map")? .entries(); assert_eq!( @@ -778,41 +818,44 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: (rand_value(), rand_value()), ])?; + let map0_slot_name = SlotName::mock(1); + let map1_slot_name = SlotName::mock(2); + let map2_slot_name = SlotName::mock(4); + // Build a public account so the proven transaction includes the account update. let account = AccountBuilder::new([1; 32]) .storage_mode(AccountStorageMode::Public) .with_auth_component(Auth::IncrNonce) .with_component(MockAccountComponent::with_slots(vec![ - AccountStorage::mock_item_0().slot, - StorageSlot::Map(map0.clone()), - StorageSlot::Map(map1.clone()), - AccountStorage::mock_item_1().slot, - StorageSlot::Map(map2.clone()), + AccountStorage::mock_value_slot0(), + NamedStorageSlot::with_map(map0_slot_name.clone(), map0.clone()), + NamedStorageSlot::with_map(map1_slot_name.clone(), map1.clone()), + AccountStorage::mock_value_slot1(), + NamedStorageSlot::with_map(map2_slot_name.clone(), map2.clone()), ])) .build()?; - let map0_index = 1; - let map1_index = 2; - let map2_index = 4; // Fetch a random existing key from the map. let existing_key = *map2.entries().next().unwrap().0; let value0 = Word::from([3, 4, 5, 6u32]); let code = format!( - " + r#" use.mock::account + const.MAP_SLOT=word("{map2_slot_name}") + begin # Update an existing key. push.{value0} push.{existing_key} - push.{map2_index} - # => [index, KEY, VALUE] + push.MAP_SLOT[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] call.account::set_map_item exec.::std::sys::truncate_stack end - " + "# ); let builder = ScriptBuilder::with_mock_libraries()?; @@ -828,15 +871,18 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: map2.insert(existing_key, value0)?; - for (map_index, expected_map) in [(map0_index, map0), (map1_index, map1), (map2_index, map2)] { - let map_delta = tx.account_delta().storage().maps().get(&map_index).unwrap(); + for (slot_name, expected_map) in + [(map0_slot_name, map0), (map1_slot_name, map1), (map2_slot_name, map2)] + { + let map_delta = tx.account_delta().storage().maps().get(&slot_name).unwrap(); assert_eq!( map_delta .entries() .iter() - .map(|(key, value)| (*key.inner(), *value)) + .map(|(key, value)| (key.inner(), value)) .collect::>(), - expected_map.into_entries() + expected_map.entries().collect(), + "map delta does not match for slot {slot_name}", ); } @@ -873,13 +919,16 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: /// delta and not normalized away. #[tokio::test] async fn delta_for_new_account_retains_empty_value_storage_slots() -> anyhow::Result<()> { + let slot_name0 = SlotName::mock(0); + let slot_name1 = SlotName::mock(1); + let slot_value2 = Word::from([1, 2, 3, 4u32]); let mut account = AccountBuilder::new(rand::random()) .account_type(AccountType::RegularAccountUpdatableCode) .storage_mode(AccountStorageMode::Network) .with_component(MockAccountComponent::with_slots(vec![ - StorageSlot::empty_value(), - StorageSlot::Value(slot_value2), + NamedStorageSlot::with_empty_value(slot_name0.clone()), + NamedStorageSlot::with_value(slot_name1.clone(), slot_value2), ])) .with_auth_component(Auth::IncrNonce) .build()?; @@ -893,8 +942,8 @@ async fn delta_for_new_account_retains_empty_value_storage_slots() -> anyhow::Re }; assert_eq!(delta.storage().values().len(), 2); - assert_eq!(delta.storage().values().get(&0).unwrap(), &Word::empty()); - assert_eq!(delta.storage().values().get(&1).unwrap(), &slot_value2); + assert_eq!(delta.storage().values().get(&slot_name0).unwrap(), &Word::empty()); + assert_eq!(delta.storage().values().get(&slot_name1).unwrap(), &slot_value2); let recreated_account = Account::try_from(delta)?; // The recreated account should match the original account with the nonce incremented (and the @@ -909,10 +958,14 @@ async fn delta_for_new_account_retains_empty_value_storage_slots() -> anyhow::Re /// delta. #[tokio::test] async fn delta_for_new_account_retains_empty_map_storage_slots() -> anyhow::Result<()> { + let slot_name0 = SlotName::mock(0); + let mut account = AccountBuilder::new(rand::random()) .account_type(AccountType::RegularAccountUpdatableCode) .storage_mode(AccountStorageMode::Network) - .with_component(MockAccountComponent::with_slots(vec![StorageSlot::empty_map()])) + .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_empty_map( + slot_name0.clone(), + )])) .with_auth_component(Auth::IncrNonce) .build()?; @@ -925,7 +978,7 @@ async fn delta_for_new_account_retains_empty_map_storage_slots() -> anyhow::Resu }; assert_eq!(delta.storage().maps().len(), 1); - assert!(delta.storage().maps().get(&0).unwrap().is_empty()); + assert!(delta.storage().maps().get(&slot_name0).unwrap().is_empty()); let recreated_account = Account::try_from(delta)?; // The recreated account should match the original account with the nonce incremented (and the @@ -971,7 +1024,7 @@ struct TestSetup { } fn setup_test( - storage_slots: impl IntoIterator, + storage_slots: impl IntoIterator, vault_assets: impl IntoIterator, note_assets: impl IntoIterator, ) -> anyhow::Result { @@ -1017,11 +1070,11 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " use.mock::account use.miden::output_note - #! Inputs: [index, VALUE] + #! Inputs: [name_id_prefix, name_id_suffix, VALUE] #! Outputs: [] proc.set_item - repeat.11 push.0 movdn.5 end - # => [index, VALUE, pad(11)] + repeat.10 push.0 movdn.6 end + # => [name_id_prefix, name_id_suffix, VALUE, pad(10)] call.account::set_item # => [OLD_VALUE, pad(12)] @@ -1029,11 +1082,11 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " dropw dropw dropw dropw end - #! Inputs: [index, KEY, VALUE] + #! Inputs: [name_id_prefix, name_id_suffix, KEY, VALUE] #! Outputs: [] proc.set_map_item - repeat.7 push.0 movdn.9 end - # => [index, KEY, VALUE, pad(7)] + repeat.6 push.0 movdn.10 end + # => [index, KEY, VALUE, pad(6)] call.account::set_map_item # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index e34f058e7d..7a065c8991 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -27,6 +27,7 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; +use miden_objects::testing::storage::MOCK_VALUE_SLOT0; use miden_objects::transaction::{OutputNote, OutputNotes}; use miden_processor::{Felt, ONE}; @@ -423,17 +424,19 @@ async fn test_epilogue_increment_nonce_success() -> anyhow::Result<()> { let expected_nonce = ONE + ONE; let code = format!( - " + r#" use.$kernel::prologue use.mock::account use.$kernel::epilogue use.$kernel::memory + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin exec.prologue::prepare_transaction push.1.2.3.4 - push.0 + push.MOCK_VALUE_SLOT0[0..2] call.account::set_item dropw @@ -445,7 +448,8 @@ async fn test_epilogue_increment_nonce_success() -> anyhow::Result<()> { exec.memory::get_account_nonce push.{expected_nonce} assert_eq end - " + "#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, ); tx_context.execute_code(code.as_str()).await?; @@ -455,19 +459,24 @@ async fn test_epilogue_increment_nonce_success() -> anyhow::Result<()> { /// Tests that changing the account state without incrementing the nonce results in an error. #[tokio::test] async fn epilogue_fails_on_account_state_change_without_nonce_increment() -> anyhow::Result<()> { - let code = " + let code = format!( + r#" use.mock::account + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin push.91.92.93.94 - push.0 + push.MOCK_VALUE_SLOT0[0..2] repeat.5 movup.5 drop end - # => [index, VALUE] + # => [name_id_prefix, name_id_suffix, VALUE] call.account::set_item # => [PREV_VALUE] dropw end - "; + "#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, + ); let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index 2280e6e318..71b7dd0f6b 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -10,13 +10,13 @@ use miden_lib::errors::tx_kernel_errors::{ }; use miden_lib::testing::mock_account::MockAccountExt; use miden_lib::transaction::TransactionKernel; -use miden_lib::transaction::memory::NATIVE_ACCT_STORAGE_SLOTS_SECTION_PTR; use miden_lib::utils::ScriptBuilder; use miden_objects::account::{ Account, AccountBuilder, AccountComponent, AccountId, + AccountStorage, AccountType, StorageMap, }; @@ -36,10 +36,8 @@ use miden_objects::testing::constants::{ NON_FUNGIBLE_ASSET_DATA_2, }; use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::testing::storage::FAUCET_STORAGE_DATA_SLOT; use miden_objects::{Felt, Word}; -use crate::kernel_tests::tx::ExecutionOutputExt; use crate::utils::create_public_p2any_note; use crate::{TransactionContextBuilder, assert_execution_error, assert_transaction_executor_error}; @@ -49,24 +47,22 @@ use crate::{TransactionContextBuilder, assert_execution_error, assert_transactio #[tokio::test] async fn test_mint_fungible_asset_succeeds() -> anyhow::Result<()> { let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap(); - let tx_context = TransactionContextBuilder::with_fungible_faucet( - faucet_id.into(), - Felt::new(FUNGIBLE_FAUCET_INITIAL_BALANCE), - ) - .build()?; + let expected_final_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE + FUNGIBLE_ASSET_AMOUNT; let code = format!( r#" - use.mock::faucet + use.mock::faucet->mock_faucet + use.miden::faucet use.$kernel::asset_vault use.$kernel::memory use.$kernel::prologue begin - # mint asset exec.prologue::prepare_transaction + + # mint asset push.{FUNGIBLE_ASSET_AMOUNT} push.0 push.{suffix} push.{prefix} - call.faucet::mint + call.mock_faucet::mint # assert the correct asset is returned push.{FUNGIBLE_ASSET_AMOUNT} push.0 push.{suffix} push.{prefix} @@ -77,23 +73,24 @@ async fn test_mint_fungible_asset_succeeds() -> anyhow::Result<()> { push.{suffix} push.{prefix} exec.asset_vault::get_balance push.{FUNGIBLE_ASSET_AMOUNT} assert_eq.err="input vault should contain minted asset" + + exec.faucet::get_total_issuance + push.{expected_final_amount} + assert_eq.err="expected total issuance to be {expected_final_amount}" end "#, prefix = faucet_id.prefix().as_felt(), suffix = faucet_id.suffix(), ); - let exec_output = &tx_context.execute_code(&code).await.unwrap(); - - let expected_final_storage_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE + FUNGIBLE_ASSET_AMOUNT; - let faucet_reserved_slot_storage_location = - FAUCET_STORAGE_DATA_SLOT as u32 + NATIVE_ACCT_STORAGE_SLOTS_SECTION_PTR; - let faucet_storage_amount_location = faucet_reserved_slot_storage_location + 7; - - let faucet_storage_amount = - exec_output.get_kernel_mem_element(faucet_storage_amount_location).as_int(); + TransactionContextBuilder::with_fungible_faucet( + faucet_id.into(), + Felt::new(FUNGIBLE_FAUCET_INITIAL_BALANCE), + ) + .build()? + .execute_code(&code) + .await?; - assert_eq!(faucet_storage_amount, expected_final_storage_amount); Ok(()) } @@ -206,6 +203,8 @@ async fn test_mint_non_fungible_asset_succeeds() -> anyhow::Result<()> { use.$kernel::prologue use.mock::faucet->mock_faucet + const FAUCET_METADATA_SLOT_NAME = word("{faucet_metadata_slot_name}") + begin # mint asset exec.prologue::prepare_transaction @@ -223,7 +222,7 @@ async fn test_mint_non_fungible_asset_succeeds() -> anyhow::Result<()> { assert.err="vault should contain asset" # assert the non-fungible asset has been added to the faucet smt - push.{FAUCET_STORAGE_DATA_SLOT} + push.FAUCET_METADATA_SLOT_NAME[0..2] exec.account::get_item push.{asset_vault_key} exec.smt::get @@ -232,6 +231,7 @@ async fn test_mint_non_fungible_asset_succeeds() -> anyhow::Result<()> { dropw end "#, + faucet_metadata_slot_name = AccountStorage::faucet_metadata_slot(), non_fungible_asset = Word::from(non_fungible_asset), asset_vault_key = StorageMap::hash_key(asset_vault_key.into()), ); @@ -344,19 +344,22 @@ async fn test_burn_fungible_asset_succeeds() -> anyhow::Result<()> { }; let faucet_id = tx_context.account().id(); + let expected_final_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE - FUNGIBLE_ASSET_AMOUNT; let code = format!( r#" - use.mock::faucet + use.mock::faucet->mock_faucet + use.miden::faucet use.$kernel::asset_vault use.$kernel::memory use.$kernel::prologue begin - # burn asset exec.prologue::prepare_transaction + + # burn asset push.{FUNGIBLE_ASSET_AMOUNT} push.0 push.{suffix} push.{prefix} - call.faucet::burn + call.mock_faucet::burn # assert the correct asset is returned push.{FUNGIBLE_ASSET_AMOUNT} push.0 push.{suffix} push.{prefix} @@ -368,7 +371,12 @@ async fn test_burn_fungible_asset_succeeds() -> anyhow::Result<()> { push.{suffix} push.{prefix} exec.asset_vault::get_balance - push.{final_input_vault_asset_amount} assert_eq.err="vault balance does not match expected balance" + push.{final_input_vault_asset_amount} + assert_eq.err="vault balance does not match expected balance" + + exec.faucet::get_total_issuance + push.{expected_final_amount} + assert_eq.err="expected total issuance to be {expected_final_amount}" end "#, prefix = faucet_id.prefix().as_felt(), @@ -376,17 +384,8 @@ async fn test_burn_fungible_asset_succeeds() -> anyhow::Result<()> { final_input_vault_asset_amount = CONSUMED_ASSET_1_AMOUNT - FUNGIBLE_ASSET_AMOUNT, ); - let exec_output = &tx_context.execute_code(&code).await.unwrap(); - - let expected_final_storage_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE - FUNGIBLE_ASSET_AMOUNT; - let faucet_reserved_slot_storage_location = - FAUCET_STORAGE_DATA_SLOT as u32 + NATIVE_ACCT_STORAGE_SLOTS_SECTION_PTR; - let faucet_storage_amount_location = faucet_reserved_slot_storage_location + 7; - - let faucet_storage_amount = - exec_output.get_kernel_mem_element(faucet_storage_amount_location).as_int(); + tx_context.execute_code(&code).await?; - assert_eq!(faucet_storage_amount, expected_final_storage_amount); Ok(()) } @@ -504,6 +503,8 @@ async fn test_burn_non_fungible_asset_succeeds() -> anyhow::Result<()> { use.$kernel::prologue use.mock::faucet->mock_faucet + const FAUCET_METADATA_SLOT_NAME = word("{faucet_metadata_slot_name}") + begin exec.prologue::prepare_transaction @@ -519,7 +520,7 @@ async fn test_burn_non_fungible_asset_succeeds() -> anyhow::Result<()> { # check that the non-fungible asset is in the account map push.{burnt_asset_vault_key} - push.{FAUCET_STORAGE_DATA_SLOT} + push.FAUCET_METADATA_SLOT_NAME[0..2] exec.account::get_map_item push.{non_fungible_asset} assert_eqw.err="non-fungible asset should be in the account map" @@ -541,13 +542,14 @@ async fn test_burn_non_fungible_asset_succeeds() -> anyhow::Result<()> { # assert that the non-fungible asset is no longer in the account map push.{burnt_asset_vault_key} - push.{FAUCET_STORAGE_DATA_SLOT} + push.FAUCET_METADATA_SLOT_NAME[0..2] exec.account::get_map_item padw assert_eqw.err="burnt asset should have been removed from map" dropw end "#, + faucet_metadata_slot_name = AccountStorage::faucet_metadata_slot(), non_fungible_asset = Word::from(non_fungible_asset_burnt), burnt_asset_vault_key = burnt_asset_vault_key, ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs index 0cc326fe60..4a8d466932 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs @@ -1,6 +1,6 @@ use anyhow::Context; use assert_matches::assert_matches; -use miden_objects::account::{AccountId, StorageMap, StorageSlot}; +use miden_objects::account::{AccountId, NamedStorageSlot, SlotName, StorageMap}; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; use miden_objects::note::NoteType; use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; @@ -132,8 +132,11 @@ async fn mutate_account_with_storage() -> anyhow::Result { let account = builder.add_existing_mock_account_with_storage_and_assets( Auth::IncrNonce, [ - StorageSlot::Value(rand_value()), - StorageSlot::Map(StorageMap::with_entries([(rand_value(), rand_value())])?), + NamedStorageSlot::with_value(SlotName::mock(0), rand_value()), + NamedStorageSlot::with_map( + SlotName::mock(1), + StorageMap::with_entries([(rand_value(), rand_value())])?, + ), ], [Asset::from(native_asset), NonFungibleAsset::mock(&[1, 2, 3, 4])], )?; @@ -161,8 +164,11 @@ async fn create_output_notes() -> anyhow::Result { let account = builder.add_existing_mock_account_with_storage_and_assets( Auth::IncrNonce, [ - StorageSlot::Value(rand_value()), - StorageSlot::Map(StorageMap::with_entries([(rand_value(), rand_value())])?), + NamedStorageSlot::with_map( + SlotName::mock(0), + StorageMap::with_entries([(rand_value(), rand_value())])?, + ), + NamedStorageSlot::with_value(SlotName::mock(1), rand_value()), ], [Asset::from(native_asset), NonFungibleAsset::mock(&[1, 2, 3, 4])], )?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index d7f55db52a..f2d03ae6b6 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -59,8 +59,8 @@ use crate::{Auth, MockChainBuilder, assert_execution_error, assert_transaction_e #[tokio::test] async fn test_fpi_memory_single_account() -> anyhow::Result<()> { // Prepare the test data - let storage_slots = - vec![AccountStorage::mock_item_0().slot, AccountStorage::mock_item_2().slot]; + let mock_value_slot0 = AccountStorage::mock_value_slot0(); + let mock_map_slot = AccountStorage::mock_map_slot(); let foreign_account_code_source = " use.miden::active_account @@ -71,7 +71,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { exec.active_account::get_item # truncate the stack - movup.6 movup.6 movup.6 drop drop drop + movup.6 movup.6 drop drop end export.get_map_item_foreign @@ -86,7 +86,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let foreign_account_component = AccountComponent::compile( foreign_account_code_source, TransactionKernel::with_kernel_library(source_manager.clone()), - storage_slots.clone(), + vec![mock_value_slot0.clone(), mock_map_slot.clone()], )? .with_supports_all_types(); @@ -97,7 +97,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let native_account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![AccountStorage::mock_item_2().slot])) + .with_component(MockAccountComponent::with_slots(vec![AccountStorage::mock_map_slot()])) .storage_mode(AccountStorageMode::Public) .build_existing()?; @@ -123,28 +123,31 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { // invocation let code = format!( - " + r#" use.std::sys use.$kernel::prologue use.miden::tx + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin exec.prologue::prepare_transaction # pad the stack for the `execute_foreign_procedure` execution - padw padw padw push.0.0 - # => [pad(14)] + padw padw + # => [pad(8)] - # push the index of desired storage item - push.0 + # push the slot name of desired storage item + push.MOCK_VALUE_SLOT0[0..2] # get the hash of the `get_item_foreign` procedure of the foreign account push.{get_item_foreign_hash} # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} - # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, pad(11)] + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, pad(8)] exec.tx::execute_foreign_procedure # => [STORAGE_VALUE_1] @@ -152,7 +155,8 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { # truncate the stack exec.sys::truncate_stack end - ", + "#, + mock_value_slot0 = mock_value_slot0.name(), foreign_prefix = foreign_account.id().prefix().as_felt(), foreign_suffix = foreign_account.id().suffix(), get_item_foreign_hash = foreign_account.code().procedures()[1].mast_root(), @@ -162,8 +166,8 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { assert_eq!( exec_output.get_stack_word_be(0), - storage_slots[0].value(), - "Value at the top of the stack (value in the storage at index 0) should be equal [1, 2, 3, 4]", + mock_value_slot0.storage_slot().value(), + "Value at the top of the stack should be equal to [1, 2, 3, 4]", ); foreign_account_data_memory_assertions(&foreign_account, &exec_output); @@ -173,31 +177,34 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { // Check the correctness of the memory layout after `get_map_item` account procedure invocation let code = format!( - " + r#" use.std::sys use.$kernel::prologue use.miden::tx + const MOCK_MAP_SLOT = word("{mock_map_slot}") + begin exec.prologue::prepare_transaction # pad the stack for the `execute_foreign_procedure` execution - padw padw push.0.0 - # => [pad(10)] + padw + # => [pad(4)] # push the key of desired storage item push.{map_key} - # push the index of desired storage item - push.1 + # push the slot name of the desired storage item + push.MOCK_MAP_SLOT[0..2] # get the hash of the `get_map_item_foreign` account procedure push.{get_map_item_foreign_hash} # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} - # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, MAP_ITEM_KEY, pad(10)] + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, MAP_KEY, pad(4)] exec.tx::execute_foreign_procedure # => [MAP_VALUE] @@ -205,14 +212,15 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { # truncate the stack exec.sys::truncate_stack end - ", + "#, + mock_map_slot = mock_map_slot.name(), foreign_prefix = foreign_account.id().prefix().as_felt(), foreign_suffix = foreign_account.id().suffix(), map_key = STORAGE_LEAVES_2[0].0, get_map_item_foreign_hash = foreign_account.code().procedures()[2].mast_root(), ); - let exec_output = tx_context.execute_code(&code).await.unwrap(); + let exec_output = tx_context.execute_code(&code).await?; assert_eq!( exec_output.get_stack_word_be(0), @@ -229,54 +237,59 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { // result in reuse of the loaded account. let code = format!( - " + r#" use.std::sys use.$kernel::prologue use.miden::tx + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin exec.prologue::prepare_transaction ### Get the storage item at index 0 ##################### # pad the stack for the `execute_foreign_procedure` execution - padw padw padw push.0.0 - # => [pad(14)] + padw padw + # => [pad(8)] - # push the index of desired storage item - push.0 + # push the slot name of desired storage item + push.MOCK_VALUE_SLOT0[0..2] # get the hash of the `get_item_foreign` procedure of the foreign account push.{get_item_foreign_hash} # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} - # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, pad(14)] + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, pad(8)] exec.tx::execute_foreign_procedure dropw # => [] ### Get the storage item at index 0 again ############### # pad the stack for the `execute_foreign_procedure` execution - padw padw padw push.0.0 - # => [pad(14)] + padw padw + # => [pad(8)] - # push the index of desired storage item - push.0 + # push the slot name of the desired storage item + push.MOCK_VALUE_SLOT0[0..2] # get the hash of the `get_item_foreign` procedure of the foreign account push.{get_item_foreign_hash} # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} - # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, pad(14)] + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, pad(8)] exec.tx::execute_foreign_procedure # truncate the stack exec.sys::truncate_stack end - ", + "#, + mock_value_slot0 = mock_value_slot0.name(), foreign_prefix = foreign_account.id().prefix().as_felt(), foreign_suffix = foreign_account.id().suffix(), get_item_foreign_hash = foreign_account.code().procedures()[1].mast_root(), @@ -301,8 +314,8 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { #[tokio::test] async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { // Prepare the test data - let storage_slots_1 = vec![AccountStorage::mock_item_0().slot]; - let storage_slots_2 = vec![AccountStorage::mock_item_1().slot]; + let mock_value_slot0 = AccountStorage::mock_value_slot0(); + let mock_value_slot1 = AccountStorage::mock_value_slot1(); let foreign_account_code_source_1 = " use.miden::active_account @@ -314,7 +327,7 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { exec.active_account::get_item # truncate the stack - movup.6 movup.6 movup.6 drop drop drop + movup.6 movup.6 drop drop end "; let foreign_account_code_source_2 = " @@ -327,21 +340,21 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { exec.active_account::get_item # truncate the stack - movup.6 movup.6 movup.6 drop drop drop + movup.6 movup.6 drop drop end "; let foreign_account_component_1 = AccountComponent::compile( foreign_account_code_source_1, TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - storage_slots_1.clone(), + vec![mock_value_slot0.clone()], )? .with_supports_all_types(); let foreign_account_component_2 = AccountComponent::compile( foreign_account_code_source_2, TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - storage_slots_2.clone(), + vec![mock_value_slot1.clone()], )? .with_supports_all_types(); @@ -388,78 +401,84 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { // two foreign procedures from the same account should result in reuse of the loaded account. let code = format!( - " + r#" use.std::sys use.$kernel::prologue use.miden::tx + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") + begin exec.prologue::prepare_transaction - ### Get the storage item at index 0 from the first account + ### Get the storage item from the first account # pad the stack for the `execute_foreign_procedure` execution - padw padw padw push.0.0 - # => [pad(14)] + padw padw + # => [pad(8)] - # push the index of desired storage item - push.0 + # push the slot name of desired storage item + push.MOCK_VALUE_SLOT0[0..2] - # get the hash of the `get_item_foreign_1` procedure of the foreign account 1 + # get the hash of the `get_item_foreign` procedure of the foreign account push.{get_item_foreign_1_hash} # push the foreign account ID push.{foreign_1_suffix} push.{foreign_1_prefix} - # => [foreign_account_1_id_prefix, foreign_account_1_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, pad(14)] + # => [foreign_account_1_id_prefix, foreign_account_1_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, pad(8)] exec.tx::execute_foreign_procedure dropw # => [] - ### Get the storage item at index 0 from the second account + ### Get the storage item from the second account # pad the stack for the `execute_foreign_procedure` execution - padw padw padw push.0.0 - # => [pad(14)] + padw padw + # => [pad(8)] - # push the index of desired storage item - push.0 + # push the slot name of desired storage item + push.MOCK_VALUE_SLOT1[0..2] # get the hash of the `get_item_foreign_2` procedure of the foreign account 2 push.{get_item_foreign_2_hash} # push the foreign account ID push.{foreign_2_suffix} push.{foreign_2_prefix} - # => [foreign_account_2_id_prefix, foreign_account_2_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, pad(14)] + # => [foreign_account_2_id_prefix, foreign_account_2_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, pad(8)] exec.tx::execute_foreign_procedure dropw # => [] - ### Get the storage item at index 0 from the first account again + ### Get the storage item from the first account again # pad the stack for the `execute_foreign_procedure` execution - padw padw padw push.0.0 - # => [pad(14)] + padw padw + # => [pad(8)] - # push the index of desired storage item - push.0 + # push the slot name of desired storage item + push.MOCK_VALUE_SLOT0[0..2] # get the hash of the `get_item_foreign_1` procedure of the foreign account 1 push.{get_item_foreign_1_hash} # push the foreign account ID push.{foreign_1_suffix} push.{foreign_1_prefix} - # => [foreign_account_1_id_prefix, foreign_account_1_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, pad(14)] + # => [foreign_account_1_id_prefix, foreign_account_1_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, pad(8)] exec.tx::execute_foreign_procedure # truncate the stack exec.sys::truncate_stack end - ", + "#, + mock_value_slot0 = mock_value_slot0.name(), + mock_value_slot1 = mock_value_slot1.name(), get_item_foreign_1_hash = foreign_account_1.code().procedures()[1].mast_root(), get_item_foreign_2_hash = foreign_account_2.code().procedures()[1].mast_root(), - foreign_1_prefix = foreign_account_1.id().prefix().as_felt(), foreign_1_suffix = foreign_account_1.id().suffix(), - foreign_2_prefix = foreign_account_2.id().prefix().as_felt(), foreign_2_suffix = foreign_account_2.id().suffix(), ); @@ -514,8 +533,9 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { #[tokio::test] async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { // Prepare the test data - let storage_slots = - vec![AccountStorage::mock_item_0().slot, AccountStorage::mock_item_2().slot]; + let mock_value_slot0 = AccountStorage::mock_value_slot0(); + let mock_map_slot = AccountStorage::mock_map_slot(); + let foreign_account_code_source = " use.miden::active_account @@ -526,7 +546,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { exec.active_account::get_item # truncate the stack - movup.6 movup.6 movup.6 drop drop drop + movup.6 movup.6 drop drop end export.get_map_item_foreign @@ -541,7 +561,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { let foreign_account_component = AccountComponent::compile( NamedSource::new("foreign_account", foreign_account_code_source), TransactionKernel::with_kernel_library(source_manager.clone()), - storage_slots, + vec![mock_value_slot0.clone(), mock_map_slot.clone()], )? .with_supports_all_types(); @@ -562,26 +582,31 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { mock_chain.prove_next_block()?; let code = format!( - " + r#" use.std::sys use.miden::tx + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + const MOCK_MAP_SLOT = word("{mock_map_slot}") + begin - # get the storage item at index 0 + # get the storage item # pad the stack for the `execute_foreign_procedure` execution - padw padw padw push.0.0 - # => [pad(14)] + # pad the stack for the `execute_foreign_procedure` execution + padw padw + # => [pad(8)] - # push the index of desired storage item - push.0 + # push the slot name of desired storage item + push.MOCK_VALUE_SLOT0[0..2] # get the hash of the `get_item_foreign` account procedure procref.::foreign_account::get_item_foreign # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} - # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, pad(14)] + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT + # slot_name_id_prefix, slot_name_id_suffix, pad(8)]] exec.tx::execute_foreign_procedure # => [STORAGE_VALUE] @@ -590,23 +615,24 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { push.1.2.3.4 assert_eqw # => [] - # get the storage map at index 1 + # get an item from the storage map # pad the stack for the `execute_foreign_procedure` execution - padw padw push.0.0 - # => [pad(10)] + padw + # => [pad(4)] # push the key of desired storage item push.{map_key} - # push the index of desired storage item - push.1 + # push the slot name of the desired storage map + push.MOCK_MAP_SLOT[0..2] # get the hash of the `get_map_item_foreign` account procedure procref.::foreign_account::get_map_item_foreign # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} - # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, MAP_ITEM_KEY, pad(10)] + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, MAP_ITEM_KEY, pad(4)] exec.tx::execute_foreign_procedure # => [MAP_VALUE] @@ -618,7 +644,9 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { # truncate the stack exec.sys::truncate_stack end - ", + "#, + mock_value_slot0 = mock_value_slot0.name(), + mock_map_slot = mock_map_slot.name(), foreign_prefix = foreign_account.id().prefix().as_felt(), foreign_suffix = foreign_account.id().suffix(), map_key = STORAGE_LEAVES_2[0].0, @@ -883,28 +911,35 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { #[tokio::test] async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { // ------ SECOND FOREIGN ACCOUNT --------------------------------------------------------------- - let storage_slots = vec![AccountStorage::mock_item_0().slot]; - let second_foreign_account_code_source = r#" + let mock_value_slot0 = AccountStorage::mock_value_slot0(); + let mock_value_slot1 = AccountStorage::mock_value_slot1(); + + let second_foreign_account_code_source = format!( + r#" use.miden::tx use.miden::active_account use.std::sys + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") + export.second_account_foreign_proc - # get the storage item at index 1 + # get the storage item at value1 # pad the stack for the `execute_foreign_procedure` execution - padw padw padw push.0.0 - # => [pad(14)] + padw padw + # => [pad(8)] # push the index of desired storage item - push.1 + push.MOCK_VALUE_SLOT1[0..2] # get the hash of the `get_item_foreign` account procedure from the advice stack adv_push.4 # push the foreign account ID from the advice stack adv_push.2 - # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, storage_item_index, pad(14)] + # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, + # slot_name_id_prefix, slot_name_id_suffix, pad(8)] exec.tx::execute_foreign_procedure # => [storage_value] @@ -912,9 +947,10 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { # make sure that the resulting value equals 5 dup push.5 assert_eq.err="value should have been 5" - # get the first element of the 0'th storage slot (it should be 1) and add it to the + # get the first element of the value0 storage slot (it should be 1) and add it to the # obtained foreign value. - push.0 exec.active_account::get_item drop drop drop + push.MOCK_VALUE_SLOT0[0..2] exec.active_account::get_item + drop drop drop add # assert that the resulting value equals 6 @@ -922,13 +958,16 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { exec.sys::truncate_stack end - "#; + "#, + mock_value_slot0 = mock_value_slot0.name(), + mock_value_slot1 = mock_value_slot1.name(), + ); let source_manager = Arc::new(DefaultSourceManager::default()); let second_foreign_account_component = AccountComponent::compile( second_foreign_account_code_source, TransactionKernel::with_kernel_library(source_manager.clone()), - storage_slots, + vec![mock_value_slot0.clone()], )? .with_supports_all_types(); @@ -938,14 +977,15 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { .build_existing()?; // ------ FIRST FOREIGN ACCOUNT --------------------------------------------------------------- - let storage_slots = - vec![AccountStorage::mock_item_0().slot, AccountStorage::mock_item_1().slot]; - let first_foreign_account_code_source = r#" + let first_foreign_account_code_source = format!( + r#" use.miden::tx use.miden::active_account use.std::sys + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + export.first_account_foreign_proc # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 @@ -961,9 +1001,10 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { exec.tx::execute_foreign_procedure # => [storage_value] - # get the second element of the 0'th storage slot (it should be 2) and add it to the + # get the second element of the value0 storage slot (it should be 2) and add it to the # obtained foreign value. - push.0 exec.active_account::get_item drop drop swap drop + push.MOCK_VALUE_SLOT0[0..2] exec.active_account::get_item + drop drop swap drop add # assert that the resulting value equals 8 @@ -981,12 +1022,14 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { # return the first element of the resulting word drop drop drop end - "#; + "#, + mock_value_slot0 = mock_value_slot0.name(), + ); let first_foreign_account_component = AccountComponent::compile( NamedSource::new("first_foreign_account", first_foreign_account_code_source), TransactionKernel::with_kernel_library(source_manager.clone()), - storage_slots, + vec![mock_value_slot0.clone(), mock_value_slot1.clone()], )? .with_supports_all_types(); @@ -1258,17 +1301,21 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { #[tokio::test] async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let mut foreign_accounts = Vec::new(); + let mock_value_slot0 = AccountStorage::mock_value_slot0(); - let last_foreign_account_code_source = " + let last_foreign_account_code_source = format!( + r#" use.miden::active_account + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + export.get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure # of the foreign account, not the native one push.1 drop # push the index of desired storage item - push.0 + push.MOCK_VALUE_SLOT0[0..2] exec.active_account::get_item @@ -1278,13 +1325,14 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { # make sure that the resulting value equals 1 assert end - "; + "#, + mock_value_slot0 = mock_value_slot0.name(), + ); - let storage_slots = vec![AccountStorage::mock_item_0().slot]; let last_foreign_account_component = AccountComponent::compile( last_foreign_account_code_source, TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - storage_slots, + vec![mock_value_slot0.clone()], ) .unwrap() .with_supports_all_types(); @@ -1541,10 +1589,11 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { end "; + let mock_value_slot0 = AccountStorage::mock_value_slot0(); let foreign_account_component = AccountComponent::compile( foreign_account_code_source, TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - vec![AccountStorage::mock_item_0().slot], + vec![mock_value_slot0.clone()], )? .with_supports_all_types(); @@ -1555,7 +1604,7 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { let native_account = AccountBuilder::new([4; 32]) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![AccountStorage::mock_item_2().slot])) + .with_component(MockAccountComponent::with_slots(vec![AccountStorage::mock_map_slot()])) .build_existing()?; let mut mock_chain = @@ -1568,9 +1617,10 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { // Modify the account's storage to change its storage commitment and in turn the account // commitment. - foreign_account - .storage_mut() - .set_item(0, Word::from([Felt::ONE, Felt::ONE, Felt::ONE, Felt::ONE]))?; + foreign_account.storage_mut().set_item( + mock_value_slot0.name(), + Word::from([Felt::ONE, Felt::ONE, Felt::ONE, Felt::ONE]), + )?; // We pass the modified foreign account with a witness that is valid against the ref block. This // means the foreign account's commitment does not match the commitment that the account witness @@ -1822,15 +1872,20 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - .storage_mode(AccountStorageMode::Public) .build_existing()?; + let mock_value_slot0 = AccountStorage::mock_value_slot0(); + let mock_map_slot = AccountStorage::mock_map_slot(); let (map_key, map_value) = STORAGE_LEAVES_2[0]; // Create foreign procedures that test get_initial_item and get_initial_map_item - let foreign_account_code_source = " + let foreign_account_code_source = format!( + r#" use.miden::active_account use.std::sys + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + export.test_get_initial_item - push.0 + push.MOCK_VALUE_SLOT0[0..2] exec.active_account::get_initial_item exec.sys::truncate_stack end @@ -1839,12 +1894,14 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - exec.active_account::get_initial_map_item exec.sys::truncate_stack end - "; + "#, + mock_value_slot0 = mock_value_slot0.name() + ); let foreign_account_component = AccountComponent::compile( NamedSource::new("foreign_account", foreign_account_code_source), - TransactionKernel::assembler(), - vec![AccountStorage::mock_item_0().slot, AccountStorage::mock_item_2().slot], + TransactionKernel::assembler().with_debug_mode(true), + vec![mock_value_slot0.clone(), mock_map_slot.clone()], )? .with_supports_all_types(); @@ -1862,37 +1919,39 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - let foreign_account_inputs = mock_chain.get_foreign_account_inputs(foreign_account.id())?; let code = format!( - " + r#" use.std::sys use.miden::tx - begin + const MOCK_MAP_SLOT = word("{mock_map_slot}") + begin # Test get_initial_item on foreign account padw padw padw push.0.0.0 - # => [ pad(4), pad(4), pad(4), 0, 0, 0 ] + # => [pad(15)] procref.::foreign_account::test_get_initial_item push.{foreign_account_id_suffix} push.{foreign_account_id_prefix} exec.tx::execute_foreign_procedure push.{expected_value_slot_0} - assert_eqw.err=\"foreign account get_initial_item should work\" + assert_eqw.err="foreign account get_initial_item should work" # Test get_initial_map_item on foreign account padw padw push.0.0 push.{map_key} - push.1 + push.MOCK_MAP_SLOT[0..2] procref.::foreign_account::test_get_initial_map_item push.{foreign_account_id_suffix} push.{foreign_account_id_prefix} exec.tx::execute_foreign_procedure push.{map_value} - assert_eqw.err=\"foreign account get_initial_map_item should work\" + assert_eqw.err="foreign account get_initial_map_item should work" exec.sys::truncate_stack end - ", + "#, + mock_map_slot = mock_map_slot.name(), foreign_account_id_prefix = foreign_account.id().prefix().as_felt(), foreign_account_id_suffix = foreign_account.id().suffix(), - expected_value_slot_0 = &AccountStorage::mock_item_0().slot.value(), + expected_value_slot_0 = mock_value_slot0.storage_slot().value(), map_key = &map_key, map_value = &map_value, ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index 83829fcd5e..7a3c20f962 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -13,6 +13,7 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, }; use miden_objects::testing::constants::FUNGIBLE_ASSET_AMOUNT; +use miden_objects::testing::storage::MOCK_MAP_SLOT; use super::Word; use crate::{Auth, MockChain, TransactionContextBuilder}; @@ -176,34 +177,36 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { "test setup requires that the non existent key does not exist" ); - // The index of the mock map in account storage is 2. - let map_index = 2; + // The slot name of the mock map in account storage. + let mock_map_slot = &*MOCK_MAP_SLOT; let value0 = Word::from([3, 4, 5, 6u32]); let value1 = Word::from([9, 8, 7, 6u32]); let code = format!( - " + r#" use.mock::account + const MOCK_MAP_SLOT = word("{mock_map_slot}") + begin # Update an existing key. push.{value0} push.{existing_key} - push.{map_index} - # => [index, KEY, VALUE] + push.MOCK_MAP_SLOT[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] call.account::set_map_item # Insert a non-existent key. push.{value1} push.{non_existent_key} - push.{map_index} - # => [index, KEY, VALUE] + push.MOCK_MAP_SLOT[0..2] + # => [name_id_prefix, name_id_suffix, KEY, VALUE] call.account::set_map_item exec.::std::sys::truncate_stack end - " + "# ); let builder = ScriptBuilder::with_mock_libraries()?; @@ -217,7 +220,7 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { .execute() .await?; - let map_delta = tx.account_delta().storage().maps().get(&map_index).unwrap(); + let map_delta = tx.account_delta().storage().maps().get(mock_map_slot).unwrap(); assert_eq!(map_delta.entries().get(&LexicographicWord::new(existing_key)).unwrap(), &value0); assert_eq!( map_delta.entries().get(&LexicographicWord::new(non_existent_key)).unwrap(), @@ -240,16 +243,20 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { "test setup requires that the non existent key does not exist" ); + let mock_map_slot = &*MOCK_MAP_SLOT; + let code = format!( r#" use.std::word use.mock::account + const MOCK_MAP_SLOT = word("{mock_map_slot}") + begin # Fetch value from existing key. push.{existing_key} - push.2 - # => [index, KEY] + push.MOCK_MAP_SLOT[0..2] + # => [name_id_prefix, name_id_suffix, KEY] call.account::get_map_item push.{existing_value} @@ -257,8 +264,8 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { # Fetch a non-existent key. push.{non_existent_key} - push.2 - # => [index, KEY] + push.MOCK_MAP_SLOT[0..2] + # => [name_id_prefix, name_id_suffix, KEY] call.account::get_map_item padw assert_eqw.err="non-existent value should be the empty word" diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 8cc034e102..e7c617a662 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -17,7 +17,6 @@ use miden_lib::transaction::memory::{ BLOCK_METADATA_PTR, BLOCK_NUMBER_IDX, CHAIN_COMMITMENT_PTR, - FAUCET_STORAGE_DATA_SLOT, FEE_PARAMETERS_PTR, INIT_ACCT_COMMITMENT_PTR, INIT_NATIVE_ACCT_STORAGE_COMMITMENT_PTR, @@ -71,6 +70,8 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, + NamedStorageSlot, + SlotName, StorageMap, StorageSlot, }; @@ -588,9 +589,10 @@ pub async fn create_multiple_accounts_test(storage_mode: AccountStorageMode) -> .account_type(account_type) .storage_mode(storage_mode) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![StorageSlot::Value(Word::from( - [255u32; WORD_SIZE], - ))])) + .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( + SlotName::mock(0), + Word::from([255u32; WORD_SIZE]), + )])) .build() .with_context(|| { format!("account build for {account_type} and {storage_mode} failed") @@ -662,7 +664,9 @@ pub async fn create_account_fungible_faucet_invalid_initial_balance() -> anyhow: // Set the initial balance to a non-zero value manually, since the builder would not allow us to // do that. let faucet_data_slot = Word::from([0, 0, 0, 100u32]); - storage.set_item(FAUCET_STORAGE_DATA_SLOT, faucet_data_slot).unwrap(); + storage + .set_item(AccountStorage::faucet_metadata_slot(), faucet_data_slot) + .unwrap(); // The compute account ID function will set the nonce to zero so this is considered a new // account. @@ -688,7 +692,11 @@ pub async fn create_account_non_fungible_faucet_invalid_initial_reserved_slot() let asset = NonFungibleAsset::mock(&[1, 2, 3, 4]); let non_fungible_storage_map = StorageMap::with_entries([(asset.vault_key().into(), asset.into())]).unwrap(); - let storage = AccountStorage::new(vec![StorageSlot::Map(non_fungible_storage_map)]).unwrap(); + let storage = AccountStorage::new(vec![NamedStorageSlot::with_map( + AccountStorage::faucet_metadata_slot().clone(), + non_fungible_storage_map, + )]) + .unwrap(); let account = AccountBuilder::new([1; 32]) .account_type(AccountType::NonFungibleFaucet) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index 7c15358a82..e751e539da 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -18,7 +18,8 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - StorageSlot, + NamedStorageSlot, + SlotName, }; use miden_objects::assembly::DefaultSourceManager; use miden_objects::assembly::diagnostics::NamedSource; @@ -770,7 +771,7 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { let component = AccountComponent::compile( account_code_script, TransactionKernel::assembler(), - vec![StorageSlot::Value(Word::default())], + vec![NamedStorageSlot::with_value(SlotName::mock(0), Word::default())], )? .with_supports_all_types(); diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 5223295f3e..58fc67fa16 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -17,16 +17,17 @@ use miden_lib::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; use miden_lib::account::wallets::BasicWallet; use miden_lib::note::{create_p2id_note, create_p2ide_note, create_swap_note}; use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::transaction::{TransactionKernel, memory}; +use miden_lib::transaction::TransactionKernel; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{ Account, AccountBuilder, AccountDelta, AccountId, + AccountStorage, AccountStorageMode, AccountType, - StorageSlot, + NamedStorageSlot, }; use miden_objects::asset::{Asset, FungibleAsset, TokenSymbol}; use miden_objects::block::account_tree::AccountTree; @@ -344,7 +345,7 @@ impl MockChainBuilder { account .storage_mut() .set_item( - memory::FAUCET_STORAGE_DATA_SLOT, + AccountStorage::faucet_metadata_slot(), Word::from([ZERO, ZERO, ZERO, Felt::new(issuance)]), ) .context("failed to set faucet storage")?; @@ -388,7 +389,7 @@ impl MockChainBuilder { account .storage_mut() .set_item( - memory::FAUCET_STORAGE_DATA_SLOT, + AccountStorage::faucet_metadata_slot(), Word::from([ZERO, ZERO, ZERO, Felt::new(issuance)]), ) .context("failed to set faucet storage")?; @@ -419,7 +420,7 @@ impl MockChainBuilder { pub fn add_existing_mock_account_with_storage( &mut self, auth_method: Auth, - slots: impl IntoIterator, + slots: impl IntoIterator, ) -> anyhow::Result { self.add_existing_mock_account_with_storage_and_assets(auth_method, slots, []) } @@ -439,7 +440,7 @@ impl MockChainBuilder { pub fn add_existing_mock_account_with_storage_and_assets( &mut self, auth_method: Auth, - slots: impl IntoIterator, + slots: impl IntoIterator, assets: impl IntoIterator, ) -> anyhow::Result { let account_builder = Account::builder(self.rng.random()) diff --git a/crates/miden-testing/tests/auth/ecdsa_acl.rs b/crates/miden-testing/tests/auth/ecdsa_acl.rs index f5e21c282c..7882515bf8 100644 --- a/crates/miden-testing/tests/auth/ecdsa_acl.rs +++ b/crates/miden-testing/tests/auth/ecdsa_acl.rs @@ -1,6 +1,7 @@ use core::slice; use assert_matches::assert_matches; +use miden_lib::account::auth::AuthEcdsaK256KeccakAcl; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::note::NoteBuilder; use miden_lib::utils::ScriptBuilder; @@ -13,6 +14,7 @@ use miden_objects::account::{ AccountType, }; use miden_objects::note::Note; +use miden_objects::testing::storage::MOCK_VALUE_SLOT0; use miden_objects::transaction::OutputNote; use miden_objects::{Felt, FieldElement, Word}; use miden_testing::{Auth, MockChain}; @@ -100,25 +102,36 @@ async fn test_ecdsa_acl() -> anyhow::Result<()> { } .build_component(); - let tx_script_with_trigger_1 = r#" + let tx_script_with_trigger_1 = format!( + r#" use.mock::account + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin - push.0 + push.MOCK_VALUE_SLOT0[0..2] call.account::get_item dropw end - "#; + "#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, + ); - let tx_script_with_trigger_2 = r#" + let tx_script_with_trigger_2 = format!( + r#" use.mock::account + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin - push.1.2.3.4 push.0 + push.1.2.3.4 + push.MOCK_VALUE_SLOT0[0..2] call.account::set_item dropw dropw end - "#; + "#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, + ); let tx_script_trigger_1 = ScriptBuilder::with_mock_libraries()?.compile_tx_script(tx_script_with_trigger_1)?; @@ -190,12 +203,15 @@ async fn test_ecdsa_acl_with_allow_unauthorized_output_notes() -> anyhow::Result let (account, mock_chain, note) = setup_ecdsa_acl_test(true, true)?; // Verify the storage layout includes both authorization flags - let slot_1 = account.storage().get_item(1).expect("storage slot 1 access failed"); - // Slot 1 should be [num_tracked_procs, allow_unauthorized_output_notes, + let config_slot = account + .storage() + .get_item(AuthEcdsaK256KeccakAcl::config_slot()) + .expect("config storage slot access failed"); + // Config Slot should be [num_tracked_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, // allow_unauthorized_output_notes=true, and allow_unauthorized_input_notes=true, this should be // [2, 1, 1, 0] - assert_eq!(slot_1, Word::from([2u32, 1, 1, 0])); + assert_eq!(config_slot, Word::from([2u32, 1, 1, 0])); let tx_script_no_trigger = ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; @@ -227,12 +243,15 @@ async fn test_ecdsa_acl_with_disallow_unauthorized_input_notes() -> anyhow::Resu let (account, mock_chain, note) = setup_ecdsa_acl_test(true, false)?; // Verify the storage layout includes both flags - let slot_1 = account.storage().get_item(1).expect("storage slot 1 access failed"); - // Slot 1 should be [num_tracked_procs, allow_unauthorized_output_notes, + let config_slot = account + .storage() + .get_item(AuthEcdsaK256KeccakAcl::config_slot()) + .expect("config storage slot access failed"); + // Config Slot should be [num_tracked_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, // allow_unauthorized_output_notes=true, and allow_unauthorized_input_notes=false, this should // be [2, 1, 0, 0] - assert_eq!(slot_1, Word::from([2u32, 1, 0, 0])); + assert_eq!(config_slot, Word::from([2u32, 1, 0, 0])); let tx_script_no_trigger = ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; diff --git a/crates/miden-testing/tests/auth/ecdsa_multisig.rs b/crates/miden-testing/tests/auth/ecdsa_multisig.rs index f75e249173..c68a1d13f1 100644 --- a/crates/miden-testing/tests/auth/ecdsa_multisig.rs +++ b/crates/miden-testing/tests/auth/ecdsa_multisig.rs @@ -1,3 +1,4 @@ +use miden_lib::account::auth::AuthEcdsaK256KeccakMultisig; use miden_lib::account::components::ecdsa_k256_keccak_multisig_library; use miden_lib::account::interface::AccountInterface; use miden_lib::account::wallets::BasicWallet; @@ -136,7 +137,7 @@ async fn test_multisig_2_of_2_with_note_creation() -> anyhow::Result<()> { let tx_summary = match tx_context_init.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), + error => anyhow::bail!("expected abort with tx effects: {error}"), }; // Get signatures from both approvers @@ -466,15 +467,21 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // Verify that the public keys were actually updated in storage for (i, expected_key) in new_public_keys.iter().enumerate() { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); - let storage_item = updated_multisig_account.storage().get_map_item(1, storage_key).unwrap(); + let storage_item = updated_multisig_account + .storage() + .get_map_item(AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(), storage_key) + .unwrap(); let expected_word: Word = expected_key.to_commitment().into(); assert_eq!(storage_item, expected_word, "Public key {} doesn't match expected value", i); } - // Verify the threshold was updated by checking storage slot 0 - let threshold_config_storage = updated_multisig_account.storage().get_item(0).unwrap(); + // Verify the threshold was updated by checking the config storage slot + let threshold_config_storage = updated_multisig_account + .storage() + .get_item(AuthEcdsaK256KeccakMultisig::threshold_config_slot()) + .unwrap(); assert_eq!( threshold_config_storage[0], @@ -703,13 +710,19 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { // Verify public keys were updated for (i, expected_key) in new_public_keys.iter().enumerate() { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); - let storage_item = updated_multisig_account.storage().get_map_item(1, storage_key).unwrap(); + let storage_item = updated_multisig_account + .storage() + .get_map_item(AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(), storage_key) + .unwrap(); let expected_word: Word = expected_key.to_commitment().into(); assert_eq!(storage_item, expected_word, "Public key {} doesn't match", i); } // Verify threshold and num_approvers - let threshold_config = updated_multisig_account.storage().get_item(0).unwrap(); + let threshold_config = updated_multisig_account + .storage() + .get_item(AuthEcdsaK256KeccakMultisig::threshold_config_slot()) + .unwrap(); assert_eq!(threshold_config[0], Felt::new(threshold), "Threshold not updated"); assert_eq!(threshold_config[1], Felt::new(num_of_approvers), "Num approvers not updated"); @@ -729,8 +742,13 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { for removed_idx in 2..5 { let removed_owner_key = [Felt::new(removed_idx), Felt::new(0), Felt::new(0), Felt::new(0)].into(); - let removed_owner_slot = - updated_multisig_account.storage().get_map_item(1, removed_owner_key).unwrap(); + let removed_owner_slot = updated_multisig_account + .storage() + .get_map_item( + AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(), + removed_owner_key, + ) + .unwrap(); assert_eq!( removed_owner_slot, Word::empty(), @@ -743,7 +761,10 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { let mut non_empty_count = 0; for i in 0..5 { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); - let storage_item = updated_multisig_account.storage().get_map_item(1, storage_key).unwrap(); + let storage_item = updated_multisig_account + .storage() + .get_map_item(AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(), storage_key) + .unwrap(); if storage_item != Word::empty() { non_empty_count += 1; diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index 4d20657bc5..45afe1473e 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -1,3 +1,4 @@ +use miden_lib::account::auth::AuthRpoFalcon512Multisig; use miden_lib::account::components::rpo_falcon_512_multisig_library; use miden_lib::account::interface::AccountInterface; use miden_lib::account::wallets::BasicWallet; @@ -462,15 +463,20 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // Verify that the public keys were actually updated in storage for (i, expected_key) in new_public_keys.iter().enumerate() { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); - let storage_item = updated_multisig_account.storage().get_map_item(1, storage_key).unwrap(); + let storage_item = updated_multisig_account + .storage() + .get_map_item(AuthRpoFalcon512Multisig::approver_public_keys_slot(), storage_key) + .unwrap(); let expected_word: Word = expected_key.to_commitment().into(); assert_eq!(storage_item, expected_word, "Public key {} doesn't match expected value", i); } - // Verify the threshold was updated by checking storage slot 0 - let threshold_config_storage = updated_multisig_account.storage().get_item(0).unwrap(); + // Verify the threshold was updated by checking the config storage slot + let threshold_config_storage = updated_multisig_account + .storage() + .get_item(AuthRpoFalcon512Multisig::threshold_config_slot())?; assert_eq!( threshold_config_storage[0], @@ -699,13 +705,17 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { // Verify public keys were updated for (i, expected_key) in new_public_keys.iter().enumerate() { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); - let storage_item = updated_multisig_account.storage().get_map_item(1, storage_key).unwrap(); + let storage_item = updated_multisig_account + .storage() + .get_map_item(AuthRpoFalcon512Multisig::approver_public_keys_slot(), storage_key)?; let expected_word: Word = expected_key.to_commitment().into(); assert_eq!(storage_item, expected_word, "Public key {} doesn't match", i); } // Verify threshold and num_approvers - let threshold_config = updated_multisig_account.storage().get_item(0).unwrap(); + let threshold_config = updated_multisig_account + .storage() + .get_item(AuthRpoFalcon512Multisig::threshold_config_slot())?; assert_eq!(threshold_config[0], Felt::new(threshold), "Threshold not updated"); assert_eq!(threshold_config[1], Felt::new(num_of_approvers), "Num approvers not updated"); @@ -725,8 +735,10 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { for removed_idx in 2..5 { let removed_owner_key = [Felt::new(removed_idx), Felt::new(0), Felt::new(0), Felt::new(0)].into(); - let removed_owner_slot = - updated_multisig_account.storage().get_map_item(1, removed_owner_key).unwrap(); + let removed_owner_slot = updated_multisig_account + .storage() + .get_map_item(AuthRpoFalcon512Multisig::approver_public_keys_slot(), removed_owner_key) + .unwrap(); assert_eq!( removed_owner_slot, Word::empty(), @@ -739,7 +751,10 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { let mut non_empty_count = 0; for i in 0..5 { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); - let storage_item = updated_multisig_account.storage().get_map_item(1, storage_key).unwrap(); + let storage_item = updated_multisig_account + .storage() + .get_map_item(AuthRpoFalcon512Multisig::approver_public_keys_slot(), storage_key) + .unwrap(); if storage_item != Word::empty() { non_empty_count += 1; diff --git a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs index 2b8d51fe19..05fa4af977 100644 --- a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs +++ b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs @@ -1,6 +1,8 @@ use core::slice; +use anyhow::Context; use assert_matches::assert_matches; +use miden_lib::account::auth::AuthRpoFalcon512Acl; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::note::NoteBuilder; use miden_lib::utils::ScriptBuilder; @@ -13,6 +15,7 @@ use miden_objects::account::{ AccountType, }; use miden_objects::note::Note; +use miden_objects::testing::storage::MOCK_VALUE_SLOT0; use miden_objects::transaction::OutputNote; use miden_objects::{Felt, FieldElement, Word}; use miden_testing::{Auth, MockChain}; @@ -98,25 +101,36 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { } .build_component(); - let tx_script_with_trigger_1 = r#" + let tx_script_with_trigger_1 = format!( + r#" use.mock::account + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin - push.0 + push.MOCK_VALUE_SLOT0[0..2] call.account::get_item dropw end - "#; + "#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, + ); - let tx_script_with_trigger_2 = r#" + let tx_script_with_trigger_2 = format!( + r#" use.mock::account + const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") + begin - push.1.2.3.4 push.0 + push.1.2.3.4 + push.MOCK_VALUE_SLOT0[0..2] call.account::set_item dropw dropw end - "#; + "#, + mock_value_slot0 = &*MOCK_VALUE_SLOT0, + ); let tx_script_trigger_1 = ScriptBuilder::with_mock_libraries()?.compile_tx_script(tx_script_with_trigger_1)?; @@ -137,7 +151,7 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { tx_context_with_auth_1 .execute() .await - .expect("trigger 1 with auth should succeed"); + .context("trigger 1 with auth should succeed")?; // Test 2: Transaction WITH authenticator calling trigger procedure 2 (should succeed) let tx_context_with_auth_2 = mock_chain @@ -149,7 +163,7 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { tx_context_with_auth_2 .execute() .await - .expect("trigger 2 with auth should succeed"); + .context("trigger 2 with auth should succeed")?; // Test 3: Transaction WITHOUT authenticator calling trigger procedure (should fail) let tx_context_no_auth = mock_chain @@ -172,7 +186,7 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { let executed = tx_context_no_trigger .execute() .await - .expect("no trigger, no auth should succeed"); + .context("no trigger, no auth should succeed")?; assert_eq!( executed.account_delta().nonce_delta(), Felt::ZERO, @@ -187,12 +201,15 @@ async fn test_rpo_falcon_acl_with_allow_unauthorized_output_notes() -> anyhow::R let (account, mock_chain, note) = setup_rpo_falcon_acl_test(true, true)?; // Verify the storage layout includes both authorization flags - let slot_1 = account.storage().get_item(1).expect("storage slot 1 access failed"); - // Slot 1 should be [num_tracked_procs, allow_unauthorized_output_notes, + let config_slot = account + .storage() + .get_item(AuthRpoFalcon512Acl::config_slot()) + .expect("config storage slot access failed"); + // Config Slot should be [num_tracked_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, // allow_unauthorized_output_notes=true, and allow_unauthorized_input_notes=true, this should be // [2, 1, 1, 0] - assert_eq!(slot_1, Word::from([2u32, 1, 1, 0])); + assert_eq!(config_slot, Word::from([2u32, 1, 1, 0])); let tx_script_no_trigger = ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; @@ -224,12 +241,15 @@ async fn test_rpo_falcon_acl_with_disallow_unauthorized_input_notes() -> anyhow: let (account, mock_chain, note) = setup_rpo_falcon_acl_test(true, false)?; // Verify the storage layout includes both flags - let slot_1 = account.storage().get_item(1).expect("storage slot 1 access failed"); - // Slot 1 should be [num_tracked_procs, allow_unauthorized_output_notes, + let config_slot = account + .storage() + .get_item(AuthRpoFalcon512Acl::config_slot()) + .expect("config storage slot access failed"); + // Config Slot should be [num_tracked_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, // allow_unauthorized_output_notes=true, and allow_unauthorized_input_notes=false, this should // be [2, 1, 0, 0] - assert_eq!(slot_1, Word::from([2u32, 1, 0, 0])); + assert_eq!(config_slot, Word::from([2u32, 1, 0, 0])); let tx_script_no_trigger = ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 6cbf19857d..567cbd93a1 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -1,7 +1,7 @@ extern crate alloc; +use alloc::sync::Arc; use core::slice; -use std::sync::Arc; use miden_lib::account::faucets::{BasicFungibleFaucet, FungibleFaucetExt, NetworkFungibleFaucet}; use miden_lib::errors::tx_kernel_errors::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; @@ -276,10 +276,12 @@ async fn prove_burning_fungible_asset_on_existing_faucet_succeeds() -> anyhow::R builder.add_output_note(OutputNote::Full(note.clone())); let mock_chain = builder.build()?; - // The Fungible Faucet component is added as the second component after auth, so it's storage - // slot offset will be 2. Check that max_supply at the word's index 0 is 200. The remainder of - // the word is initialized with the metadata of the faucet which we don't need to check. - assert_eq!(faucet.storage().get_item(2).unwrap()[0], Felt::new(200)); + // Check that max_supply at the word's index 0 is 200. The remainder of the word is initialized + // with the metadata of the faucet which we don't need to check. + assert_eq!( + faucet.storage().get_item(BasicFungibleFaucet::metadata_slot_name()).unwrap()[0], + Felt::new(200) + ); // Check that the faucet reserved slot has been correctly initialized. // The already issued amount should be 100. @@ -502,11 +504,15 @@ async fn network_faucet_mint() -> anyhow::Result<()> { // The Network Fungible Faucet component is added as the second component after auth, so its // storage slot offset will be 2. Check that max_supply at the word's index 0 is 200. - assert_eq!(faucet.storage().get_item(1).unwrap()[0], Felt::new(1000)); + assert_eq!( + faucet.storage().get_item(NetworkFungibleFaucet::metadata_slot()).unwrap()[0], + Felt::new(1000) + ); // Check that the creator account ID is stored in slot 2 (second storage slot of the component) // The owner_account_id is stored as Word [0, 0, suffix, prefix] - let stored_owner_id = faucet.storage().get_item(2).unwrap(); + let stored_owner_id = + faucet.storage().get_item(NetworkFungibleFaucet::owner_config_slot()).unwrap(); assert_eq!(stored_owner_id[3], faucet_owner_account_id.prefix().as_felt()); assert_eq!(stored_owner_id[2], Felt::new(faucet_owner_account_id.suffix().as_int())); diff --git a/crates/miden-testing/tests/wallet/mod.rs b/crates/miden-testing/tests/wallet/mod.rs index 450611693b..75f6da77f8 100644 --- a/crates/miden-testing/tests/wallet/mod.rs +++ b/crates/miden-testing/tests/wallet/mod.rs @@ -40,7 +40,10 @@ fn wallet_creation() { assert!(wallet.is_regular_account()); assert_eq!(wallet.code().commitment(), expected_code_commitment); - assert_eq!(wallet.storage().get_item(0).unwrap(), Word::from(pub_key)); + assert_eq!( + wallet.storage().get_item(AuthRpoFalcon512::public_key_slot()).unwrap(), + Word::from(pub_key) + ); } #[cfg(not(target_arch = "wasm32"))] @@ -77,5 +80,8 @@ fn wallet_creation_2() { assert!(wallet.is_regular_account()); assert_eq!(wallet.code().commitment(), expected_code_commitment); - assert_eq!(wallet.storage().get_item(0).unwrap(), Word::from(pub_key)); + assert_eq!( + wallet.storage().get_item(AuthEcdsaK256Keccak::public_key_slot()).unwrap(), + Word::from(pub_key) + ); } diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index c68f7c83ad..7203e96af0 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -497,17 +497,17 @@ where self.base_host.on_account_vault_after_add_asset(asset) }, - TransactionEvent::AccountStorageAfterSetItem { slot_idx, new_value } => { - self.base_host.on_account_storage_after_set_item(slot_idx, new_value) + TransactionEvent::AccountStorageAfterSetItem { slot_name, new_value } => { + self.base_host.on_account_storage_after_set_item(slot_name, new_value) }, TransactionEvent::AccountStorageAfterSetMapItem { - slot_index, + slot_name, key, - prev_map_value, + old_map_value: prev_map_value, new_map_value, } => self.base_host.on_account_storage_after_set_map_item( - slot_index, + slot_name, key, prev_map_value, new_map_value, diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 7e8a62b6b9..25de7e9a1d 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -1,10 +1,14 @@ use miden_lib::transaction::memory::{ ACCOUNT_STACK_TOP_PTR, ACCT_CODE_COMMITMENT_OFFSET, + ACCT_STORAGE_SLOT_NAME_ID_PREFIX_OFFSET, + ACCT_STORAGE_SLOT_NAME_ID_SUFFIX_OFFSET, + ACCT_STORAGE_SLOT_TYPE_OFFSET, + ACCT_STORAGE_SLOT_VALUE_OFFSET, ACTIVE_INPUT_NOTE_PTR, NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, }; -use miden_objects::account::AccountId; +use miden_objects::account::{AccountId, SlotNameId, StorageSlotType}; use miden_objects::note::{NoteId, NoteInputs}; use miden_objects::{Hasher, Word}; use miden_processor::{ExecutionError, Felt, ProcessState}; @@ -24,12 +28,18 @@ pub(super) trait TransactionKernelProcess { /// Returns the account code commitment of the active account. fn get_active_account_code_commitment(&self) -> Result; + #[allow(dead_code)] fn get_num_storage_slots(&self) -> Result; fn get_vault_root(&self, vault_root_ptr: Felt) -> Result; fn get_active_note_id(&self) -> Result, TransactionKernelError>; + fn get_storage_slot( + &self, + slot_ptr: Felt, + ) -> Result<(SlotNameId, StorageSlotType, Word), TransactionKernelError>; + fn read_note_recipient_info_from_adv_map( &self, recipient_digest: Word, @@ -174,6 +184,61 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { }) } + fn get_storage_slot( + &self, + slot_ptr: Felt, + ) -> Result<(SlotNameId, StorageSlotType, Word), TransactionKernelError> { + let slot_ptr = u32::try_from(slot_ptr).map_err(|_err| { + TransactionKernelError::other(format!( + "slot ptr should fit into a u32, but was {slot_ptr}" + )) + })?; + + let slot_metadata = self + .get_mem_word(self.ctx(), slot_ptr) + .map_err(|err| { + TransactionKernelError::other_with_source( + format!("misaligned slot ptr {slot_ptr}"), + err, + ) + })? + .ok_or_else(|| { + TransactionKernelError::other(format!("slot ptr {slot_ptr} is uninitialized")) + })?; + + let slot_value_ptr = slot_ptr + ACCT_STORAGE_SLOT_VALUE_OFFSET as u32; + let slot_value = self + .get_mem_word(self.ctx(), slot_value_ptr) + .map_err(|err| { + TransactionKernelError::other_with_source( + format!("misaligned slot value ptr {slot_value_ptr}"), + err, + ) + })? + .ok_or_else(|| { + TransactionKernelError::other(format!( + "slot value ptr {slot_value_ptr} is uninitialized" + )) + })?; + + let slot_type = slot_metadata[ACCT_STORAGE_SLOT_TYPE_OFFSET as usize]; + let slot_type = u8::try_from(slot_type).map_err(|err| { + TransactionKernelError::other(format!("failed to convert {slot_type} into u8: {err}")) + })?; + let slot_type = StorageSlotType::try_from(slot_type).map_err(|err| { + TransactionKernelError::other_with_source( + format!("failed to convert {slot_type} into storage slot type",), + err, + ) + })?; + + let suffix = slot_metadata[ACCT_STORAGE_SLOT_NAME_ID_SUFFIX_OFFSET as usize]; + let prefix = slot_metadata[ACCT_STORAGE_SLOT_NAME_ID_PREFIX_OFFSET as usize]; + let slot_id = SlotNameId::new(suffix, prefix); + + Ok((slot_id, slot_type, slot_value)) + } + fn read_note_recipient_info_from_adv_map( &self, recipient_digest: Word, diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index b22c8245f3..e4fb23c509 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -36,6 +36,9 @@ use miden_objects::account::{ AccountId, AccountStorageHeader, PartialAccount, + SlotName, + SlotNameId, + StorageSlotType, }; use miden_objects::asset::Asset; use miden_objects::note::{NoteId, NoteMetadata, NoteRecipient}; @@ -157,6 +160,21 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { &self.initial_account_storage_header } + /// Returns the initial storage slot of the native account identified by [`SlotNameId`], which + /// represents the state at the beginning of the transaction. + pub fn initial_account_storage_slot( + &self, + slot_id: SlotNameId, + ) -> Result<(&SlotName, &StorageSlotType, &Word), TransactionKernelError> { + self.initial_account_storage_header() + .find_slot_header_by_id(slot_id) + .ok_or_else(|| { + TransactionKernelError::other(format!( + "failed to find storage map with name {slot_id} in storage header" + )) + }) + } + /// Returns a reference to the account delta tracker of this transaction host. pub fn account_delta_tracker(&self) -> &AccountDeltaTracker { &self.account_delta @@ -316,10 +334,10 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { /// Tracks the insertion of an item in the account delta. pub fn on_account_storage_after_set_item( &mut self, - slot_index: u8, - new_slot_value: Word, + slot_name: SlotName, + new_value: Word, ) -> Result, TransactionKernelError> { - self.account_delta.storage().set_item(slot_index, new_slot_value); + self.account_delta.storage().set_item(slot_name, new_value); Ok(Vec::new()) } @@ -327,14 +345,14 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { /// Tracks the insertion of a storage map item in the account delta. pub fn on_account_storage_after_set_map_item( &mut self, - slot_index: u8, + slot_name: SlotName, key: Word, - prev_map_value: Word, + old_map_value: Word, new_map_value: Word, ) -> Result, TransactionKernelError> { self.account_delta .storage() - .set_map_item(slot_index, key, prev_map_value, new_map_value); + .set_map_item(slot_name, key, old_map_value, new_map_value); Ok(Vec::new()) } diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index 55e142ee6d..ba020da769 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -1,10 +1,12 @@ use alloc::collections::BTreeMap; +use alloc::vec::Vec; use miden_objects::Word; use miden_objects::account::{ AccountStorageDelta, AccountStorageHeader, PartialAccount, + SlotName, StorageMap, StorageSlotType, }; @@ -29,7 +31,7 @@ pub struct StorageDeltaTracker { storage_header: AccountStorageHeader, /// A map from slot index to a map of key-value pairs where the key is a storage map key and /// the value represents the value of that key at the beginning of transaction execution. - init_maps: BTreeMap>, + init_maps: BTreeMap>, /// The account storage delta. delta: AccountStorageDelta, } @@ -58,12 +60,15 @@ impl StorageDeltaTracker { // Insert account storage into delta if it is new to match the kernel behavior. if account.is_new() { - (0..u8::MAX).zip(account.storage().header().slots()).for_each( - |(slot_idx, (_, slot_type, slot_value))| match slot_type { + account + .storage() + .header() + .slots() + .for_each(|(slot_name, slot_type, slot_value)| match slot_type { StorageSlotType::Value => { // For new accounts, all values should be added to the delta, even empty // words, so that the final delta includes the storage slot. - storage_delta_tracker.set_item(slot_idx, *slot_value); + storage_delta_tracker.set_item(slot_name.clone(), *slot_value); }, StorageSlotType::Map => { let storage_map = account @@ -73,19 +78,18 @@ impl StorageDeltaTracker { .expect("storage map should be present in partial storage"); // Make sure each map is represented by at least an empty storage map delta. - storage_delta_tracker.delta.insert_empty_map_delta(slot_idx); + storage_delta_tracker.delta.insert_empty_map_delta(slot_name.clone()); storage_map.entries().for_each(|(key, value)| { storage_delta_tracker.set_map_item( - slot_idx, + slot_name.clone(), *key, Word::empty(), *value, ); }); }, - }, - ); + }); } storage_delta_tracker @@ -95,16 +99,22 @@ impl StorageDeltaTracker { // -------------------------------------------------------------------------------------------- /// Updates a value slot. - pub fn set_item(&mut self, slot_index: u8, new_value: Word) { - self.delta.set_item(slot_index, new_value); + pub fn set_item(&mut self, slot_name: SlotName, new_value: Word) { + self.delta.set_item(slot_name, new_value); } /// Updates a map slot. - pub fn set_map_item(&mut self, slot_index: u8, key: Word, prev_value: Word, new_value: Word) { + pub fn set_map_item( + &mut self, + slot_name: SlotName, + key: Word, + prev_value: Word, + new_value: Word, + ) { // Don't update the delta if the new value matches the old one. if prev_value != new_value { - self.set_init_map_item(slot_index, key, prev_value); - self.delta.set_map_item(slot_index, key, new_value); + self.set_init_map_item(slot_name.clone(), key, prev_value); + self.delta.set_map_item(slot_name, key, new_value); } } @@ -118,8 +128,8 @@ impl StorageDeltaTracker { /// Sets the initial value of the given key in the given slot to the given value, if no value is /// already tracked for that key. - fn set_init_map_item(&mut self, slot_index: u8, key: Word, prev_value: Word) { - let slot_map = self.init_maps.entry(slot_index).or_default(); + fn set_init_map_item(&mut self, slot_name: SlotName, key: Word, prev_value: Word) { + let slot_map = self.init_maps.entry(slot_name).or_default(); slot_map.entry(key).or_insert(prev_value); } @@ -143,13 +153,13 @@ impl StorageDeltaTracker { // created with an empty value. if !is_account_new { // Keep only the values whose new value is different from the initial value. - value_slots.retain(|slot_idx, new_value| { + value_slots.retain(|slot_name, new_value| { // SAFETY: The header in the initial storage is the one from the account against // which the transaction is executed, so accessing that slot index // should be fine. - let (_, _, initial_value) = storage_header - .slot_header(*slot_idx as usize) - .expect("index should be in bounds"); + let (_slot_type, initial_value) = storage_header + .find_slot_header_by_name(slot_name) + .expect("slot name should exist"); new_value != initial_value }); } @@ -182,7 +192,7 @@ impl StorageDeltaTracker { /// Creates empty slots of the same slot types as the to-be-created account. fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorageHeader { - let slots = account + let mut slots: Vec<(SlotName, StorageSlotType, Word)> = account .storage() .header() .slots() @@ -191,5 +201,10 @@ fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorage StorageSlotType::Map => (slot_name.clone(), *slot_type, StorageMap::new().root()), }) .collect(); - AccountStorageHeader::new(slots) + + slots.sort_by_key(|(slot_name, ..)| slot_name.compute_id()); + + // SAFETY: We have sorted the slots and the max number of slots should not be exceeded as + // enforced by the storage header in partial storage. + AccountStorageHeader::new(slots).expect("storage header should be valid") } diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index 4b371141ba..9d7ecae559 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use miden_lib::transaction::{EventId, TransactionEventId}; -use miden_objects::account::{AccountId, StorageMap, StorageSlotType}; +use miden_objects::account::{AccountId, SlotName, StorageMap, StorageSlotType}; use miden_objects::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; use miden_objects::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; use miden_objects::transaction::TransactionSummary; @@ -57,14 +57,14 @@ pub(crate) enum TransactionEvent { }, AccountStorageAfterSetItem { - slot_idx: u8, + slot_name: SlotName, new_value: Word, }, AccountStorageAfterSetMapItem { - slot_index: u8, + slot_name: SlotName, key: Word, - prev_map_value: Word, + old_map_value: Word, new_map_value: Word, }, @@ -254,96 +254,59 @@ impl TransactionEvent { TransactionEventId::AccountStorageBeforeSetItem => None, TransactionEventId::AccountStorageAfterSetItem => { - // Expected stack state: - // [event, slot_index, NEW_SLOT_VALUE] + // Expected stack state: [event, slot_ptr, VALUE] // get slot index from the stack and make sure it is valid - let slot_index = process.get_stack_item(1); - let slot_index = u8::try_from(slot_index).map_err(|err| { - TransactionKernelError::other(format!( - "failed to convert slot index into u8: {err}" - )) - })?; + let slot_ptr = process.get_stack_item(1); + let new_value = process.get_stack_word_be(2); - // get number of storage slots initialized by the account - let num_storage_slot = process.get_num_storage_slots()?; - if slot_index as u64 >= num_storage_slot { - return Err(TransactionKernelError::InvalidStorageSlotIndex { - max: num_storage_slot, - actual: slot_index as u64, - }); - } + let (slot_name_id, slot_type, _old_value) = process.get_storage_slot(slot_ptr)?; - // get the value to which the slot is being updated - let new_slot_value = process.get_stack_word_be(2); + let (slot_name, ..) = base_host.initial_account_storage_slot(slot_name_id)?; + let slot_name = slot_name.clone(); - Some(TransactionEvent::AccountStorageAfterSetItem { - slot_idx: slot_index, - new_value: new_slot_value, - }) + if !slot_type.is_value() { + return Err(TransactionKernelError::other(format!( + "expected slot to be of type value, found {slot_type}" + ))); + } + + Some(TransactionEvent::AccountStorageAfterSetItem { slot_name, new_value }) }, TransactionEventId::AccountStorageBeforeGetMapItem => { - // Expected stack state: [event, KEY, ROOT, index] - let map_key = process.get_stack_word_be(1); - let current_map_root = process.get_stack_word_be(5); - let slot_index = process.get_stack_item(9); + // Expected stack state: [event, slot_ptr, KEY] + let slot_ptr = process.get_stack_item(1); + let map_key = process.get_stack_word_be(2); - on_account_storage_map_item_accessed( - base_host, - process, - slot_index, - current_map_root, - map_key, - )? + on_account_storage_map_item_accessed(base_host, process, slot_ptr, map_key)? }, TransactionEventId::AccountStorageBeforeSetMapItem => { - // Expected stack state: [event, index, KEY, NEW_VALUE, OLD_ROOT] - let slot_index = process.get_stack_item(1); + // Expected stack state: [event, slot_ptr, KEY] + let slot_ptr = process.get_stack_item(1); let map_key = process.get_stack_word_be(2); - let current_map_root = process.get_stack_word_be(10); - on_account_storage_map_item_accessed( - base_host, - process, - slot_index, - current_map_root, - map_key, - )? + on_account_storage_map_item_accessed(base_host, process, slot_ptr, map_key)? }, TransactionEventId::AccountStorageAfterSetMapItem => { - // Expected stack state: [event, slot_index, KEY, PREV_MAP_VALUE, NEW_MAP_VALUE] + // Expected stack state: [event, slot_ptr, KEY, OLD_MAP_VALUE, NEW_VALUE] // get slot index from the stack and make sure it is valid - let slot_index = process.get_stack_item(1); - let slot_index = u8::try_from(slot_index).map_err(|err| { - TransactionKernelError::other(format!( - "failed to convert slot index into u8: {err}" - )) - })?; - - // get number of storage slots initialized by the account - let num_storage_slot = process.get_num_storage_slots()?; - if slot_index as u64 >= num_storage_slot { - return Err(TransactionKernelError::InvalidStorageSlotIndex { - max: num_storage_slot, - actual: slot_index as u64, - }); - } - - // get the KEY to which the slot is being updated + let slot_ptr = process.get_stack_item(1); let key = process.get_stack_word_be(2); + let old_map_value = process.get_stack_word_be(6); + let new_map_value = process.get_stack_word_be(10); - // get the previous VALUE of the slot - let prev_map_value = process.get_stack_word_be(6); + // Resolve slot name ID to slot name. + let (slot_name_id, ..) = process.get_storage_slot(slot_ptr)?; + let (slot_name, ..) = base_host.initial_account_storage_slot(slot_name_id)?; - // get the VALUE to which the slot is being updated - let new_map_value = process.get_stack_word_be(10); + let slot_name = slot_name.clone(); Some(TransactionEvent::AccountStorageAfterSetMapItem { - slot_index, + slot_name, key, - prev_map_value, + old_map_value, new_map_value, }) }, @@ -600,37 +563,33 @@ fn on_account_vault_asset_accessed<'store, STORE>( fn on_account_storage_map_item_accessed<'store, STORE>( base_host: &TransactionBaseHost<'store, STORE>, process: &ProcessState, - slot_index: Felt, - current_map_root: Word, + slot_ptr: Felt, map_key: Word, ) -> Result, TransactionKernelError> { + let (slot_name_id, slot_type, current_map_root) = process.get_storage_slot(slot_ptr)?; + + if !slot_type.is_map() { + return Err(TransactionKernelError::other(format!( + "expected slot to be of type map, found {slot_type}" + ))); + } + let active_account_id = process.get_active_account_id()?; let hashed_map_key = StorageMap::hash_key(map_key); let leaf_index = StorageMap::hashed_map_key_to_leaf_index(hashed_map_key); - let slot_index = u8::try_from(slot_index).map_err(|err| { - TransactionKernelError::other(format!("failed to convert slot index into u8: {err}")) - })?; - // For the native account we need to explicitly request the initial map root, // while for foreign accounts the current map root is always the initial one. let map_root = if active_account_id == base_host.native_account_id() { // For native accounts, we have to request witnesses against the initial // root instead of the _current_ one, since the data // store only has witnesses for initial one. - let (_slot_name, slot_type, slot_value) = base_host - .initial_account_storage_header() - // Slot index should always fit into a usize. - .slot_header(slot_index as usize) - .map_err(|err| { - TransactionKernelError::other_with_source( - "failed to access storage map in storage header", - err, - ) - })?; + let (_slot_name, slot_type, slot_value) = + base_host.initial_account_storage_slot(slot_name_id)?; + if *slot_type != StorageSlotType::Map { return Err(TransactionKernelError::other(format!( - "expected map slot type at slot index {slot_index}" + "expected slot {slot_name_id} to be of type map" ))); } *slot_value diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index cf70ffa568..3572a8eb4e 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -115,19 +115,19 @@ where self.base_host.on_account_vault_after_add_asset(asset) }, - TransactionEvent::AccountStorageAfterSetItem { slot_idx, new_value } => { - self.base_host.on_account_storage_after_set_item(slot_idx, new_value) + TransactionEvent::AccountStorageAfterSetItem { slot_name, new_value } => { + self.base_host.on_account_storage_after_set_item(slot_name, new_value) }, TransactionEvent::AccountStorageAfterSetMapItem { - slot_index, + slot_name, key, - prev_map_value, + old_map_value, new_map_value, } => self.base_host.on_account_storage_after_set_map_item( - slot_index, + slot_name, key, - prev_map_value, + old_map_value, new_map_value, ), From 23d91263a6b8f4d50cc3304d6ded6e01f802998f Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 10 Dec 2025 08:36:12 +0700 Subject: [PATCH 030/114] chore: rename `SlotNameId` to `StorageSlotId` (#2149) * chore: Rename `SlotNameId` -> `SlotId` * chore: Rename `slot_name_id` -> `slot_id` * chore: Rename `name_id_{prefix,suffix}` -> `slot_id*` * chore: Rewrite slot ID description * chore: Rewrite unknown slot ID error message * chore: Rewrite miscellaneous slot name ID occurrences * chore: Rename `SlotId` -> `StorageSlotId` * chore: Rename `SlotName` -> `StorageSlotName` * chore: add changelog * fix: doc test using `SlotName` * fix: rustfmt * chore: address review comments --- CHANGELOG.md | 2 +- .../asm/kernels/transaction/api.masm | 65 ++++----- .../asm/kernels/transaction/lib/account.masm | 137 +++++++++--------- .../transaction/lib/account_delta.masm | 24 +-- .../miden-lib/asm/miden/active_account.masm | 44 +++--- .../asm/miden/contracts/faucets/mod.masm | 2 +- .../miden-lib/asm/miden/native_account.masm | 26 ++-- .../src/account/auth/ecdsa_k256_keccak.rs | 12 +- .../src/account/auth/ecdsa_k256_keccak_acl.rs | 32 ++-- .../auth/ecdsa_k256_keccak_multisig.rs | 44 +++--- .../src/account/auth/rpo_falcon_512.rs | 12 +- .../src/account/auth/rpo_falcon_512_acl.rs | 32 ++-- .../account/auth/rpo_falcon_512_multisig.rs | 42 +++--- .../src/account/faucets/basic_fungible.rs | 11 +- crates/miden-lib/src/account/faucets/mod.rs | 6 +- .../src/account/faucets/network_fungible.rs | 16 +- .../src/account/interface/component.rs | 6 +- .../src/testing/mock_account_code.rs | 12 +- crates/miden-lib/src/transaction/memory.rs | 8 +- .../miden-objects/src/account/builder/mod.rs | 17 ++- crates/miden-objects/src/account/delta/mod.rs | 34 ++--- .../src/account/delta/storage.rs | 93 ++++++------ crates/miden-objects/src/account/mod.rs | 26 ++-- .../src/account/storage/header.rs | 37 ++--- .../miden-objects/src/account/storage/mod.rs | 60 ++++---- .../src/account/storage/partial.rs | 4 +- .../src/account/storage/slot/mod.rs | 6 +- .../src/account/storage/slot/named_slot.rs | 62 ++++---- .../slot/{slot_name_id.rs => slot_id.rs} | 34 ++--- .../src/account/storage/slot/slot_name.rs | 122 +++++++++------- crates/miden-objects/src/errors.rs | 24 +-- crates/miden-objects/src/testing/slot_name.rs | 6 +- crates/miden-objects/src/testing/storage.rs | 30 ++-- .../src/transaction/proven_tx.rs | 4 +- .../src/kernel_tests/block/header_errors.rs | 6 +- .../src/kernel_tests/tx/test_account.rs | 22 +-- .../src/kernel_tests/tx/test_account_delta.rs | 72 ++++----- .../src/kernel_tests/tx/test_epilogue.rs | 2 +- .../src/kernel_tests/tx/test_fee.rs | 10 +- .../src/kernel_tests/tx/test_fpi.rs | 20 +-- .../src/kernel_tests/tx/test_lazy_loading.rs | 8 +- .../src/kernel_tests/tx/test_prologue.rs | 4 +- .../src/kernel_tests/tx/test_tx.rs | 4 +- crates/miden-tx/src/host/kernel_process.rs | 16 +- crates/miden-tx/src/host/mod.rs | 16 +- .../src/host/storage_delta_tracker.rs | 14 +- crates/miden-tx/src/host/tx_event.rs | 22 +-- 47 files changed, 679 insertions(+), 629 deletions(-) rename crates/miden-objects/src/account/storage/slot/{slot_name_id.rs => slot_id.rs} (66%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1609f0aa6e..4c7214bdf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Features - [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987)). -- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025)). +- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149)). ## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). diff --git a/crates/miden-lib/asm/kernels/transaction/api.masm b/crates/miden-lib/asm/kernels/transaction/api.masm index 77dece1eb2..204e6b5e1f 100644 --- a/crates/miden-lib/asm/kernels/transaction/api.masm +++ b/crates/miden-lib/asm/kernels/transaction/api.masm @@ -330,22 +330,22 @@ end #! Gets an item from the account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix, pad(14)] +#! Inputs: [slot_id_prefix, slot_id_suffix, pad(14)] #! Outputs: [VALUE, pad(12)] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value of the item. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: dynexec export.account_get_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [name_id_prefix, name_id_suffix, pad(14)] + # => [slot_id_prefix, slot_id_suffix, pad(14)] # fetch the account storage item exec.account::get_item @@ -358,40 +358,40 @@ end #! Sets an item in the account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix, VALUE, pad(10)] +#! Inputs: [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] #! Outputs: [OLD_VALUE, pad(12)] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value to set. #! - OLD_VALUE is the previous value of the item. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the invocation of this procedure does not originate from the native account. -#! - the native account is a faucet and the provided name ID points to the reserved faucet storage slot. +#! - the native account is a faucet and the provided slot ID points to the reserved faucet storage slot. #! #! Invocation: dynexec export.account_set_item # check that this procedure was executed against the native account exec.memory::assert_native_account - # => [name_id_prefix, name_id_suffix, VALUE, pad(10)] + # => [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] # if the transaction is being executed against a faucet account then assert # the slot that is being written to is not the reserved faucet slot. dup.1 dup.1 exec.account::is_faucet_storage_data_slot - # => [is_faucet_storage_data_slot, name_id_prefix, name_id_suffix, VALUE, pad(10)] + # => [is_faucet_storage_data_slot, slot_id_prefix, slot_id_suffix, VALUE, pad(10)] exec.account::get_id swap drop exec.account_id::is_faucet - # => [is_faucet_account, is_faucet_storage_data_slot, name_id_prefix, name_id_suffix, VALUE, pad(10)] + # => [is_faucet_account, is_faucet_storage_data_slot, slot_id_prefix, slot_id_suffix, VALUE, pad(10)] and assertz.err=ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED - # => [name_id_prefix, name_id_suffix, VALUE, pad(10)] + # => [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [name_id_prefix, name_id_suffix, VALUE, pad(10)] + # => [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] # set the account storage item exec.account::set_item @@ -399,26 +399,25 @@ export.account_set_item end #! Returns the VALUE located under the specified KEY within the map contained in the account -#! storage slot identified by the name ID. +#! storage slot identified by the slot ID. #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY, pad(10)] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] #! Outputs: [VALUE, pad(12)] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. -#! - the slot must point to the root of the storage map. #! - VALUE is the value of the map item at KEY. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. #! #! Invocation: dynexec export.account_get_map_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [name_id_prefix, name_id_suffix, KEY, pad(10)] + # => [slot_id_prefix, slot_id_suffix, KEY, pad(10)] # fetch the map item from account storage exec.account::get_map_item @@ -427,22 +426,22 @@ end #! Gets an item from the account storage at its initial state (beginning of transaction). #! -#! Inputs: [name_id_prefix, name_id_suffix, pad(14)] +#! Inputs: [slot_id_prefix, slot_id_suffix, pad(14)] #! Outputs: [INIT_VALUE, pad(12)] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - INIT_VALUE is the initial value of the item at the beginning of the transaction. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: dynexec export.account_get_initial_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [name_id_prefix, name_id_suffix, pad(14)] + # => [slot_id_prefix, slot_id_suffix, pad(14)] # fetch the initial account storage item exec.account::get_initial_item @@ -454,26 +453,26 @@ export.account_get_initial_item end #! Returns the initial VALUE located under the specified KEY within the map contained in the -#! account storage slot identified by the name ID at the beginning of the transaction. +#! account storage slot identified by the slot ID at the beginning of the transaction. #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY, pad(10)] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] #! Outputs: [INIT_VALUE, pad(12)] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - the slot must point to the root of the storage map. #! - INIT_VALUE is the initial value of the map item at KEY at the beginning of the transaction. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. #! #! Invocation: dynexec export.account_get_initial_map_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [name_id_prefix, name_id_suffix, KEY, pad(10)] + # => [slot_id_prefix, slot_id_suffix, KEY, pad(10)] # fetch the initial map item from account storage exec.account::get_initial_map_item @@ -483,11 +482,11 @@ end #! Stores NEW_VALUE under the specified KEY within the map contained in the given account storage #! slot. #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY, NEW_VALUE, pad(6)] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - the slot must point to the root of the storage map. #! - NEW_VALUE is the value of the new map item for the respective KEY. @@ -496,7 +495,7 @@ end #! - OLD_MAP_ROOT is the root of the old map before insertion #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. #! - the procedure is called from a non-account context. #! - the invocation of this procedure does not originate from the native account. @@ -505,11 +504,11 @@ end export.account_set_map_item # check that this procedure was executed against the native account exec.memory::assert_native_account - # => [name_id_prefix, name_id_suffix, KEY, NEW_VALUE, pad(6)] + # => [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin - # => [name_id_prefix, name_id_suffix, KEY, NEW_VALUE, pad(6)] + # => [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] # set the new map item exec.account::set_map_item diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index c20c42963b..6c92626e0f 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -127,11 +127,11 @@ const.ACCOUNT_PROCEDURE_DATA_LENGTH=8 # The offset of the slot type in the storage slot. const.ACCOUNT_SLOT_TYPE_OFFSET=1 -# The offset of the slot's name ID suffix in the storage slot. -const.ACCOUNT_SLOT_NAME_ID_SUFFIX_OFFSET=2 +# The offset of the slot's ID suffix in the storage slot. +const.ACCOUNT_SLOT_ID_SUFFIX_OFFSET=2 -# The offset of the slot's name ID prefix in the storage slot. -const.ACCOUNT_SLOT_NAME_ID_PREFIX_OFFSET=3 +# The offset of the slot's ID prefix in the storage slot. +const.ACCOUNT_SLOT_ID_PREFIX_OFFSET=3 # The offset of the slot value in the storage slot. const.ACCOUNT_SLOT_VALUE_OFFSET=4 @@ -188,10 +188,11 @@ const.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_i #! Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. #! #! Inputs: [] -#! Outputs: [faucet_name_id_prefix, faucet_name_id_suffix] +#! Outputs: [faucet_slot_id_prefix, faucet_slot_id_suffix] #! #! Where: -#! - faucet_storage_data_slot is the account storage slot at which faucet data is stored. +#! - faucet_slot_id{prefix,suffix} are the prefix and suffix felts of the slot identifier, at which +#! faucet data is stored. export.get_faucet_storage_slot_id push.FAUCET_STORAGE_DATA_SLOT[0..2] end @@ -432,20 +433,20 @@ end #! Gets an item from the account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix] +#! Inputs: [slot_id_prefix, slot_id_suffix] #! Outputs: [VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value of the item. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. export.get_item # get account storage slots section offset exec.memory::get_account_storage_slots_section_ptr - # => [acct_storage_slots_section_offset, name_id_prefix, name_id_suffix] + # => [acct_storage_slots_section_offset, slot_id_prefix, slot_id_suffix] exec.find_storage_slot # => [slot_ptr] @@ -457,21 +458,21 @@ end #! Gets an item and its slot type from the account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix] +#! Inputs: [slot_id_prefix, slot_id_suffix] #! Outputs: [VALUE, slot_type] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value of the item. #! - slot_type is the type of the slot. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. export.get_typed_item # get account storage slots section offset exec.memory::get_account_storage_slots_section_ptr - # => [acct_storage_slots_section_offset, name_id_prefix, name_id_suffix] + # => [acct_storage_slots_section_offset, slot_id_prefix, slot_id_suffix] exec.find_storage_slot # => [slot_ptr] @@ -486,20 +487,20 @@ end #! Gets an item from the account storage at its initial state (beginning of transaction). #! -#! Inputs: [name_id_prefix, name_id_suffix] +#! Inputs: [slot_id_prefix, slot_id_suffix] #! Outputs: [INIT_VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - INIT_VALUE is the initial value of the item at the beginning of the transaction. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. export.get_initial_item # get account initial storage slots section offset exec.memory::get_account_initial_storage_slots_ptr - # => [account_initial_storage_slots_ptr, name_id_prefix, name_id_suffix] + # => [account_initial_storage_slots_ptr, slot_id_prefix, slot_id_suffix] exec.find_storage_slot # => [slot_ptr] @@ -511,24 +512,24 @@ end #! Sets an item in the account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix, VALUE] +#! Inputs: [slot_id_prefix, slot_id_suffix, VALUE] #! Outputs: [OLD_VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value to set. #! - OLD_VALUE is the previous value of the item. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the storage slot type is not value. export.set_item emit.ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT - # => [name_id_prefix, name_id_suffix, VALUE] + # => [slot_id_prefix, slot_id_suffix, VALUE] exec.memory::get_account_storage_slots_section_ptr - # => [storage_slots_ptr, name_id_prefix, name_id_suffix, VALUE] + # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, VALUE] exec.find_storage_slot # => [slot_ptr, VALUE] @@ -561,22 +562,22 @@ export.set_item end #! Returns the VALUE located under the specified KEY within the map contained in the account -#! storage slot identified by the name ID. +#! storage slot identified by the slot ID. #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY] #! Outputs: [VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value of the map item at KEY. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. export.get_map_item exec.memory::get_account_storage_slots_section_ptr - # => [storage_slots_ptr, name_id_prefix, name_id_suffix, KEY] + # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY] exec.get_map_item_raw end @@ -584,31 +585,31 @@ end #! Returns the VALUE located under the specified KEY within the map contained in the given #! account storage slot at its initial state (beginning of transaction). #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY] #! Outputs: [INIT_VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - INIT_VALUE is the initial value of the map item at KEY at the beginning of the transaction. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. export.get_initial_map_item exec.memory::get_account_initial_storage_slots_ptr - # => [initial_storage_slots_ptr, name_id_prefix, name_id_suffix, KEY] + # => [initial_storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY] exec.get_map_item_raw end #! Stores NEW_VALUE under the specified KEY within the map contained in the given account storage slot. #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY, NEW_VALUE] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - the slot must point to the root of the storage map. #! - NEW_VALUE is the value to set under KEY. @@ -617,12 +618,12 @@ end #! - OLD_MAP_ROOT is the root of the old map before insertion #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the storage slot type is not map. #! - no map with the root of the slot is found. export.set_map_item exec.memory::get_account_storage_slots_section_ptr - # => [storage_slots_ptr, name_id_prefix, name_id_suffix, KEY, NEW_VALUE] + # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE] # resolve the slot name to its pointer exec.find_storage_slot @@ -1456,20 +1457,20 @@ end #! Returns 1 if the provided slot ID is equal to the reserved faucet storage data slot, 0 #! otherwise. #! -#! Inputs: [name_id_prefix, name_id_suffix] +#! Inputs: [slot_id_prefix, slot_id_suffix] #! Outputs: [is_faucet_storage_data_slot] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - is_faucet_storage_data_slot is a boolean value indicating whether the provided slot is the #! reserved faucet data slot. export.is_faucet_storage_data_slot exec.get_faucet_storage_slot_id - # => [faucet_name_id_prefix, faucet_name_id_suffix, name_id_prefix, name_id_suffix] + # => [faucet_slot_id_prefix, faucet_slot_id_suffix, slot_id_prefix, slot_id_suffix] movup.2 eq - # => [prefix_eq, faucet_name_id_suffix, name_id_suffix] + # => [prefix_eq, faucet_slot_id_suffix, slot_id_suffix] movdn.2 eq # => [prefix_eq, suffix_eq] @@ -1484,11 +1485,11 @@ end #! WARNING: The index must be in bounds. #! #! Inputs: [index] -#! Outputs: [INITIAL_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix] +#! Outputs: [INITIAL_VALUE, CURRENT_VALUE, slot_id_prefix, slot_id_suffix] #! #! Where: #! - index is the index of the slot. -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - INITIAL_VALUE is the initial value of the item at the beginning of the transaction. #! - CURRENT_VALUE is the current value of the item. @@ -1504,26 +1505,26 @@ export.get_item_delta dup.1 add dup # => [slot_ptr, slot_ptr, offset] - # load the name ID - add.ACCOUNT_SLOT_NAME_ID_SUFFIX_OFFSET mem_load - # => [name_id_suffix, slot_ptr, offset] + # load the slot ID + add.ACCOUNT_SLOT_ID_SUFFIX_OFFSET mem_load + # => [slot_id_suffix, slot_ptr, offset] - dup.1 add.ACCOUNT_SLOT_NAME_ID_PREFIX_OFFSET mem_load - # => [name_id_prefix, name_id_suffix, slot_ptr, offset] + dup.1 add.ACCOUNT_SLOT_ID_PREFIX_OFFSET mem_load + # => [slot_id_prefix, slot_id_suffix, slot_ptr, offset] # load the current value movup.2 exec.get_item_raw - # => [CURRENT_VALUE, name_id_prefix, name_id_suffix, offset] + # => [CURRENT_VALUE, slot_id_prefix, slot_id_suffix, offset] # get account initial storage slots section offset exec.memory::get_account_initial_storage_slots_ptr - # => [init_storage_slots_ptr, CURRENT_VALUE, name_id_prefix, name_id_suffix, offset] + # => [init_storage_slots_ptr, CURRENT_VALUE, slot_id_prefix, slot_id_suffix, offset] movup.7 add - # => [init_slot_ptr, CURRENT_VALUE, name_id_prefix, name_id_suffix] + # => [init_slot_ptr, CURRENT_VALUE, slot_id_prefix, slot_id_suffix] exec.get_item_raw - # => [INITIAL_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix] + # => [INITIAL_VALUE, CURRENT_VALUE, slot_id_prefix, slot_id_suffix] end #! Gets the slot ID of the storage slot at the provided index. @@ -1531,11 +1532,11 @@ end #! WARNING: The index must be in bounds. #! #! Inputs: [index] -#! Outputs: [name_id_prefix, name_id_suffix] +#! Outputs: [slot_id_prefix, slot_id_suffix] #! #! Where: #! - index is the index of the slot. -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. export.get_slot_id # convert the index into a memory offset @@ -1546,11 +1547,11 @@ export.get_slot_id add # => [slot_ptr] - dup add.ACCOUNT_SLOT_NAME_ID_SUFFIX_OFFSET mem_load - # => [name_id_suffix, slot_ptr] + dup add.ACCOUNT_SLOT_ID_SUFFIX_OFFSET mem_load + # => [slot_id_suffix, slot_ptr] - swap add.ACCOUNT_SLOT_NAME_ID_PREFIX_OFFSET mem_load - # => [name_id_prefix, name_id_suffix] + swap add.ACCOUNT_SLOT_ID_PREFIX_OFFSET mem_load + # => [slot_id_prefix, slot_id_suffix] end #! Sets the value of the storage slot located at the memory address specified by the provided @@ -1592,7 +1593,7 @@ end #! - OLD_MAP_ROOT is the root of the old map before insertion #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - no map with the root of the slot is found. #! #! Locals: @@ -1681,17 +1682,17 @@ end #! Finds the storage map root in the storage slot with the provided name in the provided storage #! slots section and returns the VALUE associated with the KEY in the corresponding map. #! -#! Inputs: [storage_slots_ptr, name_id_prefix, name_id_suffix, KEY] +#! Inputs: [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY] #! Outputs: [VALUE] #! #! Where: #! - KEY is the key to look up in the map. -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value of the map item at KEY. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. proc.get_map_item_raw exec.find_storage_slot @@ -1726,32 +1727,32 @@ proc.get_map_item_raw # => [VALUE] end -#! Finds the slot identified by the key [name_id_prefix, name_id_suffix, 0, 0] (stack order) and +#! Finds the slot identified by the key [slot_id_prefix, slot_id_suffix, 0, 0] (stack order) and #! returns the pointer to that slot. #! -#! Inputs: [storage_slots_ptr, name_id_prefix, name_id_suffix] +#! Inputs: [storage_slots_ptr, slot_id_prefix, slot_id_suffix] #! Outputs: [slot_ptr] #! #! Where: #! - storage_slots_ptr is the pointer to the storage slots section. #! - slot_ptr is the pointer to the resolved storage slot. -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. proc.find_storage_slot # construct the start and end pointers of the storage slot section in which we will search dup exec.memory::get_num_storage_slots mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH add - # => [storage_slots_end_ptr, storage_slots_start_ptr, name_id_prefix, name_id_suffix] + # => [storage_slots_end_ptr, storage_slots_start_ptr, slot_id_prefix, slot_id_suffix] # TODO(named_slots): Check if we can get here if num_storage_slots == 0. If so, return a better # error than what find_key_value returns. movdn.3 movdn.2 - # => [name_id_prefix, name_id_suffix, storage_slots_start_ptr, storage_slots_end_ptr] + # => [slot_id_prefix, slot_id_suffix, storage_slots_start_ptr, storage_slots_end_ptr] - # find the slot whose slot key matches [name_id_prefix, name_id_suffix, 0, 0] + # find the slot whose slot key matches [slot_id_prefix, slot_id_suffix, 0, 0] # if the slot key does not exist, this procedure will validate its absence exec.sorted_array::find_half_key_value # => [is_slot_found, slot_ptr, storage_slots_start_ptr, storage_slots_end_ptr] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm index 7aad58b79f..f264825098 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm @@ -166,38 +166,38 @@ end #! Outputs: [RATE, RATE, PERM] proc.update_value_slot_delta exec.account::get_item_delta - # => [INIT_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] + # => [INIT_VALUE, CURRENT_VALUE, slot_id_prefix, slot_id_suffix, RATE, RATE, PERM] exec.word::test_eq not - # => [was_changed, INIT_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] + # => [was_changed, INIT_VALUE, CURRENT_VALUE, slot_id_prefix, slot_id_suffix, RATE, RATE, PERM] # set was_changed to true if the account is new # generally, the delta for a new account must include all its storage slots, regardless of the # initial value and even if it is an empty word, because the initial delta for an account must # represent its full state exec.memory::is_new_account or - # => [was_changed, INIT_VALUE, CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] + # => [was_changed, INIT_VALUE, CURRENT_VALUE, slot_id_prefix, slot_id_suffix, RATE, RATE, PERM] # only include in delta if the slot's value has changed or the account is new if.true # drop init value dropw - # => [CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] + # => [CURRENT_VALUE, slot_id_prefix, slot_id_suffix, RATE, RATE, PERM] # build value slot metadata push.DOMAIN_VALUE push.0 - # => [0, domain, CURRENT_VALUE, name_id_prefix, name_id_suffix, RATE, RATE, PERM] + # => [0, domain, CURRENT_VALUE, slot_id_prefix, slot_id_suffix, RATE, RATE, PERM] movup.7 movup.7 - # => [name_id_prefix, name_id_suffix, 0, domain, CURRENT_VALUE, RATE, RATE, PERM] + # => [slot_id_prefix, slot_id_suffix, 0, domain, CURRENT_VALUE, RATE, RATE, PERM] # clear rate elements swapdw dropw dropw - # => [name_id_prefix, name_id_suffix, 0, domain, CURRENT_VALUE, PERM] + # => [slot_id_prefix, slot_id_suffix, 0, domain, CURRENT_VALUE, PERM] # arrange rate words in correct order swapw - # => [CURRENT_VALUE, name_id_prefix, name_id_suffix, 0, domain, PERM] + # => [CURRENT_VALUE, slot_id_prefix, slot_id_suffix, 0, domain, PERM] hperm # => [RATE, RATE, PERM] @@ -215,8 +215,8 @@ end #! Outputs: [RATE, RATE, PERM] #! #! Locals: -#! - 0: name_id_suffix -#! - 1: name_id_prefix +#! - 0: slot_id_suffix +#! - 1: slot_id_prefix #! - 2: has_next #! - 3: iter #! - 4: num_changed_entries @@ -228,7 +228,7 @@ proc.update_map_slot_delta.5 # => [slot_idx, RATE, RATE, PERM] dup exec.account::get_slot_id - # => [name_id_prefix, name_id_suffix, slot_idx, RATE, RATE, PERM] + # => [slot_id_prefix, slot_id_suffix, slot_idx, RATE, RATE, PERM] loc_store.1 loc_store.0 # => [slot_idx, RATE, RATE, PERM] @@ -309,7 +309,7 @@ proc.update_map_slot_delta.5 # => [PERM] push.DOMAIN_MAP loc_load.4 loc_load.0 loc_load.1 padw - # => [EMPTY_WORD, [name_id_prefix, name_id_suffix, num_changed_entries, domain], PERM] + # => [EMPTY_WORD, [slot_id_prefix, slot_id_suffix, num_changed_entries, domain], PERM] hperm # => [RATE, RATE, PERM] diff --git a/crates/miden-lib/asm/miden/active_account.masm b/crates/miden-lib/asm/miden/active_account.masm index e6266c0a05..6c45d7d80e 100644 --- a/crates/miden-lib/asm/miden/active_account.masm +++ b/crates/miden-lib/asm/miden/active_account.masm @@ -274,28 +274,28 @@ end #! Gets an item from the active account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix] +#! Inputs: [slot_id_prefix, slot_id_suffix] #! Outputs: [VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value of the item. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: exec export.get_item push.0 movdn.2 - # => [name_id_prefix, name_id_suffix, 0] + # => [slot_id_prefix, slot_id_suffix, 0] exec.kernel_proc_offsets::account_get_item_offset - # => [offset, name_id_prefix, name_id_suffix, 0] + # => [offset, slot_id_prefix, slot_id_suffix, 0] # pad the stack padw swapw padw padw swapdw - # => [offset, name_id_prefix, name_id_suffix, pad(13)] + # => [offset, slot_id_prefix, slot_id_suffix, pad(13)] syscall.exec_kernel_proc # => [VALUE, pad(12)] @@ -308,28 +308,28 @@ end #! Gets the initial item from the active account storage slot as it was at the beginning of the #! transaction. #! -#! Inputs: [name_id_prefix, name_id_suffix] +#! Inputs: [slot_id_prefix, slot_id_suffix] #! Outputs: [INIT_VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - INIT_VALUE is the initial value of the item at the beginning of the transaction. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: exec export.get_initial_item push.0 movdn.2 - # => [name_id_prefix, name_id_suffix, 0] + # => [slot_id_prefix, slot_id_suffix, 0] exec.kernel_proc_offsets::account_get_initial_item_offset - # => [offset, name_id_prefix, name_id_suffix, 0] + # => [offset, slot_id_prefix, slot_id_suffix, 0] # pad the stack padw swapw padw padw swapdw - # => [offset, name_id_prefix, name_id_suffix, pad(13)] + # => [offset, slot_id_prefix, slot_id_suffix, pad(13)] syscall.exec_kernel_proc # => [INIT_VALUE, pad(12)] @@ -341,28 +341,28 @@ end #! Gets a map item from the active account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY] #! Outputs: [VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - the slot must point to the root of the storage map. #! - KEY is the key of the item to get. #! - VALUE is the value of the item. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the slot item at index is not a map. #! #! Invocation: exec export.get_map_item exec.kernel_proc_offsets::account_get_map_item_offset - # => [offset, name_id_prefix, name_id_suffix, KEY] + # => [offset, slot_id_prefix, slot_id_suffix, KEY] # pad the stack push.0 movdn.7 padw padw swapdw - # => [0, offset, name_id_prefix, name_id_suffix, KEY, pad(9)] + # => [0, offset, slot_id_prefix, slot_id_suffix, KEY, pad(9)] syscall.exec_kernel_proc # => [VALUE, pad(12)] @@ -375,26 +375,26 @@ end #! Gets the initial VALUE from the active account storage map as it was at the beginning of the #! transaction. #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY] #! Outputs: [INIT_VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - KEY is the key of the item to get. #! - INIT_VALUE is the initial value of the item at the beginning of the transaction. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the slot item at index is not a map. #! #! Invocation: exec export.get_initial_map_item exec.kernel_proc_offsets::account_get_initial_map_item_offset - # => [offset, name_id_prefix, name_id_suffix, KEY] + # => [offset, slot_id_prefix, slot_id_suffix, KEY] push.0 movdn.7 padw padw swapdw - # => [0, offset, name_id_prefix, name_id_suffix, KEY, pad(9)] + # => [0, offset, slot_id_prefix, slot_id_suffix, KEY, pad(9)] syscall.exec_kernel_proc # => [INIT_VALUE, pad(12)] diff --git a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm b/crates/miden-lib/asm/miden/contracts/faucets/mod.masm index 0cc2bb067b..3a02d56a3f 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm +++ b/crates/miden-lib/asm/miden/contracts/faucets/mod.masm @@ -18,7 +18,7 @@ const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no # ================================================================================================= # The slot in this component's storage where the metadata is stored. -# TODO(named_slots): Unify slot name or make distribute take name ID as a parameter to allow for +# TODO(named_slots): Unify slot name or make distribute take slot ID as a parameter to allow for # different slot names per fungible faucet? const.METADATA_SLOT=word("miden::basic_fungible_faucet::metadata") diff --git a/crates/miden-lib/asm/miden/native_account.masm b/crates/miden-lib/asm/miden/native_account.masm index a3ab85b772..6a15423752 100644 --- a/crates/miden-lib/asm/miden/native_account.masm +++ b/crates/miden-lib/asm/miden/native_account.masm @@ -114,28 +114,28 @@ end #! Sets an item in the native account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix, VALUE] +#! Inputs: [slot_id_prefix, slot_id_suffix, VALUE] #! Outputs: [OLD_VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - VALUE is the value to set. #! - OLD_VALUE is the previous value of the item. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the invocation of this procedure does not originate from the native account. -#! - the native account is a faucet and the provided name ID points to the reserved faucet storage slot. +#! - the native account is a faucet and the provided slot ID points to the reserved faucet storage slot. #! #! Invocation: exec export.set_item exec.kernel_proc_offsets::account_set_item_offset - # => [offset, name_id_prefix, name_id_suffix, VALUE] + # => [offset, slot_id_prefix, slot_id_suffix, VALUE] # pad the stack push.0 movdn.7 padw padw swapdw - # => [offset, name_id_prefix, name_id_suffix, VALUE, pad(9)] + # => [offset, slot_id_prefix, slot_id_suffix, VALUE, pad(9)] syscall.exec_kernel_proc # => [OLD_VALUE, pad(12)] @@ -147,11 +147,11 @@ end #! Sets a map item in the native account storage. #! -#! Inputs: [name_id_prefix, name_id_suffix, KEY, VALUE] +#! Inputs: [slot_id_prefix, slot_id_suffix, KEY, VALUE] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE] #! #! Where: -#! - name_id_{prefix, suffix} are the prefix and suffix felts of the name identifier, which are +#! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. #! - the slot must point to the root of the storage map. #! - KEY is the key to set at VALUE. @@ -160,7 +160,7 @@ end #! - OLD_MAP_VALUE is the old value at KEY. #! #! Panics if: -#! - a slot with the provided name ID does not exist in account storage. +#! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. #! - the procedure is called from a non-account context. #! - the invocation of this procedure does not originate from the native account. @@ -168,17 +168,17 @@ end #! Invocation: exec export.set_map_item exec.kernel_proc_offsets::account_set_map_item_offset - # => [offset, name_id_prefix, name_id_suffix, KEY, VALUE] + # => [offset, slot_id_prefix, slot_id_suffix, KEY, VALUE] # pad the stack push.0 padw - # => [pad(4), 0, offset, name_id_prefix, name_id_suffix, KEY, VALUE] + # => [pad(4), 0, offset, slot_id_prefix, slot_id_suffix, KEY, VALUE] movdnw.3 - # => [0, offset, name_id_prefix, name_id_suffix, KEY, VALUE, pad(4)] + # => [0, offset, slot_id_prefix, slot_id_suffix, KEY, VALUE, pad(4)] movdn.11 - # => [offset, name_id_prefix, name_id_suffix, KEY, VALUE, pad(5)] + # => [offset, slot_id_prefix, slot_id_suffix, KEY, VALUE, pad(5)] syscall.exec_kernel_proc # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs index ccb588f5b3..7d9077eccb 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs @@ -1,12 +1,12 @@ use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, NamedStorageSlot, SlotName}; +use miden_objects::account::{AccountComponent, NamedStorageSlot, StorageSlotName}; use miden_objects::utils::sync::LazyLock; use crate::account::components::ecdsa_k256_keccak_library; -static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::ecdsa_k256_keccak::public_key") - .expect("slot name should be valid") +static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak::public_key") + .expect("storage slot name should be valid") }); /// An [`AccountComponent`] implementing the ECDSA K256 Keccak signature scheme for authentication @@ -32,8 +32,8 @@ impl AuthEcdsaK256Keccak { Self { pub_key } } - /// Returns the [`SlotName`] where the public key is stored. - pub fn public_key_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the public key is stored. + pub fn public_key_slot() -> &'static StorageSlotName { &ECDSA_PUBKEY_SLOT_NAME } } diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs index 8214cca372..8d1aeb946c 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs @@ -5,27 +5,27 @@ use miden_objects::account::{ AccountCode, AccountComponent, NamedStorageSlot, - SlotName, StorageMap, + StorageSlotName, }; use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_acl_library; -static PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::public_key") - .expect("slot name should be valid") +static PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::public_key") + .expect("storage slot name should be valid") }); -static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::config") - .expect("slot name should be valid") +static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::config") + .expect("storage slot name should be valid") }); -static TRACKED_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::tracked_procedure_roots") - .expect("slot name should be valid") +static TRACKED_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::tracked_procedure_roots") + .expect("storage slot name should be valid") }); /// Configuration for [`AuthEcdsaK256KeccakAcl`] component. @@ -156,18 +156,18 @@ impl AuthEcdsaK256KeccakAcl { Ok(Self { pub_key, config }) } - /// Returns the [`SlotName`] where the public key is stored. - pub fn public_key_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the public key is stored. + pub fn public_key_slot() -> &'static StorageSlotName { &PUBKEY_SLOT_NAME } - /// Returns the [`SlotName`] where the component's configuration is stored. - pub fn config_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the component's configuration is stored. + pub fn config_slot() -> &'static StorageSlotName { &CONFIG_SLOT_NAME } - /// Returns the [`SlotName`] where the tracked procedure roots are stored. - pub fn tracked_procedure_roots_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the tracked procedure roots are stored. + pub fn tracked_procedure_roots_slot() -> &'static StorageSlotName { &TRACKED_PROCEDURE_ROOT_SLOT_NAME } } diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs index c24898bdaf..5d2d2f006f 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs @@ -2,30 +2,32 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, NamedStorageSlot, SlotName, StorageMap}; +use miden_objects::account::{AccountComponent, NamedStorageSlot, StorageMap, StorageSlotName}; use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_multisig_library; -static THRESHOLD_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::threshold_config") - .expect("slot name should be valid") +static THRESHOLD_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::threshold_config") + .expect("storage slot name should be valid") }); -static APPROVER_PUBKEYS_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::approver_public_keys") - .expect("slot name should be valid") +static APPROVER_PUBKEYS_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::approver_public_keys") + .expect("storage slot name should be valid") }); -static EXECUTED_TRANSACTIONS_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::executed_transactions") - .expect("slot name should be valid") +static EXECUTED_TRANSACTIONS_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new( + "miden::standards::auth::ecdsa_k256_keccak_multisig::executed_transactions", + ) + .expect("storage slot name should be valid") }); -static PROCEDURE_THRESHOLDS_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::procedure_thresholds") - .expect("slot name should be valid") +static PROCEDURE_THRESHOLDS_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak_multisig::procedure_thresholds") + .expect("storage slot name should be valid") }); // MULTISIG AUTHENTICATION COMPONENT @@ -126,23 +128,23 @@ impl AuthEcdsaK256KeccakMultisig { Ok(Self { config }) } - /// Returns the [`SlotName`] where the threshold configuration is stored. - pub fn threshold_config_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the threshold configuration is stored. + pub fn threshold_config_slot() -> &'static StorageSlotName { &THRESHOLD_CONFIG_SLOT_NAME } - /// Returns the [`SlotName`] where the approver public keys are stored. - pub fn approver_public_keys_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the approver public keys are stored. + pub fn approver_public_keys_slot() -> &'static StorageSlotName { &APPROVER_PUBKEYS_SLOT_NAME } - /// Returns the [`SlotName`] where the executed transactions are stored. - pub fn executed_transactions_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the executed transactions are stored. + pub fn executed_transactions_slot() -> &'static StorageSlotName { &EXECUTED_TRANSACTIONS_SLOT_NAME } - /// Returns the [`SlotName`] where the procedure thresholds are stored. - pub fn procedure_thresholds_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the procedure thresholds are stored. + pub fn procedure_thresholds_slot() -> &'static StorageSlotName { &PROCEDURE_THRESHOLDS_SLOT_NAME } } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs index dec8f6d7d4..7ddd558577 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs @@ -1,12 +1,12 @@ use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, NamedStorageSlot, SlotName}; +use miden_objects::account::{AccountComponent, NamedStorageSlot, StorageSlotName}; use miden_objects::utils::sync::LazyLock; use crate::account::components::rpo_falcon_512_library; -static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::rpo_falcon512::public_key") - .expect("slot name should be valid") +static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512::public_key") + .expect("storage slot name should be valid") }); /// An [`AccountComponent`] implementing the RpoFalcon512 signature scheme for authentication of @@ -36,8 +36,8 @@ impl AuthRpoFalcon512 { Self { pub_key } } - /// Returns the [`SlotName`] where the public key is stored. - pub fn public_key_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the public key is stored. + pub fn public_key_slot() -> &'static StorageSlotName { &FALCON_PUBKEY_SLOT_NAME } } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs index 8d11fa3c52..f711b9ee0c 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs @@ -5,27 +5,27 @@ use miden_objects::account::{ AccountCode, AccountComponent, NamedStorageSlot, - SlotName, StorageMap, + StorageSlotName, }; use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; use crate::account::components::rpo_falcon_512_acl_library; -static PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::rpo_falcon512_acl::public_key") - .expect("slot name should be valid") +static PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512_acl::public_key") + .expect("storage slot name should be valid") }); -static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::rpo_falcon512_acl::config") - .expect("slot name should be valid") +static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512_acl::config") + .expect("storage slot name should be valid") }); -static TRACKED_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::rpo_falcon512_acl::tracked_procedure_roots") - .expect("slot name should be valid") +static TRACKED_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512_acl::tracked_procedure_roots") + .expect("storage slot name should be valid") }); /// Configuration for [`AuthRpoFalcon512Acl`] component. @@ -157,18 +157,18 @@ impl AuthRpoFalcon512Acl { Ok(Self { pub_key, config }) } - /// Returns the [`SlotName`] where the public key is stored. - pub fn public_key_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the public key is stored. + pub fn public_key_slot() -> &'static StorageSlotName { &PUBKEY_SLOT_NAME } - /// Returns the [`SlotName`] where the component's configuration is stored. - pub fn config_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the component's configuration is stored. + pub fn config_slot() -> &'static StorageSlotName { &CONFIG_SLOT_NAME } - /// Returns the [`SlotName`] where the tracked procedure roots are stored. - pub fn tracked_procedure_roots_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the tracked procedure roots are stored. + pub fn tracked_procedure_roots_slot() -> &'static StorageSlotName { &TRACKED_PROCEDURE_ROOT_SLOT_NAME } } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs index 1ec2775f24..3f6c4694d4 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs @@ -2,30 +2,30 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, NamedStorageSlot, SlotName, StorageMap}; +use miden_objects::account::{AccountComponent, NamedStorageSlot, StorageMap, StorageSlotName}; use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; use crate::account::components::rpo_falcon_512_multisig_library; -static THRESHOLD_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::rpo_falcon512_multisig::threshold_config") - .expect("slot name should be valid") +static THRESHOLD_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512_multisig::threshold_config") + .expect("storage slot name should be valid") }); -static APPROVER_PUBKEYS_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::rpo_falcon512_multisig::approver_public_keys") - .expect("slot name should be valid") +static APPROVER_PUBKEYS_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512_multisig::approver_public_keys") + .expect("storage slot name should be valid") }); -static EXECUTED_TRANSACTIONS_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::rpo_falcon512_multisig::executed_transactions") - .expect("slot name should be valid") +static EXECUTED_TRANSACTIONS_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512_multisig::executed_transactions") + .expect("storage slot name should be valid") }); -static PROCEDURE_THRESHOLDS_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::standards::auth::rpo_falcon512_multisig::procedure_thresholds") - .expect("slot name should be valid") +static PROCEDURE_THRESHOLDS_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512_multisig::procedure_thresholds") + .expect("storage slot name should be valid") }); // MULTISIG AUTHENTICATION COMPONENT @@ -128,23 +128,23 @@ impl AuthRpoFalcon512Multisig { Ok(Self { config }) } - /// Returns the [`SlotName`] where the threshold configuration is stored. - pub fn threshold_config_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the threshold configuration is stored. + pub fn threshold_config_slot() -> &'static StorageSlotName { &THRESHOLD_CONFIG_SLOT_NAME } - /// Returns the [`SlotName`] where the approver public keys are stored. - pub fn approver_public_keys_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the approver public keys are stored. + pub fn approver_public_keys_slot() -> &'static StorageSlotName { &APPROVER_PUBKEYS_SLOT_NAME } - /// Returns the [`SlotName`] where the executed transactions are stored. - pub fn executed_transactions_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the executed transactions are stored. + pub fn executed_transactions_slot() -> &'static StorageSlotName { &EXECUTED_TRANSACTIONS_SLOT_NAME } - /// Returns the [`SlotName`] where the procedure thresholds are stored. - pub fn procedure_thresholds_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the procedure thresholds are stored. + pub fn procedure_thresholds_slot() -> &'static StorageSlotName { &PROCEDURE_THRESHOLDS_SLOT_NAME } } diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 5e7ee5e75a..528ab31ff1 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -6,7 +6,7 @@ use miden_objects::account::{ AccountStorageMode, AccountType, NamedStorageSlot, - SlotName, + StorageSlotName, }; use miden_objects::asset::{FungibleAsset, TokenSymbol}; use miden_objects::utils::sync::LazyLock; @@ -41,8 +41,9 @@ procedure_digest!( basic_fungible_faucet_library ); -static METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::basic_fungible_faucet::metadata").expect("slot name should be valid") +static METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::basic_fungible_faucet::metadata") + .expect("storage slot name should be valid") }); /// An [`AccountComponent`] implementing a basic fungible faucet. @@ -159,8 +160,8 @@ impl BasicFungibleFaucet { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the [`SlotName`] where the [`BasicFungibleFaucet`]'s metadata is stored. - pub fn metadata_slot_name() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the [`BasicFungibleFaucet`]'s metadata is stored. + pub fn metadata_slot_name() -> &'static StorageSlotName { &METADATA_SLOT_NAME } diff --git a/crates/miden-lib/src/account/faucets/mod.rs b/crates/miden-lib/src/account/faucets/mod.rs index 0b98d61103..ba799766f0 100644 --- a/crates/miden-lib/src/account/faucets/mod.rs +++ b/crates/miden-lib/src/account/faucets/mod.rs @@ -1,6 +1,6 @@ use alloc::string::String; -use miden_objects::account::{Account, AccountStorage, AccountType, SlotName}; +use miden_objects::account::{Account, AccountStorage, AccountType, StorageSlotName}; use miden_objects::{AccountError, Felt, TokenSymbolError}; use thiserror::Error; @@ -58,9 +58,9 @@ pub enum FungibleFaucetError { "account interface provided for faucet creation does not have basic fungible faucet component" )] NoAvailableInterface, - #[error("failed to retrieve slot with name {slot_name}")] + #[error("failed to retrieve storage slot with name {slot_name}")] StorageLookupFailed { - slot_name: SlotName, + slot_name: StorageSlotName, source: AccountError, }, #[error("invalid token symbol")] diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 3a02e9b660..1dde58e9c1 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -7,7 +7,7 @@ use miden_objects::account::{ AccountStorageMode, AccountType, NamedStorageSlot, - SlotName, + StorageSlotName, }; use miden_objects::asset::TokenSymbol; use miden_objects::utils::sync::LazyLock; @@ -36,9 +36,9 @@ procedure_digest!( network_fungible_faucet_library ); -static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("miden::network_fungible_faucet::owner_config") - .expect("slot name should be valid") +static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::network_fungible_faucet::owner_config") + .expect("storage slot name should be valid") }); /// An [`AccountComponent`] implementing a network fungible faucet. @@ -162,15 +162,15 @@ impl NetworkFungibleFaucet { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the [`SlotName`] where the [`NetworkFungibleFaucet`]'s metadata is stored. - pub fn metadata_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] where the [`NetworkFungibleFaucet`]'s metadata is stored. + pub fn metadata_slot() -> &'static StorageSlotName { // TODO(named_slots): Rename to metadata_slot. BasicFungibleFaucet::metadata_slot_name() } - /// Returns the [`SlotName`] where the [`NetworkFungibleFaucet`]'s owner configuration is + /// Returns the [`StorageSlotName`] where the [`NetworkFungibleFaucet`]'s owner configuration is /// stored. - pub fn owner_config_slot() -> &'static SlotName { + pub fn owner_config_slot() -> &'static StorageSlotName { &OWNER_CONFIG_SLOT_NAME } diff --git a/crates/miden-lib/src/account/interface/component.rs b/crates/miden-lib/src/account/interface/component.rs index 06ac07a6c0..52c2f0bfa3 100644 --- a/crates/miden-lib/src/account/interface/component.rs +++ b/crates/miden-lib/src/account/interface/component.rs @@ -3,7 +3,7 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountId, AccountProcedureInfo, AccountStorage, SlotName}; +use miden_objects::account::{AccountId, AccountProcedureInfo, AccountStorage, StorageSlotName}; use miden_objects::note::PartialNote; use miden_objects::{Felt, FieldElement, Word}; @@ -340,8 +340,8 @@ impl AccountComponentInterface { /// Extracts authentication scheme from a multisig component. fn extract_multisig_auth_scheme( storage: &AccountStorage, - config_slot: &SlotName, - approver_public_keys_slot: &SlotName, + config_slot: &StorageSlotName, + approver_public_keys_slot: &StorageSlotName, ) -> AuthScheme { // Read the multisig configuration from the config slot // Format: [threshold, num_approvers, 0, 0] diff --git a/crates/miden-lib/src/testing/mock_account_code.rs b/crates/miden-lib/src/testing/mock_account_code.rs index 42f8db2b11..9176d5c261 100644 --- a/crates/miden-lib/src/testing/mock_account_code.rs +++ b/crates/miden-lib/src/testing/mock_account_code.rs @@ -35,14 +35,14 @@ const MOCK_ACCOUNT_CODE: &str = " # is assumed that the operand stack at the beginning of their execution is pad'ed and # does not have any other valuable information. - #! Inputs: [name_id_prefix, name_id_suffix, VALUE, pad(10)] + #! Inputs: [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] #! Outputs: [OLD_VALUE, pad(12)] export.set_item exec.native_account::set_item # => [OLD_VALUE, pad(12)] end - #! Inputs: [name_id_prefix, name_id_suffix, pad(14)] + #! Inputs: [slot_id_prefix, slot_id_suffix, pad(14)] #! Outputs: [VALUE, pad(12)] export.get_item exec.active_account::get_item @@ -53,7 +53,7 @@ const MOCK_ACCOUNT_CODE: &str = " # => [VALUE, pad(12)] end - #! Inputs: [name_id_prefix, name_id_suffix, pad(14)] + #! Inputs: [slot_id_prefix, slot_id_suffix, pad(14)] #! Outputs: [VALUE, pad(12)] export.get_initial_item exec.active_account::get_initial_item @@ -64,21 +64,21 @@ const MOCK_ACCOUNT_CODE: &str = " # => [VALUE, pad(12)] end - #! Inputs: [name_id_prefix, name_id_suffix, KEY, NEW_VALUE, pad(6)] + #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] export.set_map_item exec.native_account::set_map_item # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] end - #! Inputs: [name_id_prefix, name_id_suffix, KEY, pad(10)] + #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] #! Outputs: [VALUE, pad(12)] export.get_map_item exec.active_account::get_map_item # => [VALUE, pad(12)] end - #! Inputs: [name_id_prefix, name_id_suffix, KEY, pad(10)] + #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] #! Outputs: [INIT_VALUE, pad(12)] export.get_initial_map_item exec.active_account::get_initial_map_item diff --git a/crates/miden-lib/src/transaction/memory.rs b/crates/miden-lib/src/transaction/memory.rs index f29e1a564d..019f8caaea 100644 --- a/crates/miden-lib/src/transaction/memory.rs +++ b/crates/miden-lib/src/transaction/memory.rs @@ -310,11 +310,11 @@ pub const ACCT_STORAGE_SLOT_NUM_ELEMENTS: u8 = 8; /// The offset of the slot type in the storage slot. pub const ACCT_STORAGE_SLOT_TYPE_OFFSET: u8 = 1; -/// The offset of the slot's name ID suffix in the storage slot. -pub const ACCT_STORAGE_SLOT_NAME_ID_SUFFIX_OFFSET: u8 = 2; +/// The offset of the slot's ID suffix in the storage slot. +pub const ACCT_STORAGE_SLOT_ID_SUFFIX_OFFSET: u8 = 2; -/// The offset of the slot's name ID prefix in the storage slot. -pub const ACCT_STORAGE_SLOT_NAME_ID_PREFIX_OFFSET: u8 = 3; +/// The offset of the slot's ID prefix in the storage slot. +pub const ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET: u8 = 3; /// The offset of the slot value in the storage slot. pub const ACCT_STORAGE_SLOT_VALUE_OFFSET: u8 = 4; diff --git a/crates/miden-objects/src/account/builder/mod.rs b/crates/miden-objects/src/account/builder/mod.rs index d93b564ffe..92ce47fce0 100644 --- a/crates/miden-objects/src/account/builder/mod.rs +++ b/crates/miden-objects/src/account/builder/mod.rs @@ -291,7 +291,7 @@ mod tests { use miden_processor::MastNodeExt; use super::*; - use crate::account::{NamedStorageSlot, SlotName}; + use crate::account::{NamedStorageSlot, StorageSlotName}; use crate::testing::noop_auth_component::NoopAuthComponent; const CUSTOM_CODE1: &str = " @@ -316,14 +316,17 @@ mod tests { .expect("code should be valid") }); - static CUSTOM_COMPONENT1_SLOT_NAME: LazyLock = LazyLock::new(|| { - SlotName::new("custom::component1::slot0").expect("slot name should be valid") + static CUSTOM_COMPONENT1_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("custom::component1::slot0") + .expect("storage slot name should be valid") }); - static CUSTOM_COMPONENT2_SLOT_NAME0: LazyLock = LazyLock::new(|| { - SlotName::new("custom::component2::slot0").expect("slot name should be valid") + static CUSTOM_COMPONENT2_SLOT_NAME0: LazyLock = LazyLock::new(|| { + StorageSlotName::new("custom::component2::slot0") + .expect("storage slot name should be valid") }); - static CUSTOM_COMPONENT2_SLOT_NAME1: LazyLock = LazyLock::new(|| { - SlotName::new("custom::component2::slot1").expect("slot name should be valid") + static CUSTOM_COMPONENT2_SLOT_NAME1: LazyLock = LazyLock::new(|| { + StorageSlotName::new("custom::component2::slot1") + .expect("storage slot name should be valid") }); struct CustomComponent1 { diff --git a/crates/miden-objects/src/account/delta/mod.rs b/crates/miden-objects/src/account/delta/mod.rs index 21cee5eedf..0e545d85bc 100644 --- a/crates/miden-objects/src/account/delta/mod.rs +++ b/crates/miden-objects/src/account/delta/mod.rs @@ -205,18 +205,18 @@ impl AccountDelta { /// assets since `faucet_id_prefix` is at the same position in the layout for both assets, /// and, by design, it is never the same for fungible and non-fungible assets. /// - Append `[hash0, hash1, hash2, faucet_id_prefix]`, i.e. the non-fungible asset. - /// - Storage Slots are sorted by slot name ID and are iterated in this order. For each slot - /// **whose value has changed**, depending on the slot type: + /// - Storage Slots are sorted by slot ID and are iterated in this order. For each slot **whose + /// value has changed**, depending on the slot type: /// - Value Slot - /// - Append `[[domain = 2, 0, name_id_suffix, name_id_prefix], NEW_VALUE]` where - /// `NEW_VALUE` is the new value of the slot and `name_id_{suffix, prefix}` are the slot - /// name identifiers of the slot. + /// - Append `[[domain = 2, 0, slot_id_suffix, slot_id_prefix], NEW_VALUE]` where + /// `NEW_VALUE` is the new value of the slot and `slot_id_{suffix, prefix}` is the + /// identifier of the slot. /// - Map Slot /// - For each key-value pair, sorted by key, whose new value is different from the previous /// value in the map: /// - Append `[KEY, NEW_VALUE]`. - /// - Append `[[domain = 3, num_changed_entries, name_id_suffix, name_id_prefix], 0, 0, 0, - /// 0]`, where `name_id_{suffix, prefix}` are the slot name identifiers and + /// - Append `[[domain = 3, num_changed_entries, slot_id_suffix, slot_id_prefix], 0, 0, 0, + /// 0]`, where `slot_id_{suffix, prefix}` are the slot identifiers and /// `num_changed_entries` is the number of changed key-value pairs in the map. /// - For partial state deltas, the map header must only be included if /// `num_changed_entries` is not zero. @@ -279,7 +279,7 @@ impl AccountDelta { /// ID_AND_NONCE, EMPTY_WORD, /// [/* no fungible asset delta */], /// [/* no non-fungible asset delta */], - /// [[domain = 2, 0, name_id_suffix = 0, name_id_prefix = 0], NEW_VALUE] + /// [[domain = 2, 0, slot_id_suffix = 0, slot_id_prefix = 0], NEW_VALUE] /// ] /// ``` /// @@ -297,8 +297,8 @@ impl AccountDelta { /// ID_AND_NONCE, EMPTY_WORD, /// [/* no fungible asset delta */], /// [/* no non-fungible asset delta */], - /// [domain = 3, num_changed_entries = 0, name_id_suffix = 20, name_id_prefix = 21, 0, 0, 0, 0] - /// [domain = 3, num_changed_entries = 0, name_id_suffix = 42, name_id_prefix = 43, 0, 0, 0, 0] + /// [domain = 3, num_changed_entries = 0, slot_id_suffix = 20, slot_id_prefix = 21, 0, 0, 0, 0] + /// [domain = 3, num_changed_entries = 0, slot_id_suffix = 42, slot_id_prefix = 43, 0, 0, 0, 0] /// ] /// ``` /// @@ -308,7 +308,7 @@ impl AccountDelta { /// [/* no fungible asset delta */], /// [/* no non-fungible asset delta */], /// [KEY0, VALUE0], - /// [domain = 3, num_changed_entries = 1, name_id_suffix = 42, name_id_prefix = 43, 0, 0, 0, 0] + /// [domain = 3, num_changed_entries = 1, slot_id_suffix = 42, slot_id_prefix = 43, 0, 0, 0, 0] /// ] /// ``` /// @@ -599,8 +599,8 @@ mod tests { AccountStorage, AccountStorageMode, AccountType, - SlotName, StorageMapDelta, + StorageSlotName, }; use crate::asset::{ Asset, @@ -627,7 +627,7 @@ mod tests { AccountDelta::new(account_id, storage_delta.clone(), vault_delta.clone(), ONE).unwrap(); // non-empty delta - let storage_delta = AccountStorageDelta::from_iters([SlotName::mock(1)], [], []); + let storage_delta = AccountStorageDelta::from_iters([StorageSlotName::mock(1)], [], []); assert_matches!( AccountDelta::new(account_id, storage_delta.clone(), vault_delta.clone(), ZERO) @@ -675,13 +675,13 @@ mod tests { assert_eq!(account_delta.to_bytes().len(), account_delta.get_size_hint()); let storage_delta = AccountStorageDelta::from_iters( - [SlotName::mock(1)], + [StorageSlotName::mock(1)], [ - (SlotName::mock(2), Word::from([1, 1, 1, 1u32])), - (SlotName::mock(3), Word::from([1, 1, 0, 1u32])), + (StorageSlotName::mock(2), Word::from([1, 1, 1, 1u32])), + (StorageSlotName::mock(3), Word::from([1, 1, 0, 1u32])), ], [( - SlotName::mock(4), + StorageSlotName::mock(4), StorageMapDelta::from_iters( [Word::from([1, 1, 1, 0u32]), Word::from([0, 1, 1, 1u32])], [(Word::from([1, 1, 1, 1u32]), Word::from([1, 1, 1, 1u32]))], diff --git a/crates/miden-objects/src/account/delta/storage.rs b/crates/miden-objects/src/account/delta/storage.rs index f9c3ddb1b7..f43a244a12 100644 --- a/crates/miden-objects/src/account/delta/storage.rs +++ b/crates/miden-objects/src/account/delta/storage.rs @@ -12,7 +12,7 @@ use super::{ Serializable, Word, }; -use crate::account::{SlotName, StorageMap, StorageSlotType}; +use crate::account::{StorageMap, StorageSlotName, StorageSlotType}; use crate::{EMPTY_WORD, Felt, LexicographicWord, ZERO}; // ACCOUNT STORAGE DELTA @@ -28,9 +28,9 @@ use crate::{EMPTY_WORD, Felt, LexicographicWord, ZERO}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct AccountStorageDelta { /// The updates to the value slots of the account. - values: BTreeMap, + values: BTreeMap, /// The updates to the map slots of the account. - maps: BTreeMap, + maps: BTreeMap, } impl AccountStorageDelta { @@ -50,8 +50,8 @@ impl AccountStorageDelta { /// - Any of the updated slot is referenced from both maps, which means a slot is treated as /// both a value and a map slot. pub fn from_parts( - values: BTreeMap, - maps: BTreeMap, + values: BTreeMap, + maps: BTreeMap, ) -> Result { let delta = Self { values, maps }; delta.validate()?; @@ -60,7 +60,7 @@ impl AccountStorageDelta { } /// Returns an iterator over the slot names and types tracked by this delta. - pub(crate) fn slots(&self) -> impl Iterator { + pub(crate) fn slots(&self) -> impl Iterator { self.values() .keys() .map(|slot_name| (slot_name, StorageSlotType::Value)) @@ -68,12 +68,12 @@ impl AccountStorageDelta { } /// Returns a reference to the updated values in this storage delta. - pub fn values(&self) -> &BTreeMap { + pub fn values(&self) -> &BTreeMap { &self.values } /// Returns a reference to the updated maps in this storage delta. - pub fn maps(&self) -> &BTreeMap { + pub fn maps(&self) -> &BTreeMap { &self.maps } @@ -86,7 +86,7 @@ impl AccountStorageDelta { /// /// This does not (and cannot) validate that the slot name _exists_ or that it points to a /// _value_ slot in the corresponding account. - pub fn set_item(&mut self, slot_name: SlotName, new_slot_value: Word) { + pub fn set_item(&mut self, slot_name: StorageSlotName, new_slot_value: Word) { self.values.insert(slot_name, new_slot_value); } @@ -94,14 +94,14 @@ impl AccountStorageDelta { /// /// This does not (and cannot) validate that the slot name _exists_ or that it points to a /// _map_ slot in the corresponding account. - pub fn set_map_item(&mut self, slot_name: SlotName, key: Word, new_value: Word) { + pub fn set_map_item(&mut self, slot_name: StorageSlotName, key: Word, new_value: Word) { self.maps.entry(slot_name).or_default().insert(key, new_value); } /// Inserts an empty storage map delta for the provided slot index. /// /// This is useful for full state deltas to represent an empty map in the delta. - pub fn insert_empty_map_delta(&mut self, slot_name: SlotName) { + pub fn insert_empty_map_delta(&mut self, slot_name: StorageSlotName) { self.maps.entry(slot_name).or_default(); } @@ -140,12 +140,12 @@ impl AccountStorageDelta { } /// Returns an iterator of all the cleared storage slots. - fn cleared_values(&self) -> impl Iterator { + fn cleared_values(&self) -> impl Iterator { self.values.iter().filter(|&(_, value)| value.is_empty()).map(|(slot, _)| slot) } /// Returns an iterator of all the updated storage slots. - fn updated_values(&self) -> impl Iterator { + fn updated_values(&self) -> impl Iterator { self.values.iter().filter(|&(_, value)| !value.is_empty()) } @@ -156,7 +156,7 @@ impl AccountStorageDelta { const DOMAIN_MAP: Felt = Felt::new(3); // Merge slots into a single map to sort them by slot name. - let mut sorted_slots: BTreeMap<&SlotName, StorageSlotDelta> = self + let mut sorted_slots: BTreeMap<&StorageSlotName, StorageSlotDelta> = self .values() .iter() .map(|(name, value)| (name, StorageSlotDelta::Value(*value))) @@ -172,15 +172,15 @@ impl AccountStorageDelta { debug_assert_eq!(sorted_slots.len(), self.values.len() + self.maps.len()); for (slot_name, slot_delta) in sorted_slots { - let name_id = slot_name.compute_id(); + let slot_id = slot_name.compute_id(); match slot_delta { StorageSlotDelta::Value(new_value) => { elements.extend_from_slice(&[ DOMAIN_VALUE, ZERO, - name_id.suffix(), - name_id.prefix(), + slot_id.suffix(), + slot_id.prefix(), ]); elements.extend_from_slice(new_value.as_elements()); }, @@ -197,8 +197,8 @@ impl AccountStorageDelta { elements.extend_from_slice(&[ DOMAIN_MAP, num_changed_entries, - name_id.suffix(), - name_id.prefix(), + slot_id.suffix(), + slot_id.prefix(), ]); elements.extend_from_slice(EMPTY_WORD.as_elements()); }, @@ -207,7 +207,9 @@ impl AccountStorageDelta { } /// Consumes self and returns the underlying parts of the storage delta. - pub fn into_parts(self) -> (BTreeMap, BTreeMap) { + pub fn into_parts( + self, + ) -> (BTreeMap, BTreeMap) { (self.values, self.maps) } } @@ -222,9 +224,9 @@ impl Default for AccountStorageDelta { impl AccountStorageDelta { /// Creates an [AccountStorageDelta] from the given iterators. pub fn from_iters( - cleared_values: impl IntoIterator, - updated_values: impl IntoIterator, - updated_maps: impl IntoIterator, + cleared_values: impl IntoIterator, + updated_values: impl IntoIterator, + updated_maps: impl IntoIterator, ) -> Self { Self { values: BTreeMap::from_iter( @@ -284,7 +286,7 @@ impl Deserializable for AccountStorageDelta { let num_cleared_items = source.read_u8()?; for _ in 0..num_cleared_items { - let cleared_value: SlotName = source.read()?; + let cleared_value: StorageSlotName = source.read()?; values.insert(cleared_value, EMPTY_WORD); } @@ -295,7 +297,10 @@ impl Deserializable for AccountStorageDelta { } let num_maps = source.read_u8()? as usize; - let maps = source.read_many::<(SlotName, StorageMapDelta)>(num_maps)?.into_iter().collect(); + let maps = source + .read_many::<(StorageSlotName, StorageMapDelta)>(num_maps)? + .into_iter() + .collect(); Self::from_parts(values, maps) .map_err(|err| DeserializationError::InvalidValue(err.to_string())) @@ -474,17 +479,17 @@ mod tests { use anyhow::Context; use super::{AccountStorageDelta, Deserializable, Serializable}; - use crate::account::{SlotName, StorageMapDelta}; + use crate::account::{StorageMapDelta, StorageSlotName}; use crate::testing::storage::AccountStorageDeltaBuilder; use crate::{ONE, Word, ZERO}; #[test] fn account_storage_delta_validation() { let delta = AccountStorageDelta::from_iters( - [SlotName::mock(1), SlotName::mock(2), SlotName::mock(3)], + [StorageSlotName::mock(1), StorageSlotName::mock(2), StorageSlotName::mock(3)], [ - (SlotName::mock(4), Word::from([ONE, ONE, ONE, ONE])), - (SlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), + (StorageSlotName::mock(4), Word::from([ONE, ONE, ONE, ONE])), + (StorageSlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), ], [], ); @@ -495,12 +500,12 @@ mod tests { // duplicate across cleared items and maps let delta = AccountStorageDelta::from_iters( - [SlotName::mock(1), SlotName::mock(2), SlotName::mock(3)], + [StorageSlotName::mock(1), StorageSlotName::mock(2), StorageSlotName::mock(3)], [ - (SlotName::mock(2), Word::from([ONE, ONE, ONE, ONE])), - (SlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), + (StorageSlotName::mock(2), Word::from([ONE, ONE, ONE, ONE])), + (StorageSlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), ], - [(SlotName::mock(1), StorageMapDelta::default())], + [(StorageSlotName::mock(1), StorageMapDelta::default())], ); assert!(delta.validate().is_err()); @@ -509,12 +514,12 @@ mod tests { // duplicate across updated items and maps let delta = AccountStorageDelta::from_iters( - [SlotName::mock(1), SlotName::mock(3)], + [StorageSlotName::mock(1), StorageSlotName::mock(3)], [ - (SlotName::mock(2), Word::from([ONE, ONE, ONE, ONE])), - (SlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), + (StorageSlotName::mock(2), Word::from([ONE, ONE, ONE, ONE])), + (StorageSlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), ], - [(SlotName::mock(2), StorageMapDelta::default())], + [(StorageSlotName::mock(2), StorageMapDelta::default())], ); assert!(delta.validate().is_err()); @@ -527,12 +532,12 @@ mod tests { let storage_delta = AccountStorageDelta::new(); assert!(storage_delta.is_empty()); - let storage_delta = AccountStorageDelta::from_iters([SlotName::mock(1)], [], []); + let storage_delta = AccountStorageDelta::from_iters([StorageSlotName::mock(1)], [], []); assert!(!storage_delta.is_empty()); let storage_delta = AccountStorageDelta::from_iters( [], - [(SlotName::mock(2), Word::from([ONE, ONE, ONE, ONE]))], + [(StorageSlotName::mock(2), Word::from([ONE, ONE, ONE, ONE]))], [], ); assert!(!storage_delta.is_empty()); @@ -540,7 +545,7 @@ mod tests { let storage_delta = AccountStorageDelta::from_iters( [], [], - [(SlotName::mock(3), StorageMapDelta::default())], + [(StorageSlotName::mock(3), StorageMapDelta::default())], ); assert!(!storage_delta.is_empty()); } @@ -552,14 +557,14 @@ mod tests { let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap(); assert_eq!(deserialized, storage_delta); - let storage_delta = AccountStorageDelta::from_iters([SlotName::mock(1)], [], []); + let storage_delta = AccountStorageDelta::from_iters([StorageSlotName::mock(1)], [], []); let serialized = storage_delta.to_bytes(); let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap(); assert_eq!(deserialized, storage_delta); let storage_delta = AccountStorageDelta::from_iters( [], - [(SlotName::mock(2), Word::from([ONE, ONE, ONE, ONE]))], + [(StorageSlotName::mock(2), Word::from([ONE, ONE, ONE, ONE]))], [], ); let serialized = storage_delta.to_bytes(); @@ -569,7 +574,7 @@ mod tests { let storage_delta = AccountStorageDelta::from_iters( [], [], - [(SlotName::mock(3), StorageMapDelta::default())], + [(StorageSlotName::mock(3), StorageMapDelta::default())], ); let serialized = storage_delta.to_bytes(); let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap(); @@ -607,7 +612,7 @@ mod tests { ) -> anyhow::Result<()> { /// Creates a delta containing the item as an update if Some, else with the item cleared. fn create_delta(item: Option) -> anyhow::Result { - let slot_name = SlotName::mock(123); + let slot_name = StorageSlotName::mock(123); let item = item.map(|x| (slot_name.clone(), Word::from([x, 0, 0, 0]))); AccountStorageDeltaBuilder::new() diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 4a6644bc68..52dd05cc21 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -70,11 +70,11 @@ pub use storage::{ NamedStorageSlot, PartialStorage, PartialStorageMap, - SlotName, - SlotNameId, StorageMap, StorageMapWitness, StorageSlot, + StorageSlotId, + StorageSlotName, StorageSlotType, }; @@ -642,10 +642,10 @@ mod tests { AccountType, NamedStorageSlot, PartialAccount, - SlotName, StorageMap, StorageMapDelta, StorageSlot, + StorageSlotName, }; use crate::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; use crate::testing::account_id::{ @@ -676,8 +676,8 @@ mod tests { let asset_0 = FungibleAsset::mock(15); let asset_1 = NonFungibleAsset::mock(&[5, 5, 5]); let storage_delta = AccountStorageDeltaBuilder::new() - .add_cleared_items([SlotName::mock(0)]) - .add_updated_values([(SlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) + .add_cleared_items([StorageSlotName::mock(0)]) + .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) .build() .unwrap(); let account_delta = build_account_delta( @@ -741,9 +741,9 @@ mod tests { // build account delta let final_nonce = Felt::new(2); let storage_delta = AccountStorageDeltaBuilder::new() - .add_cleared_items([SlotName::mock(0)]) - .add_updated_values([(SlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) - .add_updated_maps([(SlotName::mock(2), updated_map)]) + .add_cleared_items([StorageSlotName::mock(0)]) + .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) + .add_updated_maps([(StorageSlotName::mock(2), updated_map)]) .build() .unwrap(); let account_delta = build_account_delta( @@ -783,8 +783,8 @@ mod tests { // build account delta let storage_delta = AccountStorageDeltaBuilder::new() - .add_cleared_items([SlotName::mock(0)]) - .add_updated_values([(SlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) + .add_cleared_items([StorageSlotName::mock(0)]) + .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) .build() .unwrap(); let account_delta = @@ -807,8 +807,8 @@ mod tests { // build account delta let final_nonce = Felt::new(1); let storage_delta = AccountStorageDeltaBuilder::new() - .add_cleared_items([SlotName::mock(0)]) - .add_updated_values([(SlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) + .add_cleared_items([StorageSlotName::mock(0)]) + .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) .build() .unwrap(); let account_delta = @@ -861,7 +861,7 @@ mod tests { let slots = slots .into_iter() .enumerate() - .map(|(idx, slot)| NamedStorageSlot::new(SlotName::mock(idx), slot)) + .map(|(idx, slot)| NamedStorageSlot::new(StorageSlotName::mock(idx), slot)) .collect(); let storage = AccountStorage::new(slots).unwrap(); diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-objects/src/account/storage/header.rs index a197b919e8..1c2777d50a 100644 --- a/crates/miden-objects/src/account/storage/header.rs +++ b/crates/miden-objects/src/account/storage/header.rs @@ -2,7 +2,7 @@ use alloc::string::ToString; use alloc::vec::Vec; use super::{AccountStorage, Felt, StorageSlot, StorageSlotType, Word}; -use crate::account::{SlotName, SlotNameId}; +use crate::account::{StorageSlotId, StorageSlotName}; use crate::crypto::SequentialCommit; use crate::utils::serde::{ ByteReader, @@ -16,21 +16,22 @@ use crate::{AccountError, FieldElement, ZERO}; // ACCOUNT STORAGE HEADER // ================================================================================================ -/// The header of a [`StorageSlot`], storing only the name ID, slot type and value of the slot. +/// The header of a [`StorageSlot`], storing only the slot ID, slot type and value of the slot. /// /// The stored value differs based on the slot type: /// - [`StorageSlotType::Value`]: The value of the slot itself. /// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct StorageSlotHeader { - id: SlotNameId, + id: StorageSlotId, r#type: StorageSlotType, value: Word, } impl StorageSlotHeader { - /// Returns a new instance of storage slot header from the provided storage slot type and value. - pub(crate) fn new(id: SlotNameId, r#type: StorageSlotType, value: Word) -> Self { + /// Returns a new instance of storage slot header from the provided storage slot ID, type and + /// value. + pub(crate) fn new(id: StorageSlotId, r#type: StorageSlotType, value: Word) -> Self { Self { id, r#type, value } } @@ -38,7 +39,7 @@ impl StorageSlotHeader { /// /// This is done by converting this storage slot into 8 field elements as follows: /// ```text - /// [[0, slot_type, name_id_suffix, name_id_prefix], SLOT_VALUE] + /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE] /// ``` pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT] { let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT]; @@ -61,7 +62,7 @@ impl StorageSlotHeader { /// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AccountStorageHeader { - slots: Vec<(SlotName, StorageSlotType, Word)>, + slots: Vec<(StorageSlotName, StorageSlotType, Word)>, } impl AccountStorageHeader { @@ -74,8 +75,8 @@ impl AccountStorageHeader { /// /// Returns an error if: /// - The number of provided slots is greater than [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. - /// - The slots are not sorted by [`SlotNameId`]. - pub fn new(slots: Vec<(SlotName, StorageSlotType, Word)>) -> Result { + /// - The slots are not sorted by [`StorageSlotId`]. + pub fn new(slots: Vec<(StorageSlotName, StorageSlotType, Word)>) -> Result { if slots.len() > AccountStorage::MAX_NUM_STORAGE_SLOTS { return Err(AccountError::StorageTooManySlots(slots.len() as u64)); } @@ -91,7 +92,7 @@ impl AccountStorageHeader { // -------------------------------------------------------------------------------------------- /// Returns an iterator over the storage header slots. - pub fn slots(&self) -> impl Iterator { + pub fn slots(&self) -> impl Iterator { self.slots.iter().map(|(name, r#type, value)| (name, r#type, value)) } @@ -111,10 +112,10 @@ impl AccountStorageHeader { /// Returns a slot contained in the storage header at a given index. /// - /// Returns `None` if a slot with the provided name ID does not exist. + /// Returns `None` if a slot with the provided slot ID does not exist. pub fn find_slot_header_by_name( &self, - slot_name: &SlotName, + slot_name: &StorageSlotName, ) -> Option<(&StorageSlotType, &Word)> { self.find_slot_header_by_id(slot_name.compute_id()) .map(|(_slot_name, slot_type, slot_value)| (slot_type, slot_value)) @@ -122,13 +123,13 @@ impl AccountStorageHeader { /// Returns a slot contained in the storage header at a given index. /// - /// Returns `None` if a slot with the provided name ID does not exist. + /// Returns `None` if a slot with the provided slot ID does not exist. pub fn find_slot_header_by_id( &self, - name_id: SlotNameId, - ) -> Option<(&SlotName, &StorageSlotType, &Word)> { + slot_id: StorageSlotId, + ) -> Option<(&StorageSlotName, &StorageSlotType, &Word)> { self.slots - .binary_search_by_key(&name_id, |(name, ..)| name.compute_id()) + .binary_search_by_key(&slot_id, |(name, ..)| name.compute_id()) .map(|slot_idx| { let (name, r#type, value) = &self.slots[slot_idx]; (name, r#type, value) @@ -142,7 +143,7 @@ impl AccountStorageHeader { /// /// Returns an error if: /// - a slot with the provided name does not exist. - pub fn is_map_slot(&self, name: &SlotName) -> Result { + pub fn is_map_slot(&self, name: &StorageSlotName) -> Result { match self .find_slot_header_by_name(name) .ok_or(AccountError::StorageSlotNameNotFound { slot_name: name.clone() })? @@ -158,7 +159,7 @@ impl AccountStorageHeader { /// This is done by first converting each storage slot into exactly 8 elements as follows: /// /// ```text - /// [[0, slot_type, name_id_suffix, name_id_prefix], SLOT_VALUE] + /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE] /// ``` /// /// And then concatenating the resulting elements into a single vector. diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index e7e1ae3138..c89df84916 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -19,7 +19,7 @@ use crate::crypto::SequentialCommit; use crate::utils::sync::LazyLock; mod slot; -pub use slot::{NamedStorageSlot, SlotName, SlotNameId, StorageSlot, StorageSlotType}; +pub use slot::{NamedStorageSlot, StorageSlot, StorageSlotId, StorageSlotName, StorageSlotType}; mod map; pub use map::{PartialStorageMap, StorageMap, StorageMapWitness}; @@ -30,8 +30,9 @@ pub use header::AccountStorageHeader; mod partial; pub use partial::PartialStorage; -static FAUCET_METADATA_SLOT_NAME: LazyLock = - LazyLock::new(|| SlotName::new("miden::faucet::metadata").expect("slot name should be valid")); +static FAUCET_METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::faucet::metadata").expect("storage slot name should be valid") +}); // ACCOUNT STORAGE // ================================================================================================ @@ -46,7 +47,8 @@ static FAUCET_METADATA_SLOT_NAME: LazyLock = /// values are [Word]s. The value of a storage slot containing a map is the commitment to the /// underlying map. /// -/// Slots are sorted by [`SlotName`] (or [`SlotNameId`] equivalently). This order is necessary to: +/// Slots are sorted by [`StorageSlotName`] (or [`StorageSlotId`] equivalently). This order is +/// necessary to: /// - Simplify lookups of slots in the transaction kernel (using `std::collections::sorted_array` /// from the miden core library) /// - Allow the [`AccountStorageDelta`] to work only with slot names instead of slot indices. @@ -66,13 +68,13 @@ impl AccountStorage { /// Returns a new instance of account storage initialized with the provided storage slots. /// - /// This function sorts the slots by [`SlotName`]. + /// This function sorts the slots by [`StorageSlotName`]. /// /// # Errors /// /// Returns an error if: /// - The number of [`StorageSlot`]s exceeds 255. - /// - There are multiple storage slots with the same [`SlotName`]. + /// - There are multiple storage slots with the same [`StorageSlotName`]. pub fn new(mut slots: Vec) -> Result { let num_slots = slots.len(); @@ -140,8 +142,8 @@ impl AccountStorage { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the [`SlotName`] of the faucet's protocol metadata. - pub fn faucet_metadata_slot() -> &'static SlotName { + /// Returns the [`StorageSlotName`] of the faucet's protocol metadata. + pub fn faucet_metadata_slot() -> &'static StorageSlotName { &FAUCET_METADATA_SLOT_NAME } @@ -150,7 +152,7 @@ impl AccountStorage { /// Each storage slot is represented by exactly 8 elements: /// /// ```text - /// [[0, slot_type, name_id_suffix, name_id_prefix], SLOT_VALUE] + /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE] /// ``` pub fn to_elements(&self) -> Vec { ::to_elements(self) @@ -197,20 +199,20 @@ impl AccountStorage { /// Returns a reference to the storage slot with the provided name, if it exists, `None` /// otherwise. - pub fn get(&self, slot_name: &SlotName) -> Option<&NamedStorageSlot> { + pub fn get(&self, slot_name: &StorageSlotName) -> Option<&NamedStorageSlot> { debug_assert!(self.slots.is_sorted()); - let name_id = slot_name.compute_id(); + let slot_id = slot_name.compute_id(); self.slots - .binary_search_by_key(&name_id, |named_slot| named_slot.name_id()) + .binary_search_by_key(&slot_id, |named_slot| named_slot.slot_id()) .map(|idx| &self.slots[idx]) .ok() } - fn get_mut(&mut self, slot_name: &SlotName) -> Option<&mut NamedStorageSlot> { - let name_id = slot_name.compute_id(); + fn get_mut(&mut self, slot_name: &StorageSlotName) -> Option<&mut NamedStorageSlot> { + let slot_id = slot_name.compute_id(); self.slots - .binary_search_by_key(&name_id, |named_slot| named_slot.name_id()) + .binary_search_by_key(&slot_id, |named_slot| named_slot.slot_id()) .map(|idx| &mut self.slots[idx]) .ok() } @@ -221,7 +223,7 @@ impl AccountStorage { /// /// Returns an error if: /// - A slot with the provided name does not exist. - pub fn get_item(&self, slot_name: &SlotName) -> Result { + pub fn get_item(&self, slot_name: &StorageSlotName) -> Result { self.get(slot_name) .map(|named_slot| named_slot.storage_slot().value()) .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }) @@ -234,7 +236,11 @@ impl AccountStorage { /// Returns an error if: /// - A slot with the provided name does not exist. /// - If the [`StorageSlot`] is not [`StorageSlotType::Map`]. - pub fn get_map_item(&self, slot_name: &SlotName, key: Word) -> Result { + pub fn get_map_item( + &self, + slot_name: &StorageSlotName, + key: Word, + ) -> Result { self.get(slot_name) .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }) .and_then(|named_slot| match named_slot.storage_slot() { @@ -285,7 +291,11 @@ impl AccountStorage { /// Returns an error if: /// - A slot with the provided name does not exist. /// - The [`StorageSlot`] is not [`StorageSlotType::Value`]. - pub fn set_item(&mut self, slot_name: &SlotName, value: Word) -> Result { + pub fn set_item( + &mut self, + slot_name: &StorageSlotName, + value: Word, + ) -> Result { let slot = self.get_mut(slot_name).ok_or_else(|| { AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() } })?; @@ -313,7 +323,7 @@ impl AccountStorage { /// - If the [`StorageSlot`] is not [`StorageSlotType::Map`]. pub fn set_map_item( &mut self, - slot_name: &SlotName, + slot_name: &StorageSlotName, raw_key: Word, value: Word, ) -> Result<(Word, Word), AccountError> { @@ -356,7 +366,7 @@ impl SequentialCommit for AccountStorage { .iter() .flat_map(|named_slot| { StorageSlotHeader::new( - named_slot.name_id(), + named_slot.slot_id(), named_slot.storage_slot().slot_type(), named_slot.storage_slot().value(), ) @@ -403,7 +413,7 @@ impl Deserializable for AccountStorage { #[cfg(test)] mod tests { use super::{AccountStorage, Deserializable, Serializable}; - use crate::account::{NamedStorageSlot, SlotName}; + use crate::account::{NamedStorageSlot, StorageSlotName}; #[test] fn test_serde_account_storage() -> anyhow::Result<()> { @@ -414,8 +424,8 @@ mod tests { // storage with values for default types let storage = AccountStorage::new(vec![ - NamedStorageSlot::with_empty_value(SlotName::new("miden::test::value")?), - NamedStorageSlot::with_empty_map(SlotName::new("miden::test::map")?), + NamedStorageSlot::with_empty_value(StorageSlotName::new("miden::test::value")?), + NamedStorageSlot::with_empty_map(StorageSlotName::new("miden::test::map")?), ]) .unwrap(); let bytes = storage.to_bytes(); @@ -426,8 +436,8 @@ mod tests { #[test] fn test_get_slot_by_name() -> anyhow::Result<()> { - let counter_slot = SlotName::new("miden::test::counter")?; - let map_slot = SlotName::new("miden::test::map")?; + let counter_slot = StorageSlotName::new("miden::test::counter")?; + let map_slot = StorageSlotName::new("miden::test::map")?; let slots = vec![ NamedStorageSlot::with_empty_value(counter_slot.clone()), diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-objects/src/account/storage/partial.rs index c196a07ac8..2f22a90b17 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-objects/src/account/storage/partial.rs @@ -162,8 +162,8 @@ mod tests { NamedStorageSlot, PartialStorage, PartialStorageMap, - SlotName, StorageMap, + StorageSlotName, }; #[test] @@ -176,7 +176,7 @@ mod tests { map_1.insert(map_key_present, Word::try_from([5u64, 4, 3, 2])?).unwrap(); assert_eq!(map_1.get(&map_key_present), [5u64, 4, 3, 2].try_into()?); - let slot_name = SlotName::new("miden::test_map")?; + let slot_name = StorageSlotName::new("miden::test_map")?; let storage = AccountStorage::new(vec![NamedStorageSlot::with_map(slot_name.clone(), map_1.clone())]) diff --git a/crates/miden-objects/src/account/storage/slot/mod.rs b/crates/miden-objects/src/account/storage/slot/mod.rs index 25454d46f5..74dc4ece08 100644 --- a/crates/miden-objects/src/account/storage/slot/mod.rs +++ b/crates/miden-objects/src/account/storage/slot/mod.rs @@ -6,10 +6,10 @@ use super::map::EMPTY_STORAGE_MAP_ROOT; use super::{StorageMap, Word}; mod slot_name; -pub use slot_name::SlotName; +pub use slot_name::StorageSlotName; -mod slot_name_id; -pub use slot_name_id::SlotNameId; +mod slot_id; +pub use slot_id::StorageSlotId; mod r#type; pub use r#type::StorageSlotType; diff --git a/crates/miden-objects/src/account/storage/slot/named_slot.rs b/crates/miden-objects/src/account/storage/slot/named_slot.rs index da51f9017a..ec1d1d2d41 100644 --- a/crates/miden-objects/src/account/storage/slot/named_slot.rs +++ b/crates/miden-objects/src/account/storage/slot/named_slot.rs @@ -1,21 +1,21 @@ use crate::Word; -use crate::account::storage::slot::SlotNameId; -use crate::account::{SlotName, StorageMap, StorageSlot, StorageSlotType}; +use crate::account::storage::slot::StorageSlotId; +use crate::account::{StorageMap, StorageSlot, StorageSlotName, StorageSlotType}; /// An individual storage slot in [`AccountStorage`](crate::account::AccountStorage). /// -/// This consists of a [`SlotName`] that uniquely identifies the slot and its [`StorageSlot`] +/// This consists of a [`StorageSlotName`] that uniquely identifies the slot and its [`StorageSlot`] /// content. #[derive(Debug, Clone, PartialEq, Eq)] pub struct NamedStorageSlot { /// The name of the storage slot. - name: SlotName, - /// The cached [`SlotNameId`] of the slot name. This field must always be consistent with the - /// slot name. + name: StorageSlotName, + /// The cached [`StorageSlotId`] of the slot name. This field must always be consistent with + /// the slot name. /// - /// This is cached so that the `Ord` implementation can use the computed name ID instead of + /// This is cached so that the `Ord` implementation can use the computed slot ID instead of /// having to hash the slot name on every comparison operation. - name_id: SlotNameId, + slot_id: StorageSlotId, /// The underlying storage slot. slot: StorageSlot, } @@ -24,48 +24,48 @@ impl NamedStorageSlot { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and [`StorageSlot`]. - pub fn new(name: SlotName, slot: StorageSlot) -> Self { - let name_id = name.compute_id(); + /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and [`StorageSlot`]. + pub fn new(name: StorageSlotName, slot: StorageSlot) -> Self { + let slot_id = name.compute_id(); - Self { name, name_id, slot } + Self { name, slot_id, slot } } - /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and the `value` wrapped into - /// a [`StorageSlot::Value`]. - pub fn with_value(name: SlotName, value: Word) -> Self { + /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and the `value` + /// wrapped into a [`StorageSlot::Value`]. + pub fn with_value(name: StorageSlotName, value: Word) -> Self { Self::new(name, StorageSlot::Value(value)) } - /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and + /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and /// [`StorageSlot::empty_value`]. - pub fn with_empty_value(name: SlotName) -> Self { + pub fn with_empty_value(name: StorageSlotName) -> Self { Self::new(name, StorageSlot::empty_value()) } - /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and the `map` wrapped into a - /// [`StorageSlot::Map`] - pub fn with_map(name: SlotName, map: StorageMap) -> Self { + /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and the `map` wrapped + /// into a [`StorageSlot::Map`] + pub fn with_map(name: StorageSlotName, map: StorageMap) -> Self { Self::new(name, StorageSlot::Map(map)) } - /// Creates a new [`NamedStorageSlot`] with the given [`SlotName`] and + /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and /// [`StorageSlot::empty_map`]. - pub fn with_empty_map(name: SlotName) -> Self { + pub fn with_empty_map(name: StorageSlotName) -> Self { Self::new(name, StorageSlot::empty_map()) } // ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the [`SlotName`] by which the [`NamedStorageSlot`] is identified. - pub fn name(&self) -> &SlotName { + /// Returns the [`StorageSlotName`] by which the [`NamedStorageSlot`] is identified. + pub fn name(&self) -> &StorageSlotName { &self.name } - /// Returns the [`SlotNameId`] by which the [`NamedStorageSlot`] is identified. - pub fn name_id(&self) -> SlotNameId { - self.name_id + /// Returns the [`StorageSlotId`] by which the [`NamedStorageSlot`] is identified. + pub fn slot_id(&self) -> StorageSlotId { + self.slot_id } /// Returns this storage slot value as a [Word] @@ -96,14 +96,14 @@ impl NamedStorageSlot { } /// Consumes self and returns the underlying parts. - pub fn into_parts(self) -> (SlotName, SlotNameId, StorageSlot) { - (self.name, self.name_id, self.slot) + pub fn into_parts(self) -> (StorageSlotName, StorageSlotId, StorageSlot) { + (self.name, self.slot_id, self.slot) } } impl Ord for NamedStorageSlot { fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.name_id.cmp(&other.name_id) + self.slot_id.cmp(&other.slot_id) } } @@ -131,7 +131,7 @@ impl crate::utils::serde::Deserializable for NamedStorageSlot { fn read_from( source: &mut R, ) -> Result { - let name: SlotName = source.read()?; + let name: StorageSlotName = source.read()?; let slot: StorageSlot = source.read()?; Ok(Self::new(name, slot)) diff --git a/crates/miden-objects/src/account/storage/slot/slot_name_id.rs b/crates/miden-objects/src/account/storage/slot/slot_id.rs similarity index 66% rename from crates/miden-objects/src/account/storage/slot/slot_name_id.rs rename to crates/miden-objects/src/account/storage/slot/slot_id.rs index 87a6d8b7b2..f3aef26eab 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name_id.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_id.rs @@ -3,24 +3,24 @@ use core::fmt::Display; use crate::Felt; -/// The partial hash of a [`SlotName`](super::SlotName). +/// The partial hash of a [`StorageSlotName`](super::StorageSlotName). /// -/// The ID of a slot name are the first (`suffix`) and second (`prefix`) field elements of the +/// The ID of a slot are the first (`suffix`) and second (`prefix`) field elements of the /// blake3-hashed slot name. /// -/// The slot name ID is used to uniquely identify a storage slot and is used to sort slots in -/// account storage. +/// The slot ID is used to uniquely identify a storage slot and is used to sort slots in account +/// storage. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SlotNameId { +pub struct StorageSlotId { suffix: Felt, prefix: Felt, } -impl SlotNameId { +impl StorageSlotId { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new [`SlotNameId`] from the provided felts. + /// Creates a new [`StorageSlotId`] from the provided felts. pub fn new(suffix: Felt, prefix: Felt) -> Self { Self { suffix, prefix } } @@ -28,17 +28,17 @@ impl SlotNameId { // ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the suffix of the [`SlotNameId`]. + /// Returns the suffix of the [`StorageSlotId`]. pub fn suffix(&self) -> Felt { self.suffix } - /// Returns the prefix of the [`SlotNameId`]. + /// Returns the prefix of the [`StorageSlotId`]. pub fn prefix(&self) -> Felt { self.prefix } - /// Returns the [`SlotNameId`]'s felts encoded into a u128. + /// Returns the [`StorageSlotId`]'s felts encoded into a u128. fn as_u128(&self) -> u128 { let mut le_bytes = [0_u8; 16]; le_bytes[..8].copy_from_slice(&self.suffix().as_int().to_le_bytes()); @@ -47,7 +47,7 @@ impl SlotNameId { } } -impl Ord for SlotNameId { +impl Ord for StorageSlotId { fn cmp(&self, other: &Self) -> Ordering { match self.prefix.as_int().cmp(&other.prefix.as_int()) { ord @ Ordering::Less | ord @ Ordering::Greater => ord, @@ -56,13 +56,13 @@ impl Ord for SlotNameId { } } -impl PartialOrd for SlotNameId { +impl PartialOrd for StorageSlotId { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Display for SlotNameId { +impl Display for StorageSlotId { /// Returns a big-endian, hex-encoded string of length 34, including the `0x` prefix. /// /// This means it encodes 16 bytes. @@ -79,11 +79,11 @@ mod tests { use super::*; #[test] - fn test_name_id_as_u128() { + fn test_slot_id_as_u128() { let suffix = 5; let prefix = 3; - let name_id = SlotNameId::new(Felt::from(suffix as u32), Felt::from(prefix as u32)); - assert_eq!(name_id.as_u128(), (prefix << 64) + suffix); - assert_eq!(format!("{name_id}"), "0x00000000000000030000000000000005"); + let slot_id = StorageSlotId::new(Felt::from(suffix as u32), Felt::from(prefix as u32)); + assert_eq!(slot_id.as_u128(), (prefix << 64) + suffix); + assert_eq!(format!("{slot_id}"), "0x00000000000000030000000000000005"); } } diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-objects/src/account/storage/slot/slot_name.rs index b3dc9c4390..e1eb351791 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_name.rs @@ -4,7 +4,7 @@ use core::fmt::Display; use miden_core::utils::hash_string_to_word; -use crate::account::storage::slot::SlotNameId; +use crate::account::storage::slot::StorageSlotId; use crate::errors::SlotNameError; use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -36,11 +36,11 @@ use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Seri /// (underscore). /// - Each component must not start with an underscore. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct SlotName { +pub struct StorageSlotName { name: Cow<'static, str>, } -impl SlotName { +impl StorageSlotName { // CONSTANTS // -------------------------------------------------------------------------------------------- @@ -53,13 +53,14 @@ impl SlotName { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Constructs a new [`SlotName`] from a static string. + /// Constructs a new [`StorageSlotName`] from a static string. /// /// This function is `const` and can be used to define slot names as constants, e.g.: /// /// ```rust - /// # use miden_objects::account::SlotName; - /// const SLOT_NAME: SlotName = SlotName::from_static_str("miden::basic_fungible_faucet::metadata"); + /// # use miden_objects::account::StorageSlotName; + /// const SLOT_NAME: StorageSlotName = + /// StorageSlotName::from_static_str("miden::basic_fungible_faucet::metadata"); /// ``` /// /// This is convenient because using a string that is not a valid slot name fails to compile. @@ -76,7 +77,7 @@ impl SlotName { } } - /// Constructs a new [`SlotName`] from a string. + /// Constructs a new [`StorageSlotName`] from a string. /// /// # Errors /// @@ -107,11 +108,11 @@ impl SlotName { } // TODO(named_slots): Docs. - pub fn compute_id(&self) -> SlotNameId { + pub fn compute_id(&self) -> StorageSlotId { let hashed_word = hash_string_to_word(self.as_str()); let suffix = hashed_word[0]; let prefix = hashed_word[1]; - SlotNameId::new(suffix, prefix) + StorageSlotId::new(suffix, prefix) } // HELPERS @@ -209,26 +210,26 @@ impl SlotName { } } -impl Ord for SlotName { +impl Ord for StorageSlotName { fn cmp(&self, other: &Self) -> core::cmp::Ordering { // TODO(named_slots): Cache ID in SlotName for efficiency. self.compute_id().cmp(&other.compute_id()) } } -impl PartialOrd for SlotName { +impl PartialOrd for StorageSlotName { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Display for SlotName { +impl Display for StorageSlotName { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_str(self.as_str()) } } -impl Serializable for SlotName { +impl Serializable for StorageSlotName { fn write_into(&self, target: &mut W) { target.write_u8(self.len()); target.write_many(self.as_str().as_bytes()) @@ -240,7 +241,7 @@ impl Serializable for SlotName { } } -impl Deserializable for SlotName { +impl Deserializable for StorageSlotName { fn read_from( source: &mut R, ) -> Result { @@ -271,32 +272,32 @@ mod tests { // Const function tests // -------------------------------------------------------------------------------------------- - const _NAME0: SlotName = SlotName::from_static_str("name::component"); - const _NAME1: SlotName = SlotName::from_static_str("one::two::three::four::five"); - const _NAME2: SlotName = SlotName::from_static_str("one::two_three::four"); + const _NAME0: StorageSlotName = StorageSlotName::from_static_str("name::component"); + const _NAME1: StorageSlotName = StorageSlotName::from_static_str("one::two::three::four::five"); + const _NAME2: StorageSlotName = StorageSlotName::from_static_str("one::two_three::four"); #[test] #[should_panic(expected = "invalid slot name")] fn slot_name_panics_on_invalid_character() { - SlotName::from_static_str("miden!::component"); + StorageSlotName::from_static_str("miden!::component"); } #[test] #[should_panic(expected = "invalid slot name")] fn slot_name_panics_on_invalid_character2() { - SlotName::from_static_str("miden_ö::component"); + StorageSlotName::from_static_str("miden_ö::component"); } #[test] #[should_panic(expected = "invalid slot name")] fn slot_name_panics_when_too_short() { - SlotName::from_static_str("one"); + StorageSlotName::from_static_str("one"); } #[test] #[should_panic(expected = "invalid slot name")] fn slot_name_panics_on_component_starting_with_underscores() { - SlotName::from_static_str("one::_two"); + StorageSlotName::from_static_str("one::_two"); } // Invalid colon or underscore tests @@ -305,31 +306,49 @@ mod tests { #[test] fn slot_name_fails_on_invalid_colon_placement() { // Single colon. - assert_matches!(SlotName::new(":").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(SlotName::new("0::1:").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(SlotName::new(":0::1").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(SlotName::new("0::1:2").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!(StorageSlotName::new(":").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!(StorageSlotName::new("0::1:").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!(StorageSlotName::new(":0::1").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!( + StorageSlotName::new("0::1:2").unwrap_err(), + SlotNameError::UnexpectedColon + ); // Double colon (placed invalidly). - assert_matches!(SlotName::new("::").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(SlotName::new("1::2::").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(SlotName::new("::1::2").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!(StorageSlotName::new("::").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!( + StorageSlotName::new("1::2::").unwrap_err(), + SlotNameError::UnexpectedColon + ); + assert_matches!( + StorageSlotName::new("::1::2").unwrap_err(), + SlotNameError::UnexpectedColon + ); // Triple colon. - assert_matches!(SlotName::new(":::").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(SlotName::new("1::2:::").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(SlotName::new(":::1::2").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(SlotName::new("1::2:::3").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!(StorageSlotName::new(":::").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!( + StorageSlotName::new("1::2:::").unwrap_err(), + SlotNameError::UnexpectedColon + ); + assert_matches!( + StorageSlotName::new(":::1::2").unwrap_err(), + SlotNameError::UnexpectedColon + ); + assert_matches!( + StorageSlotName::new("1::2:::3").unwrap_err(), + SlotNameError::UnexpectedColon + ); } #[test] fn slot_name_fails_on_invalid_underscore_placement() { assert_matches!( - SlotName::new("_one::two").unwrap_err(), + StorageSlotName::new("_one::two").unwrap_err(), SlotNameError::UnexpectedUnderscore ); assert_matches!( - SlotName::new("one::_two").unwrap_err(), + StorageSlotName::new("one::_two").unwrap_err(), SlotNameError::UnexpectedUnderscore ); } @@ -339,19 +358,22 @@ mod tests { #[test] fn slot_name_fails_on_empty_string() { - assert_matches!(SlotName::new("").unwrap_err(), SlotNameError::TooShort); + assert_matches!(StorageSlotName::new("").unwrap_err(), SlotNameError::TooShort); } #[test] fn slot_name_fails_on_single_component() { - assert_matches!(SlotName::new("single_component").unwrap_err(), SlotNameError::TooShort); + assert_matches!( + StorageSlotName::new("single_component").unwrap_err(), + SlotNameError::TooShort + ); } #[test] fn slot_name_fails_on_string_whose_length_exceeds_max_length() { let mut string = get_max_length_slot_name(); string.push('a'); - assert_matches!(SlotName::new(string).unwrap_err(), SlotNameError::TooLong); + assert_matches!(StorageSlotName::new(string).unwrap_err(), SlotNameError::TooLong); } // Alphabet validation tests @@ -360,7 +382,7 @@ mod tests { #[test] fn slot_name_allows_ascii_alphanumeric_and_underscore() -> anyhow::Result<()> { let name = format!("{FULL_ALPHABET}::second"); - let slot_name = SlotName::new(&name)?; + let slot_name = StorageSlotName::new(&name)?; assert_eq!(slot_name.as_str(), name); Ok(()) @@ -369,15 +391,15 @@ mod tests { #[test] fn slot_name_fails_on_invalid_character() { assert_matches!( - SlotName::new("na#me::second").unwrap_err(), + StorageSlotName::new("na#me::second").unwrap_err(), SlotNameError::InvalidCharacter ); assert_matches!( - SlotName::new("first_entry::secönd").unwrap_err(), + StorageSlotName::new("first_entry::secönd").unwrap_err(), SlotNameError::InvalidCharacter ); assert_matches!( - SlotName::new("first::sec::th!rd").unwrap_err(), + StorageSlotName::new("first::sec::th!rd").unwrap_err(), SlotNameError::InvalidCharacter ); } @@ -387,19 +409,19 @@ mod tests { #[test] fn slot_name_with_min_components_is_valid() -> anyhow::Result<()> { - SlotName::new("miden::component")?; + StorageSlotName::new("miden::component")?; Ok(()) } #[test] fn slot_name_with_many_components_is_valid() -> anyhow::Result<()> { - SlotName::new("miden::faucet0::fungible_1::b4sic::metadata")?; + StorageSlotName::new("miden::faucet0::fungible_1::b4sic::metadata")?; Ok(()) } #[test] fn slot_name_with_max_length_is_valid() -> anyhow::Result<()> { - SlotName::new(get_max_length_slot_name())?; + StorageSlotName::new(get_max_length_slot_name())?; Ok(()) } @@ -408,15 +430,15 @@ mod tests { #[test] fn serde_slot_name() -> anyhow::Result<()> { - let slot_name = SlotName::new("miden::faucet0::fungible_1::b4sic::metadata")?; - assert_eq!(slot_name, SlotName::read_from_bytes(&slot_name.to_bytes())?); + let slot_name = StorageSlotName::new("miden::faucet0::fungible_1::b4sic::metadata")?; + assert_eq!(slot_name, StorageSlotName::read_from_bytes(&slot_name.to_bytes())?); Ok(()) } #[test] fn serde_max_length_slot_name() -> anyhow::Result<()> { - let slot_name = SlotName::new(get_max_length_slot_name())?; - assert_eq!(slot_name, SlotName::read_from_bytes(&slot_name.to_bytes())?); + let slot_name = StorageSlotName::new(get_max_length_slot_name())?; + assert_eq!(slot_name, StorageSlotName::read_from_bytes(&slot_name.to_bytes())?); Ok(()) } @@ -425,10 +447,10 @@ mod tests { fn get_max_length_slot_name() -> String { const MIDEN_STR: &str = "miden::"; - let remainder = ['a'; SlotName::MAX_LENGTH - MIDEN_STR.len()]; + let remainder = ['a'; StorageSlotName::MAX_LENGTH - MIDEN_STR.len()]; let mut string = MIDEN_STR.to_owned(); string.extend(remainder); - assert_eq!(string.len(), SlotName::MAX_LENGTH); + assert_eq!(string.len(), StorageSlotName::MAX_LENGTH); string } } diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 680065b25c..7b55a92a5e 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -22,11 +22,11 @@ use crate::account::{ AccountIdPrefix, AccountStorage, AccountType, - SlotName, - SlotNameId, + StorageSlotId, // StorageValueName, // StorageValueNameError, // TemplateTypeError, + StorageSlotName, }; use crate::address::AddressType; use crate::asset::AssetVaultKey; @@ -148,21 +148,21 @@ pub enum AccountError { #[error("storage map root {0} not found in the account storage")] StorageMapRootNotFound(Word), #[error("storage slot {0} is not of type map")] - StorageSlotNotMap(SlotName), + StorageSlotNotMap(StorageSlotName), #[error("storage slot {0} is not of type value")] - StorageSlotNotValue(SlotName), + StorageSlotNotValue(StorageSlotName), #[error("storage slot name {0} is assigned to more than one slot")] - DuplicateStorageSlotName(SlotName), + DuplicateStorageSlotName(StorageSlotName), #[error( "account storage cannot contain a user-provided slot with name {} as it is reserved by the protocol", AccountStorage::faucet_metadata_slot() )] StorageSlotNameMustNotBeFaucetMetadata, #[error("storage does not contain a slot with name {slot_name}")] - StorageSlotNameNotFound { slot_name: SlotName }, - #[error("storage does not contain a slot with ID {slot_name_id}")] - StorageSlotNameIdNotFound { slot_name_id: SlotNameId }, - #[error("storage slots must be sorted by slot name ID")] + StorageSlotNameNotFound { slot_name: StorageSlotName }, + #[error("storage does not contain a slot with ID {slot_id}")] + StorageSlotIdNotFound { slot_id: StorageSlotId }, + #[error("storage slots must be sorted by slot ID")] UnsortedStorageSlots, #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)] StorageTooManySlots(u64), @@ -258,10 +258,10 @@ pub enum SlotNameError { UnexpectedUnderscore, #[error( "slot names must contain at least {} components separated by double colons", - SlotName::MIN_NUM_COMPONENTS + StorageSlotName::MIN_NUM_COMPONENTS )] TooShort, - #[error("slot names must contain at most {} characters", SlotName::MAX_LENGTH)] + #[error("slot names must contain at most {} characters", StorageSlotName::MAX_LENGTH)] TooLong, } @@ -374,7 +374,7 @@ pub enum NetworkIdError { #[derive(Debug, Error)] pub enum AccountDeltaError { #[error("storage slot {0} was updated as a value and as a map")] - StorageSlotUsedAsDifferentTypes(SlotName), + StorageSlotUsedAsDifferentTypes(StorageSlotName), #[error("non fungible vault can neither be added nor removed twice")] DuplicateNonFungibleVaultUpdate(NonFungibleAsset), #[error( diff --git a/crates/miden-objects/src/testing/slot_name.rs b/crates/miden-objects/src/testing/slot_name.rs index 76174f225e..e37e00dd00 100644 --- a/crates/miden-objects/src/testing/slot_name.rs +++ b/crates/miden-objects/src/testing/slot_name.rs @@ -1,8 +1,8 @@ -use crate::account::SlotName; +use crate::account::StorageSlotName; -impl SlotName { +impl StorageSlotName { /// Returns a new slot name with the format `"miden::test::slot::{index}"`. pub fn mock(index: usize) -> Self { - Self::new(format!("miden::test::slot::{index}")).expect("slot name should be valid") + Self::new(format!("miden::test::slot::{index}")).expect("storage slot name should be valid") } } diff --git a/crates/miden-objects/src/testing/storage.rs b/crates/miden-objects/src/testing/storage.rs index d74a57d010..39afe9b227 100644 --- a/crates/miden-objects/src/testing/storage.rs +++ b/crates/miden-objects/src/testing/storage.rs @@ -10,9 +10,9 @@ use crate::account::{ AccountStorage, AccountStorageDelta, NamedStorageSlot, - SlotName, StorageMap, StorageMapDelta, + StorageSlotName, }; use crate::note::NoteAssets; use crate::utils::sync::LazyLock; @@ -22,8 +22,8 @@ use crate::utils::sync::LazyLock; #[derive(Clone, Debug, Default)] pub struct AccountStorageDeltaBuilder { - values: BTreeMap, - maps: BTreeMap, + values: BTreeMap, + maps: BTreeMap, } impl AccountStorageDeltaBuilder { @@ -40,19 +40,22 @@ impl AccountStorageDeltaBuilder { // MODIFIERS // ------------------------------------------------------------------------------------------- - pub fn add_cleared_items(mut self, items: impl IntoIterator) -> Self { + pub fn add_cleared_items(mut self, items: impl IntoIterator) -> Self { self.values.extend(items.into_iter().map(|slot| (slot, EMPTY_WORD))); self } - pub fn add_updated_values(mut self, items: impl IntoIterator) -> Self { + pub fn add_updated_values( + mut self, + items: impl IntoIterator, + ) -> Self { self.values.extend(items); self } pub fn add_updated_maps( mut self, - items: impl IntoIterator, + items: impl IntoIterator, ) -> Self { self.maps.extend(items); self @@ -69,12 +72,15 @@ impl AccountStorageDeltaBuilder { // CONSTANTS // ================================================================================================ -pub static MOCK_VALUE_SLOT0: LazyLock = - LazyLock::new(|| SlotName::new("miden::test::value0").expect("slot name should be valid")); -pub static MOCK_VALUE_SLOT1: LazyLock = - LazyLock::new(|| SlotName::new("miden::test::value1").expect("slot name should be valid")); -pub static MOCK_MAP_SLOT: LazyLock = - LazyLock::new(|| SlotName::new("miden::test::map").expect("slot name should be valid")); +pub static MOCK_VALUE_SLOT0: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::test::value0").expect("storage slot name should be valid") +}); +pub static MOCK_VALUE_SLOT1: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::test::value1").expect("storage slot name should be valid") +}); +pub static MOCK_MAP_SLOT: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::test::map").expect("storage slot name should be valid") +}); pub const STORAGE_VALUE_0: Word = Word::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]); diff --git a/crates/miden-objects/src/transaction/proven_tx.rs b/crates/miden-objects/src/transaction/proven_tx.rs index 0dc069251d..0e25ba6e5d 100644 --- a/crates/miden-objects/src/transaction/proven_tx.rs +++ b/crates/miden-objects/src/transaction/proven_tx.rs @@ -700,8 +700,8 @@ mod tests { AccountStorageMode, AccountType, AccountVaultDelta, - SlotName, StorageMapDelta, + StorageSlotName, }; use crate::asset::FungibleAsset; use crate::block::BlockNumber; @@ -778,7 +778,7 @@ mod tests { // A delta that exceeds the limit returns an error. let storage_delta = - AccountStorageDelta::from_iters([], [], [(SlotName::mock(4), storage_delta)]); + AccountStorageDelta::from_iters([], [], [(StorageSlotName::mock(4), storage_delta)]); let delta = AccountDelta::new(account_id, storage_delta, AccountVaultDelta::default(), ONE) .unwrap(); let details = AccountUpdateDetails::Delta(delta); diff --git a/crates/miden-testing/src/kernel_tests/block/header_errors.rs b/crates/miden-testing/src/kernel_tests/block/header_errors.rs index 3d38d592b3..6e8b67d4d1 100644 --- a/crates/miden-testing/src/kernel_tests/block/header_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/header_errors.rs @@ -12,7 +12,7 @@ use miden_objects::account::{ AccountComponent, AccountId, NamedStorageSlot, - SlotName, + StorageSlotName, }; use miden_objects::asset::FungibleAsset; use miden_objects::batch::ProvenBatch; @@ -260,7 +260,7 @@ async fn block_building_fails_on_creating_account_with_existing_account_id_prefi let account = AccountBuilder::new([5; 32]) .with_auth_component(auth_component.clone()) .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( - SlotName::new("miden::test_slot")?, + StorageSlotName::new("miden::test_slot")?, Word::from([5u32; 4]), )])) .build() @@ -352,7 +352,7 @@ async fn block_building_fails_on_creating_account_with_duplicate_account_id_pref let account = AccountBuilder::new([5; 32]) .with_auth_component(Auth::IncrNonce) .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( - SlotName::new("miden::test_slot")?, + StorageSlotName::new("miden::test_slot")?, Word::from([5u32; 4]), )])) .build() diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 30a7d85ea7..fc95794298 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -26,9 +26,9 @@ use miden_objects::account::{ AccountStorageMode, AccountType, NamedStorageSlot, - SlotName, StorageMap, StorageSlot, + StorageSlotName, StorageSlotType, }; use miden_objects::assembly::DefaultSourceManager; @@ -109,7 +109,7 @@ pub async fn compute_commitment() -> miette::Result<()> { push.{value} push.{key} push.MOCK_MAP_SLOT[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE, pad(7)] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE, pad(7)] call.mock_account::set_map_item dropw dropw dropw dropw # => [STORAGE_COMMITMENT0] @@ -390,7 +390,7 @@ async fn test_get_item() -> miette::Result<()> { # push the account storage item index push.SLOT_NAME[0..2] - # => [name_id_prefix, name_id_suffix] + # => [slot_id_prefix, slot_id_suffix] # assert the item value is correct exec.account::get_item @@ -545,7 +545,7 @@ async fn test_set_item() -> anyhow::Result<()> { # set the storage item push.{new_value} push.MOCK_VALUE_SLOT0[0..2] - # => [name_id_prefix, name_id_suffix, NEW_VALUE] + # => [slot_id_prefix, slot_id_suffix, NEW_VALUE] exec.account::set_item @@ -555,7 +555,7 @@ async fn test_set_item() -> anyhow::Result<()> { # assert new value has been correctly set push.MOCK_VALUE_SLOT0[0..2] - # => [name_id_prefix, name_id_suffix] + # => [slot_id_prefix, slot_id_suffix] exec.account::get_item push.{new_value} @@ -603,11 +603,11 @@ async fn test_set_map_item() -> miette::Result<()> { # double check that the storage slot is indeed the new map push.SLOT_NAME[0..2] - # => [name_id_prefix, name_id_suffix, OLD_VALUE, OLD_MAP_ROOT] + # => [slot_id_prefix, slot_id_suffix, OLD_VALUE, OLD_MAP_ROOT] # pad the stack repeat.14 push.0 movdn.2 end - # => [name_id_prefix, name_id_suffix, pad(14), OLD_VALUE, OLD_MAP_ROOT] + # => [slot_id_prefix, slot_id_suffix, pad(14), OLD_VALUE, OLD_MAP_ROOT] call.mock_account::get_item # => [MAP_ROOT, pad(12), OLD_VALUE, OLD_MAP_ROOT] @@ -752,7 +752,7 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { # update the map storage slot push.5.6.7.8.101.102.103.104 push.MOCK_MAP_SLOT[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] call.mock_account::set_map_item dropw dropw # => [] @@ -776,9 +776,9 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { /// accounts. #[tokio::test] async fn prove_account_creation_with_non_empty_storage() -> anyhow::Result<()> { - let slot_name0 = SlotName::mock(0); - let slot_name1 = SlotName::mock(1); - let slot_name2 = SlotName::mock(2); + let slot_name0 = StorageSlotName::mock(0); + let slot_name1 = StorageSlotName::mock(1); + let slot_name2 = StorageSlotName::mock(2); let slot0 = NamedStorageSlot::with_value(slot_name0.clone(), Word::from([1, 2, 3, 4u32])); let slot1 = NamedStorageSlot::with_value(slot_name1.clone(), Word::from([10, 20, 30, 40u32])); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index 58b30d27b2..cc978a5cdc 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -15,8 +15,8 @@ use miden_objects::account::{ AccountStorageMode, AccountType, NamedStorageSlot, - SlotName, StorageMap, + StorageSlotName, }; use miden_objects::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; use miden_objects::note::{Note, NoteExecutionHint, NoteTag, NoteType}; @@ -107,20 +107,20 @@ async fn delta_nonce() -> anyhow::Result<()> { /// - Slot 3: [1,3,5,7] -> [2,3,4,5] -> [1,3,5,7] -> Delta: None #[tokio::test] async fn storage_delta_for_value_slots() -> anyhow::Result<()> { - let slot_0_name = SlotName::mock(0); + let slot_0_name = StorageSlotName::mock(0); let slot_0_init_value = Word::from([2, 4, 6, 8u32]); let slot_0_tmp_value = Word::from([3, 4, 5, 6u32]); let slot_0_final_value = EMPTY_WORD; - let slot_1_name = SlotName::mock(1); + let slot_1_name = StorageSlotName::mock(1); let slot_1_init_value = EMPTY_WORD; let slot_1_final_value = Word::from([3, 4, 5, 6u32]); - let slot_2_name = SlotName::mock(2); + let slot_2_name = StorageSlotName::mock(2); let slot_2_init_value = Word::from([1, 3, 5, 7u32]); let slot_2_final_value = slot_2_init_value; - let slot_3_name = SlotName::mock(3); + let slot_3_name = StorageSlotName::mock(3); let slot_3_init_value = Word::from([1, 3, 5, 7u32]); let slot_3_tmp_value = Word::from([2, 3, 4, 5u32]); let slot_3_final_value = slot_3_init_value; @@ -146,37 +146,37 @@ async fn storage_delta_for_value_slots() -> anyhow::Result<()> { begin push.{slot_0_tmp_value} push.SLOT_0_NAME[0..2] - # => [name_id_prefix, name_id_suffix, VALUE] + # => [slot_id_prefix, slot_id_suffix, VALUE] exec.set_item # => [] push.{slot_0_final_value} push.SLOT_0_NAME[0..2] - # => [name_id_prefix, name_id_suffix, VALUE] + # => [slot_id_prefix, slot_id_suffix, VALUE] exec.set_item # => [] push.{slot_1_final_value} push.SLOT_1_NAME[0..2] - # => [name_id_prefix, name_id_suffix, VALUE] + # => [slot_id_prefix, slot_id_suffix, VALUE] exec.set_item # => [] push.{slot_2_final_value} push.SLOT_2_NAME[0..2] - # => [name_id_prefix, name_id_suffix, VALUE] + # => [slot_id_prefix, slot_id_suffix, VALUE] exec.set_item # => [] push.{slot_3_tmp_value} push.SLOT_3_NAME[0..2] - # => [name_id_prefix, name_id_suffix, VALUE] + # => [slot_id_prefix, slot_id_suffix, VALUE] exec.set_item # => [] push.{slot_3_final_value} push.SLOT_3_NAME[0..2] - # => [name_id_prefix, name_id_suffix, VALUE] + # => [slot_id_prefix, slot_id_suffix, VALUE] exec.set_item # => [] end @@ -247,18 +247,18 @@ async fn storage_delta_for_map_slots() -> anyhow::Result<()> { let key5_tmp_value = Word::from([2, 3, 4, 5u32]); let key5_final_value = Word::from([1, 2, 3, 4u32]); - let slot_0_name = SlotName::mock(0); + let slot_0_name = StorageSlotName::mock(0); let mut map0 = StorageMap::new(); map0.insert(key0, key0_init_value).unwrap(); map0.insert(key1, key1_init_value).unwrap(); - let slot_1_name = SlotName::mock(1); + let slot_1_name = StorageSlotName::mock(1); let mut map1 = StorageMap::new(); map1.insert(key2, key2_init_value).unwrap(); map1.insert(key3, key3_init_value).unwrap(); map1.insert(key4, key4_init_value).unwrap(); - let slot_2_name = SlotName::mock(2); + let slot_2_name = StorageSlotName::mock(2); let mut map2 = StorageMap::new(); map2.insert(key5, key5_init_value).unwrap(); @@ -270,7 +270,7 @@ async fn storage_delta_for_map_slots() -> anyhow::Result<()> { // Include an empty map which does not receive any updates, to test that the "metadata // header" in the delta commitment is not appended if there are no updates to a map // slot. - NamedStorageSlot::with_map(SlotName::mock(3), StorageMap::new()), + NamedStorageSlot::with_map(StorageSlotName::mock(3), StorageMap::new()), ], [], [], @@ -285,55 +285,55 @@ async fn storage_delta_for_map_slots() -> anyhow::Result<()> { begin push.{key0_final_value} push.{key0} push.SLOT_0_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] push.{key1_tmp_value} push.{key1} push.SLOT_0_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] push.{key1_final_value} push.{key1} push.SLOT_0_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] push.{key2_final_value} push.{key2} push.SLOT_1_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] push.{key3_final_value} push.{key3} push.SLOT_1_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] push.{key4_tmp_value} push.{key4} push.SLOT_1_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] push.{key4_final_value} push.{key4} push.SLOT_1_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] push.{key5_tmp_value} push.{key5} push.SLOT_2_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] push.{key5_final_value} push.{key5} push.SLOT_2_NAME[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] exec.set_map_item # => [] end @@ -674,7 +674,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { # get the index of account storage slot push.MOCK_VALUE_SLOT0[0..2] - # => [name_id_prefix, name_id_suffix, 13, 11, 9, 7] + # => [slot_id_prefix, slot_id_suffix, 13, 11, 9, 7] # update the storage value call.account::set_item dropw # => [] @@ -691,7 +691,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { # get the index of account storage slot push.MOCK_MAP_SLOT[0..2] - # => [name_id_prefix, name_id_suffix, 14, 15, 16, 17, 18, 19, 20, 21] + # => [slot_id_prefix, slot_id_suffix, 14, 15, 16, 17, 18, 19, 20, 21] # update the storage value call.account::set_map_item dropw dropw dropw @@ -818,9 +818,9 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: (rand_value(), rand_value()), ])?; - let map0_slot_name = SlotName::mock(1); - let map1_slot_name = SlotName::mock(2); - let map2_slot_name = SlotName::mock(4); + let map0_slot_name = StorageSlotName::mock(1); + let map1_slot_name = StorageSlotName::mock(2); + let map2_slot_name = StorageSlotName::mock(4); // Build a public account so the proven transaction includes the account update. let account = AccountBuilder::new([1; 32]) @@ -850,7 +850,7 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: push.{value0} push.{existing_key} push.MAP_SLOT[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] call.account::set_map_item exec.::std::sys::truncate_stack @@ -919,8 +919,8 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: /// delta and not normalized away. #[tokio::test] async fn delta_for_new_account_retains_empty_value_storage_slots() -> anyhow::Result<()> { - let slot_name0 = SlotName::mock(0); - let slot_name1 = SlotName::mock(1); + let slot_name0 = StorageSlotName::mock(0); + let slot_name1 = StorageSlotName::mock(1); let slot_value2 = Word::from([1, 2, 3, 4u32]); let mut account = AccountBuilder::new(rand::random()) @@ -958,7 +958,7 @@ async fn delta_for_new_account_retains_empty_value_storage_slots() -> anyhow::Re /// delta. #[tokio::test] async fn delta_for_new_account_retains_empty_map_storage_slots() -> anyhow::Result<()> { - let slot_name0 = SlotName::mock(0); + let slot_name0 = StorageSlotName::mock(0); let mut account = AccountBuilder::new(rand::random()) .account_type(AccountType::RegularAccountUpdatableCode) @@ -1070,11 +1070,11 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " use.mock::account use.miden::output_note - #! Inputs: [name_id_prefix, name_id_suffix, VALUE] + #! Inputs: [slot_id_prefix, slot_id_suffix, VALUE] #! Outputs: [] proc.set_item repeat.10 push.0 movdn.6 end - # => [name_id_prefix, name_id_suffix, VALUE, pad(10)] + # => [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] call.account::set_item # => [OLD_VALUE, pad(12)] @@ -1082,7 +1082,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " dropw dropw dropw dropw end - #! Inputs: [name_id_prefix, name_id_suffix, KEY, VALUE] + #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, VALUE] #! Outputs: [] proc.set_map_item repeat.6 push.0 movdn.10 end diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 7a065c8991..3cb84646ac 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -469,7 +469,7 @@ async fn epilogue_fails_on_account_state_change_without_nonce_increment() -> any push.91.92.93.94 push.MOCK_VALUE_SLOT0[0..2] repeat.5 movup.5 drop end - # => [name_id_prefix, name_id_suffix, VALUE] + # => [slot_id_prefix, slot_id_suffix, VALUE] call.account::set_item # => [PREV_VALUE] dropw diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs index 4a8d466932..fbf85a27c3 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs @@ -1,6 +1,6 @@ use anyhow::Context; use assert_matches::assert_matches; -use miden_objects::account::{AccountId, NamedStorageSlot, SlotName, StorageMap}; +use miden_objects::account::{AccountId, NamedStorageSlot, StorageMap, StorageSlotName}; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; use miden_objects::note::NoteType; use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; @@ -132,9 +132,9 @@ async fn mutate_account_with_storage() -> anyhow::Result { let account = builder.add_existing_mock_account_with_storage_and_assets( Auth::IncrNonce, [ - NamedStorageSlot::with_value(SlotName::mock(0), rand_value()), + NamedStorageSlot::with_value(StorageSlotName::mock(0), rand_value()), NamedStorageSlot::with_map( - SlotName::mock(1), + StorageSlotName::mock(1), StorageMap::with_entries([(rand_value(), rand_value())])?, ), ], @@ -165,10 +165,10 @@ async fn create_output_notes() -> anyhow::Result { Auth::IncrNonce, [ NamedStorageSlot::with_map( - SlotName::mock(0), + StorageSlotName::mock(0), StorageMap::with_entries([(rand_value(), rand_value())])?, ), - NamedStorageSlot::with_value(SlotName::mock(1), rand_value()), + NamedStorageSlot::with_value(StorageSlotName::mock(1), rand_value()), ], [Asset::from(native_asset), NonFungibleAsset::mock(&[1, 2, 3, 4])], )?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index f2d03ae6b6..54eadc8813 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -147,7 +147,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, pad(8)] + # slot_id_prefix, slot_id_suffix, pad(8)] exec.tx::execute_foreign_procedure # => [STORAGE_VALUE_1] @@ -204,7 +204,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, MAP_KEY, pad(4)] + # slot_id_prefix, slot_id_suffix, MAP_KEY, pad(4)] exec.tx::execute_foreign_procedure # => [MAP_VALUE] @@ -262,7 +262,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, pad(8)] + # slot_id_prefix, slot_id_suffix, pad(8)] exec.tx::execute_foreign_procedure dropw # => [] @@ -281,7 +281,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, pad(8)] + # slot_id_prefix, slot_id_suffix, pad(8)] exec.tx::execute_foreign_procedure @@ -427,7 +427,7 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_1_suffix} push.{foreign_1_prefix} # => [foreign_account_1_id_prefix, foreign_account_1_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, pad(8)] + # slot_id_prefix, slot_id_suffix, pad(8)] exec.tx::execute_foreign_procedure dropw # => [] @@ -446,7 +446,7 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_2_suffix} push.{foreign_2_prefix} # => [foreign_account_2_id_prefix, foreign_account_2_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, pad(8)] + # slot_id_prefix, slot_id_suffix, pad(8)] exec.tx::execute_foreign_procedure dropw # => [] @@ -465,7 +465,7 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_1_suffix} push.{foreign_1_prefix} # => [foreign_account_1_id_prefix, foreign_account_1_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, pad(8)] + # slot_id_prefix, slot_id_suffix, pad(8)] exec.tx::execute_foreign_procedure @@ -606,7 +606,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT - # slot_name_id_prefix, slot_name_id_suffix, pad(8)]] + # slot_id_prefix, slot_id_suffix, pad(8)]] exec.tx::execute_foreign_procedure # => [STORAGE_VALUE] @@ -632,7 +632,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { # push the foreign account ID push.{foreign_suffix} push.{foreign_prefix} # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, MAP_ITEM_KEY, pad(4)] + # slot_id_prefix, slot_id_suffix, MAP_ITEM_KEY, pad(4)] exec.tx::execute_foreign_procedure # => [MAP_VALUE] @@ -939,7 +939,7 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { # push the foreign account ID from the advice stack adv_push.2 # => [foreign_account_id_prefix, foreign_account_id_suffix, FOREIGN_PROC_ROOT, - # slot_name_id_prefix, slot_name_id_suffix, pad(8)] + # slot_id_prefix, slot_id_suffix, pad(8)] exec.tx::execute_foreign_procedure # => [storage_value] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index 7a3c20f962..2dc821fccc 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -194,14 +194,14 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { push.{value0} push.{existing_key} push.MOCK_MAP_SLOT[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] call.account::set_map_item # Insert a non-existent key. push.{value1} push.{non_existent_key} push.MOCK_MAP_SLOT[0..2] - # => [name_id_prefix, name_id_suffix, KEY, VALUE] + # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] call.account::set_map_item exec.::std::sys::truncate_stack @@ -256,7 +256,7 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { # Fetch value from existing key. push.{existing_key} push.MOCK_MAP_SLOT[0..2] - # => [name_id_prefix, name_id_suffix, KEY] + # => [slot_id_prefix, slot_id_suffix, KEY] call.account::get_map_item push.{existing_value} @@ -265,7 +265,7 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { # Fetch a non-existent key. push.{non_existent_key} push.MOCK_MAP_SLOT[0..2] - # => [name_id_prefix, name_id_suffix, KEY] + # => [slot_id_prefix, slot_id_suffix, KEY] call.account::get_map_item padw assert_eqw.err="non-existent value should be the empty word" diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index e7c617a662..0637276dee 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -71,9 +71,9 @@ use miden_objects::account::{ AccountStorageMode, AccountType, NamedStorageSlot, - SlotName, StorageMap, StorageSlot, + StorageSlotName, }; use miden_objects::asset::{FungibleAsset, NonFungibleAsset}; use miden_objects::testing::account_id::{ @@ -590,7 +590,7 @@ pub async fn create_multiple_accounts_test(storage_mode: AccountStorageMode) -> .storage_mode(storage_mode) .with_auth_component(Auth::IncrNonce) .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( - SlotName::mock(0), + StorageSlotName::mock(0), Word::from([255u32; WORD_SIZE]), )])) .build() diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index e751e539da..ba6593ecfb 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -19,7 +19,7 @@ use miden_objects::account::{ AccountStorageMode, AccountType, NamedStorageSlot, - SlotName, + StorageSlotName, }; use miden_objects::assembly::DefaultSourceManager; use miden_objects::assembly::diagnostics::NamedSource; @@ -771,7 +771,7 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { let component = AccountComponent::compile( account_code_script, TransactionKernel::assembler(), - vec![NamedStorageSlot::with_value(SlotName::mock(0), Word::default())], + vec![NamedStorageSlot::with_value(StorageSlotName::mock(0), Word::default())], )? .with_supports_all_types(); diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 25de7e9a1d..9e8f2b3c52 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -1,14 +1,14 @@ use miden_lib::transaction::memory::{ ACCOUNT_STACK_TOP_PTR, ACCT_CODE_COMMITMENT_OFFSET, - ACCT_STORAGE_SLOT_NAME_ID_PREFIX_OFFSET, - ACCT_STORAGE_SLOT_NAME_ID_SUFFIX_OFFSET, + ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET, + ACCT_STORAGE_SLOT_ID_SUFFIX_OFFSET, ACCT_STORAGE_SLOT_TYPE_OFFSET, ACCT_STORAGE_SLOT_VALUE_OFFSET, ACTIVE_INPUT_NOTE_PTR, NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, }; -use miden_objects::account::{AccountId, SlotNameId, StorageSlotType}; +use miden_objects::account::{AccountId, StorageSlotId, StorageSlotType}; use miden_objects::note::{NoteId, NoteInputs}; use miden_objects::{Hasher, Word}; use miden_processor::{ExecutionError, Felt, ProcessState}; @@ -38,7 +38,7 @@ pub(super) trait TransactionKernelProcess { fn get_storage_slot( &self, slot_ptr: Felt, - ) -> Result<(SlotNameId, StorageSlotType, Word), TransactionKernelError>; + ) -> Result<(StorageSlotId, StorageSlotType, Word), TransactionKernelError>; fn read_note_recipient_info_from_adv_map( &self, @@ -187,7 +187,7 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { fn get_storage_slot( &self, slot_ptr: Felt, - ) -> Result<(SlotNameId, StorageSlotType, Word), TransactionKernelError> { + ) -> Result<(StorageSlotId, StorageSlotType, Word), TransactionKernelError> { let slot_ptr = u32::try_from(slot_ptr).map_err(|_err| { TransactionKernelError::other(format!( "slot ptr should fit into a u32, but was {slot_ptr}" @@ -232,9 +232,9 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { ) })?; - let suffix = slot_metadata[ACCT_STORAGE_SLOT_NAME_ID_SUFFIX_OFFSET as usize]; - let prefix = slot_metadata[ACCT_STORAGE_SLOT_NAME_ID_PREFIX_OFFSET as usize]; - let slot_id = SlotNameId::new(suffix, prefix); + let suffix = slot_metadata[ACCT_STORAGE_SLOT_ID_SUFFIX_OFFSET as usize]; + let prefix = slot_metadata[ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET as usize]; + let slot_id = StorageSlotId::new(suffix, prefix); Ok((slot_id, slot_type, slot_value)) } diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index e4fb23c509..852564a695 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -36,8 +36,8 @@ use miden_objects::account::{ AccountId, AccountStorageHeader, PartialAccount, - SlotName, - SlotNameId, + StorageSlotId, + StorageSlotName, StorageSlotType, }; use miden_objects::asset::Asset; @@ -160,12 +160,12 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { &self.initial_account_storage_header } - /// Returns the initial storage slot of the native account identified by [`SlotNameId`], which - /// represents the state at the beginning of the transaction. + /// Returns the initial storage slot of the native account identified by [`StorageSlotId`], + /// which represents the state at the beginning of the transaction. pub fn initial_account_storage_slot( &self, - slot_id: SlotNameId, - ) -> Result<(&SlotName, &StorageSlotType, &Word), TransactionKernelError> { + slot_id: StorageSlotId, + ) -> Result<(&StorageSlotName, &StorageSlotType, &Word), TransactionKernelError> { self.initial_account_storage_header() .find_slot_header_by_id(slot_id) .ok_or_else(|| { @@ -334,7 +334,7 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { /// Tracks the insertion of an item in the account delta. pub fn on_account_storage_after_set_item( &mut self, - slot_name: SlotName, + slot_name: StorageSlotName, new_value: Word, ) -> Result, TransactionKernelError> { self.account_delta.storage().set_item(slot_name, new_value); @@ -345,7 +345,7 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { /// Tracks the insertion of a storage map item in the account delta. pub fn on_account_storage_after_set_map_item( &mut self, - slot_name: SlotName, + slot_name: StorageSlotName, key: Word, old_map_value: Word, new_map_value: Word, diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index ba020da769..105702d3d7 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -6,8 +6,8 @@ use miden_objects::account::{ AccountStorageDelta, AccountStorageHeader, PartialAccount, - SlotName, StorageMap, + StorageSlotName, StorageSlotType, }; @@ -29,9 +29,9 @@ pub struct StorageDeltaTracker { /// executed. This is only used to look up the initial values of storage _value_ slots, while /// the map slots are unused. storage_header: AccountStorageHeader, - /// A map from slot index to a map of key-value pairs where the key is a storage map key and + /// A map from slot name to a map of key-value pairs where the key is a storage map key and /// the value represents the value of that key at the beginning of transaction execution. - init_maps: BTreeMap>, + init_maps: BTreeMap>, /// The account storage delta. delta: AccountStorageDelta, } @@ -99,14 +99,14 @@ impl StorageDeltaTracker { // -------------------------------------------------------------------------------------------- /// Updates a value slot. - pub fn set_item(&mut self, slot_name: SlotName, new_value: Word) { + pub fn set_item(&mut self, slot_name: StorageSlotName, new_value: Word) { self.delta.set_item(slot_name, new_value); } /// Updates a map slot. pub fn set_map_item( &mut self, - slot_name: SlotName, + slot_name: StorageSlotName, key: Word, prev_value: Word, new_value: Word, @@ -128,7 +128,7 @@ impl StorageDeltaTracker { /// Sets the initial value of the given key in the given slot to the given value, if no value is /// already tracked for that key. - fn set_init_map_item(&mut self, slot_name: SlotName, key: Word, prev_value: Word) { + fn set_init_map_item(&mut self, slot_name: StorageSlotName, key: Word, prev_value: Word) { let slot_map = self.init_maps.entry(slot_name).or_default(); slot_map.entry(key).or_insert(prev_value); } @@ -192,7 +192,7 @@ impl StorageDeltaTracker { /// Creates empty slots of the same slot types as the to-be-created account. fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorageHeader { - let mut slots: Vec<(SlotName, StorageSlotType, Word)> = account + let mut slots: Vec<(StorageSlotName, StorageSlotType, Word)> = account .storage() .header() .slots() diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index 9d7ecae559..1d30fda79c 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use miden_lib::transaction::{EventId, TransactionEventId}; -use miden_objects::account::{AccountId, SlotName, StorageMap, StorageSlotType}; +use miden_objects::account::{AccountId, StorageMap, StorageSlotName, StorageSlotType}; use miden_objects::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; use miden_objects::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; use miden_objects::transaction::TransactionSummary; @@ -57,12 +57,12 @@ pub(crate) enum TransactionEvent { }, AccountStorageAfterSetItem { - slot_name: SlotName, + slot_name: StorageSlotName, new_value: Word, }, AccountStorageAfterSetMapItem { - slot_name: SlotName, + slot_name: StorageSlotName, key: Word, old_map_value: Word, new_map_value: Word, @@ -259,9 +259,9 @@ impl TransactionEvent { let slot_ptr = process.get_stack_item(1); let new_value = process.get_stack_word_be(2); - let (slot_name_id, slot_type, _old_value) = process.get_storage_slot(slot_ptr)?; + let (slot_id, slot_type, _old_value) = process.get_storage_slot(slot_ptr)?; - let (slot_name, ..) = base_host.initial_account_storage_slot(slot_name_id)?; + let (slot_name, ..) = base_host.initial_account_storage_slot(slot_id)?; let slot_name = slot_name.clone(); if !slot_type.is_value() { @@ -297,9 +297,9 @@ impl TransactionEvent { let old_map_value = process.get_stack_word_be(6); let new_map_value = process.get_stack_word_be(10); - // Resolve slot name ID to slot name. - let (slot_name_id, ..) = process.get_storage_slot(slot_ptr)?; - let (slot_name, ..) = base_host.initial_account_storage_slot(slot_name_id)?; + // Resolve slot ID to slot name. + let (slot_id, ..) = process.get_storage_slot(slot_ptr)?; + let (slot_name, ..) = base_host.initial_account_storage_slot(slot_id)?; let slot_name = slot_name.clone(); @@ -566,7 +566,7 @@ fn on_account_storage_map_item_accessed<'store, STORE>( slot_ptr: Felt, map_key: Word, ) -> Result, TransactionKernelError> { - let (slot_name_id, slot_type, current_map_root) = process.get_storage_slot(slot_ptr)?; + let (slot_id, slot_type, current_map_root) = process.get_storage_slot(slot_ptr)?; if !slot_type.is_map() { return Err(TransactionKernelError::other(format!( @@ -585,11 +585,11 @@ fn on_account_storage_map_item_accessed<'store, STORE>( // root instead of the _current_ one, since the data // store only has witnesses for initial one. let (_slot_name, slot_type, slot_value) = - base_host.initial_account_storage_slot(slot_name_id)?; + base_host.initial_account_storage_slot(slot_id)?; if *slot_type != StorageSlotType::Map { return Err(TransactionKernelError::other(format!( - "expected slot {slot_name_id} to be of type map" + "expected slot {slot_id} to be of type map" ))); } *slot_value From a303700580478ed8e84d5042674838f5fbdc6028 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 10 Dec 2025 08:50:45 +0700 Subject: [PATCH 031/114] chore: rename `NamedStorageSlot` to `StorageSlot` (#2150) * chore: Rename `StorageSlot` -> `StorageSlotContent` * chore: Rename `NamedStorageSlot` -> `StorageSlot` * chore: Rename `named_slot` variables to `slot` * chore: add changelog * chore: Move `NUM_ELEMENTS` constant to `StorageSlot` --- CHANGELOG.md | 2 +- .../src/account/auth/ecdsa_k256_keccak.rs | 4 +- .../src/account/auth/ecdsa_k256_keccak_acl.rs | 8 +- .../auth/ecdsa_k256_keccak_multisig.rs | 10 +- .../src/account/auth/rpo_falcon_512.rs | 4 +- .../src/account/auth/rpo_falcon_512_acl.rs | 8 +- .../account/auth/rpo_falcon_512_multisig.rs | 10 +- .../src/account/faucets/basic_fungible.rs | 8 +- .../src/account/faucets/network_fungible.rs | 6 +- .../miden-lib/src/account/interface/test.rs | 6 +- .../mock_account_component.rs | 8 +- crates/miden-lib/src/testing/mock_account.rs | 4 +- .../miden-objects/src/account/builder/mod.rs | 8 +- .../src/account/component/mod.rs | 16 +- crates/miden-objects/src/account/delta/mod.rs | 6 +- crates/miden-objects/src/account/header.rs | 4 +- crates/miden-objects/src/account/mod.rs | 40 ++--- .../src/account/storage/header.rs | 8 +- .../miden-objects/src/account/storage/mod.rs | 86 +++++----- .../src/account/storage/partial.rs | 10 +- .../src/account/storage/slot/mod.rs | 138 +--------------- .../src/account/storage/slot/named_slot.rs | 139 ----------------- .../src/account/storage/slot/slot_content.rs | 130 ++++++++++++++++ .../src/account/storage/slot/storage_slot.rs | 147 ++++++++++++++++++ crates/miden-objects/src/testing/storage.rs | 16 +- .../src/kernel_tests/block/header_errors.rs | 6 +- .../src/kernel_tests/tx/test_account.rs | 34 ++-- .../src/kernel_tests/tx/test_account_delta.rs | 32 ++-- .../src/kernel_tests/tx/test_fee.rs | 10 +- .../src/kernel_tests/tx/test_fpi.rs | 6 +- .../src/kernel_tests/tx/test_prologue.rs | 7 +- .../src/kernel_tests/tx/test_tx.rs | 4 +- .../src/mock_chain/chain_builder.rs | 6 +- .../miden-testing/src/tx_context/context.rs | 16 +- 34 files changed, 478 insertions(+), 469 deletions(-) delete mode 100644 crates/miden-objects/src/account/storage/slot/named_slot.rs create mode 100644 crates/miden-objects/src/account/storage/slot/slot_content.rs create mode 100644 crates/miden-objects/src/account/storage/slot/storage_slot.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c7214bdf3..47525b15bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Features - [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987)). -- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149)). +- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150)). ## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs index 7d9077eccb..5f963ccd25 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs @@ -1,5 +1,5 @@ use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, NamedStorageSlot, StorageSlotName}; +use miden_objects::account::{AccountComponent, StorageSlot, StorageSlotName}; use miden_objects::utils::sync::LazyLock; use crate::account::components::ecdsa_k256_keccak_library; @@ -42,7 +42,7 @@ impl From for AccountComponent { fn from(ecdsa: AuthEcdsaK256Keccak) -> Self { AccountComponent::new( ecdsa_k256_keccak_library(), - vec![NamedStorageSlot::with_value( + vec![StorageSlot::with_value( AuthEcdsaK256Keccak::public_key_slot().clone(), ecdsa.pub_key.into(), )], diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs index 8d1aeb946c..85b1a412a6 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs @@ -4,8 +4,8 @@ use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::account::{ AccountCode, AccountComponent, - NamedStorageSlot, StorageMap, + StorageSlot, StorageSlotName, }; use miden_objects::utils::sync::LazyLock; @@ -177,14 +177,14 @@ impl From for AccountComponent { let mut storage_slots = Vec::with_capacity(3); // Public key slot - storage_slots.push(NamedStorageSlot::with_value( + storage_slots.push(StorageSlot::with_value( AuthEcdsaK256KeccakAcl::public_key_slot().clone(), ecdsa.pub_key.into(), )); // Config slot let num_procs = ecdsa.config.auth_trigger_procedures.len() as u32; - storage_slots.push(NamedStorageSlot::with_value( + storage_slots.push(StorageSlot::with_value( AuthEcdsaK256KeccakAcl::config_slot().clone(), Word::from([ num_procs, @@ -205,7 +205,7 @@ impl From for AccountComponent { .map(|(i, proc_root)| (Word::from([i as u32, 0, 0, 0]), *proc_root)); // Safe to unwrap because we know that the map keys are unique. - storage_slots.push(NamedStorageSlot::with_map( + storage_slots.push(StorageSlot::with_map( AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot().clone(), StorageMap::with_entries(map_entries).unwrap(), )); diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs index 5d2d2f006f..233a0b1006 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs @@ -2,7 +2,7 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, NamedStorageSlot, StorageMap, StorageSlotName}; +use miden_objects::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; @@ -155,7 +155,7 @@ impl From for AccountComponent { // Threshold config slot (value: [threshold, num_approvers, 0, 0]) let num_approvers = multisig.config.approvers().len() as u32; - storage_slots.push(NamedStorageSlot::with_value( + storage_slots.push(StorageSlot::with_value( AuthEcdsaK256KeccakMultisig::threshold_config_slot().clone(), Word::from([multisig.config.default_threshold(), num_approvers, 0, 0]), )); @@ -169,14 +169,14 @@ impl From for AccountComponent { .map(|(i, pub_key)| (Word::from([i as u32, 0, 0, 0]), (*pub_key).into())); // Safe to unwrap because we know that the map keys are unique. - storage_slots.push(NamedStorageSlot::with_map( + storage_slots.push(StorageSlot::with_map( AuthEcdsaK256KeccakMultisig::approver_public_keys_slot().clone(), StorageMap::with_entries(map_entries).unwrap(), )); // Executed transactions slot (map) let executed_transactions = StorageMap::default(); - storage_slots.push(NamedStorageSlot::with_map( + storage_slots.push(StorageSlot::with_map( AuthEcdsaK256KeccakMultisig::executed_transactions_slot().clone(), executed_transactions, )); @@ -190,7 +190,7 @@ impl From for AccountComponent { .map(|(proc_root, threshold)| (*proc_root, Word::from([*threshold, 0, 0, 0]))), ) .unwrap(); - storage_slots.push(NamedStorageSlot::with_map( + storage_slots.push(StorageSlot::with_map( AuthEcdsaK256KeccakMultisig::procedure_thresholds_slot().clone(), proc_threshold_roots, )); diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs index 7ddd558577..eed966da81 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs @@ -1,5 +1,5 @@ use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, NamedStorageSlot, StorageSlotName}; +use miden_objects::account::{AccountComponent, StorageSlot, StorageSlotName}; use miden_objects::utils::sync::LazyLock; use crate::account::components::rpo_falcon_512_library; @@ -46,7 +46,7 @@ impl From for AccountComponent { fn from(falcon: AuthRpoFalcon512) -> Self { AccountComponent::new( rpo_falcon_512_library(), - vec![NamedStorageSlot::with_value( + vec![StorageSlot::with_value( AuthRpoFalcon512::public_key_slot().clone(), falcon.pub_key.into(), )], diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs index f711b9ee0c..3c3f0ce746 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs @@ -4,8 +4,8 @@ use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::account::{ AccountCode, AccountComponent, - NamedStorageSlot, StorageMap, + StorageSlot, StorageSlotName, }; use miden_objects::utils::sync::LazyLock; @@ -178,14 +178,14 @@ impl From for AccountComponent { let mut storage_slots = Vec::with_capacity(3); // Public key slot - storage_slots.push(NamedStorageSlot::with_value( + storage_slots.push(StorageSlot::with_value( AuthRpoFalcon512Acl::public_key_slot().clone(), falcon.pub_key.into(), )); // Config slot let num_procs = falcon.config.auth_trigger_procedures.len() as u32; - storage_slots.push(NamedStorageSlot::with_value( + storage_slots.push(StorageSlot::with_value( AuthRpoFalcon512Acl::config_slot().clone(), Word::from([ num_procs, @@ -206,7 +206,7 @@ impl From for AccountComponent { .map(|(i, proc_root)| (Word::from([i as u32, 0, 0, 0]), *proc_root)); // Safe to unwrap because we know that the map keys are unique. - storage_slots.push(NamedStorageSlot::with_map( + storage_slots.push(StorageSlot::with_map( AuthRpoFalcon512Acl::tracked_procedure_roots_slot().clone(), StorageMap::with_entries(map_entries).unwrap(), )); diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs index 3f6c4694d4..ac484892f7 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs @@ -2,7 +2,7 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, NamedStorageSlot, StorageMap, StorageSlotName}; +use miden_objects::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Word}; @@ -155,7 +155,7 @@ impl From for AccountComponent { // Threshold config slot (value: [threshold, num_approvers, 0, 0]) let num_approvers = multisig.config.approvers().len() as u32; - storage_slots.push(NamedStorageSlot::with_value( + storage_slots.push(StorageSlot::with_value( AuthRpoFalcon512Multisig::threshold_config_slot().clone(), Word::from([multisig.config.default_threshold(), num_approvers, 0, 0]), )); @@ -169,14 +169,14 @@ impl From for AccountComponent { .map(|(i, pub_key)| (Word::from([i as u32, 0, 0, 0]), (*pub_key).into())); // Safe to unwrap because we know that the map keys are unique. - storage_slots.push(NamedStorageSlot::with_map( + storage_slots.push(StorageSlot::with_map( AuthRpoFalcon512Multisig::approver_public_keys_slot().clone(), StorageMap::with_entries(map_entries).unwrap(), )); // Executed transactions slot (map) let executed_transactions = StorageMap::default(); - storage_slots.push(NamedStorageSlot::with_map( + storage_slots.push(StorageSlot::with_map( AuthRpoFalcon512Multisig::executed_transactions_slot().clone(), executed_transactions, )); @@ -190,7 +190,7 @@ impl From for AccountComponent { .map(|(proc_root, threshold)| (*proc_root, Word::from([*threshold, 0, 0, 0]))), ) .unwrap(); - storage_slots.push(NamedStorageSlot::with_map( + storage_slots.push(StorageSlot::with_map( AuthRpoFalcon512Multisig::procedure_thresholds_slot().clone(), proc_threshold_roots, )); diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 528ab31ff1..88fe5a2d3c 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -5,7 +5,7 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - NamedStorageSlot, + StorageSlot, StorageSlotName, }; use miden_objects::asset::{FungibleAsset, TokenSymbol}; @@ -201,10 +201,8 @@ impl From for AccountComponent { faucet.symbol.into(), Felt::ZERO, ]); - let storage_slot = NamedStorageSlot::with_value( - BasicFungibleFaucet::metadata_slot_name().clone(), - metadata, - ); + let storage_slot = + StorageSlot::with_value(BasicFungibleFaucet::metadata_slot_name().clone(), metadata); AccountComponent::new(basic_fungible_faucet_library(), vec![storage_slot]) .expect("basic fungible faucet component should satisfy the requirements of a valid account component") diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 1dde58e9c1..fcd77243c3 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -6,7 +6,7 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - NamedStorageSlot, + StorageSlot, StorageSlotName, }; use miden_objects::asset::TokenSymbol; @@ -226,8 +226,8 @@ impl From for AccountComponent { .into(); let metadata_slot = - NamedStorageSlot::with_value(NetworkFungibleFaucet::metadata_slot().clone(), metadata); - let owner_slot = NamedStorageSlot::with_value( + StorageSlot::with_value(NetworkFungibleFaucet::metadata_slot().clone(), metadata); + let owner_slot = StorageSlot::with_value( NetworkFungibleFaucet::owner_config_slot().clone(), owner_account_id_word, ); diff --git a/crates/miden-lib/src/account/interface/test.rs b/crates/miden-lib/src/account/interface/test.rs index c9d808fde5..7f9f7d4c1e 100644 --- a/crates/miden-lib/src/account/interface/test.rs +++ b/crates/miden-lib/src/account/interface/test.rs @@ -4,7 +4,7 @@ use alloc::vec::Vec; use assert_matches::assert_matches; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountBuilder, AccountComponent, AccountType, NamedStorageSlot}; +use miden_objects::account::{AccountBuilder, AccountComponent, AccountType, StorageSlot}; use miden_objects::assembly::diagnostics::NamedSource; use miden_objects::assembly::{Assembler, DefaultSourceManager}; use miden_objects::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; @@ -676,7 +676,7 @@ trait AccountComponentExt { fn compile_with_path( source_code: impl ToString, assembler: Assembler, - storage_slots: Vec, + storage_slots: Vec, library_path: impl AsRef, ) -> Result; } @@ -697,7 +697,7 @@ impl AccountComponentExt for AccountComponent { fn compile_with_path( source_code: impl ToString, assembler: Assembler, - storage_slots: Vec, + storage_slots: Vec, library_path: impl AsRef, ) -> Result { let source = NamedSource::new(library_path, source_code.to_string()); diff --git a/crates/miden-lib/src/testing/account_component/mock_account_component.rs b/crates/miden-lib/src/testing/account_component/mock_account_component.rs index 41ed676605..91e51c6166 100644 --- a/crates/miden-lib/src/testing/account_component/mock_account_component.rs +++ b/crates/miden-lib/src/testing/account_component/mock_account_component.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use miden_objects::account::{AccountCode, AccountComponent, AccountStorage, NamedStorageSlot}; +use miden_objects::account::{AccountCode, AccountComponent, AccountStorage, StorageSlot}; use crate::testing::mock_account_code::MockAccountCodeExt; @@ -18,7 +18,7 @@ use crate::testing::mock_account_code::MockAccountCodeExt; /// /// [account_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_account_library pub struct MockAccountComponent { - storage_slots: Vec, + storage_slots: Vec, } impl MockAccountComponent { @@ -35,14 +35,14 @@ impl MockAccountComponent { /// # Panics /// /// Panics if the number of slots exceeds [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. - pub fn with_slots(storage_slots: Vec) -> Self { + pub fn with_slots(storage_slots: Vec) -> Self { Self::new(storage_slots) } // HELPERS // -------------------------------------------------------------------------------------------- - fn new(storage_slots: Vec) -> Self { + fn new(storage_slots: Vec) -> Self { debug_assert!( storage_slots.len() <= AccountStorage::MAX_NUM_STORAGE_SLOTS, "too many storage slots passed to MockAccountComponent" diff --git a/crates/miden-lib/src/testing/mock_account.rs b/crates/miden-lib/src/testing/mock_account.rs index e9a3dc83f4..e558bcebe0 100644 --- a/crates/miden-lib/src/testing/mock_account.rs +++ b/crates/miden-lib/src/testing/mock_account.rs @@ -5,8 +5,8 @@ use miden_objects::account::{ AccountId, AccountStorage, AccountType, - NamedStorageSlot, StorageMap, + StorageSlot, }; use miden_objects::asset::{AssetVault, NonFungibleAsset}; use miden_objects::testing::constants::{self}; @@ -72,7 +72,7 @@ pub trait MockAccountExt { let asset = NonFungibleAsset::mock(&constants::NON_FUNGIBLE_ASSET_DATA_2); let non_fungible_storage_map = StorageMap::with_entries([(asset.vault_key().into(), asset.into())]).unwrap(); - let storage = AccountStorage::new(vec![NamedStorageSlot::with_map( + let storage = AccountStorage::new(vec![StorageSlot::with_map( AccountStorage::faucet_metadata_slot().clone(), non_fungible_storage_map, )]) diff --git a/crates/miden-objects/src/account/builder/mod.rs b/crates/miden-objects/src/account/builder/mod.rs index 92ce47fce0..680562830d 100644 --- a/crates/miden-objects/src/account/builder/mod.rs +++ b/crates/miden-objects/src/account/builder/mod.rs @@ -291,7 +291,7 @@ mod tests { use miden_processor::MastNodeExt; use super::*; - use crate::account::{NamedStorageSlot, StorageSlotName}; + use crate::account::{StorageSlot, StorageSlotName}; use crate::testing::noop_auth_component::NoopAuthComponent; const CUSTOM_CODE1: &str = " @@ -339,7 +339,7 @@ mod tests { AccountComponent::new( CUSTOM_LIBRARY1.clone(), - vec![NamedStorageSlot::with_value(CUSTOM_COMPONENT1_SLOT_NAME.clone(), value)], + vec![StorageSlot::with_value(CUSTOM_COMPONENT1_SLOT_NAME.clone(), value)], ) .expect("component should be valid") .with_supports_all_types() @@ -360,8 +360,8 @@ mod tests { AccountComponent::new( CUSTOM_LIBRARY2.clone(), vec![ - NamedStorageSlot::with_value(CUSTOM_COMPONENT2_SLOT_NAME0.clone(), value0), - NamedStorageSlot::with_value(CUSTOM_COMPONENT2_SLOT_NAME1.clone(), value1), + StorageSlot::with_value(CUSTOM_COMPONENT2_SLOT_NAME0.clone(), value0), + StorageSlot::with_value(CUSTOM_COMPONENT2_SLOT_NAME1.clone(), value1), ], ) .expect("component should be valid") diff --git a/crates/miden-objects/src/account/component/mod.rs b/crates/miden-objects/src/account/component/mod.rs index 563e8bd26a..518de8b180 100644 --- a/crates/miden-objects/src/account/component/mod.rs +++ b/crates/miden-objects/src/account/component/mod.rs @@ -6,7 +6,7 @@ use miden_assembly::{Assembler, Library, Parse}; // TODO(named_slots): Refactor templates. // mod template; // pub use template::*; -use crate::account::{AccountType, NamedStorageSlot}; +use crate::account::{AccountType, StorageSlot}; use crate::assembly::QualifiedProcedureName; use crate::{AccountError, MastForest, Word}; @@ -14,7 +14,7 @@ use crate::{AccountError, MastForest, Word}; // ================================================================================================ /// An [`AccountComponent`] defines a [`Library`] of code and the initial value and types of -/// the [`NamedStorageSlot`]s it accesses. +/// the [`StorageSlot`]s it accesses. /// /// One or more components can be used to built [`AccountCode`](crate::account::AccountCode) and /// [`AccountStorage`](crate::account::AccountStorage). @@ -31,7 +31,7 @@ use crate::{AccountError, MastForest, Word}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct AccountComponent { pub(super) library: Library, - pub(super) storage_slots: Vec, + pub(super) storage_slots: Vec, pub(super) supported_types: BTreeSet, } @@ -52,8 +52,8 @@ impl AccountComponent { /// or in their fallible constructors. /// /// Returns an error if: - /// - The number of given [`NamedStorageSlot`]s exceeds 255. - pub fn new(code: Library, storage_slots: Vec) -> Result { + /// - The number of given [`StorageSlot`]s exceeds 255. + pub fn new(code: Library, storage_slots: Vec) -> Result { // Check that we have less than 256 storage slots. u8::try_from(storage_slots.len()) .map_err(|_| AccountError::StorageTooManySlots(storage_slots.len() as u64))?; @@ -79,7 +79,7 @@ impl AccountComponent { pub fn compile( source_code: impl Parse, assembler: Assembler, - storage_slots: Vec, + storage_slots: Vec, ) -> Result { let library = assembler .assemble_library([source_code]) @@ -181,8 +181,8 @@ impl AccountComponent { self.library.mast_forest().as_ref() } - /// Returns a slice of the underlying [`NamedStorageSlot`]s of this component. - pub fn storage_slots(&self) -> &[NamedStorageSlot] { + /// Returns a slice of the underlying [`StorageSlot`]s of this component. + pub fn storage_slots(&self) -> &[StorageSlot] { self.storage_slots.as_slice() } diff --git a/crates/miden-objects/src/account/delta/mod.rs b/crates/miden-objects/src/account/delta/mod.rs index 0e545d85bc..90d90f8c20 100644 --- a/crates/miden-objects/src/account/delta/mod.rs +++ b/crates/miden-objects/src/account/delta/mod.rs @@ -6,7 +6,7 @@ use crate::account::{ AccountCode, AccountId, AccountStorage, - NamedStorageSlot, + StorageSlot, StorageSlotType, }; use crate::asset::AssetVault; @@ -362,8 +362,8 @@ impl TryFrom<&AccountDelta> for Account { let mut empty_storage_slots = Vec::new(); for (slot_name, slot_type) in delta.storage().slots() { let slot = match slot_type { - StorageSlotType::Value => NamedStorageSlot::with_empty_value(slot_name.clone()), - StorageSlotType::Map => NamedStorageSlot::with_empty_map(slot_name.clone()), + StorageSlotType::Value => StorageSlot::with_empty_value(slot_name.clone()), + StorageSlotType::Map => StorageSlot::with_empty_map(slot_name.clone()), }; empty_storage_slots.push(slot); } diff --git a/crates/miden-objects/src/account/header.rs b/crates/miden-objects/src/account/header.rs index 19d7d98c9a..1d6a14d802 100644 --- a/crates/miden-objects/src/account/header.rs +++ b/crates/miden-objects/src/account/header.rs @@ -187,7 +187,7 @@ mod tests { use super::AccountHeader; use crate::Word; - use crate::account::StorageSlot; + use crate::account::StorageSlotContent; use crate::account::tests::build_account; use crate::asset::FungibleAsset; @@ -196,7 +196,7 @@ mod tests { let init_nonce = Felt::new(1); let asset_0 = FungibleAsset::mock(99); let word = Word::from([1, 2, 3, 4u32]); - let storage_slot = StorageSlot::Value(word); + let storage_slot = StorageSlotContent::Value(word); let account = build_account(vec![asset_0], init_nonce, vec![storage_slot]); let account_header: AccountHeader = account.into(); diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 52dd05cc21..be3ce48801 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -67,12 +67,12 @@ mod storage; pub use storage::{ AccountStorage, AccountStorageHeader, - NamedStorageSlot, PartialStorage, PartialStorageMap, StorageMap, StorageMapWitness, StorageSlot, + StorageSlotContent, StorageSlotId, StorageSlotName, StorageSlotType, @@ -171,8 +171,8 @@ impl Account { /// To illustrate, given two components with one and two storage slots respectively: /// /// - RpoFalcon512 Component: Component slot 0 stores the public key. - /// - Custom Component: Component slot 0 stores a custom [`StorageSlot::Value`] and component - /// slot 1 stores a custom [`StorageSlot::Map`]. + /// - Custom Component: Component slot 0 stores a custom [`StorageSlotContent::Value`] and + /// component slot 1 stores a custom [`StorageSlotContent::Map`]. /// /// When combined, their assigned slots in the [`AccountStorage`] would be: /// @@ -445,10 +445,10 @@ impl TryFrom for AccountDelta { for slot in storage.into_slots() { let (slot_name, _, slot_content) = slot.into_parts(); match slot_content { - StorageSlot::Value(word) => { + StorageSlotContent::Value(word) => { value_slots.insert(slot_name, word); }, - StorageSlot::Map(storage_map) => { + StorageSlotContent::Map(storage_map) => { let map_delta = StorageMapDelta::new( storage_map .into_entries() @@ -640,11 +640,11 @@ mod tests { AccountComponent, AccountIdVersion, AccountType, - NamedStorageSlot, PartialAccount, StorageMap, StorageMapDelta, StorageSlot, + StorageSlotContent, StorageSlotName, }; use crate::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; @@ -661,7 +661,7 @@ mod tests { let init_nonce = Felt::new(1); let asset_0 = FungibleAsset::mock(99); let word = Word::from([1, 2, 3, 4u32]); - let storage_slot = StorageSlot::Value(word); + let storage_slot = StorageSlotContent::Value(word); let account = build_account(vec![asset_0], init_nonce, vec![storage_slot]); let serialized = account.to_bytes(); @@ -702,8 +702,8 @@ mod tests { let asset_1 = NonFungibleAsset::mock(&[1, 2, 3]); // build storage slots - let storage_slot_value_0 = StorageSlot::Value(Word::from([1, 2, 3, 4u32])); - let storage_slot_value_1 = StorageSlot::Value(Word::from([5, 6, 7, 8u32])); + let storage_slot_value_0 = StorageSlotContent::Value(Word::from([1, 2, 3, 4u32])); + let storage_slot_value_1 = StorageSlotContent::Value(Word::from([5, 6, 7, 8u32])); let mut storage_map = StorageMap::with_entries([ ( Word::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]), @@ -720,7 +720,7 @@ mod tests { ), ]) .unwrap(); - let storage_slot_map = StorageSlot::Map(storage_map.clone()); + let storage_slot_map = StorageSlotContent::Map(storage_map.clone()); let mut account = build_account( vec![asset_0], @@ -761,9 +761,9 @@ mod tests { vec![asset_1], final_nonce, vec![ - StorageSlot::Value(Word::empty()), - StorageSlot::Value(Word::from([1, 2, 3, 4u32])), - StorageSlot::Map(storage_map), + StorageSlotContent::Value(Word::empty()), + StorageSlotContent::Value(Word::from([1, 2, 3, 4u32])), + StorageSlotContent::Map(storage_map), ], ); @@ -779,7 +779,7 @@ mod tests { let init_nonce = Felt::new(1); let asset = FungibleAsset::mock(110); let mut account = - build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::empty())]); + build_account(vec![asset], init_nonce, vec![StorageSlotContent::Value(Word::empty())]); // build account delta let storage_delta = AccountStorageDeltaBuilder::new() @@ -802,7 +802,7 @@ mod tests { let init_nonce = Felt::new(2); let asset = FungibleAsset::mock(100); let mut account = - build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::empty())]); + build_account(vec![asset], init_nonce, vec![StorageSlotContent::Value(Word::empty())]); // build account delta let final_nonce = Felt::new(1); @@ -824,7 +824,7 @@ mod tests { let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap(); let init_nonce = Felt::new(1); let word = Word::from([1, 2, 3, 4u32]); - let storage_slot = StorageSlot::Value(word); + let storage_slot = StorageSlotContent::Value(word); let mut account = build_account(vec![], init_nonce, vec![storage_slot]); // build account delta @@ -852,7 +852,11 @@ mod tests { AccountDelta::new(account_id, storage_delta, vault_delta, nonce_delta).unwrap() } - pub fn build_account(assets: Vec, nonce: Felt, slots: Vec) -> Account { + pub fn build_account( + assets: Vec, + nonce: Felt, + slots: Vec, + ) -> Account { let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); let code = AccountCode::mock(); @@ -861,7 +865,7 @@ mod tests { let slots = slots .into_iter() .enumerate() - .map(|(idx, slot)| NamedStorageSlot::new(StorageSlotName::mock(idx), slot)) + .map(|(idx, slot)| StorageSlot::new(StorageSlotName::mock(idx), slot)) .collect(); let storage = AccountStorage::new(slots).unwrap(); diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-objects/src/account/storage/header.rs index 1c2777d50a..2ff784a65d 100644 --- a/crates/miden-objects/src/account/storage/header.rs +++ b/crates/miden-objects/src/account/storage/header.rs @@ -1,8 +1,8 @@ use alloc::string::ToString; use alloc::vec::Vec; -use super::{AccountStorage, Felt, StorageSlot, StorageSlotType, Word}; -use crate::account::{StorageSlotId, StorageSlotName}; +use super::{AccountStorage, Felt, StorageSlotType, Word}; +use crate::account::{StorageSlot, StorageSlotId, StorageSlotName}; use crate::crypto::SequentialCommit; use crate::utils::serde::{ ByteReader, @@ -41,8 +41,8 @@ impl StorageSlotHeader { /// ```text /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE] /// ``` - pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT] { - let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT]; + pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS] { + let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS]; elements[0..4].copy_from_slice(&[ Felt::ZERO, self.r#type.as_felt(), diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index c89df84916..d4154c393c 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -19,7 +19,7 @@ use crate::crypto::SequentialCommit; use crate::utils::sync::LazyLock; mod slot; -pub use slot::{NamedStorageSlot, StorageSlot, StorageSlotId, StorageSlotName, StorageSlotType}; +pub use slot::{StorageSlot, StorageSlotContent, StorageSlotId, StorageSlotName, StorageSlotType}; mod map; pub use map::{PartialStorageMap, StorageMap, StorageMapWitness}; @@ -37,15 +37,15 @@ static FAUCET_METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { // ACCOUNT STORAGE // ================================================================================================ -/// Account storage is composed of a variable number of name-addressable [`NamedStorageSlot`]s up to +/// Account storage is composed of a variable number of name-addressable [`StorageSlot`]s up to /// 255 slots in total. /// -/// Each slot has a type which defines its size and structure. Currently, the following types are -/// supported: -/// - [`StorageSlot::Value`]: contains a single [`Word`] of data (i.e., 32 bytes). -/// - [`StorageSlot::Map`]: contains a [`StorageMap`] which is a key-value map where both keys and -/// values are [Word]s. The value of a storage slot containing a map is the commitment to the -/// underlying map. +/// Each slot consists of a [`StorageSlotName`] and [`StorageSlotContent`] which defines its size +/// and structure. Currently, the following content types are supported: +/// - [`StorageSlotContent::Value`]: contains a single [`Word`] of data (i.e., 32 bytes). +/// - [`StorageSlotContent::Map`]: contains a [`StorageMap`] which is a key-value map where both +/// keys and values are [Word]s. The value of a storage slot containing a map is the commitment to +/// the underlying map. /// /// Slots are sorted by [`StorageSlotName`] (or [`StorageSlotId`] equivalently). This order is /// necessary to: @@ -56,7 +56,7 @@ static FAUCET_METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { /// adjacent items have the same slot name. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct AccountStorage { - slots: Vec, + slots: Vec, } impl AccountStorage { @@ -75,7 +75,7 @@ impl AccountStorage { /// Returns an error if: /// - The number of [`StorageSlot`]s exceeds 255. /// - There are multiple storage slots with the same [`StorageSlotName`]. - pub fn new(mut slots: Vec) -> Result { + pub fn new(mut slots: Vec) -> Result { let num_slots = slots.len(); if num_slots > Self::MAX_NUM_STORAGE_SLOTS { @@ -117,10 +117,10 @@ impl AccountStorage { ) -> Result { let mut storage_slots = match account_type { AccountType::FungibleFaucet => { - vec![NamedStorageSlot::with_empty_value(Self::faucet_metadata_slot().clone())] + vec![StorageSlot::with_empty_value(Self::faucet_metadata_slot().clone())] }, AccountType::NonFungibleFaucet => { - vec![NamedStorageSlot::with_empty_map(Self::faucet_metadata_slot().clone())] + vec![StorageSlot::with_empty_map(Self::faucet_metadata_slot().clone())] }, _ => vec![], }; @@ -171,12 +171,12 @@ impl AccountStorage { } /// Returns a reference to the storage slots. - pub fn slots(&self) -> &[NamedStorageSlot] { + pub fn slots(&self) -> &[StorageSlot] { &self.slots } /// Consumes self and returns the storage slots of the account storage. - pub fn into_slots(self) -> Vec { + pub fn into_slots(self) -> Vec { self.slots } @@ -186,11 +186,7 @@ impl AccountStorage { self.slots .iter() .map(|slot| { - ( - slot.name().clone(), - slot.storage_slot().slot_type(), - slot.storage_slot().value(), - ) + (slot.name().clone(), slot.content().slot_type(), slot.content().value()) }) .collect(), ) @@ -199,20 +195,20 @@ impl AccountStorage { /// Returns a reference to the storage slot with the provided name, if it exists, `None` /// otherwise. - pub fn get(&self, slot_name: &StorageSlotName) -> Option<&NamedStorageSlot> { + pub fn get(&self, slot_name: &StorageSlotName) -> Option<&StorageSlot> { debug_assert!(self.slots.is_sorted()); let slot_id = slot_name.compute_id(); self.slots - .binary_search_by_key(&slot_id, |named_slot| named_slot.slot_id()) + .binary_search_by_key(&slot_id, |slot| slot.slot_id()) .map(|idx| &self.slots[idx]) .ok() } - fn get_mut(&mut self, slot_name: &StorageSlotName) -> Option<&mut NamedStorageSlot> { + fn get_mut(&mut self, slot_name: &StorageSlotName) -> Option<&mut StorageSlot> { let slot_id = slot_name.compute_id(); self.slots - .binary_search_by_key(&slot_id, |named_slot| named_slot.slot_id()) + .binary_search_by_key(&slot_id, |slot| slot.slot_id()) .map(|idx| &mut self.slots[idx]) .ok() } @@ -225,7 +221,7 @@ impl AccountStorage { /// - A slot with the provided name does not exist. pub fn get_item(&self, slot_name: &StorageSlotName) -> Result { self.get(slot_name) - .map(|named_slot| named_slot.storage_slot().value()) + .map(|slot| slot.content().value()) .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }) } @@ -243,8 +239,8 @@ impl AccountStorage { ) -> Result { self.get(slot_name) .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }) - .and_then(|named_slot| match named_slot.storage_slot() { - StorageSlot::Map(map) => Ok(map.get(&key)), + .and_then(|slot| match slot.content() { + StorageSlotContent::Map(map) => Ok(map.get(&key)), _ => Err(AccountError::StorageSlotNotMap(slot_name.clone())), }) } @@ -266,12 +262,12 @@ impl AccountStorage { // Update storage maps for (slot_name, map_delta) in delta.maps().iter() { - let named_slot = self + let slot = self .get_mut(slot_name) .ok_or(AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })?; - let storage_map = match named_slot.storage_slot_mut() { - StorageSlot::Map(map) => map, + let storage_map = match slot.content_mut() { + StorageSlotContent::Map(map) => map, _ => return Err(AccountError::StorageSlotNotMap(slot_name.clone())), }; @@ -300,13 +296,13 @@ impl AccountStorage { AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() } })?; - let StorageSlot::Value(old_value) = slot.storage_slot() else { + let StorageSlotContent::Value(old_value) = slot.content() else { return Err(AccountError::StorageSlotNotValue(slot_name.clone())); }; let old_value = *old_value; - let mut new_slot = StorageSlot::Value(value); - core::mem::swap(slot.storage_slot_mut(), &mut new_slot); + let mut new_slot = StorageSlotContent::Value(value); + core::mem::swap(slot.content_mut(), &mut new_slot); Ok(old_value) } @@ -331,7 +327,7 @@ impl AccountStorage { AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() } })?; - let StorageSlot::Map(storage_map) = slot.storage_slot_mut() else { + let StorageSlotContent::Map(storage_map) = slot.content_mut() else { return Err(AccountError::StorageSlotNotMap(slot_name.clone())); }; @@ -347,8 +343,8 @@ impl AccountStorage { // ================================================================================================ impl IntoIterator for AccountStorage { - type Item = NamedStorageSlot; - type IntoIter = alloc::vec::IntoIter; + type Item = StorageSlot; + type IntoIter = alloc::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.slots.into_iter() @@ -364,11 +360,11 @@ impl SequentialCommit for AccountStorage { fn to_elements(&self) -> Vec { self.slots() .iter() - .flat_map(|named_slot| { + .flat_map(|slot| { StorageSlotHeader::new( - named_slot.slot_id(), - named_slot.storage_slot().slot_type(), - named_slot.storage_slot().value(), + slot.slot_id(), + slot.content().slot_type(), + slot.content().value(), ) .to_elements() }) @@ -401,7 +397,7 @@ impl Serializable for AccountStorage { impl Deserializable for AccountStorage { fn read_from(source: &mut R) -> Result { let num_slots = source.read_u8()? as usize; - let slots = source.read_many::(num_slots)?; + let slots = source.read_many::(num_slots)?; Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string())) } @@ -413,7 +409,7 @@ impl Deserializable for AccountStorage { #[cfg(test)] mod tests { use super::{AccountStorage, Deserializable, Serializable}; - use crate::account::{NamedStorageSlot, StorageSlotName}; + use crate::account::{StorageSlot, StorageSlotName}; #[test] fn test_serde_account_storage() -> anyhow::Result<()> { @@ -424,8 +420,8 @@ mod tests { // storage with values for default types let storage = AccountStorage::new(vec![ - NamedStorageSlot::with_empty_value(StorageSlotName::new("miden::test::value")?), - NamedStorageSlot::with_empty_map(StorageSlotName::new("miden::test::map")?), + StorageSlot::with_empty_value(StorageSlotName::new("miden::test::value")?), + StorageSlot::with_empty_map(StorageSlotName::new("miden::test::map")?), ]) .unwrap(); let bytes = storage.to_bytes(); @@ -440,8 +436,8 @@ mod tests { let map_slot = StorageSlotName::new("miden::test::map")?; let slots = vec![ - NamedStorageSlot::with_empty_value(counter_slot.clone()), - NamedStorageSlot::with_empty_map(map_slot.clone()), + StorageSlot::with_empty_value(counter_slot.clone()), + StorageSlot::with_empty_map(map_slot.clone()), ]; let storage = AccountStorage::new(slots.clone())?; diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-objects/src/account/storage/partial.rs index 2f22a90b17..acc292e419 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-objects/src/account/storage/partial.rs @@ -4,7 +4,7 @@ use miden_core::utils::{Deserializable, Serializable}; use miden_crypto::Word; use miden_crypto::merkle::{InnerNodeInfo, SmtLeaf}; -use super::{AccountStorage, AccountStorageHeader, StorageSlot}; +use super::{AccountStorage, AccountStorageHeader, StorageSlotContent}; use crate::AccountError; use crate::account::PartialStorageMap; @@ -61,7 +61,7 @@ impl PartialStorage { let mut maps = BTreeMap::new(); for slot in account_storage { - if let StorageSlot::Map(storage_map) = slot.into_parts().2 { + if let StorageSlotContent::Map(storage_map) = slot.into_parts().2 { let partial_map = PartialStorageMap::new_full(storage_map); maps.insert(partial_map.root(), partial_map); } @@ -80,7 +80,7 @@ impl PartialStorage { let mut maps = BTreeMap::new(); for slot in account_storage.slots() { - if let StorageSlot::Map(storage_map) = slot.storage_slot() { + if let StorageSlotContent::Map(storage_map) = slot.content() { let partial_map = PartialStorageMap::new_minimal(storage_map); maps.insert(partial_map.root(), partial_map); } @@ -159,10 +159,10 @@ mod tests { use crate::account::{ AccountStorage, AccountStorageHeader, - NamedStorageSlot, PartialStorage, PartialStorageMap, StorageMap, + StorageSlot, StorageSlotName, }; @@ -179,7 +179,7 @@ mod tests { let slot_name = StorageSlotName::new("miden::test_map")?; let storage = - AccountStorage::new(vec![NamedStorageSlot::with_map(slot_name.clone(), map_1.clone())]) + AccountStorage::new(vec![StorageSlot::with_map(slot_name.clone(), map_1.clone())]) .unwrap(); // Create partial storage with validation of one map key diff --git a/crates/miden-objects/src/account/storage/slot/mod.rs b/crates/miden-objects/src/account/storage/slot/mod.rs index 74dc4ece08..711ae48bed 100644 --- a/crates/miden-objects/src/account/storage/slot/mod.rs +++ b/crates/miden-objects/src/account/storage/slot/mod.rs @@ -1,10 +1,3 @@ -use miden_core::EMPTY_WORD; -use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_processor::DeserializationError; - -use super::map::EMPTY_STORAGE_MAP_ROOT; -use super::{StorageMap, Word}; - mod slot_name; pub use slot_name::StorageSlotName; @@ -14,131 +7,8 @@ pub use slot_id::StorageSlotId; mod r#type; pub use r#type::StorageSlotType; -mod named_slot; -pub use named_slot::NamedStorageSlot; - -// STORAGE SLOT -// ================================================================================================ - -/// An object representing the contents of an account's storage slot. -/// -/// An account storage slot can be of two types: -/// - A simple value which contains a single word (4 field elements or ~32 bytes). -/// - A key value map where both keys and values are words. The capacity of such storage slot is -/// theoretically unlimited. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum StorageSlot { - Value(Word), - Map(StorageMap), -} - -impl StorageSlot { - /// The number of field elements needed to represent a [StorageSlot] in kernel memory. - pub const NUM_ELEMENTS_PER_STORAGE_SLOT: usize = 8; - - /// Returns true if this storage slot has a value equal the default of it's type - pub fn is_default(&self) -> bool { - match self { - StorageSlot::Value(value) => *value == EMPTY_WORD, - StorageSlot::Map(map) => map.root() == EMPTY_STORAGE_MAP_ROOT, - } - } - - /// Returns the empty [Word] for a storage slot of this type - pub fn default_word(&self) -> Word { - match self { - StorageSlot::Value(_) => EMPTY_WORD, - StorageSlot::Map(_) => EMPTY_STORAGE_MAP_ROOT, - } - } - - /// Returns a [`StorageSlot::Value`] with an empty word. - pub fn empty_value() -> Self { - StorageSlot::Value(EMPTY_WORD) - } - - /// Returns an empty [`StorageSlot::Map`]. - pub fn empty_map() -> Self { - StorageSlot::Map(StorageMap::new()) - } - - /// Returns this storage slot value as a [Word] - /// - /// Returns: - /// - For [`StorageSlot::Value`] the value. - /// - For [`StorageSlot::Map`] the root of the [StorageMap]. - pub fn value(&self) -> Word { - match self { - Self::Value(value) => *value, - Self::Map(map) => map.root(), - } - } - - /// Returns the type of this storage slot - pub fn slot_type(&self) -> StorageSlotType { - match self { - StorageSlot::Value(_) => StorageSlotType::Value, - StorageSlot::Map(_) => StorageSlotType::Map, - } - } -} - -// SERIALIZATION -// ================================================================================================ - -impl Serializable for StorageSlot { - fn write_into(&self, target: &mut W) { - target.write(self.slot_type()); - - match self { - Self::Value(value) => target.write(value), - Self::Map(map) => target.write(map), - } - } - - fn get_size_hint(&self) -> usize { - let mut size = self.slot_type().get_size_hint(); - - size += match self { - StorageSlot::Value(word) => word.get_size_hint(), - StorageSlot::Map(storage_map) => storage_map.get_size_hint(), - }; - - size - } -} - -impl Deserializable for StorageSlot { - fn read_from(source: &mut R) -> Result { - let storage_slot_type = source.read::()?; - - match storage_slot_type { - StorageSlotType::Value => { - let word = source.read::()?; - Ok(StorageSlot::Value(word)) - }, - StorageSlotType::Map => { - let map = source.read::()?; - Ok(StorageSlot::Map(map)) - }, - } - } -} - -// TESTS -// ================================================================================================ - -#[cfg(test)] -mod tests { - use miden_core::utils::{Deserializable, Serializable}; - - use crate::account::AccountStorage; +mod storage_slot; +pub use storage_slot::StorageSlot; - #[test] - fn test_serde_account_storage_slot() { - let storage = AccountStorage::mock(); - let serialized = storage.to_bytes(); - let deserialized = AccountStorage::read_from_bytes(&serialized).unwrap(); - assert_eq!(deserialized, storage) - } -} +mod slot_content; +pub use slot_content::StorageSlotContent; diff --git a/crates/miden-objects/src/account/storage/slot/named_slot.rs b/crates/miden-objects/src/account/storage/slot/named_slot.rs deleted file mode 100644 index ec1d1d2d41..0000000000 --- a/crates/miden-objects/src/account/storage/slot/named_slot.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::Word; -use crate::account::storage::slot::StorageSlotId; -use crate::account::{StorageMap, StorageSlot, StorageSlotName, StorageSlotType}; - -/// An individual storage slot in [`AccountStorage`](crate::account::AccountStorage). -/// -/// This consists of a [`StorageSlotName`] that uniquely identifies the slot and its [`StorageSlot`] -/// content. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NamedStorageSlot { - /// The name of the storage slot. - name: StorageSlotName, - /// The cached [`StorageSlotId`] of the slot name. This field must always be consistent with - /// the slot name. - /// - /// This is cached so that the `Ord` implementation can use the computed slot ID instead of - /// having to hash the slot name on every comparison operation. - slot_id: StorageSlotId, - /// The underlying storage slot. - slot: StorageSlot, -} - -impl NamedStorageSlot { - // CONSTRUCTORS - // -------------------------------------------------------------------------------------------- - - /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and [`StorageSlot`]. - pub fn new(name: StorageSlotName, slot: StorageSlot) -> Self { - let slot_id = name.compute_id(); - - Self { name, slot_id, slot } - } - - /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and the `value` - /// wrapped into a [`StorageSlot::Value`]. - pub fn with_value(name: StorageSlotName, value: Word) -> Self { - Self::new(name, StorageSlot::Value(value)) - } - - /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and - /// [`StorageSlot::empty_value`]. - pub fn with_empty_value(name: StorageSlotName) -> Self { - Self::new(name, StorageSlot::empty_value()) - } - - /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and the `map` wrapped - /// into a [`StorageSlot::Map`] - pub fn with_map(name: StorageSlotName, map: StorageMap) -> Self { - Self::new(name, StorageSlot::Map(map)) - } - - /// Creates a new [`NamedStorageSlot`] with the given [`StorageSlotName`] and - /// [`StorageSlot::empty_map`]. - pub fn with_empty_map(name: StorageSlotName) -> Self { - Self::new(name, StorageSlot::empty_map()) - } - - // ACCESSORS - // -------------------------------------------------------------------------------------------- - - /// Returns the [`StorageSlotName`] by which the [`NamedStorageSlot`] is identified. - pub fn name(&self) -> &StorageSlotName { - &self.name - } - - /// Returns the [`StorageSlotId`] by which the [`NamedStorageSlot`] is identified. - pub fn slot_id(&self) -> StorageSlotId { - self.slot_id - } - - /// Returns this storage slot value as a [Word] - /// - /// Returns: - /// - For [`StorageSlot::Value`] the value. - /// - For [`StorageSlot::Map`] the root of the [StorageMap]. - pub fn value(&self) -> Word { - self.storage_slot().value() - } - - /// Returns a reference to the [`StorageSlot`] contained in this [`NamedStorageSlot`]. - pub fn storage_slot(&self) -> &StorageSlot { - &self.slot - } - - /// Returns the [`StorageSlotType`] of this [`NamedStorageSlot`]. - pub fn slot_type(&self) -> StorageSlotType { - self.slot.slot_type() - } - - // MUTATORS - // -------------------------------------------------------------------------------------------- - - /// Returns a mutable reference to the [`StorageSlot`] contained in this [`NamedStorageSlot`]. - pub fn storage_slot_mut(&mut self) -> &mut StorageSlot { - &mut self.slot - } - - /// Consumes self and returns the underlying parts. - pub fn into_parts(self) -> (StorageSlotName, StorageSlotId, StorageSlot) { - (self.name, self.slot_id, self.slot) - } -} - -impl Ord for NamedStorageSlot { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.slot_id.cmp(&other.slot_id) - } -} - -impl PartialOrd for NamedStorageSlot { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -// SERIALIZATION -// ================================================================================================ - -impl crate::utils::serde::Serializable for NamedStorageSlot { - fn write_into(&self, target: &mut W) { - target.write(&self.name); - target.write(&self.slot); - } - - fn get_size_hint(&self) -> usize { - self.name.get_size_hint() + self.storage_slot().get_size_hint() - } -} - -impl crate::utils::serde::Deserializable for NamedStorageSlot { - fn read_from( - source: &mut R, - ) -> Result { - let name: StorageSlotName = source.read()?; - let slot: StorageSlot = source.read()?; - - Ok(Self::new(name, slot)) - } -} diff --git a/crates/miden-objects/src/account/storage/slot/slot_content.rs b/crates/miden-objects/src/account/storage/slot/slot_content.rs new file mode 100644 index 0000000000..746391de54 --- /dev/null +++ b/crates/miden-objects/src/account/storage/slot/slot_content.rs @@ -0,0 +1,130 @@ +use miden_core::EMPTY_WORD; +use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use miden_processor::DeserializationError; + +use crate::account::StorageSlotType; +use crate::account::storage::map::EMPTY_STORAGE_MAP_ROOT; +use crate::account::storage::{StorageMap, Word}; + +// STORAGE SLOT CONTENT +// ================================================================================================ + +/// Represents the contents of a [`StorageSlot`](super::StorageSlot). +/// +/// The content of a storage slot can be of two types: +/// - A simple value which contains a single word (4 field elements or ~32 bytes). +/// - A key value map where both keys and values are words. The capacity of such storage slot is +/// theoretically unlimited. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum StorageSlotContent { + Value(Word), + Map(StorageMap), +} + +impl StorageSlotContent { + /// Returns true if this storage slot has a value equal to the default of its type. + pub fn is_default(&self) -> bool { + match self { + StorageSlotContent::Value(value) => *value == EMPTY_WORD, + StorageSlotContent::Map(map) => map.root() == EMPTY_STORAGE_MAP_ROOT, + } + } + + /// Returns the empty [Word] for a storage slot of this type + pub fn default_word(&self) -> Word { + match self { + StorageSlotContent::Value(_) => EMPTY_WORD, + StorageSlotContent::Map(_) => EMPTY_STORAGE_MAP_ROOT, + } + } + + /// Returns a [`StorageSlotContent::Value`] with an empty word. + pub fn empty_value() -> Self { + StorageSlotContent::Value(EMPTY_WORD) + } + + /// Returns an empty [`StorageSlotContent::Map`]. + pub fn empty_map() -> Self { + StorageSlotContent::Map(StorageMap::new()) + } + + /// Returns this storage slot value as a [Word] + /// + /// Returns: + /// - For [`StorageSlotContent::Value`] the value. + /// - For [`StorageSlotContent::Map`] the root of the [StorageMap]. + pub fn value(&self) -> Word { + match self { + Self::Value(value) => *value, + Self::Map(map) => map.root(), + } + } + + /// Returns the type of this storage slot + pub fn slot_type(&self) -> StorageSlotType { + match self { + StorageSlotContent::Value(_) => StorageSlotType::Value, + StorageSlotContent::Map(_) => StorageSlotType::Map, + } + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for StorageSlotContent { + fn write_into(&self, target: &mut W) { + target.write(self.slot_type()); + + match self { + Self::Value(value) => target.write(value), + Self::Map(map) => target.write(map), + } + } + + fn get_size_hint(&self) -> usize { + let mut size = self.slot_type().get_size_hint(); + + size += match self { + StorageSlotContent::Value(word) => word.get_size_hint(), + StorageSlotContent::Map(storage_map) => storage_map.get_size_hint(), + }; + + size + } +} + +impl Deserializable for StorageSlotContent { + fn read_from(source: &mut R) -> Result { + let storage_slot_type = source.read::()?; + + match storage_slot_type { + StorageSlotType::Value => { + let word = source.read::()?; + Ok(StorageSlotContent::Value(word)) + }, + StorageSlotType::Map => { + let map = source.read::()?; + Ok(StorageSlotContent::Map(map)) + }, + } + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use miden_core::utils::{Deserializable, Serializable}; + + use crate::account::AccountStorage; + + #[test] + fn test_serde_storage_slot_content() { + let storage = AccountStorage::mock(); + let serialized = storage.to_bytes(); + let deserialized = AccountStorage::read_from_bytes(&serialized).unwrap(); + assert_eq!(deserialized, storage) + } +} diff --git a/crates/miden-objects/src/account/storage/slot/storage_slot.rs b/crates/miden-objects/src/account/storage/slot/storage_slot.rs new file mode 100644 index 0000000000..ef63ca079c --- /dev/null +++ b/crates/miden-objects/src/account/storage/slot/storage_slot.rs @@ -0,0 +1,147 @@ +use crate::Word; +use crate::account::storage::slot::StorageSlotId; +use crate::account::{StorageMap, StorageSlotContent, StorageSlotName, StorageSlotType}; + +/// An individual storage slot in [`AccountStorage`](crate::account::AccountStorage). +/// +/// This consists of a [`StorageSlotName`] that uniquely identifies the slot and its +/// [`StorageSlotContent`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StorageSlot { + /// The name of the storage slot. + name: StorageSlotName, + /// The cached [`StorageSlotId`] of the slot name. This field must always be consistent with + /// the slot name. + /// + /// This is cached so that the `Ord` implementation can use the computed slot ID instead of + /// having to hash the slot name on every comparison operation. + slot_id: StorageSlotId, + /// The content of the storage slot. + content: StorageSlotContent, +} + +impl StorageSlot { + // CONSTANTS + // -------------------------------------------------------------------------------------------- + + /// The number of field elements that represent a [`StorageSlot`] in kernel memory. + pub const NUM_ELEMENTS: usize = 8; + + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`StorageSlot`] with the given [`StorageSlotName`] and + /// [`StorageSlotContent`]. + pub fn new(name: StorageSlotName, content: StorageSlotContent) -> Self { + let slot_id = name.compute_id(); + + Self { name, slot_id, content } + } + + /// Creates a new [`StorageSlot`] with the given [`StorageSlotName`] and the `value` + /// wrapped into a [`StorageSlotContent::Value`]. + pub fn with_value(name: StorageSlotName, value: Word) -> Self { + Self::new(name, StorageSlotContent::Value(value)) + } + + /// Creates a new [`StorageSlot`] with the given [`StorageSlotName`] and + /// [`StorageSlotContent::empty_value`]. + pub fn with_empty_value(name: StorageSlotName) -> Self { + Self::new(name, StorageSlotContent::empty_value()) + } + + /// Creates a new [`StorageSlot`] with the given [`StorageSlotName`] and the `map` wrapped + /// into a [`StorageSlotContent::Map`] + pub fn with_map(name: StorageSlotName, map: StorageMap) -> Self { + Self::new(name, StorageSlotContent::Map(map)) + } + + /// Creates a new [`StorageSlot`] with the given [`StorageSlotName`] and + /// [`StorageSlotContent::empty_map`]. + pub fn with_empty_map(name: StorageSlotName) -> Self { + Self::new(name, StorageSlotContent::empty_map()) + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the [`StorageSlotName`] by which the [`StorageSlot`] is identified. + pub fn name(&self) -> &StorageSlotName { + &self.name + } + + /// Returns the [`StorageSlotId`] by which the [`StorageSlot`] is identified. + pub fn slot_id(&self) -> StorageSlotId { + self.slot_id + } + + /// Returns this storage slot value as a [Word] + /// + /// Returns: + /// - For [`StorageSlotContent::Value`] the value. + /// - For [`StorageSlotContent::Map`] the root of the [StorageMap]. + pub fn value(&self) -> Word { + self.content().value() + } + + /// Returns a reference to the [`StorageSlotContent`] contained in this [`StorageSlot`]. + pub fn content(&self) -> &StorageSlotContent { + &self.content + } + + /// Returns the [`StorageSlotType`] of this [`StorageSlot`]. + pub fn slot_type(&self) -> StorageSlotType { + self.content.slot_type() + } + + // MUTATORS + // -------------------------------------------------------------------------------------------- + + /// Returns a mutable reference to the [`StorageSlotContent`] contained in this + /// [`StorageSlot`]. + pub fn content_mut(&mut self) -> &mut StorageSlotContent { + &mut self.content + } + + /// Consumes self and returns the underlying parts. + pub fn into_parts(self) -> (StorageSlotName, StorageSlotId, StorageSlotContent) { + (self.name, self.slot_id, self.content) + } +} + +impl Ord for StorageSlot { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.slot_id.cmp(&other.slot_id) + } +} + +impl PartialOrd for StorageSlot { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// SERIALIZATION +// ================================================================================================ + +impl crate::utils::serde::Serializable for StorageSlot { + fn write_into(&self, target: &mut W) { + target.write(&self.name); + target.write(&self.content); + } + + fn get_size_hint(&self) -> usize { + self.name.get_size_hint() + self.content().get_size_hint() + } +} + +impl crate::utils::serde::Deserializable for StorageSlot { + fn read_from( + source: &mut R, + ) -> Result { + let name: StorageSlotName = source.read()?; + let content: StorageSlotContent = source.read()?; + + Ok(Self::new(name, content)) + } +} diff --git a/crates/miden-objects/src/testing/storage.rs b/crates/miden-objects/src/testing/storage.rs index 39afe9b227..55a3b9ad17 100644 --- a/crates/miden-objects/src/testing/storage.rs +++ b/crates/miden-objects/src/testing/storage.rs @@ -9,9 +9,9 @@ use crate::AccountDeltaError; use crate::account::{ AccountStorage, AccountStorageDelta, - NamedStorageSlot, StorageMap, StorageMapDelta, + StorageSlot, StorageSlotName, }; use crate::note::NoteAssets; @@ -103,20 +103,20 @@ impl AccountStorage { AccountStorage::new(Self::mock_storage_slots()).unwrap() } - pub fn mock_storage_slots() -> Vec { + pub fn mock_storage_slots() -> Vec { vec![Self::mock_value_slot0(), Self::mock_value_slot1(), Self::mock_map_slot()] } - pub fn mock_value_slot0() -> NamedStorageSlot { - NamedStorageSlot::with_value(MOCK_VALUE_SLOT0.clone(), STORAGE_VALUE_0) + pub fn mock_value_slot0() -> StorageSlot { + StorageSlot::with_value(MOCK_VALUE_SLOT0.clone(), STORAGE_VALUE_0) } - pub fn mock_value_slot1() -> NamedStorageSlot { - NamedStorageSlot::with_value(MOCK_VALUE_SLOT1.clone(), STORAGE_VALUE_1) + pub fn mock_value_slot1() -> StorageSlot { + StorageSlot::with_value(MOCK_VALUE_SLOT1.clone(), STORAGE_VALUE_1) } - pub fn mock_map_slot() -> NamedStorageSlot { - NamedStorageSlot::with_map(MOCK_MAP_SLOT.clone(), Self::mock_map()) + pub fn mock_map_slot() -> StorageSlot { + StorageSlot::with_map(MOCK_MAP_SLOT.clone(), Self::mock_map()) } pub fn mock_map() -> StorageMap { diff --git a/crates/miden-testing/src/kernel_tests/block/header_errors.rs b/crates/miden-testing/src/kernel_tests/block/header_errors.rs index 6e8b67d4d1..21c4d9f66c 100644 --- a/crates/miden-testing/src/kernel_tests/block/header_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/header_errors.rs @@ -11,7 +11,7 @@ use miden_objects::account::{ AccountBuilder, AccountComponent, AccountId, - NamedStorageSlot, + StorageSlot, StorageSlotName, }; use miden_objects::asset::FungibleAsset; @@ -259,7 +259,7 @@ async fn block_building_fails_on_creating_account_with_existing_account_id_prefi let account = AccountBuilder::new([5; 32]) .with_auth_component(auth_component.clone()) - .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( + .with_component(MockAccountComponent::with_slots(vec![StorageSlot::with_value( StorageSlotName::new("miden::test_slot")?, Word::from([5u32; 4]), )])) @@ -351,7 +351,7 @@ async fn block_building_fails_on_creating_account_with_duplicate_account_id_pref let mock_chain = MockChain::new(); let account = AccountBuilder::new([5; 32]) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( + .with_component(MockAccountComponent::with_slots(vec![StorageSlot::with_value( StorageSlotName::new("miden::test_slot")?, Word::from([5u32; 4]), )])) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index fc95794298..8b922e8639 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -25,9 +25,9 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - NamedStorageSlot, StorageMap, StorageSlot, + StorageSlotContent, StorageSlotName, StorageSlotType, }; @@ -399,7 +399,7 @@ async fn test_get_item() -> miette::Result<()> { end "#, slot_name = storage_item.name(), - item_value = &storage_item.storage_slot().value(), + item_value = &storage_item.content().value(), ); tx_context.execute_code(&code).await.unwrap(); @@ -410,16 +410,16 @@ async fn test_get_item() -> miette::Result<()> { #[tokio::test] async fn test_get_map_item() -> miette::Result<()> { - let named_slot = AccountStorage::mock_map_slot(); + let slot = AccountStorage::mock_map_slot(); let account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![named_slot.clone()])) + .with_component(MockAccountComponent::with_slots(vec![slot.clone()])) .build_existing() .unwrap(); let tx_context = TransactionContextBuilder::new(account).build().unwrap(); - let StorageSlot::Map(map) = named_slot.storage_slot() else { + let StorageSlotContent::Map(map) = slot.content() else { panic!("expected map") }; @@ -446,7 +446,7 @@ async fn test_get_map_item() -> miette::Result<()> { exec.::std::sys::truncate_stack end "#, - slot_name = named_slot.name(), + slot_name = slot.name(), ); tx_context.execute_code(&code).await?; @@ -574,10 +574,10 @@ async fn test_set_map_item() -> miette::Result<()> { let (new_key, new_value) = (Word::from([109, 110, 111, 112u32]), Word::from([9, 10, 11, 12u32])); - let named_slot = AccountStorage::mock_map_slot(); + let slot = AccountStorage::mock_map_slot(); let account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![named_slot.clone()])) + .with_component(MockAccountComponent::with_slots(vec![slot.clone()])) .build_existing() .unwrap(); @@ -619,7 +619,7 @@ async fn test_set_map_item() -> miette::Result<()> { exec.sys::truncate_stack end "#, - slot_name = named_slot.name(), + slot_name = slot.name(), new_key = &new_key, new_value = &new_value, ); @@ -635,7 +635,7 @@ async fn test_set_map_item() -> miette::Result<()> { "get_item should return the updated root", ); assert_eq!( - named_slot.storage_slot().value(), + slot.content().value(), exec_output.get_stack_word_be(4), "get_item must return the new updated value", ); @@ -780,16 +780,14 @@ async fn prove_account_creation_with_non_empty_storage() -> anyhow::Result<()> { let slot_name1 = StorageSlotName::mock(1); let slot_name2 = StorageSlotName::mock(2); - let slot0 = NamedStorageSlot::with_value(slot_name0.clone(), Word::from([1, 2, 3, 4u32])); - let slot1 = NamedStorageSlot::with_value(slot_name1.clone(), Word::from([10, 20, 30, 40u32])); + let slot0 = StorageSlot::with_value(slot_name0.clone(), Word::from([1, 2, 3, 4u32])); + let slot1 = StorageSlot::with_value(slot_name1.clone(), Word::from([10, 20, 30, 40u32])); let mut map_entries = Vec::new(); for _ in 0..10 { map_entries.push((rand_value::(), rand_value::())); } - let map_slot = NamedStorageSlot::with_map( - slot_name2.clone(), - StorageMap::with_entries(map_entries.clone())?, - ); + let map_slot = + StorageSlot::with_map(slot_name2.clone(), StorageMap::with_entries(map_entries.clone())?); let account = AccountBuilder::new([6; 32]) .storage_mode(AccountStorageMode::Public) @@ -1505,7 +1503,7 @@ async fn test_get_initial_item() -> miette::Result<()> { end "#, mock_value_slot0 = &*MOCK_VALUE_SLOT0, - expected_initial_value = &AccountStorage::mock_value_slot0().storage_slot().value(), + expected_initial_value = &AccountStorage::mock_value_slot0().content().value(), ); tx_context.execute_code(&code).await?; @@ -1525,7 +1523,7 @@ async fn test_get_initial_map_item() -> miette::Result<()> { let tx_context = TransactionContextBuilder::new(account).build().unwrap(); // Use the first key-value pair from the mock storage - let StorageSlot::Map(map) = map_slot.storage_slot() else { + let StorageSlotContent::Map(map) = map_slot.content() else { panic!("expected map"); }; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index cc978a5cdc..683807536d 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -14,8 +14,8 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - NamedStorageSlot, StorageMap, + StorageSlot, StorageSlotName, }; use miden_objects::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; @@ -127,10 +127,10 @@ async fn storage_delta_for_value_slots() -> anyhow::Result<()> { let TestSetup { mock_chain, account_id, .. } = setup_test( vec![ - NamedStorageSlot::with_value(slot_0_name.clone(), slot_0_init_value), - NamedStorageSlot::with_value(slot_1_name.clone(), slot_1_init_value), - NamedStorageSlot::with_value(slot_2_name.clone(), slot_2_init_value), - NamedStorageSlot::with_value(slot_3_name.clone(), slot_3_init_value), + StorageSlot::with_value(slot_0_name.clone(), slot_0_init_value), + StorageSlot::with_value(slot_1_name.clone(), slot_1_init_value), + StorageSlot::with_value(slot_2_name.clone(), slot_2_init_value), + StorageSlot::with_value(slot_3_name.clone(), slot_3_init_value), ], [], [], @@ -264,13 +264,13 @@ async fn storage_delta_for_map_slots() -> anyhow::Result<()> { let TestSetup { mock_chain, account_id, .. } = setup_test( vec![ - NamedStorageSlot::with_map(slot_0_name.clone(), map0), - NamedStorageSlot::with_map(slot_1_name.clone(), map1), - NamedStorageSlot::with_map(slot_2_name.clone(), map2), + StorageSlot::with_map(slot_0_name.clone(), map0), + StorageSlot::with_map(slot_1_name.clone(), map1), + StorageSlot::with_map(slot_2_name.clone(), map2), // Include an empty map which does not receive any updates, to test that the "metadata // header" in the delta commitment is not appended if there are no updates to a map // slot. - NamedStorageSlot::with_map(StorageSlotName::mock(3), StorageMap::new()), + StorageSlot::with_map(StorageSlotName::mock(3), StorageMap::new()), ], [], [], @@ -828,10 +828,10 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: .with_auth_component(Auth::IncrNonce) .with_component(MockAccountComponent::with_slots(vec![ AccountStorage::mock_value_slot0(), - NamedStorageSlot::with_map(map0_slot_name.clone(), map0.clone()), - NamedStorageSlot::with_map(map1_slot_name.clone(), map1.clone()), + StorageSlot::with_map(map0_slot_name.clone(), map0.clone()), + StorageSlot::with_map(map1_slot_name.clone(), map1.clone()), AccountStorage::mock_value_slot1(), - NamedStorageSlot::with_map(map2_slot_name.clone(), map2.clone()), + StorageSlot::with_map(map2_slot_name.clone(), map2.clone()), ])) .build()?; @@ -927,8 +927,8 @@ async fn delta_for_new_account_retains_empty_value_storage_slots() -> anyhow::Re .account_type(AccountType::RegularAccountUpdatableCode) .storage_mode(AccountStorageMode::Network) .with_component(MockAccountComponent::with_slots(vec![ - NamedStorageSlot::with_empty_value(slot_name0.clone()), - NamedStorageSlot::with_value(slot_name1.clone(), slot_value2), + StorageSlot::with_empty_value(slot_name0.clone()), + StorageSlot::with_value(slot_name1.clone(), slot_value2), ])) .with_auth_component(Auth::IncrNonce) .build()?; @@ -963,7 +963,7 @@ async fn delta_for_new_account_retains_empty_map_storage_slots() -> anyhow::Resu let mut account = AccountBuilder::new(rand::random()) .account_type(AccountType::RegularAccountUpdatableCode) .storage_mode(AccountStorageMode::Network) - .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_empty_map( + .with_component(MockAccountComponent::with_slots(vec![StorageSlot::with_empty_map( slot_name0.clone(), )])) .with_auth_component(Auth::IncrNonce) @@ -1024,7 +1024,7 @@ struct TestSetup { } fn setup_test( - storage_slots: impl IntoIterator, + storage_slots: impl IntoIterator, vault_assets: impl IntoIterator, note_assets: impl IntoIterator, ) -> anyhow::Result { diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs index fbf85a27c3..53788fdc2c 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs @@ -1,6 +1,6 @@ use anyhow::Context; use assert_matches::assert_matches; -use miden_objects::account::{AccountId, NamedStorageSlot, StorageMap, StorageSlotName}; +use miden_objects::account::{AccountId, StorageMap, StorageSlot, StorageSlotName}; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; use miden_objects::note::NoteType; use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; @@ -132,8 +132,8 @@ async fn mutate_account_with_storage() -> anyhow::Result { let account = builder.add_existing_mock_account_with_storage_and_assets( Auth::IncrNonce, [ - NamedStorageSlot::with_value(StorageSlotName::mock(0), rand_value()), - NamedStorageSlot::with_map( + StorageSlot::with_value(StorageSlotName::mock(0), rand_value()), + StorageSlot::with_map( StorageSlotName::mock(1), StorageMap::with_entries([(rand_value(), rand_value())])?, ), @@ -164,11 +164,11 @@ async fn create_output_notes() -> anyhow::Result { let account = builder.add_existing_mock_account_with_storage_and_assets( Auth::IncrNonce, [ - NamedStorageSlot::with_map( + StorageSlot::with_map( StorageSlotName::mock(0), StorageMap::with_entries([(rand_value(), rand_value())])?, ), - NamedStorageSlot::with_value(StorageSlotName::mock(1), rand_value()), + StorageSlot::with_value(StorageSlotName::mock(1), rand_value()), ], [Asset::from(native_asset), NonFungibleAsset::mock(&[1, 2, 3, 4])], )?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 54eadc8813..6616ec06da 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -166,7 +166,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { assert_eq!( exec_output.get_stack_word_be(0), - mock_value_slot0.storage_slot().value(), + mock_value_slot0.content().value(), "Value at the top of the stack should be equal to [1, 2, 3, 4]", ); @@ -1830,7 +1830,7 @@ fn foreign_account_data_memory_assertions( for (i, elements) in foreign_account .storage() .to_elements() - .chunks(StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT / 2) + .chunks(StorageSlot::NUM_ELEMENTS / 2) .enumerate() { assert_eq!( @@ -1951,7 +1951,7 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - mock_map_slot = mock_map_slot.name(), foreign_account_id_prefix = foreign_account.id().prefix().as_felt(), foreign_account_id_suffix = foreign_account.id().suffix(), - expected_value_slot_0 = mock_value_slot0.storage_slot().value(), + expected_value_slot_0 = mock_value_slot0.content().value(), map_key = &map_key, map_value = &map_value, ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 0637276dee..4fac3a2517 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -70,7 +70,6 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - NamedStorageSlot, StorageMap, StorageSlot, StorageSlotName, @@ -414,7 +413,7 @@ fn account_data_memory_assertions(exec_output: &ExecutionOutput, inputs: &Transa .account() .storage() .to_elements() - .chunks(StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT / 2) + .chunks(StorageSlot::NUM_ELEMENTS / 2) .enumerate() { assert_eq!( @@ -589,7 +588,7 @@ pub async fn create_multiple_accounts_test(storage_mode: AccountStorageMode) -> .account_type(account_type) .storage_mode(storage_mode) .with_auth_component(Auth::IncrNonce) - .with_component(MockAccountComponent::with_slots(vec![NamedStorageSlot::with_value( + .with_component(MockAccountComponent::with_slots(vec![StorageSlot::with_value( StorageSlotName::mock(0), Word::from([255u32; WORD_SIZE]), )])) @@ -692,7 +691,7 @@ pub async fn create_account_non_fungible_faucet_invalid_initial_reserved_slot() let asset = NonFungibleAsset::mock(&[1, 2, 3, 4]); let non_fungible_storage_map = StorageMap::with_entries([(asset.vault_key().into(), asset.into())]).unwrap(); - let storage = AccountStorage::new(vec![NamedStorageSlot::with_map( + let storage = AccountStorage::new(vec![StorageSlot::with_map( AccountStorage::faucet_metadata_slot().clone(), non_fungible_storage_map, )]) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index ba6593ecfb..b79eda68bd 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -18,7 +18,7 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - NamedStorageSlot, + StorageSlot, StorageSlotName, }; use miden_objects::assembly::DefaultSourceManager; @@ -771,7 +771,7 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { let component = AccountComponent::compile( account_code_script, TransactionKernel::assembler(), - vec![NamedStorageSlot::with_value(StorageSlotName::mock(0), Word::default())], + vec![StorageSlot::with_value(StorageSlotName::mock(0), Word::default())], )? .with_supports_all_types(); diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 58fc67fa16..8938ccfd79 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -27,7 +27,7 @@ use miden_objects::account::{ AccountStorage, AccountStorageMode, AccountType, - NamedStorageSlot, + StorageSlot, }; use miden_objects::asset::{Asset, FungibleAsset, TokenSymbol}; use miden_objects::block::account_tree::AccountTree; @@ -420,7 +420,7 @@ impl MockChainBuilder { pub fn add_existing_mock_account_with_storage( &mut self, auth_method: Auth, - slots: impl IntoIterator, + slots: impl IntoIterator, ) -> anyhow::Result { self.add_existing_mock_account_with_storage_and_assets(auth_method, slots, []) } @@ -440,7 +440,7 @@ impl MockChainBuilder { pub fn add_existing_mock_account_with_storage_and_assets( &mut self, auth_method: Auth, - slots: impl IntoIterator, + slots: impl IntoIterator, assets: impl IntoIterator, ) -> anyhow::Result { let account_builder = Account::builder(self.rng.random()) diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index bea15c6a2f..9efdc4b408 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -4,7 +4,13 @@ use alloc::sync::Arc; use alloc::vec::Vec; use miden_lib::transaction::TransactionKernel; -use miden_objects::account::{Account, AccountId, PartialAccount, StorageMapWitness, StorageSlot}; +use miden_objects::account::{ + Account, + AccountId, + PartialAccount, + StorageMapWitness, + StorageSlotContent, +}; use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; use miden_objects::assembly::{SourceManager, SourceManagerSync}; use miden_objects::asset::{Asset, AssetVaultKey, AssetWitness}; @@ -328,8 +334,8 @@ impl DataStore for TransactionContext { .storage() .slots() .iter() - .find_map(|named_slot| match named_slot.storage_slot() { - StorageSlot::Map(storage_map) if storage_map.root() == map_root => { + .find_map(|slot| match slot.content() { + StorageSlotContent::Map(storage_map) if storage_map.root() == map_root => { Some(storage_map) }, _ => None, @@ -360,8 +366,8 @@ impl DataStore for TransactionContext { .storage() .slots() .iter() - .find_map(|named_slot| match named_slot.storage_slot() { - StorageSlot::Map(storage_map) if storage_map.root() == map_root => {Some(storage_map)}, + .find_map(|slot| match slot.content() { + StorageSlotContent::Map(storage_map) if storage_map.root() == map_root => {Some(storage_map)}, _ => None, }) .ok_or_else(|| { From e682ee095c9baaf51952adc53019aa270208f7ce Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 10 Dec 2025 13:33:24 +0700 Subject: [PATCH 032/114] feat: cache `StorageSlotId` in `StorageSlotName` (#2153) * chore: Rename `SlotNameError` -> `StorageSlotNameError` * feat: Remove `const fn from_static_str` in slot name * feat: Add convenience impls for `StorageSlotName` * feat: Inline slot ID into slot name * feat: Remove cached slot ID from storage slot * chore: Replace binary search with find * chore: add changelog * chore: roll back `validate` changes * chore: Rename `StorageSlotId::compute` to `from_str` * fix: invalid changelog pr number --- CHANGELOG.md | 2 +- .../src/account/delta/storage.rs | 2 +- crates/miden-objects/src/account/mod.rs | 2 +- .../src/account/storage/header.rs | 23 ++- .../miden-objects/src/account/storage/mod.rs | 18 +- .../src/account/storage/partial.rs | 2 +- .../src/account/storage/slot/slot_id.rs | 20 ++ .../src/account/storage/slot/slot_name.rs | 172 ++++++++---------- .../src/account/storage/slot/storage_slot.rs | 20 +- crates/miden-objects/src/errors.rs | 4 +- crates/miden-objects/src/lib.rs | 2 +- .../src/host/storage_delta_tracker.rs | 2 +- 12 files changed, 121 insertions(+), 148 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47525b15bb..ad4cf31956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Features - [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987)). -- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150)). +- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153)). ## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). diff --git a/crates/miden-objects/src/account/delta/storage.rs b/crates/miden-objects/src/account/delta/storage.rs index f43a244a12..8aa2a05a70 100644 --- a/crates/miden-objects/src/account/delta/storage.rs +++ b/crates/miden-objects/src/account/delta/storage.rs @@ -172,7 +172,7 @@ impl AccountStorageDelta { debug_assert_eq!(sorted_slots.len(), self.values.len() + self.maps.len()); for (slot_name, slot_delta) in sorted_slots { - let slot_id = slot_name.compute_id(); + let slot_id = slot_name.id(); match slot_delta { StorageSlotDelta::Value(new_value) => { diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index be3ce48801..9ee261d829 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -443,7 +443,7 @@ impl TryFrom for AccountDelta { let mut map_slots = BTreeMap::new(); for slot in storage.into_slots() { - let (slot_name, _, slot_content) = slot.into_parts(); + let (slot_name, slot_content) = slot.into_parts(); match slot_content { StorageSlotContent::Value(word) => { value_slots.insert(slot_name, word); diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-objects/src/account/storage/header.rs index 2ff784a65d..8dd504a485 100644 --- a/crates/miden-objects/src/account/storage/header.rs +++ b/crates/miden-objects/src/account/storage/header.rs @@ -81,7 +81,7 @@ impl AccountStorageHeader { return Err(AccountError::StorageTooManySlots(slots.len() as u64)); } - if !slots.is_sorted_by_key(|(slot_name, ..)| slot_name.compute_id()) { + if !slots.is_sorted_by_key(|(slot_name, ..)| slot_name.id()) { return Err(AccountError::UnsortedStorageSlots); } @@ -117,7 +117,7 @@ impl AccountStorageHeader { &self, slot_name: &StorageSlotName, ) -> Option<(&StorageSlotType, &Word)> { - self.find_slot_header_by_id(slot_name.compute_id()) + self.find_slot_header_by_id(slot_name.id()) .map(|(_slot_name, slot_type, slot_value)| (slot_type, slot_value)) } @@ -128,13 +128,13 @@ impl AccountStorageHeader { &self, slot_id: StorageSlotId, ) -> Option<(&StorageSlotName, &StorageSlotType, &Word)> { - self.slots - .binary_search_by_key(&slot_id, |(name, ..)| name.compute_id()) - .map(|slot_idx| { - let (name, r#type, value) = &self.slots[slot_idx]; - (name, r#type, value) - }) - .ok() + self.slots.iter().find_map(|(slot_name, slot_type, slot_value)| { + if slot_name.id() == slot_id { + Some((slot_name, slot_type, slot_value)) + } else { + None + } + }) } /// Indicates whether the slot with the given `name` is a map slot. @@ -188,8 +188,7 @@ impl SequentialCommit for AccountStorageHeader { fn to_elements(&self) -> Vec { self.slots() .flat_map(|(slot_name, slot_type, slot_value)| { - StorageSlotHeader::new(slot_name.compute_id(), *slot_type, *slot_value) - .to_elements() + StorageSlotHeader::new(slot_name.id(), *slot_type, *slot_value).to_elements() }) .collect() } @@ -241,7 +240,7 @@ mod tests { ), (MOCK_MAP_SLOT.clone(), StorageSlotType::Map, storage_map.root()), ]; - slots.sort_unstable_by_key(|(slot_name, ..)| slot_name.compute_id()); + slots.sort_unstable_by_key(|(slot_name, ..)| slot_name.id()); let expected_header = AccountStorageHeader { slots }; let account_storage = AccountStorage::mock(); diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index d4154c393c..7d66c46f21 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -196,21 +196,13 @@ impl AccountStorage { /// Returns a reference to the storage slot with the provided name, if it exists, `None` /// otherwise. pub fn get(&self, slot_name: &StorageSlotName) -> Option<&StorageSlot> { - debug_assert!(self.slots.is_sorted()); - - let slot_id = slot_name.compute_id(); - self.slots - .binary_search_by_key(&slot_id, |slot| slot.slot_id()) - .map(|idx| &self.slots[idx]) - .ok() + self.slots.iter().find(|slot| slot.name().id() == slot_name.id()) } + /// Returns a mutable reference to the storage slot with the provided name, if it exists, `None` + /// otherwise. fn get_mut(&mut self, slot_name: &StorageSlotName) -> Option<&mut StorageSlot> { - let slot_id = slot_name.compute_id(); - self.slots - .binary_search_by_key(&slot_id, |slot| slot.slot_id()) - .map(|idx| &mut self.slots[idx]) - .ok() + self.slots.iter_mut().find(|slot| slot.name().id() == slot_name.id()) } /// Returns an item from the storage slot with the given name. @@ -362,7 +354,7 @@ impl SequentialCommit for AccountStorage { .iter() .flat_map(|slot| { StorageSlotHeader::new( - slot.slot_id(), + slot.id(), slot.content().slot_type(), slot.content().value(), ) diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-objects/src/account/storage/partial.rs index acc292e419..c44d752704 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-objects/src/account/storage/partial.rs @@ -61,7 +61,7 @@ impl PartialStorage { let mut maps = BTreeMap::new(); for slot in account_storage { - if let StorageSlotContent::Map(storage_map) = slot.into_parts().2 { + if let StorageSlotContent::Map(storage_map) = slot.into_parts().1 { let partial_map = PartialStorageMap::new_full(storage_map); maps.insert(partial_map.root(), partial_map); } diff --git a/crates/miden-objects/src/account/storage/slot/slot_id.rs b/crates/miden-objects/src/account/storage/slot/slot_id.rs index f3aef26eab..1fcaf52e4e 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_id.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_id.rs @@ -1,5 +1,8 @@ use core::cmp::Ordering; use core::fmt::Display; +use core::hash::Hash; + +use miden_core::utils::hash_string_to_word; use crate::Felt; @@ -25,6 +28,16 @@ impl StorageSlotId { Self { suffix, prefix } } + /// Computes the [`StorageSlotId`] from a slot name. + /// + /// The provided `name`'s validity is **not** checked. + pub(super) fn from_str(name: &str) -> StorageSlotId { + let hashed_word = hash_string_to_word(name); + let suffix = hashed_word[0]; + let prefix = hashed_word[1]; + StorageSlotId::new(suffix, prefix) + } + // ACCESSORS // -------------------------------------------------------------------------------------------- @@ -62,6 +75,13 @@ impl PartialOrd for StorageSlotId { } } +impl Hash for StorageSlotId { + fn hash(&self, state: &mut H) { + self.suffix.inner().hash(state); + self.prefix.inner().hash(state); + } +} + impl Display for StorageSlotId { /// Returns a big-endian, hex-encoded string of length 34, including the `0x` prefix. /// diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-objects/src/account/storage/slot/slot_name.rs index e1eb351791..6b7d41c4ab 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_name.rs @@ -1,11 +1,9 @@ -use alloc::borrow::Cow; use alloc::string::{String, ToString}; use core::fmt::Display; - -use miden_core::utils::hash_string_to_word; +use core::str::FromStr; use crate::account::storage::slot::StorageSlotId; -use crate::errors::SlotNameError; +use crate::errors::StorageSlotNameError; use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Serializable}; /// The name of an account storage slot. @@ -37,7 +35,8 @@ use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Seri /// - Each component must not start with an underscore. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StorageSlotName { - name: Cow<'static, str>, + name: String, + id: StorageSlotId, } impl StorageSlotName { @@ -53,40 +52,17 @@ impl StorageSlotName { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Constructs a new [`StorageSlotName`] from a static string. - /// - /// This function is `const` and can be used to define slot names as constants, e.g.: - /// - /// ```rust - /// # use miden_objects::account::StorageSlotName; - /// const SLOT_NAME: StorageSlotName = - /// StorageSlotName::from_static_str("miden::basic_fungible_faucet::metadata"); - /// ``` - /// - /// This is convenient because using a string that is not a valid slot name fails to compile. - /// - /// # Panics - /// - /// Panics if: - /// - the slot name is invalid (see the type-level docs for the requirements). - pub const fn from_static_str(name: &'static str) -> Self { - match Self::validate(name) { - Ok(()) => Self { name: Cow::Borrowed(name) }, - // We cannot format the error in a const context. - Err(_) => panic!("invalid slot name"), - } - } - /// Constructs a new [`StorageSlotName`] from a string. /// /// # Errors /// /// Returns an error if: /// - the slot name is invalid (see the type-level docs for the requirements). - pub fn new(name: impl Into) -> Result { + pub fn new(name: impl Into) -> Result { let name = name.into(); Self::validate(&name)?; - Ok(Self { name: Cow::Owned(name) }) + let id = StorageSlotId::from_str(name.as_str()); + Ok(Self { name, id }) } // ACCESSORS @@ -107,12 +83,9 @@ impl StorageSlotName { self.name.len() as u8 } - // TODO(named_slots): Docs. - pub fn compute_id(&self) -> StorageSlotId { - let hashed_word = hash_string_to_word(self.as_str()); - let suffix = hashed_word[0]; - let prefix = hashed_word[1]; - StorageSlotId::new(suffix, prefix) + /// Returns the [`StorageSlotId`] derived from the slot name. + pub fn id(&self) -> StorageSlotId { + self.id } // HELPERS @@ -133,25 +106,25 @@ impl StorageSlotName { /// > are encoded in one octet having the normal US-ASCII value, and any octet with such a value /// > can only stand for a US-ASCII character, and nothing else. /// > https://www.rfc-editor.org/rfc/rfc3629 - const fn validate(name: &str) -> Result<(), SlotNameError> { + const fn validate(name: &str) -> Result<(), StorageSlotNameError> { let bytes = name.as_bytes(); let mut idx = 0; let mut num_components = 0; if bytes.is_empty() { - return Err(SlotNameError::TooShort); + return Err(StorageSlotNameError::TooShort); } if bytes.len() > Self::MAX_LENGTH { - return Err(SlotNameError::TooLong); + return Err(StorageSlotNameError::TooLong); } // Slot names must not start with a colon or underscore. // SAFETY: We just checked that we're not dealing with an empty slice. if bytes[0] == b':' { - return Err(SlotNameError::UnexpectedColon); + return Err(StorageSlotNameError::UnexpectedColon); } else if bytes[0] == b'_' { - return Err(SlotNameError::UnexpectedUnderscore); + return Err(StorageSlotNameError::UnexpectedUnderscore); } while idx < bytes.len() { @@ -164,22 +137,22 @@ impl StorageSlotName { // expect a double colon. if (idx + 1) < bytes.len() { if bytes[idx + 1] != b':' { - return Err(SlotNameError::UnexpectedColon); + return Err(StorageSlotNameError::UnexpectedColon); } } else { - return Err(SlotNameError::UnexpectedColon); + return Err(StorageSlotNameError::UnexpectedColon); } // A component cannot end with a colon, so this allows us to validate the start of a // component: It must not start with a colon or an underscore. if (idx + 2) < bytes.len() { if bytes[idx + 2] == b':' { - return Err(SlotNameError::UnexpectedColon); + return Err(StorageSlotNameError::UnexpectedColon); } else if bytes[idx + 2] == b'_' { - return Err(SlotNameError::UnexpectedUnderscore); + return Err(StorageSlotNameError::UnexpectedUnderscore); } } else { - return Err(SlotNameError::UnexpectedColon); + return Err(StorageSlotNameError::UnexpectedColon); } // Advance past the double colon. @@ -190,7 +163,7 @@ impl StorageSlotName { } else if Self::is_valid_char(byte) { idx += 1; } else { - return Err(SlotNameError::InvalidCharacter); + return Err(StorageSlotNameError::InvalidCharacter); } } @@ -198,7 +171,7 @@ impl StorageSlotName { num_components += 1; if num_components < Self::MIN_NUM_COMPONENTS { - return Err(SlotNameError::TooShort); + return Err(StorageSlotNameError::TooShort); } Ok(()) @@ -212,8 +185,7 @@ impl StorageSlotName { impl Ord for StorageSlotName { fn cmp(&self, other: &Self) -> core::cmp::Ordering { - // TODO(named_slots): Cache ID in SlotName for efficiency. - self.compute_id().cmp(&other.compute_id()) + self.id().cmp(&other.id()) } } @@ -229,6 +201,20 @@ impl Display for StorageSlotName { } } +impl FromStr for StorageSlotName { + type Err = StorageSlotNameError; + + fn from_str(string: &str) -> Result { + StorageSlotName::new(string) + } +} + +impl From for String { + fn from(slot_name: StorageSlotName) -> Self { + slot_name.name + } +} + impl Serializable for StorageSlotName { fn write_into(&self, target: &mut W) { target.write_u8(self.len()); @@ -269,75 +255,59 @@ mod tests { // A string containing all allowed characters of a slot name. const FULL_ALPHABET: &str = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"; - // Const function tests - // -------------------------------------------------------------------------------------------- - - const _NAME0: StorageSlotName = StorageSlotName::from_static_str("name::component"); - const _NAME1: StorageSlotName = StorageSlotName::from_static_str("one::two::three::four::five"); - const _NAME2: StorageSlotName = StorageSlotName::from_static_str("one::two_three::four"); - - #[test] - #[should_panic(expected = "invalid slot name")] - fn slot_name_panics_on_invalid_character() { - StorageSlotName::from_static_str("miden!::component"); - } - - #[test] - #[should_panic(expected = "invalid slot name")] - fn slot_name_panics_on_invalid_character2() { - StorageSlotName::from_static_str("miden_ö::component"); - } - - #[test] - #[should_panic(expected = "invalid slot name")] - fn slot_name_panics_when_too_short() { - StorageSlotName::from_static_str("one"); - } - - #[test] - #[should_panic(expected = "invalid slot name")] - fn slot_name_panics_on_component_starting_with_underscores() { - StorageSlotName::from_static_str("one::_two"); - } - // Invalid colon or underscore tests // -------------------------------------------------------------------------------------------- #[test] fn slot_name_fails_on_invalid_colon_placement() { // Single colon. - assert_matches!(StorageSlotName::new(":").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(StorageSlotName::new("0::1:").unwrap_err(), SlotNameError::UnexpectedColon); - assert_matches!(StorageSlotName::new(":0::1").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!( + StorageSlotName::new(":").unwrap_err(), + StorageSlotNameError::UnexpectedColon + ); + assert_matches!( + StorageSlotName::new("0::1:").unwrap_err(), + StorageSlotNameError::UnexpectedColon + ); + assert_matches!( + StorageSlotName::new(":0::1").unwrap_err(), + StorageSlotNameError::UnexpectedColon + ); assert_matches!( StorageSlotName::new("0::1:2").unwrap_err(), - SlotNameError::UnexpectedColon + StorageSlotNameError::UnexpectedColon ); // Double colon (placed invalidly). - assert_matches!(StorageSlotName::new("::").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!( + StorageSlotName::new("::").unwrap_err(), + StorageSlotNameError::UnexpectedColon + ); assert_matches!( StorageSlotName::new("1::2::").unwrap_err(), - SlotNameError::UnexpectedColon + StorageSlotNameError::UnexpectedColon ); assert_matches!( StorageSlotName::new("::1::2").unwrap_err(), - SlotNameError::UnexpectedColon + StorageSlotNameError::UnexpectedColon ); // Triple colon. - assert_matches!(StorageSlotName::new(":::").unwrap_err(), SlotNameError::UnexpectedColon); + assert_matches!( + StorageSlotName::new(":::").unwrap_err(), + StorageSlotNameError::UnexpectedColon + ); assert_matches!( StorageSlotName::new("1::2:::").unwrap_err(), - SlotNameError::UnexpectedColon + StorageSlotNameError::UnexpectedColon ); assert_matches!( StorageSlotName::new(":::1::2").unwrap_err(), - SlotNameError::UnexpectedColon + StorageSlotNameError::UnexpectedColon ); assert_matches!( StorageSlotName::new("1::2:::3").unwrap_err(), - SlotNameError::UnexpectedColon + StorageSlotNameError::UnexpectedColon ); } @@ -345,11 +315,11 @@ mod tests { fn slot_name_fails_on_invalid_underscore_placement() { assert_matches!( StorageSlotName::new("_one::two").unwrap_err(), - SlotNameError::UnexpectedUnderscore + StorageSlotNameError::UnexpectedUnderscore ); assert_matches!( StorageSlotName::new("one::_two").unwrap_err(), - SlotNameError::UnexpectedUnderscore + StorageSlotNameError::UnexpectedUnderscore ); } @@ -358,14 +328,14 @@ mod tests { #[test] fn slot_name_fails_on_empty_string() { - assert_matches!(StorageSlotName::new("").unwrap_err(), SlotNameError::TooShort); + assert_matches!(StorageSlotName::new("").unwrap_err(), StorageSlotNameError::TooShort); } #[test] fn slot_name_fails_on_single_component() { assert_matches!( StorageSlotName::new("single_component").unwrap_err(), - SlotNameError::TooShort + StorageSlotNameError::TooShort ); } @@ -373,7 +343,7 @@ mod tests { fn slot_name_fails_on_string_whose_length_exceeds_max_length() { let mut string = get_max_length_slot_name(); string.push('a'); - assert_matches!(StorageSlotName::new(string).unwrap_err(), SlotNameError::TooLong); + assert_matches!(StorageSlotName::new(string).unwrap_err(), StorageSlotNameError::TooLong); } // Alphabet validation tests @@ -392,15 +362,15 @@ mod tests { fn slot_name_fails_on_invalid_character() { assert_matches!( StorageSlotName::new("na#me::second").unwrap_err(), - SlotNameError::InvalidCharacter + StorageSlotNameError::InvalidCharacter ); assert_matches!( StorageSlotName::new("first_entry::secönd").unwrap_err(), - SlotNameError::InvalidCharacter + StorageSlotNameError::InvalidCharacter ); assert_matches!( StorageSlotName::new("first::sec::th!rd").unwrap_err(), - SlotNameError::InvalidCharacter + StorageSlotNameError::InvalidCharacter ); } diff --git a/crates/miden-objects/src/account/storage/slot/storage_slot.rs b/crates/miden-objects/src/account/storage/slot/storage_slot.rs index ef63ca079c..9fce734847 100644 --- a/crates/miden-objects/src/account/storage/slot/storage_slot.rs +++ b/crates/miden-objects/src/account/storage/slot/storage_slot.rs @@ -10,12 +10,6 @@ use crate::account::{StorageMap, StorageSlotContent, StorageSlotName, StorageSlo pub struct StorageSlot { /// The name of the storage slot. name: StorageSlotName, - /// The cached [`StorageSlotId`] of the slot name. This field must always be consistent with - /// the slot name. - /// - /// This is cached so that the `Ord` implementation can use the computed slot ID instead of - /// having to hash the slot name on every comparison operation. - slot_id: StorageSlotId, /// The content of the storage slot. content: StorageSlotContent, } @@ -33,9 +27,7 @@ impl StorageSlot { /// Creates a new [`StorageSlot`] with the given [`StorageSlotName`] and /// [`StorageSlotContent`]. pub fn new(name: StorageSlotName, content: StorageSlotContent) -> Self { - let slot_id = name.compute_id(); - - Self { name, slot_id, content } + Self { name, content } } /// Creates a new [`StorageSlot`] with the given [`StorageSlotName`] and the `value` @@ -71,8 +63,8 @@ impl StorageSlot { } /// Returns the [`StorageSlotId`] by which the [`StorageSlot`] is identified. - pub fn slot_id(&self) -> StorageSlotId { - self.slot_id + pub fn id(&self) -> StorageSlotId { + self.name.id() } /// Returns this storage slot value as a [Word] @@ -104,14 +96,14 @@ impl StorageSlot { } /// Consumes self and returns the underlying parts. - pub fn into_parts(self) -> (StorageSlotName, StorageSlotId, StorageSlotContent) { - (self.name, self.slot_id, self.content) + pub fn into_parts(self) -> (StorageSlotName, StorageSlotContent) { + (self.name, self.content) } } impl Ord for StorageSlot { fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.slot_id.cmp(&other.slot_id) + self.name().cmp(&other.name) } } diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 7b55a92a5e..1ee4b78080 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -249,8 +249,8 @@ pub enum AccountIdError { // ================================================================================================ #[derive(Debug, Error)] -pub enum SlotNameError { - #[error("slot names must only contain characters a..z, A..Z, 0..9 or underscore")] +pub enum StorageSlotNameError { + #[error("slot name must only contain characters a..z, A..Z, 0..9, double colon or underscore")] InvalidCharacter, #[error("slot names must be separated by double colons")] UnexpectedColon, diff --git a/crates/miden-objects/src/lib.rs b/crates/miden-objects/src/lib.rs index 9da7702fd4..d1ed2d7faf 100644 --- a/crates/miden-objects/src/lib.rs +++ b/crates/miden-objects/src/lib.rs @@ -44,8 +44,8 @@ pub use errors::{ ProposedBlockError, ProvenBatchError, ProvenTransactionError, - SlotNameError, StorageMapError, + StorageSlotNameError, TokenSymbolError, TransactionInputError, TransactionOutputError, diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index 105702d3d7..0067bd0bff 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -202,7 +202,7 @@ fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorage }) .collect(); - slots.sort_by_key(|(slot_name, ..)| slot_name.compute_id()); + slots.sort_by_key(|(slot_name, ..)| slot_name.id()); // SAFETY: We have sorted the slots and the max number of slots should not be exceeded as // enforced by the storage header in partial storage. From c05a30fa43a64a27c54d713eee7cb363665f0880 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:30:48 -0300 Subject: [PATCH 033/114] feat: support public/private output notes for MINT note (#2123) --- CHANGELOG.md | 1 + crates/miden-lib/asm/note_scripts/MINT.masm | 115 ++++++++++--- .../src/errors/note_script_errors.rs | 4 +- crates/miden-lib/src/note/mint_inputs.rs | 103 ++++++++++++ crates/miden-lib/src/note/mod.rs | 39 ++--- crates/miden-lib/src/note/well_known_note.rs | 6 +- crates/miden-testing/tests/scripts/faucet.rs | 152 ++++++++++++++++-- 7 files changed, 354 insertions(+), 66 deletions(-) create mode 100644 crates/miden-lib/src/note/mint_inputs.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4cf31956..18c7f73ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). - Split tx progress events out into a separate enum ([#2103](https://github.com/0xMiden/miden-base/pull/2103)). - Added `note::get_network_account_tag` procedure ([#2120](https://github.com/0xMiden/miden-base/pull/2120)). +- [BREAKING] Updated MINT note to support both private and public output note creation ([#2123](https://github.com/0xMiden/miden-base/pull/2123)). - [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-lib/asm/note_scripts/MINT.masm b/crates/miden-lib/asm/note_scripts/MINT.masm index fec23f48a4..65f102cfa1 100644 --- a/crates/miden-lib/asm/note_scripts/MINT.masm +++ b/crates/miden-lib/asm/note_scripts/MINT.masm @@ -1,14 +1,22 @@ use.miden::active_note use.miden::contracts::faucets::network_fungible->network_faucet +use.miden::note # CONSTANTS # ================================================================================================= -const.MINT_NOTE_INPUTS_NUMBER=9 +const.MINT_NOTE_NUM_INPUTS_PRIVATE=8 +const.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 + +const.OUTPUT_NOTE_TYPE_PUBLIC=1 +const.OUTPUT_NOTE_TYPE_PRIVATE=2 + +const.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 +const.OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 # ERRORS # ================================================================================================= -const.ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 9 note inputs" +const.ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" #! Network Faucet MINT script: mints assets by calling the network faucet's distribute function. #! This note is intended to be executed against a network fungible faucet account. @@ -19,43 +27,102 @@ const.ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 9 note inputs #! Inputs: [ARGS, pad(12)] #! Outputs: [pad(16)] #! -#! Note inputs are assumed to be as follows (in order): -#! - RECIPIENT: The recipient account ID (4 elements) -#! - Output note config (4 elements): -#! - execution_hint: Execution hint for the output note -#! - note_type: Type of the output note -#! - aux: Auxiliary data for the output note -#! - tag: Note tag for the output note +#! Note inputs support two modes. Depending on the number of note inputs, +#! a private or public note is created on consumption of the MINT note: +#! +#! Private mode (8 inputs) - creates a private note: +#! - execution_hint: Execution hint for the output note +#! - aux: Auxiliary data for the output note +#! - tag: Note tag for the output note #! - amount: The amount to mint +#! - RECIPIENT: The recipient digest (4 elements) +#! +#! Public mode (12+ inputs) - creates a public note with variable-length inputs: +#! - execution_hint: Execution hint for the output note +#! - aux: Auxiliary data for the output note +#! - tag: Note tag for the output note +#! - amount: The amount to mint +#! - SCRIPT_ROOT: Script root of the output note (4 elements) +#! - SERIAL_NUM: Serial number of the output note (4 elements) +#! - [INPUTS]: Variable-length inputs for the output note (Vec) +#! The number of output note inputs = num_mint_note_inputs - 12 #! #! Panics if: #! - account does not expose distribute procedure. -#! - the number of inputs is not exactly 9. +#! - the number of inputs is not exactly 8 for private or less than 12 for public output notes. begin dropw # => [pad(16)] - # Load note inputs into memory starting at address 0 push.0 exec.active_note::get_inputs - # => [num_inputs, inputs_ptr, pad(16)] + # => [total_inputs, inputs_ptr, pad(16)] - # Verify we have the correct number of inputs - eq.MINT_NOTE_INPUTS_NUMBER assert.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS drop - # => [pad(16)] + dup + # => [num_inputs, num_inputs, inputs_ptr, pad(16)] - # Load amount - mem_loadw_be.0 - # => [RECIPIENT, pad(12)] + u32assert2.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS + u32gte.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC + # => [is_public_output_note, total_inputs, inputs_ptr, pad(16)] - swapw mem_loadw_be.4 - # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + if.true + # public output note creation + # => [total_inputs, inputs_ptr, pad(16)] - mem_load.8 - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + movdn.9 drop + # => [EMPTY_WORD, EMPTY_WORD, total_inputs, pad(8)] + + mem_loadw_be.4 + # => [SCRIPT_ROOT, EMPTY_WORD, total_inputs, pad(8)] + + swapw mem_loadw_be.8 + # => [SERIAL_NUM, SCRIPT_ROOT, total_inputs, pad(8)] + + # compute variable length note inputs for the output note + movup.8 sub.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC + # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] + + push.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR + # => [inputs_ptr = 12, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] + + exec.note::build_recipient + # => [RECIPIENT, pad(12)] - movup.9 drop - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] + swapw mem_loadw_be.0 + # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + push.OUTPUT_NOTE_TYPE_PUBLIC + # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + movdn.3 + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + + else + # private output note creation + + eq.MINT_NOTE_NUM_INPUTS_PRIVATE assert.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS drop + # => [inputs_ptr, pad(16)] + + drop + # => [pad(16)] + + mem_loadw_be.4 + # => [RECIPIENT, pad(12)] + + swapw mem_loadw_be.0 + # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + push.OUTPUT_NOTE_TYPE_PRIVATE + # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + movdn.3 + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + + end + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] call.network_faucet::distribute + # => [pad(17))] + + drop # => [pad(16)] end diff --git a/crates/miden-lib/src/errors/note_script_errors.rs b/crates/miden-lib/src/errors/note_script_errors.rs index 2ee15d05e9..e64fa97c35 100644 --- a/crates/miden-lib/src/errors/note_script_errors.rs +++ b/crates/miden-lib/src/errors/note_script_errors.rs @@ -19,8 +19,8 @@ pub const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError: /// Error Message: "number of approvers must be equal to or greater than threshold" pub const ERR_MALFORMED_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers must be equal to or greater than threshold"); -/// Error Message: "MINT script expects exactly 9 note inputs" -pub const ERR_MINT_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("MINT script expects exactly 9 note inputs"); +/// Error Message: "MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" +pub const ERR_MINT_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes"); /// Error Message: "note sender is not the owner of the faucet who can mint assets" pub const ERR_ONLY_OWNER_CAN_MINT: MasmError = MasmError::from_static_str("note sender is not the owner of the faucet who can mint assets"); diff --git a/crates/miden-lib/src/note/mint_inputs.rs b/crates/miden-lib/src/note/mint_inputs.rs new file mode 100644 index 0000000000..2588aa3a7a --- /dev/null +++ b/crates/miden-lib/src/note/mint_inputs.rs @@ -0,0 +1,103 @@ +use miden_objects::note::{NoteExecutionHint, NoteInputs, NoteRecipient}; +use miden_objects::{Felt, MAX_INPUTS_PER_NOTE, NoteError, Word}; + +/// Represents the different input formats for MINT notes. +/// - Private: Creates a private output note using a precomputed recipient digest (8 MINT note +/// inputs) +/// - Public: Creates a public output note by providing script root, serial number, and +/// variable-length inputs (12+ MINT note inputs: 12 fixed + variable number of output note +/// inputs) +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MintNoteInputs { + Private { + recipient_digest: Word, + amount: Felt, + tag: Felt, + execution_hint: NoteExecutionHint, + aux: Felt, + }, + Public { + recipient: NoteRecipient, + amount: Felt, + tag: Felt, + execution_hint: NoteExecutionHint, + aux: Felt, + }, +} + +impl MintNoteInputs { + pub fn new_private( + recipient_digest: Word, + amount: Felt, + tag: Felt, + execution_hint: NoteExecutionHint, + aux: Felt, + ) -> Self { + Self::Private { + recipient_digest, + amount, + tag, + execution_hint, + aux, + } + } + + pub fn new_public( + recipient: NoteRecipient, + amount: Felt, + tag: Felt, + execution_hint: NoteExecutionHint, + aux: Felt, + ) -> Result { + // Calculate total number of inputs that will be created: + // 12 fixed inputs (execution_hint, aux, tag, amount, SCRIPT_ROOT, SERIAL_NUM) + + // variable recipient inputs length + const FIXED_PUBLIC_INPUTS: usize = 12; + let total_inputs = FIXED_PUBLIC_INPUTS + recipient.inputs().num_values() as usize; + + if total_inputs > MAX_INPUTS_PER_NOTE { + return Err(NoteError::TooManyInputs(total_inputs)); + } + + Ok(Self::Public { + recipient, + amount, + tag, + execution_hint, + aux, + }) + } +} + +impl From for NoteInputs { + fn from(mint_inputs: MintNoteInputs) -> Self { + match mint_inputs { + MintNoteInputs::Private { + recipient_digest, + amount, + tag, + execution_hint, + aux, + } => { + let mut input_values = vec![execution_hint.into(), aux, tag, amount]; + input_values.extend_from_slice(recipient_digest.as_elements()); + NoteInputs::new(input_values) + .expect("number of inputs should not exceed max inputs") + }, + MintNoteInputs::Public { + recipient, + amount, + tag, + execution_hint, + aux, + } => { + let mut input_values = vec![execution_hint.into(), aux, tag, amount]; + input_values.extend_from_slice(recipient.script().root().as_elements()); + input_values.extend_from_slice(recipient.serial_num().as_elements()); + input_values.extend_from_slice(recipient.inputs().values()); + NoteInputs::new(input_values) + .expect("number of inputs should not exceed max inputs") + }, + } + } +} diff --git a/crates/miden-lib/src/note/mod.rs b/crates/miden-lib/src/note/mod.rs index c5e1a4a5c8..67f12bf539 100644 --- a/crates/miden-lib/src/note/mod.rs +++ b/crates/miden-lib/src/note/mod.rs @@ -18,9 +18,11 @@ use miden_objects::note::{ use miden_objects::{Felt, NoteError, Word}; use utils::build_swap_tag; +pub mod mint_inputs; pub mod utils; mod well_known_note; +pub use mint_inputs::MintNoteInputs; pub use well_known_note::{NoteConsumptionStatus, WellKnownNote}; // STANDARDIZED SCRIPTS @@ -162,12 +164,12 @@ pub fn create_swap_note( /// Generates a MINT note - a note that instructs a network faucet to mint fungible assets. /// /// This script enables the creation of a PUBLIC note that, when consumed by a network faucet, -/// will mint the specified amount of fungible assets and create a PRIVATE note with the given -/// RECIPIENT. The MINT note uses note-based authentication, checking if the note sender equals -/// the faucet owner to authorize minting. +/// will mint the specified amount of fungible assets and create either a PRIVATE or PUBLIC +/// output note depending on the input configuration. The MINT note uses note-based authentication, +/// checking if the note sender equals the faucet owner to authorize minting. /// -/// MINT notes are always PUBLIC (for network execution) and output notes are always PRIVATE -/// (TODO: enable public output note creation from MINT note consumption). +/// MINT notes are always PUBLIC (for network execution). Output notes can be either PRIVATE +/// or PUBLIC depending on the MintNoteInputs variant used. /// /// The passed-in `rng` is used to generate a serial number for the note. The note's tag /// is automatically set to the faucet's account ID for proper routing. @@ -175,12 +177,8 @@ pub fn create_swap_note( /// # Parameters /// - `faucet_id`: The account ID of the network faucet that will mint the assets /// - `sender`: The account ID of the note creator (must be the faucet owner) -/// - `target_recipient`: The recipient digest for the output P2ID note that will receive the minted -/// assets -/// - `output_note_tag`: The tag for the output P2ID note -/// - `amount`: The amount of fungible assets to mint +/// - `mint_inputs`: The input configuration specifying private or public output mode /// - `aux`: Auxiliary data for the MINT note -/// - `output_note_aux`: Auxiliary data for the output P2ID note /// - `rng`: Random number generator for creating the serial number /// /// # Errors @@ -188,11 +186,8 @@ pub fn create_swap_note( pub fn create_mint_note( faucet_id: AccountId, sender: AccountId, - target_recipient: Word, - output_note_tag: Felt, - amount: Felt, + mint_inputs: MintNoteInputs, aux: Felt, - output_note_aux: Felt, rng: &mut R, ) -> Result { let note_script = WellKnownNote::MINT.script(); @@ -200,22 +195,10 @@ pub fn create_mint_note( // MINT notes are always public for network execution let note_type = NoteType::Public; - // Output notes are always private (for now) - let output_note_type = NoteType::Private; - let execution_hint = NoteExecutionHint::always(); - let inputs = NoteInputs::new(vec![ - target_recipient[0], - target_recipient[1], - target_recipient[2], - target_recipient[3], - execution_hint.into(), - output_note_type.into(), - output_note_aux, - output_note_tag, - amount, - ])?; + // Convert MintNoteInputs to NoteInputs + let inputs = NoteInputs::from(mint_inputs); let tag = NoteTag::from_account_id(faucet_id); diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index 0a0b200217..92586e58a4 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -127,8 +127,8 @@ impl WellKnownNote { /// Expected number of inputs of the SWAP note. const SWAP_NUM_INPUTS: usize = 10; - /// Expected number of inputs of the MINT note. - const MINT_NUM_INPUTS: usize = 9; + /// Expected number of inputs of the MINT note (private mode). + const MINT_NUM_INPUTS_PRIVATE: usize = 8; /// Expected number of inputs of the BURN note. const BURN_NUM_INPUTS: usize = 0; @@ -169,7 +169,7 @@ impl WellKnownNote { Self::P2ID => Self::P2ID_NUM_INPUTS, Self::P2IDE => Self::P2IDE_NUM_INPUTS, Self::SWAP => Self::SWAP_NUM_INPUTS, - Self::MINT => Self::MINT_NUM_INPUTS, + Self::MINT => Self::MINT_NUM_INPUTS_PRIVATE, Self::BURN => Self::BURN_NUM_INPUTS, } } diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 567cbd93a1..33fd60aa0a 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -5,7 +5,7 @@ use core::slice; use miden_lib::account::faucets::{BasicFungibleFaucet, FungibleFaucetExt, NetworkFungibleFaucet}; use miden_lib::errors::tx_kernel_errors::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; -use miden_lib::note::{create_burn_note, create_mint_note}; +use miden_lib::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; use miden_lib::testing::note::NoteBuilder; use miden_lib::utils::ScriptBuilder; use miden_objects::account::{ @@ -541,17 +541,17 @@ async fn network_faucet_mint() -> anyhow::Result<()> { let recipient = p2id_mint_output_note.recipient().digest(); // Create the MINT note using the helper function - let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); - let mint_note = create_mint_note( - faucet.id(), - faucet_owner_account_id, + let mint_inputs = MintNoteInputs::new_private( recipient, - output_note_tag.into(), amount, + output_note_tag.into(), + NoteExecutionHint::always(), aux, - aux, - &mut rng, - )?; + ); + + let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); + let mint_note = + create_mint_note(faucet.id(), faucet_owner_account_id, mint_inputs, aux, &mut rng)?; // Add the MINT note to the mock chain builder.add_output_note(OutputNote::Full(mint_note.clone())); @@ -668,3 +668,137 @@ async fn network_faucet_burn() -> anyhow::Result<()> { Ok(()) } + +// TESTS FOR MINT NOTE WITH PRIVATE AND PUBLIC OUTPUT MODES +// ================================================================================================ + +/// Tests creating a MINT note with different output note types (private/public) +/// The MINT note can create output notes with variable-length inputs for public notes. +#[rstest::rstest] +#[case::private(NoteType::Private)] +#[case::public(NoteType::Public)] +#[tokio::test] +async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + let faucet_owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let faucet = + builder.add_existing_network_faucet("NET", 1000, faucet_owner_account_id, Some(50))?; + let target_account = builder.add_existing_wallet(Auth::IncrNonce)?; + + let amount = Felt::new(75); + let mint_asset: Asset = FungibleAsset::new(faucet.id(), amount.into()).unwrap().into(); + let aux = Felt::new(27); + let serial_num = Word::from([1, 2, 3, 4u32]); + + // Create the expected P2ID output note + let p2id_mint_output_note = create_p2id_note_exact( + faucet.id(), + target_account.id(), + vec![mint_asset], + note_type, + aux, + serial_num, + ) + .unwrap(); + + // Create MINT note based on note type + let mint_inputs = match note_type { + NoteType::Private => { + let output_note_tag = NoteTag::from_account_id(target_account.id()); + let recipient = p2id_mint_output_note.recipient().digest(); + MintNoteInputs::new_private( + recipient, + amount, + output_note_tag.into(), + NoteExecutionHint::always(), + aux, + ) + }, + NoteType::Public => { + let output_note_tag = NoteTag::from_account_id(target_account.id()); + let p2id_script = WellKnownNote::P2ID.script(); + let p2id_inputs = + vec![target_account.id().suffix(), target_account.id().prefix().as_felt()]; + let note_inputs = NoteInputs::new(p2id_inputs)?; + let recipient = NoteRecipient::new(serial_num, p2id_script, note_inputs); + MintNoteInputs::new_public( + recipient, + amount, + output_note_tag.into(), + NoteExecutionHint::always(), + aux, + )? + }, + NoteType::Encrypted => unreachable!("Encrypted note type not used in this test"), + }; + + let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); + let mint_note = + create_mint_note(faucet.id(), faucet_owner_account_id, mint_inputs.clone(), aux, &mut rng)?; + + builder.add_output_note(OutputNote::Full(mint_note.clone())); + let mut mock_chain = builder.build()?; + + let mut tx_context_builder = + mock_chain.build_tx_context(faucet.id(), &[mint_note.id()], &[])?; + + if note_type == NoteType::Public { + let p2id_script = WellKnownNote::P2ID.script(); + tx_context_builder = tx_context_builder.add_note_script(p2id_script); + } + + let tx_context = tx_context_builder.build()?; + let executed_transaction = tx_context.execute().await?; + + assert_eq!(executed_transaction.output_notes().num_notes(), 1); + let output_note = executed_transaction.output_notes().get_note(0); + + match note_type { + NoteType::Private => { + // For private notes, we can only compare basic properties since we get + // OutputNote::Partial + assert_eq!(output_note.id(), p2id_mint_output_note.id()); + assert_eq!(output_note.metadata().sender(), p2id_mint_output_note.metadata().sender()); + assert_eq!( + output_note.metadata().note_type(), + p2id_mint_output_note.metadata().note_type() + ); + assert_eq!(output_note.metadata().aux(), p2id_mint_output_note.metadata().aux()); + }, + NoteType::Public => { + // For public notes, we get OutputNote::Full and can compare key properties + let created_note = match output_note { + OutputNote::Full(note) => note, + _ => panic!("Expected OutputNote::Full variant for public note"), + }; + + assert_eq!(created_note, &p2id_mint_output_note); + }, + NoteType::Encrypted => unreachable!("Encrypted note type not used in this test"), + } + + mock_chain.add_pending_executed_transaction(&executed_transaction)?; + mock_chain.prove_next_block()?; + + // Consume the output note with target account + let mut target_account_mut = target_account.clone(); + let consume_tx_context = mock_chain + .build_tx_context(target_account.id(), &[], slice::from_ref(&p2id_mint_output_note))? + .build()?; + let consume_executed_transaction = consume_tx_context.execute().await?; + + target_account_mut.apply_delta(consume_executed_transaction.account_delta())?; + + let expected_asset = FungibleAsset::new(faucet.id(), amount.into())?; + let balance = target_account_mut.vault().get_balance(faucet.id())?; + assert_eq!(balance, expected_asset.amount()); + + Ok(()) +} From a5b4b5bf65ee5153a7412873914e6d8b4f04288b Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Thu, 11 Dec 2025 11:29:13 +1300 Subject: [PATCH 034/114] feat: add validator public key to the `BlockHeader` (#2128) --- CHANGELOG.md | 1 + Cargo.lock | 5 +- Cargo.toml | 3 +- .../asm/kernels/transaction/lib/memory.masm | 20 ++--- .../asm/kernels/transaction/lib/prologue.masm | 8 +- crates/miden-lib/src/block/mod.rs | 48 +++-------- crates/miden-lib/src/transaction/inputs.rs | 4 +- crates/miden-lib/src/transaction/memory.rs | 38 ++++----- crates/miden-objects/Cargo.toml | 4 +- .../miden-objects/src/batch/proposed_batch.rs | 2 +- crates/miden-objects/src/block/header.rs | 80 ++++++++++++------- crates/miden-objects/src/block/mod.rs | 3 + .../miden-objects/src/block/proven_block.rs | 21 ++++- crates/miden-objects/src/block/signer.rs | 28 +++++++ crates/miden-objects/src/testing/block.rs | 10 +-- crates/miden-objects/src/testing/mod.rs | 1 + .../src/testing/random_signer.rs | 22 +++++ .../src/transaction/executed_tx.rs | 3 +- crates/miden-objects/src/transaction/mod.rs | 1 - .../src/transaction/partial_blockchain.rs | 13 ++- crates/miden-testing/Cargo.toml | 2 +- .../src/kernel_tests/tx/test_prologue.rs | 8 +- crates/miden-testing/src/mock_chain/chain.rs | 51 +++++++++++- .../src/mock_chain/chain_builder.rs | 17 +++- 24 files changed, 259 insertions(+), 134 deletions(-) create mode 100644 crates/miden-objects/src/block/signer.rs create mode 100644 crates/miden-objects/src/testing/random_signer.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 18c7f73ae5..b349c3b547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Added `note::get_network_account_tag` procedure ([#2120](https://github.com/0xMiden/miden-base/pull/2120)). - [BREAKING] Updated MINT note to support both private and public output note creation ([#2123](https://github.com/0xMiden/miden-base/pull/2123)). - [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)). +- [BREAKING] Add public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)). ## 0.12.4 (2025-11-26) diff --git a/Cargo.lock b/Cargo.lock index dde15ee20d..e6a494dbdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1471,9 +1471,9 @@ dependencies = [ [[package]] name = "miden-crypto" -version = "0.18.2" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb82051002f9c64878d3b105a7b924de1ee92019231923380cf4ecd7b824f9a" +checksum = "395e5cc76b64e24533ee55c8d1ff90305b8cad372bdbea4f4f324239e36a895f" dependencies = [ "blake3", "cc", @@ -1634,6 +1634,7 @@ dependencies = [ "miden-verifier", "pprof", "rand", + "rand_chacha", "rand_xoshiro", "rstest", "semver 1.0.27", diff --git a/Cargo.toml b/Cargo.toml index fc5e3dca8c..18222c2b59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ miden-air = { default-features = false, version = "0.19" } miden-assembly = { default-features = false, version = "0.19" } miden-assembly-syntax = { default-features = false, version = "0.19" } miden-core = { default-features = false, version = "0.19" } -miden-crypto = { default-features = false, version = "0.18" } +miden-crypto = { default-features = false, version = "0.18.5" } miden-mast-package = { default-features = false, version = "0.19" } miden-processor = { default-features = false, version = "0.19" } miden-prover = { default-features = false, version = "0.19" } @@ -66,6 +66,7 @@ miden-verifier = { default-features = false, version = "0.19" } anyhow = { default-features = false, features = ["backtrace", "std"], version = "1.0" } assert_matches = { default-features = false, version = "1.5" } rand = { default-features = false, version = "0.9" } +rand_chacha = { default-features = false, version = "0.9" } rstest = { version = "0.26" } thiserror = { default-features = false, version = "2.0" } tokio = { default-features = false, features = ["sync"], version = "1" } diff --git a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm index aee6869ec9..b954221770 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm @@ -115,7 +115,7 @@ const.TX_COMMITMENT_PTR=816 const.TX_KERNEL_COMMITMENT_PTR=820 # The memory address at which the proof commitment is stored -const.PROOF_COMMITMENT_PTR=824 +const.VALIDATOR_KEY_COMMITMENT_PTR=824 # The memory address at which the block metadata is stored [block_number, version, timestamp, 0] const.BLOCK_METADATA_PTR=828 @@ -502,14 +502,14 @@ export.get_init_native_account_vault_root padw mem_loadw_be.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR end -#! Returns the memory address of the vault root of the native account at the beginning of the +#! Returns the memory address of the vault root of the native account at the beginning of the #! transaction. #! #! Inputs: [] #! Outputs: [native_account_initial_vault_root_ptr] #! #! Where: -#! - native_account_initial_vault_root_ptr is the memory pointer to the initial vault root of the +#! - native_account_initial_vault_root_ptr is the memory pointer to the initial vault root of the #! native account. export.get_init_native_account_vault_root_ptr push.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR @@ -769,15 +769,15 @@ export.get_tx_kernel_commitment padw mem_loadw_be.TX_KERNEL_COMMITMENT_PTR end -#! Returns the proof commitment of the transaction reference block. +#! Returns the validator key commitment of the transaction reference block. #! #! Inputs: [] -#! Outputs: [PROOF_COMMITMENT] +#! Outputs: [VALIDATOR_KEY_COMMITMENT] #! #! Where: -#! - PROOF_COMMITMENT is the proof commitment of the transaction reference block. -export.get_proof_commitment - padw mem_loadw_be.PROOF_COMMITMENT_PTR +#! - VALIDATOR_KEY_COMMITMENT is the public key commitment of the transaction reference block. +export.get_validator_key_commitment + padw mem_loadw_be.VALIDATOR_KEY_COMMITMENT_PTR end #! Returns the note root of the transaction reference block. @@ -1143,7 +1143,7 @@ end #! Where: #! - account_initial_vault_root_ptr is the memory pointer to the initial vault root. export.get_account_initial_vault_root_ptr - # For foreign account, use the regular vault root pointer since foreign accounts are read-only + # For foreign account, use the regular vault root pointer since foreign accounts are read-only # and initial == current exec.get_account_vault_root_ptr # => [account_vault_root_ptr] @@ -1403,7 +1403,7 @@ end #! Where: #! - account_initial_storage_slots_ptr is the memory pointer to the initial storage slot values. export.get_account_initial_storage_slots_ptr - # For foreign account, use the regular storage slots pointer since foreign accounts are + # For foreign account, use the regular storage slots pointer since foreign accounts are # read-only and initial == current exec.get_account_storage_slots_section_ptr # => [account_storage_slots_ptr] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index 28401469f7..5c0b7030c4 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -171,7 +171,7 @@ end #! NULLIFIER_ROOT, #! TX_COMMITMENT, #! TX_KERNEL_COMMITMENT -#! PROOF_COMMITMENT, +#! VALIDATOR_KEY_COMMITMENT, #! [block_num, version, timestamp, 0], #! [native_asset_id_suffix, native_asset_id_prefix, verification_base_fee, 0], #! [0, 0, 0, 0], @@ -188,7 +188,7 @@ end #! consumed. #! - TX_COMMITMENT is a commitment to the set of transaction IDs which affected accounts in the block. #! - TX_KERNEL_COMMITMENT is the sequential hash of the kernel procedures. -#! - PROOF_COMMITMENT is the commitment of the block's STARK proof attesting to the correct state transition. +#! - VALIDATOR_KEY_COMMITMENT is the commitment to the validator's public key. #! - block_num is the reference block number. #! - version is the current protocol version. #! - timestamp is the current timestamp. @@ -1135,7 +1135,7 @@ end #! NULLIFIER_ROOT, #! TX_COMMITMENT, #! TX_KERNEL_COMMITMENT -#! PROOF_COMMITMENT, +#! VALIDATOR_KEY_COMMITMENT, #! [block_num, version, timestamp, 0], #! NOTE_ROOT, #! kernel_version @@ -1174,7 +1174,7 @@ end #! - NULLIFIER_ROOT is the root of the tree with nullifiers of all notes that have ever been #! consumed. #! - TX_COMMITMENT is a commitment to the set of transaction IDs which affected accounts in the block. -#! - PROOF_COMMITMENT is the commitment of the block's STARK proof attesting to the correct state transition. +#! - VALIDATOR_KEY_COMMITMENT is the commitment to the validator's public key. #! - block_num is the reference block number. #! - version is the current protocol version. #! - timestamp is the current timestamp. diff --git a/crates/miden-lib/src/block/mod.rs b/crates/miden-lib/src/block/mod.rs index ffdb6f4bc3..d3db8b80e8 100644 --- a/crates/miden-lib/src/block/mod.rs +++ b/crates/miden-lib/src/block/mod.rs @@ -1,17 +1,19 @@ -use miden_core::Word; use miden_objects::ProposedBlockError; -use miden_objects::block::{BlockBody, BlockHeader, BlockNumber, ProposedBlock}; +use miden_objects::block::{BlockBody, BlockHeader, ProposedBlock}; use crate::transaction::TransactionKernel; -/// Builds a [`BlockHeader`] and [`BlockBody`] by computing the following from the state updates -/// encapsulated by the provided [`ProposedBlock`]: +/// Builds a [`BlockHeader`] and [`BlockBody`] by computing the following from the state +/// updates encapsulated by the provided [`ProposedBlock`]: /// - the account root; /// - the nullifier root; /// - the note root; /// - the transaction commitment; and /// - the chain commitment. /// +/// The returned block header contains the same validator public key as the previous block, as +/// provided by the proposed block. +/// /// This functionality is handled here because the block header requires [`TransactionKernel`] for /// its various commitment fields. pub fn build_block( @@ -42,33 +44,6 @@ pub fn build_block( // Construct the header. let tx_commitment = body.transaction_commitment(); - let header = construct_block_header( - block_num, - timestamp, - prev_block_header, - tx_commitment, - new_chain_commitment, - new_account_root, - new_nullifier_root, - note_root, - ); - - Ok((header, body)) -} - -// HELPERS -// ================================================================================================ - -fn construct_block_header( - block_num: BlockNumber, - timestamp: u32, - prev_block_header: BlockHeader, - tx_commitment: Word, - new_chain_commitment: Word, - new_account_root: Word, - new_nullifier_root: Word, - note_root: Word, -) -> BlockHeader { let prev_block_commitment = prev_block_header.commitment(); // For now we copy the parameters of the previous header, which means the parameters set on @@ -80,11 +55,7 @@ fn construct_block_header( // See miden-base/1155. let version = 0; let tx_kernel_commitment = TransactionKernel.to_commitment(); - - // TODO(serge): remove proof commitment when block header is updated to no longer have it. - let proof_commitment = Word::empty(); - - BlockHeader::new( + let header = BlockHeader::new( version, prev_block_commitment, block_num, @@ -94,8 +65,9 @@ fn construct_block_header( note_root, tx_commitment, tx_kernel_commitment, - proof_commitment, + prev_block_header.validator_key().clone(), fee_parameters, timestamp, - ) + ); + Ok((header, body)) } diff --git a/crates/miden-lib/src/transaction/inputs.rs b/crates/miden-lib/src/transaction/inputs.rs index 1705ffd3fe..ca1890e5f0 100644 --- a/crates/miden-lib/src/transaction/inputs.rs +++ b/crates/miden-lib/src/transaction/inputs.rs @@ -138,7 +138,7 @@ impl TransactionAdviceInputs { /// NULLIFIER_ROOT, /// TX_COMMITMENT, /// TX_KERNEL_COMMITMENT - /// PROOF_COMMITMENT, + /// VALIDATOR_KEY_COMMITMENT, /// [block_num, version, timestamp, 0], /// [native_asset_id_suffix, native_asset_id_prefix, verification_base_fee, 0] /// [0, 0, 0, 0] @@ -163,7 +163,7 @@ impl TransactionAdviceInputs { self.extend_stack(header.nullifier_root()); self.extend_stack(header.tx_commitment()); self.extend_stack(header.tx_kernel_commitment()); - self.extend_stack(header.proof_commitment()); + self.extend_stack(header.validator_key().to_commitment()); self.extend_stack([ header.block_num().into(), header.version().into(), diff --git a/crates/miden-lib/src/transaction/memory.rs b/crates/miden-lib/src/transaction/memory.rs index 019f8caaea..0306e606f7 100644 --- a/crates/miden-lib/src/transaction/memory.rs +++ b/crates/miden-lib/src/transaction/memory.rs @@ -141,40 +141,40 @@ pub const AUTH_ARGS_PTR: MemoryAddress = 436; // BLOCK DATA // ------------------------------------------------------------------------------------------------ -/// The memory address at which the block data section begins +/// The memory address at which the block data section begins. pub const BLOCK_DATA_SECTION_OFFSET: MemoryOffset = 800; -/// The memory address at which the previous block commitment is stored +/// The memory address at which the previous block commitment is stored. pub const PREV_BLOCK_COMMITMENT_PTR: MemoryAddress = 800; -/// The memory address at which the chain commitment is stored +/// The memory address at which the chain commitment is stored. pub const CHAIN_COMMITMENT_PTR: MemoryAddress = 804; -/// The memory address at which the state root is stored +/// The memory address at which the state root is stored. pub const ACCT_DB_ROOT_PTR: MemoryAddress = 808; -/// The memory address at which the nullifier db root is store +/// The memory address at which the nullifier db root is store. pub const NULLIFIER_DB_ROOT_PTR: MemoryAddress = 812; -/// The memory address at which the TX commitment is stored +/// The memory address at which the TX commitment is stored. pub const TX_COMMITMENT_PTR: MemoryAddress = 816; -/// The memory address at which the transaction kernel commitment is stored +/// The memory address at which the transaction kernel commitment is stored. pub const TX_KERNEL_COMMITMENT_PTR: MemoryAddress = 820; -/// The memory address at which the proof commitment is stored -pub const PROOF_COMMITMENT_PTR: MemoryAddress = 824; +/// The memory address at which the public key is stored. +pub const VALIDATOR_KEY_COMMITMENT_PTR: MemoryAddress = 824; -/// The memory address at which the block number is stored +/// The memory address at which the block number is stored. pub const BLOCK_METADATA_PTR: MemoryAddress = 828; -/// The index of the block number within the block metadata +/// The index of the block number within the block metadata. pub const BLOCK_NUMBER_IDX: DataIndex = 0; -/// The index of the protocol version within the block metadata +/// The index of the protocol version within the block metadata. pub const PROTOCOL_VERSION_IDX: DataIndex = 1; -/// The index of the timestamp within the block metadata +/// The index of the timestamp within the block metadata. pub const TIMESTAMP_IDX: DataIndex = 2; /// The memory address at which the fee parameters are stored. These occupy a double word. @@ -189,19 +189,19 @@ pub const NATIVE_ASSET_ID_PREFIX_IDX: DataIndex = 1; /// The index of the verification base fee within the block fee parameters. pub const VERIFICATION_BASE_FEE_IDX: DataIndex = 2; -/// The memory address at which the note root is stored +/// The memory address at which the note root is stored. pub const NOTE_ROOT_PTR: MemoryAddress = 840; // CHAIN DATA // ------------------------------------------------------------------------------------------------ -/// The memory address at which the chain data section begins +/// The memory address at which the chain data section begins. pub const PARTIAL_BLOCKCHAIN_PTR: MemoryAddress = 1200; -/// The memory address at which the total number of leaves in the partial blockchain is stored +/// The memory address at which the total number of leaves in the partial blockchain is stored. pub const PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR: MemoryAddress = 1200; -/// The memory address at which the partial blockchain peaks are stored +/// The memory address at which the partial blockchain peaks are stored. pub const PARTIAL_BLOCKCHAIN_PEAKS_PTR: MemoryAddress = 1204; // KERNEL DATA @@ -211,13 +211,13 @@ pub const PARTIAL_BLOCKCHAIN_PEAKS_PTR: MemoryAddress = 1204; pub const NUM_KERNEL_PROCEDURES_PTR: MemoryAddress = 1600; /// The memory address at which the section, where the hashes of the kernel procedures are stored, -/// begins +/// begins. pub const KERNEL_PROCEDURES_PTR: MemoryAddress = 1604; // ACCOUNT DATA // ------------------------------------------------------------------------------------------------ -/// The size of the memory segment allocated to core account data (excluding new code commitment) +/// The size of the memory segment allocated to core account data (excluding new code commitment). pub const ACCT_DATA_MEM_SIZE: MemSize = 16; /// The memory address at which the native account is stored. diff --git a/crates/miden-objects/Cargo.toml b/crates/miden-objects/Cargo.toml index d3abe589dc..997c800f15 100644 --- a/crates/miden-objects/Cargo.toml +++ b/crates/miden-objects/Cargo.toml @@ -31,7 +31,7 @@ std = [ "miden-processor/std", "miden-verifier/std", ] -testing = ["dep:rand_xoshiro", "dep:winter-rand-utils", "miden-air/testing"] +testing = ["dep:rand_chacha", "dep:rand_xoshiro", "dep:winter-rand-utils", "miden-air/testing"] [dependencies] # Miden dependencies @@ -55,6 +55,8 @@ semver = { features = ["serde"], version = "1.0" } serde = { features = ["derive"], optional = true, version = "1.0" } thiserror = { workspace = true } toml = { optional = true, version = "0.9" } +# for SecretKey generation +rand_chacha = { optional = true, workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { features = ["wasm_js"], version = "0.3" } diff --git a/crates/miden-objects/src/batch/proposed_batch.rs b/crates/miden-objects/src/batch/proposed_batch.rs index d24364ef43..5a57cee094 100644 --- a/crates/miden-objects/src/batch/proposed_batch.rs +++ b/crates/miden-objects/src/batch/proposed_batch.rs @@ -29,7 +29,7 @@ use crate::{MAX_ACCOUNTS_PER_BATCH, MAX_INPUT_NOTES_PER_BATCH, MAX_OUTPUT_NOTES_ pub struct ProposedBatch { /// The transactions of this batch. transactions: Vec>, - /// The header is boxed as it has a large stack size. + /// The header of the reference block that this batch is proposed for. reference_block_header: BlockHeader, /// The partial blockchain used to authenticate: /// - all unauthenticated notes that can be authenticated, diff --git a/crates/miden-objects/src/block/header.rs b/crates/miden-objects/src/block/header.rs index 22e8f91f34..fec485c164 100644 --- a/crates/miden-objects/src/block/header.rs +++ b/crates/miden-objects/src/block/header.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use crate::account::{AccountId, AccountType}; use crate::block::BlockNumber; +use crate::crypto::dsa::ecdsa_k256_keccak::PublicKey; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -15,8 +16,8 @@ use crate::{FeeError, Felt, Hasher, Word, ZERO}; // BLOCK HEADER // ================================================================================================ -/// The header of a block. It contains metadata about the block, commitments to the current -/// state of the chain and the hash of the proof that attests to the integrity of the chain. +/// The header of a block. It contains metadata about the block, commitments to the current state of +/// the chain and the hash of the proof that attests to the integrity of the chain. /// /// A block header includes the following fields: /// @@ -30,8 +31,7 @@ use crate::{FeeError, Felt, Hasher, Word, ZERO}; /// - `tx_commitment` is a commitment to the set of transaction IDs which affected accounts in the /// block. /// - `tx_kernel_commitment` a commitment to all transaction kernels supported by this block. -/// - `proof_commitment` is the commitment of the block's STARK proof attesting to the correct state -/// transition. +/// - `validator_key` is the public key of the validator that is expected to sign the block. /// - `fee_parameters` are the parameters defining the base fees and the native asset, see /// [`FeeParameters`] for more details. /// - `timestamp` is the time when the block was created, in seconds since UNIX epoch. Current @@ -49,7 +49,7 @@ pub struct BlockHeader { note_root: Word, tx_commitment: Word, tx_kernel_commitment: Word, - proof_commitment: Word, // TODO(serge): remove this field as proofs are constructed later. + validator_key: PublicKey, fee_parameters: FeeParameters, timestamp: u32, sub_commitment: Word, @@ -69,11 +69,11 @@ impl BlockHeader { note_root: Word, tx_commitment: Word, tx_kernel_commitment: Word, - proof_commitment: Word, + validator_key: PublicKey, fee_parameters: FeeParameters, timestamp: u32, ) -> Self { - // compute block sub commitment + // Compute block sub commitment. let sub_commitment = Self::compute_sub_commitment( version, prev_block_commitment, @@ -82,7 +82,7 @@ impl BlockHeader { nullifier_root, tx_commitment, tx_kernel_commitment, - proof_commitment, + &validator_key, &fee_parameters, timestamp, block_num, @@ -104,7 +104,7 @@ impl BlockHeader { note_root, tx_commitment, tx_kernel_commitment, - proof_commitment, + validator_key, fee_parameters, timestamp, sub_commitment, @@ -172,6 +172,11 @@ impl BlockHeader { self.note_root } + /// Returns the public key of the block's validator. + pub fn validator_key(&self) -> &PublicKey { + &self.validator_key + } + /// Returns the commitment to all transactions in this block. /// /// The commitment is computed as sequential hash of (`transaction_id`, `account_id`) tuples. @@ -189,11 +194,6 @@ impl BlockHeader { self.tx_kernel_commitment } - /// Returns the proof commitment. - pub fn proof_commitment(&self) -> Word { - self.proof_commitment - } - /// Returns a reference to the [`FeeParameters`] in this header. pub fn fee_parameters(&self) -> &FeeParameters { &self.fee_parameters @@ -216,7 +216,7 @@ impl BlockHeader { /// /// The sub commitment is computed as a sequential hash of the following fields: /// `prev_block_commitment`, `chain_commitment`, `account_root`, `nullifier_root`, `note_root`, - /// `tx_commitment`, `tx_kernel_commitment`, `proof_commitment`, `version`, `timestamp`, + /// `tx_commitment`, `tx_kernel_commitment`, `validator_key_commitment`, `version`, `timestamp`, /// `block_num`, `native_asset_id`, `verification_base_fee` (all fields except the `note_root`). #[allow(clippy::too_many_arguments)] fn compute_sub_commitment( @@ -227,7 +227,7 @@ impl BlockHeader { nullifier_root: Word, tx_commitment: Word, tx_kernel_commitment: Word, - proof_commitment: Word, + validator_key: &PublicKey, fee_parameters: &FeeParameters, timestamp: u32, block_num: BlockNumber, @@ -239,7 +239,7 @@ impl BlockHeader { elements.extend_from_slice(nullifier_root.as_elements()); elements.extend_from_slice(tx_commitment.as_elements()); elements.extend_from_slice(tx_kernel_commitment.as_elements()); - elements.extend_from_slice(proof_commitment.as_elements()); + elements.extend(validator_key.to_commitment()); elements.extend([block_num.into(), version.into(), timestamp.into(), ZERO]); elements.extend([ fee_parameters.native_asset_id().suffix(), @@ -257,18 +257,36 @@ impl BlockHeader { impl Serializable for BlockHeader { fn write_into(&self, target: &mut W) { - self.version.write_into(target); - self.prev_block_commitment.write_into(target); - self.block_num.write_into(target); - self.chain_commitment.write_into(target); - self.account_root.write_into(target); - self.nullifier_root.write_into(target); - self.note_root.write_into(target); - self.tx_commitment.write_into(target); - self.tx_kernel_commitment.write_into(target); - self.proof_commitment.write_into(target); - self.fee_parameters.write_into(target); - self.timestamp.write_into(target); + let Self { + version, + prev_block_commitment, + block_num, + chain_commitment, + account_root, + nullifier_root, + note_root, + tx_commitment, + tx_kernel_commitment, + validator_key, + fee_parameters, + timestamp, + // Don't serialize sub commitment and commitment as they can be derived. + sub_commitment: _, + commitment: _, + } = self; + + version.write_into(target); + prev_block_commitment.write_into(target); + block_num.write_into(target); + chain_commitment.write_into(target); + account_root.write_into(target); + nullifier_root.write_into(target); + note_root.write_into(target); + tx_commitment.write_into(target); + tx_kernel_commitment.write_into(target); + validator_key.write_into(target); + fee_parameters.write_into(target); + timestamp.write_into(target); } } @@ -283,7 +301,7 @@ impl Deserializable for BlockHeader { let note_root = source.read()?; let tx_commitment = source.read()?; let tx_kernel_commitment = source.read()?; - let proof_commitment = source.read()?; + let validator_key = source.read()?; let fee_parameters = source.read()?; let timestamp = source.read()?; @@ -297,7 +315,7 @@ impl Deserializable for BlockHeader { note_root, tx_commitment, tx_kernel_commitment, - proof_commitment, + validator_key, fee_parameters, timestamp, )) diff --git a/crates/miden-objects/src/block/mod.rs b/crates/miden-objects/src/block/mod.rs index 63a0c19a01..f8c8e551c8 100644 --- a/crates/miden-objects/src/block/mod.rs +++ b/crates/miden-objects/src/block/mod.rs @@ -47,6 +47,9 @@ pub use block_inputs::BlockInputs; mod note_tree; pub use note_tree::{BlockNoteIndex, BlockNoteTree}; +mod signer; +pub use signer::BlockSigner; + /// The set of notes created in a transaction batch with their index in the batch. /// /// The index is included as some notes may be erased at the block level that were part of the diff --git a/crates/miden-objects/src/block/proven_block.rs b/crates/miden-objects/src/block/proven_block.rs index dfa2bc60bf..8e1c23c6c8 100644 --- a/crates/miden-objects/src/block/proven_block.rs +++ b/crates/miden-objects/src/block/proven_block.rs @@ -1,3 +1,5 @@ +use miden_crypto::dsa::ecdsa_k256_keccak::Signature; + use crate::MIN_PROOF_SECURITY_LEVEL; use crate::block::{BlockBody, BlockHeader, BlockProof}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -19,6 +21,9 @@ pub struct ProvenBlock { /// The body of the proven block. body: BlockBody, + /// The validator's signature over the block header. + signature: Signature, + /// The proof of the block. proof: BlockProof, } @@ -30,8 +35,13 @@ impl ProvenBlock { /// /// This constructor does not do any validation, so passing incorrect values may lead to later /// panics. - pub fn new_unchecked(header: BlockHeader, body: BlockBody, proof: BlockProof) -> Self { - Self { header, body, proof } + pub fn new_unchecked( + header: BlockHeader, + body: BlockBody, + signature: Signature, + proof: BlockProof, + ) -> Self { + Self { header, signature, body, proof } } /// Returns the proof security level of the block. @@ -44,6 +54,11 @@ impl ProvenBlock { &self.header } + /// Returns the validator's signature over the block header. + pub fn signature(&self) -> &Signature { + &self.signature + } + /// Returns the body of the block. pub fn body(&self) -> &BlockBody { &self.body @@ -62,6 +77,7 @@ impl Serializable for ProvenBlock { fn write_into(&self, target: &mut W) { self.header.write_into(target); self.body.write_into(target); + self.signature.write_into(target); self.proof.write_into(target); } } @@ -71,6 +87,7 @@ impl Deserializable for ProvenBlock { let block = Self { header: BlockHeader::read_from(source)?, body: BlockBody::read_from(source)?, + signature: Signature::read_from(source)?, proof: BlockProof::read_from(source)?, }; diff --git a/crates/miden-objects/src/block/signer.rs b/crates/miden-objects/src/block/signer.rs new file mode 100644 index 0000000000..2007e58146 --- /dev/null +++ b/crates/miden-objects/src/block/signer.rs @@ -0,0 +1,28 @@ +use crate::block::BlockHeader; +use crate::crypto::dsa::ecdsa_k256_keccak as ecdsa; +use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey; + +// BLOCK SIGNER +// ================================================================================================ + +/// Trait which abstracts the signing of block headers with ECDSA signatures. +/// +/// Production-level implementations will involve some sort of secure remote backend. The trait also +/// allows for testing with local and ephemeral signers. +pub trait BlockSigner { + fn sign(&self, header: &BlockHeader) -> ecdsa::Signature; + fn public_key(&self) -> ecdsa::PublicKey; +} + +// SECRET KEY BLOCK SIGNER +// ================================================================================================ + +impl BlockSigner for SecretKey { + fn sign(&self, header: &BlockHeader) -> ecdsa::Signature { + self.sign(header.commitment()) + } + + fn public_key(&self) -> ecdsa::PublicKey { + self.public_key() + } +} diff --git a/crates/miden-objects/src/testing/block.rs b/crates/miden-objects/src/testing/block.rs index 52948bece2..e67ef5b21f 100644 --- a/crates/miden-objects/src/testing/block.rs +++ b/crates/miden-objects/src/testing/block.rs @@ -6,7 +6,9 @@ use crate::Word; use crate::account::Account; use crate::block::account_tree::{AccountTree, account_id_to_smt_key}; use crate::block::{BlockHeader, BlockNumber, FeeParameters}; +use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey; use crate::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; +use crate::testing::random_signer::RandomBlockSigner; impl BlockHeader { /// Creates a mock block. The account tree is formed from the provided `accounts`, @@ -33,6 +35,7 @@ impl BlockHeader { let fee_parameters = FeeParameters::new(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET.try_into().unwrap(), 500) .expect("native asset ID should be a fungible faucet ID"); + let validator_key = SecretKey::random().public_key(); #[cfg(not(target_family = "wasm"))] let ( @@ -41,7 +44,6 @@ impl BlockHeader { nullifier_root, note_root, tx_commitment, - proof_commitment, timestamp, ) = { let prev_block_commitment = rand_value::(); @@ -49,7 +51,6 @@ impl BlockHeader { let nullifier_root = rand_value::(); let note_root = note_root.unwrap_or(rand_value::()); let tx_commitment = rand_value::(); - let proof_commitment = rand_value::(); let timestamp = rand_value(); ( @@ -58,7 +59,6 @@ impl BlockHeader { nullifier_root, note_root, tx_commitment, - proof_commitment, timestamp, ) }; @@ -70,7 +70,6 @@ impl BlockHeader { nullifier_root, note_root, tx_commitment, - proof_commitment, timestamp, ) = { ( @@ -80,7 +79,6 @@ impl BlockHeader { note_root.unwrap_or_default(), Default::default(), Default::default(), - Default::default(), ) }; @@ -94,7 +92,7 @@ impl BlockHeader { note_root, tx_commitment, tx_kernel_commitment, - proof_commitment, + validator_key, fee_parameters, timestamp, ) diff --git a/crates/miden-objects/src/testing/mod.rs b/crates/miden-objects/src/testing/mod.rs index c58d4ba930..ca107d73f3 100644 --- a/crates/miden-objects/src/testing/mod.rs +++ b/crates/miden-objects/src/testing/mod.rs @@ -9,6 +9,7 @@ pub mod constants; pub mod noop_auth_component; pub mod note; pub mod partial_blockchain; +pub mod random_signer; pub mod slot_name; pub mod storage; pub mod tx; diff --git a/crates/miden-objects/src/testing/random_signer.rs b/crates/miden-objects/src/testing/random_signer.rs new file mode 100644 index 0000000000..4d104e2f21 --- /dev/null +++ b/crates/miden-objects/src/testing/random_signer.rs @@ -0,0 +1,22 @@ +// NO STD ECDSA SIGNER +// ================================================================================================ + +use crate::block::BlockSigner; +use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey; + +/// An insecure, random block signer for testing purposes. +pub trait RandomBlockSigner: BlockSigner { + fn random() -> Self; +} + +// NO STD SECRET KEY BLOCK SIGNER +// ================================================================================================ + +impl RandomBlockSigner for SecretKey { + fn random() -> Self { + use rand::SeedableRng; + use rand_chacha::ChaCha20Rng; + let mut rng = ChaCha20Rng::from_os_rng(); + SecretKey::with_rng(&mut rng) + } +} diff --git a/crates/miden-objects/src/transaction/executed_tx.rs b/crates/miden-objects/src/transaction/executed_tx.rs index 65084eb1fc..27bd067a81 100644 --- a/crates/miden-objects/src/transaction/executed_tx.rs +++ b/crates/miden-objects/src/transaction/executed_tx.rs @@ -5,7 +5,6 @@ use super::{ AccountHeader, AccountId, AdviceInputs, - BlockHeader, InputNote, InputNotes, NoteId, @@ -16,7 +15,7 @@ use super::{ }; use crate::account::PartialAccount; use crate::asset::FungibleAsset; -use crate::block::BlockNumber; +use crate::block::{BlockHeader, BlockNumber}; use crate::transaction::TransactionInputs; use crate::utils::serde::{ ByteReader, diff --git a/crates/miden-objects/src/transaction/mod.rs b/crates/miden-objects/src/transaction/mod.rs index b11954de1a..916275abd2 100644 --- a/crates/miden-objects/src/transaction/mod.rs +++ b/crates/miden-objects/src/transaction/mod.rs @@ -1,5 +1,4 @@ use super::account::{AccountDelta, AccountHeader, AccountId}; -use super::block::BlockHeader; use super::note::{NoteId, Nullifier}; use super::vm::AdviceInputs; use super::{Felt, Hasher, WORD_SIZE, Word, ZERO}; diff --git a/crates/miden-objects/src/transaction/partial_blockchain.rs b/crates/miden-objects/src/transaction/partial_blockchain.rs index c91b98174a..4fcf3d0d94 100644 --- a/crates/miden-objects/src/transaction/partial_blockchain.rs +++ b/crates/miden-objects/src/transaction/partial_blockchain.rs @@ -28,9 +28,9 @@ use crate::utils::serde::{Deserializable, Serializable}; /// /// # Guarantees /// -/// The [`PartialBlockchain`] contains the full authenticated [`BlockHeader`]s of all blocks it -/// tracks in its partial MMR and users of this type can make this assumption. This is ensured when -/// using [`PartialBlockchain::new`]. [`PartialBlockchain::new_unchecked`] should only be used +/// The [`PartialBlockchain`] contains the full authenticated [`BlockHeader`]s of all blocks +/// it tracks in its partial MMR and users of this type can make this assumption. This is ensured +/// when using [`PartialBlockchain::new`]. [`PartialBlockchain::new_unchecked`] should only be used /// whenever this guarantee can be upheld. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PartialBlockchain { @@ -277,10 +277,13 @@ impl Default for PartialBlockchain { mod tests { use assert_matches::assert_matches; use miden_core::utils::{Deserializable, Serializable}; + use rand::SeedableRng; + use rand_chacha::ChaCha20Rng; use super::PartialBlockchain; use crate::alloc::vec::Vec; use crate::block::{BlockHeader, BlockNumber, FeeParameters}; + use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey; use crate::crypto::merkle::{Mmr, PartialMmr}; use crate::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; use crate::{PartialBlockchainError, Word}; @@ -426,6 +429,8 @@ mod tests { let fee_parameters = FeeParameters::new(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET.try_into().unwrap(), 500) .expect("native asset ID should be a fungible faucet ID"); + let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + let validator_key = SecretKey::with_rng(&mut rng).public_key(); BlockHeader::new( 0, @@ -437,7 +442,7 @@ mod tests { Word::empty(), Word::empty(), Word::empty(), - Word::empty(), + validator_key, fee_parameters, 0, ) diff --git a/crates/miden-testing/Cargo.toml b/crates/miden-testing/Cargo.toml index add802c7f4..4dcdc3cf2c 100644 --- a/crates/miden-testing/Cargo.toml +++ b/crates/miden-testing/Cargo.toml @@ -30,7 +30,7 @@ miden-processor = { workspace = true } anyhow = { workspace = true } itertools = { default-features = false, features = ["use_alloc"], version = "0.14" } rand = { features = ["os_rng", "small_rng"], workspace = true } -rand_chacha = { default-features = false, version = "0.9" } +rand_chacha = { workspace = true } winterfell = { version = "0.13" } [dev-dependencies] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 4fac3a2517..932e0e5208 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -53,12 +53,12 @@ use miden_lib::transaction::memory::{ PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR, PARTIAL_BLOCKCHAIN_PEAKS_PTR, PREV_BLOCK_COMMITMENT_PTR, - PROOF_COMMITMENT_PTR, PROTOCOL_VERSION_IDX, TIMESTAMP_IDX, TX_COMMITMENT_PTR, TX_KERNEL_COMMITMENT_PTR, TX_SCRIPT_ROOT_PTR, + VALIDATOR_KEY_COMMITMENT_PTR, VERIFICATION_BASE_FEE_IDX, }; use miden_objects::account::{ @@ -266,9 +266,9 @@ fn block_data_memory_assertions(exec_output: &ExecutionOutput, inputs: &Transact ); assert_eq!( - exec_output.get_kernel_mem_word(PROOF_COMMITMENT_PTR), - inputs.tx_inputs().block_header().proof_commitment(), - "The proof commitment should be stored at the PROOF_COMMITMENT_PTR" + exec_output.get_kernel_mem_word(VALIDATOR_KEY_COMMITMENT_PTR), + inputs.tx_inputs().block_header().validator_key().to_commitment(), + "The public key commitment should be stored at the VALIDATOR_KEY_COMMITMENT_PTR" ); assert_eq!( diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index 47dc10c0eb..a84d468a5c 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -21,6 +21,7 @@ use miden_objects::block::{ ProposedBlock, ProvenBlock, }; +use miden_objects::crypto::dsa::ecdsa_k256_keccak::SecretKey; use miden_objects::note::{Note, NoteHeader, NoteId, NoteInclusionProof, Nullifier}; use miden_objects::transaction::{ ExecutedTransaction, @@ -185,6 +186,9 @@ pub struct MockChain { /// AccountId |-> AccountAuthenticator mapping to store the authenticator for accounts to /// simplify transaction creation. account_authenticators: BTreeMap, + + /// Validator secret key used for signing blocks. + validator_secret_key: SecretKey, } impl MockChain { @@ -216,6 +220,7 @@ impl MockChain { genesis_block: ProvenBlock, account_tree: AccountTree, account_authenticators: BTreeMap, + secret_key: SecretKey, ) -> anyhow::Result { let mut chain = MockChain { chain: Blockchain::default(), @@ -226,6 +231,7 @@ impl MockChain { committed_notes: BTreeMap::new(), committed_accounts: BTreeMap::new(), account_authenticators, + validator_secret_key: secret_key, }; // We do not have to apply the tree changes, because the account tree is already initialized @@ -372,6 +378,13 @@ impl MockChain { self.blocks[chain_tip.as_usize()].header().clone() } + /// Returns the latest [`ProvenBlock`] in the chain. + pub fn latest_block(&self) -> ProvenBlock { + let chain_tip = + self.chain.chain_tip().expect("chain should contain at least the genesis block"); + self.blocks[chain_tip.as_usize()].clone() + } + /// Returns the [`BlockHeader`] with the specified `block_number`. /// /// # Panics @@ -982,7 +995,8 @@ impl MockChain { header.clone(), inputs, )?; - Ok(ProvenBlock::new_unchecked(header, body, block_proof)) + let signature = self.validator_secret_key.sign(header.commitment()); + Ok(ProvenBlock::new_unchecked(header, body, signature, block_proof)) } } @@ -1005,6 +1019,7 @@ impl Serializable for MockChain { self.committed_accounts.write_into(target); self.committed_notes.write_into(target); self.account_authenticators.write_into(target); + self.validator_secret_key.write_into(target); } } @@ -1019,6 +1034,7 @@ impl Deserializable for MockChain { let committed_notes = BTreeMap::::read_from(source)?; let account_authenticators = BTreeMap::::read_from(source)?; + let secret_key = SecretKey::read_from(source)?; Ok(Self { chain, @@ -1029,6 +1045,7 @@ impl Deserializable for MockChain { committed_notes, committed_accounts, account_authenticators, + validator_secret_key: secret_key, }) } } @@ -1260,4 +1277,36 @@ mod tests { assert_eq!(chain.committed_notes, deserialized.committed_notes); assert_eq!(chain.account_authenticators, deserialized.account_authenticators); } + + #[test] + fn mock_chain_block_signature() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + builder.add_existing_mock_account(Auth::IncrNonce)?; + let mut chain = builder.build()?; + + // Verify the genesis block signature. + let genesis_block = chain.latest_block(); + assert!( + genesis_block.signature().verify( + genesis_block.header().commitment(), + genesis_block.header().validator_key() + ) + ); + + // Add another block. + chain.prove_next_block()?; + + // Verify the next block signature. + let next_block = chain.latest_block(); + assert!( + next_block + .signature() + .verify(next_block.header().commitment(), next_block.header().validator_key()) + ); + + // Public keys should be carried through from the genesis header to the next. + assert_eq!(next_block.header().validator_key(), next_block.header().validator_key()); + + Ok(()) + } } diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 8938ccfd79..bb42f59678 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -44,9 +44,11 @@ use miden_objects::block::{ OutputNoteBatch, ProvenBlock, }; +use miden_objects::crypto::dsa::ecdsa_k256_keccak::SecretKey; use miden_objects::crypto::merkle::Smt; use miden_objects::note::{Note, NoteDetails, NoteType}; use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; +use miden_objects::testing::random_signer::RandomBlockSigner; use miden_objects::transaction::{OrderedTransactionHeaders, OutputNote}; use miden_objects::{Felt, FieldElement, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; use miden_processor::crypto::RpoRandomCoin; @@ -217,10 +219,11 @@ impl MockChainBuilder { let note_root = note_tree.root(); let tx_commitment = transactions.commitment(); let tx_kernel_commitment = TransactionKernel.to_commitment(); - let proof_commitment = Word::empty(); let timestamp = MockChain::TIMESTAMP_START_SECS; let fee_parameters = FeeParameters::new(self.native_asset_id, self.verification_base_fee) .context("failed to construct fee parameters")?; + let validator_secret_key = SecretKey::random(); + let validator_public_key = validator_secret_key.public_key(); let header = BlockHeader::new( version, @@ -232,7 +235,7 @@ impl MockChainBuilder { note_root, tx_commitment, tx_kernel_commitment, - proof_commitment, + validator_public_key, fee_parameters, timestamp, ); @@ -244,10 +247,16 @@ impl MockChainBuilder { transactions, ); + let signature = validator_secret_key.sign(header.commitment()); let block_proof = BlockProof::new_dummy(); - let genesis_block = ProvenBlock::new_unchecked(header, body, block_proof); + let genesis_block = ProvenBlock::new_unchecked(header, body, signature, block_proof); - MockChain::from_genesis_block(genesis_block, account_tree, self.account_authenticators) + MockChain::from_genesis_block( + genesis_block, + account_tree, + self.account_authenticators, + validator_secret_key, + ) } // ACCOUNT METHODS From 10b213c9309663921d1a64cf96b50a519e16ad04 Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 11 Dec 2025 07:47:06 +0200 Subject: [PATCH 035/114] chore: small fixes to external docs (#2155) --- docs/src/account/address.md | 10 +++++----- docs/src/index.md | 2 +- docs/src/state.md | 2 +- docs/src/transaction.md | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/account/address.md b/docs/src/account/address.md index 1287ffcedf..fe979e730e 100644 --- a/docs/src/account/address.md +++ b/docs/src/account/address.md @@ -17,11 +17,11 @@ The receiver can choose to disclose various pieces of information that control h Consider a few examples that use different address mechanisms: - The [Pay-to-ID note](../note#p2id-pay-to-id): the note itself can only be consumed if the account ID encoded in the note details matches the ID of the account that tries to consume it. To receive a P2ID note, the receiver should communicate an `AddressId::AccountId` type to the sender. -- A "Pay-to-PoW" note that can only be consumed if the receiver can provide a valid seed such that the hash of the seed results in a value with n leading zero bits. The receiver communicates an `AddressId::PoW` type to the sender, which encodes the target number of leading zero bits (and a salt to avoid re-use of the same seed).* -- A "Pay-to-Public-Key" note that stores a public (signature) key and checks if the receiver can provide a valid cryptographic signature for that key. The `AddressId::PublicKey` type must encode the public key.* +- A "Pay-to-PoW" note that can only be consumed if the receiver can provide a valid seed such that the hash of the seed results in a value with n leading zero bits. The receiver communicates an `AddressId::PoW` type to the sender, which encodes the target number of leading zero bits (and a salt to avoid re-use of the same seed). +- A "Pay-to-Public-Key" note that stores a public (signature) key and checks if the receiver can provide a valid cryptographic signature for that key. The `AddressId::PublicKey` type must encode the public key. These different address mechanisms provide different levels of privacy and security: -- `AddressId::AccountId`: the receiver is uniquely identifiable, but they are the only ones who can consume the note. +- `AddressId::AccountId`: the receiver is uniquely identifiable, but they are the only one who can consume the note. - `AddressId::PoW`: the receiver is not revealed publicly, but potentially many entities can consume the note. The receiver has an advantage by specifying the salt. - `AddressId::PublicKey`: the receiver `AccountId` is not revealed publicly, only their public key. A fresh `AddressId::PublicKey` can be used for receiving each note, resulting in increased privacy. @@ -35,13 +35,13 @@ For notes which are sent privately, the sender needs to communicate the full not Instead, our Miden client connects to a _Note Transport Layer_, which stores encrypted note details together with the associated public metadata for each note. The receiver can query the Note Transport Layer for `NoteTag`s they are interested in. Typically, a `NoteTag` encodes a few leading bits (14 by default) of the receiver's `AccountId`. Querying the Note Transport Layer for 14-bit `NoteTag`s reduces the receiver's privacy, but at the same time allows them to perform less work downloading and trial-decrypting the notes than if fewer bits were encoded. -With an `Address`, e.g. the [`AddressId::AccountId`](./address#addressaccountid) variant, the receiver could specify how many bits of their `AccountId` they want to disclose to the sender and thus choose their level of privacy. +With an `Address`, e.g. the [`AddressId::AccountId`](./address#address-types) variant, the receiver could specify how many bits of their `AccountId` they want to disclose to the sender and thus choose their level of privacy. ### Account interface discovery An address allows the sender of the note to easily discover the interface of the receiving account. As explained in the [account interface](./code#interface) section, every account can have a different set of procedures that note scripts can call, which is the _interface_ of the account. In order for the sender of a note to create a note that the receiver can consume, the sender needs to know the interface of the receiving account. This can be communicated via the address, which encodes a mapping of standard interfaces like the basic wallet. -If a sender wants to create a note, it is up to them to check whether the receiver account has an interface that it compatible with that note. The notion of an address doesn't exist at protocol level and so it is up to wallets or clients to implement this interface compatibility check. +If a sender wants to create a note, it is up to them to check whether the receiver account has an interface that is compatible with that note. The notion of an address doesn't exist at protocol level and so it is up to wallets or clients to implement this interface compatibility check. ### Note encryption diff --git a/docs/src/index.md b/docs/src/index.md index 947d63c334..bf7c554b86 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -41,7 +41,7 @@ An [Asset](asset) can be fungible and non-fungible. They are stored in the owner ### Transactions -A [Transactions](transaction) describe the production and consumption of notes by a single account. +A [Transaction](transaction) describes the production and consumption of notes by a single account. Executing a transaction always results in a STARK proof. diff --git a/docs/src/state.md b/docs/src/state.md index 6257959f54..02233cf6d3 100644 --- a/docs/src/state.md +++ b/docs/src/state.md @@ -49,7 +49,7 @@ This is done using an authenticated data structure, a sparse Merkle tree. As described in the [account ID section](account/id#account-storage-mode), accounts can have different storage modes: - **Public & Network accounts:** where all account data is stored on-chain. -- **Private accounts:** where only the commitments to the account is stored on-chain. +- **Private accounts:** where only the commitments to the account are stored on-chain. Private accounts significantly reduce storage overhead. A private account contributes only 40 bytes to the global `State` (15 bytes for the account ID + 32 bytes for the account commitment + 4 bytes for the block number). For example, 1 billion private accounts take up only 47.47 GB of `State`. diff --git a/docs/src/transaction.md b/docs/src/transaction.md index 72fe743cf5..6ac6aba0bc 100644 --- a/docs/src/transaction.md +++ b/docs/src/transaction.md @@ -68,7 +68,7 @@ Let's assume account A wants to create a P2ID note. P2ID notes are pay-to-ID not In this example, account A uses the basic wallet and the authentication component provided by `miden-lib`. The basic wallet component defines the methods `wallets::basic::create_note` and `wallets::basic::move_asset_to_note` to create notes with assets, and `wallets::basic::receive_asset` to receive assets. The authentication component exposes `auth::basic::auth_tx_rpo_falcon512` which allows for signing a transaction. Some account methods like `active_account::get_id` are always exposed. -The executor inputs to the Miden VM a `Transaction` script in which he places on the stack the data (tag, aux, note_type, execution_hint, RECIPIENT) of the note(s) that he wants to create using `wallets::basic::create_note` during the said `Transaction`. The [`NoteRecipient`](https://github.com/0xMiden/miden-base/blob/main/crates/miden-objects/src/note/recipient.rs) is a value that describes under which condition a note can be consumed and is built using a `serial_number`, the `note_script` (in this case P2ID script) and the `note_inputs`. The Miden VM will execute the `Transaction` script and create the note(s). After having been created, the executor can use `wallets::basic::move_asset_to_note` to move assets from the account's vault to the notes vault. +The executor inputs to the Miden VM a `Transaction` script in which it places on the stack the data (tag, aux, note_type, execution_hint, RECIPIENT) of the note(s) that it wants to create using `wallets::basic::create_note` during the said `Transaction`. The [`NoteRecipient`](https://github.com/0xMiden/miden-base/blob/main/crates/miden-objects/src/note/recipient.rs) is a value that describes under which condition a note can be consumed and is built using a `serial_number`, the `note_script` (in this case P2ID script) and the `note_inputs`. The Miden VM will execute the `Transaction` script and create the note(s). After having been created, the executor can use `wallets::basic::move_asset_to_note` to move assets from the account's vault to the notes vault. After finalizing the `Transaction` the updated state and created note(s) can now be submitted to the Miden operator to be recorded on-chain. @@ -128,7 +128,7 @@ The ability to facilitate both, local and network transactions, **is one of the - Not all transactions require notes. For example, the owner of a faucet can mint new tokens using only a `Transaction` script, without interacting with external notes. -- In Miden executors can choose arbitrary reference blocks to execute against their state. Hence it is possible to set `Transaction` expiration heights and in doing so, to define a block height until a `Transaction` should be included into a block. If the `Transaction` is expired, the resulting account state change is not valid and the `Transaction` cannot be verified anymore. +- In Miden, executors can choose arbitrary reference blocks to execute against their state. Hence it is possible to set `Transaction` expiration heights and in doing so, to define a block height until a `Transaction` should be included into a block. If the `Transaction` is expired, the resulting account state change is not valid and the `Transaction` cannot be verified anymore. - Note and `Transaction` scripts can read the state of foreign accounts during execution. This is called foreign procedure invocation. For example, the price of an asset for the **Swap** script might depend on a certain value stored in the oracle account. From 4b08b9c916bb3a1ecc7e509d51564e9860c1bbc8 Mon Sep 17 00:00:00 2001 From: Varun Doshi Date: Thu, 11 Dec 2025 15:21:50 +0530 Subject: [PATCH 036/114] feat: Add ability to get full public key from authenticator (#2145) * feat: Add ability to get full public key from authenticator * fix: update BasicAuthenticator to cache pubkey * fix: add changelog * fix: nits * Update CHANGELOG.md --------- Co-authored-by: Marti --- CHANGELOG.md | 1 + crates/miden-testing/src/mock_chain/chain.rs | 6 +-- crates/miden-tx/src/auth/tx_authenticator.rs | 57 ++++++++++++++++---- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b349c3b547..0318f4d996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). - [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). +Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)). ### Changes diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index a84d468a5c..21aaa14848 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -5,7 +5,7 @@ use anyhow::Context; use miden_block_prover::LocalBlockProver; use miden_lib::block::build_block; use miden_objects::MIN_PROOF_SECURITY_LEVEL; -use miden_objects::account::auth::AuthSecretKey; +use miden_objects::account::auth::{AuthSecretKey, PublicKey}; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{Account, AccountId, PartialAccount}; use miden_objects::batch::{ProposedBatch, ProvenBatch}; @@ -1105,9 +1105,9 @@ impl Serializable for AccountAuthenticator { impl Deserializable for AccountAuthenticator { fn read_from(source: &mut R) -> Result { - let authenticator = Option::>::read_from(source)?; + let authenticator = Option::>::read_from(source)?; - let authenticator = authenticator.map(|keys| BasicAuthenticator::new(&keys)); + let authenticator = authenticator.map(|keys| BasicAuthenticator::from_key_pairs(&keys)); Ok(Self { authenticator }) } diff --git a/crates/miden-tx/src/auth/tx_authenticator.rs b/crates/miden-tx/src/auth/tx_authenticator.rs index 848bcf2b21..74fc46bd77 100644 --- a/crates/miden-tx/src/auth/tx_authenticator.rs +++ b/crates/miden-tx/src/auth/tx_authenticator.rs @@ -3,7 +3,7 @@ use alloc::collections::BTreeMap; use alloc::string::ToString; use alloc::vec::Vec; -use miden_objects::account::auth::{AuthSecretKey, PublicKeyCommitment, Signature}; +use miden_objects::account::auth::{AuthSecretKey, PublicKey, PublicKeyCommitment, Signature}; use miden_objects::crypto::SequentialCommit; use miden_objects::transaction::TransactionSummary; use miden_objects::{Felt, Hasher, Word}; @@ -139,6 +139,12 @@ pub trait TransactionAuthenticator { pub_key_commitment: PublicKeyCommitment, signing_inputs: &SigningInputs, ) -> impl FutureMaybeSend>; + + /// Retrieves a public key for a specific public key commitment. + fn get_public_key( + &self, + pub_key_commitment: PublicKeyCommitment, + ) -> impl FutureMaybeSend>; } /// A placeholder type for the generic trait bound of `TransactionAuthenticator<'_,'_,_,T>` @@ -160,6 +166,13 @@ impl TransactionAuthenticator for UnreachableAuth { ) -> impl FutureMaybeSend> { async { unreachable!("Type `UnreachableAuth` must not be instantiated") } } + + fn get_public_key( + &self, + _pub_key_commitment: PublicKeyCommitment, + ) -> impl FutureMaybeSend> { + async { unreachable!("Type `UnreachableAuth` must not be instantiated") } + } } // BASIC AUTHENTICATOR @@ -168,16 +181,25 @@ impl TransactionAuthenticator for UnreachableAuth { /// Represents a signer for [AuthSecretKey] keys. #[derive(Clone, Debug)] pub struct BasicAuthenticator { - /// pub_key |-> secret_key mapping - keys: BTreeMap, + /// pub_key |-> (secret_key, public_key) mapping + keys: BTreeMap, } impl BasicAuthenticator { pub fn new(keys: &[AuthSecretKey]) -> Self { let mut key_map = BTreeMap::new(); for secret_key in keys { - let pub_key = secret_key.public_key().to_commitment(); - key_map.insert(pub_key, secret_key.clone()); + let pub_key = secret_key.public_key(); + key_map.insert(pub_key.to_commitment(), (secret_key.clone(), pub_key)); + } + + BasicAuthenticator { keys: key_map } + } + + pub fn from_key_pairs(keys: &[(AuthSecretKey, PublicKey)]) -> Self { + let mut key_map = BTreeMap::new(); + for (secret_key, public_key) in keys { + key_map.insert(public_key.to_commitment(), (secret_key.clone(), public_key.clone())); } BasicAuthenticator { keys: key_map } @@ -185,9 +207,9 @@ impl BasicAuthenticator { /// Returns a reference to the keys map. /// - /// Map keys represent the public key commitments, and values represent the secret keys that - /// the authenticator would use to sign messages. - pub fn keys(&self) -> &BTreeMap { + /// Map keys represent the public key commitments, and values represent the (secret_key, + /// public_key) pair that the authenticator would use to sign messages. + pub fn keys(&self) -> &BTreeMap { &self.keys } } @@ -213,11 +235,21 @@ impl TransactionAuthenticator for BasicAuthenticator { async move { match self.keys.get(&pub_key_commitment) { - Some(key) => Ok(key.sign(message)), + Some((auth_key, _)) => Ok(auth_key.sign(message)), None => Err(AuthenticationError::UnknownPublicKey(pub_key_commitment)), } } } + + /// Returns the public key associated with the given public key commitment. + /// + /// If the public key commitment is not contained in the `keys` map, `None` is returned. + fn get_public_key( + &self, + pub_key_commitment: PublicKeyCommitment, + ) -> impl FutureMaybeSend> { + async move { self.keys.get(&pub_key_commitment).map(|(_, pub_key)| pub_key) } + } } // HELPER FUNCTIONS @@ -236,6 +268,13 @@ impl TransactionAuthenticator for () { )) } } + + fn get_public_key( + &self, + _pub_key_commitment: PublicKeyCommitment, + ) -> impl FutureMaybeSend> { + async { None } + } } #[cfg(test)] From cdc1dc206cfa24b06a3484b56717a21ecbddf868 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 12 Dec 2025 08:32:05 +0700 Subject: [PATCH 037/114] feat: refactor `AccountProcedureIndexMap` constructor to be infallible (#2163) * feat: make account procedure idx map constructor infallible * chore: add changelog --- CHANGELOG.md | 4 ++++ .../miden-testing/src/tx_context/context.rs | 3 +-- crates/miden-tx/src/errors/mod.rs | 17 ------------- crates/miden-tx/src/executor/exec_host.rs | 12 +--------- crates/miden-tx/src/executor/mod.rs | 3 +-- .../miden-tx/src/host/account_procedures.rs | 24 +++++++------------ crates/miden-tx/src/host/mod.rs | 7 ++---- crates/miden-tx/src/prover/mod.rs | 3 +-- 8 files changed, 19 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0318f4d996..6c842b4dc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987)). - [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153)). +### Changes + +- [BREAKING] Make `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). + ## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). - [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index 9efdc4b408..2d3f443c59 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -144,8 +144,7 @@ impl TransactionContext { [tx_inputs.account().code()] .into_iter() .chain(self.foreign_account_inputs.values().map(|(account, _)| account.code())), - ) - .expect("constructing account procedure index map should work"); + ); // The ref block is unimportant when using execute_code so we can set it to any value. let ref_block = tx_inputs.block_header().block_num(); diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index e253ea5988..dd762d6c3a 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -119,8 +119,6 @@ pub enum TransactionExecutorError { "input note {0} was created in a block past the transaction reference block number ({1})" )] NoteBlockPastReferenceBlock(NoteId, BlockNumber), - #[error("failed to create transaction host")] - TransactionHostCreationFailed(#[source] TransactionHostError), #[error("failed to construct transaction outputs")] TransactionOutputConstructionFailed(#[source] TransactionOutputError), // Print the diagnostic directly instead of returning the source error. In the source error @@ -154,10 +152,6 @@ pub enum TransactionProverError { // case, the diagnostic is lost if the execution error is not explicitly unwrapped. #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))] TransactionProgramExecutionFailed(ExecutionError), - #[error("failed to create account procedure index map")] - CreateAccountProcedureIndexMap(#[source] TransactionHostError), - #[error("failed to create transaction host")] - TransactionHostCreationFailed(#[source] TransactionHostError), /// Custom error variant for errors not covered by the other variants. #[error("{error_msg}")] Other { @@ -200,17 +194,6 @@ pub enum TransactionVerifierError { InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 }, } -// TRANSACTION HOST ERROR -// ================================================================================================ - -#[derive(Debug, Error)] -pub enum TransactionHostError { - #[error("{0}")] - AccountProcedureIndexMapError(String), - #[error("failed to create account procedure info")] - AccountProcedureInfoCreationFailed(#[source] AccountError), -} - // TRANSACTION KERNEL ERROR // ================================================================================================ diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 7203e96af0..77e0e9507a 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -158,17 +158,7 @@ where let mut tx_advice_inputs = TransactionAdviceInputs::default(); tx_advice_inputs.add_foreign_accounts([&foreign_account_inputs]); - self.base_host - .load_foreign_account_code(foreign_account_inputs.code()) - .map_err(|err| { - TransactionKernelError::other_with_source( - format!( - "failed to insert account procedures for foreign account {}", - foreign_account_inputs.id() - ), - err, - ) - })?; + self.base_host.load_foreign_account_code(foreign_account_inputs.code()); // Add the foreign account's code to the list of accessed code. self.accessed_foreign_account_code.push(foreign_account_inputs.code().clone()); diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 7178834dd1..4cce13bc29 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -315,8 +315,7 @@ where // To start executing the transaction, the procedure index map only needs to contain the // native account's procedures. Foreign accounts are inserted into the map on first access. let account_procedure_index_map = - AccountProcedureIndexMap::new([tx_inputs.account().code()]) - .map_err(TransactionExecutorError::TransactionHostCreationFailed)?; + AccountProcedureIndexMap::new([tx_inputs.account().code()]); let initial_fee_asset_balance = { let native_asset_id = tx_inputs.block_header().fee_parameters().native_asset_id(); diff --git a/crates/miden-tx/src/host/account_procedures.rs b/crates/miden-tx/src/host/account_procedures.rs index 18440e8284..5d5a302cab 100644 --- a/crates/miden-tx/src/host/account_procedures.rs +++ b/crates/miden-tx/src/host/account_procedures.rs @@ -1,7 +1,7 @@ use miden_objects::account::AccountCode; use super::{BTreeMap, Word}; -use crate::errors::{TransactionHostError, TransactionKernelError}; +use crate::errors::TransactionKernelError; // ACCOUNT PROCEDURE INDEX MAP // ================================================================================================ @@ -15,19 +15,17 @@ pub struct AccountProcedureIndexMap(BTreeMap>); impl AccountProcedureIndexMap { /// Returns a new [`AccountProcedureIndexMap`] instantiated with account procedures from the /// provided iterator of [`AccountCode`]. - pub fn new<'code>( - account_codes: impl IntoIterator, - ) -> Result { + pub fn new<'code>(account_codes: impl IntoIterator) -> Self { let mut index_map = Self::default(); for account_code in account_codes { // Insert each account procedures only once. if !index_map.0.contains_key(&account_code.commitment()) { - index_map.insert_code(account_code)?; + index_map.insert_code(account_code); } } - Ok(index_map) + index_map } /// Inserts the procedures from the provided [`AccountCode`] into the advice inputs, using @@ -36,21 +34,17 @@ impl AccountProcedureIndexMap { /// The resulting instance will map the account code commitment to a mapping of /// `proc_root |-> proc_index` for any account that is expected to be involved in the /// transaction, enabling fast procedure index lookups at runtime. - pub fn insert_code(&mut self, code: &AccountCode) -> Result<(), TransactionHostError> { + pub fn insert_code(&mut self, code: &AccountCode) { let mut procedure_map = BTreeMap::new(); for (proc_idx, proc_info) in code.procedures().iter().enumerate() { - let proc_idx = u8::try_from(proc_idx).map_err(|_| { - TransactionHostError::AccountProcedureIndexMapError( - "procedure index out of bounds".into(), - ) - })?; - + // SAFETY: AccountCode::MAX_NUM_PROCEDURES is 256 and so the highest possible index is + // 255. + let proc_idx = + u8::try_from(proc_idx).expect("account code should contain at most 256 procedures"); procedure_map.insert(*proc_info.mast_root(), proc_idx); } self.0.insert(code.commitment(), procedure_map); - - Ok(()) } /// Returns the index of the requested procedure root in the account code identified by the diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index 852564a695..fcd1b61236 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -63,7 +63,7 @@ use miden_processor::{ pub(crate) use tx_event::{RecipientData, TransactionEvent, TransactionProgressEvent}; pub use tx_progress::TransactionProgress; -use crate::errors::{TransactionHostError, TransactionKernelError}; +use crate::errors::TransactionKernelError; // TRANSACTION BASE HOST // ================================================================================================ @@ -256,10 +256,7 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { } /// Loads the provided [`AccountCode`] into the host's [`AccountProcedureIndexMap`]. - pub fn load_foreign_account_code( - &mut self, - account_code: &AccountCode, - ) -> Result<(), TransactionHostError> { + pub fn load_foreign_account_code(&mut self, account_code: &AccountCode) { self.acct_procedure_index_map.insert_code(account_code) } diff --git a/crates/miden-tx/src/prover/mod.rs b/crates/miden-tx/src/prover/mod.rs index ae7a9e65d0..332b3e84ab 100644 --- a/crates/miden-tx/src/prover/mod.rs +++ b/crates/miden-tx/src/prover/mod.rs @@ -113,8 +113,7 @@ impl LocalTransactionProver { let account_procedure_index_map = AccountProcedureIndexMap::new( tx_inputs.foreign_account_code().iter().chain([tx_inputs.account().code()]), - ) - .map_err(TransactionProverError::CreateAccountProcedureIndexMap)?; + ); let (partial_account, ref_block, _, input_notes, _) = tx_inputs.into_parts(); let mut host = TransactionProverHost::new( From 4879303f5407ebddb464031df6c8e98374dc0306 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 12 Dec 2025 09:01:44 +0700 Subject: [PATCH 038/114] feat: enforce sorted and unique storage slots (#2154) * feat: Check for duplicate slots after sorting * feat: Check for sorting and uniqueness in tx kernel * chore: add changelog * chore: cover special cases in slot ID comparison in test * chore: remove implemented todo * Update crates/miden-objects/src/account/storage/mod.rs * fix: remove duplicate changelog entry --------- Co-authored-by: Marti --- CHANGELOG.md | 3 +- .../asm/kernels/transaction/lib/account.masm | 102 ++++++++++++++++++ .../asm/kernels/transaction/lib/prologue.masm | 6 +- .../miden-lib/src/errors/tx_kernel_errors.rs | 2 + .../src/account/storage/header.rs | 9 ++ .../miden-objects/src/account/storage/mod.rs | 61 ++++++++--- .../src/kernel_tests/tx/test_account.rs | 63 +++++++++++ .../src/host/storage_delta_tracker.rs | 8 +- 8 files changed, 234 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c842b4dc2..9641d5abf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,7 @@ ### Features -- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987)). -- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153)). +- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154)). ### Changes diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index 6c92626e0f..78ff1e08ac 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -33,6 +33,8 @@ const.ERR_ACCOUNT_PROC_NOT_AUTH_PROC="account procedure is not the authenticatio # TODO(named_slots): Remove along with index APIs. const.ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS="provided storage slot index is out of bounds" +const.ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE="slot IDs must be unique and sorted in ascending order" + const.ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" const.ERR_FAUCET_INVALID_STORAGE_OFFSET="storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets)" @@ -1091,6 +1093,106 @@ export.validate_seed # => [] end +#! Validates that slot IDs are sorted in ascending order and that slot IDs are unique. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Pancis if: +#! - each slot's ID is not strictly less than the next slot's ID. +#! - this ensures sorting and uniqueness among slot IDs. +pub proc validate_storage + exec.memory::get_num_storage_slots + # => [num_slots] + + # compute the number of slot ID comparisons we need to make + # generally, we need num_storage_slots - 1 comparisons, e.g., if we have 3 slots, we need 2 + # comparisons: 2 with 1 and 1 with 0. + # we subtract 1 if any slots exist and 0 otherwise, notably: + # maps 1 storage slot -> 0 comparisons, since 1 slot is always sorted and unique + # maps 0 storage slots -> 0 comparisons + dup u32gt.0 + # => [has_slots, num_slots] + + sub + # => [num_comparisons] + + # loop if we need to compare slots + dup neq.0 + # => [should_loop, num_comparisons] + + while.true + # first iteration: number of comparisons = current slot index + # => [curr_slot_idx] + + dup exec.get_slot_id + # => [curr_slot_id_prefix, curr_slot_id_suffix, curr_slot_idx] + + # we are guaranteed to not underflow because curr_slot_idx is at least 1 at the + # beginning of the loop + dup.2 sub.1 + # => [prev_slot_idx, curr_slot_id_prefix, curr_slot_id_suffix, curr_slot_idx] + + exec.get_slot_id + # => [prev_slot_id_prefix, prev_slot_id_suffix, curr_slot_id_prefix, curr_slot_id_suffix, curr_slot_idx] + + # this effectively checks that slots are sorted _and_ unique, since duplicate slot IDs are + # not less than each other + exec.is_slot_id_lt + # => [is_prev_lt_curr, curr_slot_idx] + + assert.err=ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE + # => [curr_slot_idx] + + sub.1 dup neq.0 + # => [should_continue, prev_slot_idx] + end + # => [prev_slot_idx] + + drop + # => [] +end + +#! Returns 1 if the previous slot ID is smaller than the current slot ID, 0 otherwise. +#! +#! In the slot ID comparison, the prefix takes precedence over the suffix. +#! +#! This procedure is public so it can be tested. +#! +#! Inputs: [prev_slot_id_prefix, prev_slot_id_suffix, curr_slot_id_prefix, curr_slot_id_suffix] +#! Outputs: [is_prev_lt_curr] +pub proc is_slot_id_lt + movup.2 + # => [curr_slot_id_prefix, prev_slot_id_prefix, prev_slot_id_suffix, curr_slot_id_suffix] + + # compute prev == curr for prefix + dup dup.2 eq + # => [is_prefix_eq, curr_slot_id_prefix, prev_slot_id_prefix, prev_slot_id_suffix, curr_slot_id_suffix] + + movdn.4 + # => [curr_slot_id_prefix, prev_slot_id_prefix, prev_slot_id_suffix, curr_slot_id_suffix, is_prefix_eq] + + # compute prev < curr for prefix + lt + # => [is_prev_lt_curr_prefix, prev_slot_id_suffix, curr_slot_id_suffix, is_prefix_eq] + + swap.2 + # => [curr_slot_id_suffix, prev_slot_id_suffix, is_prev_lt_curr_prefix, is_prefix_eq] + + # compute prev < curr for suffix + lt + # => [is_prev_lt_curr_suffix, is_prev_lt_curr_prefix, is_prefix_eq] + + movup.2 + # => [is_prefix_eq, is_prev_lt_curr_suffix, is_prev_lt_curr_prefix] + + # compute result as is_prefix_lt || (is_suffix_lt && is_prefix_eq) + # is_suffix_lt only affects the result if the prefix was equal, otherwise the prefix + # determines the outcome + and or + # => [is_prev_lt_curr] +end + # DATA LOADERS # ------------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index 5c0b7030c4..e7b5c9ad89 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -276,7 +276,6 @@ end # ACCOUNT DATA # ================================================================================================= -#! TODO(named_slots): Validate that slot names are unique. #! This should only be necessary whenever the slots of an account "change", which is essentially #! the case when it is created, or when slots are added or removed (currently unimplemented). #! So, it should be sufficient to do this here instead of in save_account_storage_data. @@ -363,6 +362,11 @@ proc.validate_new_account exec.account::validate_seed # => [] + # Assert the storage slots are sorted and unique. + # --------------------------------------------------------------------------------------------- + exec.account::validate_storage + # => [] + # Assert the provided procedures offsets and sizes satisfy storage requirements # --------------------------------------------------------------------------------------------- exec.account::validate_procedure_metadata diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index 44443d4fd4..d485cdf656 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -56,6 +56,8 @@ pub const ERR_ACCOUNT_STACK_UNDERFLOW: MasmError = MasmError::from_static_str("f pub const ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH: MasmError = MasmError::from_static_str("computed account storage commitment does not match recorded account storage commitment"); /// Error Message: "storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to" pub const ERR_ACCOUNT_STORAGE_MAP_ENTRIES_DO_NOT_MATCH_MAP_ROOT: MasmError = MasmError::from_static_str("storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to"); +/// Error Message: "slot IDs must be unique and sorted in ascending order" +pub const ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE: MasmError = MasmError::from_static_str("slot IDs must be unique and sorted in ascending order"); /// Error Message: "provided storage slot index is out of bounds" pub const ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("provided storage slot index is out of bounds"); /// Error Message: "number of account procedures exceeds the maximum limit of 256" diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-objects/src/account/storage/header.rs index 8dd504a485..1e43c35ac0 100644 --- a/crates/miden-objects/src/account/storage/header.rs +++ b/crates/miden-objects/src/account/storage/header.rs @@ -76,6 +76,7 @@ impl AccountStorageHeader { /// Returns an error if: /// - The number of provided slots is greater than [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. /// - The slots are not sorted by [`StorageSlotId`]. + /// - There are multiple storage slots with the same [`StorageSlotName`]. pub fn new(slots: Vec<(StorageSlotName, StorageSlotType, Word)>) -> Result { if slots.len() > AccountStorage::MAX_NUM_STORAGE_SLOTS { return Err(AccountError::StorageTooManySlots(slots.len() as u64)); @@ -85,6 +86,14 @@ impl AccountStorageHeader { return Err(AccountError::UnsortedStorageSlots); } + // Check for slot name uniqueness by checking each neighboring slot's IDs. This is + // sufficient because the slots are sorted. + for slots in slots.windows(2) { + if slots[0].0.id() == slots[1].0.id() { + return Err(AccountError::DuplicateStorageSlotName(slots[0].0.clone())); + } + } + Ok(Self { slots }) } diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index 7d66c46f21..0fc30d1996 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -1,4 +1,3 @@ -use alloc::collections::BTreeSet; use alloc::string::ToString; use alloc::vec::Vec; @@ -82,19 +81,17 @@ impl AccountStorage { return Err(AccountError::StorageTooManySlots(num_slots as u64)); } - // TODO(named_slots): Optimization: If we keep slots sorted, iterate slots instead and - // compare adjacent elements. - let mut names = BTreeSet::new(); - for slot in &slots { - if !names.insert(slot.name()) { - // TODO(named_slots): Add test for this new error. - return Err(AccountError::DuplicateStorageSlotName(slot.name().clone())); - } - } - // Unstable sort is fine because we require all names to be unique. slots.sort_unstable(); + // Check for slot name uniqueness by checking each neighboring slot's IDs. This is + // sufficient because the slots are sorted. + for slots in slots.windows(2) { + if slots[0].id() == slots[1].id() { + return Err(AccountError::DuplicateStorageSlotName(slots[0].name().clone())); + } + } + Ok(Self { slots }) } @@ -400,8 +397,11 @@ impl Deserializable for AccountStorage { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use super::{AccountStorage, Deserializable, Serializable}; - use crate::account::{StorageSlot, StorageSlotName}; + use crate::AccountError; + use crate::account::{AccountStorageHeader, StorageSlot, StorageSlotName}; #[test] fn test_serde_account_storage() -> anyhow::Result<()> { @@ -438,4 +438,41 @@ mod tests { Ok(()) } + + #[test] + fn test_account_storage_and_header_fail_on_duplicate_slot_name() -> anyhow::Result<()> { + let slot_name0 = StorageSlotName::mock(0); + let slot_name1 = StorageSlotName::mock(1); + let slot_name2 = StorageSlotName::mock(2); + + let mut slots = vec![ + StorageSlot::with_empty_value(slot_name0.clone()), + StorageSlot::with_empty_value(slot_name1.clone()), + StorageSlot::with_empty_map(slot_name0.clone()), + StorageSlot::with_empty_value(slot_name2.clone()), + ]; + + // Set up a test where the slots we pass are not already sorted + // This ensures the duplicate is correctly found + let err = AccountStorage::new(slots.clone()).unwrap_err(); + + assert_matches!(err, AccountError::DuplicateStorageSlotName(name) => { + assert_eq!(name, slot_name0); + }); + + slots.sort_unstable(); + let err = AccountStorageHeader::new( + slots + .iter() + .map(|slot| (slot.name().clone(), slot.slot_type(), slot.value())) + .collect(), + ) + .unwrap_err(); + + assert_matches!(err, AccountError::DuplicateStorageSlotName(name) => { + assert_eq!(name, slot_name0); + }); + + Ok(()) + } } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 8b922e8639..37aae419b8 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -28,6 +28,7 @@ use miden_objects::account::{ StorageMap, StorageSlot, StorageSlotContent, + StorageSlotId, StorageSlotName, StorageSlotType, }; @@ -524,6 +525,68 @@ async fn test_get_storage_slot_type() -> miette::Result<()> { Ok(()) } +#[tokio::test] +async fn test_is_slot_id_lt() -> miette::Result<()> { + // Note that the slot IDs derived from the names are essentially randomly sorted, so these cover + // "less than" and "greater than" outcomes. + let mut test_cases = (0..100) + .map(|i| { + let prev_slot = StorageSlotName::mock(i).id(); + let curr_slot = StorageSlotName::mock(i + 1).id(); + (prev_slot, curr_slot) + }) + .collect::>(); + + // Extend with special case where prefix matches and suffix determines the outcome. + let prefix = Felt::from(100u32); + test_cases.extend([ + // prev_slot == curr_slot + ( + StorageSlotId::new(Felt::from(50u32), prefix), + StorageSlotId::new(Felt::from(50u32), prefix), + ), + // prev_slot < curr_slot + ( + StorageSlotId::new(Felt::from(50u32), prefix), + StorageSlotId::new(Felt::from(51u32), prefix), + ), + // prev_slot > curr_slot + ( + StorageSlotId::new(Felt::from(51u32), prefix), + StorageSlotId::new(Felt::from(50u32), prefix), + ), + ]); + + for (prev_slot, curr_slot) in test_cases { + let code = format!( + r#" + use.$kernel::account + + begin + push.{curr_suffix}.{curr_prefix}.{prev_suffix}.{prev_prefix} + # => [prev_slot_id_prefix, prev_slot_id_suffix, curr_slot_id_prefix, curr_slot_id_suffix] + + exec.account::is_slot_id_lt + # => [is_slot_id_lt] + + push.{is_lt} + assert_eq.err="is_slot_id_lt was not {is_lt}" + # => [] + end + "#, + prev_prefix = prev_slot.prefix(), + prev_suffix = prev_slot.suffix(), + curr_prefix = curr_slot.prefix(), + curr_suffix = curr_slot.suffix(), + is_lt = u8::from(prev_slot < curr_slot) + ); + + CodeExecutor::with_default_host().run(&code).await?; + } + + Ok(()) +} + #[tokio::test] async fn test_set_item() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build().unwrap(); diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index 0067bd0bff..6a5dbe725a 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -192,7 +192,7 @@ impl StorageDeltaTracker { /// Creates empty slots of the same slot types as the to-be-created account. fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorageHeader { - let mut slots: Vec<(StorageSlotName, StorageSlotType, Word)> = account + let slots: Vec<(StorageSlotName, StorageSlotType, Word)> = account .storage() .header() .slots() @@ -202,9 +202,7 @@ fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorage }) .collect(); - slots.sort_by_key(|(slot_name, ..)| slot_name.id()); - - // SAFETY: We have sorted the slots and the max number of slots should not be exceeded as - // enforced by the storage header in partial storage. + // SAFETY: We are recreating a valid storage header with different values, which should not + // violate any constraints of the storage header. AccountStorageHeader::new(slots).expect("storage header should be valid") } From 5433d1fdc8c10e82f13ca9283f4c6bbfe6282470 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 12 Dec 2025 09:19:02 +0700 Subject: [PATCH 039/114] chore: use standard faucet metadata name for faucet components (#2161) * feat: Check for duplicate slots after sorting * feat: Check for sorting and uniqueness in tx kernel * chore: add changelog * chore: cover special cases in slot ID comparison in test * chore: remove implemented todo * feat: Add test for `ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME` * chore: rename faucet storage data slot to faucet metadata slot * chore: drop "name" from slot name getter * chore: unify basic/network faucet slot names * chore: add changelog * chore: change get_kernel_mem_element to use read_element * chore: Add test for accessing reserved slot * Update crates/miden-objects/src/account/storage/mod.rs * fix: remove duplicate changelog entry * chore: Move faucet slot name definition to faucet mod * chore: Rename faucet metadata to faucet sysdata * chore: make metadata specific to fungible faucets * fix: sysdata access test * fix: changelog entry * chore: remove duplicate test --------- Co-authored-by: Marti --- CHANGELOG.md | 2 +- .../asm/kernels/transaction/lib/account.masm | 12 +-- .../asm/kernels/transaction/lib/faucet.masm | 16 ++-- .../asm/kernels/transaction/lib/prologue.masm | 2 +- .../asm/miden/contracts/faucets/mod.masm | 6 +- .../src/account/faucets/basic_fungible.rs | 31 +++----- crates/miden-lib/src/account/faucets/mod.rs | 10 ++- .../src/account/faucets/network_fungible.rs | 7 +- crates/miden-lib/src/testing/mock_account.rs | 6 +- .../src/transaction/kernel_procedures.rs | 10 +-- .../miden-objects/src/account/storage/mod.rs | 19 ++--- .../src/account/storage/slot/slot_name.rs | 2 +- crates/miden-objects/src/errors.rs | 4 +- .../miden-testing/src/kernel_tests/tx/mod.rs | 20 ++--- .../src/kernel_tests/tx/test_account.rs | 78 +++++++++++++++++++ .../src/kernel_tests/tx/test_faucet.rs | 14 ++-- .../src/kernel_tests/tx/test_prologue.rs | 4 +- .../src/mock_chain/chain_builder.rs | 8 +- crates/miden-testing/tests/scripts/faucet.rs | 2 +- 19 files changed, 163 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9641d5abf6..5754a8827d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154)). +- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161)). ### Changes diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index 78ff1e08ac..58747967ae 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -69,8 +69,7 @@ const.ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account # The name of the account storage slot at which faucet data is stored. # Fungible faucet: The faucet data consists of [0, 0, 0, total_issuance] # Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. -# TODO(named_slots): Rename constant and procedure to FAUCET_METADATA_SLOT. -const.FAUCET_STORAGE_DATA_SLOT=word("miden::faucet::metadata") +const.FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") # The maximum storage slot index const.MAX_STORAGE_SLOT_INDEX=254 @@ -195,8 +194,8 @@ const.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_i #! Where: #! - faucet_slot_id{prefix,suffix} are the prefix and suffix felts of the slot identifier, at which #! faucet data is stored. -export.get_faucet_storage_slot_id - push.FAUCET_STORAGE_DATA_SLOT[0..2] +export.get_faucet_sysdata_slot_id + push.FAUCET_SYSDATA_SLOT[0..2] end #! Returns the maximum number of account storage slots. @@ -1568,7 +1567,7 @@ end #! - is_faucet_storage_data_slot is a boolean value indicating whether the provided slot is the #! reserved faucet data slot. export.is_faucet_storage_data_slot - exec.get_faucet_storage_slot_id + exec.get_faucet_sysdata_slot_id # => [faucet_slot_id_prefix, faucet_slot_id_suffix, slot_id_prefix, slot_id_suffix] movup.2 eq @@ -1848,9 +1847,6 @@ proc.find_storage_slot dup exec.memory::get_num_storage_slots mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH add # => [storage_slots_end_ptr, storage_slots_start_ptr, slot_id_prefix, slot_id_suffix] - # TODO(named_slots): Check if we can get here if num_storage_slots == 0. If so, return a better - # error than what find_key_value returns. - movdn.3 movdn.2 # => [slot_id_prefix, slot_id_suffix, storage_slots_start_ptr, storage_slots_end_ptr] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm b/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm index e2148bb2c7..ef55eba543 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm @@ -43,7 +43,7 @@ export.mint_fungible_asset # => [ASSET] # get the current total issuance - exec.account::get_faucet_storage_slot_id exec.account::get_item + exec.account::get_faucet_sysdata_slot_id exec.account::get_item # => [TOTAL_ISSUANCE, ASSET] # prepare stack to ensure that minting the asset will not exceed the maximum @@ -55,7 +55,7 @@ export.mint_fungible_asset # => [amount, TOTAL_ISSUANCE, ASSET] # update the total issuance - add exec.account::get_faucet_storage_slot_id exec.account::set_item dropw + add exec.account::get_faucet_sysdata_slot_id exec.account::set_item dropw # => [ASSET] # add the asset to the input vault for asset preservation checks @@ -85,7 +85,7 @@ proc.burn_fungible_asset # => [ASSET] # fetch TOTAL_ISSUANCE such that we can compute the new total issuance - exec.account::get_faucet_storage_slot_id exec.account::get_item + exec.account::get_faucet_sysdata_slot_id exec.account::get_item # => [TOTAL_ISSUANCE, ASSET] # assert that the asset amount being burned is less or equal to the total issuance @@ -93,7 +93,7 @@ proc.burn_fungible_asset # => [amount, TOTAL_ISSUANCE, ASSET] # compute new total issuance - sub exec.account::get_faucet_storage_slot_id exec.account::set_item dropw + sub exec.account::get_faucet_sysdata_slot_id exec.account::set_item dropw # => [ASSET] # remove the asset from the input vault @@ -111,7 +111,7 @@ end #! against. export.get_total_issuance # fetch the TOTAL_ISSUANCE from storage - exec.account::get_faucet_storage_slot_id exec.account::get_item + exec.account::get_faucet_sysdata_slot_id exec.account::get_item # => [TOTAL_ISSUANCE] # extract the total_issuance and purge the padding @@ -147,7 +147,7 @@ proc.mint_non_fungible_asset # => [ASSET_KEY, ASSET, ASSET] # get the faucet storage data slot - exec.account::get_faucet_storage_slot_id + exec.account::get_faucet_sysdata_slot_id # => [faucet_slot_name_prefix, faucet_slot_name_suffix, ASSET_KEY, ASSET, ASSET] # insert the non-fungible asset into the tracking SMT @@ -195,7 +195,7 @@ proc.burn_non_fungible_asset # => [ASSET_KEY, EMPTY_WORD, ASSET] # get the faucet storage data slot - exec.account::get_faucet_storage_slot_id + exec.account::get_faucet_sysdata_slot_id # => [faucet_storage_data_slot, ASSET_KEY, EMPTY_WORD, ASSET] # remove the non-fungible asset from the tracking SMT @@ -236,7 +236,7 @@ export.is_non_fungible_asset_issued # => [ASSET_KEY] # get the storage index where faucet's assets map is stored - exec.account::get_faucet_storage_slot_id + exec.account::get_faucet_sysdata_slot_id # => [map_slot_index, ASSET_KEY] # get the non-fungible asset stored by the computed account key diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index e7b5c9ad89..c6e30facd5 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -322,7 +322,7 @@ proc.validate_new_account # process conditional logic depending on whether the account is a faucet if.true # get the faucet reserved slot - exec.account::get_faucet_storage_slot_id exec.account::get_typed_item + exec.account::get_faucet_sysdata_slot_id exec.account::get_typed_item # => [FAUCET_RESERVED_SLOT, slot_type, acct_id_prefix] # check if the account is a fungible faucet diff --git a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm b/crates/miden-lib/asm/miden/contracts/faucets/mod.masm index 3a02d56a3f..45eb22f9fa 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm +++ b/crates/miden-lib/asm/miden/contracts/faucets/mod.masm @@ -17,10 +17,8 @@ const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no # CONSTANTS # ================================================================================================= -# The slot in this component's storage where the metadata is stored. -# TODO(named_slots): Unify slot name or make distribute take slot ID as a parameter to allow for -# different slot names per fungible faucet? -const.METADATA_SLOT=word("miden::basic_fungible_faucet::metadata") +# The standard slot where fungible faucet metadata like token symbol or decimals are stored. +const.METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") #! Distributes freshly minted fungible assets to the provided recipient by creating a note. #! diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 88fe5a2d3c..5784166927 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -9,7 +9,6 @@ use miden_objects::account::{ StorageSlotName, }; use miden_objects::asset::{FungibleAsset, TokenSymbol}; -use miden_objects::utils::sync::LazyLock; use miden_objects::{Felt, FieldElement, Word}; use super::FungibleFaucetError; @@ -41,11 +40,6 @@ procedure_digest!( basic_fungible_faucet_library ); -static METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::basic_fungible_faucet::metadata") - .expect("storage slot name should be valid") -}); - /// An [`AccountComponent`] implementing a basic fungible faucet. /// /// It reexports the procedures from `miden::contracts::faucets::basic_fungible`. When linking @@ -64,7 +58,7 @@ static METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// ## Storage Layout /// -/// - [`Self::metadata_slot_name`]: Basic fungible faucet's metadata +/// - [`Self::metadata_slot`]: Fungible faucet metadata /// /// [kasm]: crate::transaction::TransactionKernel::assembler pub struct BasicFungibleFaucet { @@ -133,11 +127,11 @@ impl BasicFungibleFaucet { for component in interface.components().iter() { if let AccountComponentInterface::BasicFungibleFaucet = component { let faucet_metadata = storage - .get_item(BasicFungibleFaucet::metadata_slot_name()) + .get_item(BasicFungibleFaucet::metadata_slot()) .map_err(|err| FungibleFaucetError::StorageLookupFailed { - slot_name: BasicFungibleFaucet::metadata_slot_name().clone(), - source: err, - })?; + slot_name: BasicFungibleFaucet::metadata_slot().clone(), + source: err, + })?; let [max_supply, decimals, token_symbol, _] = *faucet_metadata; // verify metadata values @@ -161,8 +155,8 @@ impl BasicFungibleFaucet { // -------------------------------------------------------------------------------------------- /// Returns the [`StorageSlotName`] where the [`BasicFungibleFaucet`]'s metadata is stored. - pub fn metadata_slot_name() -> &'static StorageSlotName { - &METADATA_SLOT_NAME + pub fn metadata_slot() -> &'static StorageSlotName { + &super::METADATA_SLOT_NAME } /// Returns the symbol of the faucet. @@ -202,7 +196,7 @@ impl From for AccountComponent { Felt::ZERO, ]); let storage_slot = - StorageSlot::with_value(BasicFungibleFaucet::metadata_slot_name().clone(), metadata); + StorageSlot::with_value(BasicFungibleFaucet::metadata_slot().clone(), metadata); AccountComponent::new(basic_fungible_faucet_library(), vec![storage_slot]) .expect("basic fungible faucet component should satisfy the requirements of a valid account component") @@ -362,11 +356,11 @@ mod tests { ) .unwrap(); - // The reserved faucet slot should be initialized to an empty word. + // The faucet sysdata slot should be initialized to an empty word. assert_eq!( faucet_account .storage() - .get_item(AccountStorage::faucet_metadata_slot()) + .get_item(AccountStorage::faucet_sysdata_slot()) .unwrap(), Word::empty() ); @@ -405,10 +399,7 @@ mod tests { // Check that faucet metadata was initialized to the given values. assert_eq!( - faucet_account - .storage() - .get_item(BasicFungibleFaucet::metadata_slot_name()) - .unwrap(), + faucet_account.storage().get_item(BasicFungibleFaucet::metadata_slot()).unwrap(), [Felt::new(123), Felt::new(2), token_symbol.into(), Felt::ZERO].into() ); diff --git a/crates/miden-lib/src/account/faucets/mod.rs b/crates/miden-lib/src/account/faucets/mod.rs index ba799766f0..4b89f0af31 100644 --- a/crates/miden-lib/src/account/faucets/mod.rs +++ b/crates/miden-lib/src/account/faucets/mod.rs @@ -1,6 +1,7 @@ use alloc::string::String; use miden_objects::account::{Account, AccountStorage, AccountType, StorageSlotName}; +use miden_objects::utils::sync::LazyLock; use miden_objects::{AccountError, Felt, TokenSymbolError}; use thiserror::Error; @@ -10,6 +11,11 @@ mod network_fungible; pub use basic_fungible::{BasicFungibleFaucet, create_basic_fungible_faucet}; pub use network_fungible::{NetworkFungibleFaucet, create_network_fungible_faucet}; +static METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::fungible_faucets::metadata") + .expect("storage slot name should be valid") +}); + // FUNGIBLE FAUCET // ================================================================================================ @@ -34,9 +40,9 @@ impl FungibleFaucetExt for Account { } let slot = - self.storage().get_item(AccountStorage::faucet_metadata_slot()).map_err(|err| { + self.storage().get_item(AccountStorage::faucet_sysdata_slot()).map_err(|err| { FungibleFaucetError::StorageLookupFailed { - slot_name: AccountStorage::faucet_metadata_slot().clone(), + slot_name: AccountStorage::faucet_sysdata_slot().clone(), source: err, } })?; diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index fcd77243c3..47d3a7d617 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -60,6 +60,10 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// - First slot: Token metadata `[max_supply, decimals, token_symbol, 0]` /// - Second slot: Owner account ID as a single Word /// +/// ## Storage Layout +/// +/// - [`Self::metadata_slot`]: Fungible faucet metadata +/// /// [kasm]: crate::transaction::TransactionKernel::assembler pub struct NetworkFungibleFaucet { faucet: BasicFungibleFaucet, @@ -164,8 +168,7 @@ impl NetworkFungibleFaucet { /// Returns the [`StorageSlotName`] where the [`NetworkFungibleFaucet`]'s metadata is stored. pub fn metadata_slot() -> &'static StorageSlotName { - // TODO(named_slots): Rename to metadata_slot. - BasicFungibleFaucet::metadata_slot_name() + &super::METADATA_SLOT_NAME } /// Returns the [`StorageSlotName`] where the [`NetworkFungibleFaucet`]'s owner configuration is diff --git a/crates/miden-lib/src/testing/mock_account.rs b/crates/miden-lib/src/testing/mock_account.rs index e558bcebe0..3f3dfdb691 100644 --- a/crates/miden-lib/src/testing/mock_account.rs +++ b/crates/miden-lib/src/testing/mock_account.rs @@ -48,9 +48,9 @@ pub trait MockAccountExt { .expect("account should be valid"); let (_id, vault, mut storage, code, nonce, _seed) = account.into_parts(); - let faucet_data_slot = Word::from([ZERO, ZERO, ZERO, initial_balance]); + let faucet_sysdata_slot = Word::from([ZERO, ZERO, ZERO, initial_balance]); storage - .set_item(AccountStorage::faucet_metadata_slot(), faucet_data_slot) + .set_item(AccountStorage::faucet_sysdata_slot(), faucet_sysdata_slot) .unwrap(); Account::new_existing(account_id, vault, storage, code, nonce) @@ -73,7 +73,7 @@ pub trait MockAccountExt { let non_fungible_storage_map = StorageMap::with_entries([(asset.vault_key().into(), asset.into())]).unwrap(); let storage = AccountStorage::new(vec![StorageSlot::with_map( - AccountStorage::faucet_metadata_slot().clone(), + AccountStorage::faucet_sysdata_slot().clone(), non_fungible_storage_map, )]) .unwrap(); diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-lib/src/transaction/kernel_procedures.rs index a1ef820640..2a77ca1bbe 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-lib/src/transaction/kernel_procedures.rs @@ -28,7 +28,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_get_initial_item word!("0x77118a4356781e36469bbfa34f45654f13627eb9a9846ee888ebd0bdb58172ef"), // account_set_item - word!("0x7bc2b8365092e8b98aa5d8b880424b6230a218437bea5e9fb19cd21ee9743dcb"), + word!("0xde6abc0e0331cd5a586f7e8893f32524b0a4072a2d3dc8023c94d1c01a785789"), // account_get_map_item word!("0x8c9f227c3c6992a4374df70bec4970c3fdf152baa8db7f8483f3b5619c312683"), // account_get_initial_map_item @@ -60,13 +60,13 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_procedure word!("0x667d5ce1b7a54c3b8965666ce90e59085c97775b82eba25dddfe218db5fe137d"), // faucet_mint_asset - word!("0x5af68530690f79bc8daa623c388d300e640adb6c123e16751b47e8739f3032f6"), + word!("0x4922681039d0ac03dc6b4d12b2385ea87f57dfd0bbd64bd6ac0c016617caafe9"), // faucet_burn_asset - word!("0xea1bfbe5a0228b5ab4fb8968d22fa5df471cce284a171bb99e7863e5a842c392"), + word!("0x7f261292b3682e2f9bdb9237ce41de08a680e983764b191b381ed111dcc23f5f"), // faucet_get_total_fungible_asset_issuance - word!("0xedbc4fa4fba063dc750576c2ad7b00ed92f1c7bf744ed096b4bc7923f92ef682"), + word!("0x51cfb361598b5dd34ff93ae2c3788e7c1cd702e8831e2ea1d45dfc2ccd97876f"), // faucet_is_non_fungible_asset_issued - word!("0x94f574852d517f8e547a0b298a6760d76f770dab3cafc46556935c094f272792"), + word!("0xb4e83658a2c6b89ce2562fed3a6dfa141f7ff00c3d36a89d0d06f70b6690f133"), // input_note_get_metadata word!("0x7ad3e94585e7a397ee27443c98b376ed8d4ba762122af6413fde9314c00a6219"), // input_note_get_assets_info diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index 0fc30d1996..7aff0806c7 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -29,8 +29,8 @@ pub use header::AccountStorageHeader; mod partial; pub use partial::PartialStorage; -static FAUCET_METADATA_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::faucet::metadata").expect("storage slot name should be valid") +static FAUCET_SYSDATA_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::faucet::sysdata").expect("storage slot name should be valid") }); // ACCOUNT STORAGE @@ -108,16 +108,17 @@ impl AccountStorage { /// /// Returns an error if: /// - The number of [`StorageSlot`]s of all components exceeds 255. + /// - Any component accesses [`AccountStorage::faucet_sysdata_slot`]. pub(super) fn from_components( components: Vec, account_type: AccountType, ) -> Result { let mut storage_slots = match account_type { AccountType::FungibleFaucet => { - vec![StorageSlot::with_empty_value(Self::faucet_metadata_slot().clone())] + vec![StorageSlot::with_empty_value(Self::faucet_sysdata_slot().clone())] }, AccountType::NonFungibleFaucet => { - vec![StorageSlot::with_empty_map(Self::faucet_metadata_slot().clone())] + vec![StorageSlot::with_empty_map(Self::faucet_sysdata_slot().clone())] }, _ => vec![], }; @@ -126,8 +127,8 @@ impl AccountStorage { let AccountComponent { storage_slots, .. } = component; storage_slots.into_iter() }) { - if component_slot.name() == Self::faucet_metadata_slot() { - return Err(AccountError::StorageSlotNameMustNotBeFaucetMetadata); + if component_slot.name() == Self::faucet_sysdata_slot() { + return Err(AccountError::StorageSlotNameMustNotBeFaucetSysdata); } storage_slots.push(component_slot); @@ -139,9 +140,9 @@ impl AccountStorage { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the [`StorageSlotName`] of the faucet's protocol metadata. - pub fn faucet_metadata_slot() -> &'static StorageSlotName { - &FAUCET_METADATA_SLOT_NAME + /// Returns the [`StorageSlotName`] of the faucet's protocol system data. + pub fn faucet_sysdata_slot() -> &'static StorageSlotName { + &FAUCET_SYSDATA_SLOT_NAME } /// Converts storage slots of this account storage into a vector of field elements. diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-objects/src/account/storage/slot/slot_name.rs index 6b7d41c4ab..b431c26b59 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_name.rs @@ -11,7 +11,7 @@ use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Seri /// A typical slot name looks like this: /// /// ```text -/// miden::basic_fungible_faucet::metadata +/// miden::standards::fungible_faucets::metadata /// ``` /// /// The double-colon (`::`) serves as a separator and the strings in between the separators are diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 1ee4b78080..d99cbf3a80 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -155,9 +155,9 @@ pub enum AccountError { DuplicateStorageSlotName(StorageSlotName), #[error( "account storage cannot contain a user-provided slot with name {} as it is reserved by the protocol", - AccountStorage::faucet_metadata_slot() + AccountStorage::faucet_sysdata_slot() )] - StorageSlotNameMustNotBeFaucetMetadata, + StorageSlotNameMustNotBeFaucetSysdata, #[error("storage does not contain a slot with name {slot_name}")] StorageSlotNameNotFound { slot_name: StorageSlotName }, #[error("storage does not contain a slot with ID {slot_id}")] diff --git a/crates/miden-testing/src/kernel_tests/tx/mod.rs b/crates/miden-testing/src/kernel_tests/tx/mod.rs index 3db274a4a6..7c488b71b6 100644 --- a/crates/miden-testing/src/kernel_tests/tx/mod.rs +++ b/crates/miden-testing/src/kernel_tests/tx/mod.rs @@ -61,16 +61,7 @@ pub trait ExecutionOutputExt { /// initialized. // Unused for now, but may become useful in the future. #[allow(dead_code)] - fn get_kernel_mem_element(&self, addr: u32) -> Felt { - // TODO: Use Memory::read_element once it no longer requires &mut self. - // https://github.com/0xMiden/miden-vm/issues/2237 - - // Copy of how Memory::read_element is implemented in Miden VM. - let idx = addr % miden_objects::WORD_SIZE as u32; - let word_addr = addr - idx; - - self.get_kernel_mem_word(word_addr)[idx as usize] - } + fn get_kernel_mem_element(&self, addr: u32) -> Felt; /// Reads an element from the stack. fn get_stack_element(&self, idx: usize) -> Felt; @@ -111,6 +102,15 @@ impl ExecutionOutputExt for ExecutionOutput { fn get_stack_word_le(&self, index: usize) -> Word { self.stack.get_stack_word_le(index).expect("index must be in bounds") } + + fn get_kernel_mem_element(&self, addr: u32) -> Felt { + let tx_kernel_context = ContextId::root(); + let err_ctx = (); + + self.memory + .read_element(tx_kernel_context, Felt::from(addr), &err_ctx) + .expect("address converted from u32 should be in bounds") + } } pub fn input_note_data_ptr(note_idx: u32) -> memory::MemoryAddress { diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 37aae419b8..43886564e4 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -10,6 +10,8 @@ use miden_lib::errors::tx_kernel_errors::{ ERR_ACCOUNT_ID_UNKNOWN_VERSION, ERR_ACCOUNT_NONCE_AT_MAX, ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE, + ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME, + ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED, }; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::mock_account::MockAccountExt; @@ -525,6 +527,82 @@ async fn test_get_storage_slot_type() -> miette::Result<()> { Ok(()) } +/// Tests that accessing an unknown slot fails with the expected error message. +/// +/// This tests both accounts with empty storage and non-empty storage. +#[tokio::test] +async fn test_account_get_item_fails_on_unknown_slot() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + let account_empty_storage = builder.add_existing_mock_account(Auth::IncrNonce)?; + assert_eq!(account_empty_storage.storage().num_slots(), 0); + + let account_non_empty_storage = builder.add_existing_mock_account(Auth::BasicAuth)?; + assert_eq!(account_non_empty_storage.storage().num_slots(), 1); + + let chain = builder.build()?; + + let code = r#" + use.mock::account + + const.UNKNOWN_SLOT_NAME = word("unknown::slot::name") + + begin + push.UNKNOWN_SLOT_NAME[0..2] + call.account::get_item + end + "#; + let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; + + let result = chain + .build_tx_context(account_empty_storage, &[], &[])? + .tx_script(tx_script.clone()) + .build()? + .execute() + .await; + assert_transaction_executor_error!(result, ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME); + + let result = chain + .build_tx_context(account_non_empty_storage, &[], &[])? + .tx_script(tx_script) + .build()? + .execute() + .await; + assert_transaction_executor_error!(result, ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME); + + Ok(()) +} + +/// Tests that accessing the protocol-reserved faucet metadata slot fails with the expected error +/// message. +#[tokio::test] +async fn test_account_set_item_fails_on_reserved_faucet_metadata_slot() -> anyhow::Result<()> { + let code = r#" + use.miden::native_account + + const.FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") + + begin + push.FAUCET_SYSDATA_SLOT[0..2] + exec.native_account::set_item + end + "#; + let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + + let tx_context = TransactionContextBuilder::with_fungible_faucet( + ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, + Felt::from(0u32), + ) + .tx_script(tx_script) + .build() + .unwrap(); + + let result = tx_context.execute().await; + assert_transaction_executor_error!(result, ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED); + + Ok(()) +} + #[tokio::test] async fn test_is_slot_id_lt() -> miette::Result<()> { // Note that the slot IDs derived from the names are essentially randomly sorted, so these cover diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index 71b7dd0f6b..e28003afe7 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -203,7 +203,7 @@ async fn test_mint_non_fungible_asset_succeeds() -> anyhow::Result<()> { use.$kernel::prologue use.mock::faucet->mock_faucet - const FAUCET_METADATA_SLOT_NAME = word("{faucet_metadata_slot_name}") + const FAUCET_SYSDATA_SLOT_NAME = word("{faucet_sysdata_slot_name}") begin # mint asset @@ -222,7 +222,7 @@ async fn test_mint_non_fungible_asset_succeeds() -> anyhow::Result<()> { assert.err="vault should contain asset" # assert the non-fungible asset has been added to the faucet smt - push.FAUCET_METADATA_SLOT_NAME[0..2] + push.FAUCET_SYSDATA_SLOT_NAME[0..2] exec.account::get_item push.{asset_vault_key} exec.smt::get @@ -231,7 +231,7 @@ async fn test_mint_non_fungible_asset_succeeds() -> anyhow::Result<()> { dropw end "#, - faucet_metadata_slot_name = AccountStorage::faucet_metadata_slot(), + faucet_sysdata_slot_name = AccountStorage::faucet_sysdata_slot(), non_fungible_asset = Word::from(non_fungible_asset), asset_vault_key = StorageMap::hash_key(asset_vault_key.into()), ); @@ -503,7 +503,7 @@ async fn test_burn_non_fungible_asset_succeeds() -> anyhow::Result<()> { use.$kernel::prologue use.mock::faucet->mock_faucet - const FAUCET_METADATA_SLOT_NAME = word("{faucet_metadata_slot_name}") + const FAUCET_SYSDATA_SLOT_NAME = word("{faucet_sysdata_slot_name}") begin exec.prologue::prepare_transaction @@ -520,7 +520,7 @@ async fn test_burn_non_fungible_asset_succeeds() -> anyhow::Result<()> { # check that the non-fungible asset is in the account map push.{burnt_asset_vault_key} - push.FAUCET_METADATA_SLOT_NAME[0..2] + push.FAUCET_SYSDATA_SLOT_NAME[0..2] exec.account::get_map_item push.{non_fungible_asset} assert_eqw.err="non-fungible asset should be in the account map" @@ -542,14 +542,14 @@ async fn test_burn_non_fungible_asset_succeeds() -> anyhow::Result<()> { # assert that the non-fungible asset is no longer in the account map push.{burnt_asset_vault_key} - push.FAUCET_METADATA_SLOT_NAME[0..2] + push.FAUCET_SYSDATA_SLOT_NAME[0..2] exec.account::get_map_item padw assert_eqw.err="burnt asset should have been removed from map" dropw end "#, - faucet_metadata_slot_name = AccountStorage::faucet_metadata_slot(), + faucet_sysdata_slot_name = AccountStorage::faucet_sysdata_slot(), non_fungible_asset = Word::from(non_fungible_asset_burnt), burnt_asset_vault_key = burnt_asset_vault_key, ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 932e0e5208..b04a2cb72a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -664,7 +664,7 @@ pub async fn create_account_fungible_faucet_invalid_initial_balance() -> anyhow: // do that. let faucet_data_slot = Word::from([0, 0, 0, 100u32]); storage - .set_item(AccountStorage::faucet_metadata_slot(), faucet_data_slot) + .set_item(AccountStorage::faucet_sysdata_slot(), faucet_data_slot) .unwrap(); // The compute account ID function will set the nonce to zero so this is considered a new @@ -692,7 +692,7 @@ pub async fn create_account_non_fungible_faucet_invalid_initial_reserved_slot() let non_fungible_storage_map = StorageMap::with_entries([(asset.vault_key().into(), asset.into())]).unwrap(); let storage = AccountStorage::new(vec![StorageSlot::with_map( - AccountStorage::faucet_metadata_slot().clone(), + AccountStorage::faucet_sysdata_slot().clone(), non_fungible_storage_map, )]) .unwrap(); diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index bb42f59678..f3adace37b 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -348,13 +348,13 @@ impl MockChainBuilder { let mut account = self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)?; - // The faucet's reserved slot is initialized to an empty word by default. + // The faucet's sysdata slot is initialized to an empty word by default. // If total_issuance is set, overwrite it and reinsert the account. if let Some(issuance) = total_issuance { account .storage_mut() .set_item( - AccountStorage::faucet_metadata_slot(), + AccountStorage::faucet_sysdata_slot(), Word::from([ZERO, ZERO, ZERO, Felt::new(issuance)]), ) .context("failed to set faucet storage")?; @@ -392,13 +392,13 @@ impl MockChainBuilder { let mut account = self.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists)?; - // The faucet's reserved slot is initialized to an empty word by default. + // The faucet's sysdata slot is initialized to an empty word by default. // If total_issuance is set, overwrite it and reinsert the account. if let Some(issuance) = total_issuance { account .storage_mut() .set_item( - AccountStorage::faucet_metadata_slot(), + AccountStorage::faucet_sysdata_slot(), Word::from([ZERO, ZERO, ZERO, Felt::new(issuance)]), ) .context("failed to set faucet storage")?; diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 33fd60aa0a..df1b2dd4fb 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -279,7 +279,7 @@ async fn prove_burning_fungible_asset_on_existing_faucet_succeeds() -> anyhow::R // Check that max_supply at the word's index 0 is 200. The remainder of the word is initialized // with the metadata of the faucet which we don't need to check. assert_eq!( - faucet.storage().get_item(BasicFungibleFaucet::metadata_slot_name()).unwrap()[0], + faucet.storage().get_item(BasicFungibleFaucet::metadata_slot()).unwrap()[0], Felt::new(200) ); From 1c62da3e493eaf6c0984fff289000701eee2e856 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 12 Dec 2025 15:11:04 +0700 Subject: [PATCH 040/114] chore: enforce minimum number of account procedures in tx kernel (#2171) --- CHANGELOG.md | 1 + .../asm/kernels/transaction/lib/account.masm | 19 +++++++++++++++++-- .../miden-lib/src/errors/tx_kernel_errors.rs | 2 ++ .../src/transaction/kernel_procedures.rs | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5754a8827d..85feb836c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Changes - [BREAKING] Make `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). +- [BREAKING] Enforce minimum number of account procedures in tx kernel ([#2171](https://github.com/0xMiden/miden-base/pull/2171)). ## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index 58747967ae..2204c35cbc 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -41,6 +41,8 @@ const.ERR_FAUCET_INVALID_STORAGE_OFFSET="storage offset is invalid for a faucet const.ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH="computed account code commitment does not match recorded account code commitment" +const.ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES="number of account procedures must be at least 2" + const.ERR_ACCOUNT_TOO_MANY_PROCEDURES="number of account procedures exceeds the maximum limit of 256" const.ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS="number of account storage slots exceeds the maximum limit of 255" @@ -77,6 +79,9 @@ const.MAX_STORAGE_SLOT_INDEX=254 # The maximum number of account storage slots. const.MAX_NUM_STORAGE_SLOTS=MAX_STORAGE_SLOT_INDEX+1 +# The minimum number of account interface procedures. +const.MIN_NUM_PROCEDURES=2 + # The maximum number of account interface procedures. const.MAX_NUM_PROCEDURES=256 @@ -1377,8 +1382,18 @@ export.save_account_procedure_data # OS => [num_procs, CODE_COMMITMENT] # AS => [[ACCOUNT_PROCEDURE_DATA]] - # assert that account does not exceed allowed maximum number of procedures - dup exec.get_max_num_procedures lte assert.err=ERR_ACCOUNT_TOO_MANY_PROCEDURES + # make sure number of procedures is a valid u32, so we can use u32 operations for validation + u32assert.err=ERR_ACCOUNT_TOO_MANY_PROCEDURES + # OS => [num_procs, CODE_COMMITMENT] + # AS => [[ACCOUNT_PROCEDURE_DATA]] + + # assert the account has at least the minimum number of procedures + dup u32gte.MIN_NUM_PROCEDURES assert.err=ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES + # OS => [num_procs, CODE_COMMITMENT] + # AS => [[ACCOUNT_PROCEDURE_DATA]] + + # assert the account does not exceed the maximum number of procedures + dup u32lte.MAX_NUM_PROCEDURES assert.err=ERR_ACCOUNT_TOO_MANY_PROCEDURES # OS => [num_procs, CODE_COMMITMENT] # AS => [[ACCOUNT_PROCEDURE_DATA]] diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index d485cdf656..a681444f1e 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -34,6 +34,8 @@ pub const ERR_ACCOUNT_IS_NOT_NATIVE: MasmError = MasmError::from_static_str("the pub const ERR_ACCOUNT_NONCE_AT_MAX: MasmError = MasmError::from_static_str("account nonce is already at its maximum possible value"); /// Error Message: "account nonce can only be incremented once" pub const ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE: MasmError = MasmError::from_static_str("account nonce can only be incremented once"); +/// Error Message: "number of account procedures must be at least 2" +pub const ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES: MasmError = MasmError::from_static_str("number of account procedures must be at least 2"); /// Error Message: "provided procedure index is out of bounds" pub const ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("provided procedure index is out of bounds"); /// Error Message: "account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure" diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-lib/src/transaction/kernel_procedures.rs index 2a77ca1bbe..6c13682875 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-lib/src/transaction/kernel_procedures.rs @@ -104,7 +104,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // tx_get_block_timestamp word!("0x7903185b847517debb6c2072364e3e757b99ee623e97c2bd0a4661316c5c5418"), // tx_start_foreign_context - word!("0x4bfde60ab4b1e42148ceea2845ecf9aae061a577972baf348379701760d476d7"), + word!("0x3755ddea584a3575bc3c97820f739d562334b91ab8c00642b519b4e22792b191"), // tx_end_foreign_context word!("0xaa0018aa8da890b73511879487f65553753fb7df22de380dd84c11e6f77eec6f"), // tx_get_expiration_delta From bfb28d4fa55231ed67f5b5fb08a287cd5fe90b82 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Fri, 12 Dec 2025 13:49:21 -0300 Subject: [PATCH 041/114] feat: AggLayer bridge-out flow (stubbed out): `B2AGG` note -> `AggLayerBridgeOut` contract -> `BURN` note (#2023) * feat: stubbed out B2AGG note & bridge out functionality * feat: create BURN note during B2AGG consumption * feat: stub out mmr_frontier for bridgeout * refactor: add padding comments to B2AGG note * refactor: cleanup B2AGG notescript implementation * Update crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * refactor: update B2AGG note doc comments * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update crates/miden-testing/tests/agglayer/bridge_out.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * refactor: clean up test logic * feat: implement ntx faucet tag in masm & compute BURN serial_num * refactor: make BURN note inputs a constant * fix: fix docs CI * feat: hash b2agg note details using keccak256 * feat: add comments to bridge out component procedures * feat: add B2AGG reclaim test * fix: clippy warning * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Revert "Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm" This reverts commit 733883d255b91bd70a07124f7b4a07a683331842. * refactor: cleanup build.rs updates * refactor: remove added comment from build.rs * feat: use build_note_tag_for_network_account in bridge_out component * refactor: update procedure comment * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Marti * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Marti * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Marti * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Marti * refactor: rm convert_b2agg_to_u32 & use store_word_u32s_le * fix: spelling in masm comment * fix: rm debug mode from build.rs * refactor: rm unused imports in bridge_out * fix: update doc comment * refactor: import std::word * refactor: use swapw instead of padw in B2AGG * refactor: rename B2AGG_NOTE_INPUTS_NUMBER to B2AGG_NOTE_INPUTS_COUNT * refactor: address stack comments & optimize cycles * refactor: add B2AGG input comments * refactor: update B2AGG input comments & stack layout * refactor: fix stack state before note::build_note_tag_for_network_account * refactor: modularize agglayer bridge * fix: rustfmt * refactor: add add_asset_message procedure * refactor: use different stack comment notation for B2AGG inputs * refactor: modify B2AGG description doc comment * Apply suggestions from code review * Update crates/miden-lib/asm/agglayer/account_components/bridge_out.masm Co-authored-by: Marti * refactor: add todo comments in masm --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Marti --- .../account_components/bridge_out.masm | 162 ++++++++++ .../account_components/local_exit_tree.masm | 120 +++++++ .../asm/agglayer/note_scripts/B2AGG.masm | 87 +++++ crates/miden-lib/build.rs | 44 +++ crates/miden-lib/src/agglayer/mod.rs | 95 ++++++ crates/miden-lib/src/agglayer/utils.rs | 156 +++++++++ .../src/errors/note_script_errors.rs | 5 + crates/miden-lib/src/lib.rs | 1 + .../tests/agglayer/bridge_out.rs | 302 ++++++++++++++++++ crates/miden-testing/tests/agglayer/mod.rs | 1 + crates/miden-testing/tests/lib.rs | 1 + 11 files changed, 974 insertions(+) create mode 100644 crates/miden-lib/asm/agglayer/account_components/bridge_out.masm create mode 100644 crates/miden-lib/asm/agglayer/account_components/local_exit_tree.masm create mode 100644 crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm create mode 100644 crates/miden-lib/src/agglayer/mod.rs create mode 100644 crates/miden-lib/src/agglayer/utils.rs create mode 100644 crates/miden-testing/tests/agglayer/bridge_out.rs create mode 100644 crates/miden-testing/tests/agglayer/mod.rs diff --git a/crates/miden-lib/asm/agglayer/account_components/bridge_out.masm b/crates/miden-lib/asm/agglayer/account_components/bridge_out.masm new file mode 100644 index 0000000000..c0e5cffbcd --- /dev/null +++ b/crates/miden-lib/asm/agglayer/account_components/bridge_out.masm @@ -0,0 +1,162 @@ +use.miden::active_note +use.miden::native_account +use.miden::note +use.miden::output_note +use std::crypto::hashes::keccak256 +use std::word +use.agglayer::local_exit_tree + +# CONSTANTS +# ================================================================================================= +const.MMR_PTR=42 +const.LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") + +const.BURN_NOTE_ROOT=[6407337173854817345,5626358912819151014,703918618794810515,17401169215223723177] +const.EXECUTION_HINT_ALWAYS=1 +const.PUBLIC_NOTE=1 +const.AUX=0 +const.NUM_BURN_NOTE_INPUTS=0 +const.BURN_ASSET_MEM_PTR=24 + +#! Computes the SERIAL_NUM of the outputted BURN note. +#! +#! The serial number is computed as hash(B2AGG_SERIAL_NUM, ASSET). +#! +#! Inputs: [ASSET] +#! Outputs: [SERIAL_NUM] +#! +#! Where: +#! - ASSET is the asset for which to compute the burn note serial number. +#! - SERIAL_NUM is the computed serial number for the BURN note. +#! +#! Invocation: exec +proc compute_burn_note_serial_num + exec.active_note::get_serial_number + # => [B2AGG_SERIAL_NUM, ASSET] + + hmerge + # => [SERIAL_NUM] +end + +#! Creates a BURN note for the specified asset. +#! +#! This procedure creates an output note that represents a burn operation for the given asset. +#! The note is configured with the appropriate recipient, tag, and execution hint. +#! +#! Inputs: [ASSET] +#! Outputs: [] +#! +#! Where: +#! - ASSET is the asset to be burned. +#! +#! Invocation: exec +proc create_burn_note.8 + loc_storew_be.0 dupw + # => [ASSET, ASSET] + + movup.2 drop movup.2 drop + # => [faucet_id_prefix, faucet_id_suffix, ASSET] + + exec.note::build_note_tag_for_network_account + # => [network_faucet_tag, ASSET] + + loc_store.5 + # => [ASSET] + + exec.compute_burn_note_serial_num + # => [SERIAL_NUM] + + push.BURN_NOTE_ROOT swapw + # => [SERIAL_NUM, SCRIPT_ROOT] + + push.NUM_BURN_NOTE_INPUTS push.0 + # => [inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT] + + exec.note::build_recipient + # => [RECIPIENT] + + push.EXECUTION_HINT_ALWAYS + push.PUBLIC_NOTE + push.AUX + loc_load.5 + # => [tag, aux, note_type, execution_hint, RECIPIENT] + + call.output_note::create + # => [note_idx] + + movdn.4 loc_loadw_be.0 + # => [ASSET, note_idx] + + exec.output_note::add_asset + # => [] +end + +#! Bridges an asset out via the AggLayer +#! +#! This procedure handles the complete bridge-out operation, including: +#! - Converting asset data to u32 format +#! - Computing Keccak hash of the data +#! - Adding the hash to the MMR frontier +#! - Storing the updated MMR root in account storage +#! - Creating a BURN note with the bridged out asset +#! +#! Inputs: [ASSET, dest_network, dest_address(5)] +#! Outputs: [] +#! +#! Where: +#! - ASSET is the asset to be bridged out. +#! - dest_network is the u32 destination network/chain ID. +#! - dest_address(5) are 5 u32 values representing a 20-byte Ethereum address. +#! +#! Invocation: call +pub proc bridge_out + mem_storew_be.BURN_ASSET_MEM_PTR + # => [ASSET, dest_network, dest_address(5)] + + # @dev TODO: Look up asset faucet id in asset registry + # -> return scaling factor + + # @dev TODO: Convert ASSET amount to EVM amount using scaling factor + # -> return amount from here: https://github.com/0xMiden/miden-base/pull/2141 + + # Converting SCALED_ASSET, dest_network, dest_address(5) to u32 representation + # in preparation for keccak256 hashing + + # keccak256 inputs: + # => [ASSET, dest_network, dest_address(5)] + # TODO we should convert Miden->Ethereum asset values, incl. amount conversion etc. + + # TODO: make building bridge message a separate procedure + # TODO: match Agglayer addLeafBridge logic + # TODO: convert Miden asset amount to Ethereum amount + # Store ASSET as u32 limbs in memory starting at address 0 + push.0 movdn.4 exec.word::store_word_u32s_le + # => [dest_network, dest_address(5)] + + # Store [dest_network, dest_address[0..3]] as u32 limbs in memory starting at address 8 + push.8 movdn.4 exec.word::store_word_u32s_le + # => [dest_address(2), 0, 0] + + # Store [dest_address[3..5], 0, 0] as u32 limbs in memory starting at address 16 + push.16 movdn.4 exec.word::store_word_u32s_le + # => [] + + # 1 u32 = 4 bytes + # 10 u32 values = 40 bytes + push.40 push.0 + # => [ptr, len_bytes] + + exec.keccak256::hash_memory + # => [DIGEST_U32[8]] + + # adding DIGEST_U32 double word leaf to mmr frontier + exec.local_exit_tree::add_asset_message + # => [] + + # creating BURN output note for ASSET + mem_loadw_be.BURN_ASSET_MEM_PTR + # => [ASSET] + + exec.create_burn_note + # => [] +end diff --git a/crates/miden-lib/asm/agglayer/account_components/local_exit_tree.masm b/crates/miden-lib/asm/agglayer/account_components/local_exit_tree.masm new file mode 100644 index 0000000000..407dfd4a24 --- /dev/null +++ b/crates/miden-lib/asm/agglayer/account_components/local_exit_tree.masm @@ -0,0 +1,120 @@ +use.miden::native_account +use.miden::active_account + + +# CONSTANTS +# ================================================================================================= + +const.MMR_PTR=42 +const.LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") + +#! Adds a leaf to the MMR frontier using Keccak hashing (stubbed implementation). +#! +#! This is a stubbed implementation that currently drops all inputs without performing +#! the actual MMR frontier addition operation. +#! +#! Inputs: [LEAF[1], LEAF[0], mmr_ptr] +#! Outputs: [] +#! +#! Where: +#! - LEAF[1], LEAF[0] are the leaf data to add to the MMR frontier. +#! - mmr_ptr is the pointer to the MMR frontier data structure. +#! +#! Invocation: exec +proc mmr_frontier_keccak_add + dropw dropw drop + # => [] +end + +#! Gets the root of the MMR frontier using Keccak hashing (stubbed implementation). +#! +#! This is a stubbed implementation that returns placeholder values instead of +#! computing the actual MMR frontier root. +#! +#! Inputs: [mmr_ptr] +#! Outputs: [ROOT[1], ROOT[0]] +#! +#! Where: +#! - ROOT[1], ROOT[0] are the root hash components of the MMR frontier whose memory location starts at mmr_ptr +#! +#! Invocation: exec +pub proc mmr_frontier_keccak_get_root + # stubbed out for now + drop + # => [] + + push.0.0.0.1 push.LOCAL_EXIT_TREE_SLOT[0..2] + # => [slot_id_prefix, slot_id_suffix, KEY] + + exec.active_account::get_map_item + # => [ROOT[0]] + + push.0.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] + # => [slot_id_prefix, slot_id_suffix, KEY, ROOT[0]] + + exec.active_account::get_map_item + # => [ROOT[1], ROOT[0]] +end + +#! Writes the MMR frontier root to account storage. +#! +#! This procedure retrieves the current MMR frontier root and stores it as a double word +#! in the account's storage map. The root is split across two storage keys: +#! - Key [0,0,0,0] stores ROOT[1] (high part) +#! - Key [0,0,0,1] stores ROOT[0] (low part) +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Invocation: exec +proc write_mmr_frontier_root + push.MMR_PTR + # => [MMR_PTR] + + # getting mmr frontier root + exec.mmr_frontier_keccak_get_root + # => [ROOT[1], ROOT[0]] + + # writing double word root to map keys [0,0,0,0] & [0,0,0,1] + push.0.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] + # => [index, KEY, ROOT[1], ROOT[0]] + + exec.native_account::set_map_item + # => [OLD_MAP_ROOT, OLD_MAP_VALUE, ROOT[0]] + + dropw dropw + # => [ROOT[0]] + + push.1.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] + # => [index, KEY, ROOT[0]] + + exec.native_account::set_map_item + # => [OLD_MAP_ROOT, OLD_MAP_VALUE] + + dropw dropw + # => [] +end + +#! Adds an asset message to the MMR frontier and updates the stored root. +#! +#! This procedure takes a Keccak digest (represented as 8 u32 values) and adds it +#! as a leaf to the MMR frontier. After adding the leaf, it updates the MMR root +#! in the account's storage to reflect the new state. +#! +#! Inputs: [DIGEST_U32[8]] +#! Outputs: [] +#! +#! Where: +#! - DIGEST_U32[8] is a Keccak256 hash represented as 8 u32 values (256 bits total). +#! +#! Invocation: exec +pub proc add_asset_message + push.MMR_PTR movdn.8 + # => [LEAF[1], LEAF[0], mmr_ptr] + + exec.mmr_frontier_keccak_add + # => [] + + exec.write_mmr_frontier_root + # => [] +end \ No newline at end of file diff --git a/crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm b/crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm new file mode 100644 index 0000000000..73ea4a1124 --- /dev/null +++ b/crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm @@ -0,0 +1,87 @@ +use.agglayer::bridge_out -> agglayer +use.miden::account_id +use.miden::active_account +use.miden::active_note + +# CONSTANTS +# ================================================================================================= + +const.B2AGG_NOTE_INPUTS_COUNT=6 + +# ERRORS +# ================================================================================================= +const.ERR_B2AGG_WRONG_NUMBER_OF_ASSETS="B2AGG script requires exactly 1 note asset" + +const.ERR_B2AGG_WRONG_NUMBER_OF_INPUTS="B2AGG script expects exactly 6 note inputs" + +#! Bridge-to-AggLayer (B2AGG) note script: bridges assets from Miden to an AggLayer-connected chain. +#! +#! This note can be consumed in two ways: +#! - If the consuming account is the sender (reclaim): the note's assets are added back to the consuming account. +#! - If the consuming account is the Agglayer Bridge: the note's assets are moved to a BURN note, +#! and the note details are hashed into a leaf and appended to the Local Exit Tree. +#! global exit root (GER) merkle tree structure. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Note inputs are assumed to be as follows: +#! - destination_network: u32 value representing the target chain ID +#! - destination_address: split into 5 u32 values representing a 20-byte Ethereum address: +#! - destination_address_0: bytes 0-3 +#! - destination_address_1: bytes 4-7 +#! - destination_address_2: bytes 8-11 +#! - destination_address_3: bytes 12-15 +#! - destination_address_4: bytes 16-19 +#! +#! Panics if: +#! - The note does not contain exactly 6 inputs. +#! - The note does not contain exactly 1 asset. +#! +begin + dropw + # => [pad(16)] + + # Check if reclaim + exec.active_account::get_id + # => [account_id_prefix, account_id_suffix, pad(16)] + + exec.active_note::get_sender + # => [sender_id_prefix, sender_id_suffix, account_id_prefix, account_id_suffix, pad(16)] + + exec.account_id::is_equal + # => [reclaim, pad(16)] + + # B2AGG note is being reclaimed; adding note assets to account + if.true + exec.active_note::add_assets_to_account + # => [pad(16)] + else + # Store note inputs -> mem[8..14] + push.8 exec.active_note::get_inputs + # => [num_inputs, dest_ptr, pad(16)] + + push.B2AGG_NOTE_INPUTS_COUNT assert_eq.err=ERR_B2AGG_WRONG_NUMBER_OF_INPUTS drop + # => [pad(16)] + + # Store note assets -> mem[0..4] + push.0 exec.active_note::get_assets + # => [num_assets, ptr, pad(16)] + + # Must be exactly 1 asset + push.1 assert_eq.err=ERR_B2AGG_WRONG_NUMBER_OF_ASSETS drop + # => [pad(16)] + + # load the 6 B2AGG note input felts as two words + mem_loadw_be.12 swapw.2 mem_loadw_be.8 swapw + # => [EMPTY_WORD, dest_network, dest_address(5), pad(6)] + + # Load ASSET onto the stack + mem_loadw_be.0 + # => [ASSET, dest_network, dest_address(5), pad(6)] + + call.agglayer::bridge_out + # => [pad(16)] + end + # => [pad(16)] +end diff --git a/crates/miden-lib/build.rs b/crates/miden-lib/build.rs index 10254ae490..8c2b449d56 100644 --- a/crates/miden-lib/build.rs +++ b/crates/miden-lib/build.rs @@ -33,6 +33,7 @@ const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN const ASSETS_DIR: &str = "assets"; const ASM_DIR: &str = "asm"; const ASM_MIDEN_DIR: &str = "miden"; +const ASM_AGGLAYER_DIR: &str = "agglayer"; const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts"; const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components"; const SHARED_UTILS_DIR: &str = "shared_utils"; @@ -99,6 +100,10 @@ fn main() -> Result<()> { let miden_lib = compile_miden_lib(&source_dir, &target_dir, assembler.clone())?; assembler.link_dynamic_library(miden_lib)?; + // compile agglayer library and link it + let agglayer_lib = compile_agglayer_lib(&source_dir, &target_dir, assembler.clone())?; + assembler.link_dynamic_library(agglayer_lib)?; + // compile note scripts compile_note_scripts( &source_dir.join(ASM_NOTE_SCRIPTS_DIR), @@ -106,10 +111,24 @@ fn main() -> Result<()> { assembler.clone(), )?; + // compile agglayer note scripts + compile_note_scripts( + &source_dir.join(ASM_AGGLAYER_DIR).join(ASM_NOTE_SCRIPTS_DIR), + &target_dir.join(ASM_AGGLAYER_DIR).join(ASM_NOTE_SCRIPTS_DIR), + assembler.clone(), + )?; + // compile account components compile_account_components( &source_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), &target_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), + assembler.clone(), + )?; + + // compile agglayer account components + compile_account_components( + &source_dir.join(ASM_AGGLAYER_DIR).join(ASM_ACCOUNT_COMPONENTS_DIR), + &target_dir.join(ASM_AGGLAYER_DIR).join(ASM_ACCOUNT_COMPONENTS_DIR), assembler, )?; @@ -315,6 +334,31 @@ fn compile_miden_lib( Ok(miden_lib) } +// COMPILE AGGLAYER LIB +// ================================================================================================ + +/// Reads the MASM files from "{source_dir}/agglayer/account_components" directory, compiles them +/// into an AggLayer assembly library, saves the library into "{target_dir}/agglayer.masl", +/// and returns the compiled library. +fn compile_agglayer_lib( + source_dir: &Path, + target_dir: &Path, + assembler: Assembler, +) -> Result { + let agglayer_namespace = ASM_AGGLAYER_DIR + .parse::() + .expect("invalid agglayer namespace"); + let agglayer_lib = assembler.assemble_library_from_dir( + source_dir.join(ASM_AGGLAYER_DIR).join(ASM_ACCOUNT_COMPONENTS_DIR), + agglayer_namespace, + )?; + + let output_file = target_dir.join(ASM_AGGLAYER_DIR).with_extension(Library::LIBRARY_EXTENSION); + agglayer_lib.write_to_file(output_file).into_diagnostic()?; + + Ok(agglayer_lib) +} + // COMPILE EXECUTABLE MODULES // ================================================================================================ diff --git a/crates/miden-lib/src/agglayer/mod.rs b/crates/miden-lib/src/agglayer/mod.rs new file mode 100644 index 0000000000..6802d6aa44 --- /dev/null +++ b/crates/miden-lib/src/agglayer/mod.rs @@ -0,0 +1,95 @@ +use alloc::vec::Vec; + +use miden_objects::account::{AccountComponent, StorageSlot}; +use miden_objects::assembly::Library; +use miden_objects::note::NoteScript; +use miden_objects::utils::Deserializable; +use miden_objects::utils::sync::LazyLock; +use miden_objects::vm::Program; + +pub mod utils; + +// AGGLAYER NOTE SCRIPTS +// ================================================================================================ + +// Initialize the B2AGG note script only once +static B2AGG_SCRIPT: LazyLock = LazyLock::new(|| { + let bytes = + include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer/note_scripts/B2AGG.masb")); + let program = Program::read_from_bytes(bytes).expect("Shipped B2AGG script is well-formed"); + NoteScript::new(program) +}); + +/// Returns the B2AGG (Bridge to AggLayer) note script. +pub fn b2agg_script() -> NoteScript { + B2AGG_SCRIPT.clone() +} + +// AGGLAYER ACCOUNT COMPONENTS +// ================================================================================================ + +// Initialize the unified AggLayer library only once +static AGGLAYER_LIBRARY: LazyLock = LazyLock::new(|| { + let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer.masl")); + Library::read_from_bytes(bytes).expect("Shipped AggLayer library is well-formed") +}); + +/// Returns the unified AggLayer Library containing all agglayer modules. +pub fn agglayer_library() -> Library { + AGGLAYER_LIBRARY.clone() +} + +/// Returns the Bridge Out Library. +/// +/// Note: This is now the same as agglayer_library() since all agglayer components +/// are compiled into a single library. +pub fn bridge_out_library() -> Library { + agglayer_library() +} + +/// Returns the Local Exit Tree Library. +/// +/// Note: This is now the same as agglayer_library() since all agglayer components +/// are compiled into a single library. +pub fn local_exit_tree_library() -> Library { + agglayer_library() +} + +/// Creates a Local Exit Tree component with the specified storage slots. +/// +/// This component uses the local_exit_tree library and can be added to accounts +/// that need to manage local exit tree functionality. +pub fn local_exit_tree_component(storage_slots: Vec) -> AccountComponent { + let library = local_exit_tree_library(); + + AccountComponent::new(library, storage_slots) + .expect("local_exit_tree component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} + +/// Creates a Bridge Out component with the specified storage slots. +/// +/// This component uses the bridge_out library and can be added to accounts +/// that need to bridge assets out to the AggLayer. +pub fn bridge_out_component(storage_slots: Vec) -> AccountComponent { + let library = bridge_out_library(); + + AccountComponent::new(library, storage_slots) + .expect("bridge_out component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} + +/// Creates a combined Bridge Out component that includes both bridge_out and local_exit_tree +/// modules. +/// +/// This is a convenience function that creates a component with multiple modules. +/// For more fine-grained control, use the individual component functions and combine them +/// using the AccountBuilder pattern. +pub fn bridge_out_with_local_exit_tree_component( + storage_slots: Vec, +) -> Vec { + vec![ + bridge_out_component(storage_slots.clone()), + local_exit_tree_component(vec![]), // local_exit_tree typically doesn't need storage slots + ] +} diff --git a/crates/miden-lib/src/agglayer/utils.rs b/crates/miden-lib/src/agglayer/utils.rs new file mode 100644 index 0000000000..2a17275ee7 --- /dev/null +++ b/crates/miden-lib/src/agglayer/utils.rs @@ -0,0 +1,156 @@ +use alloc::string::String; +use alloc::vec::Vec; + +use miden_objects::Felt; + +/// Converts an Ethereum address (20 bytes) into a vector of 5 Felt values. +/// +/// An Ethereum address is 20 bytes, which we split into 5 u32 values (4 bytes each). +/// The address bytes are distributed as follows: +/// - u32\[0\]: bytes 0-3 +/// - u32\[1\]: bytes 4-7 +/// - u32\[2\]: bytes 8-11 +/// - u32\[3\]: bytes 12-15 +/// - u32\[4\]: bytes 16-19 +/// +/// # Arguments +/// * `address` - A 20-byte Ethereum address +/// +/// # Returns +/// A vector of 5 Felt values representing the address +/// +/// # Panics +/// Panics if the address is not exactly 20 bytes +pub fn ethereum_address_to_felts(address: &[u8; 20]) -> Vec { + let mut result = Vec::with_capacity(5); + + // Convert each 4-byte chunk to a u32 (big-endian) + for i in 0..5 { + let start = i * 4; + let chunk = &address[start..start + 4]; + let value = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + result.push(Felt::new(value as u64)); + } + + result +} + +/// Converts a vector of 5 Felt values back into a 20-byte Ethereum address. +/// +/// # Arguments +/// * `felts` - A vector of 5 Felt values representing an Ethereum address +/// +/// # Returns +/// A Result containing a 20-byte Ethereum address array, or an error string +/// +/// # Errors +/// Returns an error if the vector doesn't contain exactly 5 felts +pub fn felts_to_ethereum_address(felts: &[Felt]) -> Result<[u8; 20], String> { + if felts.len() != 5 { + return Err(alloc::format!("Expected 5 felts for Ethereum address, got {}", felts.len())); + } + + let mut address = [0u8; 20]; + + for (i, felt) in felts.iter().enumerate() { + let value = felt.as_int() as u32; + let bytes = value.to_be_bytes(); + let start = i * 4; + address[start..start + 4].copy_from_slice(&bytes); + } + + Ok(address) +} + +/// Converts an Ethereum address string (with or without "0x" prefix) into a vector of 5 Felt +/// values. +/// +/// # Arguments +/// * `address_str` - A hex string representing an Ethereum address (40 hex chars, optionally +/// prefixed with "0x") +/// +/// # Returns +/// A Result containing a vector of 5 Felt values representing the address, or an error string +/// +/// # Errors +/// Returns an error if: +/// - The string is not a valid hex string +/// - The string does not represent exactly 20 bytes (40 hex characters) +pub fn ethereum_address_string_to_felts(address_str: &str) -> Result, String> { + // Remove "0x" prefix if present + let hex_str = address_str.strip_prefix("0x").unwrap_or(address_str); + + // Check length (should be 40 hex characters for 20 bytes) + if hex_str.len() != 40 { + return Err(alloc::format!( + "Invalid Ethereum address length: expected 40 hex characters, got {}", + hex_str.len() + )); + } + + // Parse hex string to bytes + let mut address_bytes = [0u8; 20]; + for (i, chunk) in hex_str.as_bytes().chunks(2).enumerate() { + let hex_byte = core::str::from_utf8(chunk) + .map_err(|_| String::from("Invalid UTF-8 in address string"))?; + address_bytes[i] = u8::from_str_radix(hex_byte, 16) + .map_err(|_| alloc::format!("Invalid hex character in address: {}", hex_byte))?; + } + + Ok(ethereum_address_to_felts(&address_bytes)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ethereum_address_round_trip() { + // Test that converting from string to felts and back gives the same result + let original_address = "0x1234567890abcdef1122334455667788990011aa"; + + // Convert string to felts + let felts = ethereum_address_string_to_felts(original_address).unwrap(); + + // Convert felts back to bytes + let recovered_bytes = felts_to_ethereum_address(&felts).unwrap(); + + // Convert original string to bytes for comparison + let original_hex = original_address.strip_prefix("0x").unwrap(); + let mut expected_bytes = [0u8; 20]; + for (i, chunk) in original_hex.as_bytes().chunks(2).enumerate() { + let hex_byte = core::str::from_utf8(chunk).unwrap(); + expected_bytes[i] = u8::from_str_radix(hex_byte, 16).unwrap(); + } + + // Assert they match + assert_eq!(recovered_bytes, expected_bytes); + } + + #[test] + fn test_ethereum_address_to_felts_basic() { + let address: [u8; 20] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, + ]; + + let result = ethereum_address_to_felts(&address); + assert_eq!(result.len(), 5); + assert_eq!(result[0], Felt::new(0x12345678)); + assert_eq!(result[1], Felt::new(0x9abcdef0)); + } + + #[test] + fn test_felts_to_ethereum_address_invalid_length() { + let felts = vec![Felt::new(1), Felt::new(2)]; // Only 2 felts + let result = felts_to_ethereum_address(&felts); + assert!(result.is_err()); + } + + #[test] + fn test_ethereum_address_string_invalid_length() { + let address_str = "0x123456"; // Too short + let result = ethereum_address_string_to_felts(address_str); + assert!(result.is_err()); + } +} diff --git a/crates/miden-lib/src/errors/note_script_errors.rs b/crates/miden-lib/src/errors/note_script_errors.rs index e64fa97c35..1d989afca8 100644 --- a/crates/miden-lib/src/errors/note_script_errors.rs +++ b/crates/miden-lib/src/errors/note_script_errors.rs @@ -13,6 +13,11 @@ use crate::errors::MasmError; /// Error Message: "auth procedure had been called from outside the epilogue" pub const ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT: MasmError = MasmError::from_static_str("auth procedure had been called from outside the epilogue"); +/// Error Message: "B2AGG script requires exactly 1 note asset" +pub const ERR_B2AGG_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("B2AGG script requires exactly 1 note asset"); +/// Error Message: "B2AGG script expects exactly 6 note inputs" +pub const ERR_B2AGG_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("B2AGG script expects exactly 6 note inputs"); + /// Error Message: "burn requires exactly 1 note asset" pub const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("burn requires exactly 1 note asset"); diff --git a/crates/miden-lib/src/lib.rs b/crates/miden-lib/src/lib.rs index 126df397a1..7ebc5a2797 100644 --- a/crates/miden-lib/src/lib.rs +++ b/crates/miden-lib/src/lib.rs @@ -17,6 +17,7 @@ mod auth; pub use auth::AuthScheme; pub mod account; +pub mod agglayer; pub mod block; pub mod errors; pub mod note; diff --git a/crates/miden-testing/tests/agglayer/bridge_out.rs b/crates/miden-testing/tests/agglayer/bridge_out.rs new file mode 100644 index 0000000000..21d8351509 --- /dev/null +++ b/crates/miden-testing/tests/agglayer/bridge_out.rs @@ -0,0 +1,302 @@ +extern crate alloc; + +use miden_lib::account::faucets::FungibleFaucetExt; +use miden_lib::agglayer::utils::ethereum_address_string_to_felts; +use miden_lib::agglayer::{b2agg_script, bridge_out_component}; +use miden_lib::note::WellKnownNote; +use miden_objects::account::{ + Account, + AccountId, + AccountIdVersion, + AccountStorageMode, + AccountType, + StorageSlot, + StorageSlotName, +}; +use miden_objects::asset::{Asset, FungibleAsset}; +use miden_objects::note::{ + Note, + NoteAssets, + NoteExecutionHint, + NoteInputs, + NoteMetadata, + NoteRecipient, + NoteScript, + NoteTag, + NoteType, +}; +use miden_objects::transaction::OutputNote; +use miden_objects::{Felt, Word}; +use miden_testing::{AccountState, Auth, MockChain}; +use rand::Rng; + +/// Tests the B2AGG (Bridge to AggLayer) note script with bridge_out account component. +/// +/// This test flow: +/// 1. Creates a network faucet to provide assets +/// 2. Creates a bridge account with the bridge_out component (using network storage) +/// 3. Creates a B2AGG note with assets from the network faucet +/// 4. Executes the B2AGG note consumption via network transaction +/// 5. Consumes the BURN note +#[tokio::test] +async fn test_bridge_out_consumes_b2agg_note() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + // Create a network faucet owner account + let faucet_owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + // Create a network faucet to provide assets for the B2AGG note + let faucet = + builder.add_existing_network_faucet("AGG", 1000, faucet_owner_account_id, Some(100))?; + + // Create a bridge account with the bridge_out component using network (public) storage + // Add a storage map for the bridge component to store MMR frontier data + let storage_slot_name = StorageSlotName::new("miden::agglayer::let").unwrap(); + let storage_slots = vec![StorageSlot::with_empty_map(storage_slot_name)]; + let bridge_component = bridge_out_component(storage_slots); + let account_builder = Account::builder(builder.rng_mut().random()) + .storage_mode(AccountStorageMode::Public) + .with_component(bridge_component); + let mut bridge_account = + builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists)?; + + // CREATE B2AGG NOTE WITH ASSETS + // -------------------------------------------------------------------------------------------- + + let amount = Felt::new(100); + let bridge_asset: Asset = FungibleAsset::new(faucet.id(), amount.into()).unwrap().into(); + let tag = NoteTag::for_local_use_case(0, 0).unwrap(); + let aux = Felt::new(0); + let note_execution_hint = NoteExecutionHint::always(); + let note_type = NoteType::Public; // Use Public note type for network transaction + + // Get the B2AGG note script + let b2agg_script = b2agg_script(); + + // Create note inputs with destination network and address + // destination_network: u32 (AggLayer-assigned network ID) + // destination_address: 20 bytes (Ethereum address) split into 5 u32 values + let destination_network = Felt::new(1); // Example network ID + let destination_address = "0x1234567890abcdef1122334455667788990011aa"; + let address_felts = + ethereum_address_string_to_felts(destination_address).expect("Valid Ethereum address"); + + // Combine network ID and address felts into note inputs (6 felts total) + let mut input_felts = vec![destination_network]; + input_felts.extend(address_felts); + + let inputs = NoteInputs::new(input_felts.clone())?; + + // Create the B2AGG note with assets from the faucet + let b2agg_note_metadata = + NoteMetadata::new(faucet.id(), note_type, tag, note_execution_hint, aux)?; + let b2agg_note_assets = NoteAssets::new(vec![bridge_asset])?; + let serial_num = Word::from([1, 2, 3, 4u32]); + let b2agg_note_recipient = NoteRecipient::new(serial_num, b2agg_script, inputs); + let b2agg_note = Note::new(b2agg_note_assets, b2agg_note_metadata, b2agg_note_recipient); + + // Add the B2AGG note to the mock chain + builder.add_output_note(OutputNote::Full(b2agg_note.clone())); + let mut mock_chain = builder.build()?; + + // Get BURN note script to add to the transaction context + let burn_note_script: NoteScript = WellKnownNote::BURN.script(); + + // EXECUTE B2AGG NOTE AGAINST BRIDGE ACCOUNT (NETWORK TRANSACTION) + // -------------------------------------------------------------------------------------------- + let tx_context = mock_chain + .build_tx_context(bridge_account.id(), &[b2agg_note.id()], &[])? + .add_note_script(burn_note_script.clone()) + .build()?; + let executed_transaction = tx_context.execute().await?; + + // VERIFY PUBLIC BURN NOTE WAS CREATED + // -------------------------------------------------------------------------------------------- + // The bridge_out component should create a PUBLIC BURN note addressed to the faucet + assert_eq!( + executed_transaction.output_notes().num_notes(), + 1, + "Expected one BURN note to be created" + ); + + let output_note = executed_transaction.output_notes().get_note(0); + + // Extract the full note from the OutputNote enum + let burn_note = match output_note { + OutputNote::Full(note) => note, + _ => panic!("Expected OutputNote::Full variant for BURN note"), + }; + + // Verify the BURN note is public + assert_eq!(burn_note.metadata().note_type(), NoteType::Public, "BURN note should be public"); + + // Verify the BURN note contains the bridged asset + let expected_asset = FungibleAsset::new(faucet.id(), amount.into())?; + let expected_asset_obj = Asset::from(expected_asset); + assert!( + burn_note.assets().iter().any(|asset| asset == &expected_asset_obj), + "BURN note should contain the bridged asset" + ); + + assert_eq!( + burn_note.metadata().tag(), + NoteTag::from_account_id(faucet.id()), + "BURN note should have the correct tag" + ); + + // Verify the BURN note uses the correct script + assert_eq!( + burn_note.recipient().script().root(), + burn_note_script.root(), + "BURN note should use the BURN script" + ); + + // Apply the delta to the bridge account + bridge_account.apply_delta(executed_transaction.account_delta())?; + + // Apply the transaction to the mock chain + mock_chain.add_pending_executed_transaction(&executed_transaction)?; + mock_chain.prove_next_block()?; + + // CONSUME THE BURN NOTE WITH THE NETWORK FAUCET + // -------------------------------------------------------------------------------------------- + // Check the initial token issuance before burning + let initial_issuance = faucet.get_token_issuance().unwrap(); + assert_eq!(initial_issuance, Felt::new(100), "Initial issuance should be 100"); + + // Execute the BURN note against the network faucet + let burn_tx_context = + mock_chain.build_tx_context(faucet.id(), &[burn_note.id()], &[])?.build()?; + let burn_executed_transaction = burn_tx_context.execute().await?; + + // Verify the burn transaction was successful - no output notes should be created + assert_eq!( + burn_executed_transaction.output_notes().num_notes(), + 0, + "Burn transaction should not create output notes" + ); + + // Apply the delta to the faucet account and verify the token issuance decreased + let mut faucet = faucet; + faucet.apply_delta(burn_executed_transaction.account_delta())?; + let final_issuance = faucet.get_token_issuance().unwrap(); + assert_eq!( + final_issuance, + Felt::new(initial_issuance.as_int() - amount.as_int()), + "Token issuance should decrease by the burned amount" + ); + + Ok(()) +} + +/// Tests the B2AGG (Bridge to AggLayer) note script reclaim functionality. +/// +/// This test covers the "reclaim" branch where the note creator consumes their own B2AGG note. +/// In this scenario, the assets are simply added back to the account without creating a BURN note. +/// +/// Test flow: +/// 1. Creates a network faucet to provide assets +/// 2. Creates a user account that will create and consume the B2AGG note +/// 3. Creates a B2AGG note with the user account as sender +/// 4. The same user account consumes the B2AGG note (triggering reclaim branch) +/// 5. Verifies that assets are added back to the account and no BURN note is created +#[tokio::test] +async fn test_b2agg_note_reclaim_scenario() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + // Create a network faucet owner account + let faucet_owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + // Create a network faucet to provide assets for the B2AGG note + let faucet = + builder.add_existing_network_faucet("AGG", 1000, faucet_owner_account_id, Some(100))?; + + // Create a user account that will create and consume the B2AGG note + let mut user_account = builder.add_existing_wallet(Auth::BasicAuth)?; + + // CREATE B2AGG NOTE WITH USER ACCOUNT AS SENDER + // -------------------------------------------------------------------------------------------- + + let amount = Felt::new(50); + let bridge_asset: Asset = FungibleAsset::new(faucet.id(), amount.into()).unwrap().into(); + let tag = NoteTag::for_local_use_case(0, 0).unwrap(); + let aux = Felt::new(0); + let note_execution_hint = NoteExecutionHint::always(); + let note_type = NoteType::Public; + + // Get the B2AGG note script + let b2agg_script = b2agg_script(); + + // Create note inputs with destination network and address + let destination_network = Felt::new(1); + let destination_address = "0x1234567890abcdef1122334455667788990011aa"; + let address_felts = + ethereum_address_string_to_felts(destination_address).expect("Valid Ethereum address"); + + // Combine network ID and address felts into note inputs (6 felts total) + let mut input_felts = vec![destination_network]; + input_felts.extend(address_felts); + + let inputs = NoteInputs::new(input_felts.clone())?; + + // Create the B2AGG note with the USER ACCOUNT as the sender + // This is the key difference - the note sender will be the same as the consuming account + let b2agg_note_metadata = + NoteMetadata::new(user_account.id(), note_type, tag, note_execution_hint, aux)?; + let b2agg_note_assets = NoteAssets::new(vec![bridge_asset])?; + let serial_num = Word::from([1, 2, 3, 4u32]); + let b2agg_note_recipient = NoteRecipient::new(serial_num, b2agg_script, inputs); + let b2agg_note = Note::new(b2agg_note_assets, b2agg_note_metadata, b2agg_note_recipient); + + // Add the B2AGG note to the mock chain + builder.add_output_note(OutputNote::Full(b2agg_note.clone())); + let mut mock_chain = builder.build()?; + + // Store the initial asset balance of the user account + let initial_balance = user_account.vault().get_balance(faucet.id()).unwrap_or(0u64); + + // EXECUTE B2AGG NOTE WITH THE SAME USER ACCOUNT (RECLAIM SCENARIO) + // -------------------------------------------------------------------------------------------- + let tx_context = mock_chain + .build_tx_context(user_account.id(), &[b2agg_note.id()], &[])? + .build()?; + let executed_transaction = tx_context.execute().await?; + + // VERIFY NO BURN NOTE WAS CREATED (RECLAIM BRANCH) + // -------------------------------------------------------------------------------------------- + // In the reclaim scenario, no BURN note should be created + assert_eq!( + executed_transaction.output_notes().num_notes(), + 0, + "Reclaim scenario should not create any output notes" + ); + + // Apply the delta to the user account + user_account.apply_delta(executed_transaction.account_delta())?; + + // VERIFY ASSETS WERE ADDED BACK TO THE ACCOUNT + // -------------------------------------------------------------------------------------------- + let final_balance = user_account.vault().get_balance(faucet.id()).unwrap_or(0u64); + let expected_balance = initial_balance + amount.as_int(); + + assert_eq!( + final_balance, expected_balance, + "User account should have received the assets back from the B2AGG note" + ); + + // Apply the transaction to the mock chain + mock_chain.add_pending_executed_transaction(&executed_transaction)?; + mock_chain.prove_next_block()?; + + Ok(()) +} diff --git a/crates/miden-testing/tests/agglayer/mod.rs b/crates/miden-testing/tests/agglayer/mod.rs new file mode 100644 index 0000000000..064331ee01 --- /dev/null +++ b/crates/miden-testing/tests/agglayer/mod.rs @@ -0,0 +1 @@ +mod bridge_out; diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index c5992e03d5..e0bb631b7a 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -1,5 +1,6 @@ extern crate alloc; +mod agglayer; mod auth; mod scripts; mod wallet; From 7ffb33a1643a23036912b8da3c82c0ffe6fd7e9f Mon Sep 17 00:00:00 2001 From: igamigo Date: Sat, 13 Dec 2025 14:25:27 -0300 Subject: [PATCH 042/114] feat: allow building account code with `ScriptBuilder` (#2142) --- CHANGELOG.md | 1 + bin/bench-transaction/src/context_setups.rs | 4 +- .../src/account/auth/ecdsa_k256_keccak.rs | 4 +- .../src/account/auth/rpo_falcon_512.rs | 4 +- .../src/account/faucets/basic_fungible.rs | 4 +- .../src/account/faucets/network_fungible.rs | 13 +- crates/miden-lib/src/account/interface/mod.rs | 8 +- .../miden-lib/src/account/interface/test.rs | 118 ++----- crates/miden-lib/src/account/wallets/mod.rs | 4 +- ...ilder_errors.rs => code_builder_errors.rs} | 14 +- crates/miden-lib/src/errors/mod.rs | 4 +- .../account_component/conditional_auth.rs | 11 +- .../testing/account_component/incr_nonce.rs | 10 +- .../src/testing/mock_account_code.rs | 17 +- crates/miden-lib/src/testing/mock_util_lib.rs | 9 +- crates/miden-lib/src/testing/note.rs | 20 +- crates/miden-lib/src/transaction/mod.rs | 54 +--- .../{script_builder.rs => code_builder.rs} | 288 +++++++++++------- crates/miden-lib/src/utils/mod.rs | 6 +- crates/miden-objects/src/account/code/mod.rs | 11 +- .../src/account/component/code.rs | 47 +++ .../src/account/component/mod.rs | 75 ++--- .../src/account/component/template/mod.rs | 3 + crates/miden-objects/src/account/mod.rs | 1 + .../miden-objects/src/testing/account_code.rs | 7 +- crates/miden-testing/src/executor.rs | 6 +- .../src/kernel_tests/block/utils.rs | 4 +- .../src/kernel_tests/tx/test_account.rs | 36 +-- .../src/kernel_tests/tx/test_account_delta.rs | 20 +- .../src/kernel_tests/tx/test_active_note.rs | 10 +- .../src/kernel_tests/tx/test_auth.rs | 8 +- .../src/kernel_tests/tx/test_epilogue.rs | 8 +- .../src/kernel_tests/tx/test_faucet.rs | 26 +- .../src/kernel_tests/tx/test_fpi.rs | 163 +++++----- .../src/kernel_tests/tx/test_input_note.rs | 16 +- .../src/kernel_tests/tx/test_lazy_loading.rs | 10 +- .../src/kernel_tests/tx/test_note.rs | 9 +- .../src/kernel_tests/tx/test_output_note.rs | 8 +- .../src/kernel_tests/tx/test_prologue.rs | 10 +- .../src/kernel_tests/tx/test_tx.rs | 49 +-- .../miden-testing/src/tx_context/context.rs | 18 +- crates/miden-testing/src/utils.rs | 6 +- crates/miden-testing/tests/auth/ecdsa_acl.rs | 12 +- .../tests/auth/ecdsa_multisig.rs | 14 +- crates/miden-testing/tests/auth/multisig.rs | 14 +- .../tests/auth/rpo_falcon_acl.rs | 12 +- crates/miden-testing/tests/lib.rs | 4 +- crates/miden-testing/tests/scripts/faucet.rs | 8 +- crates/miden-testing/tests/scripts/p2id.rs | 4 +- .../miden-testing/tests/scripts/send_note.rs | 6 +- crates/miden-testing/tests/scripts/swap.rs | 4 +- 51 files changed, 610 insertions(+), 612 deletions(-) rename crates/miden-lib/src/errors/{script_builder_errors.rs => code_builder_errors.rs} (83%) rename crates/miden-lib/src/utils/{script_builder.rs => code_builder.rs} (62%) create mode 100644 crates/miden-objects/src/account/component/code.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 85feb836c0..133c61a69d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Added the ability to get full public key from `TransactionAuthenticator` ([#2145 - [BREAKING] Updated MINT note to support both private and public output note creation ([#2123](https://github.com/0xMiden/miden-base/pull/2123)). - [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)). - [BREAKING] Add public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)). +- [BREAKING] Renamed `ScriptBuilder` into `CodeBuilder` and added the ability to build component code ([#2142](https://github.com/0xMiden/miden-base/pull/2142)). ## 0.12.4 (2025-11-26) diff --git a/bin/bench-transaction/src/context_setups.rs b/bin/bench-transaction/src/context_setups.rs index d784ef52ae..f08b376e90 100644 --- a/bin/bench-transaction/src/context_setups.rs +++ b/bin/bench-transaction/src/context_setups.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::note::NoteType; use miden_objects::testing::account_id::ACCOUNT_ID_SENDER; @@ -55,7 +55,7 @@ pub fn tx_create_single_p2id_note() -> Result { asset = Word::from(fungible_asset), ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_note_creation_script)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_note_creation_script)?; // construct the transaction context mock_chain diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs index 5f963ccd25..00c8078cc4 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs @@ -14,14 +14,14 @@ static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// It reexports the procedures from `miden::contracts::auth::basic`. When linking against this /// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`TransactionKernel::assembler()`][kasm]. The procedures +/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: /// - `auth_tx_ecdsa_k256_keccak`, which can be used to verify a signature provided via the advice /// stack to authenticate a transaction. /// /// This component supports all account types. /// -/// [kasm]: crate::transaction::TransactionKernel::assembler +/// [builder]: crate::utils::CodeBuilder pub struct AuthEcdsaK256Keccak { pub_key: PublicKeyCommitment, } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs index eed966da81..9d0795915d 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs @@ -14,7 +14,7 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// It reexports the procedures from `miden::contracts::auth::basic`. When linking against this /// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`TransactionKernel::assembler()`][kasm]. The procedures +/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: /// - `auth_tx_rpo_falcon512`, which can be used to verify a signature provided via the advice stack /// to authenticate a transaction. @@ -25,7 +25,7 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// - [`Self::public_key_slot`]: Public key /// -/// [kasm]: crate::transaction::TransactionKernel::assembler +/// [builder]: crate::utils::CodeBuilder pub struct AuthRpoFalcon512 { pub_key: PublicKeyCommitment, } diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 5784166927..6ce02e36f4 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -45,7 +45,7 @@ procedure_digest!( /// It reexports the procedures from `miden::contracts::faucets::basic_fungible`. When linking /// against this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be /// available to the assembler which is the case when using -/// [`TransactionKernel::assembler()`][kasm]. The procedures of this component are: +/// [`CodeBuilder`][builder]. The procedures of this component are: /// - `distribute`, which mints an assets and create a note for the provided recipient. /// - `burn`, which burns the provided asset. /// @@ -60,7 +60,7 @@ procedure_digest!( /// /// - [`Self::metadata_slot`]: Fungible faucet metadata /// -/// [kasm]: crate::transaction::TransactionKernel::assembler +/// [builder]: crate::utils::CodeBuilder pub struct BasicFungibleFaucet { symbol: TokenSymbol, decimals: u8, diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 47d3a7d617..3fe3730df8 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -46,7 +46,7 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// It reexports the procedures from `miden::contracts::faucets::network_fungible`. When linking /// against this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be /// available to the assembler which is the case when using -/// [`TransactionKernel::assembler()`][kasm]. The procedures of this component are: +/// [`CodeBuilder`][builder]. The procedures of this component are: /// - `distribute`, which mints an assets and create a note for the provided recipient. /// - `burn`, which burns the provided asset. /// @@ -54,17 +54,12 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// authentication while `burn` does not require authentication and can be called by anyone. /// Thus, this component must be combined with a component providing authentication. /// -/// This component supports accounts of type [`AccountType::FungibleFaucet`]. -/// -/// Unlike [`super::BasicFungibleFaucet`], this component uses two storage slots: -/// - First slot: Token metadata `[max_supply, decimals, token_symbol, 0]` -/// - Second slot: Owner account ID as a single Word -/// /// ## Storage Layout /// -/// - [`Self::metadata_slot`]: Fungible faucet metadata +/// - [`Self::metadata_slot`]: Fungible faucet metadata. +/// - [`Self::owner_config_slot`]: The owner account of this network facuet. /// -/// [kasm]: crate::transaction::TransactionKernel::assembler +/// [builder]: crate::utils::CodeBuilder pub struct NetworkFungibleFaucet { faucet: BasicFungibleFaucet, owner_account_id: AccountId, diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-lib/src/account/interface/mod.rs index f60e8881d4..2b51c9d17a 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-lib/src/account/interface/mod.rs @@ -24,9 +24,9 @@ use crate::account::components::{ rpo_falcon_512_library, rpo_falcon_512_multisig_library, }; -use crate::errors::ScriptBuilderError; +use crate::errors::CodeBuilderError; use crate::note::WellKnownNote; -use crate::utils::ScriptBuilder; +use crate::utils::CodeBuilder; #[cfg(test)] mod test; @@ -247,7 +247,7 @@ impl AccountInterface { note_creation_source, ); - let tx_script = ScriptBuilder::new(in_debug_mode) + let tx_script = CodeBuilder::new(in_debug_mode) .compile_tx_script(script) .map_err(AccountInterfaceError::InvalidTransactionScript)?; @@ -431,7 +431,7 @@ pub enum AccountInterfaceError { #[error("note created by the basic fungible faucet doesn't contain exactly one asset")] FaucetNoteWithoutAsset, #[error("invalid transaction script")] - InvalidTransactionScript(#[source] ScriptBuilderError), + InvalidTransactionScript(#[source] CodeBuilderError), #[error("invalid sender account: {0}")] InvalidSenderAccount(AccountId), #[error("{} interface does not support the generation of the standard send_note script", interface.name())] diff --git a/crates/miden-lib/src/account/interface/test.rs b/crates/miden-lib/src/account/interface/test.rs index 7f9f7d4c1e..a574e03284 100644 --- a/crates/miden-lib/src/account/interface/test.rs +++ b/crates/miden-lib/src/account/interface/test.rs @@ -1,12 +1,6 @@ -use alloc::string::ToString; -use alloc::sync::Arc; -use alloc::vec::Vec; - use assert_matches::assert_matches; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountBuilder, AccountComponent, AccountType, StorageSlot}; -use miden_objects::assembly::diagnostics::NamedSource; -use miden_objects::assembly::{Assembler, DefaultSourceManager}; +use miden_objects::account::{AccountBuilder, AccountComponent, AccountType}; use miden_objects::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; use miden_objects::note::{ @@ -23,7 +17,7 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, }; -use miden_objects::{AccountError, Felt, NoteError, Word, ZERO}; +use miden_objects::{Felt, NoteError, Word, ZERO}; use crate::AuthScheme; use crate::account::auth::{ @@ -42,8 +36,7 @@ use crate::account::interface::{ use crate::account::wallets::BasicWallet; use crate::note::{create_p2id_note, create_p2ide_note, create_swap_note}; use crate::testing::account_interface::get_public_keys_from_account; -use crate::transaction::TransactionKernel; -use crate::utils::ScriptBuilder; +use crate::utils::CodeBuilder; // DEFAULT NOTES // ================================================================================================ @@ -155,13 +148,11 @@ fn test_custom_account_default_note() { export.basic::receive_asset "; - let account_component = AccountComponent::compile( - account_custom_code_source, - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - vec![], - ) - .unwrap() - .with_supports_all_types(); + let account_code = CodeBuilder::default() + .compile_component_code("test::account_custom", account_custom_code_source) + .unwrap(); + let account_component = + AccountComponent::new(account_code, vec![]).unwrap().with_supports_all_types(); let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let target_account = AccountBuilder::new(mock_seed) @@ -292,7 +283,7 @@ fn test_basic_wallet_custom_notes() { end end "; - let note_script = ScriptBuilder::default().compile_note_script(compatible_source_code).unwrap(); + let note_script = CodeBuilder::default().compile_note_script(compatible_source_code).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); let compatible_custom_note = Note::new(vault.clone(), metadata, recipient); assert_eq!( @@ -320,8 +311,7 @@ fn test_basic_wallet_custom_notes() { end end "; - let note_script = - ScriptBuilder::default().compile_note_script(incompatible_source_code).unwrap(); + let note_script = CodeBuilder::default().compile_note_script(incompatible_source_code).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); let incompatible_custom_note = Note::new(vault, metadata, recipient); assert_eq!( @@ -381,7 +371,7 @@ fn test_basic_fungible_faucet_custom_notes() { end end "; - let note_script = ScriptBuilder::default().compile_note_script(compatible_source_code).unwrap(); + let note_script = CodeBuilder::default().compile_note_script(compatible_source_code).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); let compatible_custom_note = Note::new(vault.clone(), metadata, recipient); assert_eq!( @@ -411,8 +401,7 @@ fn test_basic_fungible_faucet_custom_notes() { end end "; - let note_script = - ScriptBuilder::default().compile_note_script(incompatible_source_code).unwrap(); + let note_script = CodeBuilder::default().compile_note_script(incompatible_source_code).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); let incompatible_custom_note = Note::new(vault, metadata, recipient); assert_eq!( @@ -438,14 +427,11 @@ fn test_custom_account_custom_notes() { end "; - let account_component = AccountComponent::compile_with_path( - account_custom_code_source, - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - vec![], - "test::account::component_1", - ) - .unwrap() - .with_supports_all_types(); + let account_code = CodeBuilder::default() + .compile_component_code("test::account::component_1", account_custom_code_source) + .unwrap(); + let account_component = + AccountComponent::new(account_code, vec![]).unwrap().with_supports_all_types(); let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let target_account = AccountBuilder::new(mock_seed) @@ -494,8 +480,8 @@ fn test_custom_account_custom_notes() { end end "; - let note_script = ScriptBuilder::default() - .with_dynamically_linked_library(account_component.library()) + let note_script = CodeBuilder::default() + .with_dynamically_linked_library(account_component.component_code()) .unwrap() .compile_note_script(compatible_source_code) .unwrap(); @@ -521,8 +507,8 @@ fn test_custom_account_custom_notes() { end end "; - let note_script = ScriptBuilder::default() - .with_dynamically_linked_library(account_component.library()) + let note_script = CodeBuilder::default() + .with_dynamically_linked_library(account_component.component_code()) .unwrap() .compile_note_script(incompatible_source_code) .unwrap(); @@ -551,14 +537,11 @@ fn test_custom_account_multiple_components_custom_notes() { end "; - let custom_component = AccountComponent::compile_with_path( - account_custom_code_source, - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - vec![], - "test::account::component_1", - ) - .unwrap() - .with_supports_all_types(); + let custom_code = CodeBuilder::default() + .compile_component_code("test::account::component_1", account_custom_code_source) + .unwrap(); + let custom_component = + AccountComponent::new(custom_code, vec![]).unwrap().with_supports_all_types(); let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let target_account = AccountBuilder::new(mock_seed) @@ -615,8 +598,8 @@ fn test_custom_account_multiple_components_custom_notes() { end end "; - let note_script = ScriptBuilder::default() - .with_dynamically_linked_library(custom_component.library()) + let note_script = CodeBuilder::default() + .with_dynamically_linked_library(custom_component.component_code()) .unwrap() .compile_note_script(compatible_source_code) .unwrap(); @@ -654,8 +637,8 @@ fn test_custom_account_multiple_components_custom_notes() { end end "; - let note_script = ScriptBuilder::default() - .with_dynamically_linked_library(custom_component.library()) + let note_script = CodeBuilder::default() + .with_dynamically_linked_library(custom_component.component_code()) .unwrap() .compile_note_script(incompatible_source_code) .unwrap(); @@ -667,48 +650,9 @@ fn test_custom_account_multiple_components_custom_notes() { ); } -// HELPER TRAIT +// HELPERS // ================================================================================================ -/// [AccountComponentExt] is a helper trait which only implements the `compile_with_path` procedure -/// for testing purposes. -trait AccountComponentExt { - fn compile_with_path( - source_code: impl ToString, - assembler: Assembler, - storage_slots: Vec, - library_path: impl AsRef, - ) -> Result; -} - -impl AccountComponentExt for AccountComponent { - /// Returns a new [`AccountComponent`] whose library is compiled from the provided `source_code` - /// using the specified `assembler`, `library_path`, and with the given `storage_slots`. - /// - /// All procedures exported from the provided code will become members of the account's public - /// interface when added to an [`AccountCode`](crate::account::AccountCode), and could be called - /// using the provided library path. - /// - /// # Errors - /// - /// Returns an error if: - /// - the compilation of the provided source code fails. - /// - The number of storage slots exceeds 255. - fn compile_with_path( - source_code: impl ToString, - assembler: Assembler, - storage_slots: Vec, - library_path: impl AsRef, - ) -> Result { - let source = NamedSource::new(library_path, source_code.to_string()); - let library = assembler - .assemble_library([source]) - .map_err(AccountError::AccountComponentAssemblyError)?; - - Self::new(library, storage_slots) - } -} - /// Helper function to create a mock auth component for testing fn get_mock_auth_component() -> AuthRpoFalcon512 { let mock_word = Word::from([0, 1, 2, 3u32]); diff --git a/crates/miden-lib/src/account/wallets/mod.rs b/crates/miden-lib/src/account/wallets/mod.rs index 6bd874271b..3fc7851efd 100644 --- a/crates/miden-lib/src/account/wallets/mod.rs +++ b/crates/miden-lib/src/account/wallets/mod.rs @@ -43,7 +43,7 @@ procedure_digest!( /// /// It reexports the procedures from `miden::contracts::wallets::basic`. When linking against this /// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`TransactionKernel::assembler()`][kasm]. The procedures +/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: /// - `receive_asset`, which can be used to add an asset to the account. /// - `move_asset_to_note`, which can be used to remove the specified asset from the account and add @@ -54,7 +54,7 @@ procedure_digest!( /// /// This component supports all account types. /// -/// [kasm]: crate::transaction::TransactionKernel::assembler +/// [builder]: crate::utils::CodeBuilder pub struct BasicWallet; impl BasicWallet { diff --git a/crates/miden-lib/src/errors/script_builder_errors.rs b/crates/miden-lib/src/errors/code_builder_errors.rs similarity index 83% rename from crates/miden-lib/src/errors/script_builder_errors.rs rename to crates/miden-lib/src/errors/code_builder_errors.rs index 76fe01e66b..0c2f1913d5 100644 --- a/crates/miden-lib/src/errors/script_builder_errors.rs +++ b/crates/miden-lib/src/errors/code_builder_errors.rs @@ -5,20 +5,20 @@ use core::error::Error; use miden_objects::assembly::diagnostics::Report; use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic; -// SCRIPT BUILDER ERROR +// CODE BUILDER ERROR // ================================================================================================ #[derive(Debug, thiserror::Error)] #[error("failed to build script: {message}")] -pub struct ScriptBuilderError { +pub struct CodeBuilderError { /// Stack size of `Box` is smaller than String. message: Box, - /// thiserror will return this when calling Error::source on ScriptBuilderError. + /// thiserror will return this when calling Error::source on CodeBuilderError. source: Option>, } -impl ScriptBuilderError { - /// Creates a script builder error from an error message and a source error. +impl CodeBuilderError { + /// Creates a code builder error from an error message and a source error. pub fn build_error_with_source( message: impl Into, source: impl Error + Send + Sync + 'static, @@ -30,7 +30,7 @@ impl ScriptBuilderError { } } - /// Creates a script builder error from a context message and a Report. + /// Creates a code builder error from a context message and a Report. /// /// This method uses PrintDiagnostic to stringify the Report since Report doesn't /// implement core::error::Error and cannot be returned as a source error. @@ -48,7 +48,7 @@ mod error_assertions { /// Asserts at compile time that the passed error has Send + Sync + 'static bounds. fn _assert_error_is_send_sync_static(_: E) {} - fn _assert_script_builder_error_bounds(err: ScriptBuilderError) { + fn _assert_code_builder_error_bounds(err: CodeBuilderError) { _assert_error_is_send_sync_static(err); } } diff --git a/crates/miden-lib/src/errors/mod.rs b/crates/miden-lib/src/errors/mod.rs index 6bd4bda685..32a5d0b629 100644 --- a/crates/miden-lib/src/errors/mod.rs +++ b/crates/miden-lib/src/errors/mod.rs @@ -9,8 +9,8 @@ pub mod note_script_errors; mod masm_error; pub use masm_error::MasmError; -mod script_builder_errors; -pub use script_builder_errors::ScriptBuilderError; +mod code_builder_errors; +pub use code_builder_errors::CodeBuilderError; mod transaction_errors; pub use transaction_errors::{TransactionEventError, TransactionTraceParsingError}; diff --git a/crates/miden-lib/src/testing/account_component/conditional_auth.rs b/crates/miden-lib/src/testing/account_component/conditional_auth.rs index 40e847e26f..fbc3a7d989 100644 --- a/crates/miden-lib/src/testing/account_component/conditional_auth.rs +++ b/crates/miden-lib/src/testing/account_component/conditional_auth.rs @@ -1,10 +1,9 @@ use alloc::string::String; -use miden_objects::account::AccountComponent; -use miden_objects::assembly::Library; +use miden_objects::account::{AccountComponent, AccountComponentCode}; use miden_objects::utils::sync::LazyLock; -use crate::transaction::TransactionKernel; +use crate::utils::CodeBuilder; pub const ERR_WRONG_ARGS_MSG: &str = "auth procedure args are incorrect"; @@ -34,9 +33,9 @@ static CONDITIONAL_AUTH_CODE: LazyLock = LazyLock::new(|| { ) }); -static CONDITIONAL_AUTH_LIBRARY: LazyLock = LazyLock::new(|| { - TransactionKernel::assembler() - .assemble_library([CONDITIONAL_AUTH_CODE.as_str()]) +static CONDITIONAL_AUTH_LIBRARY: LazyLock = LazyLock::new(|| { + CodeBuilder::default() + .compile_component_code("mock::conditional_auth", CONDITIONAL_AUTH_CODE.as_str()) .expect("conditional auth code should be valid") }); diff --git a/crates/miden-lib/src/testing/account_component/incr_nonce.rs b/crates/miden-lib/src/testing/account_component/incr_nonce.rs index 1af38a1529..a505205dad 100644 --- a/crates/miden-lib/src/testing/account_component/incr_nonce.rs +++ b/crates/miden-lib/src/testing/account_component/incr_nonce.rs @@ -2,7 +2,7 @@ use miden_objects::account::AccountComponent; use miden_objects::assembly::Library; use miden_objects::utils::sync::LazyLock; -use crate::transaction::TransactionKernel; +use crate::utils::CodeBuilder; const INCR_NONCE_AUTH_CODE: &str = " use.miden::native_account @@ -13,12 +13,14 @@ const INCR_NONCE_AUTH_CODE: &str = " "; static INCR_NONCE_AUTH_LIBRARY: LazyLock = LazyLock::new(|| { - TransactionKernel::assembler() - .assemble_library([INCR_NONCE_AUTH_CODE]) + CodeBuilder::default() + .compile_component_code("incr_nonce", INCR_NONCE_AUTH_CODE) .expect("incr nonce code should be valid") + .into_library() }); -/// Creates a mock authentication [`AccountComponent`] for testing purposes. +/// Creates a mock authentication [`AccountComponent`] for testing purposes under the "incr_nonce" +/// namespace. /// /// The component defines an `auth_incr_nonce` procedure that always increments the nonce by 1. pub struct IncrNonceAuthComponent; diff --git a/crates/miden-lib/src/testing/mock_account_code.rs b/crates/miden-lib/src/testing/mock_account_code.rs index 9176d5c261..86a3d4f888 100644 --- a/crates/miden-lib/src/testing/mock_account_code.rs +++ b/crates/miden-lib/src/testing/mock_account_code.rs @@ -1,9 +1,8 @@ use miden_objects::account::AccountCode; use miden_objects::assembly::Library; -use miden_objects::assembly::diagnostics::NamedSource; use miden_objects::utils::sync::LazyLock; -use crate::transaction::TransactionKernel; +use crate::utils::CodeBuilder; const MOCK_FAUCET_CODE: &str = " use.miden::faucet @@ -140,19 +139,17 @@ const MOCK_ACCOUNT_CODE: &str = " "; static MOCK_FAUCET_LIBRARY: LazyLock = LazyLock::new(|| { - let source = NamedSource::new("mock::faucet", MOCK_FAUCET_CODE); - TransactionKernel::assembler() - .with_debug_mode(cfg!(feature = "with-debug-info")) - .assemble_library([source]) + CodeBuilder::default() + .compile_component_code("mock::faucet", MOCK_FAUCET_CODE) .expect("mock faucet code should be valid") + .into_library() }); static MOCK_ACCOUNT_LIBRARY: LazyLock = LazyLock::new(|| { - let source = NamedSource::new("mock::account", MOCK_ACCOUNT_CODE); - TransactionKernel::assembler() - .with_debug_mode(cfg!(feature = "with-debug-info")) - .assemble_library([source]) + CodeBuilder::default() + .compile_component_code("mock::account", MOCK_ACCOUNT_CODE) .expect("mock account code should be valid") + .into_library() }); // MOCK ACCOUNT CODE EXT diff --git a/crates/miden-lib/src/testing/mock_util_lib.rs b/crates/miden-lib/src/testing/mock_util_lib.rs index 5f6c99cb37..7be47cb256 100644 --- a/crates/miden-lib/src/testing/mock_util_lib.rs +++ b/crates/miden-lib/src/testing/mock_util_lib.rs @@ -1,8 +1,7 @@ use miden_objects::assembly::Library; -use miden_objects::assembly::diagnostics::NamedSource; use miden_objects::utils::sync::LazyLock; -use crate::transaction::TransactionKernel; +use crate::utils::CodeBuilder; const MOCK_UTIL_LIBRARY_CODE: &str = " use.miden::output_note @@ -36,10 +35,10 @@ const MOCK_UTIL_LIBRARY_CODE: &str = " "; static MOCK_UTIL_LIBRARY: LazyLock = LazyLock::new(|| { - let source = NamedSource::new("mock::util", MOCK_UTIL_LIBRARY_CODE); - TransactionKernel::assembler() - .assemble_library([source]) + CodeBuilder::new(false) + .compile_component_code("mock::util", MOCK_UTIL_LIBRARY_CODE) .expect("mock util library should be valid") + .into_library() }); /// Returns the mock test [`Library`] under the `mock::util` namespace. diff --git a/crates/miden-lib/src/testing/note.rs b/crates/miden-lib/src/testing/note.rs index 34c9e372b7..f6d590a008 100644 --- a/crates/miden-lib/src/testing/note.rs +++ b/crates/miden-lib/src/testing/note.rs @@ -13,7 +13,6 @@ use miden_objects::note::{ NoteInputs, NoteMetadata, NoteRecipient, - NoteScript, NoteTag, NoteType, }; @@ -21,7 +20,7 @@ use miden_objects::testing::note::DEFAULT_NOTE_CODE; use miden_objects::{Felt, NoteError, Word, ZERO}; use rand::Rng; -use crate::transaction::TransactionKernel; +use crate::utils::CodeBuilder; // NOTE BUILDER // ================================================================================================ @@ -144,21 +143,16 @@ impl NoteBuilder { self.code, ); - let mut assembler = TransactionKernel::assembler_with_source_manager(self.source_manager) - .with_debug_mode(true); + let mut builder = CodeBuilder::with_source_manager(self.source_manager.clone()); for dyn_library in self.dyn_libraries { - assembler - .link_dynamic_library(dyn_library) + builder + .link_dynamic_library(&dyn_library) .expect("library should link successfully"); } - let code = assembler - .clone() - .with_debug_mode(true) - .assemble_program(virtual_source_file) - .unwrap(); - - let note_script = NoteScript::new(code); + 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, diff --git a/crates/miden-lib/src/transaction/mod.rs b/crates/miden-lib/src/transaction/mod.rs index a81c44adcb..2c592597fb 100644 --- a/crates/miden-lib/src/transaction/mod.rs +++ b/crates/miden-lib/src/transaction/mod.rs @@ -441,58 +441,6 @@ impl TransactionKernel { Library::read_from_bytes(Self::KERNEL_TESTING_LIB_BYTES) .expect("failed to deserialize transaction kernel library") } - - /// Returns the mock account and faucet libraries. - pub fn mock_libraries() -> impl Iterator { - use miden_objects::account::AccountCode; - - use crate::testing::mock_account_code::MockAccountCodeExt; - - vec![AccountCode::mock_account_library(), AccountCode::mock_faucet_library()].into_iter() - } - - /// Returns an [`Assembler`] with the transaction kernel as a library. - /// - /// This assembler is the same as [`TransactionKernel::assembler`] but additionally includes the - /// kernel library on the namespace of `$kernel`. The `$kernel` library is added separately - /// because even though the library (`api.masm`) and the kernel binary (`main.masm`) include - /// this code, it is not otherwise accessible. By adding it separately, we can invoke procedures - /// from the kernel library to test them individually. - pub fn with_kernel_library(source_manager: Arc) -> Assembler { - Self::assembler_with_source_manager(source_manager) - .with_dynamic_library(Self::library()) - .expect("failed to load kernel library (/lib)") - .with_debug_mode(true) - } - - /// Returns an [`Assembler`] with the `mock::{account, faucet, util}` libraries. - /// - /// This assembler is the same as [`TransactionKernel::with_kernel_library`] but additionally - /// includes: - /// - [`MockAccountCodeExt::mock_account_library`][account_lib], - /// - [`MockAccountCodeExt::mock_faucet_library`][faucet_lib], - /// - [`mock_util_library`][util_lib] - /// - /// [account_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_account_library - /// [faucet_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_faucet_library - /// [util_lib]: crate::testing::mock_util_lib::mock_util_library - pub fn with_mock_libraries(source_manager: Arc) -> Assembler { - use crate::testing::mock_util_lib::mock_util_library; - - let mut assembler = Self::with_kernel_library(source_manager); - - for library in Self::mock_libraries() { - assembler - .link_dynamic_library(library) - .expect("failed to add mock account libraries"); - } - - assembler - .link_static_library(mock_util_library()) - .expect("failed to add mock test library"); - - assembler - } } impl SequentialCommit for TransactionKernel { @@ -505,7 +453,7 @@ impl SequentialCommit for TransactionKernel { } #[cfg(all(any(feature = "testing", test), feature = "std"))] -mod source_manager_ext { +pub(crate) mod source_manager_ext { use std::path::{Path, PathBuf}; use std::vec::Vec; use std::{fs, io}; diff --git a/crates/miden-lib/src/utils/script_builder.rs b/crates/miden-lib/src/utils/code_builder.rs similarity index 62% rename from crates/miden-lib/src/utils/script_builder.rs rename to crates/miden-lib/src/utils/code_builder.rs index e2df108f5e..e7c48cf3c8 100644 --- a/crates/miden-lib/src/utils/script_builder.rs +++ b/crates/miden-lib/src/utils/code_builder.rs @@ -1,26 +1,27 @@ -use alloc::string::String; use alloc::sync::Arc; -use miden_objects::assembly::diagnostics::NamedSource; +use miden_objects::account::AccountComponentCode; use miden_objects::assembly::{ Assembler, DefaultSourceManager, Library, LibraryPath, + Parse, + ParseOptions, SourceManagerSync, }; use miden_objects::note::NoteScript; use miden_objects::transaction::TransactionScript; -use crate::errors::ScriptBuilderError; +use crate::errors::CodeBuilderError; use crate::transaction::TransactionKernel; -// SCRIPT BUILDER +// CODE BUILDER // ================================================================================================ /// A builder for compiling note scripts and transaction scripts with optional library dependencies. /// -/// The ScriptBuilder simplifies the process of creating transaction scripts by providing: +/// The [`CodeBuilder`] simplifies the process of creating transaction scripts by providing: /// - A clean API for adding multiple libraries with static or dynamic linking /// - Automatic assembler configuration with all added libraries /// - Debug mode support @@ -41,51 +42,51 @@ use crate::transaction::TransactionKernel; /// /// ## Typical Workflow /// -/// 1. Create a new ScriptBuilder with debug mode preference +/// 1. Create a new CodeBuilder with debug mode preference /// 2. Add any required modules using `link_module()` or `with_linked_module()` /// 3. Add libraries using `link_static_library()` / `link_dynamic_library()` as appropriate -/// 4. Compile your script with `compile_note_script()` or `compile_tx_script()` +/// 4. Parse your script with `parse_note_script()` or `parse_tx_script()` /// -/// Note that the compilation methods consume the ScriptBuilder, so if you need to compile +/// Note that the Compiling methods consume the CodeBuilder, so if you need to parse /// multiple scripts with the same configuration, you should clone the builder first. /// /// ## Builder Pattern Example /// /// ```no_run /// # use anyhow::Context; -/// # use miden_lib::utils::ScriptBuilder; +/// # use miden_lib::utils::CodeBuilder; /// # use miden_objects::assembly::Library; /// # use miden_stdlib::StdLibrary; /// # fn example() -> anyhow::Result<()> { /// # let module_code = "export.test push.1 add end"; /// # let script_code = "begin nop end"; /// # // Create sample libraries for the example -/// # let my_lib = StdLibrary::default().into(); // Convert StdLibrary to Library -/// # let fpi_lib = StdLibrary::default().into(); -/// let script = ScriptBuilder::default() +/// # let my_lib: Library = StdLibrary::default().into(); // Convert StdLibrary to Library +/// # let fpi_lib: Library = StdLibrary::default().into(); +/// let script = CodeBuilder::default() /// .with_linked_module("my::module", module_code).context("failed to link module")? /// .with_statically_linked_library(&my_lib).context("failed to link static library")? /// .with_dynamically_linked_library(&fpi_lib).context("failed to link dynamic library")? // For FPI calls -/// .compile_tx_script(script_code).context("failed to compile tx script")?; +/// .compile_tx_script(script_code).context("failed to parse tx script")?; /// # Ok(()) /// # } /// ``` /// /// # Note -/// The ScriptBuilder automatically includes the `miden` and `std` libraries, which +/// The CodeBuilder automatically includes the `miden` and `std` libraries, which /// provide access to transaction kernel procedures. Due to being available on-chain /// these libraries are linked dynamically and do not add to the size of built script. #[derive(Clone)] -pub struct ScriptBuilder { +pub struct CodeBuilder { assembler: Assembler, source_manager: Arc, } -impl ScriptBuilder { +impl CodeBuilder { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new ScriptBuilder with the specified debug mode. + /// Creates a new CodeBuilder with the specified debug mode. /// /// # Arguments /// * `in_debug_mode` - Whether to enable debug mode in the assembler @@ -96,7 +97,7 @@ impl ScriptBuilder { Self { assembler, source_manager } } - /// Creates a new ScriptBuilder with the specified source manager. + /// Creates a new CodeBuilder with the specified source manager. /// /// The returned builder is instantiated with debug mode enabled. /// @@ -111,7 +112,7 @@ impl ScriptBuilder { // LIBRARY MANAGEMENT // -------------------------------------------------------------------------------------------- - /// Compiles and links a module to the script builder. + /// Parses and links a module to the code builder. /// /// This method compiles the provided module code and adds it directly to the assembler /// for use in script compilation. @@ -128,20 +129,27 @@ impl ScriptBuilder { pub fn link_module( &mut self, module_path: impl AsRef, - module_code: impl AsRef, - ) -> Result<(), ScriptBuilderError> { + module_code: impl Parse, + ) -> Result<(), CodeBuilderError> { // Parse the library path let lib_path = LibraryPath::new(module_path.as_ref()).map_err(|err| { - ScriptBuilderError::build_error_with_source( + CodeBuilderError::build_error_with_source( format!("invalid module path: {}", module_path.as_ref()), err, ) })?; - let module = NamedSource::new(format!("{lib_path}"), String::from(module_code.as_ref())); + let mut parse_options = ParseOptions::for_library(); + parse_options.path = Some(lib_path); + + let module = module_code + .parse_with_options(self.source_manager.as_ref(), parse_options) + .map_err(|err| { + CodeBuilderError::build_error_with_report("failed to parse module code", err) + })?; self.assembler.compile_and_statically_link(module).map_err(|err| { - ScriptBuilderError::build_error_with_report("failed to assemble module", err) + CodeBuilderError::build_error_with_report("failed to assemble module", err) })?; Ok(()) @@ -158,9 +166,9 @@ impl ScriptBuilder { /// # Errors /// Returns an error if: /// - adding the library to the assembler failed - pub fn link_static_library(&mut self, library: &Library) -> Result<(), ScriptBuilderError> { + pub fn link_static_library(&mut self, library: &Library) -> Result<(), CodeBuilderError> { self.assembler.link_static_library(library).map_err(|err| { - ScriptBuilderError::build_error_with_report("failed to add static library", err) + CodeBuilderError::build_error_with_report("failed to add static library", err) }) } @@ -177,9 +185,9 @@ impl ScriptBuilder { /// /// # Errors /// Returns an error if the library cannot be added to the assembler - pub fn link_dynamic_library(&mut self, library: &Library) -> Result<(), ScriptBuilderError> { + pub fn link_dynamic_library(&mut self, library: &Library) -> Result<(), CodeBuilderError> { self.assembler.link_dynamic_library(library).map_err(|err| { - ScriptBuilderError::build_error_with_report("failed to add dynamic library", err) + CodeBuilderError::build_error_with_report("failed to add dynamic library", err) }) } @@ -195,7 +203,7 @@ impl ScriptBuilder { pub fn with_statically_linked_library( mut self, library: &Library, - ) -> Result { + ) -> Result { self.link_static_library(library)?; Ok(self) } @@ -211,9 +219,9 @@ impl ScriptBuilder { /// Returns an error if the library cannot be added to the assembler pub fn with_dynamically_linked_library( mut self, - library: &Library, - ) -> Result { - self.link_dynamic_library(library)?; + library: impl AsRef, + ) -> Result { + self.link_dynamic_library(library.as_ref())?; Ok(self) } @@ -230,55 +238,93 @@ impl ScriptBuilder { pub fn with_linked_module( mut self, module_path: impl AsRef, - module_code: impl AsRef, - ) -> Result { + module_code: impl Parse, + ) -> Result { self.link_module(module_path, module_code)?; Ok(self) } - // SCRIPT COMPILATION + // COMPILATION // -------------------------------------------------------------------------------------------- - /// Compiles a transaction script with the provided program code. + /// Compiles the provided module path and MASM code into an [`AccountComponentCode`]. + /// The resulting code can be used to create account components. /// - /// The compiled script will have access to all modules that have been added to this builder. + /// # Arguments + /// * `component_path` - The path to the account code module (e.g., `my_account::my_module`) + /// * `component_code` - The account component source code + /// + /// # Errors + /// Returns an error if: + /// - Compiling the account component code fails + /// - If `component_path` is not a valid [`LibraryPath`] + pub fn compile_component_code( + self, + component_path: impl AsRef, + component_code: impl Parse, + ) -> Result { + let CodeBuilder { assembler, source_manager } = self; + let component_path = component_path.as_ref(); + let lib_path = LibraryPath::new(component_path).map_err(|err| { + CodeBuilderError::build_error_with_source( + format!("invalid component path: {component_path}"), + err, + ) + })?; + + let mut parse_options = ParseOptions::for_library(); + parse_options.path = Some(lib_path); + + let module = component_code + .parse_with_options(source_manager.as_ref(), parse_options) + .map_err(|err| { + CodeBuilderError::build_error_with_report("failed to parse component code", err) + })?; + + let library = assembler.assemble_library([module]).map_err(|err| { + CodeBuilderError::build_error_with_report("failed to parse component code", err) + })?; + + Ok(AccountComponentCode::from(library)) + } + + /// Compiles the provided MASM code into a [`TransactionScript`]. + /// + /// The parsed script will have access to all modules that have been added to this builder. /// /// # Arguments - /// * `program` - The transaction script source code + /// * `tx_script` - The transaction script source code /// /// # Errors /// Returns an error if: - /// - The transaction script compilation fails + /// - The transaction script compiling fails pub fn compile_tx_script( self, - tx_script: impl AsRef, - ) -> Result { + tx_script: impl Parse, + ) -> Result { let assembler = self.assembler; - let program = assembler.assemble_program(tx_script.as_ref()).map_err(|err| { - ScriptBuilderError::build_error_with_report("failed to compile transaction script", err) + let program = assembler.assemble_program(tx_script).map_err(|err| { + CodeBuilderError::build_error_with_report("failed to parse transaction script", err) })?; Ok(TransactionScript::new(program)) } - /// Compiles a note script with the provided program code. + /// Compiles the provided MASM code into a [`NoteScript`]. /// - /// The compiled script will have access to all modules that have been added to this builder. + /// The parsed script will have access to all modules that have been added to this builder. /// /// # Arguments /// * `program` - The note script source code /// /// # Errors /// Returns an error if: - /// - The note script compilation fails - pub fn compile_note_script( - self, - program: impl AsRef, - ) -> Result { + /// - The note script compiling fails + pub fn compile_note_script(self, program: impl Parse) -> Result { let assembler = self.assembler; - let program = assembler.assemble_program(program.as_ref()).map_err(|err| { - ScriptBuilderError::build_error_with_report("failed to compile note script", err) + let program = assembler.assemble_program(program).map_err(|err| { + CodeBuilderError::build_error_with_report("failed to parse note script", err) })?; Ok(NoteScript::new(program)) } @@ -294,9 +340,25 @@ impl ScriptBuilder { // TESTING CONVENIENCE FUNCTIONS // -------------------------------------------------------------------------------------------- - /// Returns a [`ScriptBuilder`] with the `mock::{account, faucet, util}` libraries. + /// Returns a [`CodeBuilder`] with the transaction kernel as a library. + /// + /// This assembler is the same as [`TransactionKernel::assembler`] but additionally includes the + /// kernel library on the namespace of `$kernel`. The `$kernel` library is added separately + /// because even though the library (`api.masm`) and the kernel binary (`main.masm`) include + /// this code, it is not otherwise accessible. By adding it separately, we can invoke procedures + /// from the kernel library to test them individually. + #[cfg(any(feature = "testing", test))] + pub fn with_kernel_library(source_manager: Arc) -> Self { + let mut builder = Self::with_source_manager(source_manager); + builder + .link_dynamic_library(&TransactionKernel::library()) + .expect("failed to link kernel library"); + builder + } + + /// Returns a [`CodeBuilder`] with the `mock::{account, faucet, util}` libraries. /// - /// This script builder includes: + /// This assembler includes: /// - [`MockAccountCodeExt::mock_account_library`][account_lib], /// - [`MockAccountCodeExt::mock_faucet_library`][faucet_lib], /// - [`mock_util_library`][util_lib] @@ -305,46 +367,84 @@ impl ScriptBuilder { /// [faucet_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_faucet_library /// [util_lib]: crate::testing::mock_util_lib::mock_util_library #[cfg(any(feature = "testing", test))] - pub fn with_mock_libraries() -> Result { + pub fn with_mock_libraries() -> Self { + Self::with_mock_libraries_with_source_manager(Arc::new(DefaultSourceManager::default())) + } + + /// Returns the mock account and faucet libraries used in testing. + #[cfg(any(feature = "testing", test))] + pub fn mock_libraries() -> impl Iterator { use miden_objects::account::AccountCode; use crate::testing::mock_account_code::MockAccountCodeExt; + + vec![AccountCode::mock_account_library(), AccountCode::mock_faucet_library()].into_iter() + } + + #[cfg(any(feature = "testing", test))] + pub fn with_mock_libraries_with_source_manager( + source_manager: Arc, + ) -> Self { use crate::testing::mock_util_lib::mock_util_library; - Self::new(true) - .with_dynamically_linked_library(&AccountCode::mock_account_library())? - .with_dynamically_linked_library(&AccountCode::mock_faucet_library())? - .with_statically_linked_library(&mock_util_library()) + // Start from the full kernel-aware assembler (includes stdlib and miden-lib). + let mut assembler = + TransactionKernel::assembler_with_source_manager(source_manager.clone()) + .with_debug_mode(true); + + // Expose kernel procedures under `$kernel` for testing. + assembler + .link_dynamic_library(TransactionKernel::library()) + .expect("failed to link kernel library"); + + // Add mock account/faucet libs (built in debug mode) and mock util. + for library in Self::mock_libraries() { + assembler + .link_dynamic_library(library) + .expect("failed to link mock account libraries"); + } + assembler + .link_static_library(mock_util_library()) + .expect("failed to link mock util library"); + + Self { assembler, source_manager } } } -impl Default for ScriptBuilder { +impl Default for CodeBuilder { fn default() -> Self { Self::new(true) } } +impl From for Assembler { + fn from(builder: CodeBuilder) -> Self { + builder.assembler + } +} + // TESTS // ================================================================================================ #[cfg(test)] mod tests { use anyhow::Context; + use miden_objects::assembly::diagnostics::NamedSource; use super::*; #[test] - fn test_script_builder_new() { - let _builder = ScriptBuilder::default(); + fn test_code_builder_new() { + let _builder = CodeBuilder::default(); // Test that the builder can be created successfully } #[test] - fn test_script_builder_basic_script_compilation() -> anyhow::Result<()> { - let builder = ScriptBuilder::default(); + fn test_code_builder_basic_script_compiling() -> anyhow::Result<()> { + let builder = CodeBuilder::default(); builder .compile_tx_script("begin nop end") - .context("failed to compile basic tx script")?; + .context("failed to parse basic tx script")?; Ok(()) } @@ -375,19 +475,19 @@ mod tests { let library_path = "external_contract::counter_contract"; - let mut builder_with_lib = ScriptBuilder::default(); + let mut builder_with_lib = CodeBuilder::default(); builder_with_lib .link_module(library_path, account_code) .context("failed to link module")?; builder_with_lib .compile_tx_script(script_code) - .context("failed to compile tx script")?; + .context("failed to parse tx script")?; Ok(()) } #[test] - fn test_compile_library_and_add_to_builder() -> anyhow::Result<()> { + fn test_parse_library_and_add_to_builder() -> anyhow::Result<()> { let script_code = " use.external_contract::counter_contract @@ -414,16 +514,16 @@ mod tests { let library_path = "external_contract::counter_contract"; // Test single library - let mut builder_with_lib = ScriptBuilder::default(); + let mut builder_with_lib = CodeBuilder::default(); builder_with_lib .link_module(library_path, account_code) .context("failed to link module")?; builder_with_lib .compile_tx_script(script_code) - .context("failed to compile tx script")?; + .context("failed to parse tx script")?; // Test multiple libraries - let mut builder_with_libs = ScriptBuilder::default(); + let mut builder_with_libs = CodeBuilder::default(); builder_with_libs .link_module(library_path, account_code) .context("failed to link first module")?; @@ -432,7 +532,7 @@ mod tests { .context("failed to link second module")?; builder_with_libs .compile_tx_script(script_code) - .context("failed to compile tx script with multiple libraries")?; + .context("failed to parse tx script with multiple libraries")?; Ok(()) } @@ -463,11 +563,11 @@ mod tests { "; // Test builder-style chaining with modules - let builder = ScriptBuilder::default() + let builder = CodeBuilder::default() .with_linked_module("external_contract::counter_contract", account_code) .context("failed to link module")?; - builder.compile_tx_script(script_code).context("failed to compile tx script")?; + builder.compile_tx_script(script_code).context("failed to parse tx script")?; Ok(()) } @@ -478,13 +578,13 @@ mod tests { "use.test::lib1 use.test::lib2 begin exec.lib1::test1 exec.lib2::test2 end"; // Test chaining multiple modules - let builder = ScriptBuilder::default() + let builder = CodeBuilder::default() .with_linked_module("test::lib1", "export.test1 push.1 add end") .context("failed to link first module")? .with_linked_module("test::lib2", "export.test2 push.2 add end") .context("failed to link second module")?; - builder.compile_tx_script(script_code).context("failed to compile tx script")?; + builder.compile_tx_script(script_code).context("failed to parse tx script")?; Ok(()) } @@ -492,42 +592,22 @@ mod tests { #[test] fn test_static_and_dynamic_linking() -> anyhow::Result<()> { let script_code = " - use.external_contract::contract_1 - use.external_contract::contract_2 + use.contracts::static_contract begin - call.contract_1::increment_1 - call.contract_2::increment_2 + call.static_contract::increment_1 end "; let account_code_1 = " - use.miden::active_account - use.miden::native_account - use.std::sys - export.increment_1 - push.0 - exec.active_account::get_item - push.1 add - push.0 - exec.native_account::set_item - exec.sys::truncate_stack + push.0 drop end "; let account_code_2 = " - use.miden::active_account - use.miden::native_account - use.std::sys - export.increment_2 - push.0 - exec.active_account::get_item - push.2 add - push.0 - exec.native_account::set_item - exec.sys::truncate_stack + push.0 drop end "; @@ -536,15 +616,15 @@ mod tests { let static_lib = temp_assembler .clone() - .assemble_library([NamedSource::new("external_contract::contract_1", account_code_1)]) + .assemble_library([NamedSource::new("contracts::static_contract", account_code_1)]) .map_err(|e| anyhow::anyhow!("failed to assemble static library: {}", e))?; let dynamic_lib = temp_assembler - .assemble_library([NamedSource::new("external_contract::contract_2", account_code_2)]) + .assemble_library([NamedSource::new("contracts::dynamic_contract", account_code_2)]) .map_err(|e| anyhow::anyhow!("failed to assemble dynamic library: {}", e))?; // Test linking both static and dynamic libraries - let builder = ScriptBuilder::default() + let builder = CodeBuilder::default() .with_statically_linked_library(&static_lib) .context("failed to link static library")? .with_dynamically_linked_library(&dynamic_lib) @@ -552,7 +632,7 @@ mod tests { builder .compile_tx_script(script_code) - .context("failed to compile tx script with static and dynamic libraries")?; + .context("failed to parse tx script with static and dynamic libraries")?; Ok(()) } diff --git a/crates/miden-lib/src/utils/mod.rs b/crates/miden-lib/src/utils/mod.rs index a826650cf9..408cd69922 100644 --- a/crates/miden-lib/src/utils/mod.rs +++ b/crates/miden-lib/src/utils/mod.rs @@ -1,6 +1,6 @@ -pub mod script_builder; +pub mod code_builder; +pub use code_builder::CodeBuilder; pub use miden_objects::utils::*; -pub use script_builder::ScriptBuilder; -pub use crate::errors::ScriptBuilderError; +pub use crate::errors::CodeBuilderError; diff --git a/crates/miden-objects/src/account/code/mod.rs b/crates/miden-objects/src/account/code/mod.rs index e8f471631c..867859c717 100644 --- a/crates/miden-objects/src/account/code/mod.rs +++ b/crates/miden-objects/src/account/code/mod.rs @@ -486,9 +486,8 @@ mod tests { #[test] fn test_account_code_no_auth_component() { - let component = AccountComponent::compile(CODE, Assembler::default(), vec![]) - .unwrap() - .with_supports_all_types(); + let library = Assembler::default().assemble_library([CODE]).unwrap(); + let component = AccountComponent::new(library, vec![]).unwrap().with_supports_all_types(); let err = AccountCode::from_components(&[component], AccountType::RegularAccountUpdatableCode) @@ -524,10 +523,8 @@ mod tests { end "; - let component = - AccountComponent::compile(code_with_multiple_auth, Assembler::default(), vec![]) - .unwrap() - .with_supports_all_types(); + let library = Assembler::default().assemble_library([code_with_multiple_auth]).unwrap(); + let component = AccountComponent::new(library, vec![]).unwrap().with_supports_all_types(); let err = AccountCode::from_components(&[component], AccountType::RegularAccountUpdatableCode) diff --git a/crates/miden-objects/src/account/component/code.rs b/crates/miden-objects/src/account/component/code.rs new file mode 100644 index 0000000000..2ddcdfca53 --- /dev/null +++ b/crates/miden-objects/src/account/component/code.rs @@ -0,0 +1,47 @@ +use miden_assembly::Library; +use miden_processor::MastForest; + +// ACCOUNT COMPONENT CODE +// ================================================================================================ + +/// A [`Library`] that has been assembled for use as component code. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AccountComponentCode(Library); + +impl AccountComponentCode { + /// Returns a reference to the underlying [`Library`] + pub fn as_library(&self) -> &Library { + &self.0 + } + + /// Returns a reference to the code's [`MastForest`] + pub fn mast_forest(&self) -> &MastForest { + self.0.mast_forest().as_ref() + } + + /// Consumes `self` and returns the underlying [`Library`] + pub fn into_library(self) -> Library { + self.0 + } +} + +impl AsRef for AccountComponentCode { + fn as_ref(&self) -> &Library { + self.as_library() + } +} + +// CONVERSIONS +// ================================================================================================ + +impl From for AccountComponentCode { + fn from(value: Library) -> Self { + Self(value) + } +} + +impl From for Library { + fn from(value: AccountComponentCode) -> Self { + value.into_library() + } +} diff --git a/crates/miden-objects/src/account/component/mod.rs b/crates/miden-objects/src/account/component/mod.rs index 518de8b180..6211584d7e 100644 --- a/crates/miden-objects/src/account/component/mod.rs +++ b/crates/miden-objects/src/account/component/mod.rs @@ -1,11 +1,12 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_assembly::{Assembler, Library, Parse}; - // TODO(named_slots): Refactor templates. // mod template; // pub use template::*; +mod code; +pub use code::AccountComponentCode; + use crate::account::{AccountType, StorageSlot}; use crate::assembly::QualifiedProcedureName; use crate::{AccountError, MastForest, Word}; @@ -13,8 +14,8 @@ use crate::{AccountError, MastForest, Word}; // ACCOUNT COMPONENT // ================================================================================================ -/// An [`AccountComponent`] defines a [`Library`] of code and the initial value and types of -/// the [`StorageSlot`]s it accesses. +/// An [`AccountComponent`] defines a [`Library`](miden_assembly::Library) of code and the initial +/// value and types of the [`StorageSlot`]s it accesses. /// /// One or more components can be used to built [`AccountCode`](crate::account::AccountCode) and /// [`AccountStorage`](crate::account::AccountStorage). @@ -30,7 +31,7 @@ use crate::{AccountError, MastForest, Word}; /// is forced to explicitly define what it supports. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AccountComponent { - pub(super) library: Library, + pub(super) code: AccountComponentCode, pub(super) storage_slots: Vec, pub(super) supported_types: BTreeSet, } @@ -53,41 +54,21 @@ impl AccountComponent { /// /// Returns an error if: /// - The number of given [`StorageSlot`]s exceeds 255. - pub fn new(code: Library, storage_slots: Vec) -> Result { + pub fn new( + code: impl Into, + storage_slots: Vec, + ) -> Result { // Check that we have less than 256 storage slots. u8::try_from(storage_slots.len()) .map_err(|_| AccountError::StorageTooManySlots(storage_slots.len() as u64))?; Ok(Self { - library: code, + code: code.into(), storage_slots, supported_types: BTreeSet::new(), }) } - /// Returns a new [`AccountComponent`] whose library is compiled from the provided `source_code` - /// using the specified `assembler` and with the given `storage_slots`. - /// - /// All procedures exported from the provided code will become members of the account's public - /// interface when added to an [`AccountCode`](crate::account::AccountCode). - /// - /// # Errors - /// - /// Returns an error if: - /// - the compilation of the provided source code fails. - /// - The number of storage slots exceeds 255. - pub fn compile( - source_code: impl Parse, - assembler: Assembler, - storage_slots: Vec, - ) -> Result { - let library = assembler - .assemble_library([source_code]) - .map_err(AccountError::AccountComponentAssemblyError)?; - - Self::new(library, storage_slots) - } - /* /// Creates an [`AccountComponent`] from a [`Package`] using [`InitStorageData`]. /// @@ -96,7 +77,8 @@ impl AccountComponent { /// /// # Arguments /// - /// * `package` - The package containing the library and account component metadata + /// * `package` - The package containing the [`Library`](miden_assembly::Library) and account + /// component metadata /// * `init_storage_data` - The initialization data for storage slots /// /// # Errors @@ -121,10 +103,13 @@ impl AccountComponent { }, }; - AccountComponent::from_library(&library, &metadata, init_storage_data) + let component_code = AccountComponentCode::from(library); + + AccountComponent::from_library(&component_code, &metadata, init_storage_data) } - /// Creates an [`AccountComponent`] from a [`Library`] and [`AccountComponentMetadata`]. + /// Creates an [`AccountComponent`] from an [`AccountComponentCode`] and + /// [`AccountComponentMetadata`]. /// /// This method provides type safety by leveraging the component's metadata to validate /// the passed storage initialization data ([`InitStorageData`]). @@ -145,7 +130,7 @@ impl AccountComponent { /// - The storage initialization fails due to invalid or missing data /// - The component creation fails pub fn from_library( - library: &Library, + library: &AccountComponentCode, account_component_metadata: &AccountComponentMetadata, init_storage_data: &InitStorageData, ) -> Result { @@ -171,14 +156,14 @@ impl AccountComponent { .expect("storage slots len should fit in u8 per the constructor") } - /// Returns a reference to the underlying [`Library`] of this component. - pub fn library(&self) -> &Library { - &self.library + /// Returns a reference to the underlying [`AccountComponentCode`] of this component. + pub fn component_code(&self) -> &AccountComponentCode { + &self.code } /// Returns a reference to the underlying [`MastForest`] of this component. pub fn mast_forest(&self) -> &MastForest { - self.library.mast_forest().as_ref() + self.code.mast_forest() } /// Returns a slice of the underlying [`StorageSlot`]s of this component. @@ -199,7 +184,7 @@ impl AccountComponent { /// Returns a vector of tuples (digest, is_auth) for all procedures in this component. pub fn get_procedures(&self) -> Vec<(Word, bool)> { let mut procedures = Vec::new(); - for module in self.library.module_infos() { + for module in self.code.as_library().module_infos() { for (_, procedure_info) in module.procedures() { let is_auth = procedure_info.name.starts_with("auth_"); procedures.push((procedure_info.digest, is_auth)); @@ -214,7 +199,7 @@ impl AccountComponent { &self, proc_name: impl TryInto, ) -> Option { - self.library.get_procedure_root_by_name(proc_name) + self.code.as_library().get_procedure_root_by_name(proc_name) } // MUTATORS @@ -250,9 +235,9 @@ impl AccountComponent { } } -impl From for Library { +impl From for AccountComponentCode { fn from(component: AccountComponent) -> Self { - component.library + component.code } } @@ -266,7 +251,7 @@ mod tests { use miden_assembly::Assembler; use miden_core::utils::Serializable; - use miden_mast_package::{MastArtifact, Package, PackageManifest, Section}; + use miden_mast_package::{MastArtifact, Package, PackageManifest, Section, SectionId}; use semver::Version; use super::*; @@ -330,6 +315,7 @@ mod tests { fn test_from_library_with_init_data() { // Create a simple library for testing let library = Assembler::default().assemble_library([CODE]).unwrap(); + let component_code = AccountComponentCode::from(library.clone()); // Create metadata for the component let metadata = AccountComponentMetadata::new( @@ -347,7 +333,8 @@ mod tests { // Test with empty init data - this tests the complete workflow: // Library + Metadata -> AccountComponent let init_data = InitStorageData::default(); - let component = AccountComponent::from_library(&library, &metadata, &init_data).unwrap(); + let component = + AccountComponent::from_library(&component_code, &metadata, &init_data).unwrap(); // Verify the component was created correctly assert_eq!(component.storage_size(), 0); diff --git a/crates/miden-objects/src/account/component/template/mod.rs b/crates/miden-objects/src/account/component/template/mod.rs index a17eac36fb..5a75e41584 100644 --- a/crates/miden-objects/src/account/component/template/mod.rs +++ b/crates/miden-objects/src/account/component/template/mod.rs @@ -245,6 +245,9 @@ impl AccountComponentMetadata { } } +// CONVERSIONS +// ================================================================================================ + impl TryFrom<&Package> for AccountComponentMetadata { type Error = AccountError; diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 9ee261d829..47764d6c20 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -37,6 +37,7 @@ pub use code::procedure::AccountProcedureInfo; pub mod component; pub use component::{ AccountComponent, + AccountComponentCode, // TODO(named_slots): Uncomment when refactored. // AccountComponentMetadata, // FeltRepresentation, diff --git a/crates/miden-objects/src/testing/account_code.rs b/crates/miden-objects/src/testing/account_code.rs index e8ddbbdbb9..90e0afeb56 100644 --- a/crates/miden-objects/src/testing/account_code.rs +++ b/crates/miden-objects/src/testing/account_code.rs @@ -19,9 +19,10 @@ pub const CODE: &str = " impl AccountCode { /// Creates a mock [AccountCode] with default assembler and mock code pub fn mock() -> AccountCode { - let component = AccountComponent::compile(CODE, Assembler::default(), vec![]) - .unwrap() - .with_supports_all_types(); + let library = Assembler::default() + .assemble_library([CODE]) + .expect("mock account component should assemble"); + let component = AccountComponent::new(library, vec![]).unwrap().with_supports_all_types(); Self::from_components( &[NoopAuthComponent.into(), component], diff --git a/crates/miden-testing/src/executor.rs b/crates/miden-testing/src/executor.rs index a8ae18cc2e..9cc0800179 100644 --- a/crates/miden-testing/src/executor.rs +++ b/crates/miden-testing/src/executor.rs @@ -1,4 +1,6 @@ #[cfg(test)] +use miden_objects::assembly::Assembler; +#[cfg(test)] use miden_processor::DefaultHost; use miden_processor::fast::{ExecutionOutput, FastProcessor}; use miden_processor::{AdviceInputs, AsyncHost, ExecutionError, Program, StackInputs}; @@ -43,12 +45,12 @@ impl CodeExecutor { use alloc::borrow::ToOwned; use alloc::sync::Arc; - use miden_lib::transaction::TransactionKernel; + use miden_lib::utils::CodeBuilder; use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; use miden_objects::assembly::{DefaultSourceManager, SourceManagerSync}; let source_manager: Arc = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::with_kernel_library(source_manager.clone()); + let assembler: Assembler = CodeBuilder::with_kernel_library(source_manager.clone()).into(); // Virtual file name should be unique. let virtual_source_file = diff --git a/crates/miden-testing/src/kernel_tests/block/utils.rs b/crates/miden-testing/src/kernel_tests/block/utils.rs index 20e407616b..e2e35dc488 100644 --- a/crates/miden-testing/src/kernel_tests/block/utils.rs +++ b/crates/miden-testing/src/kernel_tests/block/utils.rs @@ -1,6 +1,6 @@ use std::vec::Vec; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::AccountId; use miden_objects::batch::ProvenBatch; use miden_objects::block::BlockNumber; @@ -110,5 +110,5 @@ fn update_expiration_tx_script(expiration_delta: u16) -> TransactionScript { " ); - ScriptBuilder::default().compile_tx_script(code).unwrap() + CodeBuilder::default().compile_tx_script(code).unwrap() } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 43886564e4..c2fcae7018 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -16,7 +16,7 @@ use miden_lib::errors::tx_kernel_errors::{ use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::mock_account::MockAccountExt; use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{ Account, @@ -143,8 +143,7 @@ pub async fn compute_commitment() -> miette::Result<()> { ); let tx_context_builder = TransactionContextBuilder::new(account); - let tx_script = ScriptBuilder::with_mock_libraries() - .into_diagnostic()? + let tx_script = CodeBuilder::with_mock_libraries() .compile_tx_script(tx_script) .into_diagnostic()?; let tx_context = tx_context_builder @@ -552,7 +551,7 @@ async fn test_account_get_item_fails_on_unknown_slot() -> anyhow::Result<()> { call.account::get_item end "#; - let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(code)?; let result = chain .build_tx_context(account_empty_storage, &[], &[])? @@ -587,7 +586,7 @@ async fn test_account_set_item_fails_on_reserved_faucet_metadata_slot() -> anyho exec.native_account::set_item end "#; - let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + let tx_script = CodeBuilder::default().compile_tx_script(code)?; let tx_context = TransactionContextBuilder::with_fungible_faucet( ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, @@ -1130,7 +1129,7 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { initial_balance + fungible_asset_for_note_existing.unwrap_fungible().amount(), ); - let tx_script = ScriptBuilder::default().compile_tx_script(add_existing_source)?; + let tx_script = CodeBuilder::default().compile_tx_script(add_existing_source)?; let tx_context = mock_chain .build_tx_context( @@ -1183,7 +1182,7 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { final_balance = initial_balance + fungible_asset_for_note_new.unwrap_fungible().amount(), ); - let tx_script = ScriptBuilder::default().compile_tx_script(add_new_source)?; + let tx_script = CodeBuilder::default().compile_tx_script(add_new_source)?; let tx_context = mock_chain .build_tx_context(TxContextInput::AccountId(account.id()), &[], &[p2id_note_new_asset])? @@ -1287,8 +1286,7 @@ async fn test_get_init_balance_subtraction() -> anyhow::Result<()> { initial_balance - fungible_asset_for_note_existing.unwrap_fungible().amount(), ); - let tx_script = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(remove_existing_source)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(remove_existing_source)?; let tx_context = mock_chain .build_tx_context(TxContextInput::AccountId(account.id()), &[], &[])? @@ -1414,8 +1412,7 @@ async fn test_was_procedure_called() -> miette::Result<()> { ); // Compile the transaction script using the testing assembler with mock account - let tx_script = ScriptBuilder::with_mock_libraries() - .into_diagnostic()? + let tx_script = CodeBuilder::with_mock_libraries() .compile_tx_script(tx_script_code) .into_diagnostic()?; @@ -1465,8 +1462,11 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res let external_library = TransactionKernel::assembler().assemble_library([external_library_source])?; - let mut assembler = - TransactionKernel::with_mock_libraries(Arc::new(DefaultSourceManager::default())); + let mut assembler: miden_objects::assembly::Assembler = + CodeBuilder::with_mock_libraries_with_source_manager(Arc::new( + DefaultSourceManager::default(), + )) + .into(); assembler.link_static_library(&external_library)?; let account_component_source = @@ -1493,7 +1493,7 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res .build_existing() .into_diagnostic()?; - let tx_script = ScriptBuilder::default() + let tx_script = CodeBuilder::default() .with_dynamically_linked_library(&account_component_lib) .into_diagnostic()? .compile_tx_script(tx_script_src) @@ -1529,9 +1529,10 @@ async fn incrementing_nonce_twice_fails() -> anyhow::Result<()> { end "; + let faulty_auth_code = + CodeBuilder::default().compile_component_code("test::faulty_auth", source_code)?; let faulty_auth_component = - AccountComponent::compile(source_code, TransactionKernel::assembler(), vec![])? - .with_supports_all_types(); + AccountComponent::new(faulty_auth_code, vec![])?.with_supports_all_types(); let account = AccountBuilder::new([5; 32]) .with_auth_component(faulty_auth_component) .with_component(MockAccountComponent::with_empty_slots()) @@ -1582,8 +1583,7 @@ async fn test_has_procedure() -> miette::Result<()> { "#; // Compile the transaction script using the testing assembler with mock account - let tx_script = ScriptBuilder::with_mock_libraries() - .into_diagnostic()? + let tx_script = CodeBuilder::with_mock_libraries() .compile_tx_script(tx_script_code) .into_diagnostic()?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index 683807536d..7e3293cf5b 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -4,7 +4,7 @@ use std::string::String; use anyhow::Context; use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{ Account, @@ -136,7 +136,7 @@ async fn storage_delta_for_value_slots() -> anyhow::Result<()> { [], )?; - let tx_script = compile_tx_script(format!( + let tx_script = parse_tx_script(format!( r#" const SLOT_0_NAME = word("{slot_0_name}") const SLOT_1_NAME = word("{slot_1_name}") @@ -276,7 +276,7 @@ async fn storage_delta_for_map_slots() -> anyhow::Result<()> { [], )?; - let tx_script = compile_tx_script(format!( + let tx_script = parse_tx_script(format!( r#" const SLOT_0_NAME = word("{slot_0_name}") const SLOT_1_NAME = word("{slot_1_name}") @@ -422,7 +422,7 @@ async fn fungible_asset_delta() -> anyhow::Result<()> { [added_asset0, added_asset1, added_asset2, added_asset4].map(Asset::from), )?; - let tx_script = compile_tx_script(format!( + let tx_script = parse_tx_script(format!( " begin push.{asset0} exec.create_note_with_asset @@ -514,7 +514,7 @@ async fn non_fungible_asset_delta() -> anyhow::Result<()> { let TestSetup { mock_chain, account_id, notes } = setup_test([], [asset1, asset3].map(Asset::from), [asset0, asset2].map(Asset::from))?; - let tx_script = compile_tx_script(format!( + let tx_script = parse_tx_script(format!( " begin push.{asset1} exec.create_note_with_asset @@ -708,7 +708,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { mock_map_slot = &*MOCK_MAP_SLOT, ); - let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(tx_script_src)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(tx_script_src)?; // Create the input note that carries the assets that we will assert later let input_note = { @@ -858,7 +858,7 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: "# ); - let builder = ScriptBuilder::with_mock_libraries()?; + let builder = CodeBuilder::with_mock_libraries(); let source_manager = builder.source_manager(); let tx_script = builder.compile_tx_script(code)?; @@ -1052,7 +1052,7 @@ fn setup_test( }) } -fn compile_tx_script(code: impl AsRef) -> anyhow::Result { +fn parse_tx_script(code: impl AsRef) -> anyhow::Result { let code = format!( " {TEST_ACCOUNT_CONVENIENCE_WRAPPERS} @@ -1061,9 +1061,9 @@ fn compile_tx_script(code: impl AsRef) -> anyhow::Result code = code.as_ref() ); - ScriptBuilder::with_mock_libraries()? + CodeBuilder::with_mock_libraries() .compile_tx_script(&code) - .context("failed to compile tx script") + .context("failed to parse tx script") } const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index ae4122d326..cbd725e035 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -3,7 +3,7 @@ use alloc::string::String; use anyhow::Context; use miden_lib::errors::tx_kernel_errors::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::Account; use miden_objects::asset::FungibleAsset; use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; @@ -56,9 +56,9 @@ async fn test_active_note_get_sender_fails_from_tx_script() -> anyhow::Result<() exec.active_note::get_sender end "; - let tx_script = ScriptBuilder::default() + let tx_script = CodeBuilder::default() .compile_tx_script(code) - .context("failed to compile tx script")?; + .context("failed to parse tx script")?; let tx_context = mock_chain .build_tx_context(TxContextInput::AccountId(account.id()), &[p2id_note.id()], &[])? @@ -412,9 +412,9 @@ async fn test_active_note_get_exactly_8_inputs() -> anyhow::Result<()> { ) .context("failed to create metadata")?; let vault = NoteAssets::new(vec![]).context("failed to create input note assets")?; - let note_script = ScriptBuilder::default() + let note_script = CodeBuilder::default() .compile_note_script("begin nop end") - .context("failed to compile note script")?; + .context("failed to parse note script")?; // create a recipient with note inputs, which number divides by 8. For simplicity create 8 input // values diff --git a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs index 676bd9f4e9..b6d0cf449b 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs @@ -4,7 +4,7 @@ use miden_lib::errors::MasmError; use miden_lib::errors::note_script_errors::ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; use miden_lib::testing::account_component::{ConditionalAuthComponent, ERR_WRONG_ARGS_MSG}; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{Account, AccountBuilder}; use miden_objects::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; use miden_objects::{Felt, ONE}; @@ -77,12 +77,12 @@ async fn test_auth_procedure_called_from_wrong_context() -> anyhow::Result<()> { // Create a transaction script that calls the auth procedure let tx_script_source = " begin - call.::auth_incr_nonce + call.::incr_nonce::auth_incr_nonce end "; - let tx_script = ScriptBuilder::default() - .with_dynamically_linked_library(auth_component.library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(auth_component.component_code())? .compile_tx_script(tx_script_source)?; let tx_context = TransactionContextBuilder::new(account).tx_script(tx_script).build()?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 3cb84646ac..42a8c7aa9b 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -16,7 +16,7 @@ use miden_lib::transaction::memory::{ OUTPUT_NOTE_ASSET_COMMITMENT_OFFSET, OUTPUT_NOTE_SECTION_OFFSET, }; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::Word; use miden_objects::account::{Account, AccountDelta, AccountStorageDelta, AccountVaultDelta}; use miden_objects::asset::{Asset, FungibleAsset}; @@ -240,7 +240,7 @@ async fn epilogue_fails_when_num_output_assets_exceed_num_input_assets() -> anyh OUTPUT_ASSET = Word::from(output_asset), ); - let builder = ScriptBuilder::with_mock_libraries()?; + let builder = CodeBuilder::with_mock_libraries(); let source_manager = builder.source_manager(); let tx_script = builder.compile_tx_script(code)?; @@ -293,7 +293,7 @@ async fn epilogue_fails_when_num_input_assets_exceed_num_output_assets() -> anyh OUTPUT_ASSET = Word::from(input_asset), ); - let builder = ScriptBuilder::with_mock_libraries()?; + let builder = CodeBuilder::with_mock_libraries(); let source_manager = builder.source_manager(); let tx_script = builder.compile_tx_script(code)?; @@ -478,7 +478,7 @@ async fn epilogue_fails_on_account_state_change_without_nonce_increment() -> any mock_value_slot0 = &*MOCK_VALUE_SLOT0, ); - let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(code)?; let result = TransactionContextBuilder::with_noop_auth_account() .tx_script(tx_script) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index e28003afe7..7a091aab88 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -9,8 +9,7 @@ use miden_lib::errors::tx_kernel_errors::{ ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, }; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -110,7 +109,7 @@ async fn mint_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result<()> ", asset = Word::from(FungibleAsset::mock(50)) ); - let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(code)?; let result = TransactionContextBuilder::new(account) .tx_script(tx_script) @@ -163,7 +162,7 @@ async fn test_mint_fungible_asset_fails_saturate_max_amount() -> anyhow::Result< ", asset = Word::from(FungibleAsset::mock(FungibleAsset::MAX_AMOUNT)) ); - let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(code)?; let result = TransactionContextBuilder::with_fungible_faucet( FungibleAsset::mock_issuer().into(), @@ -285,7 +284,7 @@ async fn mint_non_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result ", asset = Word::from(FungibleAsset::mock(50)) ); - let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(code)?; let result = TransactionContextBuilder::new(account) .tx_script(tx_script) @@ -405,7 +404,7 @@ async fn burn_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result<()> ", asset = Word::from(FungibleAsset::mock(50)) ); - let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(code)?; let result = TransactionContextBuilder::new(account) .tx_script(tx_script) @@ -603,7 +602,7 @@ async fn burn_non_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result ", asset = Word::from(FungibleAsset::mock(50)) ); - let tx_script = ScriptBuilder::with_mock_libraries()?.compile_tx_script(code)?; + let tx_script = CodeBuilder::with_mock_libraries().compile_tx_script(code)?; let result = TransactionContextBuilder::new(account) .tx_script(tx_script) @@ -733,13 +732,16 @@ async fn test_get_total_issuance_succeeds() -> anyhow::Result<()> { /// This is used to test that calling these procedures fails as expected. fn setup_non_faucet_account() -> anyhow::Result { // Build a custom non-faucet account that (invalidly) exposes faucet procedures. - let faucet_component = AccountComponent::compile( + let faucet_code = CodeBuilder::with_mock_libraries_with_source_manager(Arc::new( + DefaultSourceManager::default(), + )) + .compile_component_code( + "test::non_faucet_component", "export.::miden::faucet::mint export.::miden::faucet::burn", - TransactionKernel::with_mock_libraries(Arc::new(DefaultSourceManager::default())), - vec![], - )? - .with_supported_type(AccountType::RegularAccountUpdatableCode); + )?; + let faucet_component = AccountComponent::new(faucet_code, vec![])? + .with_supported_type(AccountType::RegularAccountUpdatableCode); Ok(AccountBuilder::new([4; 32]) .account_type(AccountType::RegularAccountUpdatableCode) .with_auth_component(NoopAuthComponent) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 6616ec06da..65d6ec870e 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -8,7 +8,6 @@ use miden_lib::errors::tx_kernel_errors::{ ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED, }; use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::transaction::TransactionKernel; use miden_lib::transaction::memory::{ ACCOUNT_DATA_LENGTH, ACCT_CODE_COMMITMENT_OFFSET, @@ -21,7 +20,7 @@ use miden_lib::transaction::memory::{ NUM_ACCT_PROCEDURES_OFFSET, NUM_ACCT_STORAGE_SLOTS_OFFSET, }; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -33,7 +32,6 @@ use miden_objects::account::{ StorageSlot, }; use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::diagnostics::NamedSource; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; use miden_objects::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, @@ -83,9 +81,9 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { "; let source_manager = Arc::new(DefaultSourceManager::default()); - let foreign_account_component = AccountComponent::compile( - foreign_account_code_source, - TransactionKernel::with_kernel_library(source_manager.clone()), + let foreign_account_component = AccountComponent::new( + CodeBuilder::with_kernel_library(source_manager.clone()) + .compile_component_code("test::foreign_account", foreign_account_code_source)?, vec![mock_value_slot0.clone(), mock_map_slot.clone()], )? .with_supports_all_types(); @@ -344,16 +342,16 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { end "; - let foreign_account_component_1 = AccountComponent::compile( - foreign_account_code_source_1, - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), + let foreign_account_component_1 = AccountComponent::new( + CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + .compile_component_code("test::foreign_account_1", foreign_account_code_source_1)?, vec![mock_value_slot0.clone()], )? .with_supports_all_types(); - let foreign_account_component_2 = AccountComponent::compile( - foreign_account_code_source_2, - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), + let foreign_account_component_2 = AccountComponent::new( + CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + .compile_component_code("test::foreign_account_2", foreign_account_code_source_2)?, vec![mock_value_slot1.clone()], )? .with_supports_all_types(); @@ -558,9 +556,9 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { "; let source_manager = Arc::new(DefaultSourceManager::default()); - let foreign_account_component = AccountComponent::compile( - NamedSource::new("foreign_account", foreign_account_code_source), - TransactionKernel::with_kernel_library(source_manager.clone()), + let foreign_account_component = AccountComponent::new( + CodeBuilder::with_kernel_library(source_manager.clone()) + .compile_component_code("foreign_account", foreign_account_code_source)?, vec![mock_value_slot0.clone(), mock_map_slot.clone()], )? .with_supports_all_types(); @@ -652,8 +650,8 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { map_key = STORAGE_LEAVES_2[0].0, ); - let tx_script = ScriptBuilder::with_source_manager(source_manager.clone()) - .with_dynamically_linked_library(foreign_account_component.library())? + let tx_script = CodeBuilder::with_source_manager(source_manager.clone()) + .with_dynamically_linked_library(foreign_account_component.component_code())? .compile_tx_script(code)?; let foreign_account_inputs = mock_chain @@ -716,9 +714,9 @@ async fn foreign_account_can_get_balance_and_presence_of_asset() -> anyhow::Resu ); let source_manager = Arc::new(DefaultSourceManager::default()); - let foreign_account_component = AccountComponent::compile( - NamedSource::new("foreign_account_code", foreign_account_code_source), - TransactionKernel::assembler_with_source_manager(source_manager.clone()), + let foreign_account_component = AccountComponent::new( + CodeBuilder::with_source_manager(source_manager.clone()) + .compile_component_code("foreign_account_code", foreign_account_code_source)?, vec![], )? .with_supports_all_types(); @@ -774,8 +772,8 @@ async fn foreign_account_can_get_balance_and_presence_of_asset() -> anyhow::Resu foreign_suffix = foreign_account.id().suffix(), ); - let tx_script = ScriptBuilder::with_source_manager(source_manager.clone()) - .with_dynamically_linked_library(foreign_account_component.library())? + let tx_script = CodeBuilder::with_source_manager(source_manager.clone()) + .with_dynamically_linked_library(foreign_account_component.component_code())? .compile_tx_script(code)?; let foreign_account_inputs = mock_chain.get_foreign_account_inputs(foreign_account.id())?; @@ -821,9 +819,9 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { ); let source_manager = Arc::new(DefaultSourceManager::default()); - let foreign_account_component = AccountComponent::compile( - NamedSource::new("foreign_account_code", foreign_account_code_source), - TransactionKernel::assembler_with_source_manager(source_manager.clone()), + let foreign_account_component = AccountComponent::new( + CodeBuilder::with_source_manager(source_manager.clone()) + .compile_component_code("foreign_account_code", foreign_account_code_source)?, vec![], )? .with_supports_all_types(); @@ -880,8 +878,8 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { foreign_suffix = foreign_account.id().suffix(), ); - let tx_script = ScriptBuilder::with_source_manager(source_manager.clone()) - .with_dynamically_linked_library(foreign_account_component.library())? + let tx_script = CodeBuilder::with_source_manager(source_manager.clone()) + .with_dynamically_linked_library(foreign_account_component.component_code())? .compile_tx_script(code)?; let foreign_account_inputs = mock_chain.get_foreign_account_inputs(foreign_account.id())?; @@ -964,9 +962,11 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { ); let source_manager = Arc::new(DefaultSourceManager::default()); - let second_foreign_account_component = AccountComponent::compile( - second_foreign_account_code_source, - TransactionKernel::with_kernel_library(source_manager.clone()), + let second_foreign_account_component = AccountComponent::new( + CodeBuilder::with_kernel_library(source_manager.clone()).compile_component_code( + "test::second_foreign_account", + second_foreign_account_code_source, + )?, vec![mock_value_slot0.clone()], )? .with_supports_all_types(); @@ -1026,9 +1026,9 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { mock_value_slot0 = mock_value_slot0.name(), ); - let first_foreign_account_component = AccountComponent::compile( - NamedSource::new("first_foreign_account", first_foreign_account_code_source), - TransactionKernel::with_kernel_library(source_manager.clone()), + let first_foreign_account_component = AccountComponent::new( + CodeBuilder::with_kernel_library(source_manager.clone()) + .compile_component_code("first_foreign_account", first_foreign_account_code_source)?, vec![mock_value_slot0.clone(), mock_value_slot1.clone()], )? .with_supports_all_types(); @@ -1116,8 +1116,8 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { foreign_suffix = first_foreign_account.id().suffix(), ); - let tx_script = ScriptBuilder::with_source_manager(source_manager.clone()) - .with_dynamically_linked_library(first_foreign_account_component.library())? + let tx_script = CodeBuilder::with_source_manager(source_manager.clone()) + .with_dynamically_linked_library(first_foreign_account_component.component_code())? .compile_tx_script(code)?; mock_chain @@ -1159,9 +1159,9 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { "#; let source_manager = Arc::new(DefaultSourceManager::default()); - let second_foreign_account_component = AccountComponent::compile( - NamedSource::new("foreign_account", second_foreign_account_code_source), - TransactionKernel::with_kernel_library(source_manager.clone()), + let second_foreign_account_component = AccountComponent::new( + CodeBuilder::with_kernel_library(source_manager.clone()) + .compile_component_code("foreign_account", second_foreign_account_code_source)?, vec![], )? .with_supports_all_types(); @@ -1203,14 +1203,11 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { ); // Link against the second foreign account. - let first_foreign_account_component = AccountComponent::compile( - NamedSource::new("first_foreign_account", first_foreign_account_code_source), - TransactionKernel::with_kernel_library(source_manager.clone()) - .with_dynamic_library(second_foreign_account_component.library()) - .map_err(|e| anyhow::anyhow!("Failed to link dynamic library: {}", e))?, - vec![], - )? - .with_supports_all_types(); + let first_foreign_account_code = CodeBuilder::with_kernel_library(source_manager.clone()) + .with_dynamically_linked_library(second_foreign_account_component.component_code())? + .compile_component_code("first_foreign_account", first_foreign_account_code_source)?; + let first_foreign_account_component = + AccountComponent::new(first_foreign_account_code, vec![])?.with_supports_all_types(); let first_foreign_account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) @@ -1274,8 +1271,8 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { foreign_suffix = first_foreign_account.id().suffix(), ); - let tx_script = ScriptBuilder::with_source_manager(source_manager.clone()) - .with_dynamically_linked_library(first_foreign_account_component.library())? + let tx_script = CodeBuilder::with_source_manager(source_manager.clone()) + .with_dynamically_linked_library(first_foreign_account_component.component_code())? .compile_tx_script(code)?; let executed_transaction = mock_chain @@ -1329,13 +1326,14 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { mock_value_slot0 = mock_value_slot0.name(), ); - let last_foreign_account_component = AccountComponent::compile( - last_foreign_account_code_source, - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - vec![mock_value_slot0.clone()], - ) - .unwrap() - .with_supports_all_types(); + let last_foreign_account_code = + CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + .compile_component_code("test::last_foreign_account", last_foreign_account_code_source) + .unwrap(); + let last_foreign_account_component = + AccountComponent::new(last_foreign_account_code, vec![mock_value_slot0.clone()]) + .unwrap() + .with_supports_all_types(); let last_foreign_account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) @@ -1376,13 +1374,16 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { next_foreign_prefix = next_account.id().prefix().as_felt(), ); - let foreign_account_component = AccountComponent::compile( - foreign_account_code_source, - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), - vec![], - ) - .unwrap() - .with_supports_all_types(); + let foreign_account_code = + CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + .compile_component_code( + format!("test::foreign_account_chain_{foreign_account_index}"), + foreign_account_code_source, + ) + .unwrap(); + let foreign_account_component = AccountComponent::new(foreign_account_code, vec![]) + .unwrap() + .with_supports_all_types(); let foreign_account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random()) .with_auth_component(Auth::IncrNonce) @@ -1449,7 +1450,7 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { foreign_suffix = foreign_accounts.last().unwrap().0.id().suffix(), ); - let tx_script = ScriptBuilder::default().compile_tx_script(code).unwrap(); + let tx_script = CodeBuilder::default().compile_tx_script(code).unwrap(); let tx_context = mock_chain .build_tx_context(native_account.id(), &[], &[])? @@ -1491,9 +1492,9 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { end "; - let foreign_account_component = AccountComponent::compile( - NamedSource::new("foreign_account", foreign_account_code_source), - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), + let foreign_account_component = AccountComponent::new( + CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + .compile_component_code("foreign_account", foreign_account_code_source)?, vec![], )? .with_supports_all_types(); @@ -1544,8 +1545,8 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { first_account_foreign_proc_hash = foreign_account.code().procedures()[1].mast_root(), ); - let tx_script = ScriptBuilder::default() - .with_dynamically_linked_library(foreign_account_component.library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(foreign_account_component.component_code())? .compile_tx_script(code)?; let foreign_account_inputs = mock_chain @@ -1590,9 +1591,9 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { "; let mock_value_slot0 = AccountStorage::mock_value_slot0(); - let foreign_account_component = AccountComponent::compile( - foreign_account_code_source, - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), + let foreign_account_component = AccountComponent::new( + CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + .compile_component_code("foreign_account_invalid", foreign_account_code_source)?, vec![mock_value_slot0.clone()], )? .with_supports_all_types(); @@ -1698,9 +1699,9 @@ async fn test_fpi_get_account_id() -> anyhow::Result<()> { end "; - let foreign_account_component = AccountComponent::compile( - NamedSource::new("foreign_account", foreign_account_code_source), - TransactionKernel::with_kernel_library(Arc::new(DefaultSourceManager::default())), + let foreign_account_component = AccountComponent::new( + CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + .compile_component_code("foreign_account", foreign_account_code_source)?, Vec::new(), )? .with_supports_all_types(); @@ -1768,8 +1769,8 @@ async fn test_fpi_get_account_id() -> anyhow::Result<()> { expected_native_prefix = native_account.id().prefix().as_felt(), ); - let tx_script = ScriptBuilder::default() - .with_dynamically_linked_library(foreign_account_component.library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(foreign_account_component.component_code())? .compile_tx_script(code)?; let foreign_account_inputs = mock_chain @@ -1898,9 +1899,9 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - mock_value_slot0 = mock_value_slot0.name() ); - let foreign_account_component = AccountComponent::compile( - NamedSource::new("foreign_account", foreign_account_code_source), - TransactionKernel::assembler().with_debug_mode(true), + let foreign_account_component = AccountComponent::new( + CodeBuilder::default() + .compile_component_code("foreign_account", foreign_account_code_source)?, vec![mock_value_slot0.clone(), mock_map_slot.clone()], )? .with_supports_all_types(); @@ -1956,8 +1957,8 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - map_value = &map_value, ); - let tx_script = ScriptBuilder::with_mock_libraries()? - .with_dynamically_linked_library(foreign_account_component.library())? + let tx_script = CodeBuilder::with_mock_libraries() + .with_dynamically_linked_library(foreign_account_component.component_code())? .compile_tx_script(code)?; mock_chain diff --git a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs index b903b3d6b0..e0b9fb9f12 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs @@ -1,6 +1,6 @@ use alloc::string::String; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::Word; use miden_objects::note::Note; @@ -74,7 +74,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { ), ); - let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + let tx_script = CodeBuilder::default().compile_tx_script(code)?; let tx_context = mock_chain .build_tx_context( @@ -132,7 +132,7 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { METADATA = Word::from(p2id_note_1_asset.metadata()), ); - let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + let tx_script = CodeBuilder::default().compile_tx_script(code)?; let tx_context = mock_chain .build_tx_context(TxContextInput::AccountId(account.id()), &[], &[p2id_note_1_asset])? @@ -181,7 +181,7 @@ async fn test_get_sender() -> anyhow::Result<()> { sender_suffix = p2id_note_1_asset.metadata().sender().suffix(), ); - let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + let tx_script = CodeBuilder::default().compile_tx_script(code)?; let tx_context = mock_chain .build_tx_context(TxContextInput::AccountId(account.id()), &[], &[p2id_note_1_asset])? @@ -272,7 +272,7 @@ async fn test_get_assets() -> anyhow::Result<()> { check_note_2 = check_assets_code(2, 8, &p2id_note_2_assets), ); - let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + let tx_script = CodeBuilder::default().compile_tx_script(code)?; let tx_context = mock_chain .build_tx_context( @@ -326,7 +326,7 @@ async fn test_get_inputs_info() -> anyhow::Result<()> { inputs_num = p2id_note_1_asset.inputs().num_values(), ); - let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + let tx_script = CodeBuilder::default().compile_tx_script(code)?; let tx_context = mock_chain .build_tx_context(TxContextInput::AccountId(account.id()), &[], &[p2id_note_1_asset])? @@ -369,7 +369,7 @@ async fn test_get_script_root() -> anyhow::Result<()> { SCRIPT_ROOT = p2id_note_1_asset.script().root(), ); - let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + let tx_script = CodeBuilder::default().compile_tx_script(code)?; let tx_context = mock_chain .build_tx_context(TxContextInput::AccountId(account.id()), &[], &[p2id_note_1_asset])? @@ -412,7 +412,7 @@ async fn test_get_serial_number() -> anyhow::Result<()> { SERIAL_NUMBER = p2id_note_1_asset.serial_num(), ); - let tx_script = ScriptBuilder::default().compile_tx_script(code)?; + let tx_script = CodeBuilder::default().compile_tx_script(code)?; let tx_context = mock_chain .build_tx_context(TxContextInput::AccountId(account.id()), &[], &[p2id_note_1_asset])? diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index 2dc821fccc..9670228f27 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -3,7 +3,7 @@ //! Once lazy loading is enabled generally, it can be removed and/or integrated into other tests. use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::LexicographicWord; use miden_objects::account::{AccountId, AccountStorage}; use miden_objects::asset::{Asset, FungibleAsset}; @@ -54,7 +54,7 @@ async fn adding_fungible_assets_with_lazy_loading_succeeds() -> anyhow::Result<( FUNGIBLE_ASSET2 = Word::from(fungible_asset2) ); - let builder = ScriptBuilder::with_mock_libraries()?; + let builder = CodeBuilder::with_mock_libraries(); let source_manager = builder.source_manager(); let tx_script = builder.compile_tx_script(code)?; let tx_context = TransactionContextBuilder::with_existing_mock_account() @@ -112,7 +112,7 @@ async fn removing_fungible_assets_with_lazy_loading_succeeds() -> anyhow::Result FUNGIBLE_ASSET2 = Word::from(fungible_asset2) ); - let builder = ScriptBuilder::with_mock_libraries()?; + let builder = CodeBuilder::with_mock_libraries(); let source_manager = builder.source_manager(); let tx_script = builder.compile_tx_script(code)?; @@ -209,7 +209,7 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { "# ); - let builder = ScriptBuilder::with_mock_libraries()?; + let builder = CodeBuilder::with_mock_libraries(); let source_manager = builder.source_manager(); let tx_script = builder.compile_tx_script(code)?; @@ -275,7 +275,7 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { "# ); - let builder = ScriptBuilder::with_mock_libraries()?; + let builder = CodeBuilder::with_mock_libraries(); let source_manager = builder.source_manager(); let tx_script = builder.compile_tx_script(code)?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 76f1bc8590..a439f64e8a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -5,9 +5,8 @@ use anyhow::Context; use miden_lib::account::wallets::BasicWallet; use miden_lib::errors::MasmError; use miden_lib::testing::note::NoteBuilder; -use miden_lib::transaction::TransactionKernel; use miden_lib::transaction::memory::ACTIVE_INPUT_NOTE_PTR; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::account::{AccountBuilder, AccountId}; use miden_objects::assembly::DefaultSourceManager; @@ -193,7 +192,7 @@ async fn test_build_recipient() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; // Create test script and serial number - let note_script = ScriptBuilder::default().compile_note_script("begin nop end")?; + let note_script = CodeBuilder::default().compile_note_script("begin nop end")?; let serial_num = Word::default(); // Define test values as Words @@ -470,7 +469,7 @@ pub async fn test_timelock() -> anyhow::Result<()> { .note_inputs([Felt::from(lock_timestamp)])? .source_manager(source_manager.clone()) .code(code.clone()) - .dynamically_linked_libraries(TransactionKernel::mock_libraries()) + .dynamically_linked_libraries(CodeBuilder::mock_libraries()) .build()?; builder.add_output_note(OutputNote::Full(timelock_note.clone())); @@ -542,7 +541,7 @@ async fn test_public_key_as_note_input() -> anyhow::Result<()> { Default::default(), )?; let vault = NoteAssets::new(vec![])?; - let note_script = ScriptBuilder::default().compile_note_script("begin nop end")?; + let note_script = CodeBuilder::default().compile_note_script("begin nop end")?; let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::new(public_key_value.to_vec())?); let note_with_pub_key = Note::new(vault.clone(), metadata, recipient); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index b004a03ff7..d4256a7182 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -16,7 +16,7 @@ use miden_lib::transaction::memory::{ OUTPUT_NOTE_RECIPIENT_OFFSET, OUTPUT_NOTE_SECTION_OFFSET, }; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{Account, AccountId}; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; use miden_objects::crypto::rand::RpoRandomCoin; @@ -835,7 +835,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { assets_number_1 = output_note_1.assets().num_assets(), ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_script_src)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; let tx_context = mock_chain .build_tx_context(account.id(), &[], &[])? @@ -907,7 +907,7 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { METADATA = Word::from(output_note.metadata()), ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_script_src)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; let tx_context = mock_chain .build_tx_context(account.id(), &[], &[])? @@ -1009,7 +1009,7 @@ async fn test_get_assets() -> anyhow::Result<()> { check_note_2 = check_assets_code(2, 8, &p2id_note_2_assets), ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_script_src)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; let tx_context = mock_chain .build_tx_context(account.id(), &[], &[])? diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index b04a2cb72a..e96c901f69 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -61,6 +61,7 @@ use miden_lib::transaction::memory::{ VALIDATOR_KEY_COMMITMENT_PTR, VERIFICATION_BASE_FEE_IDX, }; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -80,7 +81,7 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_SENDER, }; use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::transaction::{ExecutedTransaction, TransactionArgs, TransactionScript}; +use miden_objects::transaction::{ExecutedTransaction, TransactionArgs}; use miden_objects::{EMPTY_WORD, ONE, WORD_SIZE}; use miden_processor::fast::ExecutionOutput; use miden_processor::{AdviceInputs, Word}; @@ -136,12 +137,7 @@ async fn test_transaction_prologue() -> anyhow::Result<()> { end "; - let mock_tx_script_program = TransactionKernel::assembler() - .with_debug_mode(true) - .assemble_program(mock_tx_script_code) - .unwrap(); - - let tx_script = TransactionScript::new(mock_tx_script_program); + let tx_script = CodeBuilder::default().compile_tx_script(mock_tx_script_code).unwrap(); let note_args = [Word::from([91u32; 4]), Word::from([92u32; 4])]; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index b79eda68bd..b8cb5480ae 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -9,7 +9,7 @@ use miden_lib::note::create_p2id_note; use miden_lib::testing::account_component::IncrNonceAuthComponent; use miden_lib::testing::mock_account::MockAccountExt; use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -220,7 +220,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { // Create the expected output note for Note 2 which is public let serial_num_2 = Word::from([1, 2, 3, 4u32]); - let note_script_2 = ScriptBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; + let note_script_2 = CodeBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; let inputs_2 = NoteInputs::new(vec![ONE])?; let metadata_2 = NoteMetadata::new(account_id, note_type2, tag2, NoteExecutionHint::none(), aux2)?; @@ -230,7 +230,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { // Create the expected output note for Note 3 which is public let serial_num_3 = Word::from([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]); - let note_script_3 = ScriptBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; + let note_script_3 = CodeBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; let inputs_3 = NoteInputs::new(vec![ONE, Felt::new(2)])?; let metadata_3 = NoteMetadata::new( account_id, @@ -344,7 +344,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { EXECUTION_HINT_3 = Felt::from(NoteExecutionHint::on_block_slot(11, 22, 33)), ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_script_src)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; // expected delta // -------------------------------------------------------------------------------------------- @@ -440,10 +440,12 @@ async fn user_code_can_abort_transaction_with_summary() -> anyhow::Result<()> { end "#; - let auth_component = - AccountComponent::compile(source_code, TransactionKernel::assembler(), vec![]) - .context("failed to compile auth component")? - .with_supports_all_types(); + let auth_code = CodeBuilder::default() + .compile_component_code("test::auth_component", source_code) + .context("failed to parse auth component")?; + let auth_component = AccountComponent::new(auth_code, vec![]) + .context("failed to parse auth component")? + .with_supports_all_types(); let account = AccountBuilder::new([42; 32]) .storage_mode(AccountStorageMode::Private) @@ -646,7 +648,7 @@ async fn execute_tx_view_script() -> anyhow::Result<()> { end "; - let tx_script = ScriptBuilder::new(false) + let tx_script = CodeBuilder::new(false) .with_statically_linked_library(&library)? .compile_tx_script(source)?; let tx_context = TransactionContextBuilder::with_existing_mock_account() @@ -694,7 +696,7 @@ async fn test_tx_script_inputs() -> anyhow::Result<()> { " ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_script_src)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; let tx_context = TransactionContextBuilder::with_existing_mock_account() .tx_script(tx_script) @@ -732,9 +734,9 @@ async fn test_tx_script_args() -> anyhow::Result<()> { push.5.6.7.8 assert_eqw.err="obtained advice map value doesn't match the expected one" end"#; - let tx_script = ScriptBuilder::default() + let tx_script = CodeBuilder::default() .compile_tx_script(tx_script_src) - .context("failed to compile transaction script")?; + .context("failed to parse transaction script")?; // extend the advice map with the entry that is accessed using the provided transaction script // argument @@ -756,7 +758,7 @@ async fn test_tx_script_args() -> anyhow::Result<()> { // part of the transaction advice inputs #[tokio::test] async fn inputs_created_correctly() -> anyhow::Result<()> { - let account_code_script = r#" + let account_component_masm = r#" adv_map.A([6,7,8,9])=[10,11,12,13] export.assert_adv_map @@ -767,10 +769,11 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { assert_eqw.err="script adv map not found" end "#; + let component_code = CodeBuilder::default() + .compile_component_code("test::adv_map_component", account_component_masm)?; - let component = AccountComponent::compile( - account_code_script, - TransactionKernel::assembler(), + let component = AccountComponent::new( + component_code.clone(), vec![StorageSlot::with_value(StorageSlotName::mock(0), Word::default())], )? .with_supports_all_types(); @@ -780,14 +783,13 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { AccountType::RegularAccountUpdatableCode, )?; - let script = format!( - r#" + let script = r#" use.miden::account adv_map.A([1,2,3,4])=[5,6,7,8] begin - call.{assert_adv_map_proc_root} + call.::test::adv_map_component::assert_adv_map # test account code advice map push.[6,7,8,9] @@ -795,12 +797,11 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { push.[10,11,12,13] assert_eqw.err="account code adv map not found" end - "#, - assert_adv_map_proc_root = - component.library().get_procedure_root_by_name("$anon::assert_adv_map").unwrap() - ); + "#; - let tx_script = ScriptBuilder::default().compile_tx_script(script)?; + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(component_code.as_library())? + .compile_tx_script(script)?; assert!(tx_script.mast().advice_map().get(&Word::try_from([1u64, 2, 3, 4])?).is_some()); assert!( diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index 2d3f443c59..012b096e41 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -4,6 +4,7 @@ use alloc::sync::Arc; use alloc::vec::Vec; use miden_lib::transaction::TransactionKernel; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountId, @@ -12,7 +13,7 @@ use miden_objects::account::{ StorageSlotContent, }; use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; -use miden_objects::assembly::{SourceManager, SourceManagerSync}; +use miden_objects::assembly::{Assembler, SourceManager, SourceManagerSync}; use miden_objects::asset::{Asset, AssetVaultKey, AssetWitness}; use miden_objects::block::{AccountWitness, BlockHeader, BlockNumber}; use miden_objects::note::{Note, NoteScript}; @@ -65,11 +66,10 @@ impl TransactionContext { /// Executes arbitrary code within the context of a mocked transaction environment and returns /// the resulting [`ExecutionOutput`]. /// - /// The code is compiled with the assembler returned by - /// [`TransactionKernel::with_mock_libraries`] and executed with advice inputs constructed from - /// the data stored in the context. The program is run on a modified [`TransactionExecutorHost`] - /// which is loaded with the procedures exposed by the transaction kernel, and also - /// individual kernel functions (not normally exposed). + /// The code is compiled with the assembler built by [`CodeBuilder::with_mock_libraries`] + /// and executed with advice inputs constructed from the data stored in the context. The program + /// is run on a modified [`TransactionExecutorHost`] which is loaded with the procedures exposed + /// by the transaction kernel, and also individual kernel functions (not normally exposed). /// /// To improve the error message quality, convert the returned [`ExecutionError`] into a /// [`Report`](miden_objects::assembly::diagnostics::Report) or use `?` with @@ -127,8 +127,10 @@ impl TransactionContext { code.to_owned(), ); - let assembler = TransactionKernel::with_mock_libraries(self.source_manager.clone()) - .with_debug_mode(true); + let assembler: Assembler = + CodeBuilder::with_mock_libraries_with_source_manager(self.source_manager.clone()) + .into(); + let program = assembler .with_debug_mode(true) .assemble_program(virtual_source_file) diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index f6b5b1c7d3..4b430f47cd 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -2,7 +2,7 @@ use alloc::string::String; use alloc::vec::Vec; use miden_lib::testing::note::NoteBuilder; -use miden_lib::transaction::TransactionKernel; +use miden_lib::utils::CodeBuilder; use miden_objects::account::AccountId; use miden_objects::asset::Asset; use miden_objects::crypto::rand::FeltRng; @@ -151,7 +151,7 @@ pub fn create_p2any_note( .note_type(note_type) .serial_number(serial_number) .code(code) - .dynamically_linked_libraries(TransactionKernel::mock_libraries()) + .dynamically_linked_libraries(CodeBuilder::mock_libraries()) .build() .expect("generated note script should compile") } @@ -187,7 +187,7 @@ where let note = NoteBuilder::new(sender_id, SmallRng::from_os_rng()) .code(note_code) - .dynamically_linked_libraries(TransactionKernel::mock_libraries()) + .dynamically_linked_libraries(CodeBuilder::mock_libraries()) .build()?; Ok(note) diff --git a/crates/miden-testing/tests/auth/ecdsa_acl.rs b/crates/miden-testing/tests/auth/ecdsa_acl.rs index 7882515bf8..b7247ca8be 100644 --- a/crates/miden-testing/tests/auth/ecdsa_acl.rs +++ b/crates/miden-testing/tests/auth/ecdsa_acl.rs @@ -4,7 +4,7 @@ use assert_matches::assert_matches; use miden_lib::account::auth::AuthEcdsaK256KeccakAcl; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -134,13 +134,13 @@ async fn test_ecdsa_acl() -> anyhow::Result<()> { ); let tx_script_trigger_1 = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(tx_script_with_trigger_1)?; + CodeBuilder::with_mock_libraries().compile_tx_script(tx_script_with_trigger_1)?; let tx_script_trigger_2 = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(tx_script_with_trigger_2)?; + CodeBuilder::with_mock_libraries().compile_tx_script(tx_script_with_trigger_2)?; let tx_script_no_trigger = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; + CodeBuilder::with_mock_libraries().compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; // Test 1: Transaction WITH authenticator calling trigger procedure 1 (should succeed) let tx_context_with_auth_1 = mock_chain @@ -214,7 +214,7 @@ async fn test_ecdsa_acl_with_allow_unauthorized_output_notes() -> anyhow::Result assert_eq!(config_slot, Word::from([2u32, 1, 1, 0])); let tx_script_no_trigger = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; + CodeBuilder::with_mock_libraries().compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; // Test: Transaction WITHOUT authenticator calling non-trigger procedure (should succeed) // This tests that when allow_unauthorized_output_notes=true, transactions without @@ -254,7 +254,7 @@ async fn test_ecdsa_acl_with_disallow_unauthorized_input_notes() -> anyhow::Resu assert_eq!(config_slot, Word::from([2u32, 1, 0, 0])); let tx_script_no_trigger = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; + CodeBuilder::with_mock_libraries().compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; // Test: Transaction WITHOUT authenticator calling non-trigger procedure but consuming input // notes This should FAIL because allow_unauthorized_input_notes=false and we're consuming diff --git a/crates/miden-testing/tests/auth/ecdsa_multisig.rs b/crates/miden-testing/tests/auth/ecdsa_multisig.rs index c68a1d13f1..35e716e087 100644 --- a/crates/miden-testing/tests/auth/ecdsa_multisig.rs +++ b/crates/miden-testing/tests/auth/ecdsa_multisig.rs @@ -5,7 +5,7 @@ use miden_lib::account::wallets::BasicWallet; use miden_lib::errors::tx_kernel_errors::ERR_TX_ALREADY_EXECUTED; use miden_lib::note::create_p2id_note; use miden_lib::testing::account_interface::get_public_keys_from_account; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::auth::{AuthSecretKey, PublicKey}; use miden_objects::account::{Account, AccountBuilder, AccountId, AccountStorageMode, AccountType}; use miden_objects::asset::FungibleAsset; @@ -403,8 +403,8 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { end "; - let tx_script = ScriptBuilder::new(true) - .with_dynamically_linked_library(&ecdsa_k256_keccak_multisig_library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(ecdsa_k256_keccak_multisig_library())? .compile_tx_script(tx_script_code)?; let advice_inputs = AdviceInputs { @@ -642,8 +642,8 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { advice_map.insert(multisig_config_hash, config_and_pubkeys_vector); // Create transaction script - let tx_script = ScriptBuilder::new(true) - .with_dynamically_linked_library(&ecdsa_k256_keccak_multisig_library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(ecdsa_k256_keccak_multisig_library())? .compile_tx_script("begin\n call.::update_signers_and_threshold\nend")?; let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; @@ -850,8 +850,8 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu end "; - let tx_script = ScriptBuilder::new(true) - .with_dynamically_linked_library(&ecdsa_k256_keccak_multisig_library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(ecdsa_k256_keccak_multisig_library())? .compile_tx_script(tx_script_code)?; let advice_inputs = AdviceInputs { diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index 45afe1473e..c35ffcd880 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -5,7 +5,7 @@ use miden_lib::account::wallets::BasicWallet; use miden_lib::errors::tx_kernel_errors::ERR_TX_ALREADY_EXECUTED; use miden_lib::note::create_p2id_note; use miden_lib::testing::account_interface::get_public_keys_from_account; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::auth::{AuthSecretKey, PublicKey}; use miden_objects::account::{Account, AccountBuilder, AccountId, AccountStorageMode, AccountType}; use miden_objects::asset::FungibleAsset; @@ -399,8 +399,8 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { end "; - let tx_script = ScriptBuilder::new(true) - .with_dynamically_linked_library(&rpo_falcon_512_multisig_library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(rpo_falcon_512_multisig_library())? .compile_tx_script(tx_script_code)?; let advice_inputs = AdviceInputs { @@ -637,8 +637,8 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { advice_map.insert(multisig_config_hash, config_and_pubkeys_vector); // Create transaction script - let tx_script = ScriptBuilder::new(true) - .with_dynamically_linked_library(&rpo_falcon_512_multisig_library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(rpo_falcon_512_multisig_library())? .compile_tx_script("begin\n call.::update_signers_and_threshold\nend")?; let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; @@ -840,8 +840,8 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu end "; - let tx_script = ScriptBuilder::new(true) - .with_dynamically_linked_library(&rpo_falcon_512_multisig_library())? + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(rpo_falcon_512_multisig_library())? .compile_tx_script(tx_script_code)?; let advice_inputs = AdviceInputs { diff --git a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs index 05fa4af977..a793732901 100644 --- a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs +++ b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs @@ -5,7 +5,7 @@ use assert_matches::assert_matches; use miden_lib::account::auth::AuthRpoFalcon512Acl; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -133,13 +133,13 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { ); let tx_script_trigger_1 = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(tx_script_with_trigger_1)?; + CodeBuilder::with_mock_libraries().compile_tx_script(tx_script_with_trigger_1)?; let tx_script_trigger_2 = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(tx_script_with_trigger_2)?; + CodeBuilder::with_mock_libraries().compile_tx_script(tx_script_with_trigger_2)?; let tx_script_no_trigger = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; + CodeBuilder::with_mock_libraries().compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; // Test 1: Transaction WITH authenticator calling trigger procedure 1 (should succeed) let tx_context_with_auth_1 = mock_chain @@ -212,7 +212,7 @@ async fn test_rpo_falcon_acl_with_allow_unauthorized_output_notes() -> anyhow::R assert_eq!(config_slot, Word::from([2u32, 1, 1, 0])); let tx_script_no_trigger = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; + CodeBuilder::with_mock_libraries().compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; // Test: Transaction WITHOUT authenticator calling non-trigger procedure (should succeed) // This tests that when allow_unauthorized_output_notes=true, transactions without @@ -252,7 +252,7 @@ async fn test_rpo_falcon_acl_with_disallow_unauthorized_input_notes() -> anyhow: assert_eq!(config_slot, Word::from([2u32, 1, 0, 0])); let tx_script_no_trigger = - ScriptBuilder::with_mock_libraries()?.compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; + CodeBuilder::with_mock_libraries().compile_tx_script(TX_SCRIPT_NO_TRIGGER)?; // Test: Transaction WITHOUT authenticator calling non-trigger procedure but consuming input // notes This should FAIL because allow_unauthorized_input_notes=false and we're consuming diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index c5992e03d5..e7ec9e9abb 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -4,7 +4,7 @@ mod auth; mod scripts; mod wallet; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::AccountId; use miden_objects::asset::FungibleAsset; use miden_objects::crypto::utils::Serializable; @@ -53,7 +53,7 @@ pub fn get_note_with_fungible_asset_and_script( ) -> Note { use miden_objects::note::NoteExecutionHint; - let note_script = ScriptBuilder::default().compile_note_script(note_script).unwrap(); + let note_script = CodeBuilder::default().compile_note_script(note_script).unwrap(); let serial_num = Word::from([1, 2, 3, 4u32]); let sender_id = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap(); diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index df1b2dd4fb..11b4b1c96a 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -7,7 +7,7 @@ use miden_lib::account::faucets::{BasicFungibleFaucet, FungibleFaucetExt, Networ use miden_lib::errors::tx_kernel_errors::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; use miden_lib::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountId, @@ -91,7 +91,7 @@ pub async fn execute_mint_transaction( ) -> anyhow::Result { let source_manager = Arc::new(DefaultSourceManager::default()); let tx_script_code = create_mint_script_code(params); - let tx_script = ScriptBuilder::with_source_manager(source_manager.clone()) + let tx_script = CodeBuilder::with_source_manager(source_manager.clone()) .compile_tx_script(tx_script_code)?; let tx_context = mock_chain .build_tx_context(faucet, &[], &[])? @@ -198,7 +198,7 @@ async fn faucet_contract_mint_fungible_asset_fails_exceeds_max_supply() -> anyho recipient = recipient, ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_script_code)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_script_code)?; let tx = mock_chain .build_tx_context(faucet.id(), &[], &[])? .tx_script(tx_script) @@ -328,7 +328,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul // Create a simple output note script let output_note_script_code = "begin push.1 drop end"; let source_manager = Arc::new(DefaultSourceManager::default()); - let output_note_script = ScriptBuilder::with_source_manager(source_manager.clone()) + let output_note_script = CodeBuilder::with_source_manager(source_manager.clone()) .compile_note_script(output_note_script_code)?; let serial_num = Word::default(); diff --git a/crates/miden-testing/tests/scripts/p2id.rs b/crates/miden-testing/tests/scripts/p2id.rs index dda0578692..7e4b7a5874 100644 --- a/crates/miden-testing/tests/scripts/p2id.rs +++ b/crates/miden-testing/tests/scripts/p2id.rs @@ -1,6 +1,6 @@ use miden_lib::errors::note_script_errors::ERR_P2ID_TARGET_ACCT_MISMATCH; use miden_lib::note::create_p2id_note; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::Account; use miden_objects::asset::{Asset, AssetVault, FungibleAsset}; use miden_objects::crypto::rand::RpoRandomCoin; @@ -259,7 +259,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { note_execution_hint_2 = Felt::from(output_note_2.metadata().execution_hint()) ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_script_src)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; let tx_context = mock_chain .build_tx_context(account.id(), &[input_note_1.id(), input_note_2.id()], &[])? diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index e18f44942d..bc50fc1e93 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -2,7 +2,7 @@ use core::slice; use std::collections::BTreeMap; use miden_lib::account::interface::AccountInterface; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::Word; use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; @@ -44,7 +44,7 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { Default::default(), )?; let assets = NoteAssets::new(vec![sent_asset]).unwrap(); - let note_script = ScriptBuilder::default().compile_note_script("begin nop end").unwrap(); + let note_script = CodeBuilder::default().compile_note_script("begin nop end").unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); @@ -108,7 +108,7 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { let assets = NoteAssets::new(vec![Asset::Fungible( FungibleAsset::new(sender_basic_fungible_faucet_account.id(), 10).unwrap(), )])?; - let note_script = ScriptBuilder::default().compile_note_script("begin nop end").unwrap(); + let note_script = CodeBuilder::default().compile_note_script("begin nop end").unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index f5aa7ebe25..748b641421 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -1,6 +1,6 @@ use anyhow::Context; use miden_lib::note::utils; -use miden_lib::utils::ScriptBuilder; +use miden_lib::utils::CodeBuilder; use miden_objects::account::{Account, AccountId, AccountStorageMode, AccountType}; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; use miden_objects::note::{ @@ -61,7 +61,7 @@ pub async fn prove_send_swap_note() -> anyhow::Result<()> { note_execution_hint = Felt::from(swap_note.metadata().execution_hint()) ); - let tx_script = ScriptBuilder::default().compile_tx_script(tx_script_src)?; + let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; let create_swap_note_tx = mock_chain .build_tx_context(sender_account.id(), &[], &[]) From d0e1a9c21db2af896e817f5cd30808d846bd5468 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 13 Dec 2025 09:26:45 -0800 Subject: [PATCH 043/114] chore: fix code builder comment --- crates/miden-lib/src/utils/code_builder.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/miden-lib/src/utils/code_builder.rs b/crates/miden-lib/src/utils/code_builder.rs index e7c48cf3c8..9e245850f1 100644 --- a/crates/miden-lib/src/utils/code_builder.rs +++ b/crates/miden-lib/src/utils/code_builder.rs @@ -19,7 +19,8 @@ use crate::transaction::TransactionKernel; // CODE BUILDER // ================================================================================================ -/// A builder for compiling note scripts and transaction scripts with optional library dependencies. +/// A builder for compiling account components, note scripts, and transaction scripts with optional +/// library dependencies. /// /// The [`CodeBuilder`] simplifies the process of creating transaction scripts by providing: /// - A clean API for adding multiple libraries with static or dynamic linking From e6c2ca59189750a108cc301a4e0ad214615eb3b0 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Sun, 14 Dec 2025 01:10:32 +0700 Subject: [PATCH 044/114] feat: update account storage docs with named storage slot changes (#2170) --- CHANGELOG.md | 2 +- .../asm/shared_modules/account_id.masm | 2 - crates/miden-objects/src/account/delta/mod.rs | 5 +- .../src/account/delta/storage.rs | 2 +- crates/miden-tx/src/errors/mod.rs | 4 -- .../src/host/storage_delta_tracker.rs | 3 +- crates/miden-tx/src/host/tx_event.rs | 2 - docs/src/account/storage.md | 54 ++++++++++++++++--- 8 files changed, 53 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 133c61a69d..8195a9bf12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161)). +- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). ### Changes diff --git a/crates/miden-lib/asm/shared_modules/account_id.masm b/crates/miden-lib/asm/shared_modules/account_id.masm index 27ab23b767..4158a6e04e 100644 --- a/crates/miden-lib/asm/shared_modules/account_id.masm +++ b/crates/miden-lib/asm/shared_modules/account_id.masm @@ -9,8 +9,6 @@ const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in accou const.ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO="least significant byte of the account ID suffix must be zero" -const.ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS="provided storage slot index is out of bounds" - const.ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT="the account ID must have storage mode public if the network flag is set" # CONSTANTS diff --git a/crates/miden-objects/src/account/delta/mod.rs b/crates/miden-objects/src/account/delta/mod.rs index 90d90f8c20..367cabdc57 100644 --- a/crates/miden-objects/src/account/delta/mod.rs +++ b/crates/miden-objects/src/account/delta/mod.rs @@ -256,10 +256,9 @@ impl AccountDelta { /// a value slot being set to EMPTY_WORD and its value being unchanged. /// - Storage map slots: /// - Map slots append a header which summarizes the changes in the slot, in particular the - /// slot index and number of changed entries. Since only changed slots are included, the - /// number of changed entries is never zero. + /// slot ID and number of changed entries. /// - Two distinct storage map slots use the same domain but are disambiguated due to - /// inclusion of the slot index. + /// inclusion of the slot ID. /// /// ### Domain Separators /// diff --git a/crates/miden-objects/src/account/delta/storage.rs b/crates/miden-objects/src/account/delta/storage.rs index 8aa2a05a70..e5d800837a 100644 --- a/crates/miden-objects/src/account/delta/storage.rs +++ b/crates/miden-objects/src/account/delta/storage.rs @@ -98,7 +98,7 @@ impl AccountStorageDelta { self.maps.entry(slot_name).or_default().insert(key, new_value); } - /// Inserts an empty storage map delta for the provided slot index. + /// Inserts an empty storage map delta for the provided slot name. /// /// This is useful for full state deltas to represent an empty map in the delta. pub fn insert_empty_map_delta(&mut self, slot_name: StorageSlotName) { diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index dd762d6c3a..3b51d5ee1d 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -207,10 +207,6 @@ pub enum TransactionKernelError { FailedToAddAssetToNote(#[source] NoteError), #[error("note input data has hash {actual} but expected hash {expected}")] InvalidNoteInputs { expected: Word, actual: Word }, - #[error( - "storage slot index {actual} is invalid, must be smaller than the number of account storage slots {max}" - )] - InvalidStorageSlotIndex { max: u64, actual: u64 }, #[error( "failed to respond to signature requested since no authenticator is assigned to the host" )] diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index 6a5dbe725a..d6e413ff29 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -155,8 +155,7 @@ impl StorageDeltaTracker { // Keep only the values whose new value is different from the initial value. value_slots.retain(|slot_name, new_value| { // SAFETY: The header in the initial storage is the one from the account against - // which the transaction is executed, so accessing that slot index - // should be fine. + // which the transaction is executed, so accessing that slot name should be fine. let (_slot_type, initial_value) = storage_header .find_slot_header_by_name(slot_name) .expect("slot name should exist"); diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index 1d30fda79c..a3f246070e 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -255,7 +255,6 @@ impl TransactionEvent { TransactionEventId::AccountStorageAfterSetItem => { // Expected stack state: [event, slot_ptr, VALUE] - // get slot index from the stack and make sure it is valid let slot_ptr = process.get_stack_item(1); let new_value = process.get_stack_word_be(2); @@ -291,7 +290,6 @@ impl TransactionEvent { TransactionEventId::AccountStorageAfterSetMapItem => { // Expected stack state: [event, slot_ptr, KEY, OLD_MAP_VALUE, NEW_VALUE] - // get slot index from the stack and make sure it is valid let slot_ptr = process.get_stack_item(1); let key = process.get_stack_word_be(2); let old_map_value = process.get_stack_word_be(6); diff --git a/docs/src/account/storage.md b/docs/src/account/storage.md index 06146ef588..f3e9ad45d0 100644 --- a/docs/src/account/storage.md +++ b/docs/src/account/storage.md @@ -9,18 +9,56 @@ title: "Storage" A flexible, arbitrary data store within the `Account`. ::: -The [storage](https://docs.rs/miden-objects/latest/miden_objects/account/struct.AccountStorage.html) is divided into a maximum of 255 indexed [storage slots](https://docs.rs/miden-objects/latest/miden_objects/account/enum.StorageSlot.html). Each slot can either store a 32-byte value or serve as the cryptographic root to a key-value store with the capacity to store large amounts of data. +The [storage](https://docs.rs/miden-objects/latest/miden_objects/account/struct.AccountStorage.html) consists of up to 256 individual [storage slots](https://docs.rs/miden-objects/latest/miden_objects/account/enum.StorageSlot.html), where each slot consists of: +- Key: Slot name. +- Value: either a single [`Word`](#storage-units) or a key-value map of [`Word`](#storage-units)s. -- **Value slots:** Contains 32 bytes of arbitrary data. -- **Map slots:** Contains a [StorageMap](#map-slots), a key-value store where both keys and values are 32 bytes. The slot's value is a commitment to the entire map. +So, account storage can be thought of as a map from slot name to slot value. + +#### Storage units + +The basic storage unit in account storage is a `Word`. A `Word` consists of 4 elements where each element is slightly less than a 64-bit value. Specifically, a maximum value of an element is $2^{64} - 2^{32}$. Thus, a single `Word` can contain almost 32 bytes of data. + +Since a `Word` cannot store exactly 32 bytes of data, we may need to use multiple words to store such binary data as Keccak or SHA256 hashes. At the minimum, a 32-byte hash would require 5 elements to store, but it is recommended to encode such hashes into 8 elements with each element containing 32 bits of data. In both cases, two words would be used to store the hash. + +## Slot Name + +Each slot has a name associated with it that uniquely identifies it in the account's storage. + +One example for a slot name is: + +```text +miden::standards::fungible_faucets::metadata +``` + +Slot names are intended to be _globally unique_. This means `miden::standards::fungible_faucets::metadata` should always identify a slot that contains the standardized metadata for a fungible faucet. [Account components](components.md) that define their own storage slots should therefore choose names that avoid collisions. + +To reduce the chance of slot name collisions, it is recommended that slot names have at least three components separated by `::`. A recommended pattern is: + +```text +project_name::component_name::slot_name +``` + +### Slot ID + +Because slot names are too large to be used directly in the transaction kernel, a _slot ID_ is used instead. The slot ID is derived from the hash of the slot name. Miden Assembly APIs will always work with the slot ID instead of the slot name. + +## Slot types + +Each slot has one of the following types: + +- **Value slot:** Contains a single `Word` of arbitrary data. +- **Map slot:** Contains a [StorageMap](#map-slots), a key-value store where both keys and values are `Word`s. The slot's value is set to the root of the map. An account's storage is typically the result of merging multiple [account components](./components). -## Value Slots +### Value Slots + +A value slot can be used whenever a single `Word` (almost 32 bytes) of data is enough, e.g. for storing a single public key commitment for use in [authentication procedures](code#authentication). -A value slot can be used whenever 32 bytes of data is enough, e.g. for storing a single public key for use in [authentication procedures](code#authentication). +Value slots can be used with `set_item`, `get_item` and `get_initial_item` APIs. -## Map Slots +### Map Slots A map slot contains a `StorageMap` which is a key-value store implemented as a sparse Merkle tree (SMT). This allows an account to store a much larger amount of data than would be possible using only the account's storage slots. The root of the underlying SMT is stored in a single account storage slot, and each map entry is a leaf in the tree. When retrieving an entry (e.g., via `active_account::get_map_item`), its inclusion is proven using a Merkle proof. @@ -31,3 +69,7 @@ Key properties of `StorageMap`: - **Key hashing:** Since map keys are user-chosen and may not be uniformly distributed, keys are hashed before being inserted into the SMT. This ensures a more balanced tree and mitigates efficiency issues due to key clustering. The original keys are retained in a separate map, allowing for introspection (e.g., querying the set of stored original keys for debugging or explorer scenarios). This introduces some redundancy, but enables useful features such as listing all stored keys. This design allows for flexible, scalable, and privacy-preserving storage within accounts, supporting both large datasets and efficient proof generation. + +Map slots can be used with `set_map_item`, `get_map_item` and `get_initial_map_item` APIs. + +Additionally, `get_item` and `get_initial_item` can also be used to access the root of the storage map. From 5c4edb40e770c8dea93c4a0adc57f3a48aefc04f Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Sun, 14 Dec 2025 01:17:34 +0700 Subject: [PATCH 045/114] feat: remove offset and size from `AccountProcedureInfo` (#2162) --- CHANGELOG.md | 14 +- .../asm/kernels/transaction/api.masm | 19 +- .../asm/kernels/transaction/lib/account.masm | 215 +++--------------- .../asm/kernels/transaction/lib/memory.masm | 7 +- .../asm/kernels/transaction/lib/prologue.masm | 5 - .../miden-lib/src/account/components/mod.rs | 42 ++-- .../src/account/interface/component.rs | 41 ++-- .../miden-lib/src/errors/tx_kernel_errors.rs | 8 +- .../src/transaction/kernel_procedures.rs | 34 +-- crates/miden-lib/src/transaction/memory.rs | 4 +- .../miden-objects/src/account/builder/mod.rs | 21 +- crates/miden-objects/src/account/code/mod.rs | 178 ++++++--------- .../src/account/code/procedure.rs | 173 ++------------ crates/miden-objects/src/account/mod.rs | 32 +-- crates/miden-objects/src/errors.rs | 14 -- .../src/kernel_tests/tx/test_fpi.rs | 4 +- .../src/kernel_tests/tx/test_prologue.rs | 6 +- .../miden-tx/src/host/account_procedures.rs | 4 +- 18 files changed, 213 insertions(+), 608 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8195a9bf12..012215e54f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,16 +8,7 @@ ### Changes -- [BREAKING] Make `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). -- [BREAKING] Enforce minimum number of account procedures in tx kernel ([#2171](https://github.com/0xMiden/miden-base/pull/2171)). - -## 0.12.2 (unreleased) - Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). -- [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). -Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)). - -### Changes - - [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)). - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). @@ -26,7 +17,10 @@ Added the ability to get full public key from `TransactionAuthenticator` ([#2145 - [BREAKING] Updated MINT note to support both private and public output note creation ([#2123](https://github.com/0xMiden/miden-base/pull/2123)). - [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)). - [BREAKING] Add public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)). -- [BREAKING] Renamed `ScriptBuilder` into `CodeBuilder` and added the ability to build component code ([#2142](https://github.com/0xMiden/miden-base/pull/2142)). +- [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). +Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)). +- [BREAKING] Make `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). +- [BREAKING] Rename `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-lib/asm/kernels/transaction/api.masm b/crates/miden-lib/asm/kernels/transaction/api.masm index 204e6b5e1f..5138dc37e1 100644 --- a/crates/miden-lib/asm/kernels/transaction/api.masm +++ b/crates/miden-lib/asm/kernels/transaction/api.masm @@ -59,10 +59,6 @@ proc.authenticate_account_origin # assert that the caller is from the user context exec.account::authenticate_and_track_procedure - # => [storage_offset, storage_size] - - # TODO(named_slots): Remove storage offset and size from procedure info. - drop drop # => [] end @@ -735,14 +731,12 @@ end #! #! Invocation: dynexec export.account_get_procedure_root - # get the procedure information - exec.account::get_procedure_info - # => [PROC_ROOT, storage_offset, storage_size, pad(15)] - - swapw dropw - # => [PROC_ROOT, pad(13)] + # get the procedure root + exec.account::get_procedure_root + # => [PROC_ROOT, pad(15)] - movup.4 drop + # truncate the stack + movup.4 drop movup.4 drop movup.4 drop # => [PROC_ROOT, pad(12)] end @@ -1446,8 +1440,7 @@ end #! - STORAGE_SLOT_DATA is the data contained in the storage slot which is constructed as follows: #! [SLOT_VALUE, slot_type, 0, 0, 0]. #! - CODE_COMMITMENT is the commitment of the foreign account's code. -#! - ACCOUNT_PROCEDURE_DATA is the information about account procedure which is constructed as -#! follows: [PROCEDURE_MAST_ROOT, storage_offset, 0, 0, 0]. +#! - ACCOUNT_PROCEDURE_DATA are the roots of the public procedures of the foreign account. #! #! Panics if: #! - foreign context is created against the native account. diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index 2204c35cbc..2d2a361ea9 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -24,21 +24,16 @@ const.ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT="failed to write an accou const.ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT="failed to write an account map item to a non-map storage slot" -const.ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE="account procedure is not part of the account code" +const.ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE="procedure is not part of the account code" const.ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS="provided procedure index is out of bounds" const.ERR_ACCOUNT_PROC_NOT_AUTH_PROC="account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure" -# TODO(named_slots): Remove along with index APIs. -const.ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS="provided storage slot index is out of bounds" - const.ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE="slot IDs must be unique and sorted in ascending order" const.ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" -const.ERR_FAUCET_INVALID_STORAGE_OFFSET="storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets)" - const.ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH="computed account code commitment does not match recorded account code commitment" const.ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES="number of account procedures must be at least 2" @@ -51,8 +46,6 @@ const.ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH="computed account storage commitme const.ERR_ACCOUNT_STORAGE_MAP_ENTRIES_DO_NOT_MATCH_MAP_ROOT="storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to" -const.ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE="storage size can only be zero if storage offset is also zero" - const.ERR_FOREIGN_ACCOUNT_ID_IS_ZERO="ID of the provided foreign account equals zero" const.ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED="maximum allowed number of foreign account to be loaded (64) was exceeded" @@ -128,7 +121,7 @@ const.ACCOUNT_TREE_DEPTH=64 const.ACCOUNT_STORAGE_SLOT_DATA_LENGTH=8 # The number of field elements it takes to store one account procedure. -const.ACCOUNT_PROCEDURE_DATA_LENGTH=8 +const.ACCOUNT_PROCEDURE_DATA_LENGTH=4 # The offset of the slot type in the storage slot. const.ACCOUNT_SLOT_TYPE_OFFSET=1 @@ -836,140 +829,14 @@ end # CODE # ------------------------------------------------------------------------------------------------- -#! Validates all account procedure's storage metadata. -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Panics if: -#! - Storage offset + storage size > number of storage slots. -#! - Storage size is zero and storage offset is non-zero. -#! - This is validated to ensure users do not accidentally set a non-zero offset with a -#! zero size which would prevent any access to storage. -#! - The storage offset of a faucet account's procedure is 0 with a size != 0. -#! - This prevents access to the reserved storage slot. -export.validate_procedure_metadata - # get number of account procedures and number of storage slots - exec.memory::get_num_account_procedures exec.memory::get_num_storage_slots - # => [num_storage_slots, num_account_procedures] - - # prepare stack for looping - push.0.1 - # => [start_loop, index, num_storage_slots, num_account_procedures] - - # check if the account is a faucet - exec.get_id swap drop exec.account_id::is_faucet - # => [is_faucet, start_loop, index, num_storage_slots, num_account_procedures] - - # we do not check if num_account_procedures == 0 here because a valid - # account has between 1 and 256 procedures with associated offsets - if.true - # This branch handles procedures from faucet accounts. - while.true - # get storage offset and size from memory - dup exec.get_procedure_metadata - # => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures] - - # Procedures that do not access storage are defined with (offset, size) = (0, 0). - # But we want to fail on tuples defined with a zero size but non-zero offset, since that - # is a logic error. - # We assert this with: (size == 0 && offset != 0) == 0. - dup.1 eq.0 dup.1 eq.0 not and assertz.err=ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE - # => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures] - - # No procedure should access the reserved faucet slot (slot 0). However (0, 0) should - # still be allowed per the above. - # We assert this with: (offset == 0 && size != 0) == 0. - dup.1 eq.0 not dup.1 eq.0 and assertz.err=ERR_FAUCET_INVALID_STORAGE_OFFSET - # => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures] - - # assert that storage limit is in bounds - add dup.2 lte assert.err=ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS - # => [index, num_storage_slots, num_account_procedures] - - # check if we should continue looping - add.1 dup dup.3 lt - # => [should_loop, index, num_storage_slots, num_account_procedures] - end - else - # This branch handles procedures from regular accounts. - while.true - # get storage offset and size from memory - dup exec.get_procedure_metadata - # => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures] - - # Procedures that do not access storage are defined with (offset, size) = (0, 0). - # But we want to fail on tuples defined with a zero size but non-zero offset, since that - # is a logic error. - # We assert this with: (size == 0 && offset != 0) == 0. - dup.1 eq.0 dup.1 eq.0 not and assertz.err=ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE - # => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures] - - # assert that storage limit is in bounds - add dup.2 lte assert.err=ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS - # => [index, num_storage_slots, num_account_procedures] - - # check if we should continue looping - add.1 dup dup.3 lt - # => [should_loop, index, num_storage_slots, num_account_procedures] - end - end - - # clean stack - drop drop drop - # => [] -end - -#! Returns the procedure information. -#! -#! Inputs: [index] -#! Outputs: [PROC_ROOT, storage_offset, storage_size] -#! -#! Where: -#! - PROC_ROOT is the hash of the procedure. -#! - storage_offset is the procedure storage offset. -#! - storage_size is the number of storage slots the procedure is allowed to access. -#! -#! Panics if: -#! - the procedure index is out of bounds. -export.get_procedure_info - # check that index < number of procedures contained in the account code - dup exec.memory::get_num_account_procedures - u32assert2.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS u32lt assert.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS - # => [index] - - # get procedure pointer - exec.memory::get_account_procedure_ptr - # => [proc_ptr] - - # get metadata pointer - dup add.4 swap - # => [proc_ptr, metadata_ptr] - - # load procedure information from memory - padw movup.4 mem_loadw_be padw movup.8 mem_loadw_be - # => [METADATA, PROC_ROOT] - # more explicitly: - # => [0, 0, storage_size, storage_offset, PROC_ROOT] - - # keep relevant data - drop drop - # => [storage_size, storage_offset, PROC_ROOT] - - swap movdn.5 movdn.5 - # => [PROC_ROOT, storage_offset, storage_size] -end - #! Verifies that the procedure root is part of the account code and tracks whether it has been #! called. #! #! Inputs: [PROC_ROOT] -#! Outputs: [storage_offset, storage_size] +#! Outputs: [] #! #! Where: #! - PROC_ROOT is the hash of the procedure to authenticate. -#! - storage_offset is the procedure storage offset. -#! - storage_size is the number of storage slots the procedure is allowed to access. #! #! Panics if: #! - the procedure root is not part of the account code. @@ -978,19 +845,16 @@ export.authenticate_and_track_procedure emit.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT adv_push.1 # => [index, PROC_ROOT] - dup movdn.5 - # get procedure info (PROC_ROOT, storage_offset, storage_size) from memory stored at index - exec.get_procedure_info - # => [MEM_PROC_ROOT, storage_offset, storage_size, PROC_ROOT, index] + dup movdn.5 exec.get_procedure_root + # => [MEM_PROC_ROOT, PROC_ROOT, index] - # verify that PROC_ROOT exists in memory at index - movup.4 movdn.9 movup.4 movdn.9 assert_eqw.err=ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE - # => [storage_offset, storage_size, index] + # verify that PROC_ROOT and MEM_PROC_ROOT fetched from memory match + assert_eqw.err=ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE + # => [index] # Set the was_called flag to 1 for this procedure - movup.2 exec.set_was_procedure_called - - # => [storage_offset, storage_size] + exec.set_was_procedure_called + # => [] end #! Asserts that the specified procedure root is the root of the authentication procedure for an @@ -1013,7 +877,7 @@ export.assert_auth_procedure dup exec.set_was_procedure_called # => [index, PROC_ROOT] - # get procedure info (PROC_ROOT, storage_offset, storage_size) from memory stored at index + # get procedure root from memory stored at index exec.get_procedure_root # => [MEM_PROC_ROOT, PROC_ROOT] @@ -1223,8 +1087,7 @@ end #! - STORAGE_SLOT_DATA is the data contained in the storage slot which is constructed as follows: #! [SLOT_VALUE, slot_type, 0, 0, 0]. #! - CODE_COMMITMENT is the commitment to the account's code. -#! - ACCOUNT_PROCEDURE_DATA is the information about account procedures which is constructed as -#! follows: [PROCEDURE_MAST_ROOT, storage_offset, 0, 0, 0]. +#! - ACCOUNT_PROCEDURE_DATA are the roots of the public procedures of the foreign account. #! #! Panics if: #! - the number of account procedures exceeded the maximum limit of 256. @@ -1330,7 +1193,7 @@ export.save_account_storage_data # pad stack before reading from advice stack padw padw padw - # OS => [PAD, PAD, PAD, acct_proc_offset, end_ptr, STORAGE_COMMITMENT] + # OS => [PAD, PAD, PAD, acct_storage_slots_ptr, end_ptr, STORAGE_COMMITMENT] # AS => [[STORAGE_SLOT_DATA]] # read the data from advice stack to memory and hash @@ -1364,8 +1227,8 @@ end #! #! Where: #! - CODE_COMMITMENT is the commitment of the active account's code. -#! - ACCOUNT_PROCEDURE_DATA is the information about account procedure which is constructed as -#! follows: [PROCEDURE_MAST_ROOT, storage_offset, storage_size, 0, 0] +#! - ACCOUNT_PROCEDURE_DATA are the roots of the public procedures of the account laid out as +#! [ACCOUNT_PROCEDURE_DATA]. #! #! Panics if: #! - the number of account procedures exceeded the maximum limit of 256. @@ -1402,19 +1265,13 @@ export.save_account_procedure_data # OS => [num_procs, CODE_COMMITMENT] # AS => [[ACCOUNT_PROCEDURE_DATA]] - # setup acct_proc_offset and end_ptr for reading from advice stack - exec.memory::get_account_procedure_ptr - push.0 exec.memory::get_account_procedure_ptr - # OS => [acct_proc_offset, end_ptr, CODE_COMMITMENT] - # AS => [[ACCOUNT_PROCEDURE_DATA]] - - # pad stack before reading from advice stack - padw padw padw - # OS => [PAD, PAD, PAD, acct_proc_offset, end_ptr, CODE_COMMITMENT] + # setup start ptr and num procedures for reading from advice stack + exec.memory::get_account_procedures_section_ptr swap + # OS => [num_procs, account_proc_ptr, CODE_COMMITMENT] # AS => [[ACCOUNT_PROCEDURE_DATA]] # read the data from advice stack to memory and hash - exec.mem::pipe_double_words_to_memory + exec.mem::pipe_words_to_memory # OS => [PERM, PERM, PERM, end_ptr', CODE_COMMITMENT] # AS => [] @@ -1911,7 +1768,8 @@ end #! - the procedure index is out of bounds. proc.get_procedure_root dup exec.memory::get_num_account_procedures - u32assert2.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS u32lt assert.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS + u32assert2.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS + u32lt assert.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS # => [index] # get procedure pointer @@ -1923,27 +1781,6 @@ proc.get_procedure_root # => [PROC_ROOT] end -#! Returns the procedure metadata. -#! -#! Note: -#! - We assume that index has been validated and is within bounds. -#! -#! Inputs: [index] -#! Outputs: [storage_offset, storage_size] -#! -#! Where: -#! - storage_offset is the procedure storage offset. -#! - storage_size is the number of storage slots the procedure is allowed to access. -proc.get_procedure_metadata - # get procedure storage metadata pointer - padw exec.memory::get_account_procedure_ptr add.4 - # => [storage_offset_ptr, EMPTY_WORD] - - # load procedure metadata from memory and keep relevant data - mem_loadw_be drop drop swap - # => [storage_offset, storage_size] -end - #! Returns the pointer to the next vacant memory slot if the account was not loaded before, and the #! pointer to the account data otherwise. #! @@ -2216,18 +2053,18 @@ export.has_procedure # => [PROC_ROOT, curr_proc_ptr, end_ptr, is_procedure_available'] # move the current procedure pointer - movup.4 add.8 - # => [curr_proc_ptr + 8, PROC_ROOT, end_ptr, is_procedure_available'] + movup.4 add.ACCOUNT_PROCEDURE_DATA_LENGTH + # => [curr_proc_ptr + 4, PROC_ROOT, end_ptr, is_procedure_available'] # compute should_loop flag: we should continue iterating if - # !(is_procedure_available' || curr_proc_ptr + 8 == end_ptr), i.e. we didn't find the + # !(is_procedure_available' || curr_proc_ptr + 4 == end_ptr), i.e. we didn't find the # procedure and we didn't reach the end of the procedures memory block dup dup.6 eq dup.7 or eq.0 - # => [should_loop, curr_proc_ptr + 8, PROC_ROOT, end_ptr, is_procedure_available'] + # => [should_loop, curr_proc_ptr + 4, PROC_ROOT, end_ptr, is_procedure_available'] # rearrange the stack swap movdn.5 - # => [should_loop, PROC_ROOT, curr_proc_ptr + 8, end_ptr, is_procedure_available'] + # => [should_loop, PROC_ROOT, curr_proc_ptr + 4, end_ptr, is_procedure_available'] end # => [PROC_ROOT, curr_proc_ptr', end_ptr, is_procedure_available'] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm index b954221770..6f103d1295 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm @@ -1,4 +1,5 @@ use.$kernel::constants +use.$kernel::account use.std::mem # ERRORS @@ -166,6 +167,10 @@ const.NATIVE_ACCOUNT_DATA_PTR=8192 # The length of the memory interval that the account data occupies. const.ACCOUNT_DATA_LENGTH=8192 +# The number of field elements it takes to store one account procedure. +# TODO: Duplicated in account.masm, can we remove that? +const.ACCOUNT_PROCEDURE_DATA_LENGTH=4 + # The offsets at which the account data is stored relative to the start of the account data segment. const.ACCT_ID_AND_NONCE_OFFSET=0 const.ACCT_NONCE_OFFSET=3 @@ -1265,7 +1270,7 @@ end #! - proc_idx is the index of the account procedure. #! - proc_ptr is the memory pointer to the account procedure at the specified index. export.get_account_procedure_ptr - mul.8 exec.get_account_procedures_section_ptr add + mul.ACCOUNT_PROCEDURE_DATA_LENGTH exec.get_account_procedures_section_ptr add end ### ACCOUNT STORAGE ################################################# diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index c6e30facd5..c3226643a8 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -366,11 +366,6 @@ proc.validate_new_account # --------------------------------------------------------------------------------------------- exec.account::validate_storage # => [] - - # Assert the provided procedures offsets and sizes satisfy storage requirements - # --------------------------------------------------------------------------------------------- - exec.account::validate_procedure_metadata - # => [] end #! Saves the account data to memory and validates it. diff --git a/crates/miden-lib/src/account/components/mod.rs b/crates/miden-lib/src/account/components/mod.rs index c1946b28bd..b16d1984b9 100644 --- a/crates/miden-lib/src/account/components/mod.rs +++ b/crates/miden-lib/src/account/components/mod.rs @@ -1,8 +1,8 @@ -use alloc::collections::BTreeMap; +use alloc::collections::BTreeSet; use alloc::vec::Vec; use miden_objects::Word; -use miden_objects::account::AccountProcedureInfo; +use miden_objects::account::AccountProcedureRoot; use miden_objects::assembly::Library; use miden_objects::utils::Deserializable; use miden_objects::utils::sync::LazyLock; @@ -191,20 +191,16 @@ impl WellKnownComponent { /// interface to the component interface vector. fn extract_component( &self, - procedures_map: &mut BTreeMap, + procedures_set: &mut BTreeSet, component_interface_vec: &mut Vec, ) { // Determine if this component should be extracted based on procedure matching - if self - .procedure_digests() - .all(|proc_digest| procedures_map.contains_key(&proc_digest)) - { - // Extract the storage offset from any matching procedure - let mut storage_offset = 0u8; + if self.procedure_digests().all(|proc_digest| { + procedures_set.contains(&AccountProcedureRoot::from_raw(proc_digest)) + }) { + // Remove the procedure root of any matching procedure. self.procedure_digests().for_each(|component_procedure| { - if let Some(proc_info) = procedures_map.remove(&component_procedure) { - storage_offset = proc_info.storage_offset(); - } + procedures_set.remove(&AccountProcedureRoot::from_raw(component_procedure)); }); // Create the appropriate component interface @@ -244,19 +240,19 @@ impl WellKnownComponent { /// Gets all well known components which could be constructed from the provided procedures map /// and pushes them to the `component_interface_vec`. pub fn extract_well_known_components( - procedures_map: &mut BTreeMap, + procedures_set: &mut BTreeSet, component_interface_vec: &mut Vec, ) { - Self::BasicWallet.extract_component(procedures_map, component_interface_vec); - Self::BasicFungibleFaucet.extract_component(procedures_map, component_interface_vec); - Self::NetworkFungibleFaucet.extract_component(procedures_map, component_interface_vec); - Self::AuthEcdsaK256Keccak.extract_component(procedures_map, component_interface_vec); - Self::AuthEcdsaK256KeccakAcl.extract_component(procedures_map, component_interface_vec); + Self::BasicWallet.extract_component(procedures_set, component_interface_vec); + Self::BasicFungibleFaucet.extract_component(procedures_set, component_interface_vec); + Self::NetworkFungibleFaucet.extract_component(procedures_set, component_interface_vec); + Self::AuthEcdsaK256Keccak.extract_component(procedures_set, component_interface_vec); + Self::AuthEcdsaK256KeccakAcl.extract_component(procedures_set, component_interface_vec); Self::AuthEcdsaK256KeccakMultisig - .extract_component(procedures_map, component_interface_vec); - Self::AuthRpoFalcon512.extract_component(procedures_map, component_interface_vec); - Self::AuthRpoFalcon512Acl.extract_component(procedures_map, component_interface_vec); - Self::AuthRpoFalcon512Multisig.extract_component(procedures_map, component_interface_vec); - Self::AuthNoAuth.extract_component(procedures_map, component_interface_vec); + .extract_component(procedures_set, component_interface_vec); + Self::AuthRpoFalcon512.extract_component(procedures_set, component_interface_vec); + Self::AuthRpoFalcon512Acl.extract_component(procedures_set, component_interface_vec); + Self::AuthRpoFalcon512Multisig.extract_component(procedures_set, component_interface_vec); + Self::AuthNoAuth.extract_component(procedures_set, component_interface_vec); } } diff --git a/crates/miden-lib/src/account/interface/component.rs b/crates/miden-lib/src/account/interface/component.rs index 52c2f0bfa3..fc26de424d 100644 --- a/crates/miden-lib/src/account/interface/component.rs +++ b/crates/miden-lib/src/account/interface/component.rs @@ -1,9 +1,9 @@ -use alloc::collections::BTreeMap; +use alloc::collections::BTreeSet; use alloc::string::{String, ToString}; use alloc::vec::Vec; use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountId, AccountProcedureInfo, AccountStorage, StorageSlotName}; +use miden_objects::account::{AccountId, AccountProcedureRoot, AccountStorage, StorageSlotName}; use miden_objects::note::PartialNote; use miden_objects::{Felt, FieldElement, Word}; @@ -58,9 +58,9 @@ pub enum AccountComponentInterface { AuthNoAuth, /// A non-standard, custom interface which exposes the contained procedures. /// - /// Custom interface holds procedures which are not part of some standard interface which is - /// used by this account. Each custom interface holds procedures with the same storage offset. - Custom(Vec), + /// Custom interface holds all procedures which are not part of some standard interface which is + /// used by this account. + Custom(Vec), } impl AccountComponentInterface { @@ -90,10 +90,10 @@ impl AccountComponentInterface { }, AccountComponentInterface::AuthNoAuth => "No Auth".to_string(), - AccountComponentInterface::Custom(proc_info_vec) => { - let result = proc_info_vec + AccountComponentInterface::Custom(proc_root_vec) => { + let result = proc_root_vec .iter() - .map(|proc_info| proc_info.mast_root().to_hex()[..9].to_string()) + .map(|proc_root| proc_root.mast_root().to_hex()[..9].to_string()) .collect::>() .join(", "); format!("Custom([{result}])") @@ -177,13 +177,10 @@ impl AccountComponentInterface { /// Creates a vector of [AccountComponentInterface] instances. This vector specifies the /// components which were used to create an account with the provided procedures info array. - pub fn from_procedures(procedures: &[AccountProcedureInfo]) -> Vec { + pub fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec { let mut component_interface_vec = Vec::new(); - let mut procedures: BTreeMap<_, _> = procedures - .iter() - .map(|procedure_info| (*procedure_info.mast_root(), procedure_info)) - .collect(); + let mut procedures = BTreeSet::from_iter(procedures.iter().copied()); // Well known component interfaces // ---------------------------------------------------------------------------------------- @@ -198,21 +195,9 @@ impl AccountComponentInterface { // Custom component interfaces // ---------------------------------------------------------------------------------------- - let mut custom_interface_procs_map = BTreeMap::>::new(); - procedures.into_iter().for_each(|(_, proc_info)| { - match custom_interface_procs_map.get_mut(&proc_info.storage_offset()) { - Some(proc_vec) => proc_vec.push(*proc_info), - None => { - custom_interface_procs_map.insert(proc_info.storage_offset(), vec![*proc_info]); - }, - } - }); - - if !custom_interface_procs_map.is_empty() { - for proc_vec in custom_interface_procs_map.into_values() { - component_interface_vec.push(AccountComponentInterface::Custom(proc_vec)); - } - } + // All remaining procedures are put into the custom bucket. + component_interface_vec + .push(AccountComponentInterface::Custom(procedures.into_iter().collect())); component_interface_vec } diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index a681444f1e..140c2248d1 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -26,8 +26,6 @@ pub const ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO: MasmError = M pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE: MasmError = MasmError::from_static_str("unknown account storage mode in account ID"); /// Error Message: "unknown version in account ID" pub const ERR_ACCOUNT_ID_UNKNOWN_VERSION: MasmError = MasmError::from_static_str("unknown version in account ID"); -/// Error Message: "storage size can only be zero if storage offset is also zero" -pub const ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE: MasmError = MasmError::from_static_str("storage size can only be zero if storage offset is also zero"); /// Error Message: "the active account is not native" pub const ERR_ACCOUNT_IS_NOT_NATIVE: MasmError = MasmError::from_static_str("the active account is not native"); /// Error Message: "account nonce is already at its maximum possible value" @@ -40,8 +38,8 @@ pub const ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES: MasmError = MasmError::from_static_ pub const ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("provided procedure index is out of bounds"); /// Error Message: "account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure" pub const ERR_ACCOUNT_PROC_NOT_AUTH_PROC: MasmError = MasmError::from_static_str("account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure"); -/// Error Message: "account procedure is not part of the account code" -pub const ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE: MasmError = MasmError::from_static_str("account procedure is not part of the account code"); +/// Error Message: "procedure is not part of the account code" +pub const ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE: MasmError = MasmError::from_static_str("procedure is not part of the account code"); /// Error Message: "failed to read an account map item from a non-map storage slot" pub const ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT: MasmError = MasmError::from_static_str("failed to read an account map item from a non-map storage slot"); /// Error Message: "ID of the new account does not match the ID computed from the seed and commitments" @@ -80,8 +78,6 @@ pub const ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME: MasmError = Ma pub const ERR_FAUCET_BURN_CANNOT_EXCEED_EXISTING_TOTAL_SUPPLY: MasmError = MasmError::from_static_str("asset amount to burn can not exceed the existing total supply"); /// Error Message: "the burn_non_fungible_asset procedure can only be called on a non-fungible faucet" pub const ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET: MasmError = MasmError::from_static_str("the burn_non_fungible_asset procedure can only be called on a non-fungible faucet"); -/// Error Message: "storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets)" -pub const ERR_FAUCET_INVALID_STORAGE_OFFSET: MasmError = MasmError::from_static_str("storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets)"); /// Error Message: "the faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet" pub const ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET: MasmError = MasmError::from_static_str("the faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet"); /// Error Message: "asset mint operation would cause the new total supply to exceed the maximum allowed asset amount" diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-lib/src/transaction/kernel_procedures.rs index 6c13682875..47cea98a15 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-lib/src/transaction/kernel_procedures.rs @@ -16,33 +16,33 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_get_nonce word!("0x4a1f11db21ddb1f0ebf7c9fd244f896a95e99bb136008185da3e7d6aa85827a3"), // account_incr_nonce - word!("0x99c6b16e86eb9eae02657256b8859e2809acd05bf25810933cf50e72d876d8bf"), + word!("0x8bcc1f43091608403ea8d2fdefbcbfc799b41a0ff3fccc29b9873a23ff8edfbd"), // account_get_code_commitment - word!("0xb2ebd0acc4ef40d37c403190dc07d03d7df9169fb8752e8025e3fe469b5ee192"), + word!("0x54406e5633978e8319003d8d65527a48c0d0a4828263aa30f40687a2bbfd7be0"), // account_get_initial_storage_commitment word!("0x91377c2852feb7a2798e54d7dbaa2d97000270ec4c0d0888b26d720a25ae0e84"), // account_compute_storage_commitment - word!("0xa87008550383e1a88dde5d0adefc68ee3bf477aec07e4700f9101241aa1e868f"), + word!("0xaa54d5c070ad5e2a39c9b8b3a17aaa3c9e9387c35c59f72c965060ba91e4f748"), // account_get_item - word!("0xb1952306b30686d1e1736702bd04ddc980a8110192277d69648f6fdff843ea16"), + word!("0x6e3dc7fd548bfd6c802b6799be6e996eff247ecb2f16834a9363eca1b1751607"), // account_get_initial_item - word!("0x77118a4356781e36469bbfa34f45654f13627eb9a9846ee888ebd0bdb58172ef"), + word!("0x4835f7023bcafd46f28ece1e5c879f0f99191f78e4026c5c0590c01edc8ab598"), // account_set_item - word!("0xde6abc0e0331cd5a586f7e8893f32524b0a4072a2d3dc8023c94d1c01a785789"), + word!("0x07737e8ce29230526bb76b64f64da0e45739363e11b11cb746657adaa749b37c"), // account_get_map_item - word!("0x8c9f227c3c6992a4374df70bec4970c3fdf152baa8db7f8483f3b5619c312683"), + word!("0xd816dc31953635af927407f3b1cd8f1ec6825522363a93c264cc36eaeb23d019"), // account_get_initial_map_item - word!("0x013cfa17c70872230771d0c174a929a801ee9507689fa169d085ca3d443bf439"), + word!("0x773b9197efb18287cc9f1009ea53dbd3945b79b7168717b6a56ff0292083d1ea"), // account_set_map_item - word!("0x7e82c23b18727f1ac451fb96fec3ec2ef1a93c9a7efb7e16f667c8c3d9dba478"), + word!("0x5e8aafb3ad8a442e29c9850c372d391ffbfd5e55e90109d33e4017d7c2626e62"), // account_get_initial_vault_root word!("0x46297d9ac95afd60c7ef1a065e024ad49aa4c019f6b3924191905449b244d4ec"), // account_get_vault_root word!("0x42a2bfb8eac4fce9bbf75ea15215b00729faeeaf7fff784692948d3f618a9bb7"), // account_add_asset - word!("0x11890564573ff2ad0916b1a8ca12a9d2546f4c3dcf09fa4bfb6d72d701a3d7ff"), + word!("0x222ae6f550bb09b675cd73acf2e8ab25e4b8f06afa3ef1a1f66d3372e843ff3e"), // account_remove_asset - word!("0xcb3118521acfe552030b988439713717fdee54d9e7cb54116d4034cf89ce8a34"), + word!("0xc927af9fb41cece5eeafa4cc05ad4d82ec7771e8bc94db677a357bfca1f92ca7"), // account_get_balance word!("0x1ed792cc7775aa1ce2f32367a3d430561ec9bceb33f5bb222691c49a6bde8112"), // account_get_initial_balance @@ -54,15 +54,15 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_get_num_procedures word!("0x53b5ec38b7841948762c258010e6e07ad93963bcaac2d83813f8edb6710dc720"), // account_get_procedure_root - word!("0x4d7b2e6083820088cd1139ed658b631cf391989b16de8af6741d7e17de9245cd"), + word!("0xa45796077477599813ea54d88a83dfca3e64b1c112117970d63a1c24f7cfef5a"), // account_was_procedure_called - word!("0x37f1f8e67ec9105153721ad59c1f765f58da8f87b0b8eea303701bf4583a5b89"), + word!("0x23ccd2f94111eabb33481664f8ca5a400b1518380a5adadabd2cee7a825a0465"), // account_has_procedure - word!("0x667d5ce1b7a54c3b8965666ce90e59085c97775b82eba25dddfe218db5fe137d"), + word!("0xb0b63fdd01af0bcb4aacb2412e934cdc7691308647152d416c7ae4fc909da076"), // faucet_mint_asset - word!("0x4922681039d0ac03dc6b4d12b2385ea87f57dfd0bbd64bd6ac0c016617caafe9"), + word!("0xf44dbb74a478e3ac63eada9680c32d57890e481f7ade41382cbf2a98592320fe"), // faucet_burn_asset - word!("0x7f261292b3682e2f9bdb9237ce41de08a680e983764b191b381ed111dcc23f5f"), + word!("0x522a0592e159ac3fa15bfc73bba8afd1b753fdb9f86a6526f3c02a25b42bacec"), // faucet_get_total_fungible_asset_issuance word!("0x51cfb361598b5dd34ff93ae2c3788e7c1cd702e8831e2ea1d45dfc2ccd97876f"), // faucet_is_non_fungible_asset_issued @@ -104,7 +104,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // tx_get_block_timestamp word!("0x7903185b847517debb6c2072364e3e757b99ee623e97c2bd0a4661316c5c5418"), // tx_start_foreign_context - word!("0x3755ddea584a3575bc3c97820f739d562334b91ab8c00642b519b4e22792b191"), + word!("0x923b99b2b14173d265dfdeda4375fb4195f1246a6c96b102b256bb1a6400d2e9"), // tx_end_foreign_context word!("0xaa0018aa8da890b73511879487f65553753fb7df22de380dd84c11e6f77eec6f"), // tx_get_expiration_delta diff --git a/crates/miden-lib/src/transaction/memory.rs b/crates/miden-lib/src/transaction/memory.rs index 0306e606f7..ad08eaf5d4 100644 --- a/crates/miden-lib/src/transaction/memory.rs +++ b/crates/miden-lib/src/transaction/memory.rs @@ -40,9 +40,9 @@ pub type StorageSlot = u8; // | Code commitment | 12 (3) | 15 (3) | | // | Padding | 16 (4) | 27 (6) | | // | Num procedures | 28 (7) | 31 (7) | | -// | Procedures info | 32 (8) | 2_079 (519) | 255 procedures max, 8 elements each | +// | Procedures roots | 32 (8) | 1_055 (263) | 256 procedures max, 4 elements each | // | Padding | 2_080 (520) | 2_083 (520) | | -// | Proc tracking | 2_084 (521) | 2_339 (584) | 255 procedures max, 1 element each | +// | Proc tracking | 2_084 (521) | 2_339 (584) | 256 procedures max, 1 element each | // | Num storage slots | 2_340 (585) | 2_343 (585) | | // | Storage slot info | 2_344 (586) | 4_383 (1095) | 255 slots max, 8 elements each | // | Initial slot info | 4_384 (1096) | 6_423 (1545) | Only present on the native account | diff --git a/crates/miden-objects/src/account/builder/mod.rs b/crates/miden-objects/src/account/builder/mod.rs index 680562830d..1f43367d7d 100644 --- a/crates/miden-objects/src/account/builder/mod.rs +++ b/crates/miden-objects/src/account/builder/mod.rs @@ -291,7 +291,7 @@ mod tests { use miden_processor::MastNodeExt; use super::*; - use crate::account::{StorageSlot, StorageSlotName}; + use crate::account::{AccountProcedureRoot, StorageSlot, StorageSlotName}; use crate::testing::noop_auth_component::NoopAuthComponent; const CUSTOM_CODE1: &str = " @@ -407,23 +407,8 @@ mod tests { [CUSTOM_LIBRARY2.get_export_node_id(&CUSTOM_LIBRARY2.exports().next().unwrap().name)] .digest(); - let foo_procedure_info = &account - .code() - .procedures() - .iter() - .find(|info| info.mast_root() == &foo_root) - .unwrap(); - assert_eq!(foo_procedure_info.storage_offset(), 0); - assert_eq!(foo_procedure_info.storage_size(), 1); - - let bar_procedure_info = &account - .code() - .procedures() - .iter() - .find(|info| info.mast_root() == &bar_root) - .unwrap(); - assert_eq!(bar_procedure_info.storage_offset(), 1); - assert_eq!(bar_procedure_info.storage_size(), 2); + assert!(account.code().procedures().contains(&AccountProcedureRoot::from_raw(foo_root))); + assert!(account.code().procedures().contains(&AccountProcedureRoot::from_raw(bar_root))); assert_eq!( account.storage().get_item(&CUSTOM_COMPONENT1_SLOT_NAME).unwrap(), diff --git a/crates/miden-objects/src/account/code/mod.rs b/crates/miden-objects/src/account/code/mod.rs index 867859c717..1121693c28 100644 --- a/crates/miden-objects/src/account/code/mod.rs +++ b/crates/miden-objects/src/account/code/mod.rs @@ -1,4 +1,3 @@ -use alloc::collections::BTreeSet; use alloc::sync::Arc; use alloc::vec::Vec; @@ -19,38 +18,44 @@ use crate::Word; use crate::account::{AccountComponent, AccountType}; pub mod procedure; -use procedure::{AccountProcedureInfo, PrintableProcedure}; +use procedure::{AccountProcedureRoot, PrintableProcedure}; // ACCOUNT CODE // ================================================================================================ -/// A public interface of an account. +/// The public interface of an account. /// -/// Account's public interface consists of a set of account procedures, each procedure being a -/// Miden VM program. Thus, MAST root of each procedure commits to the underlying program. +/// An account's public interface consists of a set of account procedures, each of which is +/// identified and committed to by a MAST root. They are represented by [`AccountProcedureRoot`]. /// -/// Each exported procedure is associated with a storage offset and a storage size. +/// The set of procedures has an arbitrary order, i.e. they are not sorted. The only exception is +/// the authentication procedure of the account, which is always at index 0. This procedure is +/// automatically called at the end of a transaction to validate an account's state transition. /// -/// We commit to the entire account interface by building a sequential hash of all procedure MAST -/// roots and associated storage_offset's. Specifically, each procedure contributes exactly 8 field -/// elements to the sequence of elements to be hashed. These elements are defined as follows: +/// The code commits to the entire account interface by building a sequential hash of all procedure +/// MAST roots. Specifically, each procedure contributes exactly 4 field elements to the sequence of +/// elements to be hashed. Each procedure is represented by its MAST root: /// /// ```text -/// [PROCEDURE_MAST_ROOT, storage_offset, 0, 0, storage_size] +/// [PROCEDURE_MAST_ROOT] /// ``` #[derive(Debug, Clone)] pub struct AccountCode { mast: Arc, - procedures: Vec, + procedures: Vec, commitment: Word, } impl AccountCode { - /// The maximum number of account interface procedures. - pub const MAX_NUM_PROCEDURES: usize = 256; + // CONSTANTS + // -------------------------------------------------------------------------------------------- + /// The minimum number of account interface procedures (one auth and at least one non-auth). pub const MIN_NUM_PROCEDURES: usize = 2; + /// The maximum number of account interface procedures. + pub const MAX_NUM_PROCEDURES: usize = 256; + // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -63,7 +68,7 @@ impl AccountCode { account_type: AccountType, ) -> Result { super::validate_components_support_account_type(components, account_type)?; - Self::from_components_unchecked(components, account_type) + Self::from_components_unchecked(components) } /// Creates a new [`AccountCode`] from the provided components' libraries. @@ -85,13 +90,12 @@ impl AccountCode { /// - [`MastForest::merge`] fails on all libraries. pub(super) fn from_components_unchecked( components: &[AccountComponent], - account_type: AccountType, ) -> Result { let (merged_mast_forest, _) = MastForest::merge(components.iter().map(|component| component.mast_forest())) .map_err(AccountError::AccountComponentMastForestMergeError)?; - let mut builder = ProcedureInfoBuilder::new(account_type); + let mut builder = AccountProcedureBuilder::new(); let mut components_iter = components.iter(); let first_component = @@ -119,16 +123,15 @@ impl AccountCode { Self::read_from_bytes(bytes).map_err(AccountError::AccountCodeDeserializationError) } - /// Returns a new definition of an account's interface instantiated from the provided - /// [MastForest] and a list of [AccountProcedureInfo]s. + /// Returns a new [`AccountCode`] instantiated from the provided [`MastForest`] and a list of + /// [`AccountProcedureRoot`]s. /// /// # Panics + /// /// Panics if: - /// - The number of procedures is smaller than 1 or greater than 256. - /// - If some any of the provided procedures does not have a corresponding root in the provided - /// MAST forest. - pub fn from_parts(mast: Arc, procedures: Vec) -> Self { - assert!(!procedures.is_empty(), "no account procedures"); + /// - The number of procedures is smaller than 2 or greater than 256. + pub fn from_parts(mast: Arc, procedures: Vec) -> Self { + assert!(procedures.len() >= Self::MIN_NUM_PROCEDURES, "not enough account procedures"); assert!(procedures.len() <= Self::MAX_NUM_PROCEDURES, "too many account procedures"); Self { @@ -151,8 +154,8 @@ impl AccountCode { self.mast.clone() } - /// Returns a reference to the account procedures. - pub fn procedures(&self) -> &[AccountProcedureInfo] { + /// Returns a reference to the account procedure roots. + pub fn procedures(&self) -> &[AccountProcedureRoot] { &self.procedures } @@ -171,29 +174,19 @@ impl AccountCode { self.procedures.iter().any(|procedure| procedure.mast_root() == &mast_root) } - /// Returns information about the procedure at the specified index. - /// - /// # Panics - /// Panics if the provided index is out of bounds. - pub fn get_procedure_by_index(&self, index: usize) -> &AccountProcedureInfo { - &self.procedures[index] - } - - /// Returns the procedure index for the procedure with the specified MAST root or None if such - /// procedure is not defined in this [AccountCode]. - pub fn get_procedure_index_by_root(&self, root: Word) -> Option { - self.procedures - .iter() - .map(|procedure| procedure.mast_root()) - .position(|r| r == &root) + /// Returns the procedure root at the specified index. + pub fn get(&self, index: usize) -> Option<&AccountProcedureRoot> { + self.procedures.get(index) } - /// Converts procedure information in this [AccountCode] into a vector of field elements. + /// Converts the procedure root in this [`AccountCode`] into a vector of field elements. + /// + /// This is done by first converting each procedure into 4 field elements as follows: /// - /// This is done by first converting each procedure into 8 field elements as follows: /// ```text - /// [PROCEDURE_MAST_ROOT, storage_offset, storage_size, 0, 0] + /// [PROCEDURE_MAST_ROOT] /// ``` + /// /// And then concatenating the resulting elements into a single vector. pub fn as_elements(&self) -> Vec { procedures_as_elements(self.procedures()) @@ -202,12 +195,13 @@ impl AccountCode { /// Returns an iterator of printable representations for all procedures in this account code. /// /// # Returns + /// /// An iterator yielding [`PrintableProcedure`] instances for all procedures in this account /// code. pub fn printable_procedures(&self) -> impl Iterator { self.procedures() .iter() - .filter_map(move |procedure_info| self.printable_procedure(procedure_info).ok()) + .filter_map(move |proc_root| self.printable_procedure(proc_root).ok()) } // HELPER FUNCTIONS @@ -219,14 +213,14 @@ impl AccountCode { /// Returns an error if no procedure with the specified root exists in this account code. fn printable_procedure( &self, - proc_info: &AccountProcedureInfo, + proc_root: &AccountProcedureRoot, ) -> Result { let node_id = self .mast - .find_procedure_root(*proc_info.mast_root()) + .find_procedure_root(*proc_root.mast_root()) .expect("procedure root should be present in the mast forest"); - Ok(PrintableProcedure::new(self.mast.clone(), *proc_info, node_id)) + Ok(PrintableProcedure::new(self.mast.clone(), *proc_root, node_id)) } } @@ -260,7 +254,7 @@ impl Eq for AccountCode {} impl Serializable for AccountCode { fn write_into(&self, target: &mut W) { self.mast.write_into(target); - // since the number of procedures is guaranteed to be between 1 and 256, we can store the + // since the number of procedures is guaranteed to be between 2 and 256, we can store the // number as a single byte - but we do have to subtract 1 to store 256 as 255. target.write_u8((self.procedures.len() - 1) as u8); target.write_many(self.procedures()); @@ -287,7 +281,7 @@ impl Deserializable for AccountCode { fn read_from(source: &mut R) -> Result { let module = Arc::new(MastForest::read_from(source)?); let num_procedures = (source.read_u8()? as usize) + 1; - let procedures = source.read_many::(num_procedures)?; + let procedures = source.read_many::(num_procedures)?; Ok(Self::from_parts(module, procedures)) } @@ -307,13 +301,7 @@ impl PrettyPrint for AccountCode { 0, indent( 4, - text(format!("proc.{}", printable_procedure.mast_root())) - + nl() - + text(format!( - "storage.{}.{}", - printable_procedure.storage_offset(), - printable_procedure.storage_size() - )) + text(format!("proc {}", printable_procedure.mast_root())) + nl() + printable_procedure.render(), ) + nl() @@ -330,28 +318,25 @@ impl PrettyPrint for AccountCode { // ACCOUNT PROCEDURE BUILDER // ================================================================================================ -struct ProcedureInfoBuilder { - procedures: Vec, - proc_root_set: BTreeSet, - storage_offset: u8, +/// A helper type for building the set of account procedures from account components. +/// +/// In particular, this ensures that the auth procedure ends up at index 0. +struct AccountProcedureBuilder { + procedures: Vec, } -impl ProcedureInfoBuilder { - fn new(account_type: AccountType) -> Self { - let storage_offset = if account_type.is_faucet() { 1 } else { 0 }; - - Self { - procedures: Vec::new(), - proc_root_set: BTreeSet::new(), - storage_offset, - } +impl AccountProcedureBuilder { + fn new() -> Self { + Self { procedures: Vec::new() } } + /// This method must be called before add_component is called. fn add_auth_component(&mut self, component: &AccountComponent) -> Result<(), AccountError> { let mut auth_proc_count = 0; for (proc_root, is_auth) in component.get_procedures() { - self.add_procedure(proc_root, component.storage_size())?; + self.add_procedure(proc_root)?; + if is_auth { let auth_proc_idx = self.procedures.len() - 1; self.procedures.swap(0, auth_proc_idx); @@ -365,10 +350,6 @@ impl ProcedureInfoBuilder { return Err(AccountError::AccountComponentMultipleAuthProcedures); } - self.storage_offset = self.storage_offset.checked_add(component.storage_size()).expect( - "account procedure info constructor should return an error if the addition overflows", - ); - Ok(()) } @@ -377,47 +358,30 @@ impl ProcedureInfoBuilder { if is_auth { return Err(AccountError::AccountCodeMultipleAuthComponents); } - self.add_procedure(proc_mast_root, component.storage_size())?; + self.add_procedure(proc_mast_root)?; } - self.storage_offset = self.storage_offset.checked_add(component.storage_size()).expect( - "account procedure info constructor should return an error if the addition overflows", - ); - Ok(()) } - fn add_procedure( - &mut self, - proc_mast_root: Word, - component_storage_size: u8, - ) -> Result<(), AccountError> { - // We cannot support procedures from multiple components with the same MAST root - // since storage offsets/sizes are set per MAST root. Setting them again for - // procedures where the offset has already been inserted would cause that - // procedure of the earlier component to write to the wrong slot. - if !self.proc_root_set.insert(proc_mast_root) { - return Err(AccountError::AccountComponentDuplicateProcedureRoot(proc_mast_root)); + fn add_procedure(&mut self, proc_mast_root: Word) -> Result<(), AccountError> { + // TODO: Check if we can now safely allow this. + // Disallow procedures with the same MAST root from different components. + let proc_mast_root = AccountProcedureRoot::from_raw(proc_mast_root); + // The number of procedures in accounts is generally small, so checking via linear search + // should be fine. + if self.procedures.contains(&proc_mast_root) { + return Err(AccountError::AccountComponentDuplicateProcedureRoot( + proc_mast_root.as_word(), + )); } - // Components that do not access storage need to have offset and size set to 0. - let (storage_offset, storage_size) = if component_storage_size == 0 { - (0, 0) - } else { - (self.storage_offset, component_storage_size) - }; - - // Note: Offset and size are validated in `AccountProcedureInfo::new`. - self.procedures.push(AccountProcedureInfo::new( - proc_mast_root, - storage_offset, - storage_size, - )?); + self.procedures.push(proc_mast_root); Ok(()) } - fn build(self) -> Result, AccountError> { + fn build(self) -> Result, AccountError> { if self.procedures.len() < AccountCode::MIN_NUM_PROCEDURES { Err(AccountError::AccountCodeNoProcedures) } else if self.procedures.len() > AccountCode::MAX_NUM_PROCEDURES { @@ -432,14 +396,14 @@ impl ProcedureInfoBuilder { // ================================================================================================ /// Computes the commitment to the given procedures -pub(crate) fn build_procedure_commitment(procedures: &[AccountProcedureInfo]) -> Word { +pub(crate) fn build_procedure_commitment(procedures: &[AccountProcedureRoot]) -> Word { let elements = procedures_as_elements(procedures); Hasher::hash_elements(&elements) } /// Converts given procedures into field elements -pub(crate) fn procedures_as_elements(procedures: &[AccountProcedureInfo]) -> Vec { - procedures.iter().flat_map(|procedure| <[Felt; 8]>::from(*procedure)).collect() +pub(crate) fn procedures_as_elements(procedures: &[AccountProcedureRoot]) -> Vec { + procedures.iter().flat_map(AccountProcedureRoot::as_elements).copied().collect() } // TESTS diff --git a/crates/miden-objects/src/account/code/procedure.rs b/crates/miden-objects/src/account/code/procedure.rs index 8281bc532e..fbef026007 100644 --- a/crates/miden-objects/src/account/code/procedure.rs +++ b/crates/miden-objects/src/account/code/procedure.rs @@ -1,11 +1,13 @@ -use alloc::string::ToString; +use alloc::string::String; use alloc::sync::Arc; use miden_core::mast::MastForest; use miden_core::prettier::PrettyPrint; use miden_processor::{MastNode, MastNodeExt, MastNodeId}; +use miden_protocol_macros::WordWrapper; use super::Felt; +use crate::Word; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -13,148 +15,47 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{AccountError, FieldElement, Word}; -// ACCOUNT PROCEDURE INFO +// ACCOUNT PROCEDURE ROOT // ================================================================================================ -/// Information about a procedure exposed in a public account interface. -/// -/// The info included the MAST root of the procedure, the storage offset applied to all account -/// storage-related accesses made by this procedure and the storage size allowed to be accessed -/// by this procedure. -/// -/// The offset is applied to any accesses made from within the procedure to the associated -/// account's storage. For example, if storage offset for a procedure is set to 1, a call -/// to the account::get_item(storage_slot=4) made from this procedure would actually access -/// storage slot with index 5. -/// -/// The size is used to limit how many storage slots a given procedure can access in the associated -/// account's storage. For example, if storage size for a procedure is set to 3, the procedure will -/// be bounded to access storage slots in the range [storage_offset, storage_offset + 3 - 1]. -/// Furthermore storage_size = 0 indicates that a procedure does not need to access storage. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct AccountProcedureInfo { - mast_root: Word, - storage_offset: u8, - storage_size: u8, -} - -impl AccountProcedureInfo { - /// The number of field elements needed to represent an [AccountProcedureInfo] in kernel memory. - pub const NUM_ELEMENTS_PER_PROC: usize = 8; +/// The MAST root of a public procedure in an account's interface. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, WordWrapper)] +pub struct AccountProcedureRoot(Word); - // CONSTRUCTOR - // -------------------------------------------------------------------------------------------- - - /// Returns a new instance of an [AccountProcedureInfo]. - /// - /// # Errors - /// - If `storage_size` is 0 and `storage_offset` is not 0. - /// - If `storage_size + storage_offset` is greater than `MAX_NUM_STORAGE_SLOTS`. - pub fn new( - mast_root: Word, - storage_offset: u8, - storage_size: u8, - ) -> Result { - if storage_size == 0 && storage_offset != 0 { - return Err(AccountError::PureProcedureWithStorageOffset); - } - - // Check if the addition would exceed AccountStorage::MAX_NUM_STORAGE_SLOTS (= 255) which is - // the case if the addition overflows. - if storage_offset.checked_add(storage_size).is_none() { - return Err(AccountError::StorageOffsetPlusSizeOutOfBounds( - storage_offset as u16 + storage_size as u16, - )); - } - - Ok(Self { mast_root, storage_offset, storage_size }) - } +impl AccountProcedureRoot { + /// The number of field elements that represent an [`AccountProcedureRoot`] in kernel memory. + pub const NUM_ELEMENTS: usize = 4; // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- /// Returns a reference to the procedure's mast root. pub fn mast_root(&self) -> &Word { - &self.mast_root - } - - /// Returns the procedure's storage offset. - pub fn storage_offset(&self) -> u8 { - self.storage_offset - } - - /// Returns the procedure's storage size. - pub fn storage_size(&self) -> u8 { - self.storage_size + &self.0 } } -impl From for [Felt; 8] { - fn from(value: AccountProcedureInfo) -> Self { - let mut result = [Felt::ZERO; 8]; - - // copy mast_root into first 4 elements - result[0..4].copy_from_slice(value.mast_root().as_elements()); - - // copy the storage offset into value[4] - result[4] = Felt::from(value.storage_offset); - - // copy the storage size into value[5] - result[5] = Felt::from(value.storage_size); - - result +impl From for Word { + fn from(root: AccountProcedureRoot) -> Self { + *root.mast_root() } } -impl TryFrom<[Felt; 8]> for AccountProcedureInfo { - type Error = AccountError; - - fn try_from(value: [Felt; 8]) -> Result { - // get mast_root from first 4 elements - let mast_root = Word::from(<[Felt; 4]>::try_from(&value[0..4]).unwrap()); - - // get storage_offset form value[4] - let storage_offset: u8 = value[4].try_into().map_err(|_| { - AccountError::AccountCodeProcedureStorageOffsetTooLarge(mast_root, value[4]) - })?; - - // get storage_size form value[5] - let storage_size: u8 = value[5].try_into().map_err(|_| { - AccountError::AccountCodeProcedureStorageSizeTooLarge(mast_root, value[5]) - })?; - - // Check if the remaining values are 0 - if value[6] != Felt::ZERO || value[7] != Felt::ZERO { - return Err(AccountError::AccountCodeProcedureInvalidPadding(mast_root)); - } - - Ok(Self { mast_root, storage_offset, storage_size }) - } -} - -impl Serializable for AccountProcedureInfo { +impl Serializable for AccountProcedureRoot { fn write_into(&self, target: &mut W) { - target.write(self.mast_root); - target.write_u8(self.storage_offset); - target.write_u8(self.storage_size) + target.write(self.0); } fn get_size_hint(&self) -> usize { - self.mast_root.get_size_hint() - + self.storage_offset.get_size_hint() - + self.storage_size.get_size_hint() + self.0.get_size_hint() } } -impl Deserializable for AccountProcedureInfo { +impl Deserializable for AccountProcedureRoot { fn read_from(source: &mut R) -> Result { let mast_root: Word = source.read()?; - let storage_offset = source.read_u8()?; - let storage_size = source.read_u8()?; - Self::new(mast_root, storage_offset, storage_size) - .map_err(|err| DeserializationError::InvalidValue(err.to_string())) + Ok(Self::from_raw(mast_root)) } } @@ -165,7 +66,7 @@ impl Deserializable for AccountProcedureInfo { #[derive(Debug, Clone)] pub struct PrintableProcedure { mast: Arc, - procedure_info: AccountProcedureInfo, + procedure_root: AccountProcedureRoot, entrypoint: MastNodeId, } @@ -173,26 +74,18 @@ impl PrintableProcedure { /// Creates a new PrintableProcedure instance from its components. pub(crate) fn new( mast: Arc, - procedure_info: AccountProcedureInfo, + procedure_root: AccountProcedureRoot, entrypoint: MastNodeId, ) -> Self { - Self { mast, procedure_info, entrypoint } + Self { mast, procedure_root, entrypoint } } fn entrypoint(&self) -> &MastNode { &self.mast[self.entrypoint] } - pub(crate) fn storage_offset(&self) -> u8 { - self.procedure_info.storage_offset() - } - - pub(crate) fn storage_size(&self) -> u8 { - self.procedure_info.storage_size() - } - pub(crate) fn mast_root(&self) -> &Word { - self.procedure_info.mast_root() + self.procedure_root.mast_root() } } @@ -214,32 +107,16 @@ impl PrettyPrint for PrintableProcedure { #[cfg(test)] mod tests { - use miden_core::Felt; use miden_crypto::utils::{Deserializable, Serializable}; - use crate::account::{AccountCode, AccountProcedureInfo}; - - #[test] - fn test_from_to_account_procedure() { - let account_code = AccountCode::mock(); - - let procedure = account_code.procedures()[0]; - - // from procedure to [Felt; 8] - let felts: [Felt; 8] = procedure.into(); - - // try_from [Felt; 8] to procedure - let final_procedure: AccountProcedureInfo = felts.try_into().unwrap(); - - assert_eq!(procedure, final_procedure); - } + use crate::account::{AccountCode, AccountProcedureRoot}; #[test] fn test_serde_account_procedure() { let account_code = AccountCode::mock(); let serialized = account_code.procedures()[0].to_bytes(); - let deserialized = AccountProcedureInfo::read_from_bytes(&serialized).unwrap(); + let deserialized = AccountProcedureRoot::read_from_bytes(&serialized).unwrap(); assert_eq!(account_code.procedures()[0], deserialized); } diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 47764d6c20..cabbde9432 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -32,7 +32,7 @@ pub use builder::AccountBuilder; pub mod code; pub use code::AccountCode; -pub use code::procedure::AccountProcedureInfo; +pub use code::procedure::AccountProcedureRoot; pub mod component; pub use component::{ @@ -165,31 +165,23 @@ impl Account { /// Creates an account's [`AccountCode`] and [`AccountStorage`] from the provided components. /// /// This merges all libraries of the components into a single - /// [`MastForest`](miden_processor::MastForest) to produce the [`AccountCode`]. For each - /// procedure in the resulting forest, the storage offset and size are set so that the - /// procedure can only access the storage slots of the component in which it was defined and - /// each component's storage offset is the total number of slots in the previous components. - /// To illustrate, given two components with one and two storage slots respectively: + /// [`MastForest`](miden_processor::MastForest) to produce the [`AccountCode`]. /// - /// - RpoFalcon512 Component: Component slot 0 stores the public key. - /// - Custom Component: Component slot 0 stores a custom [`StorageSlotContent::Value`] and - /// component slot 1 stores a custom [`StorageSlotContent::Map`]. - /// - /// When combined, their assigned slots in the [`AccountStorage`] would be: - /// - /// - The RpoFalcon512 Component has offset 0 and size 1: Account slot 0 stores the public key. - /// - The Custom Component has offset 1 and size 2: Account slot 1 stores the value and account - /// slot 2 stores the map. + /// The storage slots of all components are merged into a single [`AccountStorage`], where the + /// slots are sorted by their [`StorageSlotName`]. /// /// The resulting commitments from code and storage can then be used to construct an /// [`AccountId`]. Finally, a new account can then be instantiated from those parts using /// [`Account::new`]. /// - /// If the account type is faucet the reserved slot (slot 0) will be initialized. - /// - For Fungible Faucets the value is [`StorageSlot::empty_value`]. - /// - For Non-Fungible Faucets the value is [`StorageSlot::empty_map`]. + /// If the account type is faucet the reserved slot ([`AccountStorage::faucet_metadata_slot`]) + /// will be initialized as follows: + /// - For [`AccountType::FungibleFaucet`] the value is set to + /// [`StorageSlotContent::empty_value`]. + /// - For [`AccountType::NonFungibleFaucet`] the value is set to + /// [`StorageSlotContent::empty_map`]. /// - /// If the storage needs to be initialized with certain values in that slot, those can be added + /// If the storage needs to be initialized with certain values in that slot, those must be added /// after construction with the standard set methods for items and maps. /// /// # Errors @@ -209,7 +201,7 @@ impl Account { ) -> Result<(AccountCode, AccountStorage), AccountError> { validate_components_support_account_type(&components, account_type)?; - let code = AccountCode::from_components_unchecked(&components, account_type)?; + let code = AccountCode::from_components_unchecked(&components)?; let storage = AccountStorage::from_components(components, account_type)?; Ok((code, storage)) diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index d99cbf3a80..96ff957242 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -99,12 +99,6 @@ pub enum AccountError { AccountCodeNoProcedures, #[error("account code contains {0} procedures but it may contain at most {max} procedures", max = AccountCode::MAX_NUM_PROCEDURES)] AccountCodeTooManyProcedures(usize), - #[error("account procedure {0}'s storage offset {1} does not fit into u8")] - AccountCodeProcedureStorageOffsetTooLarge(Word, Felt), - #[error("account procedure {0}'s storage size {1} does not fit into u8")] - AccountCodeProcedureStorageSizeTooLarge(Word, Felt), - #[error("account procedure {0}'s final two elements must be Felt::ZERO")] - AccountCodeProcedureInvalidPadding(Word), #[error("failed to assemble account component:\n{}", PrintDiagnostic::new(.0))] AccountComponentAssemblyError(Report), #[error("failed to merge components into one account code mast forest")] @@ -166,14 +160,6 @@ pub enum AccountError { UnsortedStorageSlots, #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)] StorageTooManySlots(u64), - #[error("procedure storage offset + size is {0} which exceeds the maximum value of {max}", - max = AccountStorage::MAX_NUM_STORAGE_SLOTS - )] - StorageOffsetPlusSizeOutOfBounds(u16), - #[error( - "procedure which does not access storage (storage size = 0) has non-zero storage offset" - )] - PureProcedureWithStorageOffset, #[error( "account component at index {component_index} is incompatible with account of type {account_type}" )] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 65d6ec870e..010e5d9874 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -26,7 +26,7 @@ use miden_objects::account::{ AccountBuilder, AccountComponent, AccountId, - AccountProcedureInfo, + AccountProcedureRoot, AccountStorage, AccountStorageMode, StorageSlot, @@ -1850,7 +1850,7 @@ fn foreign_account_data_memory_assertions( for (i, elements) in foreign_account .code() .as_elements() - .chunks(AccountProcedureInfo::NUM_ELEMENTS_PER_PROC / 2) + .chunks(AccountProcedureRoot::NUM_ELEMENTS) .enumerate() { assert_eq!( diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index e96c901f69..6eb4ca1334 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -67,7 +67,7 @@ use miden_objects::account::{ AccountBuilder, AccountId, AccountIdVersion, - AccountProcedureInfo, + AccountProcedureRoot, AccountStorage, AccountStorageMode, AccountType, @@ -431,14 +431,14 @@ fn account_data_memory_assertions(exec_output: &ExecutionOutput, inputs: &Transa .account() .code() .as_elements() - .chunks(AccountProcedureInfo::NUM_ELEMENTS_PER_PROC / 2) + .chunks(AccountProcedureRoot::NUM_ELEMENTS) .enumerate() { assert_eq!( exec_output .get_kernel_mem_word(NATIVE_ACCT_PROCEDURES_SECTION_PTR + (i * WORD_SIZE) as u32), Word::try_from(elements).unwrap(), - "The account procedures and storage offsets should be stored starting at NATIVE_ACCT_PROCEDURES_SECTION_PTR" + "The account procedures should be stored starting at NATIVE_ACCT_PROCEDURES_SECTION_PTR" ); } } diff --git a/crates/miden-tx/src/host/account_procedures.rs b/crates/miden-tx/src/host/account_procedures.rs index 5d5a302cab..bbe747b481 100644 --- a/crates/miden-tx/src/host/account_procedures.rs +++ b/crates/miden-tx/src/host/account_procedures.rs @@ -36,12 +36,12 @@ impl AccountProcedureIndexMap { /// transaction, enabling fast procedure index lookups at runtime. pub fn insert_code(&mut self, code: &AccountCode) { let mut procedure_map = BTreeMap::new(); - for (proc_idx, proc_info) in code.procedures().iter().enumerate() { + for (proc_idx, proc_root) in code.procedures().iter().enumerate() { // SAFETY: AccountCode::MAX_NUM_PROCEDURES is 256 and so the highest possible index is // 255. let proc_idx = u8::try_from(proc_idx).expect("account code should contain at most 256 procedures"); - procedure_map.insert(*proc_info.mast_root(), proc_idx); + procedure_map.insert(*proc_root.mast_root(), proc_idx); } self.0.insert(code.commitment(), procedure_map); From 551fa0b078dc839d01f83cfabf424997312c7faa Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Sun, 14 Dec 2025 01:56:40 +0700 Subject: [PATCH 046/114] feat: allow components with duplicate procedure roots to be merged (#2164) --- CHANGELOG.md | 15 +- .../src/account/faucets/network_fungible.rs | 2 +- .../miden-lib/src/errors/tx_kernel_errors.rs | 2 - crates/miden-lib/src/transaction/memory.rs | 2 + crates/miden-objects/src/account/code/mod.rs | 24 ++-- crates/miden-objects/src/account/mod.rs | 22 --- crates/miden-objects/src/errors.rs | 2 - .../src/kernel_tests/tx/test_account.rs | 129 +++++++++++++++++- 8 files changed, 147 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 012215e54f..8d434dd7a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,23 +4,24 @@ ### Features -- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). +- [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). +- [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). ### Changes -- Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). -- [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)). +- Added proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). - Split tx progress events out into a separate enum ([#2103](https://github.com/0xMiden/miden-base/pull/2103)). - Added `note::get_network_account_tag` procedure ([#2120](https://github.com/0xMiden/miden-base/pull/2120)). - [BREAKING] Updated MINT note to support both private and public output note creation ([#2123](https://github.com/0xMiden/miden-base/pull/2123)). - [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)). -- [BREAKING] Add public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)). +- [BREAKING] Added public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)). - [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). -Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)). -- [BREAKING] Make `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). -- [BREAKING] Rename `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). +- [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)). +- Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)). +- [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). +- [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 3fe3730df8..28c3f27cf7 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -57,7 +57,7 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// ## Storage Layout /// /// - [`Self::metadata_slot`]: Fungible faucet metadata. -/// - [`Self::owner_config_slot`]: The owner account of this network facuet. +/// - [`Self::owner_config_slot`]: The owner account of this network faucet. /// /// [builder]: crate::utils::CodeBuilder pub struct NetworkFungibleFaucet { diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index 140c2248d1..530c59315a 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -58,8 +58,6 @@ pub const ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH: MasmError = MasmError::from_s pub const ERR_ACCOUNT_STORAGE_MAP_ENTRIES_DO_NOT_MATCH_MAP_ROOT: MasmError = MasmError::from_static_str("storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to"); /// Error Message: "slot IDs must be unique and sorted in ascending order" pub const ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE: MasmError = MasmError::from_static_str("slot IDs must be unique and sorted in ascending order"); -/// Error Message: "provided storage slot index is out of bounds" -pub const ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("provided storage slot index is out of bounds"); /// Error Message: "number of account procedures exceeds the maximum limit of 256" pub const ERR_ACCOUNT_TOO_MANY_PROCEDURES: MasmError = MasmError::from_static_str("number of account procedures exceeds the maximum limit of 256"); /// Error Message: "number of account storage slots exceeds the maximum limit of 255" diff --git a/crates/miden-lib/src/transaction/memory.rs b/crates/miden-lib/src/transaction/memory.rs index ad08eaf5d4..d143a96bc5 100644 --- a/crates/miden-lib/src/transaction/memory.rs +++ b/crates/miden-lib/src/transaction/memory.rs @@ -32,6 +32,8 @@ pub type StorageSlot = u8; // // Here the "end pointer" is the last memory pointer occupied by the current data // +// TODO: Rearrange the memory sections that follow account procedures to be contiguous to it. +// // | Section | Start address, pointer (word pointer) | End address, pointer (word pointer) | Comment | // | ------------------ | :-----------------------------------: | :---------------------------------: | ----------------------------------- | // | ID and nonce | 0 (0) | 3 (0) | | diff --git a/crates/miden-objects/src/account/code/mod.rs b/crates/miden-objects/src/account/code/mod.rs index 1121693c28..39f58e897c 100644 --- a/crates/miden-objects/src/account/code/mod.rs +++ b/crates/miden-objects/src/account/code/mod.rs @@ -335,7 +335,7 @@ impl AccountProcedureBuilder { let mut auth_proc_count = 0; for (proc_root, is_auth) in component.get_procedures() { - self.add_procedure(proc_root)?; + self.add_procedure(proc_root); if is_auth { let auth_proc_idx = self.procedures.len() - 1; @@ -358,27 +358,19 @@ impl AccountProcedureBuilder { if is_auth { return Err(AccountError::AccountCodeMultipleAuthComponents); } - self.add_procedure(proc_mast_root)?; + self.add_procedure(proc_mast_root); } Ok(()) } - fn add_procedure(&mut self, proc_mast_root: Word) -> Result<(), AccountError> { - // TODO: Check if we can now safely allow this. - // Disallow procedures with the same MAST root from different components. - let proc_mast_root = AccountProcedureRoot::from_raw(proc_mast_root); - // The number of procedures in accounts is generally small, so checking via linear search - // should be fine. - if self.procedures.contains(&proc_mast_root) { - return Err(AccountError::AccountComponentDuplicateProcedureRoot( - proc_mast_root.as_word(), - )); + fn add_procedure(&mut self, proc_mast_root: Word) { + // Allow procedures with the same MAST root from different components, but only add them + // once. + let proc_root = AccountProcedureRoot::from_raw(proc_mast_root); + if !self.procedures.contains(&proc_root) { + self.procedures.push(proc_root); } - - self.procedures.push(proc_mast_root); - - Ok(()) } fn build(self) -> Result, AccountError> { diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index cabbde9432..83d7bf3b55 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -895,28 +895,6 @@ mod tests { )) } - /// Two components who export a procedure with the same MAST root should fail to convert into - /// code and storage. - #[test] - fn test_account_duplicate_exported_mast_root() { - let code1 = "export.foo add eq.1 end"; - let code2 = "export.bar add eq.1 end"; - - let library1 = Assembler::default().assemble_library([code1]).unwrap(); - let library2 = Assembler::default().assemble_library([code2]).unwrap(); - - let component1 = AccountComponent::new(library1, vec![]).unwrap().with_supports_all_types(); - let component2 = AccountComponent::new(library2, vec![]).unwrap().with_supports_all_types(); - - let err = Account::initialize_from_components( - AccountType::RegularAccountUpdatableCode, - vec![NoopAuthComponent.into(), component1, component2], - ) - .unwrap_err(); - - assert_matches!(err, AccountError::AccountComponentDuplicateProcedureRoot(_)) - } - /// Tests all cases of account ID seed validation. #[test] fn seed_validation() -> anyhow::Result<()> { diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 96ff957242..46e4348873 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -103,8 +103,6 @@ pub enum AccountError { AccountComponentAssemblyError(Report), #[error("failed to merge components into one account code mast forest")] AccountComponentMastForestMergeError(#[source] MastForestError), - #[error("procedure with MAST root {0} is present in multiple account components")] - AccountComponentDuplicateProcedureRoot(Word), // #[error("failed to create account component")] // AccountComponentTemplateInstantiationError(#[source] AccountComponentTemplateError), #[error("account component contains multiple authentication procedures")] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index c2fcae7018..b7d08b9132 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -34,8 +34,8 @@ use miden_objects::account::{ StorageSlotName, StorageSlotType, }; -use miden_objects::assembly::DefaultSourceManager; use miden_objects::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette}; +use miden_objects::assembly::{DefaultSourceManager, Library}; use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::note::NoteType; use miden_objects::testing::account_id::{ @@ -48,6 +48,7 @@ use miden_objects::testing::account_id::{ }; use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; use miden_objects::transaction::OutputNote; +use miden_objects::utils::sync::LazyLock; use miden_objects::{LexicographicWord, StarkField}; use miden_processor::{ExecutionError, Word}; use miden_tx::LocalTransactionProver; @@ -1749,3 +1750,129 @@ async fn incrementing_nonce_overflow_fails() -> anyhow::Result<()> { Ok(()) } + +/// Tests that merging two components that have a procedure with the same mast root +/// (`get_slot_content`) works. +/// +/// Asserts that the procedure is callable via both names. +#[tokio::test] +async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> { + static TEST_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::slot::test").expect("storage slot name should be valid") + }); + + static COMPONENT_1_LIBRARY: LazyLock = LazyLock::new(|| { + let code = format!( + r#" + use.miden::active_account + + const TEST_SLOT_NAME = word("{test_slot_name}") + + pub proc get_slot_content + push.TEST_SLOT_NAME[0..2] + exec.active_account::get_item + swapw dropw + end + "#, + test_slot_name = &*TEST_SLOT_NAME + ); + + let source = NamedSource::new("component1::interface", code); + TransactionKernel::assembler() + .with_debug_mode(true) + .assemble_library([source]) + .expect("mock account code should be valid") + }); + + static COMPONENT_2_LIBRARY: LazyLock = LazyLock::new(|| { + let code = format!( + r#" + use.miden::active_account + use.miden::native_account + + const TEST_SLOT_NAME = word("{test_slot_name}") + + pub proc get_slot_content + push.TEST_SLOT_NAME[0..2] + exec.active_account::get_item + swapw dropw + end + + pub proc set_slot_content + push.5.6.7.8 + push.TEST_SLOT_NAME[0..2] + exec.native_account::set_item + swapw dropw + end + "#, + test_slot_name = &*TEST_SLOT_NAME + ); + + let source = NamedSource::new("component2::interface", code); + TransactionKernel::assembler() + .with_debug_mode(true) + .assemble_library([source]) + .expect("mock account code should be valid") + }); + + struct CustomComponent1 { + slot: StorageSlot, + } + + impl From for AccountComponent { + fn from(component: CustomComponent1) -> AccountComponent { + AccountComponent::new(COMPONENT_1_LIBRARY.clone(), vec![component.slot]) + .expect("should be valid") + .with_supports_all_types() + } + } + + struct CustomComponent2; + + impl From for AccountComponent { + fn from(_component: CustomComponent2) -> AccountComponent { + AccountComponent::new(COMPONENT_2_LIBRARY.clone(), vec![]) + .expect("should be valid") + .with_supports_all_types() + } + } + + let slot = StorageSlot::with_value(TEST_SLOT_NAME.clone(), Word::from([1, 2, 3, 4u32])); + + let account = AccountBuilder::new([42; 32]) + .with_auth_component(Auth::IncrNonce) + .with_component(CustomComponent1 { slot: slot.clone() }) + .with_component(CustomComponent2) + .build() + .context("failed to build account")?; + + let tx_script = r#" + use.component1::interface->comp1_interface + use.component2::interface->comp2_interface + + begin + call.comp1_interface::get_slot_content + push.1.2.3.4 + assert_eqw.err="failed to get slot content1" + + call.comp2_interface::set_slot_content + + call.comp2_interface::get_slot_content + push.5.6.7.8 + assert_eqw.err="failed to get slot content2" + end + "#; + + let tx_script = CodeBuilder::default() + .with_dynamically_linked_library(COMPONENT_1_LIBRARY.clone())? + .with_dynamically_linked_library(COMPONENT_2_LIBRARY.clone())? + .compile_tx_script(tx_script)?; + + TransactionContextBuilder::new(account) + .tx_script(tx_script) + .build()? + .execute() + .await?; + + Ok(()) +} From eabe72599db09585dbb532d37f36dd5cd53799e6 Mon Sep 17 00:00:00 2001 From: Himess <95512809+Himess@users.noreply.github.com> Date: Sun, 14 Dec 2025 00:17:37 +0300 Subject: [PATCH 047/114] feat: add `TokenSymbol::from_static_str()` const function (#2148) --- CHANGELOG.md | 1 + .../miden-objects/src/asset/token_symbol.rs | 114 ++++++++++++++++-- crates/miden-objects/src/errors.rs | 4 +- 3 files changed, 104 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d434dd7a6..493d029a09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). - [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)). - Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)). +- Added `TokenSymbol::from_static_str` const function for compile-time token symbol validation ([#2148](https://github.com/0xMiden/miden-base/pull/2148)). - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). diff --git a/crates/miden-objects/src/asset/token_symbol.rs b/crates/miden-objects/src/asset/token_symbol.rs index 656a9c2ff5..289c66443a 100644 --- a/crates/miden-objects/src/asset/token_symbol.rs +++ b/crates/miden-objects/src/asset/token_symbol.rs @@ -1,4 +1,4 @@ -use alloc::string::{String, ToString}; +use alloc::string::String; use super::{Felt, TokenSymbolError}; @@ -20,8 +20,33 @@ impl TokenSymbol { /// This value encodes the "ZZZZZZ" token symbol. pub const MAX_ENCODED_VALUE: u64 = 8031810156; + /// Constructs a new [`TokenSymbol`] from a static string. + /// + /// This function is `const` and can be used to define token symbols as constants, e.g.: + /// + /// ```rust + /// # use miden_objects::asset::TokenSymbol; + /// const TOKEN: TokenSymbol = TokenSymbol::from_static_str("ETH"); + /// ``` + /// + /// This is convenient because using a string that is not a valid token symbol fails to + /// compile. + /// + /// # Panics + /// + /// Panics if: + /// - The length of the provided string is less than 1 or greater than 6. + /// - The provided token string contains characters that are not uppercase ASCII. + pub const fn from_static_str(symbol: &'static str) -> Self { + match encode_symbol_to_felt(symbol) { + Ok(felt) => Self(felt), + // We cannot format the error in a const context. + Err(_) => panic!("invalid token symbol"), + } + } + /// Creates a new [`TokenSymbol`] instance from the provided token name string. - /// + /// /// # Errors /// Returns an error if: /// - The length of the provided string is less than 1 or greater than 6. @@ -89,22 +114,31 @@ impl TryFrom for TokenSymbol { /// Returns an error if: /// - The length of the provided string is less than 1 or greater than 6. /// - The provided token string contains characters that are not uppercase ASCII. -fn encode_symbol_to_felt(s: &str) -> Result { - if s.is_empty() || s.len() > TokenSymbol::MAX_SYMBOL_LENGTH { - return Err(TokenSymbolError::InvalidLength(s.len())); - } else if s.chars().any(|c| !c.is_ascii_uppercase()) { - return Err(TokenSymbolError::InvalidCharacter(s.to_string())); +const fn encode_symbol_to_felt(s: &str) -> Result { + let bytes = s.as_bytes(); + let len = bytes.len(); + + if len == 0 || len > TokenSymbol::MAX_SYMBOL_LENGTH { + return Err(TokenSymbolError::InvalidLength(len)); } - let mut encoded_value = 0; - for char in s.chars() { - let digit = char as u64 - b'A' as u64; - debug_assert!(digit < TokenSymbol::ALPHABET_LENGTH); + let mut encoded_value: u64 = 0; + let mut idx = 0; + + while idx < len { + let byte = bytes[idx]; + + if !byte.is_ascii_uppercase() { + return Err(TokenSymbolError::InvalidCharacter); + } + + let digit = (byte - b'A') as u64; encoded_value = encoded_value * TokenSymbol::ALPHABET_LENGTH + digit; + idx += 1; } // add token length to the encoded value to be able to decode the exact number of characters - encoded_value = encoded_value * TokenSymbol::ALPHABET_LENGTH + s.len() as u64; + encoded_value = encoded_value * TokenSymbol::ALPHABET_LENGTH + len as u64; Ok(Felt::new(encoded_value)) } @@ -198,7 +232,7 @@ mod test { let symbol = "$$$"; let felt = encode_symbol_to_felt(symbol); - assert_matches!(felt.unwrap_err(), TokenSymbolError::InvalidCharacter(s) if s == *"$$$"); + assert_matches!(felt.unwrap_err(), TokenSymbolError::InvalidCharacter); let symbol = "ABCDEF"; let token_symbol = TokenSymbol::try_from(symbol); @@ -231,4 +265,58 @@ mod test { let token_symbol = TokenSymbol::try_from("ZZZZZZ").unwrap(); assert_eq!(Felt::from(token_symbol).as_int(), TokenSymbol::MAX_ENCODED_VALUE); } + + // Const function tests + // -------------------------------------------------------------------------------------------- + + const _TOKEN0: TokenSymbol = TokenSymbol::from_static_str("A"); + const _TOKEN1: TokenSymbol = TokenSymbol::from_static_str("ETH"); + const _TOKEN2: TokenSymbol = TokenSymbol::from_static_str("MIDEN"); + const _TOKEN3: TokenSymbol = TokenSymbol::from_static_str("ZZZZZZ"); + + #[test] + fn test_from_static_str_matches_new() { + // Test that from_static_str produces the same result as new + let symbols = ["A", "BC", "ETH", "MIDEN", "ZZZZZZ"]; + for symbol in symbols { + let from_new = TokenSymbol::new(symbol).unwrap(); + let from_static = TokenSymbol::from_static_str(symbol); + assert_eq!( + Felt::from(from_new), + Felt::from(from_static), + "Mismatch for symbol: {}", + symbol + ); + } + } + + #[test] + #[should_panic(expected = "invalid token symbol")] + fn token_symbol_panics_on_empty_string() { + TokenSymbol::from_static_str(""); + } + + #[test] + #[should_panic(expected = "invalid token symbol")] + fn token_symbol_panics_on_too_long_string() { + TokenSymbol::from_static_str("ABCDEFG"); + } + + #[test] + #[should_panic(expected = "invalid token symbol")] + fn token_symbol_panics_on_lowercase() { + TokenSymbol::from_static_str("eth"); + } + + #[test] + #[should_panic(expected = "invalid token symbol")] + fn token_symbol_panics_on_invalid_character() { + TokenSymbol::from_static_str("ET$"); + } + + #[test] + #[should_panic(expected = "invalid token symbol")] + fn token_symbol_panics_on_number() { + TokenSymbol::from_static_str("ETH1"); + } } diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 46e4348873..b6fc03a67c 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -479,8 +479,8 @@ pub enum TokenSymbolError { ValueTooLarge(u64), #[error("token symbol should have length between 1 and 6 characters, but {0} was provided")] InvalidLength(usize), - #[error("token symbol `{0}` contains characters that are not uppercase ASCII")] - InvalidCharacter(String), + #[error("token symbol contains a character that is not uppercase ASCII")] + InvalidCharacter, #[error("token symbol data left after decoding the specified number of characters")] DataNotFullyDecoded, } From c1ade39d702b1cc5cb88916c24593ae9bab98c8a Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:13:23 -0800 Subject: [PATCH 048/114] refactor: rename 'tracked' to 'trigger' for ACL procedure naming consistency (#2166) --- CHANGELOG.md | 1 + .../ecdsa_k256_keccak_acl.masm | 2 +- .../rpo_falcon_512_acl.masm | 2 +- .../src/account/auth/ecdsa_k256_keccak_acl.rs | 20 ++++++++--------- .../src/account/auth/rpo_falcon_512_acl.rs | 22 +++++++++---------- .../src/account/faucets/basic_fungible.rs | 10 ++++----- .../src/account/faucets/network_fungible.rs | 4 ++-- crates/miden-testing/tests/auth/ecdsa_acl.rs | 4 ++-- .../tests/auth/rpo_falcon_acl.rs | 4 ++-- docs/src/account/code.md | 4 ++-- 10 files changed, 37 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 493d029a09..c543c335a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Added `TokenSymbol::from_static_str` const function for compile-time token symbol validation ([#2148](https://github.com/0xMiden/miden-base/pull/2148)). - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). +- [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm index 978f24881a..ade3c9c9f8 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm +++ b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm @@ -19,7 +19,7 @@ const PUBLIC_KEY_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_acl::pub const AUTH_CONFIG_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_acl::config") # The slot where the map of auth trigger procedure roots is stored. -const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_acl::tracked_procedure_roots") +const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::ecdsa_k256_keccak_acl::trigger_procedure_roots") #! Authenticate a transaction using the ECDSA signature scheme based on procedure calls and note usage. #! diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm b/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm index 2c50339084..d4d7e42e07 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm +++ b/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm @@ -19,7 +19,7 @@ const PUBLIC_KEY_SLOT = word("miden::standards::auth::rpo_falcon512_acl::public_ const AUTH_CONFIG_SLOT = word("miden::standards::auth::rpo_falcon512_acl::config") # The slot where the map of auth trigger procedure roots is stored. -const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::rpo_falcon512_acl::tracked_procedure_roots") +const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::rpo_falcon512_acl::trigger_procedure_roots") #! Authenticate a transaction using the Falcon signature scheme based on procedure calls and note usage. #! diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs index 85b1a412a6..834222c7f6 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs @@ -23,8 +23,8 @@ static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { .expect("storage slot name should be valid") }); -static TRACKED_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::tracked_procedure_roots") +static TRIGGER_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::ecdsa_k256_keccak_acl::trigger_procedure_roots") .expect("storage slot name should be valid") }); @@ -118,7 +118,7 @@ impl Default for AuthEcdsaK256KeccakAclConfig { /// /// ## Storage Layout /// - Slot 0(value): Public key (same as EcdsaK256Keccak) -/// - Slot 1(value): [num_tracked_procs, allow_unauthorized_output_notes, +/// - Slot 1(value): [num_trigger_procs, allow_unauthorized_output_notes, /// allow_unauthorized_input_notes, 0] /// - Slot 2(map): A map with trigger procedure roots /// @@ -166,9 +166,9 @@ impl AuthEcdsaK256KeccakAcl { &CONFIG_SLOT_NAME } - /// Returns the [`StorageSlotName`] where the tracked procedure roots are stored. - pub fn tracked_procedure_roots_slot() -> &'static StorageSlotName { - &TRACKED_PROCEDURE_ROOT_SLOT_NAME + /// Returns the [`StorageSlotName`] where the trigger procedure roots are stored. + pub fn trigger_procedure_roots_slot() -> &'static StorageSlotName { + &TRIGGER_PROCEDURE_ROOT_SLOT_NAME } } @@ -194,7 +194,7 @@ impl From for AccountComponent { ]), )); - // Tracked procedure roots slot + // Trigger procedure roots slot // We add the map even if there are no trigger procedures, to always maintain the same // storage layout. let map_entries = ecdsa @@ -206,7 +206,7 @@ impl From for AccountComponent { // Safe to unwrap because we know that the map keys are unique. storage_slots.push(StorageSlot::with_map( - AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot().clone(), + AuthEcdsaK256KeccakAcl::trigger_procedure_roots_slot().clone(), StorageMap::with_entries(map_entries).unwrap(), )); @@ -295,7 +295,7 @@ mod tests { let proc_root = account .storage() .get_map_item( - AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot(), + AuthEcdsaK256KeccakAcl::trigger_procedure_roots_slot(), Word::from([i as u32, 0, 0, 0]), ) .expect("storage map access failed"); @@ -305,7 +305,7 @@ mod tests { // When no procedures, the map should return empty for key [0,0,0,0] let proc_root = account .storage() - .get_map_item(AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot(), Word::empty()) + .get_map_item(AuthEcdsaK256KeccakAcl::trigger_procedure_roots_slot(), Word::empty()) .expect("storage map access failed"); assert_eq!(proc_root, Word::empty()); } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs index 3c3f0ce746..10c27a140f 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs @@ -23,8 +23,8 @@ static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { .expect("storage slot name should be valid") }); -static TRACKED_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512_acl::tracked_procedure_roots") +static TRIGGER_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::auth::rpo_falcon512_acl::trigger_procedure_roots") .expect("storage slot name should be valid") }); @@ -119,9 +119,9 @@ impl Default for AuthRpoFalcon512AclConfig { /// ## Storage Layout /// /// - [`Self::public_key_slot`]: Public key -/// - [`Self::config_slot`]: `[num_tracked_procs, allow_unauthorized_output_notes, +/// - [`Self::config_slot`]: `[num_trigger_procs, allow_unauthorized_output_notes, /// allow_unauthorized_input_notes, 0]` -/// - [`Self::tracked_procedure_roots_slot`]: A map with trigger procedure roots +/// - [`Self::trigger_procedure_roots_slot`]: A map with trigger procedure roots /// /// ## Important Note on Procedure Detection /// The procedure-based authentication relies on the `was_procedure_called` kernel function, @@ -167,9 +167,9 @@ impl AuthRpoFalcon512Acl { &CONFIG_SLOT_NAME } - /// Returns the [`StorageSlotName`] where the tracked procedure roots are stored. - pub fn tracked_procedure_roots_slot() -> &'static StorageSlotName { - &TRACKED_PROCEDURE_ROOT_SLOT_NAME + /// Returns the [`StorageSlotName`] where the trigger procedure roots are stored. + pub fn trigger_procedure_roots_slot() -> &'static StorageSlotName { + &TRIGGER_PROCEDURE_ROOT_SLOT_NAME } } @@ -195,7 +195,7 @@ impl From for AccountComponent { ]), )); - // Tracked procedure roots slot + // Trigger procedure roots slot // We add the map even if there are no trigger procedures, to always maintain the same // storage layout. let map_entries = falcon @@ -207,7 +207,7 @@ impl From for AccountComponent { // Safe to unwrap because we know that the map keys are unique. storage_slots.push(StorageSlot::with_map( - AuthRpoFalcon512Acl::tracked_procedure_roots_slot().clone(), + AuthRpoFalcon512Acl::trigger_procedure_roots_slot().clone(), StorageMap::with_entries(map_entries).unwrap(), )); @@ -296,7 +296,7 @@ mod tests { let proc_root = account .storage() .get_map_item( - AuthRpoFalcon512Acl::tracked_procedure_roots_slot(), + AuthRpoFalcon512Acl::trigger_procedure_roots_slot(), Word::from([i as u32, 0, 0, 0]), ) .expect("storage map access failed"); @@ -306,7 +306,7 @@ mod tests { // When no procedures, the map should return empty for key [0,0,0,0] let proc_root = account .storage() - .get_map_item(AuthRpoFalcon512Acl::tracked_procedure_roots_slot(), Word::empty()) + .get_map_item(AuthRpoFalcon512Acl::trigger_procedure_roots_slot(), Word::empty()) .expect("storage map access failed"); assert_eq!(proc_root, Word::empty()); } diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 6ce02e36f4..481f131155 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -239,9 +239,9 @@ impl TryFrom<&Account> for BasicFungibleFaucet { /// The storage layout of the faucet account is: /// - Slot 0: Reserved slot for faucets. /// - Slot 1: Public Key of the authentication component. -/// - Slot 2: [num_tracked_procs, allow_unauthorized_output_notes, allow_unauthorized_input_notes, +/// - Slot 2: [num_trigger_procs, allow_unauthorized_output_notes, allow_unauthorized_input_notes, /// 0]. -/// - Slot 3: A map with tracked procedure roots. +/// - Slot 3: A map with trigger procedure roots. /// - Slot 4: Token metadata of the faucet. pub fn create_basic_fungible_faucet( init_seed: [u8; 32], @@ -375,9 +375,9 @@ mod tests { ); // The config slot of the auth component stores: - // [num_tracked_procs, allow_unauthorized_output_notes, allow_unauthorized_input_notes, 0]. + // [num_trigger_procs, allow_unauthorized_output_notes, allow_unauthorized_input_notes, 0]. // - // With 1 tracked procedure (distribute), allow_unauthorized_output_notes=false, and + // With 1 trigger procedure (distribute), allow_unauthorized_output_notes=false, and // allow_unauthorized_input_notes=true, this should be [1, 0, 1, 0]. assert_eq!( faucet_account.storage().get_item(AuthRpoFalcon512Acl::config_slot()).unwrap(), @@ -390,7 +390,7 @@ mod tests { faucet_account .storage() .get_map_item( - AuthRpoFalcon512Acl::tracked_procedure_roots_slot(), + AuthRpoFalcon512Acl::trigger_procedure_roots_slot(), [Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO].into() ) .unwrap(), diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 28c3f27cf7..aeecc214a9 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -277,9 +277,9 @@ impl TryFrom<&Account> for NetworkFungibleFaucet { /// The storage layout of the network faucet account is: /// - Slot 0: Reserved slot for faucets. /// - Slot 1: Public Key of the authentication component. -/// - Slot 2: [num_tracked_procs, allow_unauthorized_output_notes, allow_unauthorized_input_notes, +/// - Slot 2: [num_trigger_procs, allow_unauthorized_output_notes, allow_unauthorized_input_notes, /// 0]. -/// - Slot 3: A map with tracked procedure roots. +/// - Slot 3: A map with trigger procedure roots. /// - Slot 4: Token metadata of the faucet. /// - Slot 5: Owner account ID. pub fn create_network_fungible_faucet( diff --git a/crates/miden-testing/tests/auth/ecdsa_acl.rs b/crates/miden-testing/tests/auth/ecdsa_acl.rs index b7247ca8be..0a5d1980ec 100644 --- a/crates/miden-testing/tests/auth/ecdsa_acl.rs +++ b/crates/miden-testing/tests/auth/ecdsa_acl.rs @@ -207,7 +207,7 @@ async fn test_ecdsa_acl_with_allow_unauthorized_output_notes() -> anyhow::Result .storage() .get_item(AuthEcdsaK256KeccakAcl::config_slot()) .expect("config storage slot access failed"); - // Config Slot should be [num_tracked_procs, allow_unauthorized_output_notes, + // Config Slot should be [num_trigger_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, // allow_unauthorized_output_notes=true, and allow_unauthorized_input_notes=true, this should be // [2, 1, 1, 0] @@ -247,7 +247,7 @@ async fn test_ecdsa_acl_with_disallow_unauthorized_input_notes() -> anyhow::Resu .storage() .get_item(AuthEcdsaK256KeccakAcl::config_slot()) .expect("config storage slot access failed"); - // Config Slot should be [num_tracked_procs, allow_unauthorized_output_notes, + // Config Slot should be [num_trigger_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, // allow_unauthorized_output_notes=true, and allow_unauthorized_input_notes=false, this should // be [2, 1, 0, 0] diff --git a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs index a793732901..791b33ffc4 100644 --- a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs +++ b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs @@ -205,7 +205,7 @@ async fn test_rpo_falcon_acl_with_allow_unauthorized_output_notes() -> anyhow::R .storage() .get_item(AuthRpoFalcon512Acl::config_slot()) .expect("config storage slot access failed"); - // Config Slot should be [num_tracked_procs, allow_unauthorized_output_notes, + // Config Slot should be [num_trigger_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, // allow_unauthorized_output_notes=true, and allow_unauthorized_input_notes=true, this should be // [2, 1, 1, 0] @@ -245,7 +245,7 @@ async fn test_rpo_falcon_acl_with_disallow_unauthorized_input_notes() -> anyhow: .storage() .get_item(AuthRpoFalcon512Acl::config_slot()) .expect("config storage slot access failed"); - // Config Slot should be [num_tracked_procs, allow_unauthorized_output_notes, + // Config Slot should be [num_trigger_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, // allow_unauthorized_output_notes=true, and allow_unauthorized_input_notes=false, this should // be [2, 1, 0, 0] diff --git a/docs/src/account/code.md b/docs/src/account/code.md index 2f5eedf9e5..2b22c67b06 100644 --- a/docs/src/account/code.md +++ b/docs/src/account/code.md @@ -35,6 +35,6 @@ Such an authentication procedure typically inspects the transaction and then dec Recall that an [account's nonce](index.md#nonce) must be incremented whenever its state changes. Only authentication procedures are allowed to do so, to prevent accidental or unintended authorization of state changes. -### Procedure tracking +### Procedure invocation checks -The authentication procedure can base its authentication decision on whether a specific account procedure was called during the transaction (procedure tracking). A procedure is considered "tracked" only if it invokes account-restricted kernel APIs (procedures that are only allowed to be called from the account context, e.g. `exec.faucet::mint`). Procedures that execute only local instructions (e.g., a noop `push.0 drop`) will not be marked as tracked. +The authentication procedure can base its authentication decision on whether a specific account procedure was called during the transaction. A procedure invocation is tracked by the kernel only if it invokes account-restricted kernel APIs (procedures that are only allowed to be called from the account context, e.g. `exec.faucet::mint`). Invocation of procedures that execute only local instructions (e.g., a noop `push.0 drop`) will not be tracked by the kernel. From 2af3be6f358e8ebf59dd26788e48fe0b3c64cc23 Mon Sep 17 00:00:00 2001 From: Varun Doshi Date: Sun, 14 Dec 2025 04:34:56 +0530 Subject: [PATCH 049/114] feat: Create `NullifierBlock` newtype wrapper (#2136) --- CHANGELOG.md | 1 + .../miden-objects/src/block/nullifier_tree.rs | 138 ++++++++++++------ .../src/block/partial_nullifier_tree.rs | 6 +- crates/miden-objects/src/errors.rs | 3 + 4 files changed, 98 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c543c335a8..c4b776c561 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)). - [BREAKING] Added public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)). - [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). +- Create `NullifierLeafValue` newtype wrapper ([#2136](https://github.com/0xMiden/miden-base/pull/2136)). - [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)). - Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)). - Added `TokenSymbol::from_static_str` const function for compile-time token symbol validation ([#2148](https://github.com/0xMiden/miden-base/pull/2148)). diff --git a/crates/miden-objects/src/block/nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree.rs index db63c76571..2cf5360153 100644 --- a/crates/miden-objects/src/block/nullifier_tree.rs +++ b/crates/miden-objects/src/block/nullifier_tree.rs @@ -2,37 +2,74 @@ use alloc::boxed::Box; use alloc::string::ToString; use alloc::vec::Vec; -use miden_core::EMPTY_WORD; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use miden_core::{EMPTY_WORD, Felt, FieldElement, Word}; #[cfg(feature = "std")] use miden_crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; use miden_crypto::merkle::{MerkleError, MutationSet, Smt, SmtProof}; use miden_processor::{DeserializationError, SMT_DEPTH}; -use crate::Word; use crate::block::{BlockNumber, NullifierWitness}; use crate::errors::NullifierTreeError; use crate::note::Nullifier; -// FREE HELPER FUNCTIONS AND CONSTANTS +// CONSTANTS // ================================================================================================ /// The value of an unspent nullifier in the tree. pub(super) const UNSPENT_NULLIFIER: Word = EMPTY_WORD; -/// Returns the nullifier's leaf value in the SMT by its block number. -pub(super) fn block_num_to_nullifier_leaf_value(block: BlockNumber) -> Word { - Word::from([block.as_u32(), 0, 0, 0]) +/// A nullifier block value in the [`NullifierTree`]. +/// +/// # Invariants +/// +/// The [`NullifierBlock`] guarantees the following: +/// - The felt at index 0 is a valid [`BlockNumber`]. +/// - The remaining felts are set to zero. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub struct NullifierBlock(BlockNumber); + +impl NullifierBlock { + pub const UNSPENT: NullifierBlock = NullifierBlock(BlockNumber::GENESIS); + + pub fn new(value: Word) -> Result { + if TryInto::::try_into(value[0].as_int()).is_ok() + && value[1..4].iter().all(|l| l.inner() == 0) + { + return Ok(Self(BlockNumber::from(value[0].as_int() as u32))); + } + Err(NullifierTreeError::InvalidNullifierBlockNumber) + } + + pub fn is_spent(&self) -> bool { + *self != Self::UNSPENT + } + + pub fn is_consumed(&self) -> bool { + self.0 != BlockNumber::GENESIS + } } -/// Given the leaf value of the nullifier SMT, returns the nullifier's block number. +impl From for NullifierBlock { + fn from(block_num: BlockNumber) -> Self { + Self(block_num) + } +} + +/// Returns the [`BlockNumber`] at which the nullifier was consumed. /// -/// There are no nullifiers in the genesis block. The value zero is instead used to signal -/// absence of a value. -pub(super) fn nullifier_leaf_value_to_block_num(value: Word) -> BlockNumber { - let block_num: u32 = value[0].as_int().try_into().expect("invalid block number found in store"); +/// There are no nullifiers in the genesis block, and the block number zero is used to signal +/// an unconsumed nullifier. +impl From for BlockNumber { + fn from(value: NullifierBlock) -> BlockNumber { + value.0 + } +} - block_num.into() +impl From for Word { + fn from(value: NullifierBlock) -> Word { + Word::from([Felt::from(value.0), Felt::ZERO, Felt::ZERO, Felt::ZERO]) + } } // NULLIFIER TREE BACKEND TRAIT @@ -48,8 +85,7 @@ pub(super) fn nullifier_leaf_value_to_block_num(value: Word) -> BlockNumber { /// /// Assumes the provided SMT upholds the guarantees of the [`NullifierTree`]. Specifically: /// - Nullifiers are only spent once and their block numbers do not change. -/// - Nullifier entries must follow the format `Word([block_num, 0, 0, 0])` with `block_num` a valid -/// block number. +/// - Nullifier leaf values must be valid according to [`NullifierBlock`]. pub trait NullifierTreeBackend: Sized { type Error: core::error::Error + Send + 'static; @@ -75,10 +111,10 @@ pub trait NullifierTreeBackend: Sized { ) -> Result, Self::Error>; /// Inserts a key-value pair into the SMT, returning the previous value at that key. - fn insert(&mut self, key: Word, value: Word) -> Result; + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result; /// Returns the value associated with the given key. - fn get_value(&self, key: &Word) -> Word; + fn get_value(&self, key: &Word) -> NullifierBlock; /// Returns the root of the SMT. fn root(&self) -> Word; @@ -113,12 +149,12 @@ impl NullifierTreeBackend for Smt { Smt::compute_mutations(self, updates) } - fn insert(&mut self, key: Word, value: Word) -> Result { - Smt::insert(self, key, value) + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { + Smt::insert(self, key, value.into()) } - fn get_value(&self, key: &Word) -> Word { - Smt::get_value(self, key) + fn get_value(&self, key: &Word) -> NullifierBlock { + NullifierBlock::new(Smt::get_value(self, key)).expect("unable to create NullifierBlock") } fn root(&self) -> Word { @@ -176,12 +212,13 @@ where LargeSmt::compute_mutations(self, updates).map_err(large_smt_error_to_merkle_error) } - fn insert(&mut self, key: Word, value: Word) -> Result { - LargeSmt::insert(self, key, value) + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { + LargeSmt::insert(self, key, value.into()) } - fn get_value(&self, key: &Word) -> Word { - LargeSmt::get_value(self, key) + fn get_value(&self, key: &Word) -> NullifierBlock { + NullifierBlock::new(LargeSmt::get_value(self, key)) + .expect("unable to create NullifierBlock") } fn root(&self) -> Word { @@ -256,8 +293,13 @@ where /// Returns an iterator over the nullifiers and their block numbers in the tree. pub fn entries(&self) -> impl Iterator { - self.smt.entries().map(|(nullifier, block_num)| { - (Nullifier::from_raw(nullifier), nullifier_leaf_value_to_block_num(block_num)) + self.smt.entries().map(|(nullifier, value)| { + ( + Nullifier::from_raw(nullifier), + NullifierBlock::new(value) + .expect("Invalid value to create NullifierBlock") + .into(), + ) }) } @@ -274,12 +316,12 @@ where /// Returns the block number for the given nullifier or `None` if the nullifier wasn't spent /// yet. pub fn get_block_num(&self, nullifier: &Nullifier) -> Option { - let value = self.smt.get_value(&nullifier.as_word()); - if value == Self::UNSPENT_NULLIFIER { + let nullifier_block = self.smt.get_value(&nullifier.as_word()); + if Self::UNSPENT_NULLIFIER == nullifier_block.into() { return None; } - Some(nullifier_leaf_value_to_block_num(value)) + Some(nullifier_block.into()) } /// Computes a mutation set resulting from inserting the provided nullifiers into this nullifier @@ -309,7 +351,7 @@ where nullifiers .into_iter() .map(|(nullifier, block_num)| { - (nullifier.as_word(), block_num_to_nullifier_leaf_value(block_num)) + (nullifier.as_word(), NullifierBlock::from(block_num).into()) }) .collect::>(), ) @@ -334,7 +376,7 @@ where ) -> Result<(), NullifierTreeError> { let prev_nullifier_value = self .smt - .insert(nullifier.as_word(), block_num_to_nullifier_leaf_value(block_num)) + .insert(nullifier.as_word(), NullifierBlock::from(block_num)) .map_err(NullifierTreeError::MaxLeafEntriesExceeded)?; if prev_nullifier_value != Self::UNSPENT_NULLIFIER { @@ -377,7 +419,7 @@ impl NullifierTree { entries: impl IntoIterator, ) -> Result { let leaves = entries.into_iter().map(|(nullifier, block_num)| { - (nullifier.as_word(), block_num_to_nullifier_leaf_value(block_num)) + (nullifier.as_word(), NullifierBlock::from(block_num).into()) }); let smt = Smt::with_entries(leaves) @@ -407,7 +449,7 @@ where entries: impl IntoIterator, ) -> Result { let leaves = entries.into_iter().map(|(nullifier, block_num)| { - (nullifier.as_word(), block_num_to_nullifier_leaf_value(block_num)) + (nullifier.as_word(), NullifierBlock::from(block_num).into()) }); let smt = LargeSmt::::with_entries(storage, leaves) @@ -485,29 +527,33 @@ mod tests { use super::NullifierTree; use crate::block::BlockNumber; + use crate::block::nullifier_tree::NullifierBlock; use crate::note::Nullifier; use crate::{NullifierTreeError, Word}; #[test] fn leaf_value_encode_decode() { let block_num = BlockNumber::from(0xffff_ffff_u32); - let leaf = super::block_num_to_nullifier_leaf_value(block_num); - let block_num_recovered = super::nullifier_leaf_value_to_block_num(leaf); + let nullifier_block = NullifierBlock::from(block_num); + let block_num_recovered = nullifier_block.into(); assert_eq!(block_num, block_num_recovered); } #[test] fn leaf_value_encoding() { - let block_num = 123; - let nullifier_value = super::block_num_to_nullifier_leaf_value(block_num.into()); - assert_eq!(nullifier_value, Word::from([block_num, 0, 0, 0u32])); + let block_num = BlockNumber::from(123); + let nullifier_value = NullifierBlock::from(block_num); + assert_eq!( + nullifier_value, + NullifierBlock::new(Word::from([block_num.as_u32(), 0, 0, 0u32])).unwrap() + ); } #[test] fn leaf_value_decoding() { let block_num = 123; - let nullifier_value = Word::from([block_num, 0, 0, 0u32]); - let decoded_block_num = super::nullifier_leaf_value_to_block_num(nullifier_value); + let nullifier_value = NullifierBlock::new(Word::from([block_num, 0, 0, 0u32])).unwrap(); + let decoded_block_num: BlockNumber = nullifier_value.into(); assert_eq!(decoded_block_num, block_num.into()); } @@ -580,8 +626,8 @@ mod tests { LargeSmt::with_entries( MemoryStorage::default(), [ - (nullifier1.as_word(), super::block_num_to_nullifier_leaf_value(block1)), - (nullifier2.as_word(), super::block_num_to_nullifier_leaf_value(block2)), + (nullifier1.as_word(), NullifierBlock::from(block1).into()), + (nullifier2.as_word(), NullifierBlock::from(block2).into()), ], ) .unwrap(), @@ -614,7 +660,7 @@ mod tests { let mut tree = NullifierTree::new_unchecked( LargeSmt::with_entries( MemoryStorage::default(), - [(nullifier1.as_word(), super::block_num_to_nullifier_leaf_value(block1))], + [(nullifier1.as_word(), NullifierBlock::from(block1).into())], ) .unwrap(), ); @@ -640,7 +686,7 @@ mod tests { let mut tree = LargeSmt::with_entries( MemoryStorage::default(), - [(nullifier1.as_word(), super::block_num_to_nullifier_leaf_value(block1))], + [(nullifier1.as_word(), NullifierBlock::from(block1).into())], ) .map(NullifierTree::new_unchecked) .unwrap(); @@ -671,8 +717,8 @@ mod tests { let large_tree = LargeSmt::with_entries( MemoryStorage::default(), [ - (nullifier1.as_word(), super::block_num_to_nullifier_leaf_value(block1)), - (nullifier2.as_word(), super::block_num_to_nullifier_leaf_value(block2)), + (nullifier1.as_word(), NullifierBlock::from(block1).into()), + (nullifier2.as_word(), NullifierBlock::from(block2).into()), ], ) .map(NullifierTree::new_unchecked) diff --git a/crates/miden-objects/src/block/partial_nullifier_tree.rs b/crates/miden-objects/src/block/partial_nullifier_tree.rs index c7b71b4878..8d02f691b1 100644 --- a/crates/miden-objects/src/block/partial_nullifier_tree.rs +++ b/crates/miden-objects/src/block/partial_nullifier_tree.rs @@ -1,3 +1,4 @@ +use super::nullifier_tree::NullifierBlock; use crate::Word; use crate::block::{BlockNumber, NullifierWitness, nullifier_tree}; use crate::crypto::merkle::PartialSmt; @@ -86,10 +87,7 @@ impl PartialNullifierTree { ) -> Result<(), NullifierTreeError> { let prev_nullifier_value = self .0 - .insert( - nullifier.as_word(), - nullifier_tree::block_num_to_nullifier_leaf_value(block_num), - ) + .insert(nullifier.as_word(), NullifierBlock::from(block_num).into()) .map_err(|source| NullifierTreeError::UntrackedNullifier { nullifier, source })?; if prev_nullifier_value != nullifier_tree::UNSPENT_NULLIFIER { diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index b6fc03a67c..f242519e82 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -1081,6 +1081,9 @@ pub enum NullifierTreeError { #[error("failed to compute nullifier tree mutations")] ComputeMutations(#[source] MerkleError), + + #[error("invalid nullifier block number")] + InvalidNullifierBlockNumber, } // AUTH SCHEME ERROR From 72d0f87dd56c8cf9bcd6b9c3a58ffa357f6a5975 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 13 Dec 2025 16:05:50 -0800 Subject: [PATCH 050/114] refactor: minor nullifier tree cleanup --- .../miden-objects/src/block/nullifier_tree.rs | 167 ++++++++++-------- .../src/block/partial_nullifier_tree.rs | 67 ++++--- .../miden-objects/src/block/proposed_block.rs | 6 +- crates/miden-objects/src/errors.rs | 2 +- 4 files changed, 136 insertions(+), 106 deletions(-) diff --git a/crates/miden-objects/src/block/nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree.rs index 2cf5360153..2a55132c91 100644 --- a/crates/miden-objects/src/block/nullifier_tree.rs +++ b/crates/miden-objects/src/block/nullifier_tree.rs @@ -3,7 +3,7 @@ use alloc::string::ToString; use alloc::vec::Vec; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_core::{EMPTY_WORD, Felt, FieldElement, Word}; +use miden_core::{Felt, FieldElement, Word}; #[cfg(feature = "std")] use miden_crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; use miden_crypto::merkle::{MerkleError, MutationSet, Smt, SmtProof}; @@ -13,65 +13,6 @@ use crate::block::{BlockNumber, NullifierWitness}; use crate::errors::NullifierTreeError; use crate::note::Nullifier; -// CONSTANTS -// ================================================================================================ - -/// The value of an unspent nullifier in the tree. -pub(super) const UNSPENT_NULLIFIER: Word = EMPTY_WORD; - -/// A nullifier block value in the [`NullifierTree`]. -/// -/// # Invariants -/// -/// The [`NullifierBlock`] guarantees the following: -/// - The felt at index 0 is a valid [`BlockNumber`]. -/// - The remaining felts are set to zero. -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub struct NullifierBlock(BlockNumber); - -impl NullifierBlock { - pub const UNSPENT: NullifierBlock = NullifierBlock(BlockNumber::GENESIS); - - pub fn new(value: Word) -> Result { - if TryInto::::try_into(value[0].as_int()).is_ok() - && value[1..4].iter().all(|l| l.inner() == 0) - { - return Ok(Self(BlockNumber::from(value[0].as_int() as u32))); - } - Err(NullifierTreeError::InvalidNullifierBlockNumber) - } - - pub fn is_spent(&self) -> bool { - *self != Self::UNSPENT - } - - pub fn is_consumed(&self) -> bool { - self.0 != BlockNumber::GENESIS - } -} - -impl From for NullifierBlock { - fn from(block_num: BlockNumber) -> Self { - Self(block_num) - } -} - -/// Returns the [`BlockNumber`] at which the nullifier was consumed. -/// -/// There are no nullifiers in the genesis block, and the block number zero is used to signal -/// an unconsumed nullifier. -impl From for BlockNumber { - fn from(value: NullifierBlock) -> BlockNumber { - value.0 - } -} - -impl From for Word { - fn from(value: NullifierBlock) -> Word { - Word::from([Felt::from(value.0), Felt::ZERO, Felt::ZERO, Felt::ZERO]) - } -} - // NULLIFIER TREE BACKEND TRAIT // ================================================================================================ @@ -111,7 +52,7 @@ pub trait NullifierTreeBackend: Sized { ) -> Result, Self::Error>; /// Inserts a key-value pair into the SMT, returning the previous value at that key. - fn insert(&mut self, key: Word, value: NullifierBlock) -> Result; + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result; /// Returns the value associated with the given key. fn get_value(&self, key: &Word) -> NullifierBlock; @@ -149,12 +90,15 @@ impl NullifierTreeBackend for Smt { Smt::compute_mutations(self, updates) } - fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { - Smt::insert(self, key, value.into()) + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { + Smt::insert(self, key, value.into()).map(|word| { + NullifierBlock::try_from(word).expect("SMT should only store valid NullifierBlocks") + }) } fn get_value(&self, key: &Word) -> NullifierBlock { - NullifierBlock::new(Smt::get_value(self, key)).expect("unable to create NullifierBlock") + NullifierBlock::new(Smt::get_value(self, key)) + .expect("SMT should only store valid NullifierBlocks") } fn root(&self) -> Word { @@ -212,12 +156,15 @@ where LargeSmt::compute_mutations(self, updates).map_err(large_smt_error_to_merkle_error) } - fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { - LargeSmt::insert(self, key, value.into()) + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { + LargeSmt::insert(self, key, value.into()).map(|word| { + NullifierBlock::try_from(word).expect("SMT should only store valid NullifierBlocks") + }) } fn get_value(&self, key: &Word) -> NullifierBlock { - NullifierBlock::new(LargeSmt::get_value(self, key)) + LargeSmt::get_value(self, key) + .try_into() .expect("unable to create NullifierBlock") } @@ -231,6 +178,9 @@ where } } +// NULLIFIER TREE +// ================================================================================================ + /// The sparse merkle tree of all nullifiers in the blockchain. /// /// A nullifier can only ever be spent once and its value in the tree is the block number at which @@ -263,9 +213,6 @@ where /// The depth of the nullifier tree. pub const DEPTH: u8 = SMT_DEPTH; - /// The value of an unspent nullifier in the tree. - pub const UNSPENT_NULLIFIER: Word = EMPTY_WORD; - // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -297,7 +244,7 @@ where ( Nullifier::from_raw(nullifier), NullifierBlock::new(value) - .expect("Invalid value to create NullifierBlock") + .expect("SMT should only store valid NullifierBlocks") .into(), ) }) @@ -317,7 +264,7 @@ where /// yet. pub fn get_block_num(&self, nullifier: &Nullifier) -> Option { let nullifier_block = self.smt.get_value(&nullifier.as_word()); - if Self::UNSPENT_NULLIFIER == nullifier_block.into() { + if nullifier_block.is_unspent() { return None; } @@ -379,7 +326,7 @@ where .insert(nullifier.as_word(), NullifierBlock::from(block_num)) .map_err(NullifierTreeError::MaxLeafEntriesExceeded)?; - if prev_nullifier_value != Self::UNSPENT_NULLIFIER { + if prev_nullifier_value.is_spent() { Err(NullifierTreeError::NullifierAlreadySpent(nullifier)) } else { Ok(()) @@ -518,6 +465,80 @@ impl NullifierMutationSet { } } +// NULLIFIER BLOCK +// ================================================================================================ + +/// The [`BlockNumber`] at which a [`Nullifier`] was consumed. +/// +/// Since there are no nullifiers in the genesis block the [`BlockNumber::GENESIS`] is used to +/// signal an unconsumed nullifier. +/// +/// This type can be converted to a [`Word`] which is laid out like this: +/// +/// ```text +/// [block_num, 0, 0, 0] +/// ``` +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub struct NullifierBlock(BlockNumber); + +impl NullifierBlock { + pub const UNSPENT: NullifierBlock = NullifierBlock(BlockNumber::GENESIS); + + /// Returns a new [NullifierBlock] constructed from the provided word. + /// + /// # Errors + /// Returns an error if: + /// - The 0th element in the word is not a valid [BlockNumber]. + /// - Any of the remaining elements is non-zero. + pub fn new(word: Word) -> Result { + let block_num = u32::try_from(word[0].as_int()) + .map(BlockNumber::from) + .map_err(|_| NullifierTreeError::InvalidNullifierBlockNumber(word))?; + + if word[1..4].iter().any(|felt| *felt != Felt::ZERO) { + return Err(NullifierTreeError::InvalidNullifierBlockNumber(word)); + } + + Ok(NullifierBlock(block_num)) + } + + /// Returns true if the nullifier has already been spent. + pub fn is_spent(&self) -> bool { + !self.is_unspent() + } + + /// Returns true if the nullifier has not yet been spent. + pub fn is_unspent(&self) -> bool { + self == &Self::UNSPENT + } +} + +impl From for NullifierBlock { + fn from(block_num: BlockNumber) -> Self { + Self(block_num) + } +} + +impl From for BlockNumber { + fn from(value: NullifierBlock) -> BlockNumber { + value.0 + } +} + +impl From for Word { + fn from(value: NullifierBlock) -> Word { + Word::from([Felt::from(value.0), Felt::ZERO, Felt::ZERO, Felt::ZERO]) + } +} + +impl TryFrom for NullifierBlock { + type Error = NullifierTreeError; + + fn try_from(value: Word) -> Result { + Self::new(value) + } +} + // TESTS // ================================================================================================ diff --git a/crates/miden-objects/src/block/partial_nullifier_tree.rs b/crates/miden-objects/src/block/partial_nullifier_tree.rs index 8d02f691b1..1ea2c6485d 100644 --- a/crates/miden-objects/src/block/partial_nullifier_tree.rs +++ b/crates/miden-objects/src/block/partial_nullifier_tree.rs @@ -1,10 +1,13 @@ use super::nullifier_tree::NullifierBlock; use crate::Word; -use crate::block::{BlockNumber, NullifierWitness, nullifier_tree}; +use crate::block::{BlockNumber, NullifierWitness}; use crate::crypto::merkle::PartialSmt; use crate::errors::NullifierTreeError; use crate::note::Nullifier; +// PARTIAL NULLIFIER TREE +// ================================================================================================ + /// The partial sparse merkle tree containing the nullifiers of consumed notes. /// /// A nullifier can only ever be spent once and its value in the tree is the block number at which @@ -37,8 +40,15 @@ impl PartialNullifierTree { .map(Self) } - /// Adds the given nullifier witness to the partial tree and tracks it. Once a nullifier has - /// been added to the tree, it can be marked as spent using [`Self::mark_spent`]. + /// Returns the root of the tree. + pub fn root(&self) -> Word { + self.0.root() + } + + /// Adds the given nullifier witness to the partial tree and tracks it. + /// + /// Once a nullifier has been added to the tree, it can be marked as spent using + /// [`Self::mark_spent`]. /// /// # Errors /// @@ -50,7 +60,7 @@ impl PartialNullifierTree { self.0.add_path(leaf, path).map_err(NullifierTreeError::TreeRootConflict) } - /// Marks the given nullifiers as spent at the given block number. + /// Marks the given nullifier as spent at the given block number. /// /// # Errors /// @@ -58,46 +68,45 @@ impl PartialNullifierTree { /// - a nullifier was already spent. /// - a nullifier is not tracked by this partial nullifier tree, that is, its /// [`NullifierWitness`] was not added to the tree previously. - pub fn mark_spent( + fn mark_spent( &mut self, - nullifiers: impl IntoIterator, + nullifier: Nullifier, block_num: BlockNumber, ) -> Result<(), NullifierTreeError> { - for nullifier in nullifiers { - self.mark_spent_single(nullifier, block_num)?; - } - - Ok(()) - } + let prev_nullifier_value: NullifierBlock = self + .0 + .insert(nullifier.as_word(), NullifierBlock::from(block_num).into()) + .map_err(|source| NullifierTreeError::UntrackedNullifier { nullifier, source })? + .try_into()?; - /// Returns the root of the tree. - pub fn root(&self) -> Word { - self.0.root() + if prev_nullifier_value.is_spent() { + Err(NullifierTreeError::NullifierAlreadySpent(nullifier)) + } else { + Ok(()) + } } - /// Marks the given nullifier as spent at the given block number. + /// Marks the given nullifiers as spent at the given block number. /// /// # Errors /// /// See [`Self::mark_spent`] for the possible error conditions. - fn mark_spent_single( + pub fn mark_spent_all( &mut self, - nullifier: Nullifier, + nullifiers: impl IntoIterator, block_num: BlockNumber, ) -> Result<(), NullifierTreeError> { - let prev_nullifier_value = self - .0 - .insert(nullifier.as_word(), NullifierBlock::from(block_num).into()) - .map_err(|source| NullifierTreeError::UntrackedNullifier { nullifier, source })?; - - if prev_nullifier_value != nullifier_tree::UNSPENT_NULLIFIER { - Err(NullifierTreeError::NullifierAlreadySpent(nullifier)) - } else { - Ok(()) + for nullifier in nullifiers { + self.mark_spent(nullifier, block_num)?; } + + Ok(()) } } +// TESTS +// ================================================================================================ + #[cfg(test)] mod tests { use assert_matches::assert_matches; @@ -153,7 +162,7 @@ mod tests { let mut partial_tree = PartialNullifierTree::with_witnesses([witness]).unwrap(); // Attempt to insert nullifier 1 again at a different block number. - let err = partial_tree.mark_spent([nullifier1], block2).unwrap_err(); + let err = partial_tree.mark_spent_all([nullifier1], block2).unwrap_err(); assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1); } @@ -182,7 +191,7 @@ mod tests { // Insert a new value into partial and full tree and assert the root is the same. tree.mark_spent(nullifier3, block3).unwrap(); - partial_tree.mark_spent([nullifier3], block3).unwrap(); + partial_tree.mark_spent(nullifier3, block3).unwrap(); assert_eq!(tree.root(), partial_tree.root()); } diff --git a/crates/miden-objects/src/block/proposed_block.rs b/crates/miden-objects/src/block/proposed_block.rs index 747247c848..3274e578a6 100644 --- a/crates/miden-objects/src/block/proposed_block.rs +++ b/crates/miden-objects/src/block/proposed_block.rs @@ -398,9 +398,9 @@ impl ProposedBlock { // SAFETY: As mentioned above, we can safely assume that each nullifier's witness was // added and every nullifier should be tracked by the partial tree and // therefore updatable. - partial_nullifier_tree.mark_spent(self.created_nullifiers.keys().copied(), self.block_num()).expect( - "nullifiers' merkle path should have been added to the partial tree and the nullifiers should be unspent", - ); + partial_nullifier_tree + .mark_spent_all(self.created_nullifiers.keys().copied(), self.block_num()) + .expect("nullifiers' merkle path should have been added to the partial tree and the nullifiers should be unspent"); Ok(partial_nullifier_tree.root()) } diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index f242519e82..2cf3fe9074 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -1083,7 +1083,7 @@ pub enum NullifierTreeError { ComputeMutations(#[source] MerkleError), #[error("invalid nullifier block number")] - InvalidNullifierBlockNumber, + InvalidNullifierBlockNumber(Word), } // AUTH SCHEME ERROR From c1385b3ecb9f3eba54230cb01856de6109fc2d9b Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 13 Dec 2025 16:30:11 -0800 Subject: [PATCH 051/114] refactor: consolidate nullifier tree modules --- .../miden-objects/src/block/block_inputs.rs | 3 +- crates/miden-objects/src/block/mod.rs | 6 - .../src/block/nullifier_tree/backend.rs | 239 ++++++++++++++++++ .../mod.rs} | 239 +----------------- .../{ => nullifier_tree}/nullifier_witness.rs | 0 .../partial_nullifier_tree.rs | 4 +- .../miden-objects/src/block/proposed_block.rs | 3 +- crates/miden-testing/src/mock_chain/chain.rs | 3 +- 8 files changed, 255 insertions(+), 242 deletions(-) create mode 100644 crates/miden-objects/src/block/nullifier_tree/backend.rs rename crates/miden-objects/src/block/{nullifier_tree.rs => nullifier_tree/mod.rs} (68%) rename crates/miden-objects/src/block/{ => nullifier_tree}/nullifier_witness.rs (100%) rename crates/miden-objects/src/block/{ => nullifier_tree}/partial_nullifier_tree.rs (98%) diff --git a/crates/miden-objects/src/block/block_inputs.rs b/crates/miden-objects/src/block/block_inputs.rs index 15049dfa80..bc7aa9d7df 100644 --- a/crates/miden-objects/src/block/block_inputs.rs +++ b/crates/miden-objects/src/block/block_inputs.rs @@ -3,7 +3,8 @@ use alloc::collections::BTreeMap; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; use crate::account::AccountId; -use crate::block::{AccountWitness, BlockHeader, NullifierWitness}; +use crate::block::nullifier_tree::NullifierWitness; +use crate::block::{AccountWitness, BlockHeader}; use crate::note::{NoteId, NoteInclusionProof, Nullifier}; use crate::transaction::PartialBlockchain; use crate::utils::serde::DeserializationError; diff --git a/crates/miden-objects/src/block/mod.rs b/crates/miden-objects/src/block/mod.rs index f8c8e551c8..27ff025714 100644 --- a/crates/miden-objects/src/block/mod.rs +++ b/crates/miden-objects/src/block/mod.rs @@ -16,9 +16,6 @@ pub use proposed_block::ProposedBlock; mod proven_block; pub use proven_block::ProvenBlock; -mod nullifier_witness; -pub use nullifier_witness::NullifierWitness; - mod partial_account_tree; pub use partial_account_tree::PartialAccountTree; @@ -29,9 +26,6 @@ pub mod nullifier_tree; mod blockchain; pub use blockchain::Blockchain; -mod partial_nullifier_tree; -pub use partial_nullifier_tree::PartialNullifierTree; - mod block_account_update; pub use block_account_update::BlockAccountUpdate; diff --git a/crates/miden-objects/src/block/nullifier_tree/backend.rs b/crates/miden-objects/src/block/nullifier_tree/backend.rs new file mode 100644 index 0000000000..8f7f037769 --- /dev/null +++ b/crates/miden-objects/src/block/nullifier_tree/backend.rs @@ -0,0 +1,239 @@ +use alloc::boxed::Box; + +use super::{BlockNumber, Nullifier, NullifierBlock, NullifierTree, NullifierTreeError}; +use crate::Word; +#[cfg(feature = "std")] +use crate::crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; +use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt, SmtProof}; + +// NULLIFIER TREE BACKEND +// ================================================================================================ + +/// This trait abstracts over different SMT backends (e.g., `Smt` and `LargeSmt`) to allow +/// the `NullifierTree` to work with either implementation transparently. +/// +/// Users should instantiate the backend directly (potentially with entries) and then +/// pass it to [`NullifierTree::new_unchecked`]. +/// +/// # Invariants +/// +/// Assumes the provided SMT upholds the guarantees of the [`NullifierTree`]. Specifically: +/// - Nullifiers are only spent once and their block numbers do not change. +/// - Nullifier leaf values must be valid according to [`NullifierBlock`]. +pub trait NullifierTreeBackend: Sized { + type Error: core::error::Error + Send + 'static; + + /// Returns the number of entries in the SMT. + fn num_entries(&self) -> usize; + + /// Returns all entries in the SMT as an iterator over key-value pairs. + fn entries(&self) -> Box + '_>; + + /// Opens the leaf at the given key, returning a Merkle proof. + fn open(&self, key: &Word) -> SmtProof; + + /// Applies the given mutation set to the SMT. + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error>; + + /// Computes the mutation set required to apply the given updates to the SMT. + fn compute_mutations( + &self, + updates: impl IntoIterator, + ) -> Result, Self::Error>; + + /// Inserts a key-value pair into the SMT, returning the previous value at that key. + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result; + + /// Returns the value associated with the given key. + fn get_value(&self, key: &Word) -> NullifierBlock; + + /// Returns the root of the SMT. + fn root(&self) -> Word; +} + +// BACKEND IMPLEMENTATION FOR SMT +// ================================================================================================ + +impl NullifierTreeBackend for Smt { + type Error = MerkleError; + + fn num_entries(&self) -> usize { + Smt::num_entries(self) + } + + fn entries(&self) -> Box + '_> { + Box::new(Smt::entries(self).map(|(k, v)| (*k, *v))) + } + + fn open(&self, key: &Word) -> SmtProof { + Smt::open(self, key) + } + + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error> { + Smt::apply_mutations(self, set) + } + + fn compute_mutations( + &self, + updates: impl IntoIterator, + ) -> Result, Self::Error> { + Smt::compute_mutations(self, updates) + } + + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { + Smt::insert(self, key, value.into()).map(|word| { + NullifierBlock::try_from(word).expect("SMT should only store valid NullifierBlocks") + }) + } + + fn get_value(&self, key: &Word) -> NullifierBlock { + NullifierBlock::new(Smt::get_value(self, key)) + .expect("SMT should only store valid NullifierBlocks") + } + + fn root(&self) -> Word { + Smt::root(self) + } +} + +// NULLIFIER TREE BACKEND FOR LARGE SMT +// ================================================================================================ + +#[cfg(feature = "std")] +impl NullifierTreeBackend for LargeSmt +where + Backend: SmtStorage, +{ + type Error = MerkleError; + + fn num_entries(&self) -> usize { + // SAFETY: We panic on storage errors here as they represent unrecoverable I/O failures. + // This maintains API compatibility with the non-fallible Smt::num_entries(). + // See issue #2010 for future improvements to error handling. + LargeSmt::num_entries(self) + .map_err(large_smt_error_to_merkle_error) + .expect("Storage I/O error accessing num_entries") + } + + fn entries(&self) -> Box + '_> { + // SAFETY: We expect here as only I/O errors can occur. Storage failures are considered + // unrecoverable at this layer. See issue #2010 for future error handling improvements. + Box::new(LargeSmt::entries(self).expect("Storage I/O error accessing entries")) + } + + fn open(&self, key: &Word) -> SmtProof { + LargeSmt::open(self, key) + } + + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error> { + LargeSmt::apply_mutations(self, set).map_err(large_smt_error_to_merkle_error) + } + + fn compute_mutations( + &self, + updates: impl IntoIterator, + ) -> Result, Self::Error> { + LargeSmt::compute_mutations(self, updates).map_err(large_smt_error_to_merkle_error) + } + + fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { + LargeSmt::insert(self, key, value.into()).map(|word| { + NullifierBlock::try_from(word).expect("SMT should only store valid NullifierBlocks") + }) + } + + fn get_value(&self, key: &Word) -> NullifierBlock { + LargeSmt::get_value(self, key) + .try_into() + .expect("unable to create NullifierBlock") + } + + fn root(&self) -> Word { + // SAFETY: We expect here as storage errors are considered unrecoverable. This maintains + // API compatibility with the non-fallible Smt::root(). + // See issue #2010 for future improvements to error handling. + LargeSmt::root(self) + .map_err(large_smt_error_to_merkle_error) + .expect("Storage I/O error accessing root") + } +} + +// CONVENIENCE METHODS +// ================================================================================================ + +impl NullifierTree { + /// Creates a new nullifier tree from the provided entries. + /// + /// This is a convenience method that creates an SMT backend with the provided entries and + /// wraps it in a NullifierTree. + /// + /// # Errors + /// + /// Returns an error if: + /// - the provided entries contain multiple block numbers for the same nullifier. + pub fn with_entries( + entries: impl IntoIterator, + ) -> Result { + let leaves = entries.into_iter().map(|(nullifier, block_num)| { + (nullifier.as_word(), NullifierBlock::from(block_num).into()) + }); + + let smt = Smt::with_entries(leaves) + .map_err(NullifierTreeError::DuplicateNullifierBlockNumbers)?; + + Ok(Self::new_unchecked(smt)) + } +} + +#[cfg(feature = "std")] +impl NullifierTree> +where + Backend: SmtStorage, +{ + /// Creates a new nullifier tree from the provided entries using the given storage backend + /// + /// This is a convenience method that creates an SMT on the provided storage backend using the + /// provided entries and wraps it in a NullifierTree. + /// + /// # Errors + /// + /// Returns an error if: + /// - the provided entries contain multiple block numbers for the same nullifier. + /// - a storage error is encountered. + pub fn with_storage_from_entries( + storage: Backend, + entries: impl IntoIterator, + ) -> Result { + let leaves = entries.into_iter().map(|(nullifier, block_num)| { + (nullifier.as_word(), NullifierBlock::from(block_num).into()) + }); + + let smt = LargeSmt::::with_entries(storage, leaves) + .map_err(large_smt_error_to_merkle_error) + .map_err(NullifierTreeError::DuplicateNullifierBlockNumbers)?; + + Ok(Self::new_unchecked(smt)) + } +} + +// HELPER FUNCTIONS +// ================================================================================================ + +#[cfg(feature = "std")] +pub(super) fn large_smt_error_to_merkle_error(err: LargeSmtError) -> MerkleError { + match err { + LargeSmtError::Storage(storage_err) => { + panic!("Storage error encountered: {:?}", storage_err) + }, + LargeSmtError::Merkle(merkle_err) => merkle_err, + } +} diff --git a/crates/miden-objects/src/block/nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree/mod.rs similarity index 68% rename from crates/miden-objects/src/block/nullifier_tree.rs rename to crates/miden-objects/src/block/nullifier_tree/mod.rs index 2a55132c91..be56f66f36 100644 --- a/crates/miden-objects/src/block/nullifier_tree.rs +++ b/crates/miden-objects/src/block/nullifier_tree/mod.rs @@ -1,182 +1,21 @@ -use alloc::boxed::Box; use alloc::string::ToString; use alloc::vec::Vec; -use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_core::{Felt, FieldElement, Word}; -#[cfg(feature = "std")] -use miden_crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; -use miden_crypto::merkle::{MerkleError, MutationSet, Smt, SmtProof}; -use miden_processor::{DeserializationError, SMT_DEPTH}; - -use crate::block::{BlockNumber, NullifierWitness}; +use crate::block::BlockNumber; +use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt}; use crate::errors::NullifierTreeError; use crate::note::Nullifier; +use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; +use crate::{Felt, FieldElement, Word}; -// NULLIFIER TREE BACKEND TRAIT -// ================================================================================================ - -/// This trait abstracts over different SMT backends (e.g., `Smt` and `LargeSmt`) to allow -/// the `NullifierTree` to work with either implementation transparently. -/// -/// Users should instantiate the backend directly (potentially with entries) and then -/// pass it to [`NullifierTree::new_unchecked`]. -/// -/// # Invariants -/// -/// Assumes the provided SMT upholds the guarantees of the [`NullifierTree`]. Specifically: -/// - Nullifiers are only spent once and their block numbers do not change. -/// - Nullifier leaf values must be valid according to [`NullifierBlock`]. -pub trait NullifierTreeBackend: Sized { - type Error: core::error::Error + Send + 'static; +mod backend; +pub use backend::NullifierTreeBackend; - /// Returns the number of entries in the SMT. - fn num_entries(&self) -> usize; +mod nullifier_witness; +pub use nullifier_witness::NullifierWitness; - /// Returns all entries in the SMT as an iterator over key-value pairs. - fn entries(&self) -> Box + '_>; - - /// Opens the leaf at the given key, returning a Merkle proof. - fn open(&self, key: &Word) -> SmtProof; - - /// Applies the given mutation set to the SMT. - fn apply_mutations( - &mut self, - set: MutationSet, - ) -> Result<(), Self::Error>; - - /// Computes the mutation set required to apply the given updates to the SMT. - fn compute_mutations( - &self, - updates: impl IntoIterator, - ) -> Result, Self::Error>; - - /// Inserts a key-value pair into the SMT, returning the previous value at that key. - fn insert(&mut self, key: Word, value: NullifierBlock) -> Result; - - /// Returns the value associated with the given key. - fn get_value(&self, key: &Word) -> NullifierBlock; - - /// Returns the root of the SMT. - fn root(&self) -> Word; -} - -impl NullifierTreeBackend for Smt { - type Error = MerkleError; - - fn num_entries(&self) -> usize { - Smt::num_entries(self) - } - - fn entries(&self) -> Box + '_> { - Box::new(Smt::entries(self).map(|(k, v)| (*k, *v))) - } - - fn open(&self, key: &Word) -> SmtProof { - Smt::open(self, key) - } - - fn apply_mutations( - &mut self, - set: MutationSet, - ) -> Result<(), Self::Error> { - Smt::apply_mutations(self, set) - } - - fn compute_mutations( - &self, - updates: impl IntoIterator, - ) -> Result, Self::Error> { - Smt::compute_mutations(self, updates) - } - - fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { - Smt::insert(self, key, value.into()).map(|word| { - NullifierBlock::try_from(word).expect("SMT should only store valid NullifierBlocks") - }) - } - - fn get_value(&self, key: &Word) -> NullifierBlock { - NullifierBlock::new(Smt::get_value(self, key)) - .expect("SMT should only store valid NullifierBlocks") - } - - fn root(&self) -> Word { - Smt::root(self) - } -} - -#[cfg(feature = "std")] -fn large_smt_error_to_merkle_error(err: LargeSmtError) -> MerkleError { - match err { - LargeSmtError::Storage(storage_err) => { - panic!("Storage error encountered: {:?}", storage_err) - }, - LargeSmtError::Merkle(merkle_err) => merkle_err, - } -} - -#[cfg(feature = "std")] -impl NullifierTreeBackend for LargeSmt -where - Backend: SmtStorage, -{ - type Error = MerkleError; - - fn num_entries(&self) -> usize { - // SAFETY: We panic on storage errors here as they represent unrecoverable I/O failures. - // This maintains API compatibility with the non-fallible Smt::num_entries(). - // See issue #2010 for future improvements to error handling. - LargeSmt::num_entries(self) - .map_err(large_smt_error_to_merkle_error) - .expect("Storage I/O error accessing num_entries") - } - - fn entries(&self) -> Box + '_> { - // SAFETY: We expect here as only I/O errors can occur. Storage failures are considered - // unrecoverable at this layer. See issue #2010 for future error handling improvements. - Box::new(LargeSmt::entries(self).expect("Storage I/O error accessing entries")) - } - - fn open(&self, key: &Word) -> SmtProof { - LargeSmt::open(self, key) - } - - fn apply_mutations( - &mut self, - set: MutationSet, - ) -> Result<(), Self::Error> { - LargeSmt::apply_mutations(self, set).map_err(large_smt_error_to_merkle_error) - } - - fn compute_mutations( - &self, - updates: impl IntoIterator, - ) -> Result, Self::Error> { - LargeSmt::compute_mutations(self, updates).map_err(large_smt_error_to_merkle_error) - } - - fn insert(&mut self, key: Word, value: NullifierBlock) -> Result { - LargeSmt::insert(self, key, value.into()).map(|word| { - NullifierBlock::try_from(word).expect("SMT should only store valid NullifierBlocks") - }) - } - - fn get_value(&self, key: &Word) -> NullifierBlock { - LargeSmt::get_value(self, key) - .try_into() - .expect("unable to create NullifierBlock") - } - - fn root(&self) -> Word { - // SAFETY: We expect here as storage errors are considered unrecoverable. This maintains - // API compatibility with the non-fallible Smt::root(). - // See issue #2010 for future improvements to error handling. - LargeSmt::root(self) - .map_err(large_smt_error_to_merkle_error) - .expect("Storage I/O error accessing root") - } -} +mod partial_nullifier_tree; +pub use partial_nullifier_tree::PartialNullifierTree; // NULLIFIER TREE // ================================================================================================ @@ -349,64 +188,6 @@ where } } -// CONVENIENCE METHODS -// ================================================================================================ - -impl NullifierTree { - /// Creates a new nullifier tree from the provided entries. - /// - /// This is a convenience method that creates an SMT backend with the provided entries and - /// wraps it in a NullifierTree. - /// - /// # Errors - /// - /// Returns an error if: - /// - the provided entries contain multiple block numbers for the same nullifier. - pub fn with_entries( - entries: impl IntoIterator, - ) -> Result { - let leaves = entries.into_iter().map(|(nullifier, block_num)| { - (nullifier.as_word(), NullifierBlock::from(block_num).into()) - }); - - let smt = Smt::with_entries(leaves) - .map_err(NullifierTreeError::DuplicateNullifierBlockNumbers)?; - - Ok(Self::new_unchecked(smt)) - } -} - -#[cfg(feature = "std")] -impl NullifierTree> -where - Backend: SmtStorage, -{ - /// Creates a new nullifier tree from the provided entries using the given storage backend - /// - /// This is a convenience method that creates an SMT on the provided storage backend using the - /// provided entries and wraps it in a NullifierTree. - /// - /// # Errors - /// - /// Returns an error if: - /// - the provided entries contain multiple block numbers for the same nullifier. - /// - a storage error is encountered. - pub fn with_storage_from_entries( - storage: Backend, - entries: impl IntoIterator, - ) -> Result { - let leaves = entries.into_iter().map(|(nullifier, block_num)| { - (nullifier.as_word(), NullifierBlock::from(block_num).into()) - }); - - let smt = LargeSmt::::with_entries(storage, leaves) - .map_err(large_smt_error_to_merkle_error) - .map_err(NullifierTreeError::DuplicateNullifierBlockNumbers)?; - - Ok(Self::new_unchecked(smt)) - } -} - // SERIALIZATION // ================================================================================================ diff --git a/crates/miden-objects/src/block/nullifier_witness.rs b/crates/miden-objects/src/block/nullifier_tree/nullifier_witness.rs similarity index 100% rename from crates/miden-objects/src/block/nullifier_witness.rs rename to crates/miden-objects/src/block/nullifier_tree/nullifier_witness.rs diff --git a/crates/miden-objects/src/block/partial_nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree/partial_nullifier_tree.rs similarity index 98% rename from crates/miden-objects/src/block/partial_nullifier_tree.rs rename to crates/miden-objects/src/block/nullifier_tree/partial_nullifier_tree.rs index 1ea2c6485d..ca1787d7c9 100644 --- a/crates/miden-objects/src/block/partial_nullifier_tree.rs +++ b/crates/miden-objects/src/block/nullifier_tree/partial_nullifier_tree.rs @@ -1,6 +1,6 @@ -use super::nullifier_tree::NullifierBlock; +use super::{NullifierBlock, NullifierWitness}; use crate::Word; -use crate::block::{BlockNumber, NullifierWitness}; +use crate::block::BlockNumber; use crate::crypto::merkle::PartialSmt; use crate::errors::NullifierTreeError; use crate::note::Nullifier; diff --git a/crates/miden-objects/src/block/proposed_block.rs b/crates/miden-objects/src/block/proposed_block.rs index 3274e578a6..9d5c859321 100644 --- a/crates/miden-objects/src/block/proposed_block.rs +++ b/crates/miden-objects/src/block/proposed_block.rs @@ -12,6 +12,7 @@ use crate::batch::{ ProvenBatch, }; use crate::block::block_inputs::BlockInputs; +use crate::block::nullifier_tree::{NullifierWitness, PartialNullifierTree}; use crate::block::{ AccountUpdateWitness, AccountWitness, @@ -19,10 +20,8 @@ use crate::block::{ BlockNoteIndex, BlockNoteTree, BlockNumber, - NullifierWitness, OutputNoteBatch, PartialAccountTree, - PartialNullifierTree, }; use crate::errors::ProposedBlockError; use crate::note::{NoteId, Nullifier}; diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index 21aaa14848..7dac4ef9aa 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -10,14 +10,13 @@ use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{Account, AccountId, PartialAccount}; use miden_objects::batch::{ProposedBatch, ProvenBatch}; use miden_objects::block::account_tree::AccountTree; -use miden_objects::block::nullifier_tree::NullifierTree; +use miden_objects::block::nullifier_tree::{NullifierTree, NullifierWitness}; use miden_objects::block::{ AccountWitness, BlockHeader, BlockInputs, BlockNumber, Blockchain, - NullifierWitness, ProposedBlock, ProvenBlock, }; From fa8c70126d2b81fcf0ce67c935d01b06035c0348 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 13 Dec 2025 16:32:05 -0800 Subject: [PATCH 052/114] chore: minor module renaming --- crates/miden-objects/src/block/nullifier_tree/mod.rs | 8 ++++---- .../{partial_nullifier_tree.rs => partial.rs} | 0 .../nullifier_tree/{nullifier_witness.rs => witness.rs} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename crates/miden-objects/src/block/nullifier_tree/{partial_nullifier_tree.rs => partial.rs} (100%) rename crates/miden-objects/src/block/nullifier_tree/{nullifier_witness.rs => witness.rs} (100%) diff --git a/crates/miden-objects/src/block/nullifier_tree/mod.rs b/crates/miden-objects/src/block/nullifier_tree/mod.rs index be56f66f36..ee8226a6b8 100644 --- a/crates/miden-objects/src/block/nullifier_tree/mod.rs +++ b/crates/miden-objects/src/block/nullifier_tree/mod.rs @@ -11,11 +11,11 @@ use crate::{Felt, FieldElement, Word}; mod backend; pub use backend::NullifierTreeBackend; -mod nullifier_witness; -pub use nullifier_witness::NullifierWitness; +mod witness; +pub use witness::NullifierWitness; -mod partial_nullifier_tree; -pub use partial_nullifier_tree::PartialNullifierTree; +mod partial; +pub use partial::PartialNullifierTree; // NULLIFIER TREE // ================================================================================================ diff --git a/crates/miden-objects/src/block/nullifier_tree/partial_nullifier_tree.rs b/crates/miden-objects/src/block/nullifier_tree/partial.rs similarity index 100% rename from crates/miden-objects/src/block/nullifier_tree/partial_nullifier_tree.rs rename to crates/miden-objects/src/block/nullifier_tree/partial.rs diff --git a/crates/miden-objects/src/block/nullifier_tree/nullifier_witness.rs b/crates/miden-objects/src/block/nullifier_tree/witness.rs similarity index 100% rename from crates/miden-objects/src/block/nullifier_tree/nullifier_witness.rs rename to crates/miden-objects/src/block/nullifier_tree/witness.rs From 63011a4c9eecc6d62ee1e027015aeada94ba46a2 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 13 Dec 2025 19:38:33 -0800 Subject: [PATCH 053/114] refactor: consolidate account tree modules --- crates/miden-lib/src/transaction/inputs.rs | 2 +- .../src/block/account_tree/backend.rs | 246 +++++++++++++++ .../{account_tree.rs => account_tree/mod.rs} | 280 ++---------------- .../partial.rs} | 3 +- .../witness.rs} | 0 .../src/block/account_update_witness.rs | 2 +- .../miden-objects/src/block/block_inputs.rs | 7 +- crates/miden-objects/src/block/mod.rs | 7 - .../src/block/nullifier_tree/partial.rs | 2 +- .../miden-objects/src/block/proposed_block.rs | 3 +- .../src/transaction/inputs/account.rs | 4 +- crates/miden-testing/src/mock_chain/chain.rs | 3 +- .../miden-testing/src/tx_context/builder.rs | 2 +- .../miden-testing/src/tx_context/context.rs | 3 +- 14 files changed, 281 insertions(+), 283 deletions(-) create mode 100644 crates/miden-objects/src/block/account_tree/backend.rs rename crates/miden-objects/src/block/{account_tree.rs => account_tree/mod.rs} (73%) rename crates/miden-objects/src/block/{partial_account_tree.rs => account_tree/partial.rs} (99%) rename crates/miden-objects/src/block/{account_witness.rs => account_tree/witness.rs} (100%) diff --git a/crates/miden-lib/src/transaction/inputs.rs b/crates/miden-lib/src/transaction/inputs.rs index ca1890e5f0..d49641c942 100644 --- a/crates/miden-lib/src/transaction/inputs.rs +++ b/crates/miden-lib/src/transaction/inputs.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use miden_objects::account::{AccountHeader, AccountId, PartialAccount}; use miden_objects::asset::AssetWitness; -use miden_objects::block::AccountWitness; +use miden_objects::block::account_tree::AccountWitness; use miden_objects::crypto::SequentialCommit; use miden_objects::crypto::merkle::{InnerNodeInfo, SmtProof}; use miden_objects::transaction::{AccountInputs, InputNote, PartialBlockchain, TransactionInputs}; diff --git a/crates/miden-objects/src/block/account_tree/backend.rs b/crates/miden-objects/src/block/account_tree/backend.rs new file mode 100644 index 0000000000..9679d255cf --- /dev/null +++ b/crates/miden-objects/src/block/account_tree/backend.rs @@ -0,0 +1,246 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; + +use super::{AccountId, AccountIdPrefix, AccountTree, AccountTreeError, account_id_to_smt_key}; +use crate::Word; +#[cfg(feature = "std")] +use crate::crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; +use crate::crypto::merkle::{ + LeafIndex, + MerkleError, + MutationSet, + SMT_DEPTH, + Smt, + SmtLeaf, + SmtProof, +}; + +// ACCOUNT TREE BACKEND +// ================================================================================================ + +/// This trait abstracts over different SMT backends (e.g., `Smt` and `LargeSmt`) to allow +/// the `AccountTree` to work with either implementation transparently. +/// +/// Implementors must provide `Default` for creating empty instances. Users should +/// instantiate the backend directly (potentially with entries) and then pass it to +/// [`AccountTree::new`]. +pub trait AccountTreeBackend: Sized { + type Error: core::error::Error + Send + 'static; + + /// Returns the number of leaves in the SMT. + fn num_leaves(&self) -> usize; + + /// Returns all leaves in the SMT as an iterator over leaf index and leaf pairs. + fn leaves<'a>(&'a self) -> Box, SmtLeaf)>>; + + /// Opens the leaf at the given key, returning a Merkle proof. + fn open(&self, key: &Word) -> SmtProof; + + /// Applies the given mutation set to the SMT. + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error>; + + /// Applies the given mutation set to the SMT and returns the reverse mutation set. + /// + /// The reverse mutation set can be used to revert the changes made by this operation. + fn apply_mutations_with_reversion( + &mut self, + set: MutationSet, + ) -> Result, Self::Error>; + + /// Computes the mutation set required to apply the given updates to the SMT. + fn compute_mutations( + &self, + updates: Vec<(Word, Word)>, + ) -> Result, Self::Error>; + + /// Inserts a key-value pair into the SMT, returning the previous value at that key. + fn insert(&mut self, key: Word, value: Word) -> Result; + + /// Returns the value associated with the given key. + fn get_value(&self, key: &Word) -> Word; + + /// Returns the leaf at the given key. + fn get_leaf(&self, key: &Word) -> SmtLeaf; + + /// Returns the root of the SMT. + fn root(&self) -> Word; +} + +// BACKEND IMPLEMENTATION FOR SMT +// ================================================================================================ + +impl AccountTreeBackend for Smt { + type Error = MerkleError; + + fn num_leaves(&self) -> usize { + Smt::num_leaves(self) + } + + fn leaves<'a>(&'a self) -> Box, SmtLeaf)>> { + Box::new(Smt::leaves(self).map(|(idx, leaf)| (idx, leaf.clone()))) + } + + fn open(&self, key: &Word) -> SmtProof { + Smt::open(self, key) + } + + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error> { + Smt::apply_mutations(self, set) + } + + fn apply_mutations_with_reversion( + &mut self, + set: MutationSet, + ) -> Result, Self::Error> { + Smt::apply_mutations_with_reversion(self, set) + } + + fn compute_mutations( + &self, + updates: Vec<(Word, Word)>, + ) -> Result, Self::Error> { + Smt::compute_mutations(self, updates) + } + + fn insert(&mut self, key: Word, value: Word) -> Result { + Smt::insert(self, key, value) + } + + fn get_value(&self, key: &Word) -> Word { + Smt::get_value(self, key) + } + + fn get_leaf(&self, key: &Word) -> SmtLeaf { + Smt::get_leaf(self, key) + } + + fn root(&self) -> Word { + Smt::root(self) + } +} + +// BACKEND IMPLEMENTATION FOR LARGE SMT +// ================================================================================================ + +#[cfg(feature = "std")] +impl AccountTreeBackend for LargeSmt +where + Backend: SmtStorage, +{ + type Error = MerkleError; + + fn num_leaves(&self) -> usize { + // LargeSmt::num_leaves returns Result + // We'll unwrap or return 0 on error + LargeSmt::num_leaves(self).map_err(large_smt_error_to_merkle_error).unwrap_or(0) + } + + fn leaves<'a>(&'a self) -> Box, SmtLeaf)>> { + Box::new(LargeSmt::leaves(self).expect("Only IO can error out here")) + } + + fn open(&self, key: &Word) -> SmtProof { + LargeSmt::open(self, key) + } + + fn apply_mutations( + &mut self, + set: MutationSet, + ) -> Result<(), Self::Error> { + LargeSmt::apply_mutations(self, set).map_err(large_smt_error_to_merkle_error) + } + + fn apply_mutations_with_reversion( + &mut self, + set: MutationSet, + ) -> Result, Self::Error> { + LargeSmt::apply_mutations_with_reversion(self, set).map_err(large_smt_error_to_merkle_error) + } + + fn compute_mutations( + &self, + updates: Vec<(Word, Word)>, + ) -> Result, Self::Error> { + LargeSmt::compute_mutations(self, updates).map_err(large_smt_error_to_merkle_error) + } + + fn insert(&mut self, key: Word, value: Word) -> Result { + LargeSmt::insert(self, key, value) + } + + fn get_value(&self, key: &Word) -> Word { + LargeSmt::get_value(self, key) + } + + fn get_leaf(&self, key: &Word) -> SmtLeaf { + LargeSmt::get_leaf(self, key) + } + + fn root(&self) -> Word { + LargeSmt::root(self).map_err(large_smt_error_to_merkle_error).unwrap() + } +} + +// CONVENIENCE METHODS +// ================================================================================================ + +impl AccountTree { + /// Creates a new [`AccountTree`] with the provided entries. + /// + /// This is a convenience method for testing that creates an SMT backend with the provided + /// entries and wraps it in an AccountTree. It validates that the entries don't contain + /// duplicate prefixes. + /// + /// # Errors + /// + /// Returns an error if: + /// - The provided entries contain duplicate account ID prefixes + /// - The backend fails to create the SMT with the entries + pub fn with_entries( + entries: impl IntoIterator, + ) -> Result + where + I: ExactSizeIterator, + { + // Create the SMT with the entries + let smt = Smt::with_entries( + entries + .into_iter() + .map(|(id, commitment)| (account_id_to_smt_key(id), commitment)), + ) + .map_err(|err| { + let MerkleError::DuplicateValuesForIndex(leaf_idx) = err else { + unreachable!("the only error returned by Smt::with_entries is of this type"); + }; + + // SAFETY: Since we only inserted account IDs into the SMT, it is guaranteed that + // the leaf_idx is a valid Felt as well as a valid account ID prefix. + AccountTreeError::DuplicateStateCommitments { + prefix: AccountIdPrefix::new_unchecked( + crate::Felt::try_from(leaf_idx).expect("leaf index should be a valid felt"), + ), + } + })?; + + AccountTree::new(smt) + } +} + +// HELPER FUNCTIONS +// ================================================================================================ + +#[cfg(feature = "std")] +fn large_smt_error_to_merkle_error(err: LargeSmtError) -> MerkleError { + match err { + LargeSmtError::Storage(storage_err) => { + panic!("Storage error encountered: {:?}", storage_err) + }, + LargeSmtError::Merkle(merkle_err) => merkle_err, + } +} diff --git a/crates/miden-objects/src/block/account_tree.rs b/crates/miden-objects/src/block/account_tree/mod.rs similarity index 73% rename from crates/miden-objects/src/block/account_tree.rs rename to crates/miden-objects/src/block/account_tree/mod.rs index 0aa6655f9c..ed3585d58a 100644 --- a/crates/miden-objects/src/block/account_tree.rs +++ b/crates/miden-objects/src/block/account_tree/mod.rs @@ -1,15 +1,20 @@ -use alloc::boxed::Box; use alloc::string::ToString; use alloc::vec::Vec; -use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_crypto::merkle::{LeafIndex, MerkleError, MutationSet, Smt, SmtLeaf, SmtProof}; -use miden_processor::{DeserializationError, SMT_DEPTH}; - use crate::Word; use crate::account::{AccountId, AccountIdPrefix}; -use crate::block::AccountWitness; +use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt, SmtLeaf}; use crate::errors::AccountTreeError; +use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; + +mod partial; +pub use partial::PartialAccountTree; + +mod witness; +pub use witness::AccountWitness; + +mod backend; +pub use backend::AccountTreeBackend; // FREE HELPER FUNCTIONS // ================================================================================================ @@ -33,191 +38,16 @@ pub fn account_id_to_smt_key(account_id: AccountId) -> Word { /// /// # Panics /// -/// Panics if the key does not represent a valid account ID. This should never happen -/// when used with keys from account trees, as the tree only stores valid IDs. +/// Panics if the key does not represent a valid account ID. This should never happen when used +/// with keys from account trees, as the tree only stores valid IDs. pub fn smt_key_to_account_id(key: Word) -> AccountId { AccountId::try_from([key[KEY_PREFIX_IDX], key[KEY_SUFFIX_IDX]]) .expect("account tree should only contain valid IDs") } -// ACCOUNT TREE BACKEND TRAIT +// ACCOUNT TREE // ================================================================================================ -/// This trait abstracts over different SMT backends (e.g., `Smt` and `LargeSmt`) to allow -/// the `AccountTree` to work with either implementation transparently. -/// -/// Implementors must provide `Default` for creating empty instances. Users should -/// instantiate the backend directly (potentially with entries) and then pass it to -/// [`AccountTree::new`]. -pub trait AccountTreeBackend: Sized { - type Error: core::error::Error + Send + 'static; - - /// Returns the number of leaves in the SMT. - fn num_leaves(&self) -> usize; - - /// Returns all leaves in the SMT as an iterator over leaf index and leaf pairs. - fn leaves<'a>(&'a self) -> Box, SmtLeaf)>>; - - /// Opens the leaf at the given key, returning a Merkle proof. - fn open(&self, key: &Word) -> SmtProof; - - /// Applies the given mutation set to the SMT. - fn apply_mutations( - &mut self, - set: MutationSet, - ) -> Result<(), Self::Error>; - - /// Applies the given mutation set to the SMT and returns the reverse mutation set. - /// - /// The reverse mutation set can be used to revert the changes made by this operation. - fn apply_mutations_with_reversion( - &mut self, - set: MutationSet, - ) -> Result, Self::Error>; - - /// Computes the mutation set required to apply the given updates to the SMT. - fn compute_mutations( - &self, - updates: Vec<(Word, Word)>, - ) -> Result, Self::Error>; - - /// Inserts a key-value pair into the SMT, returning the previous value at that key. - fn insert(&mut self, key: Word, value: Word) -> Result; - - /// Returns the value associated with the given key. - fn get_value(&self, key: &Word) -> Word; - - /// Returns the leaf at the given key. - fn get_leaf(&self, key: &Word) -> SmtLeaf; - - /// Returns the root of the SMT. - fn root(&self) -> Word; -} - -impl AccountTreeBackend for Smt { - type Error = MerkleError; - - fn num_leaves(&self) -> usize { - Smt::num_leaves(self) - } - - fn leaves<'a>(&'a self) -> Box, SmtLeaf)>> { - Box::new(Smt::leaves(self).map(|(idx, leaf)| (idx, leaf.clone()))) - } - - fn open(&self, key: &Word) -> SmtProof { - Smt::open(self, key) - } - - fn apply_mutations( - &mut self, - set: MutationSet, - ) -> Result<(), Self::Error> { - Smt::apply_mutations(self, set) - } - - fn apply_mutations_with_reversion( - &mut self, - set: MutationSet, - ) -> Result, Self::Error> { - Smt::apply_mutations_with_reversion(self, set) - } - - fn compute_mutations( - &self, - updates: Vec<(Word, Word)>, - ) -> Result, Self::Error> { - Smt::compute_mutations(self, updates) - } - - fn insert(&mut self, key: Word, value: Word) -> Result { - Smt::insert(self, key, value) - } - - fn get_value(&self, key: &Word) -> Word { - Smt::get_value(self, key) - } - - fn get_leaf(&self, key: &Word) -> SmtLeaf { - Smt::get_leaf(self, key) - } - - fn root(&self) -> Word { - Smt::root(self) - } -} - -#[cfg(feature = "std")] -use miden_crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; -#[cfg(feature = "std")] -fn large_smt_error_to_merkle_error(err: LargeSmtError) -> MerkleError { - match err { - LargeSmtError::Storage(storage_err) => { - panic!("Storage error encountered: {:?}", storage_err) - }, - LargeSmtError::Merkle(merkle_err) => merkle_err, - } -} - -#[cfg(feature = "std")] -impl AccountTreeBackend for LargeSmt -where - Backend: SmtStorage, -{ - type Error = MerkleError; - - fn num_leaves(&self) -> usize { - // LargeSmt::num_leaves returns Result - // We'll unwrap or return 0 on error - LargeSmt::num_leaves(self).map_err(large_smt_error_to_merkle_error).unwrap_or(0) - } - - fn leaves<'a>(&'a self) -> Box, SmtLeaf)>> { - Box::new(LargeSmt::leaves(self).expect("Only IO can error out here")) - } - - fn open(&self, key: &Word) -> SmtProof { - LargeSmt::open(self, key) - } - - fn apply_mutations( - &mut self, - set: MutationSet, - ) -> Result<(), Self::Error> { - LargeSmt::apply_mutations(self, set).map_err(large_smt_error_to_merkle_error) - } - - fn apply_mutations_with_reversion( - &mut self, - set: MutationSet, - ) -> Result, Self::Error> { - LargeSmt::apply_mutations_with_reversion(self, set).map_err(large_smt_error_to_merkle_error) - } - - fn compute_mutations( - &self, - updates: Vec<(Word, Word)>, - ) -> Result, Self::Error> { - LargeSmt::compute_mutations(self, updates).map_err(large_smt_error_to_merkle_error) - } - - fn insert(&mut self, key: Word, value: Word) -> Result { - LargeSmt::insert(self, key, value) - } - - fn get_value(&self, key: &Word) -> Word { - LargeSmt::get_value(self, key) - } - - fn get_leaf(&self, key: &Word) -> SmtLeaf { - LargeSmt::get_leaf(self, key) - } - - fn root(&self) -> Word { - LargeSmt::root(self).map_err(large_smt_error_to_merkle_error).unwrap() - } -} - /// The sparse merkle tree of all accounts in the blockchain. /// /// The key is the [`AccountId`] while the value is the current state commitment of the account, @@ -276,13 +106,13 @@ where }, SmtLeaf::Single((key, _)) => { // Single entry is good - verify it's a valid account ID - Self::smt_key_to_id(key); + smt_key_to_account_id(key); }, SmtLeaf::Multiple(entries) => { // Multiple entries means duplicate prefixes // Extract one of the keys to identify the duplicate prefix if let Some((key, _)) = entries.first() { - let account_id = Self::smt_key_to_id(*key); + let account_id = smt_key_to_account_id(*key); return Err(AccountTreeError::DuplicateIdPrefix { duplicate_prefix: account_id.prefix(), }); @@ -320,7 +150,7 @@ where /// /// Panics if the SMT backend fails to open the leaf (only possible with [`LargeSmt`] backend). pub fn open(&self, account_id: AccountId) -> AccountWitness { - let key = Self::id_to_smt_key(account_id); + let key = account_id_to_smt_key(account_id); let proof = self.smt.open(&key); AccountWitness::from_smt_proof(account_id, proof) @@ -328,7 +158,7 @@ where /// Returns the current state commitment of the given account ID. pub fn get(&self, account_id: AccountId) -> Word { - let key = Self::id_to_smt_key(account_id); + let key = account_id_to_smt_key(account_id); self.smt.get_value(&key) } @@ -396,7 +226,7 @@ where .compute_mutations(Vec::from_iter( account_commitments .into_iter() - .map(|(id, commitment)| (Self::id_to_smt_key(id), commitment)), + .map(|(id, commitment)| (account_id_to_smt_key(id), commitment)), )) .map_err(AccountTreeError::ComputeMutations)?; @@ -410,7 +240,7 @@ where // valid. If it does not match, then we would insert a duplicate. if existing_key != *id_key { return Err(AccountTreeError::DuplicateIdPrefix { - duplicate_prefix: Self::smt_key_to_id(*id_key).prefix(), + duplicate_prefix: smt_key_to_account_id(*id_key).prefix(), }); } }, @@ -443,7 +273,7 @@ where account_id: AccountId, state_commitment: Word, ) -> Result { - let key = Self::id_to_smt_key(account_id); + let key = account_id_to_smt_key(account_id); // SAFETY: account tree should not contain multi-entry leaves and so the maximum number // of entries per leaf should never be exceeded. let prev_value = self.smt.insert(key, state_commitment) @@ -498,17 +328,6 @@ where // HELPERS // -------------------------------------------------------------------------------------------- - /// Returns the SMT key of the given account ID. - pub(super) fn id_to_smt_key(account_id: AccountId) -> Word { - // We construct this in such a way that we're forced to use the constants, so that when - // they're updated, the other usages of the constants are also updated. - let mut key = Word::empty(); - key[Self::KEY_SUFFIX_IDX] = account_id.suffix(); - key[Self::KEY_PREFIX_IDX] = account_id.prefix().as_felt(); - - key - } - /// Returns the SMT key of the given account ID prefix. fn id_prefix_to_smt_key(account_id: AccountIdPrefix) -> Word { // We construct this in such a way that we're forced to use the constants, so that when @@ -518,63 +337,6 @@ where key } - - /// Returns the [`AccountId`] recovered from the given SMT key. - /// - /// # Panics - /// - /// Panics if: - /// - the key is not a valid account ID. This should not happen when used on keys from (partial) - /// account tree. - pub(super) fn smt_key_to_id(key: Word) -> AccountId { - AccountId::try_from([key[Self::KEY_PREFIX_IDX], key[Self::KEY_SUFFIX_IDX]]) - .expect("account tree should only contain valid IDs") - } -} - -// CONVENIENCE METHODS -// ================================================================================================ - -impl AccountTree { - /// Creates a new [`AccountTree`] with the provided entries. - /// - /// This is a convenience method for testing that creates an SMT backend with the provided - /// entries and wraps it in an AccountTree. It validates that the entries don't contain - /// duplicate prefixes. - /// - /// # Errors - /// - /// Returns an error if: - /// - The provided entries contain duplicate account ID prefixes - /// - The backend fails to create the SMT with the entries - pub fn with_entries( - entries: impl IntoIterator, - ) -> Result - where - I: ExactSizeIterator, - { - // Create the SMT with the entries - let smt = Smt::with_entries( - entries - .into_iter() - .map(|(id, commitment)| (account_id_to_smt_key(id), commitment)), - ) - .map_err(|err| { - let MerkleError::DuplicateValuesForIndex(leaf_idx) = err else { - unreachable!("the only error returned by Smt::with_entries is of this type"); - }; - - // SAFETY: Since we only inserted account IDs into the SMT, it is guaranteed that - // the leaf_idx is a valid Felt as well as a valid account ID prefix. - AccountTreeError::DuplicateStateCommitments { - prefix: AccountIdPrefix::new_unchecked( - crate::Felt::try_from(leaf_idx).expect("leaf index should be a valid felt"), - ), - } - })?; - - AccountTree::new(smt) - } } // SERIALIZATION diff --git a/crates/miden-objects/src/block/partial_account_tree.rs b/crates/miden-objects/src/block/account_tree/partial.rs similarity index 99% rename from crates/miden-objects/src/block/partial_account_tree.rs rename to crates/miden-objects/src/block/account_tree/partial.rs index 34cfd20c01..f92b8918f0 100644 --- a/crates/miden-objects/src/block/partial_account_tree.rs +++ b/crates/miden-objects/src/block/account_tree/partial.rs @@ -1,9 +1,8 @@ use miden_crypto::merkle::SmtLeaf; +use super::{AccountWitness, account_id_to_smt_key}; use crate::Word; use crate::account::AccountId; -use crate::block::AccountWitness; -use crate::block::account_tree::account_id_to_smt_key; use crate::crypto::merkle::PartialSmt; use crate::errors::AccountTreeError; diff --git a/crates/miden-objects/src/block/account_witness.rs b/crates/miden-objects/src/block/account_tree/witness.rs similarity index 100% rename from crates/miden-objects/src/block/account_witness.rs rename to crates/miden-objects/src/block/account_tree/witness.rs diff --git a/crates/miden-objects/src/block/account_update_witness.rs b/crates/miden-objects/src/block/account_update_witness.rs index 9d8c9a485c..359784d3b7 100644 --- a/crates/miden-objects/src/block/account_update_witness.rs +++ b/crates/miden-objects/src/block/account_update_witness.rs @@ -1,6 +1,6 @@ use crate::Word; use crate::account::delta::AccountUpdateDetails; -use crate::block::AccountWitness; +use crate::block::account_tree::AccountWitness; use crate::utils::serde::{ ByteReader, ByteWriter, diff --git a/crates/miden-objects/src/block/block_inputs.rs b/crates/miden-objects/src/block/block_inputs.rs index bc7aa9d7df..e67f4e0bba 100644 --- a/crates/miden-objects/src/block/block_inputs.rs +++ b/crates/miden-objects/src/block/block_inputs.rs @@ -1,13 +1,12 @@ use alloc::collections::BTreeMap; -use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; - use crate::account::AccountId; +use crate::block::BlockHeader; +use crate::block::account_tree::AccountWitness; use crate::block::nullifier_tree::NullifierWitness; -use crate::block::{AccountWitness, BlockHeader}; use crate::note::{NoteId, NoteInclusionProof, Nullifier}; use crate::transaction::PartialBlockchain; -use crate::utils::serde::DeserializationError; +use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; // BLOCK INPUTS // ================================================================================================ diff --git a/crates/miden-objects/src/block/mod.rs b/crates/miden-objects/src/block/mod.rs index 27ff025714..e829aacdb6 100644 --- a/crates/miden-objects/src/block/mod.rs +++ b/crates/miden-objects/src/block/mod.rs @@ -16,11 +16,7 @@ pub use proposed_block::ProposedBlock; mod proven_block; pub use proven_block::ProvenBlock; -mod partial_account_tree; -pub use partial_account_tree::PartialAccountTree; - pub mod account_tree; - pub mod nullifier_tree; mod blockchain; @@ -29,9 +25,6 @@ pub use blockchain::Blockchain; mod block_account_update; pub use block_account_update::BlockAccountUpdate; -mod account_witness; -pub use account_witness::AccountWitness; - mod account_update_witness; pub use account_update_witness::AccountUpdateWitness; diff --git a/crates/miden-objects/src/block/nullifier_tree/partial.rs b/crates/miden-objects/src/block/nullifier_tree/partial.rs index ca1787d7c9..e745096e90 100644 --- a/crates/miden-objects/src/block/nullifier_tree/partial.rs +++ b/crates/miden-objects/src/block/nullifier_tree/partial.rs @@ -68,7 +68,7 @@ impl PartialNullifierTree { /// - a nullifier was already spent. /// - a nullifier is not tracked by this partial nullifier tree, that is, its /// [`NullifierWitness`] was not added to the tree previously. - fn mark_spent( + pub fn mark_spent( &mut self, nullifier: Nullifier, block_num: BlockNumber, diff --git a/crates/miden-objects/src/block/proposed_block.rs b/crates/miden-objects/src/block/proposed_block.rs index 9d5c859321..7e4b6dbc62 100644 --- a/crates/miden-objects/src/block/proposed_block.rs +++ b/crates/miden-objects/src/block/proposed_block.rs @@ -11,17 +11,16 @@ use crate::batch::{ OrderedBatches, ProvenBatch, }; +use crate::block::account_tree::{AccountWitness, PartialAccountTree}; use crate::block::block_inputs::BlockInputs; use crate::block::nullifier_tree::{NullifierWitness, PartialNullifierTree}; use crate::block::{ AccountUpdateWitness, - AccountWitness, BlockHeader, BlockNoteIndex, BlockNoteTree, BlockNumber, OutputNoteBatch, - PartialAccountTree, }; use crate::errors::ProposedBlockError; use crate::note::{NoteId, Nullifier}; diff --git a/crates/miden-objects/src/transaction/inputs/account.rs b/crates/miden-objects/src/transaction/inputs/account.rs index 4d7dd2faf9..848f33ef64 100644 --- a/crates/miden-objects/src/transaction/inputs/account.rs +++ b/crates/miden-objects/src/transaction/inputs/account.rs @@ -1,7 +1,7 @@ use crate::Word; use crate::account::{AccountCode, AccountId, PartialAccount, PartialStorage}; use crate::asset::PartialVault; -use crate::block::AccountWitness; +use crate::block::account_tree::AccountWitness; use crate::crypto::merkle::{SmtProof, SmtProofError}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -103,7 +103,7 @@ mod tests { use crate::account::{Account, AccountCode, AccountId, AccountStorage, PartialAccount}; use crate::asset::AssetVault; - use crate::block::AccountWitness; + use crate::block::account_tree::AccountWitness; use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE; use crate::transaction::AccountInputs; diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index 7dac4ef9aa..badceba12d 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -9,10 +9,9 @@ use miden_objects::account::auth::{AuthSecretKey, PublicKey}; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{Account, AccountId, PartialAccount}; use miden_objects::batch::{ProposedBatch, ProvenBatch}; -use miden_objects::block::account_tree::AccountTree; +use miden_objects::block::account_tree::{AccountTree, AccountWitness}; use miden_objects::block::nullifier_tree::{NullifierTree, NullifierWitness}; use miden_objects::block::{ - AccountWitness, BlockHeader, BlockInputs, BlockNumber, diff --git a/crates/miden-testing/src/tx_context/builder.rs b/crates/miden-testing/src/tx_context/builder.rs index 8e524171f9..ba6e20f708 100644 --- a/crates/miden-testing/src/tx_context/builder.rs +++ b/crates/miden-testing/src/tx_context/builder.rs @@ -13,7 +13,7 @@ use miden_objects::account::auth::{PublicKeyCommitment, Signature}; use miden_objects::account::{Account, AccountHeader, AccountId}; use miden_objects::assembly::DefaultSourceManager; use miden_objects::assembly::debuginfo::SourceManagerSync; -use miden_objects::block::AccountWitness; +use miden_objects::block::account_tree::AccountWitness; use miden_objects::note::{Note, NoteId, NoteScript}; use miden_objects::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; use miden_objects::testing::noop_auth_component::NoopAuthComponent; diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index 012b096e41..4f2db63216 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -15,7 +15,8 @@ use miden_objects::account::{ use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; use miden_objects::assembly::{Assembler, SourceManager, SourceManagerSync}; use miden_objects::asset::{Asset, AssetVaultKey, AssetWitness}; -use miden_objects::block::{AccountWitness, BlockHeader, BlockNumber}; +use miden_objects::block::account_tree::AccountWitness; +use miden_objects::block::{BlockHeader, BlockNumber}; use miden_objects::note::{Note, NoteScript}; use miden_objects::transaction::{ AccountInputs, From 7af940ed51b5d86b3f37d6fa1dea49fc408775fb Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 13 Dec 2025 19:48:31 -0800 Subject: [PATCH 054/114] chore: fix lints --- crates/miden-objects/src/block/account_tree/mod.rs | 2 +- crates/miden-objects/src/block/account_tree/witness.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/miden-objects/src/block/account_tree/mod.rs b/crates/miden-objects/src/block/account_tree/mod.rs index ed3585d58a..38efc26e82 100644 --- a/crates/miden-objects/src/block/account_tree/mod.rs +++ b/crates/miden-objects/src/block/account_tree/mod.rs @@ -148,7 +148,7 @@ where /// /// # Panics /// - /// Panics if the SMT backend fails to open the leaf (only possible with [`LargeSmt`] backend). + /// Panics if the SMT backend fails to open the leaf (only possible with `LargeSmt` backend). pub fn open(&self, account_id: AccountId) -> AccountWitness { let key = account_id_to_smt_key(account_id); let proof = self.smt.open(&key); diff --git a/crates/miden-objects/src/block/account_tree/witness.rs b/crates/miden-objects/src/block/account_tree/witness.rs index ca654f90cb..16ca22f451 100644 --- a/crates/miden-objects/src/block/account_tree/witness.rs +++ b/crates/miden-objects/src/block/account_tree/witness.rs @@ -18,11 +18,10 @@ use crate::{AccountTreeError, Word}; // ACCOUNT WITNESS // ================================================================================================ -/// A specialized version of an [`SmtProof`] for use in -/// [`AccountTree`](super::account_tree::AccountTree) and -/// [`PartialAccountTree`](crate::block::PartialAccountTree). It proves the inclusion of an account +/// A specialized version of an [`SmtProof`] for use in [`AccountTree`](super::AccountTree) and +/// [`PartialAccountTree`](super::PartialAccountTree). It proves the inclusion of an account /// ID at a certain state (i.e. [`Account::commitment`](crate::account::Account::commitment)) in the -/// [`AccountTree`](super::account_tree::AccountTree). +/// [`AccountTree`](super::AccountTree). /// /// By construction the witness can only represent the equivalent of an [`SmtLeaf`] with zero or one /// entries, which guarantees that the account ID prefix it represents is unique in the tree. From 43eb33365f5e1198b2fa9ad81a59aec37406739c Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sun, 14 Dec 2025 01:03:04 -0800 Subject: [PATCH 055/114] chore: fix `TransactionAuthenticator::get_signature()` docs --- crates/miden-tx/src/auth/tx_authenticator.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/miden-tx/src/auth/tx_authenticator.rs b/crates/miden-tx/src/auth/tx_authenticator.rs index 74fc46bd77..854ea34801 100644 --- a/crates/miden-tx/src/auth/tx_authenticator.rs +++ b/crates/miden-tx/src/auth/tx_authenticator.rs @@ -121,19 +121,20 @@ impl Deserializable for SigningInputs { /// a key managed by the authenticator. That is, the authenticator maintains a set of public- /// private key pairs, and can be requested to generate signatures against any of the managed keys. /// -/// The public keys are defined by [Word]'s which are the hashes of the actual public keys. +/// The public keys are defined by [PublicKeyCommitment]'s which are the hashes of the actual +/// public keys. pub trait TransactionAuthenticator { /// Retrieves a signature for a specific message as a list of [Felt]. /// /// The request is initiated by the VM as a consequence of the SigToStack advice /// injector. /// - /// - `pub_key_hash`: The hash of the public key used for signature generation. - /// - `message`: The message to sign, usually a commitment to the transaction data. - /// - `account_delta`: An informational parameter describing the changes made to the account up - /// to the point of calling `get_signature()`. This allows the authenticator to review any - /// alterations to the account prior to signing. It should not be directly used in the - /// signature computation. + /// - `pub_key_commitment`: the hash of the public key used for signature generation. + /// - `signing_inputs`: description of the message to be singed. The inputs could contain + /// arbitrary data or a [TransactionSummary] which would describe the changes made to the + /// account up to the point of calling `get_signature()`. This allows the authenticator to + /// review any alterations to the account prior to signing. It should not be directly used + /// in the signature computation. fn get_signature( &self, pub_key_commitment: PublicKeyCommitment, @@ -219,10 +220,6 @@ impl TransactionAuthenticator for BasicAuthenticator { /// /// The key should be included in the `keys` map and should be a variant of [AuthSecretKey]. /// - /// Supported signature schemes: - /// - RpoFalcon512 - /// - EcdsaK256Keccak - /// /// # Errors /// If the public key is not contained in the `keys` map, /// [`AuthenticationError::UnknownPublicKey`] is returned. From f33480f1a8ee50ad3d278a78d2b22cedbdf0e2c3 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sun, 14 Dec 2025 01:05:57 -0800 Subject: [PATCH 056/114] chore: fix lints --- crates/miden-tx/src/auth/tx_authenticator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/miden-tx/src/auth/tx_authenticator.rs b/crates/miden-tx/src/auth/tx_authenticator.rs index 854ea34801..86a9febb70 100644 --- a/crates/miden-tx/src/auth/tx_authenticator.rs +++ b/crates/miden-tx/src/auth/tx_authenticator.rs @@ -133,8 +133,8 @@ pub trait TransactionAuthenticator { /// - `signing_inputs`: description of the message to be singed. The inputs could contain /// arbitrary data or a [TransactionSummary] which would describe the changes made to the /// account up to the point of calling `get_signature()`. This allows the authenticator to - /// review any alterations to the account prior to signing. It should not be directly used - /// in the signature computation. + /// review any alterations to the account prior to signing. It should not be directly used in + /// the signature computation. fn get_signature( &self, pub_key_commitment: PublicKeyCommitment, From 4cd21c694e79305ce7187a8426a18582e574fa8e Mon Sep 17 00:00:00 2001 From: igamigo Date: Sun, 14 Dec 2025 21:38:06 -0300 Subject: [PATCH 057/114] test: simplify codebuilder creation (#2175) --- crates/miden-lib/src/utils/code_builder.rs | 4 +-- .../src/kernel_tests/tx/test_fpi.rs | 32 +++++++++---------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/crates/miden-lib/src/utils/code_builder.rs b/crates/miden-lib/src/utils/code_builder.rs index 9e245850f1..2e58cded31 100644 --- a/crates/miden-lib/src/utils/code_builder.rs +++ b/crates/miden-lib/src/utils/code_builder.rs @@ -46,9 +46,9 @@ use crate::transaction::TransactionKernel; /// 1. Create a new CodeBuilder with debug mode preference /// 2. Add any required modules using `link_module()` or `with_linked_module()` /// 3. Add libraries using `link_static_library()` / `link_dynamic_library()` as appropriate -/// 4. Parse your script with `parse_note_script()` or `parse_tx_script()` +/// 4. Compile your script with `compile_note_script()` or `compile_tx_script()` /// -/// Note that the Compiling methods consume the CodeBuilder, so if you need to parse +/// Note that the compiling methods consume the CodeBuilder, so if you need to compile /// multiple scripts with the same configuration, you should clone the builder first. /// /// ## Builder Pattern Example diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 010e5d9874..81416446ec 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -82,7 +82,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let source_manager = Arc::new(DefaultSourceManager::default()); let foreign_account_component = AccountComponent::new( - CodeBuilder::with_kernel_library(source_manager.clone()) + CodeBuilder::with_source_manager(source_manager.clone()) .compile_component_code("test::foreign_account", foreign_account_code_source)?, vec![mock_value_slot0.clone(), mock_map_slot.clone()], )? @@ -343,14 +343,14 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { "; let foreign_account_component_1 = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("test::foreign_account_1", foreign_account_code_source_1)?, vec![mock_value_slot0.clone()], )? .with_supports_all_types(); let foreign_account_component_2 = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("test::foreign_account_2", foreign_account_code_source_2)?, vec![mock_value_slot1.clone()], )? @@ -1326,10 +1326,9 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { mock_value_slot0 = mock_value_slot0.name(), ); - let last_foreign_account_code = - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) - .compile_component_code("test::last_foreign_account", last_foreign_account_code_source) - .unwrap(); + let last_foreign_account_code = CodeBuilder::default() + .compile_component_code("test::last_foreign_account", last_foreign_account_code_source) + .unwrap(); let last_foreign_account_component = AccountComponent::new(last_foreign_account_code, vec![mock_value_slot0.clone()]) .unwrap() @@ -1374,13 +1373,12 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { next_foreign_prefix = next_account.id().prefix().as_felt(), ); - let foreign_account_code = - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) - .compile_component_code( - format!("test::foreign_account_chain_{foreign_account_index}"), - foreign_account_code_source, - ) - .unwrap(); + let foreign_account_code = CodeBuilder::default() + .compile_component_code( + format!("test::foreign_account_chain_{foreign_account_index}"), + foreign_account_code_source, + ) + .unwrap(); let foreign_account_component = AccountComponent::new(foreign_account_code, vec![]) .unwrap() .with_supports_all_types(); @@ -1493,7 +1491,7 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { "; let foreign_account_component = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("foreign_account", foreign_account_code_source)?, vec![], )? @@ -1592,7 +1590,7 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { let mock_value_slot0 = AccountStorage::mock_value_slot0(); let foreign_account_component = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("foreign_account_invalid", foreign_account_code_source)?, vec![mock_value_slot0.clone()], )? @@ -1700,7 +1698,7 @@ async fn test_fpi_get_account_id() -> anyhow::Result<()> { "; let foreign_account_component = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("foreign_account", foreign_account_code_source)?, Vec::new(), )? From b7036d209170461618c7f23a4da6b130fe3da247 Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:59:55 +1300 Subject: [PATCH 058/114] feat: Add From<&ExecutedTransaction> for TransactionHeader impl (#2178) --- CHANGELOG.md | 1 + .../src/transaction/inputs/notes.rs | 7 ++ .../src/transaction/tx_header.rs | 92 +++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4b776c561..337d256511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). - [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). +- Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). ### Changes diff --git a/crates/miden-objects/src/transaction/inputs/notes.rs b/crates/miden-objects/src/transaction/inputs/notes.rs index 35663c12ca..72cad6931f 100644 --- a/crates/miden-objects/src/transaction/inputs/notes.rs +++ b/crates/miden-objects/src/transaction/inputs/notes.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use super::TransactionInputError; use crate::note::{Note, NoteId, NoteInclusionProof, NoteLocation, Nullifier}; +use crate::transaction::InputNoteCommitment; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -145,6 +146,12 @@ impl InputNotes { Self::new(input_note_vec) } + + /// Returns a vector of input note commitments based on the input notes. + pub fn to_commitments(&self) -> InputNotes { + let notes = self.notes.iter().map(InputNoteCommitment::from).collect(); + InputNotes::::new_unchecked(notes) + } } impl IntoIterator for InputNotes { diff --git a/crates/miden-objects/src/transaction/tx_header.rs b/crates/miden-objects/src/transaction/tx_header.rs index 2b4b0cef1f..9a86a9f0b9 100644 --- a/crates/miden-objects/src/transaction/tx_header.rs +++ b/crates/miden-objects/src/transaction/tx_header.rs @@ -7,6 +7,7 @@ use crate::asset::FungibleAsset; use crate::note::NoteHeader; use crate::transaction::{ AccountId, + ExecutedTransaction, InputNoteCommitment, InputNotes, OutputNotes, @@ -171,6 +172,21 @@ impl From<&ProvenTransaction> for TransactionHeader { } } +impl From<&ExecutedTransaction> for TransactionHeader { + /// Constructs a [`TransactionHeader`] from a [`ExecutedTransaction`]. + fn from(tx: &ExecutedTransaction) -> Self { + TransactionHeader::new_unchecked( + tx.id(), + tx.account_id(), + tx.initial_account().initial_commitment(), + tx.final_account().commitment(), + tx.input_notes().to_commitments(), + tx.output_notes().iter().map(NoteHeader::from).collect(), + tx.fee(), + ) + } +} + // SERIALIZATION // ================================================================================================ @@ -216,3 +232,79 @@ impl Deserializable for TransactionHeader { Ok(tx_header) } } + +#[cfg(test)] +mod tests { + use miden_verifier::ExecutionProof; + + use super::*; + use crate::asset::FungibleAsset; + use crate::transaction::ProvenTransactionBuilder; + + #[test] + fn from_proven_transaction() { + // Build proven transaction. + let account_id = crate::account::AccountId::dummy( + [1; 15], + crate::account::AccountIdVersion::Version0, + crate::account::AccountType::FungibleFaucet, + crate::account::AccountStorageMode::Private, + ); + let initial_commitment = + [2; 32].try_into().expect("failed to create initial account commitment"); + let final_commitment = + [3; 32].try_into().expect("failed to create final account commitment"); + let account_delta_commitment = + [4; 32].try_into().expect("failed to create account delta commitment"); + let block_num = crate::block::BlockNumber::from(1); + let block_ref = Word::empty(); + let fee = FungibleAsset::mock(42).unwrap_fungible(); + let expiration_block_num = crate::block::BlockNumber::from(2); + let proof = ExecutionProof::new_dummy(); + + let proven_tx = ProvenTransactionBuilder::new( + account_id, + initial_commitment, + final_commitment, + account_delta_commitment, + block_num, + block_ref, + fee, + expiration_block_num, + proof, + ) + .build() + .unwrap(); + + // Create header from proven transaction. + let header = TransactionHeader::from(&proven_tx); + + // Verify the header data matches the proven transaction. + assert_eq!(header.id(), proven_tx.id()); + assert_eq!(header.account_id(), proven_tx.account_id()); + assert_eq!( + header.initial_state_commitment(), + proven_tx.account_update().initial_state_commitment() + ); + assert_eq!( + header.final_state_commitment(), + proven_tx.account_update().final_state_commitment() + ); + assert_eq!(header.fee(), proven_tx.fee()); + + assert_eq!(header.input_notes().iter().count(), proven_tx.input_notes().iter().count()); + + for (header_note, tx_note) in + header.input_notes().iter().zip(proven_tx.input_notes().iter()) + { + assert_eq!(header_note, tx_note); + } + + assert_eq!(header.output_notes().len(), proven_tx.output_notes().iter().count()); + for (header_note, tx_note) in + header.output_notes().iter().zip(proven_tx.output_notes().iter()) + { + assert_eq!(*header_note, crate::note::NoteHeader::from(tx_note)); + } + } +} From ead1b8b19b0edda69ef2ed27ef1b460b8018e4ad Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 15 Dec 2025 13:08:39 +0700 Subject: [PATCH 059/114] chore: update protocol library docs (#2179) --- docs/src/protocol_library.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index aa49c87157..d17f09ed68 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -40,10 +40,10 @@ Active account procedures can be used to read from storage, fetch or compute com | `get_code_commitment` | Gets the account code commitment of the active account.

**Inputs:** `[]`
**Outputs:** `[CODE_COMMITMENT]` | Account | | `get_initial_storage_commitment` | Returns the storage commitment of the active account at the beginning of the transaction.

**Inputs:** `[]`
**Outputs:** `[INIT_STORAGE_COMMITMENT]` | Any | | `compute_storage_commitment` | Computes the latest account storage commitment of the active account.

**Inputs:** `[]`
**Outputs:** `[STORAGE_COMMITMENT]` | Account | -| `get_item` | Gets an item from the account storage.

**Inputs:** `[index]`
**Outputs:** `[VALUE]` | Account | -| `get_initial_item` | Gets the initial item from the account storage slot as it was at the beginning of the transaction.

**Inputs:** `[index]`
**Outputs:** `[VALUE]` | Account | -| `get_map_item` | Returns the VALUE located under the specified KEY within the map contained in the given account storage slot.

**Inputs:** `[index, KEY]`
**Outputs:** `[VALUE]` | Account | -| `get_initial_map_item` | Gets the initial VALUE from the account storage map as it was at the beginning of the transaction.

**Inputs:** `[index, KEY]`
**Outputs:** `[VALUE]` | Account | +| `get_item` | Gets an item from the account storage.

**Inputs:** `[slot_id_prefix, slot_id_suffix]`
**Outputs:** `[VALUE]` | Account | +| `get_initial_item` | Gets the initial item from the account storage slot as it was at the beginning of the transaction.

**Inputs:** `[slot_id_prefix, slot_id_suffix]`
**Outputs:** `[VALUE]` | Account | +| `get_map_item` | Returns the VALUE located under the specified KEY within the map contained in the given account storage slot.

**Inputs:** `[slot_id_prefix, slot_id_suffix, KEY]`
**Outputs:** `[VALUE]` | Account | +| `get_initial_map_item` | Gets the initial VALUE from the account storage map as it was at the beginning of the transaction.

**Inputs:** `[slot_id_prefix, slot_id_suffix, KEY]`
**Outputs:** `[VALUE]` | Account | | `get_balance` | Returns the balance of the fungible asset associated with the provided faucet_id in the active account's vault.

**Inputs:** `[faucet_id_prefix, faucet_id_suffix]`
**Outputs:** `[balance]` | Any | | `get_initial_balance` | Returns the balance of the fungible asset associated with the provided faucet_id in the active account's vault at the beginning of the transaction.

**Inputs:** `[faucet_id_prefix, faucet_id_suffix]`
**Outputs:** `[init_balance]` | Any | | `has_non_fungible_asset` | Returns a boolean indicating whether the non-fungible asset is present in the active account's vault.

**Inputs:** `[ASSET]`
**Outputs:** `[has_asset]` | Any | @@ -62,8 +62,8 @@ Native account procedures can be used to write to storage, add or remove assets | `get_id` | Returns the ID of the native account of the transaction.

**Inputs:** `[]`
**Outputs:** `[account_id_prefix, account_id_suffix]` | Any | | `incr_nonce` | Increments the nonce of the native account by one and returns the new nonce. Can only be called from auth procedures.

**Inputs:** `[]`
**Outputs:** `[final_nonce]` | Auth | | `compute_delta_commitment` | Computes the commitment to the native account's delta. Can only be called from auth procedures.

**Inputs:** `[]`
**Outputs:** `[DELTA_COMMITMENT]` | Auth | -| `set_item` | Sets an item in the native account storage.

**Inputs:** `[index, VALUE]`
**Outputs:** `[OLD_VALUE]` | Native & Account | -| `set_map_item` | Sets VALUE under the specified KEY within the map contained in the given native account storage slot.

**Inputs:** `[index, KEY, VALUE]`
**Outputs:** `[OLD_MAP_ROOT, OLD_MAP_VALUE]` | Native & Account | +| `set_item` | Sets an item in the native account storage.

**Inputs:** `[slot_id_prefix, slot_id_suffix, VALUE]`
**Outputs:** `[OLD_VALUE]` | Native & Account | +| `set_map_item` | Sets VALUE under the specified KEY within the map contained in the given native account storage slot.

**Inputs:** `[slot_id_prefix, slot_id_suffix, KEY, VALUE]`
**Outputs:** `[OLD_MAP_ROOT, OLD_MAP_VALUE]` | Native & Account | | `add_asset` | Adds the specified asset to the vault. For fungible assets, returns the total after addition.

**Inputs:** `[ASSET]`
**Outputs:** `[ASSET']` | Native & Account | | `remove_asset` | Removes the specified asset from the vault.

**Inputs:** `[ASSET]`
**Outputs:** `[ASSET]` | Native & Account | | `was_procedure_called` | Returns 1 if a native account procedure was called during transaction execution, and 0 otherwise.

**Inputs:** `[PROC_ROOT]`
**Outputs:** `[was_called]` | Any | From 26f47039c1384d265c96f1abdfa65de88323b977 Mon Sep 17 00:00:00 2001 From: igamigo Date: Sun, 14 Dec 2025 21:38:06 -0300 Subject: [PATCH 060/114] test: simplify codebuilder creation (#2175) --- crates/miden-lib/src/utils/code_builder.rs | 4 +-- .../src/kernel_tests/tx/test_fpi.rs | 32 +++++++++---------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/crates/miden-lib/src/utils/code_builder.rs b/crates/miden-lib/src/utils/code_builder.rs index 9e245850f1..2e58cded31 100644 --- a/crates/miden-lib/src/utils/code_builder.rs +++ b/crates/miden-lib/src/utils/code_builder.rs @@ -46,9 +46,9 @@ use crate::transaction::TransactionKernel; /// 1. Create a new CodeBuilder with debug mode preference /// 2. Add any required modules using `link_module()` or `with_linked_module()` /// 3. Add libraries using `link_static_library()` / `link_dynamic_library()` as appropriate -/// 4. Parse your script with `parse_note_script()` or `parse_tx_script()` +/// 4. Compile your script with `compile_note_script()` or `compile_tx_script()` /// -/// Note that the Compiling methods consume the CodeBuilder, so if you need to parse +/// Note that the compiling methods consume the CodeBuilder, so if you need to compile /// multiple scripts with the same configuration, you should clone the builder first. /// /// ## Builder Pattern Example diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 010e5d9874..81416446ec 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -82,7 +82,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let source_manager = Arc::new(DefaultSourceManager::default()); let foreign_account_component = AccountComponent::new( - CodeBuilder::with_kernel_library(source_manager.clone()) + CodeBuilder::with_source_manager(source_manager.clone()) .compile_component_code("test::foreign_account", foreign_account_code_source)?, vec![mock_value_slot0.clone(), mock_map_slot.clone()], )? @@ -343,14 +343,14 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { "; let foreign_account_component_1 = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("test::foreign_account_1", foreign_account_code_source_1)?, vec![mock_value_slot0.clone()], )? .with_supports_all_types(); let foreign_account_component_2 = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("test::foreign_account_2", foreign_account_code_source_2)?, vec![mock_value_slot1.clone()], )? @@ -1326,10 +1326,9 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { mock_value_slot0 = mock_value_slot0.name(), ); - let last_foreign_account_code = - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) - .compile_component_code("test::last_foreign_account", last_foreign_account_code_source) - .unwrap(); + let last_foreign_account_code = CodeBuilder::default() + .compile_component_code("test::last_foreign_account", last_foreign_account_code_source) + .unwrap(); let last_foreign_account_component = AccountComponent::new(last_foreign_account_code, vec![mock_value_slot0.clone()]) .unwrap() @@ -1374,13 +1373,12 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { next_foreign_prefix = next_account.id().prefix().as_felt(), ); - let foreign_account_code = - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) - .compile_component_code( - format!("test::foreign_account_chain_{foreign_account_index}"), - foreign_account_code_source, - ) - .unwrap(); + let foreign_account_code = CodeBuilder::default() + .compile_component_code( + format!("test::foreign_account_chain_{foreign_account_index}"), + foreign_account_code_source, + ) + .unwrap(); let foreign_account_component = AccountComponent::new(foreign_account_code, vec![]) .unwrap() .with_supports_all_types(); @@ -1493,7 +1491,7 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { "; let foreign_account_component = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("foreign_account", foreign_account_code_source)?, vec![], )? @@ -1592,7 +1590,7 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { let mock_value_slot0 = AccountStorage::mock_value_slot0(); let foreign_account_component = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("foreign_account_invalid", foreign_account_code_source)?, vec![mock_value_slot0.clone()], )? @@ -1700,7 +1698,7 @@ async fn test_fpi_get_account_id() -> anyhow::Result<()> { "; let foreign_account_component = AccountComponent::new( - CodeBuilder::with_kernel_library(Arc::new(DefaultSourceManager::default())) + CodeBuilder::default() .compile_component_code("foreign_account", foreign_account_code_source)?, Vec::new(), )? From 5a795e0fced6df00eec0169b51811383d24fd2f9 Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:59:55 +1300 Subject: [PATCH 061/114] feat: Add From<&ExecutedTransaction> for TransactionHeader impl (#2178) --- CHANGELOG.md | 1 + .../src/transaction/inputs/notes.rs | 7 ++ .../src/transaction/tx_header.rs | 92 +++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4b776c561..337d256511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). - [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). +- Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). ### Changes diff --git a/crates/miden-objects/src/transaction/inputs/notes.rs b/crates/miden-objects/src/transaction/inputs/notes.rs index 35663c12ca..72cad6931f 100644 --- a/crates/miden-objects/src/transaction/inputs/notes.rs +++ b/crates/miden-objects/src/transaction/inputs/notes.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use super::TransactionInputError; use crate::note::{Note, NoteId, NoteInclusionProof, NoteLocation, Nullifier}; +use crate::transaction::InputNoteCommitment; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -145,6 +146,12 @@ impl InputNotes { Self::new(input_note_vec) } + + /// Returns a vector of input note commitments based on the input notes. + pub fn to_commitments(&self) -> InputNotes { + let notes = self.notes.iter().map(InputNoteCommitment::from).collect(); + InputNotes::::new_unchecked(notes) + } } impl IntoIterator for InputNotes { diff --git a/crates/miden-objects/src/transaction/tx_header.rs b/crates/miden-objects/src/transaction/tx_header.rs index 2b4b0cef1f..9a86a9f0b9 100644 --- a/crates/miden-objects/src/transaction/tx_header.rs +++ b/crates/miden-objects/src/transaction/tx_header.rs @@ -7,6 +7,7 @@ use crate::asset::FungibleAsset; use crate::note::NoteHeader; use crate::transaction::{ AccountId, + ExecutedTransaction, InputNoteCommitment, InputNotes, OutputNotes, @@ -171,6 +172,21 @@ impl From<&ProvenTransaction> for TransactionHeader { } } +impl From<&ExecutedTransaction> for TransactionHeader { + /// Constructs a [`TransactionHeader`] from a [`ExecutedTransaction`]. + fn from(tx: &ExecutedTransaction) -> Self { + TransactionHeader::new_unchecked( + tx.id(), + tx.account_id(), + tx.initial_account().initial_commitment(), + tx.final_account().commitment(), + tx.input_notes().to_commitments(), + tx.output_notes().iter().map(NoteHeader::from).collect(), + tx.fee(), + ) + } +} + // SERIALIZATION // ================================================================================================ @@ -216,3 +232,79 @@ impl Deserializable for TransactionHeader { Ok(tx_header) } } + +#[cfg(test)] +mod tests { + use miden_verifier::ExecutionProof; + + use super::*; + use crate::asset::FungibleAsset; + use crate::transaction::ProvenTransactionBuilder; + + #[test] + fn from_proven_transaction() { + // Build proven transaction. + let account_id = crate::account::AccountId::dummy( + [1; 15], + crate::account::AccountIdVersion::Version0, + crate::account::AccountType::FungibleFaucet, + crate::account::AccountStorageMode::Private, + ); + let initial_commitment = + [2; 32].try_into().expect("failed to create initial account commitment"); + let final_commitment = + [3; 32].try_into().expect("failed to create final account commitment"); + let account_delta_commitment = + [4; 32].try_into().expect("failed to create account delta commitment"); + let block_num = crate::block::BlockNumber::from(1); + let block_ref = Word::empty(); + let fee = FungibleAsset::mock(42).unwrap_fungible(); + let expiration_block_num = crate::block::BlockNumber::from(2); + let proof = ExecutionProof::new_dummy(); + + let proven_tx = ProvenTransactionBuilder::new( + account_id, + initial_commitment, + final_commitment, + account_delta_commitment, + block_num, + block_ref, + fee, + expiration_block_num, + proof, + ) + .build() + .unwrap(); + + // Create header from proven transaction. + let header = TransactionHeader::from(&proven_tx); + + // Verify the header data matches the proven transaction. + assert_eq!(header.id(), proven_tx.id()); + assert_eq!(header.account_id(), proven_tx.account_id()); + assert_eq!( + header.initial_state_commitment(), + proven_tx.account_update().initial_state_commitment() + ); + assert_eq!( + header.final_state_commitment(), + proven_tx.account_update().final_state_commitment() + ); + assert_eq!(header.fee(), proven_tx.fee()); + + assert_eq!(header.input_notes().iter().count(), proven_tx.input_notes().iter().count()); + + for (header_note, tx_note) in + header.input_notes().iter().zip(proven_tx.input_notes().iter()) + { + assert_eq!(header_note, tx_note); + } + + assert_eq!(header.output_notes().len(), proven_tx.output_notes().iter().count()); + for (header_note, tx_note) in + header.output_notes().iter().zip(proven_tx.output_notes().iter()) + { + assert_eq!(*header_note, crate::note::NoteHeader::from(tx_note)); + } + } +} From aa87bcae2ab533cac0649393a4e58d17add4044d Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 15 Dec 2025 13:08:39 +0700 Subject: [PATCH 062/114] chore: update protocol library docs (#2179) --- docs/src/protocol_library.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index aa49c87157..d17f09ed68 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -40,10 +40,10 @@ Active account procedures can be used to read from storage, fetch or compute com | `get_code_commitment` | Gets the account code commitment of the active account.

**Inputs:** `[]`
**Outputs:** `[CODE_COMMITMENT]` | Account | | `get_initial_storage_commitment` | Returns the storage commitment of the active account at the beginning of the transaction.

**Inputs:** `[]`
**Outputs:** `[INIT_STORAGE_COMMITMENT]` | Any | | `compute_storage_commitment` | Computes the latest account storage commitment of the active account.

**Inputs:** `[]`
**Outputs:** `[STORAGE_COMMITMENT]` | Account | -| `get_item` | Gets an item from the account storage.

**Inputs:** `[index]`
**Outputs:** `[VALUE]` | Account | -| `get_initial_item` | Gets the initial item from the account storage slot as it was at the beginning of the transaction.

**Inputs:** `[index]`
**Outputs:** `[VALUE]` | Account | -| `get_map_item` | Returns the VALUE located under the specified KEY within the map contained in the given account storage slot.

**Inputs:** `[index, KEY]`
**Outputs:** `[VALUE]` | Account | -| `get_initial_map_item` | Gets the initial VALUE from the account storage map as it was at the beginning of the transaction.

**Inputs:** `[index, KEY]`
**Outputs:** `[VALUE]` | Account | +| `get_item` | Gets an item from the account storage.

**Inputs:** `[slot_id_prefix, slot_id_suffix]`
**Outputs:** `[VALUE]` | Account | +| `get_initial_item` | Gets the initial item from the account storage slot as it was at the beginning of the transaction.

**Inputs:** `[slot_id_prefix, slot_id_suffix]`
**Outputs:** `[VALUE]` | Account | +| `get_map_item` | Returns the VALUE located under the specified KEY within the map contained in the given account storage slot.

**Inputs:** `[slot_id_prefix, slot_id_suffix, KEY]`
**Outputs:** `[VALUE]` | Account | +| `get_initial_map_item` | Gets the initial VALUE from the account storage map as it was at the beginning of the transaction.

**Inputs:** `[slot_id_prefix, slot_id_suffix, KEY]`
**Outputs:** `[VALUE]` | Account | | `get_balance` | Returns the balance of the fungible asset associated with the provided faucet_id in the active account's vault.

**Inputs:** `[faucet_id_prefix, faucet_id_suffix]`
**Outputs:** `[balance]` | Any | | `get_initial_balance` | Returns the balance of the fungible asset associated with the provided faucet_id in the active account's vault at the beginning of the transaction.

**Inputs:** `[faucet_id_prefix, faucet_id_suffix]`
**Outputs:** `[init_balance]` | Any | | `has_non_fungible_asset` | Returns a boolean indicating whether the non-fungible asset is present in the active account's vault.

**Inputs:** `[ASSET]`
**Outputs:** `[has_asset]` | Any | @@ -62,8 +62,8 @@ Native account procedures can be used to write to storage, add or remove assets | `get_id` | Returns the ID of the native account of the transaction.

**Inputs:** `[]`
**Outputs:** `[account_id_prefix, account_id_suffix]` | Any | | `incr_nonce` | Increments the nonce of the native account by one and returns the new nonce. Can only be called from auth procedures.

**Inputs:** `[]`
**Outputs:** `[final_nonce]` | Auth | | `compute_delta_commitment` | Computes the commitment to the native account's delta. Can only be called from auth procedures.

**Inputs:** `[]`
**Outputs:** `[DELTA_COMMITMENT]` | Auth | -| `set_item` | Sets an item in the native account storage.

**Inputs:** `[index, VALUE]`
**Outputs:** `[OLD_VALUE]` | Native & Account | -| `set_map_item` | Sets VALUE under the specified KEY within the map contained in the given native account storage slot.

**Inputs:** `[index, KEY, VALUE]`
**Outputs:** `[OLD_MAP_ROOT, OLD_MAP_VALUE]` | Native & Account | +| `set_item` | Sets an item in the native account storage.

**Inputs:** `[slot_id_prefix, slot_id_suffix, VALUE]`
**Outputs:** `[OLD_VALUE]` | Native & Account | +| `set_map_item` | Sets VALUE under the specified KEY within the map contained in the given native account storage slot.

**Inputs:** `[slot_id_prefix, slot_id_suffix, KEY, VALUE]`
**Outputs:** `[OLD_MAP_ROOT, OLD_MAP_VALUE]` | Native & Account | | `add_asset` | Adds the specified asset to the vault. For fungible assets, returns the total after addition.

**Inputs:** `[ASSET]`
**Outputs:** `[ASSET']` | Native & Account | | `remove_asset` | Removes the specified asset from the vault.

**Inputs:** `[ASSET]`
**Outputs:** `[ASSET]` | Native & Account | | `was_procedure_called` | Returns 1 if a native account procedure was called during transaction execution, and 0 otherwise.

**Inputs:** `[PROC_ROOT]`
**Outputs:** `[was_called]` | Any | From fad69ad4427409b699bfeffbb2893a15a5ad411f Mon Sep 17 00:00:00 2001 From: Farukest Date: Mon, 15 Dec 2025 21:56:14 +0300 Subject: [PATCH 063/114] refactor: add `SlotName` to `StorageSlotHeader` (#2160) --- CHANGELOG.md | 2 +- crates/miden-objects/src/account/mod.rs | 1 + .../src/account/storage/header.rs | 218 ++++++++++++------ .../miden-objects/src/account/storage/mod.rs | 27 +-- .../src/account/storage/partial.rs | 4 +- .../src/account/storage/slot/slot_name.rs | 13 +- crates/miden-tx/src/host/mod.rs | 4 +- .../src/host/storage_delta_tracker.rs | 39 ++-- crates/miden-tx/src/host/tx_event.rs | 16 +- 9 files changed, 193 insertions(+), 131 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 337d256511..e5ddb467ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). +- [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2160](https://github.com/0xMiden/miden-base/pull/2160), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). - [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). - Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 83d7bf3b55..43ca9f8680 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -74,6 +74,7 @@ pub use storage::{ StorageMapWitness, StorageSlot, StorageSlotContent, + StorageSlotHeader, StorageSlotId, StorageSlotName, StorageSlotType, diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-objects/src/account/storage/header.rs index 1e43c35ac0..8f48bfe646 100644 --- a/crates/miden-objects/src/account/storage/header.rs +++ b/crates/miden-objects/src/account/storage/header.rs @@ -1,6 +1,7 @@ use alloc::string::ToString; use alloc::vec::Vec; +use super::map::EMPTY_STORAGE_MAP_ROOT; use super::{AccountStorage, Felt, StorageSlotType, Word}; use crate::account::{StorageSlot, StorageSlotId, StorageSlotName}; use crate::crypto::SequentialCommit; @@ -16,44 +17,6 @@ use crate::{AccountError, FieldElement, ZERO}; // ACCOUNT STORAGE HEADER // ================================================================================================ -/// The header of a [`StorageSlot`], storing only the slot ID, slot type and value of the slot. -/// -/// The stored value differs based on the slot type: -/// - [`StorageSlotType::Value`]: The value of the slot itself. -/// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct StorageSlotHeader { - id: StorageSlotId, - r#type: StorageSlotType, - value: Word, -} - -impl StorageSlotHeader { - /// Returns a new instance of storage slot header from the provided storage slot ID, type and - /// value. - pub(crate) fn new(id: StorageSlotId, r#type: StorageSlotType, value: Word) -> Self { - Self { id, r#type, value } - } - - /// Returns this storage slot header as field elements. - /// - /// This is done by converting this storage slot into 8 field elements as follows: - /// ```text - /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE] - /// ``` - pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS] { - let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS]; - elements[0..4].copy_from_slice(&[ - Felt::ZERO, - self.r#type.as_felt(), - self.id.suffix(), - self.id.prefix(), - ]); - elements[4..8].copy_from_slice(self.value.as_elements()); - elements - } -} - /// The header of an [`AccountStorage`], storing only the slot name, slot type and value of each /// storage slot. /// @@ -62,7 +25,7 @@ impl StorageSlotHeader { /// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AccountStorageHeader { - slots: Vec<(StorageSlotName, StorageSlotType, Word)>, + slots: Vec, } impl AccountStorageHeader { @@ -77,39 +40,60 @@ impl AccountStorageHeader { /// - The number of provided slots is greater than [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. /// - The slots are not sorted by [`StorageSlotId`]. /// - There are multiple storage slots with the same [`StorageSlotName`]. - pub fn new(slots: Vec<(StorageSlotName, StorageSlotType, Word)>) -> Result { + pub fn new(slots: Vec) -> Result { if slots.len() > AccountStorage::MAX_NUM_STORAGE_SLOTS { return Err(AccountError::StorageTooManySlots(slots.len() as u64)); } - if !slots.is_sorted_by_key(|(slot_name, ..)| slot_name.id()) { + if !slots.is_sorted_by_key(|slot| slot.id()) { return Err(AccountError::UnsortedStorageSlots); } // Check for slot name uniqueness by checking each neighboring slot's IDs. This is // sufficient because the slots are sorted. for slots in slots.windows(2) { - if slots[0].0.id() == slots[1].0.id() { - return Err(AccountError::DuplicateStorageSlotName(slots[0].0.clone())); + if slots[0].id() == slots[1].id() { + return Err(AccountError::DuplicateStorageSlotName(slots[0].name().clone())); } } Ok(Self { slots }) } + /// Returns a new instance of account storage header initialized with the provided slot tuples. + /// + /// This is a convenience method that converts tuples to [`StorageSlotHeader`]s. + /// + /// # Errors + /// + /// Returns an error if: + /// - The number of provided slots is greater than [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. + /// - The slots are not sorted by [`StorageSlotId`]. + #[cfg(any(feature = "testing", test))] + pub fn from_tuples( + slots: Vec<(StorageSlotName, StorageSlotType, Word)>, + ) -> Result { + let slots = slots + .into_iter() + .map(|(name, slot_type, value)| StorageSlotHeader::new(name, slot_type, value)) + .collect(); + + Self::new(slots) + } + // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- /// Returns an iterator over the storage header slots. - pub fn slots(&self) -> impl Iterator { - self.slots.iter().map(|(name, r#type, value)| (name, r#type, value)) + pub fn slots(&self) -> impl Iterator { + self.slots.iter() } /// Returns an iterator over the storage header map slots. - pub fn map_slot_roots(&self) -> impl Iterator { - self.slots.iter().filter_map(|(_, slot_type, value)| match slot_type { + pub fn map_slot_roots(&self) -> impl Iterator + '_ { + self.slots.iter().filter_map(|slot| match slot.slot_type() { StorageSlotType::Value => None, - StorageSlotType::Map => Some(*value), + StorageSlotType::Map => Some(slot.value()), }) } @@ -119,31 +103,21 @@ impl AccountStorageHeader { self.slots.len() as u8 } - /// Returns a slot contained in the storage header at a given index. + /// Returns the storage slot header for the slot with the given name. /// - /// Returns `None` if a slot with the provided slot ID does not exist. + /// Returns `None` if a slot with the provided name does not exist. pub fn find_slot_header_by_name( &self, slot_name: &StorageSlotName, - ) -> Option<(&StorageSlotType, &Word)> { + ) -> Option<&StorageSlotHeader> { self.find_slot_header_by_id(slot_name.id()) - .map(|(_slot_name, slot_type, slot_value)| (slot_type, slot_value)) } - /// Returns a slot contained in the storage header at a given index. + /// Returns the storage slot header for the slot with the given ID. /// /// Returns `None` if a slot with the provided slot ID does not exist. - pub fn find_slot_header_by_id( - &self, - slot_id: StorageSlotId, - ) -> Option<(&StorageSlotName, &StorageSlotType, &Word)> { - self.slots.iter().find_map(|(slot_name, slot_type, slot_value)| { - if slot_name.id() == slot_id { - Some((slot_name, slot_type, slot_value)) - } else { - None - } - }) + pub fn find_slot_header_by_id(&self, slot_id: StorageSlotId) -> Option<&StorageSlotHeader> { + self.slots.iter().find(|slot| slot.id() == slot_id) } /// Indicates whether the slot with the given `name` is a map slot. @@ -156,7 +130,7 @@ impl AccountStorageHeader { match self .find_slot_header_by_name(name) .ok_or(AccountError::StorageSlotNameNotFound { slot_name: name.clone() })? - .0 + .slot_type() { StorageSlotType::Map => Ok(true), StorageSlotType::Value => Ok(false), @@ -195,11 +169,7 @@ impl SequentialCommit for AccountStorageHeader { type Commitment = Word; fn to_elements(&self) -> Vec { - self.slots() - .flat_map(|(slot_name, slot_type, slot_value)| { - StorageSlotHeader::new(slot_name.id(), *slot_type, *slot_value).to_elements() - }) - .collect() + self.slots().flat_map(|slot| slot.to_elements()).collect() } } @@ -217,11 +187,115 @@ impl Serializable for AccountStorageHeader { impl Deserializable for AccountStorageHeader { fn read_from(source: &mut R) -> Result { let len = source.read_u8()?; - let slots = source.read_many(len as usize)?; + let slots: Vec = source.read_many(len as usize)?; Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } +// STORAGE SLOT HEADER +// ================================================================================================ + +/// The header of a [`StorageSlot`], storing only the slot name (or ID), slot type and value of the +/// slot. +/// +/// The stored value differs based on the slot type: +/// - [`StorageSlotType::Value`]: The value of the slot itself. +/// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StorageSlotHeader { + name: StorageSlotName, + r#type: StorageSlotType, + value: Word, +} + +impl StorageSlotHeader { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Returns a new instance of storage slot header. + pub fn new(name: StorageSlotName, r#type: StorageSlotType, value: Word) -> Self { + Self { name, r#type, value } + } + + /// Returns a new instance of storage slot header with an empty value slot. + pub fn with_empty_value(name: StorageSlotName) -> StorageSlotHeader { + StorageSlotHeader::new(name, StorageSlotType::Value, Word::default()) + } + + /// Returns a new instance of storage slot header with an empty map slot. + pub fn with_empty_map(name: StorageSlotName) -> StorageSlotHeader { + StorageSlotHeader::new(name, StorageSlotType::Map, EMPTY_STORAGE_MAP_ROOT) + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns a reference to the slot name. + pub fn name(&self) -> &StorageSlotName { + &self.name + } + + /// Returns the slot ID. + pub fn id(&self) -> StorageSlotId { + self.name.id() + } + + /// Returns the slot type. + pub fn slot_type(&self) -> StorageSlotType { + self.r#type + } + + /// Returns the slot value. + pub fn value(&self) -> Word { + self.value + } + + /// Returns this storage slot header as field elements. + /// + /// This is done by converting this storage slot into 8 field elements as follows: + /// ```text + /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE] + /// ``` + pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS] { + let id = self.id(); + let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS]; + elements[0..4].copy_from_slice(&[ + Felt::ZERO, + self.r#type.as_felt(), + id.suffix(), + id.prefix(), + ]); + elements[4..8].copy_from_slice(self.value.as_elements()); + elements + } +} + +impl From<&StorageSlot> for StorageSlotHeader { + fn from(slot: &StorageSlot) -> Self { + StorageSlotHeader::new(slot.name().clone(), slot.slot_type(), slot.value()) + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for StorageSlotHeader { + fn write_into(&self, target: &mut W) { + self.name.write_into(target); + self.r#type.write_into(target); + self.value.write_into(target); + } +} + +impl Deserializable for StorageSlotHeader { + fn read_from(source: &mut R) -> Result { + let name = StorageSlotName::read_from(source)?; + let slot_type = StorageSlotType::read_from(source)?; + let value = Word::read_from(source)?; + Ok(Self::new(name, slot_type, value)) + } +} + // TESTS // ================================================================================================ @@ -251,7 +325,7 @@ mod tests { ]; slots.sort_unstable_by_key(|(slot_name, ..)| slot_name.id()); - let expected_header = AccountStorageHeader { slots }; + let expected_header = AccountStorageHeader::from_tuples(slots).unwrap(); let account_storage = AccountStorage::mock(); assert_eq!(expected_header, AccountStorageHeader::from(&account_storage)) diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index 7aff0806c7..23c4aa9efe 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -12,7 +12,6 @@ use super::{ Serializable, Word, }; -use crate::account::storage::header::StorageSlotHeader; use crate::account::{AccountComponent, AccountType}; use crate::crypto::SequentialCommit; use crate::utils::sync::LazyLock; @@ -24,7 +23,7 @@ mod map; pub use map::{PartialStorageMap, StorageMap, StorageMapWitness}; mod header; -pub use header::AccountStorageHeader; +pub use header::{AccountStorageHeader, StorageSlotHeader}; mod partial; pub use partial::PartialStorage; @@ -180,15 +179,8 @@ impl AccountStorage { /// Returns an [AccountStorageHeader] for this account storage. pub fn to_header(&self) -> AccountStorageHeader { - AccountStorageHeader::new( - self.slots - .iter() - .map(|slot| { - (slot.name().clone(), slot.content().slot_type(), slot.content().value()) - }) - .collect(), - ) - .expect("slots should be valid as ensured by AccountStorage") + AccountStorageHeader::new(self.slots.iter().map(StorageSlotHeader::from).collect()) + .expect("slots should be valid as ensured by AccountStorage") } /// Returns a reference to the storage slot with the provided name, if it exists, `None` @@ -352,7 +344,7 @@ impl SequentialCommit for AccountStorage { .iter() .flat_map(|slot| { StorageSlotHeader::new( - slot.id(), + slot.name().clone(), slot.content().slot_type(), slot.content().value(), ) @@ -402,7 +394,7 @@ mod tests { use super::{AccountStorage, Deserializable, Serializable}; use crate::AccountError; - use crate::account::{AccountStorageHeader, StorageSlot, StorageSlotName}; + use crate::account::{AccountStorageHeader, StorageSlot, StorageSlotHeader, StorageSlotName}; #[test] fn test_serde_account_storage() -> anyhow::Result<()> { @@ -462,13 +454,8 @@ mod tests { }); slots.sort_unstable(); - let err = AccountStorageHeader::new( - slots - .iter() - .map(|slot| (slot.name().clone(), slot.slot_type(), slot.value())) - .collect(), - ) - .unwrap_err(); + let err = AccountStorageHeader::new(slots.iter().map(StorageSlotHeader::from).collect()) + .unwrap_err(); assert_matches!(err, AccountError::DuplicateStorageSlotName(name) => { assert_eq!(name, slot_name0); diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-objects/src/account/storage/partial.rs index c44d752704..755f867d4e 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-objects/src/account/storage/partial.rs @@ -190,8 +190,8 @@ mod tests { PartialStorage::new(storage_header, [PartialStorageMap::with_witnesses([witness])?]) .context("creating partial storage")?; - let (_, map_root) = partial_storage.header.find_slot_header_by_name(&slot_name).unwrap(); - let retrieved_map = partial_storage.maps.get(map_root).unwrap(); + let slot_header = partial_storage.header.find_slot_header_by_name(&slot_name).unwrap(); + let retrieved_map = partial_storage.maps.get(&slot_header.value()).unwrap(); assert!(retrieved_map.open(&map_key_absent).is_err()); assert!(retrieved_map.open(&map_key_present).is_ok()); Ok(()) diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-objects/src/account/storage/slot/slot_name.rs index b431c26b59..27d0c7ce45 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_name.rs @@ -1,4 +1,5 @@ use alloc::string::{String, ToString}; +use alloc::sync::Arc; use core::fmt::Display; use core::str::FromStr; @@ -35,7 +36,7 @@ use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Seri /// - Each component must not start with an underscore. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StorageSlotName { - name: String, + name: Arc, id: StorageSlotId, } @@ -58,10 +59,10 @@ impl StorageSlotName { /// /// Returns an error if: /// - the slot name is invalid (see the type-level docs for the requirements). - pub fn new(name: impl Into) -> Result { - let name = name.into(); + pub fn new(name: impl Into>) -> Result { + let name: Arc = name.into(); Self::validate(&name)?; - let id = StorageSlotId::from_str(name.as_str()); + let id = StorageSlotId::from_str(&name); Ok(Self { name, id }) } @@ -211,7 +212,7 @@ impl FromStr for StorageSlotName { impl From for String { fn from(slot_name: StorageSlotName) -> Self { - slot_name.name + slot_name.name.to_string() } } @@ -352,7 +353,7 @@ mod tests { #[test] fn slot_name_allows_ascii_alphanumeric_and_underscore() -> anyhow::Result<()> { let name = format!("{FULL_ALPHABET}::second"); - let slot_name = StorageSlotName::new(&name)?; + let slot_name = StorageSlotName::new(name.clone())?; assert_eq!(slot_name.as_str(), name); Ok(()) diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index fcd1b61236..fc39d3b517 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -36,9 +36,9 @@ use miden_objects::account::{ AccountId, AccountStorageHeader, PartialAccount, + StorageSlotHeader, StorageSlotId, StorageSlotName, - StorageSlotType, }; use miden_objects::asset::Asset; use miden_objects::note::{NoteId, NoteMetadata, NoteRecipient}; @@ -165,7 +165,7 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { pub fn initial_account_storage_slot( &self, slot_id: StorageSlotId, - ) -> Result<(&StorageSlotName, &StorageSlotType, &Word), TransactionKernelError> { + ) -> Result<&StorageSlotHeader, TransactionKernelError> { self.initial_account_storage_header() .find_slot_header_by_id(slot_id) .ok_or_else(|| { diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index d6e413ff29..5e8b5c4f03 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -6,7 +6,7 @@ use miden_objects::account::{ AccountStorageDelta, AccountStorageHeader, PartialAccount, - StorageMap, + StorageSlotHeader, StorageSlotName, StorageSlotType, }; @@ -60,36 +60,37 @@ impl StorageDeltaTracker { // Insert account storage into delta if it is new to match the kernel behavior. if account.is_new() { - account - .storage() - .header() - .slots() - .for_each(|(slot_name, slot_type, slot_value)| match slot_type { + account.storage().header().slots().for_each(|slot_header| { + match slot_header.slot_type() { StorageSlotType::Value => { // For new accounts, all values should be added to the delta, even empty // words, so that the final delta includes the storage slot. - storage_delta_tracker.set_item(slot_name.clone(), *slot_value); + storage_delta_tracker + .set_item(slot_header.name().clone(), slot_header.value()); }, StorageSlotType::Map => { let storage_map = account .storage() .maps() - .find(|map| map.root() == *slot_value) + .find(|map| map.root() == slot_header.value()) .expect("storage map should be present in partial storage"); // Make sure each map is represented by at least an empty storage map delta. - storage_delta_tracker.delta.insert_empty_map_delta(slot_name.clone()); + storage_delta_tracker + .delta + .insert_empty_map_delta(slot_header.name().clone()); storage_map.entries().for_each(|(key, value)| { storage_delta_tracker.set_map_item( - slot_name.clone(), + slot_header.name().clone(), *key, Word::empty(), *value, ); }); }, - }); + } + }); } storage_delta_tracker @@ -156,10 +157,10 @@ impl StorageDeltaTracker { value_slots.retain(|slot_name, new_value| { // SAFETY: The header in the initial storage is the one from the account against // which the transaction is executed, so accessing that slot name should be fine. - let (_slot_type, initial_value) = storage_header + let slot_header = storage_header .find_slot_header_by_name(slot_name) .expect("slot name should exist"); - new_value != initial_value + *new_value != slot_header.value() }); } @@ -191,17 +192,17 @@ impl StorageDeltaTracker { /// Creates empty slots of the same slot types as the to-be-created account. fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorageHeader { - let slots: Vec<(StorageSlotName, StorageSlotType, Word)> = account + let slots: Vec = account .storage() .header() .slots() - .map(|(slot_name, slot_type, _)| match slot_type { - StorageSlotType::Value => (slot_name.clone(), *slot_type, Word::empty()), - StorageSlotType::Map => (slot_name.clone(), *slot_type, StorageMap::new().root()), + .map(|slot_header| match slot_header.slot_type() { + StorageSlotType::Value => { + StorageSlotHeader::with_empty_value(slot_header.name().clone()) + }, + StorageSlotType::Map => StorageSlotHeader::with_empty_map(slot_header.name().clone()), }) .collect(); - // SAFETY: We are recreating a valid storage header with different values, which should not - // violate any constraints of the storage header. AccountStorageHeader::new(slots).expect("storage header should be valid") } diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index a3f246070e..e62441740e 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -260,8 +260,8 @@ impl TransactionEvent { let (slot_id, slot_type, _old_value) = process.get_storage_slot(slot_ptr)?; - let (slot_name, ..) = base_host.initial_account_storage_slot(slot_id)?; - let slot_name = slot_name.clone(); + let slot_header = base_host.initial_account_storage_slot(slot_id)?; + let slot_name = slot_header.name().clone(); if !slot_type.is_value() { return Err(TransactionKernelError::other(format!( @@ -297,9 +297,8 @@ impl TransactionEvent { // Resolve slot ID to slot name. let (slot_id, ..) = process.get_storage_slot(slot_ptr)?; - let (slot_name, ..) = base_host.initial_account_storage_slot(slot_id)?; - - let slot_name = slot_name.clone(); + let slot_header = base_host.initial_account_storage_slot(slot_id)?; + let slot_name = slot_header.name().clone(); Some(TransactionEvent::AccountStorageAfterSetMapItem { slot_name, @@ -582,15 +581,14 @@ fn on_account_storage_map_item_accessed<'store, STORE>( // For native accounts, we have to request witnesses against the initial // root instead of the _current_ one, since the data // store only has witnesses for initial one. - let (_slot_name, slot_type, slot_value) = - base_host.initial_account_storage_slot(slot_id)?; + let slot_header = base_host.initial_account_storage_slot(slot_id)?; - if *slot_type != StorageSlotType::Map { + if slot_header.slot_type() != StorageSlotType::Map { return Err(TransactionKernelError::other(format!( "expected slot {slot_id} to be of type map" ))); } - *slot_value + slot_header.value() } else { current_map_root }; From 198e25ee552638caefa356ff5b248a654b119083 Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Tue, 16 Dec 2025 09:01:29 +1300 Subject: [PATCH 064/114] test: move tx header test logic (#2181) --- .../src/transaction/tx_header.rs | 76 ------------------- crates/miden-testing/tests/lib.rs | 5 ++ 2 files changed, 5 insertions(+), 76 deletions(-) diff --git a/crates/miden-objects/src/transaction/tx_header.rs b/crates/miden-objects/src/transaction/tx_header.rs index 9a86a9f0b9..4b7adafad6 100644 --- a/crates/miden-objects/src/transaction/tx_header.rs +++ b/crates/miden-objects/src/transaction/tx_header.rs @@ -232,79 +232,3 @@ impl Deserializable for TransactionHeader { Ok(tx_header) } } - -#[cfg(test)] -mod tests { - use miden_verifier::ExecutionProof; - - use super::*; - use crate::asset::FungibleAsset; - use crate::transaction::ProvenTransactionBuilder; - - #[test] - fn from_proven_transaction() { - // Build proven transaction. - let account_id = crate::account::AccountId::dummy( - [1; 15], - crate::account::AccountIdVersion::Version0, - crate::account::AccountType::FungibleFaucet, - crate::account::AccountStorageMode::Private, - ); - let initial_commitment = - [2; 32].try_into().expect("failed to create initial account commitment"); - let final_commitment = - [3; 32].try_into().expect("failed to create final account commitment"); - let account_delta_commitment = - [4; 32].try_into().expect("failed to create account delta commitment"); - let block_num = crate::block::BlockNumber::from(1); - let block_ref = Word::empty(); - let fee = FungibleAsset::mock(42).unwrap_fungible(); - let expiration_block_num = crate::block::BlockNumber::from(2); - let proof = ExecutionProof::new_dummy(); - - let proven_tx = ProvenTransactionBuilder::new( - account_id, - initial_commitment, - final_commitment, - account_delta_commitment, - block_num, - block_ref, - fee, - expiration_block_num, - proof, - ) - .build() - .unwrap(); - - // Create header from proven transaction. - let header = TransactionHeader::from(&proven_tx); - - // Verify the header data matches the proven transaction. - assert_eq!(header.id(), proven_tx.id()); - assert_eq!(header.account_id(), proven_tx.account_id()); - assert_eq!( - header.initial_state_commitment(), - proven_tx.account_update().initial_state_commitment() - ); - assert_eq!( - header.final_state_commitment(), - proven_tx.account_update().final_state_commitment() - ); - assert_eq!(header.fee(), proven_tx.fee()); - - assert_eq!(header.input_notes().iter().count(), proven_tx.input_notes().iter().count()); - - for (header_note, tx_note) in - header.input_notes().iter().zip(proven_tx.input_notes().iter()) - { - assert_eq!(header_note, tx_note); - } - - assert_eq!(header.output_notes().len(), proven_tx.output_notes().iter().count()); - for (header_note, tx_note) in - header.output_notes().iter().zip(proven_tx.output_notes().iter()) - { - assert_eq!(*header_note, crate::note::NoteHeader::from(tx_note)); - } - } -} diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index e7ec9e9abb..129298d88e 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -27,14 +27,19 @@ use miden_tx::{ pub fn prove_and_verify_transaction( executed_transaction: ExecutedTransaction, ) -> Result<(), TransactionVerifierError> { + use miden_objects::transaction::TransactionHeader; + let executed_transaction_id = executed_transaction.id(); + let executed_tx_header = TransactionHeader::from(&executed_transaction); // Prove the transaction let proof_options = ProvingOptions::default(); let prover = LocalTransactionProver::new(proof_options); let proven_transaction = prover.prove(executed_transaction).unwrap(); + let proven_tx_header = TransactionHeader::from(&proven_transaction); assert_eq!(proven_transaction.id(), executed_transaction_id); + assert_eq!(proven_tx_header, executed_tx_header); // Serialize & deserialize the ProvenTransaction let serialised_transaction = proven_transaction.to_bytes(); From cb885a8db54d867a88982278e76b8e41bc795917 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:44:28 -0300 Subject: [PATCH 065/114] feat: implement u256 agglayer asset scaling up procedure (#2141) --- Cargo.lock | 50 ++++ Cargo.toml | 15 +- .../account_components/asset_conversion.masm | 106 ++++++++ crates/miden-lib/src/agglayer/mod.rs | 26 ++ crates/miden-lib/src/agglayer/utils.rs | 17 ++ .../src/errors/note_script_errors.rs | 3 + crates/miden-testing/Cargo.toml | 1 + .../tests/agglayer/asset_conversion.rs | 248 ++++++++++++++++++ crates/miden-testing/tests/agglayer/mod.rs | 1 + 9 files changed, 460 insertions(+), 7 deletions(-) create mode 100644 crates/miden-lib/asm/agglayer/account_components/asset_conversion.masm create mode 100644 crates/miden-testing/tests/agglayer/asset_conversion.rs diff --git a/Cargo.lock b/Cargo.lock index e6a494dbdf..ccf2c41231 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -317,6 +317,12 @@ version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.11.0" @@ -880,6 +886,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "static_assertions", +] + [[package]] name = "fixedbitset" version = "0.5.7" @@ -1098,6 +1113,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hkdf" version = "0.12.4" @@ -1719,6 +1740,7 @@ dependencies = [ "miden-processor", "miden-tx", "miden-tx-batch-prover", + "primitive-types", "rand", "rand_chacha", "rstest", @@ -2155,6 +2177,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "primitive-types" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721a1da530b5a2633218dc9f75713394c983c352be88d2d7c9ee85e2c4c21794" +dependencies = [ + "fixed-hash", + "uint", +] + [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -2668,6 +2700,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "str_stack" version = "0.1.0" @@ -3052,6 +3090,18 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unarray" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index 18222c2b59..6c774e9967 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,10 +63,11 @@ miden-utils-sync = { default-features = false, version = "0.19" } miden-verifier = { default-features = false, version = "0.19" } # External dependencies -anyhow = { default-features = false, features = ["backtrace", "std"], version = "1.0" } -assert_matches = { default-features = false, version = "1.5" } -rand = { default-features = false, version = "0.9" } -rand_chacha = { default-features = false, version = "0.9" } -rstest = { version = "0.26" } -thiserror = { default-features = false, version = "2.0" } -tokio = { default-features = false, features = ["sync"], version = "1" } +anyhow = { default-features = false, features = ["backtrace", "std"], version = "1.0" } +assert_matches = { default-features = false, version = "1.5" } +primitive-types = { default-features = false, version = "0.14" } +rand = { default-features = false, version = "0.9" } +rand_chacha = { default-features = false, version = "0.9" } +rstest = { version = "0.26" } +thiserror = { default-features = false, version = "2.0" } +tokio = { default-features = false, features = ["sync"], version = "1" } diff --git a/crates/miden-lib/asm/agglayer/account_components/asset_conversion.masm b/crates/miden-lib/asm/agglayer/account_components/asset_conversion.masm new file mode 100644 index 0000000000..58f3462eab --- /dev/null +++ b/crates/miden-lib/asm/agglayer/account_components/asset_conversion.masm @@ -0,0 +1,106 @@ +use.std::math::u64 +use.std::word + +# CONSTANTS +# ================================================================================================= + +const.MAX_SCALING_FACTOR=18 + +# ERRORS +# ================================================================================================= +const.ERR_SCALE_AMOUNT_EXCEEDED_LIMIT="maximum scaling factor is 18" + +#! Calculate 10^scale where scale is a u8 exponent. +#! +#! Inputs: [scale] +#! Outputs: [10^scale] +#! +#! Where: +#! - scale is expected to be a small integer (0-18 typical for crypto decimals) +#! +#! Panics if: +#! - scale > 18 (overflow protection) +proc pow10 + u32assert.err=ERR_SCALE_AMOUNT_EXCEEDED_LIMIT + # => [scale] + + dup u32lte.MAX_SCALING_FACTOR assert.err=ERR_SCALE_AMOUNT_EXCEEDED_LIMIT + # => [scale] + + push.1 swap + # => [scale, result] + + dup neq.0 + # => [is_not_zero, scale, result] + + # Loop to calculate 10^scale + while.true + # => [scale, result] + + # result *= 10 + swap mul.10 swap + # => [scale, result*10] + + # scale -= 1 + sub.1 + # => [scale-1, result*10] + + dup neq.0 + # => [is_not_zero, scale-1, result*10] + end + # => [0, result] + + drop + # => [result] +end + +#! Convert an asset amount to a scaled U256 representation for bridging to Agglayer. +#! +#! This procedure is used to convert Miden asset amounts to EVM asset amounts. +#! It multiplies the input amount by 10^target_scale to adjust for decimal differences +#! between the current representation and the target chain's native decimals. +#! +#! The procedure first calculates 10^target_scale using the pow10 helper, then converts +#! both the amount and scale factor to U64 format, performs U64 multiplication, and +#! returns the result as 8 u32 limbs in little-endian order (U256 format). +#! +#! Inputs: [amount, target_scale] +#! Outputs: [[RESULT_U256[0], RESULT_U256[1]]] +#! +#! Where: +#! - amount: The asset amount to be converted (range: 0 to 2^63 - 2^31) +#! - target_scale: Exponent for scaling factor (10^target_scale) +#! - [RESULT_U256[0], RESULT_U256[1]]: U256 value as 8 u32 limbs in little-endian order +#! (least significant limb at the top of the stack, each limb stored in little-endian format) +#! +#! Examples: +#! - USDC: amount=1000000000, target_scale=0 → 1000000000 (no scaling) +#! - ETH: amount=1e10, target_scale=8 → 1e18 +#! +#! Invocation: exec +pub proc scale_native_amount_to_u256 + swap + # => [target_scale, amount] + + exec.pow10 + # => [scale, amount] + + u32split + # => [scale_hi, scale_lo, amount] + + movup.2 u32split + # => [amount_hi, amount_lo, scale_hi, scale_lo] + + # Perform U64 multiplication: amount * scale + # This is safe because both the scaling factor and amount are guaranteed to be smaller + # than 2^64, so we will never overflow a 256-bit value. + exec.u64::overflowing_mul + # => [res_hi, res_mid_hi, res_mid_lo, res_lo] + + exec.word::reverse + # => [res_lo, res_mid_lo, res_mid_hi, res_hi] + + # convert to U256 & little endian + padw swapw + # => [RESULT_U256[0], RESULT_U256[1]] +end diff --git a/crates/miden-lib/src/agglayer/mod.rs b/crates/miden-lib/src/agglayer/mod.rs index 6802d6aa44..be1f75cab5 100644 --- a/crates/miden-lib/src/agglayer/mod.rs +++ b/crates/miden-lib/src/agglayer/mod.rs @@ -93,3 +93,29 @@ pub fn bridge_out_with_local_exit_tree_component( local_exit_tree_component(vec![]), // local_exit_tree typically doesn't need storage slots ] } + +// Initialize the Asset Conversion library only once +static ASSET_CONVERSION_LIBRARY: LazyLock = LazyLock::new(|| { + let bytes = include_bytes!(concat!( + env!("OUT_DIR"), + "/assets/agglayer/account_components/asset_conversion.masl" + )); + Library::read_from_bytes(bytes).expect("Shipped Asset Conversion library is well-formed") +}); + +/// Returns the Asset Conversion Library. +pub fn asset_conversion_library() -> Library { + ASSET_CONVERSION_LIBRARY.clone() +} + +/// Creates an Asset Conversion component with the specified storage slots. +/// +/// This component uses the asset_conversion library and can be added to accounts +/// that need to convert assets between Miden and Ethereum formats. +pub fn asset_conversion_component(storage_slots: Vec) -> AccountComponent { + let library = asset_conversion_library(); + + AccountComponent::new(library, storage_slots) + .expect("asset_conversion component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} diff --git a/crates/miden-lib/src/agglayer/utils.rs b/crates/miden-lib/src/agglayer/utils.rs index 2a17275ee7..3995ff9ca4 100644 --- a/crates/miden-lib/src/agglayer/utils.rs +++ b/crates/miden-lib/src/agglayer/utils.rs @@ -3,6 +3,23 @@ use alloc::vec::Vec; use miden_objects::Felt; +/// Convert 8 Felt values (u32 limbs in little-endian order) to U256 bytes in little-endian format. +/// +/// The input limbs are expected to be in little-endian order (least significant limb first). +/// This function converts them to a 32-byte array in little-endian format for compatibility +/// with Ethereum/EVM which expects U256 values as 32 bytes in little-endian format. +/// This ensures compatibility when bridging assets between Miden and Ethereum-based chains. +pub fn felts_to_u256_bytes(limbs: [Felt; 8]) -> [u8; 32] { + let mut bytes = [0u8; 32]; + + for (i, limb) in limbs.iter().enumerate() { + let u32_value = limb.as_int() as u32; + let limb_bytes = u32_value.to_le_bytes(); + bytes[i * 4..(i + 1) * 4].copy_from_slice(&limb_bytes); + } + + bytes +} /// Converts an Ethereum address (20 bytes) into a vector of 5 Felt values. /// /// An Ethereum address is 20 bytes, which we split into 5 u32 values (4 bytes each). diff --git a/crates/miden-lib/src/errors/note_script_errors.rs b/crates/miden-lib/src/errors/note_script_errors.rs index 1d989afca8..c4fc42471a 100644 --- a/crates/miden-lib/src/errors/note_script_errors.rs +++ b/crates/miden-lib/src/errors/note_script_errors.rs @@ -46,6 +46,9 @@ pub const ERR_P2ID_TARGET_ACCT_MISMATCH: MasmError = MasmError::from_static_str( /// Error Message: "P2ID note expects exactly 2 note inputs" pub const ERR_P2ID_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("P2ID note expects exactly 2 note inputs"); +/// Error Message: "maximum scaling factor is 18" +pub const ERR_SCALE_AMOUNT_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("maximum scaling factor is 18"); + /// Error Message: "SWAP script requires exactly 1 note asset" pub const ERR_SWAP_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("SWAP script requires exactly 1 note asset"); /// Error Message: "SWAP script expects exactly 12 note inputs" diff --git a/crates/miden-testing/Cargo.toml b/crates/miden-testing/Cargo.toml index 4dcdc3cf2c..3735bacedf 100644 --- a/crates/miden-testing/Cargo.toml +++ b/crates/miden-testing/Cargo.toml @@ -37,6 +37,7 @@ winterfell = { version = "0.13" } anyhow = { features = ["backtrace", "std"], workspace = true } assert_matches = { workspace = true } miden-objects = { features = ["std"], workspace = true } +primitive-types = { workspace = true } rstest = { workspace = true } tokio = { features = ["macros", "rt"], workspace = true } winter-rand-utils = { version = "0.13" } diff --git a/crates/miden-testing/tests/agglayer/asset_conversion.rs b/crates/miden-testing/tests/agglayer/asset_conversion.rs new file mode 100644 index 0000000000..9a72ca1430 --- /dev/null +++ b/crates/miden-testing/tests/agglayer/asset_conversion.rs @@ -0,0 +1,248 @@ +extern crate alloc; + +use alloc::sync::Arc; + +use miden_lib::StdLibrary; +use miden_lib::agglayer::{asset_conversion_library, utils}; +use miden_lib::transaction::TransactionKernel; +use miden_objects::Felt; +use miden_objects::assembly::{Assembler, DefaultSourceManager}; +use miden_processor::fast::{ExecutionOutput, FastProcessor}; +use miden_processor::{AdviceInputs, DefaultHost, ExecutionError, Program, StackInputs}; +use primitive_types::U256; + +/// Convert a Vec to a U256 +fn felts_to_u256(felts: Vec) -> U256 { + assert_eq!(felts.len(), 8, "expected exactly 8 felts"); + let array: [Felt; 8] = + [felts[0], felts[1], felts[2], felts[3], felts[4], felts[5], felts[6], felts[7]]; + let bytes = utils::felts_to_u256_bytes(array); + U256::from_little_endian(&bytes) +} + +/// Convert the top 8 u32 values from the execution stack to a U256 +fn stack_to_u256(exec_output: &ExecutionOutput) -> U256 { + let felts: Vec = exec_output.stack[0..8].to_vec(); + felts_to_u256(felts) +} + +/// Execute a program with default host +async fn execute_program_with_default_host( + program: Program, +) -> Result { + let mut host = DefaultHost::default(); + + let test_lib = TransactionKernel::library(); + host.load_library(test_lib.mast_forest()).unwrap(); + + let std_lib = StdLibrary::default(); + host.load_library(std_lib.mast_forest()).unwrap(); + + let asset_conversion_lib = miden_lib::agglayer::asset_conversion_library(); + host.load_library(asset_conversion_lib.mast_forest()).unwrap(); + + let stack_inputs = StackInputs::new(vec![]).unwrap(); + let advice_inputs = AdviceInputs::default(); + + let processor = FastProcessor::new_debug(stack_inputs.as_slice(), advice_inputs); + processor.execute(&program, &mut host).await +} + +/// Helper function to test convert_felt_to_u256_scaled with given parameters +async fn test_convert_to_u256_helper( + miden_amount: Felt, + scale_exponent: Felt, + expected_result_array: [u32; 8], + expected_result_u256: U256, +) -> anyhow::Result<()> { + let asset_conversion_lib = asset_conversion_library(); + + let script_code = format!( + " + use.std::sys + + begin + push.{}.{} + exec.::scale_native_amount_to_u256 + exec.sys::truncate_stack + end + ", + scale_exponent, miden_amount, + ); + + let program = Assembler::new(Arc::new(DefaultSourceManager::default())) + .with_debug_mode(true) + .with_dynamic_library(StdLibrary::default()) + .unwrap() + .with_dynamic_library(asset_conversion_lib.clone()) + .unwrap() + .assemble_program(&script_code) + .unwrap(); + + let exec_output = execute_program_with_default_host(program).await?; + + // Extract the first 8 u32 values from the stack (the U256 representation) + let actual_result: [u32; 8] = [ + exec_output.stack[0].as_int() as u32, + exec_output.stack[1].as_int() as u32, + exec_output.stack[2].as_int() as u32, + exec_output.stack[3].as_int() as u32, + exec_output.stack[4].as_int() as u32, + exec_output.stack[5].as_int() as u32, + exec_output.stack[6].as_int() as u32, + exec_output.stack[7].as_int() as u32, + ]; + + let actual_result_u256 = stack_to_u256(&exec_output); + + assert_eq!(actual_result, expected_result_array); + assert_eq!(actual_result_u256, expected_result_u256); + + Ok(()) +} + +#[tokio::test] +async fn test_convert_to_u256_basic_examples() -> anyhow::Result<()> { + // Test case 1: amount=1, no scaling (scale_exponent=0) + test_convert_to_u256_helper( + Felt::new(1), + Felt::new(0), + [1, 0, 0, 0, 0, 0, 0, 0], + U256::from(1u64), + ) + .await?; + + // Test case 2: amount=1, scale to 1e18 (scale_exponent=18) + test_convert_to_u256_helper( + Felt::new(1), + Felt::new(18), + [2808348672, 232830643, 0, 0, 0, 0, 0, 0], + U256::from_dec_str("1000000000000000000").unwrap(), + ) + .await?; + + Ok(()) +} + +#[tokio::test] +async fn test_convert_to_u256_scaled_eth() -> anyhow::Result<()> { + // 100 units base 1e6 + let miden_amount = Felt::new(100_000_000); + + // scale to 1e18 + let target_scale = Felt::new(12); + + let asset_conversion_lib = asset_conversion_library(); + + let script_code = format!( + " + use.std::sys + + begin + push.{}.{} + exec.::scale_native_amount_to_u256 + exec.sys::truncate_stack + end + ", + target_scale, miden_amount, + ); + + let program = Assembler::new(Arc::new(DefaultSourceManager::default())) + .with_debug_mode(true) + .with_dynamic_library(StdLibrary::default()) + .unwrap() + .with_dynamic_library(asset_conversion_lib.clone()) + .unwrap() + .assemble_program(&script_code) + .unwrap(); + + let exec_output = execute_program_with_default_host(program).await?; + + let expected_result = U256::from_dec_str("100000000000000000000").unwrap(); + let actual_result = stack_to_u256(&exec_output); + + assert_eq!(actual_result, expected_result); + + Ok(()) +} + +#[tokio::test] +async fn test_convert_to_u256_scaled_large_amount() -> anyhow::Result<()> { + // 100,000,000 units (base 1e10) + let miden_amount = Felt::new(1000000000000000000); + + // scale to base 1e18 + let scale_exponent = Felt::new(8); + + let asset_conversion_lib = asset_conversion_library(); + + let script_code = format!( + " + use.std::sys + + begin + push.{}.{} + + exec.::scale_native_amount_to_u256 + exec.sys::truncate_stack + end + ", + scale_exponent, miden_amount, + ); + + let program = Assembler::new(Arc::new(DefaultSourceManager::default())) + .with_debug_mode(true) + .with_dynamic_library(StdLibrary::default()) + .unwrap() + .with_dynamic_library(asset_conversion_lib.clone()) + .unwrap() + .assemble_program(&script_code) + .unwrap(); + + let exec_output = execute_program_with_default_host(program).await?; + + let expected_result = U256::from_dec_str("100000000000000000000000000").unwrap(); + let actual_result = stack_to_u256(&exec_output); + + assert_eq!(actual_result, expected_result); + + Ok(()) +} + +#[test] +fn test_felts_to_u256_bytes_sequential_values() { + let limbs = [ + Felt::new(1), + Felt::new(2), + Felt::new(3), + Felt::new(4), + Felt::new(5), + Felt::new(6), + Felt::new(7), + Felt::new(8), + ]; + let result = utils::felts_to_u256_bytes(limbs); + assert_eq!(result.len(), 32); + + // Verify the byte layout: limbs are processed in little-endian order, each as little-endian u32 + // First byte should be 1 (limbs[0] = 1, least significant limb, least significant byte) + assert_eq!(result[0], 1); + // Byte at position 28 should be 8 (limbs[7] = 8, most significant limb, least significant + // byte) + assert_eq!(result[28], 8); +} + +#[test] +fn test_felts_to_u256_bytes_edge_cases() { + // Test case 1: All zeros (minimum) + let limbs = [Felt::new(0); 8]; + let result = utils::felts_to_u256_bytes(limbs); + assert_eq!(result.len(), 32); + assert!(result.iter().all(|&b| b == 0)); + + // Test case 2: All max u32 values (maximum) + let limbs = [Felt::new(u32::MAX as u64); 8]; + let result = utils::felts_to_u256_bytes(limbs); + assert_eq!(result.len(), 32); + assert!(result.iter().all(|&b| b == 255)); +} diff --git a/crates/miden-testing/tests/agglayer/mod.rs b/crates/miden-testing/tests/agglayer/mod.rs index 064331ee01..72354aa6c9 100644 --- a/crates/miden-testing/tests/agglayer/mod.rs +++ b/crates/miden-testing/tests/agglayer/mod.rs @@ -1 +1,2 @@ +pub mod asset_conversion; mod bridge_out; From 81c2b166b136cdb055bf97d29154f869dc70353a Mon Sep 17 00:00:00 2001 From: Farukest Date: Mon, 15 Dec 2025 21:56:14 +0300 Subject: [PATCH 066/114] refactor: add `SlotName` to `StorageSlotHeader` (#2160) --- CHANGELOG.md | 2 +- crates/miden-objects/src/account/mod.rs | 1 + .../src/account/storage/header.rs | 218 ++++++++++++------ .../miden-objects/src/account/storage/mod.rs | 27 +-- .../src/account/storage/partial.rs | 4 +- .../src/account/storage/slot/slot_name.rs | 13 +- crates/miden-tx/src/host/mod.rs | 4 +- .../src/host/storage_delta_tracker.rs | 39 ++-- crates/miden-tx/src/host/tx_event.rs | 16 +- 9 files changed, 193 insertions(+), 131 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 337d256511..e5ddb467ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). +- [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2160](https://github.com/0xMiden/miden-base/pull/2160), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). - [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). - Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 83d7bf3b55..43ca9f8680 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -74,6 +74,7 @@ pub use storage::{ StorageMapWitness, StorageSlot, StorageSlotContent, + StorageSlotHeader, StorageSlotId, StorageSlotName, StorageSlotType, diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-objects/src/account/storage/header.rs index 1e43c35ac0..8f48bfe646 100644 --- a/crates/miden-objects/src/account/storage/header.rs +++ b/crates/miden-objects/src/account/storage/header.rs @@ -1,6 +1,7 @@ use alloc::string::ToString; use alloc::vec::Vec; +use super::map::EMPTY_STORAGE_MAP_ROOT; use super::{AccountStorage, Felt, StorageSlotType, Word}; use crate::account::{StorageSlot, StorageSlotId, StorageSlotName}; use crate::crypto::SequentialCommit; @@ -16,44 +17,6 @@ use crate::{AccountError, FieldElement, ZERO}; // ACCOUNT STORAGE HEADER // ================================================================================================ -/// The header of a [`StorageSlot`], storing only the slot ID, slot type and value of the slot. -/// -/// The stored value differs based on the slot type: -/// - [`StorageSlotType::Value`]: The value of the slot itself. -/// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct StorageSlotHeader { - id: StorageSlotId, - r#type: StorageSlotType, - value: Word, -} - -impl StorageSlotHeader { - /// Returns a new instance of storage slot header from the provided storage slot ID, type and - /// value. - pub(crate) fn new(id: StorageSlotId, r#type: StorageSlotType, value: Word) -> Self { - Self { id, r#type, value } - } - - /// Returns this storage slot header as field elements. - /// - /// This is done by converting this storage slot into 8 field elements as follows: - /// ```text - /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE] - /// ``` - pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS] { - let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS]; - elements[0..4].copy_from_slice(&[ - Felt::ZERO, - self.r#type.as_felt(), - self.id.suffix(), - self.id.prefix(), - ]); - elements[4..8].copy_from_slice(self.value.as_elements()); - elements - } -} - /// The header of an [`AccountStorage`], storing only the slot name, slot type and value of each /// storage slot. /// @@ -62,7 +25,7 @@ impl StorageSlotHeader { /// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AccountStorageHeader { - slots: Vec<(StorageSlotName, StorageSlotType, Word)>, + slots: Vec, } impl AccountStorageHeader { @@ -77,39 +40,60 @@ impl AccountStorageHeader { /// - The number of provided slots is greater than [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. /// - The slots are not sorted by [`StorageSlotId`]. /// - There are multiple storage slots with the same [`StorageSlotName`]. - pub fn new(slots: Vec<(StorageSlotName, StorageSlotType, Word)>) -> Result { + pub fn new(slots: Vec) -> Result { if slots.len() > AccountStorage::MAX_NUM_STORAGE_SLOTS { return Err(AccountError::StorageTooManySlots(slots.len() as u64)); } - if !slots.is_sorted_by_key(|(slot_name, ..)| slot_name.id()) { + if !slots.is_sorted_by_key(|slot| slot.id()) { return Err(AccountError::UnsortedStorageSlots); } // Check for slot name uniqueness by checking each neighboring slot's IDs. This is // sufficient because the slots are sorted. for slots in slots.windows(2) { - if slots[0].0.id() == slots[1].0.id() { - return Err(AccountError::DuplicateStorageSlotName(slots[0].0.clone())); + if slots[0].id() == slots[1].id() { + return Err(AccountError::DuplicateStorageSlotName(slots[0].name().clone())); } } Ok(Self { slots }) } + /// Returns a new instance of account storage header initialized with the provided slot tuples. + /// + /// This is a convenience method that converts tuples to [`StorageSlotHeader`]s. + /// + /// # Errors + /// + /// Returns an error if: + /// - The number of provided slots is greater than [`AccountStorage::MAX_NUM_STORAGE_SLOTS`]. + /// - The slots are not sorted by [`StorageSlotId`]. + #[cfg(any(feature = "testing", test))] + pub fn from_tuples( + slots: Vec<(StorageSlotName, StorageSlotType, Word)>, + ) -> Result { + let slots = slots + .into_iter() + .map(|(name, slot_type, value)| StorageSlotHeader::new(name, slot_type, value)) + .collect(); + + Self::new(slots) + } + // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- /// Returns an iterator over the storage header slots. - pub fn slots(&self) -> impl Iterator { - self.slots.iter().map(|(name, r#type, value)| (name, r#type, value)) + pub fn slots(&self) -> impl Iterator { + self.slots.iter() } /// Returns an iterator over the storage header map slots. - pub fn map_slot_roots(&self) -> impl Iterator { - self.slots.iter().filter_map(|(_, slot_type, value)| match slot_type { + pub fn map_slot_roots(&self) -> impl Iterator + '_ { + self.slots.iter().filter_map(|slot| match slot.slot_type() { StorageSlotType::Value => None, - StorageSlotType::Map => Some(*value), + StorageSlotType::Map => Some(slot.value()), }) } @@ -119,31 +103,21 @@ impl AccountStorageHeader { self.slots.len() as u8 } - /// Returns a slot contained in the storage header at a given index. + /// Returns the storage slot header for the slot with the given name. /// - /// Returns `None` if a slot with the provided slot ID does not exist. + /// Returns `None` if a slot with the provided name does not exist. pub fn find_slot_header_by_name( &self, slot_name: &StorageSlotName, - ) -> Option<(&StorageSlotType, &Word)> { + ) -> Option<&StorageSlotHeader> { self.find_slot_header_by_id(slot_name.id()) - .map(|(_slot_name, slot_type, slot_value)| (slot_type, slot_value)) } - /// Returns a slot contained in the storage header at a given index. + /// Returns the storage slot header for the slot with the given ID. /// /// Returns `None` if a slot with the provided slot ID does not exist. - pub fn find_slot_header_by_id( - &self, - slot_id: StorageSlotId, - ) -> Option<(&StorageSlotName, &StorageSlotType, &Word)> { - self.slots.iter().find_map(|(slot_name, slot_type, slot_value)| { - if slot_name.id() == slot_id { - Some((slot_name, slot_type, slot_value)) - } else { - None - } - }) + pub fn find_slot_header_by_id(&self, slot_id: StorageSlotId) -> Option<&StorageSlotHeader> { + self.slots.iter().find(|slot| slot.id() == slot_id) } /// Indicates whether the slot with the given `name` is a map slot. @@ -156,7 +130,7 @@ impl AccountStorageHeader { match self .find_slot_header_by_name(name) .ok_or(AccountError::StorageSlotNameNotFound { slot_name: name.clone() })? - .0 + .slot_type() { StorageSlotType::Map => Ok(true), StorageSlotType::Value => Ok(false), @@ -195,11 +169,7 @@ impl SequentialCommit for AccountStorageHeader { type Commitment = Word; fn to_elements(&self) -> Vec { - self.slots() - .flat_map(|(slot_name, slot_type, slot_value)| { - StorageSlotHeader::new(slot_name.id(), *slot_type, *slot_value).to_elements() - }) - .collect() + self.slots().flat_map(|slot| slot.to_elements()).collect() } } @@ -217,11 +187,115 @@ impl Serializable for AccountStorageHeader { impl Deserializable for AccountStorageHeader { fn read_from(source: &mut R) -> Result { let len = source.read_u8()?; - let slots = source.read_many(len as usize)?; + let slots: Vec = source.read_many(len as usize)?; Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } +// STORAGE SLOT HEADER +// ================================================================================================ + +/// The header of a [`StorageSlot`], storing only the slot name (or ID), slot type and value of the +/// slot. +/// +/// The stored value differs based on the slot type: +/// - [`StorageSlotType::Value`]: The value of the slot itself. +/// - [`StorageSlotType::Map`]: The root of the SMT that represents the storage map. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StorageSlotHeader { + name: StorageSlotName, + r#type: StorageSlotType, + value: Word, +} + +impl StorageSlotHeader { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Returns a new instance of storage slot header. + pub fn new(name: StorageSlotName, r#type: StorageSlotType, value: Word) -> Self { + Self { name, r#type, value } + } + + /// Returns a new instance of storage slot header with an empty value slot. + pub fn with_empty_value(name: StorageSlotName) -> StorageSlotHeader { + StorageSlotHeader::new(name, StorageSlotType::Value, Word::default()) + } + + /// Returns a new instance of storage slot header with an empty map slot. + pub fn with_empty_map(name: StorageSlotName) -> StorageSlotHeader { + StorageSlotHeader::new(name, StorageSlotType::Map, EMPTY_STORAGE_MAP_ROOT) + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns a reference to the slot name. + pub fn name(&self) -> &StorageSlotName { + &self.name + } + + /// Returns the slot ID. + pub fn id(&self) -> StorageSlotId { + self.name.id() + } + + /// Returns the slot type. + pub fn slot_type(&self) -> StorageSlotType { + self.r#type + } + + /// Returns the slot value. + pub fn value(&self) -> Word { + self.value + } + + /// Returns this storage slot header as field elements. + /// + /// This is done by converting this storage slot into 8 field elements as follows: + /// ```text + /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE] + /// ``` + pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS] { + let id = self.id(); + let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS]; + elements[0..4].copy_from_slice(&[ + Felt::ZERO, + self.r#type.as_felt(), + id.suffix(), + id.prefix(), + ]); + elements[4..8].copy_from_slice(self.value.as_elements()); + elements + } +} + +impl From<&StorageSlot> for StorageSlotHeader { + fn from(slot: &StorageSlot) -> Self { + StorageSlotHeader::new(slot.name().clone(), slot.slot_type(), slot.value()) + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for StorageSlotHeader { + fn write_into(&self, target: &mut W) { + self.name.write_into(target); + self.r#type.write_into(target); + self.value.write_into(target); + } +} + +impl Deserializable for StorageSlotHeader { + fn read_from(source: &mut R) -> Result { + let name = StorageSlotName::read_from(source)?; + let slot_type = StorageSlotType::read_from(source)?; + let value = Word::read_from(source)?; + Ok(Self::new(name, slot_type, value)) + } +} + // TESTS // ================================================================================================ @@ -251,7 +325,7 @@ mod tests { ]; slots.sort_unstable_by_key(|(slot_name, ..)| slot_name.id()); - let expected_header = AccountStorageHeader { slots }; + let expected_header = AccountStorageHeader::from_tuples(slots).unwrap(); let account_storage = AccountStorage::mock(); assert_eq!(expected_header, AccountStorageHeader::from(&account_storage)) diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index 7aff0806c7..23c4aa9efe 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -12,7 +12,6 @@ use super::{ Serializable, Word, }; -use crate::account::storage::header::StorageSlotHeader; use crate::account::{AccountComponent, AccountType}; use crate::crypto::SequentialCommit; use crate::utils::sync::LazyLock; @@ -24,7 +23,7 @@ mod map; pub use map::{PartialStorageMap, StorageMap, StorageMapWitness}; mod header; -pub use header::AccountStorageHeader; +pub use header::{AccountStorageHeader, StorageSlotHeader}; mod partial; pub use partial::PartialStorage; @@ -180,15 +179,8 @@ impl AccountStorage { /// Returns an [AccountStorageHeader] for this account storage. pub fn to_header(&self) -> AccountStorageHeader { - AccountStorageHeader::new( - self.slots - .iter() - .map(|slot| { - (slot.name().clone(), slot.content().slot_type(), slot.content().value()) - }) - .collect(), - ) - .expect("slots should be valid as ensured by AccountStorage") + AccountStorageHeader::new(self.slots.iter().map(StorageSlotHeader::from).collect()) + .expect("slots should be valid as ensured by AccountStorage") } /// Returns a reference to the storage slot with the provided name, if it exists, `None` @@ -352,7 +344,7 @@ impl SequentialCommit for AccountStorage { .iter() .flat_map(|slot| { StorageSlotHeader::new( - slot.id(), + slot.name().clone(), slot.content().slot_type(), slot.content().value(), ) @@ -402,7 +394,7 @@ mod tests { use super::{AccountStorage, Deserializable, Serializable}; use crate::AccountError; - use crate::account::{AccountStorageHeader, StorageSlot, StorageSlotName}; + use crate::account::{AccountStorageHeader, StorageSlot, StorageSlotHeader, StorageSlotName}; #[test] fn test_serde_account_storage() -> anyhow::Result<()> { @@ -462,13 +454,8 @@ mod tests { }); slots.sort_unstable(); - let err = AccountStorageHeader::new( - slots - .iter() - .map(|slot| (slot.name().clone(), slot.slot_type(), slot.value())) - .collect(), - ) - .unwrap_err(); + let err = AccountStorageHeader::new(slots.iter().map(StorageSlotHeader::from).collect()) + .unwrap_err(); assert_matches!(err, AccountError::DuplicateStorageSlotName(name) => { assert_eq!(name, slot_name0); diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-objects/src/account/storage/partial.rs index c44d752704..755f867d4e 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-objects/src/account/storage/partial.rs @@ -190,8 +190,8 @@ mod tests { PartialStorage::new(storage_header, [PartialStorageMap::with_witnesses([witness])?]) .context("creating partial storage")?; - let (_, map_root) = partial_storage.header.find_slot_header_by_name(&slot_name).unwrap(); - let retrieved_map = partial_storage.maps.get(map_root).unwrap(); + let slot_header = partial_storage.header.find_slot_header_by_name(&slot_name).unwrap(); + let retrieved_map = partial_storage.maps.get(&slot_header.value()).unwrap(); assert!(retrieved_map.open(&map_key_absent).is_err()); assert!(retrieved_map.open(&map_key_present).is_ok()); Ok(()) diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-objects/src/account/storage/slot/slot_name.rs index b431c26b59..27d0c7ce45 100644 --- a/crates/miden-objects/src/account/storage/slot/slot_name.rs +++ b/crates/miden-objects/src/account/storage/slot/slot_name.rs @@ -1,4 +1,5 @@ use alloc::string::{String, ToString}; +use alloc::sync::Arc; use core::fmt::Display; use core::str::FromStr; @@ -35,7 +36,7 @@ use crate::utils::serde::{ByteWriter, Deserializable, DeserializationError, Seri /// - Each component must not start with an underscore. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StorageSlotName { - name: String, + name: Arc, id: StorageSlotId, } @@ -58,10 +59,10 @@ impl StorageSlotName { /// /// Returns an error if: /// - the slot name is invalid (see the type-level docs for the requirements). - pub fn new(name: impl Into) -> Result { - let name = name.into(); + pub fn new(name: impl Into>) -> Result { + let name: Arc = name.into(); Self::validate(&name)?; - let id = StorageSlotId::from_str(name.as_str()); + let id = StorageSlotId::from_str(&name); Ok(Self { name, id }) } @@ -211,7 +212,7 @@ impl FromStr for StorageSlotName { impl From for String { fn from(slot_name: StorageSlotName) -> Self { - slot_name.name + slot_name.name.to_string() } } @@ -352,7 +353,7 @@ mod tests { #[test] fn slot_name_allows_ascii_alphanumeric_and_underscore() -> anyhow::Result<()> { let name = format!("{FULL_ALPHABET}::second"); - let slot_name = StorageSlotName::new(&name)?; + let slot_name = StorageSlotName::new(name.clone())?; assert_eq!(slot_name.as_str(), name); Ok(()) diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index fcd1b61236..fc39d3b517 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -36,9 +36,9 @@ use miden_objects::account::{ AccountId, AccountStorageHeader, PartialAccount, + StorageSlotHeader, StorageSlotId, StorageSlotName, - StorageSlotType, }; use miden_objects::asset::Asset; use miden_objects::note::{NoteId, NoteMetadata, NoteRecipient}; @@ -165,7 +165,7 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { pub fn initial_account_storage_slot( &self, slot_id: StorageSlotId, - ) -> Result<(&StorageSlotName, &StorageSlotType, &Word), TransactionKernelError> { + ) -> Result<&StorageSlotHeader, TransactionKernelError> { self.initial_account_storage_header() .find_slot_header_by_id(slot_id) .ok_or_else(|| { diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index d6e413ff29..5e8b5c4f03 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -6,7 +6,7 @@ use miden_objects::account::{ AccountStorageDelta, AccountStorageHeader, PartialAccount, - StorageMap, + StorageSlotHeader, StorageSlotName, StorageSlotType, }; @@ -60,36 +60,37 @@ impl StorageDeltaTracker { // Insert account storage into delta if it is new to match the kernel behavior. if account.is_new() { - account - .storage() - .header() - .slots() - .for_each(|(slot_name, slot_type, slot_value)| match slot_type { + account.storage().header().slots().for_each(|slot_header| { + match slot_header.slot_type() { StorageSlotType::Value => { // For new accounts, all values should be added to the delta, even empty // words, so that the final delta includes the storage slot. - storage_delta_tracker.set_item(slot_name.clone(), *slot_value); + storage_delta_tracker + .set_item(slot_header.name().clone(), slot_header.value()); }, StorageSlotType::Map => { let storage_map = account .storage() .maps() - .find(|map| map.root() == *slot_value) + .find(|map| map.root() == slot_header.value()) .expect("storage map should be present in partial storage"); // Make sure each map is represented by at least an empty storage map delta. - storage_delta_tracker.delta.insert_empty_map_delta(slot_name.clone()); + storage_delta_tracker + .delta + .insert_empty_map_delta(slot_header.name().clone()); storage_map.entries().for_each(|(key, value)| { storage_delta_tracker.set_map_item( - slot_name.clone(), + slot_header.name().clone(), *key, Word::empty(), *value, ); }); }, - }); + } + }); } storage_delta_tracker @@ -156,10 +157,10 @@ impl StorageDeltaTracker { value_slots.retain(|slot_name, new_value| { // SAFETY: The header in the initial storage is the one from the account against // which the transaction is executed, so accessing that slot name should be fine. - let (_slot_type, initial_value) = storage_header + let slot_header = storage_header .find_slot_header_by_name(slot_name) .expect("slot name should exist"); - new_value != initial_value + *new_value != slot_header.value() }); } @@ -191,17 +192,17 @@ impl StorageDeltaTracker { /// Creates empty slots of the same slot types as the to-be-created account. fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorageHeader { - let slots: Vec<(StorageSlotName, StorageSlotType, Word)> = account + let slots: Vec = account .storage() .header() .slots() - .map(|(slot_name, slot_type, _)| match slot_type { - StorageSlotType::Value => (slot_name.clone(), *slot_type, Word::empty()), - StorageSlotType::Map => (slot_name.clone(), *slot_type, StorageMap::new().root()), + .map(|slot_header| match slot_header.slot_type() { + StorageSlotType::Value => { + StorageSlotHeader::with_empty_value(slot_header.name().clone()) + }, + StorageSlotType::Map => StorageSlotHeader::with_empty_map(slot_header.name().clone()), }) .collect(); - // SAFETY: We are recreating a valid storage header with different values, which should not - // violate any constraints of the storage header. AccountStorageHeader::new(slots).expect("storage header should be valid") } diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index a3f246070e..e62441740e 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -260,8 +260,8 @@ impl TransactionEvent { let (slot_id, slot_type, _old_value) = process.get_storage_slot(slot_ptr)?; - let (slot_name, ..) = base_host.initial_account_storage_slot(slot_id)?; - let slot_name = slot_name.clone(); + let slot_header = base_host.initial_account_storage_slot(slot_id)?; + let slot_name = slot_header.name().clone(); if !slot_type.is_value() { return Err(TransactionKernelError::other(format!( @@ -297,9 +297,8 @@ impl TransactionEvent { // Resolve slot ID to slot name. let (slot_id, ..) = process.get_storage_slot(slot_ptr)?; - let (slot_name, ..) = base_host.initial_account_storage_slot(slot_id)?; - - let slot_name = slot_name.clone(); + let slot_header = base_host.initial_account_storage_slot(slot_id)?; + let slot_name = slot_header.name().clone(); Some(TransactionEvent::AccountStorageAfterSetMapItem { slot_name, @@ -582,15 +581,14 @@ fn on_account_storage_map_item_accessed<'store, STORE>( // For native accounts, we have to request witnesses against the initial // root instead of the _current_ one, since the data // store only has witnesses for initial one. - let (_slot_name, slot_type, slot_value) = - base_host.initial_account_storage_slot(slot_id)?; + let slot_header = base_host.initial_account_storage_slot(slot_id)?; - if *slot_type != StorageSlotType::Map { + if slot_header.slot_type() != StorageSlotType::Map { return Err(TransactionKernelError::other(format!( "expected slot {slot_id} to be of type map" ))); } - *slot_value + slot_header.value() } else { current_map_root }; From 4cf0388daafe271f383cf16e50c033f4b36cca3c Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Tue, 16 Dec 2025 09:01:29 +1300 Subject: [PATCH 067/114] test: move tx header test logic (#2181) --- .../src/transaction/tx_header.rs | 76 ------------------- crates/miden-testing/tests/lib.rs | 5 ++ 2 files changed, 5 insertions(+), 76 deletions(-) diff --git a/crates/miden-objects/src/transaction/tx_header.rs b/crates/miden-objects/src/transaction/tx_header.rs index 9a86a9f0b9..4b7adafad6 100644 --- a/crates/miden-objects/src/transaction/tx_header.rs +++ b/crates/miden-objects/src/transaction/tx_header.rs @@ -232,79 +232,3 @@ impl Deserializable for TransactionHeader { Ok(tx_header) } } - -#[cfg(test)] -mod tests { - use miden_verifier::ExecutionProof; - - use super::*; - use crate::asset::FungibleAsset; - use crate::transaction::ProvenTransactionBuilder; - - #[test] - fn from_proven_transaction() { - // Build proven transaction. - let account_id = crate::account::AccountId::dummy( - [1; 15], - crate::account::AccountIdVersion::Version0, - crate::account::AccountType::FungibleFaucet, - crate::account::AccountStorageMode::Private, - ); - let initial_commitment = - [2; 32].try_into().expect("failed to create initial account commitment"); - let final_commitment = - [3; 32].try_into().expect("failed to create final account commitment"); - let account_delta_commitment = - [4; 32].try_into().expect("failed to create account delta commitment"); - let block_num = crate::block::BlockNumber::from(1); - let block_ref = Word::empty(); - let fee = FungibleAsset::mock(42).unwrap_fungible(); - let expiration_block_num = crate::block::BlockNumber::from(2); - let proof = ExecutionProof::new_dummy(); - - let proven_tx = ProvenTransactionBuilder::new( - account_id, - initial_commitment, - final_commitment, - account_delta_commitment, - block_num, - block_ref, - fee, - expiration_block_num, - proof, - ) - .build() - .unwrap(); - - // Create header from proven transaction. - let header = TransactionHeader::from(&proven_tx); - - // Verify the header data matches the proven transaction. - assert_eq!(header.id(), proven_tx.id()); - assert_eq!(header.account_id(), proven_tx.account_id()); - assert_eq!( - header.initial_state_commitment(), - proven_tx.account_update().initial_state_commitment() - ); - assert_eq!( - header.final_state_commitment(), - proven_tx.account_update().final_state_commitment() - ); - assert_eq!(header.fee(), proven_tx.fee()); - - assert_eq!(header.input_notes().iter().count(), proven_tx.input_notes().iter().count()); - - for (header_note, tx_note) in - header.input_notes().iter().zip(proven_tx.input_notes().iter()) - { - assert_eq!(header_note, tx_note); - } - - assert_eq!(header.output_notes().len(), proven_tx.output_notes().iter().count()); - for (header_note, tx_note) in - header.output_notes().iter().zip(proven_tx.output_notes().iter()) - { - assert_eq!(*header_note, crate::note::NoteHeader::from(tx_note)); - } - } -} diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index cb3546e9ad..2171d2dc5b 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -28,14 +28,19 @@ use miden_tx::{ pub fn prove_and_verify_transaction( executed_transaction: ExecutedTransaction, ) -> Result<(), TransactionVerifierError> { + use miden_objects::transaction::TransactionHeader; + let executed_transaction_id = executed_transaction.id(); + let executed_tx_header = TransactionHeader::from(&executed_transaction); // Prove the transaction let proof_options = ProvingOptions::default(); let prover = LocalTransactionProver::new(proof_options); let proven_transaction = prover.prove(executed_transaction).unwrap(); + let proven_tx_header = TransactionHeader::from(&proven_transaction); assert_eq!(proven_transaction.id(), executed_transaction_id); + assert_eq!(proven_tx_header, executed_tx_header); // Serialize & deserialize the ProvenTransaction let serialised_transaction = proven_transaction.to_bytes(); From 2dff616cf907af134bf86f81158b167115818d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= <4142+huitseeker@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:40:02 -0800 Subject: [PATCH 068/114] ci: improve dependency control & runner disk space (#2133) * feat: add cargo-deny security and license checking - Add cargo-deny job to lint.yml workflow for dependency security checking - Create deny.toml configuration with appropriate license allowances and security ignores - Add cleanup-runner GitHub Action to free disk space in CI workflows Configures automated detection of: - Security vulnerabilities in dependencies - License compliance issues - Duplicate dependency versions - Unknown or untrusted crate sources * ci: add cleanup steps to existing workflows Add cleanup-runner action to existing CI workflows to free disk space: - build.yml: added to no-std, feature-check, and build-benches jobs - msrv.yml: added to msrv job - test.yml: added to test and doc-tests jobs This prevents build failures due to insufficient disk space on GitHub runners by removing unused tools like dotnet, android SDK, GHC, and CodeQL. * feat: add comprehensive feature combination checking Add check-features job to validate all workspace feature combinations: - Create scripts/check-features.sh with cargo-hack integration - Add check-features target to Makefile for local development - Add check-features job to test.yml CI workflow Features validated: - miden-objects: std, testing features - miden-tx: concurrent, std, testing features - miden-testing: std feature - miden-block-prover: testing feature - miden-tx-batch-prover: std, testing features - miden-lib: all-features (build dependency aware) Prevents regressions in feature matrix and ensures all combinations compile without warnings using RUSTFLAGS="-D warnings". * refactor: optimize cargo-deny configuration - Remove unnecessary skip-tree entries and verify all are required - Confirm all license allowances are actually encountered in dependency tree - Improve comments to be more specific about exemption reasons - Maintain security coverage while reducing configuration complexity Verified that all 8 skip-tree entries and 8 license allowances are genuinely needed. Configuration is now optimally minimal while maintaining compatibility. * docs: add detailed comment for miden-lib testing exception Explain why miden-lib gets special treatment in check-features.sh: - Build script conditionally generates kernel_library.masl only with testing feature - This creates backwards dependency where testing enables build script generation - Without testing, compilation fails due to missing generated files - TODO indicates proper fix: always generate libraries, conditionally use them * fix: improve miden-lib feature checking and cleanup CI - Add self-dev-dependency trick to auto-enable testing feature for miden-lib - Remove special case for miden-lib in check-features.sh script - Remove superseded feature-check job from build.yml workflow - Add missing trailing newlines to action.yml and check-features.sh The self-dev-dependency trick resolves the issue where cargo check with --all-targets failed for miden-lib when --no-default-features was used. This enables the testing feature automatically when the crate is built as a dev-dependency (e.g., during testing or when using --all-targets). Fixes cargo check -p miden-lib --no-default-features --all-targets --- .github/actions/cleanup-runner/action.yml | 12 ++++ .github/workflows/build.yml | 27 ++++---- .github/workflows/lint.yml | 21 ++++++ .github/workflows/msrv.yml | 2 + .github/workflows/test.yml | 4 ++ Cargo.lock | 1 + Makefile | 5 ++ crates/miden-lib/Cargo.toml | 5 ++ crates/miden-objects/src/account/code/mod.rs | 4 +- deny.toml | 69 ++++++++++++++++++++ deny.toml~ | 69 ++++++++++++++++++++ scripts/check-features.sh | 23 +++++++ 12 files changed, 228 insertions(+), 14 deletions(-) create mode 100644 .github/actions/cleanup-runner/action.yml create mode 100644 deny.toml create mode 100644 deny.toml~ create mode 100755 scripts/check-features.sh diff --git a/.github/actions/cleanup-runner/action.yml b/.github/actions/cleanup-runner/action.yml new file mode 100644 index 0000000000..22edac443f --- /dev/null +++ b/.github/actions/cleanup-runner/action.yml @@ -0,0 +1,12 @@ +name: 'Cleanup Runner' +description: 'Remove unused tools in the runner image to free disk space' + +runs: + using: 'composite' + steps: + - name: Remove unused tools in the runner image + shell: bash + run: | + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force || true + sudo docker builder prune -a --force || true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 772d3cbc8c..8da873adce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: Swatinem/rust-cache@v2 with: # Only update the cache on push onto the next branch. This strikes a nice balance between @@ -34,31 +36,30 @@ jobs: make build-no-std make build-no-std-testing - feature-check: - name: Check feature combinations + check-features: + name: check all feature combinations runs-on: ubuntu-latest steps: - - uses: actions/checkout@main + - uses: actions/checkout@v4 + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: Swatinem/rust-cache@v2 with: - # Only update the cache on push onto the next branch. This strikes a nice balance between - # cache hits and cache evictions (github has a 10GB cache limit). save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/next' }} - # Install cargo-hack or restore from cache if already built. - - uses: taiki-e/cache-cargo-install-action@v2 - with: - tool: cargo-hack - - name: Update Rust toolchain + - name: Install rust run: rustup update --no-self-update - # Run cargo hack check with each feature to ensure crates compile with individual features - - name: Check each feature - run: cargo hack check --each-feature --workspace --exclude bench-prover + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - name: Check all feature combinations + run: make check-features build-benches: name: Check benchmarks compilation runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: Swatinem/rust-cache@v2 with: # Only update the cache on push onto the next branch. This strikes a nice balance between diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 273b129312..c7ffceecce 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,6 +17,17 @@ permissions: contents: read jobs: + cargo-deny: + name: cargo-deny on ubuntu-latest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/install-action@v2 + with: + tool: cargo-deny + - name: Check dependencies with cargo-deny + run: cargo deny check + typos: name: spellcheck runs-on: ubuntu-latest @@ -44,6 +55,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: Swatinem/rust-cache@v2 with: # Only update the cache on push onto the next branch. This strikes a nice balance between @@ -60,6 +73,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: Swatinem/rust-cache@v2 with: # Only update the cache on push onto the next branch. This strikes a nice balance between @@ -77,6 +92,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: Swatinem/rust-cache@v2 with: # Only update the cache on push onto the next branch. This strikes a nice balance between @@ -93,6 +110,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: Swatinem/rust-cache@v2 with: # Only update the cache on push onto the next branch. This strikes a nice balance between @@ -108,6 +127,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - name: Rustup run: rustup update --no-self-update - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index 60b81bfad6..aa9b9d6f79 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -20,6 +20,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y jq - uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 483daf8f7e..f08787e669 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 with: @@ -38,6 +40,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner - uses: Swatinem/rust-cache@v2 with: save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/next' }} diff --git a/Cargo.lock b/Cargo.lock index e6a494dbdf..52914dd77c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1547,6 +1547,7 @@ dependencies = [ "fs-err", "miden-assembly", "miden-core", + "miden-lib", "miden-objects", "miden-processor", "miden-stdlib", diff --git a/Makefile b/Makefile index c506650d8e..6f67df4e17 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,11 @@ check: ## Check all targets and features for errors without code generation check-no-std: ## Check the no-std target without any features for errors without code generation $(BUILD_GENERATED_FILES_IN_SRC) cargo check --no-default-features --target wasm32-unknown-unknown --workspace --lib + +.PHONY: check-features +check-features: ## Checks all feature combinations compile without warnings using cargo-hack + @scripts/check-features.sh + # --- building ------------------------------------------------------------------------------------ .PHONY: build diff --git a/crates/miden-lib/Cargo.toml b/crates/miden-lib/Cargo.toml index fe845f123a..325f64deb1 100644 --- a/crates/miden-lib/Cargo.toml +++ b/crates/miden-lib/Cargo.toml @@ -44,3 +44,8 @@ anyhow = "1.0" assert_matches = { workspace = true } miden-objects = { features = ["testing"], workspace = true } miden-processor = { features = ["testing"], workspace = true } + +# When building as a dev-dependency (e.g., `cargo test --workspace` or `cargo check --all-targets`), +# enable the `testing` feature. This is a workaround for Cargo's lack of test-specific features. +# See: https://github.com/rust-lang/cargo/issues/2911#issuecomment-1483256987 +miden-lib = { features = ["testing"], path = "." } diff --git a/crates/miden-objects/src/account/code/mod.rs b/crates/miden-objects/src/account/code/mod.rs index 39f58e897c..a548921b5a 100644 --- a/crates/miden-objects/src/account/code/mod.rs +++ b/crates/miden-objects/src/account/code/mod.rs @@ -15,7 +15,9 @@ use super::{ Serializable, }; use crate::Word; -use crate::account::{AccountComponent, AccountType}; +use crate::account::AccountComponent; +#[cfg(any(feature = "testing", test))] +use crate::account::AccountType; pub mod procedure; use procedure::{AccountProcedureRoot, PrintableProcedure}; diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000000..799128b16d --- /dev/null +++ b/deny.toml @@ -0,0 +1,69 @@ +# cargo-deny configuration, see https://github.com/EmbarkStudios/cargo-deny + +# Graph configuration +[graph] +targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"] + +# Advisory database configuration +[advisories] +db-path = "~/.cargo/advisory-db" +db-urls = ["https://github.com/rustsec/advisory-db"] +ignore = [ + "RUSTSEC-2024-0436", # paste is unmaintained but no alternative available + "RUSTSEC-2025-0055", # tracing-subscriber vulnerability - will be fixed by upgrade + "RUSTSEC-2025-0056", # adler is unmaintained but used by miniz_oxide +] +yanked = "warn" + +# License configuration +[licenses] +allow = [ + "Apache-2.0 WITH LLVM-exception", + "Apache-2.0", + "BSD-2-Clause", + "BSD-3-Clause", + "ISC", + "MIT", + "Unicode-3.0", + "Zlib", +] +exceptions = [ + # Each entry is the crate and version constraint, and its specific allowed licenses + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +# Bans configuration +[bans] +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +deny = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +highlight = "all" +multiple-versions = "deny" +skip = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +skip-tree = [ + # Allow getrandom v0.2.x - legacy version used by nanorand + { name = "getrandom", version = "=0.2.*" }, + # Allow rand_core v0.6.x - legacy version used by winterfell crates + { name = "rand_core", version = "=0.6.*" }, + # Allow rustc_version v0.2.x - build dependency version + { name = "rustc_version", version = "=0.2.*" }, + # Allow unicode-width v0.1.x - used by miden-formatting vs textwrap conflict + { name = "unicode-width", version = "=0.1.*" }, + # Allow windows-targets v0.48.x - older Windows target version + { name = "windows-targets", version = "=0.48.*" }, + # Allow windows-sys v0.48.x/v0.59.x - multiple Windows system libraries + { name = "windows-sys", version = "=0.48.*" }, + { name = "windows-sys", version = "=0.59.*" }, +] +wildcards = "allow" + +# Sources configuration +[sources] +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +unknown-git = "deny" +unknown-registry = "deny" diff --git a/deny.toml~ b/deny.toml~ new file mode 100644 index 0000000000..1d91acb183 --- /dev/null +++ b/deny.toml~ @@ -0,0 +1,69 @@ +# cargo-deny configuration for DeepProve workspace + +# Graph configuration +[graph] +targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"] + +# Advisory database configuration +[advisories] +db-path = "~/.cargo/advisory-db" +db-urls = ["https://github.com/rustsec/advisory-db"] +ignore = [ + "RUSTSEC-2024-0436", # paste is unmaintained but no alternative available + "RUSTSEC-2025-0055", # tracing-subscriber vulnerability - will be fixed by upgrade + "RUSTSEC-2025-0056", # adler is unmaintained but used by miniz_oxide +] +yanked = "warn" + +# License configuration +[licenses] +allow = [ + "Apache-2.0 WITH LLVM-exception", + "Apache-2.0", + "BSD-2-Clause", + "BSD-3-Clause", + "ISC", + "MIT", + "Unicode-3.0", + "Zlib", +] +exceptions = [ + # Each entry is the crate and version constraint, and its specific allowed licenses + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +# Bans configuration +[bans] +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +deny = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +highlight = "all" +multiple-versions = "deny" +skip = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +skip-tree = [ + # Allow getrandom v0.2.x - legacy version used by nanorand + { name = "getrandom", version = "=0.2.*" }, + # Allow rand_core v0.6.x - legacy version used by winterfell crates + { name = "rand_core", version = "=0.6.*" }, + # Allow rustc_version v0.2.x - build dependency version + { name = "rustc_version", version = "=0.2.*" }, + # Allow unicode-width v0.1.x - used by miden-formatting vs textwrap conflict + { name = "unicode-width", version = "=0.1.*" }, + # Allow windows-targets v0.48.x - older Windows target version + { name = "windows-targets", version = "=0.48.*" }, + # Allow windows-sys v0.48.x/v0.59.x - multiple Windows system libraries + { name = "windows-sys", version = "=0.48.*" }, + { name = "windows-sys", version = "=0.59.*" }, +] +wildcards = "allow" + +# Sources configuration +[sources] +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +unknown-git = "deny" +unknown-registry = "deny" diff --git a/scripts/check-features.sh b/scripts/check-features.sh new file mode 100755 index 0000000000..1aa931c832 --- /dev/null +++ b/scripts/check-features.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -euo pipefail + +# Script to check all feature combinations compile without warnings +# This script ensures that warnings are treated as errors for CI + +echo "Checking all feature combinations with cargo-hack..." + +# Set environment variables to treat warnings as errors +export RUSTFLAGS="-D warnings" + +# Enable file generation in the `src` directory for miden-lib build scripts +export BUILD_GENERATED_FILES_IN_SRC=1 + +# Run cargo-hack with comprehensive feature checking +# Focus on library packages that have significant feature matrices +for package in miden-objects miden-lib miden-tx miden-testing miden-block-prover miden-tx-batch-prover; do + echo "Checking package: $package" + cargo hack check -p "$package" --each-feature --all-targets +done + +echo "All feature combinations compiled successfully!" From 0f1ce8606326e931e360e1096a4aa995bb4f5501 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 17 Dec 2025 08:24:02 +0700 Subject: [PATCH 069/114] chore: refactor `AccountStorageDelta` to use `StorageSlotDelta` (#2182) * feat: Refactor storage delta to use `StorageSlotDelta` * chore: add changelog * fix: error message inconsistencies * chore: add `StorageSlotDelta::merge` * chore: Remove `AccountStorageDeltaBuilder` --- CHANGELOG.md | 1 + crates/miden-objects/src/account/delta/mod.rs | 6 +- .../src/account/delta/storage.rs | 529 +++++++++++------- crates/miden-objects/src/account/mod.rs | 59 +- .../miden-objects/src/account/storage/mod.rs | 4 +- crates/miden-objects/src/errors.rs | 2 +- crates/miden-objects/src/testing/storage.rs | 68 ++- .../src/kernel_tests/tx/test_account.rs | 33 +- .../src/kernel_tests/tx/test_account_delta.rs | 73 ++- .../src/kernel_tests/tx/test_lazy_loading.rs | 10 +- .../src/host/storage_delta_tracker.rs | 83 +-- 11 files changed, 532 insertions(+), 336 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5ddb467ed..baa11c1f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). +- [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-objects/src/account/delta/mod.rs b/crates/miden-objects/src/account/delta/mod.rs index 367cabdc57..178f3a8020 100644 --- a/crates/miden-objects/src/account/delta/mod.rs +++ b/crates/miden-objects/src/account/delta/mod.rs @@ -15,7 +15,7 @@ use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, use crate::{AccountDeltaError, AccountError, Felt, Word, ZERO}; mod storage; -pub use storage::{AccountStorageDelta, StorageMapDelta}; +pub use storage::{AccountStorageDelta, StorageMapDelta, StorageSlotDelta}; mod vault; pub use vault::{ @@ -359,8 +359,8 @@ impl TryFrom<&AccountDelta> for Account { // this to create an empty account and use `Account::apply_delta` instead. // For now, we need to create the initial storage of the account with the same slot types. let mut empty_storage_slots = Vec::new(); - for (slot_name, slot_type) in delta.storage().slots() { - let slot = match slot_type { + for (slot_name, slot_delta) in delta.storage().slots() { + let slot = match slot_delta.slot_type() { StorageSlotType::Value => StorageSlot::with_empty_value(slot_name.clone()), StorageSlotType::Map => StorageSlot::with_empty_map(slot_name.clone()), }; diff --git a/crates/miden-objects/src/account/delta/storage.rs b/crates/miden-objects/src/account/delta/storage.rs index e5d800837a..c8f9a852de 100644 --- a/crates/miden-objects/src/account/delta/storage.rs +++ b/crates/miden-objects/src/account/delta/storage.rs @@ -1,6 +1,5 @@ use alloc::collections::BTreeMap; use alloc::collections::btree_map::Entry; -use alloc::string::ToString; use alloc::vec::Vec; use super::{ @@ -12,127 +11,138 @@ use super::{ Serializable, Word, }; -use crate::account::{StorageMap, StorageSlotName, StorageSlotType}; +use crate::account::{StorageMap, StorageSlotContent, StorageSlotName, StorageSlotType}; use crate::{EMPTY_WORD, Felt, LexicographicWord, ZERO}; // ACCOUNT STORAGE DELTA // ================================================================================================ -/// [AccountStorageDelta] stores the differences between two states of account storage. +/// The [`AccountStorageDelta`] stores the differences between two states of account storage. /// -/// The delta consists of two maps: -/// - A map containing the updates to value storage slots. The keys in this map are indexes of the -/// updated storage slots and the values are the new values for these slots. -/// - A map containing updates to storage maps. The keys in this map are indexes of the updated -/// storage slots and the values are corresponding storage map delta objects. +/// The delta consists of a map from [`StorageSlotName`] to [`StorageSlotDelta`]. #[derive(Clone, Debug, PartialEq, Eq)] pub struct AccountStorageDelta { - /// The updates to the value slots of the account. - values: BTreeMap, - /// The updates to the map slots of the account. - maps: BTreeMap, + /// The updates to the slots of the account. + deltas: BTreeMap, } impl AccountStorageDelta { /// Creates a new, empty storage delta. pub fn new() -> Self { - Self { - values: BTreeMap::new(), - maps: BTreeMap::new(), - } + Self { deltas: BTreeMap::new() } } - /// Creates a new storage delta from the provided fields. - /// - /// # Errors - /// - /// Returns an error if: - /// - Any of the updated slot is referenced from both maps, which means a slot is treated as - /// both a value and a map slot. - pub fn from_parts( - values: BTreeMap, - maps: BTreeMap, - ) -> Result { - let delta = Self { values, maps }; - delta.validate()?; + /// Creates a new storage delta from the provided slot deltas. + pub fn from_raw(deltas: BTreeMap) -> Self { + Self { deltas } + } - Ok(delta) + /// Returns the delta for the provided slot name, or `None` if no delta exists. + pub fn get(&self, slot_name: &StorageSlotName) -> Option<&StorageSlotDelta> { + self.deltas.get(slot_name) } - /// Returns an iterator over the slot names and types tracked by this delta. - pub(crate) fn slots(&self) -> impl Iterator { - self.values() - .keys() - .map(|slot_name| (slot_name, StorageSlotType::Value)) - .chain(self.maps.keys().map(|slot_name| (slot_name, StorageSlotType::Map))) + /// Returns an iterator over the slot deltas. + pub(crate) fn slots(&self) -> impl Iterator { + self.deltas.iter() } - /// Returns a reference to the updated values in this storage delta. - pub fn values(&self) -> &BTreeMap { - &self.values + /// Returns an iterator over the updated values in this storage delta. + pub fn values(&self) -> impl Iterator { + self.deltas.iter().filter_map(|(slot_name, slot_delta)| match slot_delta { + StorageSlotDelta::Value(word) => Some((slot_name, word)), + StorageSlotDelta::Map(_) => None, + }) } - /// Returns a reference to the updated maps in this storage delta. - pub fn maps(&self) -> &BTreeMap { - &self.maps + /// Returns an iterator over the updated maps in this storage delta. + pub fn maps(&self) -> impl Iterator { + self.deltas.iter().filter_map(|(slot_name, slot_delta)| match slot_delta { + StorageSlotDelta::Value(_) => None, + StorageSlotDelta::Map(map_delta) => Some((slot_name, map_delta)), + }) } /// Returns true if storage delta contains no updates. pub fn is_empty(&self) -> bool { - self.values.is_empty() && self.maps.is_empty() + self.deltas.is_empty() } /// Tracks a slot change. /// /// This does not (and cannot) validate that the slot name _exists_ or that it points to a /// _value_ slot in the corresponding account. - pub fn set_item(&mut self, slot_name: StorageSlotName, new_slot_value: Word) { - self.values.insert(slot_name, new_slot_value); + /// + /// # Errors + /// + /// Returns an error if: + /// - the slot name points to an existing slot that is not of type value. + pub fn set_item( + &mut self, + slot_name: StorageSlotName, + new_slot_value: Word, + ) -> Result<(), AccountDeltaError> { + if !self.deltas.get(&slot_name).map(StorageSlotDelta::is_value).unwrap_or(true) { + return Err(AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name)); + } + + self.deltas.insert(slot_name, StorageSlotDelta::Value(new_slot_value)); + + Ok(()) } /// Tracks a map item change. /// /// This does not (and cannot) validate that the slot name _exists_ or that it points to a /// _map_ slot in the corresponding account. - pub fn set_map_item(&mut self, slot_name: StorageSlotName, key: Word, new_value: Word) { - self.maps.entry(slot_name).or_default().insert(key, new_value); + /// + /// # Errors + /// + /// Returns an error if: + /// - the slot name points to an existing slot that is not of type map. + pub fn set_map_item( + &mut self, + slot_name: StorageSlotName, + key: Word, + new_value: Word, + ) -> Result<(), AccountDeltaError> { + match self + .deltas + .entry(slot_name.clone()) + .or_insert(StorageSlotDelta::Map(StorageMapDelta::default())) + { + StorageSlotDelta::Value(_) => { + return Err(AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name)); + }, + StorageSlotDelta::Map(storage_map_delta) => { + storage_map_delta.insert(key, new_value); + }, + }; + + Ok(()) } /// Inserts an empty storage map delta for the provided slot name. /// /// This is useful for full state deltas to represent an empty map in the delta. + /// + /// This overwrites the existing slot delta, if any. pub fn insert_empty_map_delta(&mut self, slot_name: StorageSlotName) { - self.maps.entry(slot_name).or_default(); + self.deltas.insert(slot_name, StorageSlotDelta::with_empty_map()); } /// Merges another delta into this one, overwriting any existing values. pub fn merge(&mut self, other: Self) -> Result<(), AccountDeltaError> { - self.values.extend(other.values); - - // merge maps - for (slot, update) in other.maps.into_iter() { - match self.maps.entry(slot) { - Entry::Vacant(entry) => { - entry.insert(update); + for (slot_name, slot_delta) in other.deltas { + match self.deltas.entry(slot_name.clone()) { + Entry::Vacant(vacant_entry) => { + vacant_entry.insert(slot_delta); + }, + Entry::Occupied(mut occupied_entry) => { + occupied_entry.get_mut().merge(slot_delta).ok_or_else(|| { + AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name) + })?; }, - Entry::Occupied(mut entry) => entry.get_mut().merge(update), - } - } - - self.validate() - } - - /// Checks whether this storage delta is valid. - /// - /// # Errors - /// - /// Returns an error if: - /// - Any of the updated slot is referenced from both maps, which means a slot is treated as - /// both a value and a map slot. - fn validate(&self) -> Result<(), AccountDeltaError> { - for slot in self.maps.keys() { - if self.values.contains_key(slot) { - return Err(AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot.clone())); } } @@ -141,12 +151,22 @@ impl AccountStorageDelta { /// Returns an iterator of all the cleared storage slots. fn cleared_values(&self) -> impl Iterator { - self.values.iter().filter(|&(_, value)| value.is_empty()).map(|(slot, _)| slot) + self.values().filter_map( + |(slot_name, slot_value)| { + if slot_value.is_empty() { Some(slot_name) } else { None } + }, + ) } /// Returns an iterator of all the updated storage slots. fn updated_values(&self) -> impl Iterator { - self.values.iter().filter(|&(_, value)| !value.is_empty()) + self.values().filter_map(|(slot_name, slot_value)| { + if !slot_value.is_empty() { + Some((slot_name, slot_value)) + } else { + None + } + }) } /// Appends the storage slots delta to the given `elements` from which the delta commitment will @@ -155,23 +175,7 @@ impl AccountStorageDelta { const DOMAIN_VALUE: Felt = Felt::new(2); const DOMAIN_MAP: Felt = Felt::new(3); - // Merge slots into a single map to sort them by slot name. - let mut sorted_slots: BTreeMap<&StorageSlotName, StorageSlotDelta> = self - .values() - .iter() - .map(|(name, value)| (name, StorageSlotDelta::Value(*value))) - .collect(); - sorted_slots.extend( - self.maps() - .iter() - .map(|(name, map_delta)| (name, StorageSlotDelta::Map(map_delta))), - ); - - // The storage delta ensures that the value slots and map slots do not have overlapping - // slot names, so the number of entries should be identical after merging. - debug_assert_eq!(sorted_slots.len(), self.values.len() + self.maps.len()); - - for (slot_name, slot_delta) in sorted_slots { + for (slot_name, slot_delta) in self.deltas.iter() { let slot_id = slot_name.id(); match slot_delta { @@ -206,11 +210,9 @@ impl AccountStorageDelta { } } - /// Consumes self and returns the underlying parts of the storage delta. - pub fn into_parts( - self, - ) -> (BTreeMap, BTreeMap) { - (self.values, self.maps) + /// Consumes self and returns the underlying map of the storage delta. + pub fn into_map(self) -> BTreeMap { + self.deltas } } @@ -220,51 +222,40 @@ impl Default for AccountStorageDelta { } } -#[cfg(any(feature = "testing", test))] -impl AccountStorageDelta { - /// Creates an [AccountStorageDelta] from the given iterators. - pub fn from_iters( - cleared_values: impl IntoIterator, - updated_values: impl IntoIterator, - updated_maps: impl IntoIterator, - ) -> Self { - Self { - values: BTreeMap::from_iter( - cleared_values.into_iter().map(|key| (key, EMPTY_WORD)).chain(updated_values), - ), - maps: BTreeMap::from_iter(updated_maps), - } - } -} - impl Serializable for AccountStorageDelta { fn write_into(&self, target: &mut W) { - let num_cleared = self.cleared_values().count(); - let num_cleared = u8::try_from(num_cleared).expect("number of slots should fit in u8"); - let cleared = self.cleared_values(); + let num_cleared_values = self.cleared_values().count(); + let num_cleared_values = + u8::try_from(num_cleared_values).expect("number of slots should fit in u8"); + let cleared_values = self.cleared_values(); + + let num_updated_values = self.updated_values().count(); + let num_updated_values = + u8::try_from(num_updated_values).expect("number of slots should fit in u8"); + let updated_values = self.updated_values(); - let num_updated = self.updated_values().count(); - let num_updated = u8::try_from(num_updated).expect("number of slots should fit in u8"); - let updated = self.updated_values(); + let num_maps = self.maps().count(); + let num_maps = u8::try_from(num_maps).expect("number of slots should fit in u8"); + let maps = self.maps(); - target.write_u8(num_cleared); - target.write_many(cleared); + target.write_u8(num_cleared_values); + target.write_many(cleared_values); - target.write_u8(num_updated); - target.write_many(updated); + target.write_u8(num_updated_values); + target.write_many(updated_values); - target.write_u8(self.maps.len() as u8); - target.write_many(self.maps.iter()); + target.write_u8(num_maps); + target.write_many(maps); } fn get_size_hint(&self) -> usize { let u8_size = 0u8.get_size_hint(); let mut storage_map_delta_size = 0; - for (slot, storage_map_delta) in self.maps.iter() { + for (slot_name, storage_map_delta) in self.maps() { // The serialized size of each entry is the combination of slot (key) and the delta // (value). - storage_map_delta_size += slot.get_size_hint() + storage_map_delta.get_size_hint(); + storage_map_delta_size += slot_name.get_size_hint() + storage_map_delta.get_size_hint(); } // Length Prefixes @@ -282,28 +273,29 @@ impl Serializable for AccountStorageDelta { impl Deserializable for AccountStorageDelta { fn read_from(source: &mut R) -> Result { - let mut values = BTreeMap::new(); + let mut deltas = BTreeMap::new(); - let num_cleared_items = source.read_u8()?; - for _ in 0..num_cleared_items { + let num_cleared_values = source.read_u8()?; + for _ in 0..num_cleared_values { let cleared_value: StorageSlotName = source.read()?; - values.insert(cleared_value, EMPTY_WORD); + deltas.insert(cleared_value, StorageSlotDelta::with_empty_value()); } - let num_updated_items = source.read_u8()?; - for _ in 0..num_updated_items { + let num_updated_values = source.read_u8()?; + for _ in 0..num_updated_values { let (updated_slot, updated_value) = source.read()?; - values.insert(updated_slot, updated_value); + deltas.insert(updated_slot, StorageSlotDelta::Value(updated_value)); } let num_maps = source.read_u8()? as usize; - let maps = source - .read_many::<(StorageSlotName, StorageMapDelta)>(num_maps)? - .into_iter() - .collect(); + deltas.extend( + source + .read_many::<(StorageSlotName, StorageMapDelta)>(num_maps)? + .into_iter() + .map(|(slot_name, map_delta)| (slot_name, StorageSlotDelta::Map(map_delta))), + ); - Self::from_parts(values, maps) - .map_err(|err| DeserializationError::InvalidValue(err.to_string())) + Ok(Self::from_raw(deltas)) } } @@ -311,10 +303,155 @@ impl Deserializable for AccountStorageDelta { // ================================================================================================ /// The delta of a single storage slot. -#[derive(Clone, Debug, PartialEq, Eq)] -enum StorageSlotDelta<'map> { +/// +/// - [`StorageSlotDelta::Value`] contains the value to which a value slot is updated. +/// - [`StorageSlotDelta::Map`] contains the [`StorageMapDelta`] which contains the key-value pairs +/// that were updated in a map slot. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum StorageSlotDelta { Value(Word), - Map(&'map StorageMapDelta), + Map(StorageMapDelta), +} + +impl StorageSlotDelta { + // CONSTANTS + // ---------------------------------------------------------------------------------------- + + /// The type byte for value slot deltas. + const VALUE: u8 = 0; + + /// The type byte for map slot deltas. + const MAP: u8 = 1; + + // CONSTRUCTORS + // ---------------------------------------------------------------------------------------- + + /// Returns a new [`StorageSlotDelta::Value`] with an empty value. + pub fn with_empty_value() -> Self { + Self::Value(Word::empty()) + } + + /// Returns a new [`StorageSlotDelta::Map`] with an empty map delta. + pub fn with_empty_map() -> Self { + Self::Map(StorageMapDelta::default()) + } + + // ACCESSORS + // ---------------------------------------------------------------------------------------- + + /// Returns the [`StorageSlotType`] of this slot delta. + pub fn slot_type(&self) -> StorageSlotType { + match self { + StorageSlotDelta::Value(_) => StorageSlotType::Value, + StorageSlotDelta::Map(_) => StorageSlotType::Map, + } + } + + /// Returns `true` if the slot delta is of type [`StorageSlotDelta::Value`], `false` otherwise. + pub fn is_value(&self) -> bool { + matches!(self, Self::Value(_)) + } + + /// Returns `true` if the slot delta is of type [`StorageSlotDelta::Map`], `false` otherwise. + pub fn is_map(&self) -> bool { + matches!(self, Self::Map(_)) + } + + // MUTATORS + // ---------------------------------------------------------------------------------------- + + /// Unwraps a value slot delta into a [`Word`]. + /// + /// # Panics + /// + /// Panics if: + /// - `self` is not of type [`StorageSlotDelta::Value`]. + pub fn unwrap_value(self) -> Word { + match self { + StorageSlotDelta::Value(value) => value, + StorageSlotDelta::Map(_) => panic!("called unwrap_value on a map slot delta"), + } + } + + /// Unwraps a map slot delta into a [`StorageMapDelta`]. + /// + /// # Panics + /// + /// Panics if: + /// - `self` is not of type [`StorageSlotDelta::Map`]. + pub fn unwrap_map(self) -> StorageMapDelta { + match self { + StorageSlotDelta::Value(_) => panic!("called unwrap_map on a value slot delta"), + StorageSlotDelta::Map(map_delta) => map_delta, + } + } + + /// Merges `other` into `self`. + /// + /// # Errors + /// + /// Returns `None` if: + /// - merging failed due to a slot type mismatch. + #[must_use] + fn merge(&mut self, other: Self) -> Option<()> { + match (self, other) { + (StorageSlotDelta::Value(current_value), StorageSlotDelta::Value(new_value)) => { + *current_value = new_value; + }, + (StorageSlotDelta::Map(current_map_delta), StorageSlotDelta::Map(new_map_delta)) => { + current_map_delta.merge(new_map_delta); + }, + (..) => { + return None; + }, + } + + Some(()) + } +} + +impl From for StorageSlotDelta { + fn from(content: StorageSlotContent) -> Self { + match content { + StorageSlotContent::Value(word) => StorageSlotDelta::Value(word), + StorageSlotContent::Map(storage_map) => { + StorageSlotDelta::Map(StorageMapDelta::from(storage_map)) + }, + } + } +} + +impl Serializable for StorageSlotDelta { + fn write_into(&self, target: &mut W) { + match self { + StorageSlotDelta::Value(value) => { + target.write_u8(Self::VALUE); + target.write(value); + }, + StorageSlotDelta::Map(storage_map_delta) => { + target.write_u8(Self::MAP); + target.write(storage_map_delta); + }, + } + } +} + +impl Deserializable for StorageSlotDelta { + fn read_from(source: &mut R) -> Result { + match source.read_u8()? { + Self::VALUE => { + let value = source.read()?; + Ok(Self::Value(value)) + }, + Self::MAP => { + let map_delta = source.read()?; + Ok(Self::Map(map_delta)) + }, + other => Err(DeserializationError::InvalidValue(format!( + "unknown storage slot delta variant {other}" + ))), + } + } } // STORAGE MAP DELTA @@ -477,54 +614,34 @@ impl Deserializable for StorageMapDelta { #[cfg(test)] mod tests { use anyhow::Context; + use assert_matches::assert_matches; use super::{AccountStorageDelta, Deserializable, Serializable}; - use crate::account::{StorageMapDelta, StorageSlotName}; - use crate::testing::storage::AccountStorageDeltaBuilder; - use crate::{ONE, Word, ZERO}; + use crate::account::{StorageMapDelta, StorageSlotDelta, StorageSlotName}; + use crate::{AccountDeltaError, ONE, Word}; #[test] - fn account_storage_delta_validation() { - let delta = AccountStorageDelta::from_iters( - [StorageSlotName::mock(1), StorageSlotName::mock(2), StorageSlotName::mock(3)], - [ - (StorageSlotName::mock(4), Word::from([ONE, ONE, ONE, ONE])), - (StorageSlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), - ], + fn account_storage_delta_returns_err_on_slot_type_mismatch() { + let value_slot_name = StorageSlotName::mock(1); + let map_slot_name = StorageSlotName::mock(2); + + let mut delta = AccountStorageDelta::from_iters( + [value_slot_name.clone()], [], + [(map_slot_name.clone(), StorageMapDelta::default())], ); - assert!(delta.validate().is_ok()); - - let bytes = delta.to_bytes(); - assert_eq!(AccountStorageDelta::read_from_bytes(&bytes), Ok(delta)); - - // duplicate across cleared items and maps - let delta = AccountStorageDelta::from_iters( - [StorageSlotName::mock(1), StorageSlotName::mock(2), StorageSlotName::mock(3)], - [ - (StorageSlotName::mock(2), Word::from([ONE, ONE, ONE, ONE])), - (StorageSlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), - ], - [(StorageSlotName::mock(1), StorageMapDelta::default())], - ); - assert!(delta.validate().is_err()); - - let bytes = delta.to_bytes(); - assert!(AccountStorageDelta::read_from_bytes(&bytes).is_err()); - - // duplicate across updated items and maps - let delta = AccountStorageDelta::from_iters( - [StorageSlotName::mock(1), StorageSlotName::mock(3)], - [ - (StorageSlotName::mock(2), Word::from([ONE, ONE, ONE, ONE])), - (StorageSlotName::mock(5), Word::from([ONE, ONE, ONE, ZERO])), - ], - [(StorageSlotName::mock(2), StorageMapDelta::default())], - ); - assert!(delta.validate().is_err()); - let bytes = delta.to_bytes(); - assert!(AccountStorageDelta::read_from_bytes(&bytes).is_err()); + let err = delta + .set_map_item(value_slot_name.clone(), Word::empty(), Word::empty()) + .unwrap_err(); + assert_matches!(err, AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name) => { + assert_eq!(value_slot_name, slot_name) + }); + + let err = delta.set_item(map_slot_name.clone(), Word::empty()).unwrap_err(); + assert_matches!(err, AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name) => { + assert_eq!(map_slot_name, slot_name) + }); } #[test] @@ -600,6 +717,36 @@ mod tests { assert_eq!(deserialized, storage_map_delta); } + #[test] + fn test_serde_storage_slot_value_delta() { + let slot_delta = StorageSlotDelta::with_empty_value(); + let serialized = slot_delta.to_bytes(); + let deserialized = StorageSlotDelta::read_from_bytes(&serialized).unwrap(); + assert_eq!(deserialized, slot_delta); + + let slot_delta = StorageSlotDelta::Value(Word::from([1, 2, 3, 4u32])); + let serialized = slot_delta.to_bytes(); + let deserialized = StorageSlotDelta::read_from_bytes(&serialized).unwrap(); + assert_eq!(deserialized, slot_delta); + } + + #[test] + fn test_serde_storage_slot_map_delta() { + let slot_delta = StorageSlotDelta::with_empty_map(); + let serialized = slot_delta.to_bytes(); + let deserialized = StorageSlotDelta::read_from_bytes(&serialized).unwrap(); + assert_eq!(deserialized, slot_delta); + + let map_delta = StorageMapDelta::from_iters( + [Word::from([1, 2, 3, 4u32])], + [(Word::from([5, 6, 7, 8u32]), Word::from([3, 4, 5, 6u32]))], + ); + let slot_delta = StorageSlotDelta::Map(map_delta); + let serialized = slot_delta.to_bytes(); + let deserialized = StorageSlotDelta::read_from_bytes(&serialized).unwrap(); + assert_eq!(deserialized, slot_delta); + } + #[rstest::rstest] #[case::some_some(Some(1), Some(2), Some(2))] #[case::none_some(None, Some(2), Some(2))] @@ -611,20 +758,18 @@ mod tests { #[case] expected: Option, ) -> anyhow::Result<()> { /// Creates a delta containing the item as an update if Some, else with the item cleared. - fn create_delta(item: Option) -> anyhow::Result { + fn create_delta(item: Option) -> AccountStorageDelta { let slot_name = StorageSlotName::mock(123); let item = item.map(|x| (slot_name.clone(), Word::from([x, 0, 0, 0]))); - AccountStorageDeltaBuilder::new() + AccountStorageDelta::new() .add_cleared_items(item.is_none().then_some(slot_name.clone())) .add_updated_values(item) - .build() - .context("failed to build storage delta") } - let mut delta_x = create_delta(x)?; - let delta_y = create_delta(y)?; - let expected = create_delta(expected)?; + let mut delta_x = create_delta(x); + let delta_y = create_delta(y); + let expected = create_delta(expected); delta_x.merge(delta_y).context("failed to merge deltas")?; diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 43ca9f8680..2f38dab713 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -1,9 +1,6 @@ -use alloc::collections::BTreeMap; use alloc::string::ToString; use alloc::vec::Vec; -use miden_core::LexicographicWord; - use crate::asset::{Asset, AssetVault}; use crate::utils::serde::{ ByteReader, @@ -62,6 +59,7 @@ pub use delta::{ NonFungibleAssetDelta, NonFungibleDeltaAction, StorageMapDelta, + StorageSlotDelta, }; mod storage; @@ -433,29 +431,13 @@ impl TryFrom for AccountDelta { return Err(AccountError::DeltaFromAccountWithSeed); } - let mut value_slots = BTreeMap::new(); - let mut map_slots = BTreeMap::new(); - - for slot in storage.into_slots() { - let (slot_name, slot_content) = slot.into_parts(); - match slot_content { - StorageSlotContent::Value(word) => { - value_slots.insert(slot_name, word); - }, - StorageSlotContent::Map(storage_map) => { - let map_delta = StorageMapDelta::new( - storage_map - .into_entries() - .into_iter() - .map(|(key, value)| (LexicographicWord::from(key), value)) - .collect(), - ); - map_slots.insert(slot_name, map_delta); - }, - } - } - let storage_delta = AccountStorageDelta::from_parts(value_slots, map_slots) - .expect("value and map slots from account storage should not overlap"); + let slot_deltas = storage + .into_slots() + .into_iter() + .map(StorageSlot::into_parts) + .map(|(slot_name, slot_content)| (slot_name, StorageSlotDelta::from(slot_content))) + .collect(); + let storage_delta = AccountStorageDelta::from_raw(slot_deltas); let mut fungible_delta = FungibleAssetDelta::default(); let mut non_fungible_delta = NonFungibleAssetDelta::default(); @@ -648,7 +630,6 @@ mod tests { }; use crate::testing::add_component::AddComponent; use crate::testing::noop_auth_component::NoopAuthComponent; - use crate::testing::storage::AccountStorageDeltaBuilder; #[test] fn test_serde_account() { @@ -669,11 +650,9 @@ mod tests { let nonce_delta = Felt::new(2); let asset_0 = FungibleAsset::mock(15); let asset_1 = NonFungibleAsset::mock(&[5, 5, 5]); - let storage_delta = AccountStorageDeltaBuilder::new() + let storage_delta = AccountStorageDelta::new() .add_cleared_items([StorageSlotName::mock(0)]) - .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) - .build() - .unwrap(); + .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]); let account_delta = build_account_delta( account_id, vec![asset_1], @@ -734,12 +713,10 @@ mod tests { // build account delta let final_nonce = Felt::new(2); - let storage_delta = AccountStorageDeltaBuilder::new() + let storage_delta = AccountStorageDelta::new() .add_cleared_items([StorageSlotName::mock(0)]) .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) - .add_updated_maps([(StorageSlotName::mock(2), updated_map)]) - .build() - .unwrap(); + .add_updated_maps([(StorageSlotName::mock(2), updated_map)]); let account_delta = build_account_delta( account_id, vec![asset_1], @@ -776,11 +753,9 @@ mod tests { build_account(vec![asset], init_nonce, vec![StorageSlotContent::Value(Word::empty())]); // build account delta - let storage_delta = AccountStorageDeltaBuilder::new() + let storage_delta = AccountStorageDelta::new() .add_cleared_items([StorageSlotName::mock(0)]) - .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) - .build() - .unwrap(); + .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]); let account_delta = build_account_delta(account_id, vec![], vec![asset], init_nonce, storage_delta); @@ -800,11 +775,9 @@ mod tests { // build account delta let final_nonce = Felt::new(1); - let storage_delta = AccountStorageDeltaBuilder::new() + let storage_delta = AccountStorageDelta::new() .add_cleared_items([StorageSlotName::mock(0)]) - .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]) - .build() - .unwrap(); + .add_updated_values([(StorageSlotName::mock(1), Word::from([1, 2, 3, 4u32]))]); let account_delta = build_account_delta(account_id, vec![], vec![asset], final_nonce, storage_delta); diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-objects/src/account/storage/mod.rs index 23c4aa9efe..6109a2c8a3 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-objects/src/account/storage/mod.rs @@ -238,12 +238,12 @@ impl AccountStorage { /// - The updates violate storage constraints. pub(super) fn apply_delta(&mut self, delta: &AccountStorageDelta) -> Result<(), AccountError> { // Update storage values - for (slot_name, &value) in delta.values().iter() { + for (slot_name, &value) in delta.values() { self.set_item(slot_name, value)?; } // Update storage maps - for (slot_name, map_delta) in delta.maps().iter() { + for (slot_name, map_delta) in delta.maps() { let slot = self .get_mut(slot_name) .ok_or(AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })?; diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 2cf3fe9074..bbe4eba16e 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -357,7 +357,7 @@ pub enum NetworkIdError { #[derive(Debug, Error)] pub enum AccountDeltaError { - #[error("storage slot {0} was updated as a value and as a map")] + #[error("storage slot {0} was used as different slot types")] StorageSlotUsedAsDifferentTypes(StorageSlotName), #[error("non fungible vault can neither be added nor removed twice")] DuplicateNonFungibleVaultUpdate(NonFungibleAsset), diff --git a/crates/miden-objects/src/testing/storage.rs b/crates/miden-objects/src/testing/storage.rs index 55a3b9ad17..c4c0fc47cb 100644 --- a/crates/miden-objects/src/testing/storage.rs +++ b/crates/miden-objects/src/testing/storage.rs @@ -1,47 +1,58 @@ -use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; use alloc::vec::Vec; use miden_core::{Felt, Word}; -use miden_crypto::EMPTY_WORD; -use crate::AccountDeltaError; use crate::account::{ AccountStorage, AccountStorageDelta, StorageMap, StorageMapDelta, StorageSlot, + StorageSlotDelta, StorageSlotName, }; use crate::note::NoteAssets; use crate::utils::sync::LazyLock; -// ACCOUNT STORAGE DELTA BUILDER +// ACCOUNT STORAGE DELTA // ================================================================================================ -#[derive(Clone, Debug, Default)] -pub struct AccountStorageDeltaBuilder { - values: BTreeMap, - maps: BTreeMap, -} - -impl AccountStorageDeltaBuilder { +impl AccountStorageDelta { // CONSTRUCTORS - // ------------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- - pub fn new() -> Self { - Self { - values: BTreeMap::new(), - maps: BTreeMap::new(), - } + /// Creates an [`AccountStorageDelta`] from the given iterators. + pub fn from_iters( + cleared_values: impl IntoIterator, + updated_values: impl IntoIterator, + updated_maps: impl IntoIterator, + ) -> Self { + let deltas = + cleared_values + .into_iter() + .map(|slot_name| (slot_name, StorageSlotDelta::with_empty_value())) + .chain(updated_values.into_iter().map(|(slot_name, slot_value)| { + (slot_name, StorageSlotDelta::Value(slot_value)) + })) + .chain( + updated_maps.into_iter().map(|(slot_name, map_delta)| { + (slot_name, StorageSlotDelta::Map(map_delta)) + }), + ) + .collect(); + + Self::from_raw(deltas) } - // MODIFIERS + // MUTATORS // ------------------------------------------------------------------------------------------- pub fn add_cleared_items(mut self, items: impl IntoIterator) -> Self { - self.values.extend(items.into_iter().map(|slot| (slot, EMPTY_WORD))); + items + .into_iter() + .for_each(|slot_name| self.set_item(slot_name, Word::empty()).expect("TODO")); + self } @@ -49,7 +60,10 @@ impl AccountStorageDeltaBuilder { mut self, items: impl IntoIterator, ) -> Self { - self.values.extend(items); + items.into_iter().for_each(|(slot_name, slot_value)| { + self.set_item(slot_name, slot_value).expect("TODO") + }); + self } @@ -57,15 +71,13 @@ impl AccountStorageDeltaBuilder { mut self, items: impl IntoIterator, ) -> Self { - self.maps.extend(items); - self - } + items.into_iter().for_each(|(slot_name, map_delta)| { + for (key, value) in map_delta.entries() { + self.set_map_item(slot_name.clone(), *key.inner(), *value).expect("TODO") + } + }); - // BUILDERS - // ------------------------------------------------------------------------------------------- - - pub fn build(self) -> Result { - AccountStorageDelta::from_parts(self.values, self.maps) + self } } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index b7d08b9132..bcf0b213e8 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use std::collections::BTreeMap; use anyhow::Context; +use assert_matches::assert_matches; use miden_lib::errors::tx_kernel_errors::{ ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO, ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO, @@ -30,6 +31,7 @@ use miden_objects::account::{ StorageMap, StorageSlot, StorageSlotContent, + StorageSlotDelta, StorageSlotId, StorageSlotName, StorageSlotType, @@ -948,16 +950,28 @@ async fn prove_account_creation_with_non_empty_storage() -> anyhow::Result<()> { assert_eq!(tx.account_delta().nonce_delta(), Felt::new(1)); - assert_eq!(tx.account_delta().storage().values().get(&slot_name0).unwrap(), &slot0.value()); - assert_eq!(tx.account_delta().storage().values().get(&slot_name1).unwrap(), &slot1.value()); - - assert_eq!( - tx.account_delta().storage().maps().get(&slot_name2).unwrap().entries(), - &BTreeMap::from_iter( + assert_matches!( + tx.account_delta().storage().get(&slot_name0).unwrap(), + StorageSlotDelta::Value(value) => { + assert_eq!(*value, slot0.value()) + } + ); + assert_matches!( + tx.account_delta().storage().get(&slot_name1).unwrap(), + StorageSlotDelta::Value(value) => { + assert_eq!(*value, slot1.value()) + } + ); + assert_matches!( + tx.account_delta().storage().get(&slot_name2).unwrap(), + StorageSlotDelta::Map(map_delta) => { + let expected = &BTreeMap::from_iter( map_entries .into_iter() .map(|(key, value)| { (LexicographicWord::new(key), value) }) - ) + ); + assert_eq!(expected, map_delta.entries()) + } ); assert!(tx.account_delta().vault().is_empty()); @@ -1511,9 +1525,10 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res assert_eq!(executed_tx.account_delta().nonce_delta(), Felt::new(1)); // Make sure that account storage has been updated as per the tx script call. + assert_eq!(executed_tx.account_delta().storage().values().count(), 1); assert_eq!( - *executed_tx.account_delta().storage().values(), - BTreeMap::from([(MOCK_VALUE_SLOT0.clone(), Word::from([2, 3, 4, 5u32]))]), + executed_tx.account_delta().storage().get(&MOCK_VALUE_SLOT0).unwrap(), + &StorageSlotDelta::Value(Word::from([2, 3, 4, 5u32])), ); Ok(()) } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index 7e3293cf5b..fa7ae57a3d 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -16,6 +16,7 @@ use miden_objects::account::{ AccountType, StorageMap, StorageSlot, + StorageSlotDelta, StorageSlotName, }; use miden_objects::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; @@ -196,7 +197,6 @@ async fn storage_delta_for_value_slots() -> anyhow::Result<()> { .account_delta() .storage() .values() - .iter() .map(|(slot_name, value)| (slot_name.clone(), *value)) .collect::>(); @@ -347,20 +347,22 @@ async fn storage_delta_for_map_slots() -> anyhow::Result<()> { .execute() .await .context("failed to execute transaction")?; - let maps_delta = executed_tx.account_delta().storage().maps(); + let maps_delta = executed_tx.account_delta().storage().maps().collect::>(); // Note that there should be no delta for map2 since it was normalized to an empty map which // should be removed. assert_eq!(maps_delta.len(), 2); - assert!(maps_delta.get(&slot_2_name).is_none(), "map2 should not have a delta"); + assert!(!maps_delta.contains_key(&slot_2_name), "map2 should not have a delta"); let mut map0_delta = maps_delta .get(&slot_0_name) + .map(|map_delta| (*map_delta).clone()) .expect("delta for map 0 should exist") - .clone() .into_map(); + let mut map1_delta = maps_delta .get(&slot_1_name) + .map(|map_delta| (*map_delta).clone()) .expect("delta for map 1 should exist") .clone() .into_map(); @@ -748,22 +750,27 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { // storage delta // -------------------------------------------------------------------------------------------- // We expect one updated item and one updated map - assert_eq!(executed_transaction.account_delta().storage().values().len(), 1); + assert_eq!(executed_transaction.account_delta().storage().values().count(), 1); assert_eq!( - executed_transaction.account_delta().storage().values().get(&MOCK_VALUE_SLOT0), - Some(&updated_slot_value) + executed_transaction + .account_delta() + .storage() + .get(&MOCK_VALUE_SLOT0) + .cloned() + .map(StorageSlotDelta::unwrap_value), + Some(updated_slot_value) ); - assert_eq!(executed_transaction.account_delta().storage().maps().len(), 1); + assert_eq!(executed_transaction.account_delta().storage().maps().count(), 1); let map_delta = executed_transaction .account_delta() .storage() - .maps() .get(&MOCK_MAP_SLOT) - .context("failed to get expected value from storage map")? - .entries(); + .cloned() + .map(StorageSlotDelta::unwrap_map) + .unwrap(); assert_eq!( - *map_delta.get(&LexicographicWord::new(updated_map_key)).unwrap(), + *map_delta.entries().get(&LexicographicWord::new(updated_map_key)).unwrap(), updated_map_value ); @@ -874,7 +881,13 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: for (slot_name, expected_map) in [(map0_slot_name, map0), (map1_slot_name, map1), (map2_slot_name, map2)] { - let map_delta = tx.account_delta().storage().maps().get(&slot_name).unwrap(); + let map_delta = tx + .account_delta() + .storage() + .get(&slot_name) + .cloned() + .map(StorageSlotDelta::unwrap_map) + .unwrap(); assert_eq!( map_delta .entries() @@ -941,9 +954,25 @@ async fn delta_for_new_account_retains_empty_value_storage_slots() -> anyhow::Re panic!("expected delta"); }; - assert_eq!(delta.storage().values().len(), 2); - assert_eq!(delta.storage().values().get(&slot_name0).unwrap(), &Word::empty()); - assert_eq!(delta.storage().values().get(&slot_name1).unwrap(), &slot_value2); + assert_eq!(delta.storage().values().count(), 2); + assert_eq!( + delta + .storage() + .get(&slot_name0) + .cloned() + .map(StorageSlotDelta::unwrap_value) + .unwrap(), + Word::empty() + ); + assert_eq!( + delta + .storage() + .get(&slot_name1) + .cloned() + .map(StorageSlotDelta::unwrap_value) + .unwrap(), + slot_value2 + ); let recreated_account = Account::try_from(delta)?; // The recreated account should match the original account with the nonce incremented (and the @@ -977,8 +1006,16 @@ async fn delta_for_new_account_retains_empty_map_storage_slots() -> anyhow::Resu panic!("expected delta"); }; - assert_eq!(delta.storage().maps().len(), 1); - assert!(delta.storage().maps().get(&slot_name0).unwrap().is_empty()); + assert_eq!(delta.storage().maps().count(), 1); + assert!( + delta + .storage() + .get(&slot_name0) + .cloned() + .map(StorageSlotDelta::unwrap_map) + .unwrap() + .is_empty() + ); let recreated_account = Account::try_from(delta)?; // The recreated account should match the original account with the nonce incremented (and the diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index 9670228f27..a48cfcbd0f 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -5,7 +5,7 @@ use miden_lib::testing::note::NoteBuilder; use miden_lib::utils::CodeBuilder; use miden_objects::LexicographicWord; -use miden_objects::account::{AccountId, AccountStorage}; +use miden_objects::account::{AccountId, AccountStorage, StorageSlotDelta}; use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::testing::account_id::{ ACCOUNT_ID_NATIVE_ASSET_FAUCET, @@ -220,7 +220,13 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { .execute() .await?; - let map_delta = tx.account_delta().storage().maps().get(mock_map_slot).unwrap(); + let map_delta = tx + .account_delta() + .storage() + .get(mock_map_slot) + .cloned() + .map(StorageSlotDelta::unwrap_map) + .unwrap(); assert_eq!(map_delta.entries().get(&LexicographicWord::new(existing_key)).unwrap(), &value0); assert_eq!( map_delta.entries().get(&LexicographicWord::new(non_existent_key)).unwrap(), diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index 5e8b5c4f03..0baef19a3b 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -6,6 +6,7 @@ use miden_objects::account::{ AccountStorageDelta, AccountStorageHeader, PartialAccount, + StorageSlotDelta, StorageSlotHeader, StorageSlotName, StorageSlotType, @@ -101,7 +102,9 @@ impl StorageDeltaTracker { /// Updates a value slot. pub fn set_item(&mut self, slot_name: StorageSlotName, new_value: Word) { - self.delta.set_item(slot_name, new_value); + self.delta + .set_item(slot_name, new_value) + .expect("transaction kernel should not change slot types"); } /// Updates a map slot. @@ -115,7 +118,9 @@ impl StorageDeltaTracker { // Don't update the delta if the new value matches the old one. if prev_value != new_value { self.set_init_map_item(slot_name.clone(), key, prev_value); - self.delta.set_map_item(slot_name, key, new_value); + self.delta + .set_map_item(slot_name, key, new_value) + .expect("transaction kernel should not change slot types"); } } @@ -147,46 +152,48 @@ impl StorageDeltaTracker { init_maps, delta, } = self; - let (mut value_slots, mut map_slots) = delta.into_parts(); - - // Skip normalization of value slots for new accounts. Since the initial value for - // normalization defaults to Word::empty, this prevents slots being removed that are validly - // created with an empty value. - if !is_account_new { - // Keep only the values whose new value is different from the initial value. - value_slots.retain(|slot_name, new_value| { - // SAFETY: The header in the initial storage is the one from the account against - // which the transaction is executed, so accessing that slot name should be fine. - let slot_header = storage_header - .find_slot_header_by_name(slot_name) - .expect("slot name should exist"); - *new_value != slot_header.value() - }); - } + let mut deltas = delta.into_map(); + + deltas.retain(|slot_name, slot_delta| { + match slot_delta { + StorageSlotDelta::Value(new_value) => { + // SAFETY: The header in the initial storage is the one from the account + // against which the transaction is executed, so accessing that slot name + // should be fine. + let slot_header = storage_header + .find_slot_header_by_name(slot_name) + .expect("slot name should exist"); + + // Only retain the value if the account is new or if it has changed. + // New accounts must contain all slots, even empty ones, to represent the full + // storage state. + is_account_new || *new_value != slot_header.value() + }, + + // On the key-value level: Keep only the key-value pairs whose new value is + // different from the initial value. + // On the map level: Keep only the maps that are non-empty after its key-value + // pairs have been normalized, or if the account is new. + StorageSlotDelta::Map(map_delta) => { + let init_map = init_maps.get(slot_name); + + if let Some(init_map) = init_map { + map_delta.as_map_mut().retain(|key, new_value| { + let initial_value = init_map.get(key.inner()).expect( + "the initial value should be present for every value that was updated", + ); + new_value != initial_value + }); + } - // On the key-value level: Keep only the key-value pairs whose new value is different from - // the initial value. - // On the map level: Keep only the maps that are non-empty after its key-value pairs have - // been normalized, or if the account is new. - map_slots.retain(|slot_idx, map_delta| { - let init_map = init_maps.get(slot_idx); - - if let Some(init_map) = init_map { - map_delta.as_map_mut().retain(|key, new_value| { - let initial_value = init_map.get(key.inner()).expect( - "the initial value should be present for every value that was updated", - ); - new_value != initial_value - }); + // Only retain the map delta if the account is new or if it still contains + // values after normalization. + is_account_new || !map_delta.is_empty() + }, } - - // Only retain the map delta if the account is new or if it still contains values after - // normalization. - self.is_account_new || !map_delta.is_empty() }); - AccountStorageDelta::from_parts(value_slots, map_slots) - .expect("storage delta should still be valid since no new values were added") + AccountStorageDelta::from_raw(deltas) } } From 83c5e650ee1e29d2d0765f843383588a69b4a248 Mon Sep 17 00:00:00 2001 From: igamigo Date: Fri, 19 Dec 2025 12:23:59 -0300 Subject: [PATCH 070/114] refactor: Implement component schemas (#2193) * refactor: replace templates with component schemas * chore: overrideable -> overridable * chore: more docs * feat: avoid overloading type word and define slot type with the dotted key * chore: CHANGELOG * chore: update docs * chore: update docs * chore: display * chore: spellcheck * reviews: address most of the first review's smaller comments * reviews: infer type based o type structure * reviews: re-enable tests * reviews: docs, typeregstry renames, doc comment rewrites * chore: lints * reviews: give context to errors, simplify validations, revert felt parsing to use registry * reviews: simplify further * reviews: initvaluerequirements -> schemarequirements; now collected into map * reviews: more doc suggestions applied; validate schema * reviews: singular->simple, scalar->atomic, docs reviews, nits * reviews: initstoragedata duplicate detection * feat: scope storagevaluename * chore: docs fix * chore: fix indentaion --------- Co-authored-by: Marti --- CHANGELOG.md | 1 + .../src/account/faucets/network_fungible.rs | 2 +- .../src/account/component/metadata/mod.rs | 213 ++++ .../src/account/component/mod.rs | 42 +- .../component/storage/init_storage_data.rs | 77 ++ .../src/account/component/storage/mod.rs | 14 + .../src/account/component/storage/schema.rs | 1133 +++++++++++++++++ .../storage/toml/init_storage_data.rs | 184 +++ .../src/account/component/storage/toml/mod.rs | 497 ++++++++ .../component/storage/toml/serde_impls.rs | 122 ++ .../account/component/storage/toml/tests.rs | 725 +++++++++++ .../component/storage/type_registry.rs | 652 ++++++++++ .../account/component/storage/value_name.rs | 219 ++++ .../src/account/component/template/mod.rs | 521 -------- .../template/storage/entry_content.rs | 748 ----------- .../template/storage/init_storage_data.rs | 63 - .../account/component/template/storage/mod.rs | 1130 ---------------- .../component/template/storage/placeholder.rs | 549 -------- .../component/template/storage/toml.rs | 789 ------------ crates/miden-objects/src/account/mod.rs | 18 +- crates/miden-objects/src/asset/nonfungible.rs | 2 +- crates/miden-objects/src/errors.rs | 35 +- docs/src/account/components.md | 237 ++-- 23 files changed, 4005 insertions(+), 3968 deletions(-) create mode 100644 crates/miden-objects/src/account/component/metadata/mod.rs create mode 100644 crates/miden-objects/src/account/component/storage/init_storage_data.rs create mode 100644 crates/miden-objects/src/account/component/storage/mod.rs create mode 100644 crates/miden-objects/src/account/component/storage/schema.rs create mode 100644 crates/miden-objects/src/account/component/storage/toml/init_storage_data.rs create mode 100644 crates/miden-objects/src/account/component/storage/toml/mod.rs create mode 100644 crates/miden-objects/src/account/component/storage/toml/serde_impls.rs create mode 100644 crates/miden-objects/src/account/component/storage/toml/tests.rs create mode 100644 crates/miden-objects/src/account/component/storage/type_registry.rs create mode 100644 crates/miden-objects/src/account/component/storage/value_name.rs delete mode 100644 crates/miden-objects/src/account/component/template/mod.rs delete mode 100644 crates/miden-objects/src/account/component/template/storage/entry_content.rs delete mode 100644 crates/miden-objects/src/account/component/template/storage/init_storage_data.rs delete mode 100644 crates/miden-objects/src/account/component/template/storage/mod.rs delete mode 100644 crates/miden-objects/src/account/component/template/storage/placeholder.rs delete mode 100644 crates/miden-objects/src/account/component/template/storage/toml.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index baa11c1f57..39b7695bf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). +- [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index aeecc214a9..25d88717f7 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -214,7 +214,7 @@ impl From for AccountComponent { Felt::ZERO, ]); - // Convert AccountId to Word representation for storage + // Convert AccountId into its Word encoding for storage. let owner_account_id_word: Word = [ Felt::new(0), Felt::new(0), diff --git a/crates/miden-objects/src/account/component/metadata/mod.rs b/crates/miden-objects/src/account/component/metadata/mod.rs new file mode 100644 index 0000000000..961ccacfbb --- /dev/null +++ b/crates/miden-objects/src/account/component/metadata/mod.rs @@ -0,0 +1,213 @@ +use alloc::collections::{BTreeMap, BTreeSet}; +use alloc::string::{String, ToString}; +use core::str::FromStr; + +use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use miden_mast_package::{Package, SectionId}; +use miden_processor::DeserializationError; +use semver::Version; + +use super::{AccountStorageSchema, AccountType, SchemaRequirement, StorageValueName}; +use crate::AccountError; + +// ACCOUNT COMPONENT METADATA +// ================================================================================================ + +/// Represents the full component metadata configuration. +/// +/// An account component metadata describes the component alongside its storage layout. +/// The storage layout can declare typed values which must be provided at instantiation time via +/// [InitStorageData](`super::storage::InitStorageData`). These can appear either at the slot level +/// (a singular word slot) or inside composed words as typed fields. +/// +/// When the `std` feature is enabled, this struct allows for serialization and deserialization to +/// and from a TOML file. +/// +/// # Guarantees +/// +/// - The metadata's storage schema does not contain duplicate slot names. +/// - The schema cannot contain protocol-reserved slot names. +/// - Each init-time value name uniquely identifies a single value. The expected init-time metadata +/// can be retrieved with [AccountComponentMetadata::schema_requirements()], which returns a map +/// from keys to [SchemaRequirement] (which indicates the expected value type and optional +/// defaults). +/// +/// # Example +/// +/// ``` +/// use std::collections::BTreeSet; +/// +/// use miden_objects::account::StorageSlotName; +/// use miden_objects::account::component::{ +/// AccountComponentMetadata, +/// AccountStorageSchema, +/// FeltSchema, +/// InitStorageData, +/// SchemaTypeId, +/// StorageSlotSchema, +/// StorageValueName, +/// ValueSlotSchema, +/// WordSchema, +/// }; +/// use semver::Version; +/// +/// let slot_name = StorageSlotName::new("demo::test_value")?; +/// +/// let word = WordSchema::new_value([ +/// FeltSchema::new_void(), +/// FeltSchema::new_void(), +/// FeltSchema::new_void(), +/// FeltSchema::new_typed(SchemaTypeId::native_felt(), "foo"), +/// ]); +/// +/// let storage_schema = AccountStorageSchema::new([( +/// slot_name.clone(), +/// StorageSlotSchema::Value(ValueSlotSchema::new(Some("demo slot".into()), word)), +/// )])?; +/// +/// let metadata = AccountComponentMetadata::new( +/// "test name".into(), +/// "description of the component".into(), +/// Version::parse("0.1.0")?, +/// BTreeSet::new(), +/// storage_schema, +/// ); +/// +/// // Init value keys are derived from slot name: `demo::test_value.foo`. +/// let init_storage_data = InitStorageData::new( +/// [(StorageValueName::from_slot_name(&slot_name).with_suffix("foo")?, "300".into())], +/// [], +/// ); +/// +/// let storage_slots = metadata.storage_schema().build_storage_slots(&init_storage_data)?; +/// assert_eq!(storage_slots.len(), 1); +/// # Ok::<(), Box>(()) +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "std", serde(rename_all = "kebab-case"))] +pub struct AccountComponentMetadata { + /// The human-readable name of the component. + name: String, + + /// A brief description of what this component is and how it works. + description: String, + + /// The version of the component using semantic versioning. + /// This can be used to track and manage component upgrades. + version: Version, + + /// A set of supported target account types for this component. + supported_types: BTreeSet, + + /// Storage schema defining the component's storage layout, defaults, and init-supplied values. + #[cfg_attr(feature = "std", serde(rename = "storage"))] + storage_schema: AccountStorageSchema, +} + +impl AccountComponentMetadata { + /// Create a new [AccountComponentMetadata]. + pub fn new( + name: String, + description: String, + version: Version, + targets: BTreeSet, + storage_schema: AccountStorageSchema, + ) -> Self { + Self { + name, + description, + version, + supported_types: targets, + storage_schema, + } + } + + /// Returns the init-time values's requirements for this schema. + /// + /// These values are used for initializing storage slot values or storage map entries. For a + /// full example, refer to the docs for [AccountComponentMetadata]. + /// + /// Types for returned init values are inferred based on their location in the storage layout. + pub fn schema_requirements(&self) -> BTreeMap { + self.storage_schema.schema_requirements().expect("storage schema is validated") + } + + /// Returns the name of the account component. + pub fn name(&self) -> &str { + &self.name + } + + /// Returns the description of the account component. + pub fn description(&self) -> &str { + &self.description + } + + /// Returns the semantic version of the account component. + pub fn version(&self) -> &Version { + &self.version + } + + /// Returns the account types supported by the component. + pub fn supported_types(&self) -> &BTreeSet { + &self.supported_types + } + + /// Returns the storage schema of the component. + pub fn storage_schema(&self) -> &AccountStorageSchema { + &self.storage_schema + } +} + +impl TryFrom<&Package> for AccountComponentMetadata { + type Error = AccountError; + + fn try_from(package: &Package) -> Result { + package + .sections + .iter() + .find_map(|section| { + (section.id == SectionId::ACCOUNT_COMPONENT_METADATA).then(|| { + AccountComponentMetadata::read_from_bytes(§ion.data).map_err(|err| { + AccountError::other_with_source( + "failed to deserialize account component metadata", + err, + ) + }) + }) + }) + .transpose()? + .ok_or_else(|| { + AccountError::other( + "package does not contain account component metadata section - packages without explicit metadata may be intended for other purposes (e.g., note scripts, transaction scripts)", + ) + }) + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for AccountComponentMetadata { + fn write_into(&self, target: &mut W) { + self.name.write_into(target); + self.description.write_into(target); + self.version.to_string().write_into(target); + self.supported_types.write_into(target); + self.storage_schema.write_into(target); + } +} + +impl Deserializable for AccountComponentMetadata { + fn read_from(source: &mut R) -> Result { + Ok(Self { + name: String::read_from(source)?, + description: String::read_from(source)?, + version: semver::Version::from_str(&String::read_from(source)?).map_err( + |err: semver::Error| DeserializationError::InvalidValue(err.to_string()), + )?, + supported_types: BTreeSet::::read_from(source)?, + storage_schema: AccountStorageSchema::read_from(source)?, + }) + } +} diff --git a/crates/miden-objects/src/account/component/mod.rs b/crates/miden-objects/src/account/component/mod.rs index 6211584d7e..60dbef0a58 100644 --- a/crates/miden-objects/src/account/component/mod.rs +++ b/crates/miden-objects/src/account/component/mod.rs @@ -1,9 +1,14 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -// TODO(named_slots): Refactor templates. -// mod template; -// pub use template::*; +use miden_mast_package::{MastArtifact, Package}; + +mod metadata; +pub use metadata::*; + +pub mod storage; +pub use storage::*; + mod code; pub use code::AccountComponentCode; @@ -69,7 +74,6 @@ impl AccountComponent { }) } - /* /// Creates an [`AccountComponent`] from a [`Package`] using [`InitStorageData`]. /// /// This method provides type safety by leveraging the component's metadata to validate @@ -104,8 +108,7 @@ impl AccountComponent { }; let component_code = AccountComponentCode::from(library); - - AccountComponent::from_library(&component_code, &metadata, init_storage_data) + Self::from_library(&component_code, &metadata, init_storage_data) } /// Creates an [`AccountComponent`] from an [`AccountComponentCode`] and @@ -134,18 +137,16 @@ impl AccountComponent { account_component_metadata: &AccountComponentMetadata, init_storage_data: &InitStorageData, ) -> Result { - let mut storage_slots = vec![]; - for storage_entry in account_component_metadata.storage_entries() { - let entry_storage_slots = storage_entry - .try_build_storage_slots(init_storage_data) - .map_err(AccountError::AccountComponentTemplateInstantiationError)?; - storage_slots.extend(entry_storage_slots); - } + let storage_slots = account_component_metadata + .storage_schema() + .build_storage_slots(init_storage_data) + .map_err(|err| { + AccountError::other_with_source("failed to instantiate account component", err) + })?; Ok(AccountComponent::new(library.clone(), storage_slots)? .with_supported_types(account_component_metadata.supported_types().clone())) } - */ // ACCESSORS // -------------------------------------------------------------------------------------------- @@ -241,8 +242,6 @@ impl From for AccountComponentCode { } } -// TODO(named_slots): Reactivate tests once template is refactored. -/* #[cfg(test)] mod tests { use alloc::collections::BTreeSet; @@ -268,9 +267,8 @@ mod tests { "A test component".to_string(), Version::new(1, 0, 0), BTreeSet::from_iter([AccountType::RegularAccountImmutableCode]), - vec![], - ) - .unwrap(); + AccountStorageSchema::default(), + ); let metadata_bytes = metadata.to_bytes(); let package_with_metadata = Package { @@ -326,9 +324,8 @@ mod tests { AccountType::RegularAccountImmutableCode, AccountType::RegularAccountUpdatableCode, ]), - vec![], - ) - .unwrap(); + AccountStorageSchema::default(), + ); // Test with empty init data - this tests the complete workflow: // Library + Metadata -> AccountComponent @@ -358,4 +355,3 @@ mod tests { assert!(error_msg.contains("package does not contain account component metadata")); } } -*/ diff --git a/crates/miden-objects/src/account/component/storage/init_storage_data.rs b/crates/miden-objects/src/account/component/storage/init_storage_data.rs new file mode 100644 index 0000000000..bb2d5c0a49 --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/init_storage_data.rs @@ -0,0 +1,77 @@ +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::vec::Vec; + +use super::StorageValueName; + +/// A raw word value provided via [`InitStorageData`]. +/// +/// This is used for defining specific values in relation to a component's schema, where each values +/// is supplied as either an atomic string (e.g. `"0x1234"`, `"16"`, `"BTC"`) or an array of 4 field +/// elements. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "std", serde(untagged))] +pub enum WordValue { + /// Represents a single word value, given by a single string input. + Atomic(String), + /// Represents a word through four string-encoded field elements. + Elements([String; 4]), +} + +impl From for WordValue { + fn from(value: String) -> Self { + WordValue::Atomic(value) + } +} + +impl From<&str> for WordValue { + fn from(value: &str) -> Self { + WordValue::Atomic(String::from(value)) + } +} + +/// Represents the data required to initialize storage entries when instantiating an +/// [AccountComponent](crate::account::AccountComponent) from component metadata (either provided +/// directly or extracted from a package). +/// +/// An [`InitStorageData`] can be created from a TOML string when the `std` feature flag is set. +#[derive(Clone, Debug, Default)] +pub struct InitStorageData { + /// A mapping of init value names to their raw values. + value_entries: BTreeMap, + /// A mapping of storage map slot names to their raw key/value entries. + map_entries: BTreeMap>, +} + +impl InitStorageData { + /// Creates a new instance of [InitStorageData]. + /// + /// A [`BTreeMap`] is constructed from the passed iterator, so duplicate keys will cause + /// overridden values. + pub fn new( + entries: impl IntoIterator, + map_entries: impl IntoIterator)>, + ) -> Self { + InitStorageData { + value_entries: entries.into_iter().collect(), + map_entries: map_entries.into_iter().collect(), + } + } + + /// Returns a reference to the underlying init values map. + pub fn values(&self) -> &BTreeMap { + &self.value_entries + } + + /// Returns a reference to the stored init value, or [`Option::None`] if the key is not + /// present. + pub fn get(&self, key: &StorageValueName) -> Option<&WordValue> { + self.value_entries.get(key) + } + + /// Returns the map entries associated with the given storage map slot name, if any. + pub fn map_entries(&self, key: &StorageValueName) -> Option<&Vec<(WordValue, WordValue)>> { + self.map_entries.get(key) + } +} diff --git a/crates/miden-objects/src/account/component/storage/mod.rs b/crates/miden-objects/src/account/component/storage/mod.rs new file mode 100644 index 0000000000..2c28296c49 --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/mod.rs @@ -0,0 +1,14 @@ +mod schema; +pub use schema::*; + +mod value_name; +pub use value_name::{StorageValueName, StorageValueNameError}; + +mod type_registry; +pub use type_registry::{SchemaRequirement, SchemaTypeError, SchemaTypeId}; + +mod init_storage_data; +pub use init_storage_data::{InitStorageData, WordValue}; + +#[cfg(feature = "std")] +pub mod toml; diff --git a/crates/miden-objects/src/account/component/storage/schema.rs b/crates/miden-objects/src/account/component/storage/schema.rs new file mode 100644 index 0000000000..d8b6f5a170 --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/schema.rs @@ -0,0 +1,1133 @@ +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use miden_processor::DeserializationError; + +use super::type_registry::{SCHEMA_TYPE_REGISTRY, SchemaRequirement, SchemaTypeId}; +use super::{InitStorageData, StorageValueName, WordValue}; +use crate::account::{AccountStorage, StorageMap, StorageSlot, StorageSlotName}; +use crate::errors::AccountComponentTemplateError; +use crate::{Felt, FieldElement, Word}; + +// STORAGE SCHEMA +// ================================================================================================ + +/// Describes the storage schema of an account component in terms of its named storage slots. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct AccountStorageSchema { + slots: BTreeMap, +} + +impl AccountStorageSchema { + /// Creates a new [`AccountStorageSchema`]. + /// + /// # Errors + /// - If `fields` contains duplicate slot names. + /// - If `fields` contains the protocol-reserved faucet metadata slot name. + /// - If any slot schema is invalid. + /// - If multiple schema fields map to the same init value name. + pub fn new( + slots: impl IntoIterator, + ) -> Result { + let mut map = BTreeMap::new(); + for (slot_name, schema) in slots { + if map.insert(slot_name.clone(), schema).is_some() { + return Err(AccountComponentTemplateError::DuplicateSlotName(slot_name)); + } + } + + let schema = Self { slots: map }; + schema.validate()?; + Ok(schema) + } + + /// Returns an iterator over `(slot_name, schema)` pairs in slot-id order. + pub fn iter(&self) -> impl Iterator { + self.slots.iter() + } + + /// Returns a reference to the underlying slots map. + pub fn slots(&self) -> &BTreeMap { + &self.slots + } + + /// Builds the initial [`StorageSlot`]s for this schema using the provided initialization data. + pub fn build_storage_slots( + &self, + init_storage_data: &InitStorageData, + ) -> Result, AccountComponentTemplateError> { + self.slots + .iter() + .map(|(slot_name, schema)| schema.try_build_storage_slot(slot_name, init_storage_data)) + .collect() + } + + /// Returns init-value requirements for the entire schema. + /// + /// The returned map includes both required values (no `default_value`) and optional values + /// (with `default_value`), and excludes map entries. + pub fn schema_requirements( + &self, + ) -> Result, AccountComponentTemplateError> { + let mut requirements = BTreeMap::new(); + for (slot_name, schema) in self.slots.iter() { + schema.collect_init_value_requirements(slot_name, &mut requirements)?; + } + Ok(requirements) + } + + fn validate(&self) -> Result<(), AccountComponentTemplateError> { + let mut init_values = BTreeMap::new(); + + for (slot_name, schema) in self.slots.iter() { + if slot_name.id() == AccountStorage::faucet_sysdata_slot().id() { + return Err(AccountComponentTemplateError::ReservedSlotName(slot_name.clone())); + } + + schema.validate(slot_name)?; + schema.collect_init_value_requirements(slot_name, &mut init_values)?; + } + + Ok(()) + } +} + +impl Serializable for AccountStorageSchema { + fn write_into(&self, target: &mut W) { + target.write_u16(self.slots.len() as u16); + for (slot_name, schema) in self.slots.iter() { + target.write(slot_name); + target.write(schema); + } + } +} + +impl Deserializable for AccountStorageSchema { + fn read_from(source: &mut R) -> Result { + let num_entries = source.read_u16()? as usize; + let mut fields = BTreeMap::new(); + + for _ in 0..num_entries { + let slot_name = StorageSlotName::read_from(source)?; + let schema = StorageSlotSchema::read_from(source)?; + + if fields.insert(slot_name.clone(), schema).is_some() { + return Err(DeserializationError::InvalidValue(format!( + "duplicate slot name in storage schema: {slot_name}", + ))); + } + } + + let schema = AccountStorageSchema::new(fields) + .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?; + Ok(schema) + } +} + +// STORAGE SLOT SCHEMA +// ================================================================================================ + +/// Describes the schema for a storage slot. +/// Can describe either a value slot, or a map slot. +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum StorageSlotSchema { + Value(ValueSlotSchema), + Map(MapSlotSchema), +} + +impl StorageSlotSchema { + fn collect_init_value_requirements( + &self, + slot_name: &StorageSlotName, + requirements: &mut BTreeMap, + ) -> Result<(), AccountComponentTemplateError> { + let slot_prefix = StorageValueName::from_slot_name(slot_name); + match self { + StorageSlotSchema::Value(slot) => { + slot.collect_init_value_requirements(slot_prefix, requirements) + }, + StorageSlotSchema::Map(_) => Ok(()), + } + } + + /// Builds a [`StorageSlot`] for the specified `slot_name` using the provided initialization + /// data. + pub fn try_build_storage_slot( + &self, + slot_name: &StorageSlotName, + init_storage_data: &InitStorageData, + ) -> Result { + let slot_prefix = StorageValueName::from_slot_name(slot_name); + match self { + StorageSlotSchema::Value(slot) => { + let word = slot.try_build_word(init_storage_data, slot_prefix)?; + Ok(StorageSlot::with_value(slot_name.clone(), word)) + }, + StorageSlotSchema::Map(slot) => { + let storage_map = slot.try_build_map(init_storage_data, slot_prefix)?; + Ok(StorageSlot::with_map(slot_name.clone(), storage_map)) + }, + } + } + + pub(crate) fn validate( + &self, + slot_name: &StorageSlotName, + ) -> Result<(), AccountComponentTemplateError> { + match self { + StorageSlotSchema::Value(slot) => slot.validate(slot_name)?, + StorageSlotSchema::Map(slot) => slot.validate()?, + } + + Ok(()) + } +} + +impl Serializable for StorageSlotSchema { + fn write_into(&self, target: &mut W) { + match self { + StorageSlotSchema::Value(slot) => { + target.write_u8(0u8); + slot.write_into(target); + }, + StorageSlotSchema::Map(slot) => { + target.write_u8(1u8); + slot.write_into(target); + }, + } + } +} + +impl Deserializable for StorageSlotSchema { + fn read_from(source: &mut R) -> Result { + let variant_tag = source.read_u8()?; + match variant_tag { + 0 => Ok(StorageSlotSchema::Value(ValueSlotSchema::read_from(source)?)), + 1 => Ok(StorageSlotSchema::Map(MapSlotSchema::read_from(source)?)), + _ => Err(DeserializationError::InvalidValue(format!( + "unknown variant tag '{variant_tag}' for StorageSlotSchema" + ))), + } + } +} + +// WORDS +// ================================================================================================ + +/// Defines how a word slot is described within the component's storage schema. +/// +/// Each word schema can either describe a whole-word typed value supplied at instantiation time +/// (`Simple`) or a composite word that explicitly defines each felt element (`Composite`). +#[derive(Debug, Clone, PartialEq, Eq)] +#[allow(clippy::large_enum_variant)] +pub enum WordSchema { + /// A whole-word typed value supplied at instantiation time. + Simple { + r#type: SchemaTypeId, + default_value: Option, + }, + /// A composed word that may mix defaults and typed fields. + Composite { value: [FeltSchema; 4] }, +} + +impl WordSchema { + pub fn new_simple(r#type: SchemaTypeId) -> Self { + WordSchema::Simple { r#type, default_value: None } + } + + pub fn new_simple_with_default(r#type: SchemaTypeId, default_value: Word) -> Self { + WordSchema::Simple { + r#type, + default_value: Some(default_value), + } + } + + pub fn new_value(value: impl Into<[FeltSchema; 4]>) -> Self { + WordSchema::Composite { value: value.into() } + } + + pub fn value(&self) -> Option<&[FeltSchema; 4]> { + match self { + WordSchema::Composite { value } => Some(value), + WordSchema::Simple { .. } => None, + } + } + + /// Returns the schema type identifier associated with whole-word init-supplied values. + pub fn word_type(&self) -> SchemaTypeId { + match self { + WordSchema::Simple { r#type, .. } => r#type.clone(), + WordSchema::Composite { .. } => SchemaTypeId::native_word(), + } + } + + fn collect_init_value_requirements( + &self, + slot_prefix: StorageValueName, + description: Option, + requirements: &mut BTreeMap, + ) -> Result<(), AccountComponentTemplateError> { + match self { + WordSchema::Simple { r#type, default_value } => { + if *r#type == SchemaTypeId::void() { + return Ok(()); + } + + let default_value = + default_value.map(|word| SCHEMA_TYPE_REGISTRY.display_word(r#type, word)); + + if requirements + .insert( + slot_prefix.clone(), + SchemaRequirement { + description, + r#type: r#type.clone(), + default_value, + }, + ) + .is_some() + { + return Err(AccountComponentTemplateError::DuplicateInitValueName(slot_prefix)); + } + + Ok(()) + }, + WordSchema::Composite { value } => { + for felt in value.iter() { + felt.collect_init_value_requirements(slot_prefix.clone(), requirements)?; + } + Ok(()) + }, + } + } + + /// Validates that the defined word type exists and its inner felts (if any) are valid. + fn validate(&self) -> Result<(), AccountComponentTemplateError> { + let type_exists = SCHEMA_TYPE_REGISTRY.contains_word_type(&self.word_type()); + if !type_exists { + return Err(AccountComponentTemplateError::InvalidType( + self.word_type().to_string(), + "Word".into(), + )); + } + + if let WordSchema::Simple { + r#type, + default_value: Some(default_value), + } = self + { + validate_word_value(word_type_kind(r#type), r#type, *default_value) + .map_err(AccountComponentTemplateError::StorageValueParsingError)?; + } + + if let Some(felts) = self.value() { + for felt in felts { + felt.validate()?; + } + } + + Ok(()) + } + + pub(crate) fn try_build_word( + &self, + init_storage_data: &InitStorageData, + value_prefix: StorageValueName, + ) -> Result { + match self { + WordSchema::Simple { r#type, default_value } => { + let value_name = value_prefix; + match init_storage_data.get(&value_name) { + Some(WordValue::Atomic(raw)) => SCHEMA_TYPE_REGISTRY + .try_parse_word(r#type, raw) + .map_err(AccountComponentTemplateError::StorageValueParsingError), + Some(WordValue::Elements(elements)) => { + let felts = elements + .iter() + .map(|element| { + SCHEMA_TYPE_REGISTRY + .try_parse_felt(&SchemaTypeId::native_felt(), element) + }) + .collect::, _>>() + .map_err(AccountComponentTemplateError::StorageValueParsingError)?; + let felts: [Felt; 4] = felts.try_into().expect("length is 4"); + let word = Word::from(felts); + validate_word_value(word_type_kind(r#type), r#type, word) + .map_err(AccountComponentTemplateError::StorageValueParsingError)?; + Ok(word) + }, + None => { + if *r#type == SchemaTypeId::void() { + Ok(Word::empty()) + } else { + default_value.as_ref().copied().ok_or_else(|| { + AccountComponentTemplateError::InitValueNotProvided(value_name) + }) + } + }, + } + }, + WordSchema::Composite { value } => { + let mut result = [Felt::ZERO; 4]; + for (index, felt_schema) in value.iter().enumerate() { + result[index] = + felt_schema.try_build_felt(init_storage_data, value_prefix.clone())?; + } + Ok(Word::from(result)) + }, + } + } + + pub(crate) fn validate_word_value( + &self, + slot_prefix: &StorageValueName, + label: &str, + word: Word, + ) -> Result<(), AccountComponentTemplateError> { + match self { + WordSchema::Simple { r#type, .. } => { + validate_word_value(word_type_kind(r#type), r#type, word).map_err(|err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("{label} does not match `{}`: {err}", r#type), + ) + }) + }, + WordSchema::Composite { value } => { + for (index, felt_schema) in value.iter().enumerate() { + let felt_type = felt_schema.felt_type(); + validate_felt_value(&felt_type, word[index]).map_err(|err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("{label}[{index}] does not match `{felt_type}`: {err}"), + ) + })?; + } + + Ok(()) + }, + } + } +} + +impl Serializable for WordSchema { + fn write_into(&self, target: &mut W) { + match self { + WordSchema::Simple { r#type, default_value } => { + target.write_u8(0); + target.write(r#type); + target.write(default_value); + }, + WordSchema::Composite { value } => { + target.write_u8(1); + target.write(value); + }, + } + } +} + +impl Deserializable for WordSchema { + fn read_from(source: &mut R) -> Result { + let tag = source.read_u8()?; + match tag { + 0 => { + let r#type = SchemaTypeId::read_from(source)?; + let default_value = Option::::read_from(source)?; + Ok(WordSchema::Simple { r#type, default_value }) + }, + 1 => { + let value = <[FeltSchema; 4]>::read_from(source)?; + Ok(WordSchema::Composite { value }) + }, + other => Err(DeserializationError::InvalidValue(format!( + "unknown tag '{other}' for WordSchema" + ))), + } + } +} + +impl From<[FeltSchema; 4]> for WordSchema { + fn from(value: [FeltSchema; 4]) -> Self { + WordSchema::new_value(value) + } +} + +impl From<[Felt; 4]> for WordSchema { + fn from(value: [Felt; 4]) -> Self { + WordSchema::new_simple_with_default(SchemaTypeId::native_word(), Word::from(value)) + } +} + +// FELT SCHEMA +// ================================================================================================ + +/// Supported element schema descriptors for a component's storage entries. +/// +/// Each felt element in a composed word slot is typed, can have an optional default value, and can +/// optionally be named to allow overriding at instantiation time. +/// +/// To avoid non-overridable constants, unnamed elements are allowed only when `type = "void"`, +/// which always evaluates to `0` and does not require init data. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FeltSchema { + name: Option, + description: Option, + r#type: SchemaTypeId, + default_value: Option, +} + +impl FeltSchema { + /// Creates a new required typed felt field. + pub fn new_typed(r#type: SchemaTypeId, name: impl Into) -> Self { + FeltSchema { + name: Some(name.into()), + description: None, + r#type, + default_value: None, + } + } + + /// Creates a new typed felt field with a default value. + pub fn new_typed_with_default( + r#type: SchemaTypeId, + name: impl Into, + default_value: Felt, + ) -> Self { + FeltSchema { + name: Some(name.into()), + description: None, + r#type, + default_value: Some(default_value), + } + } + + /// Creates an unnamed `void` felt element. + pub fn new_void() -> Self { + FeltSchema { + name: None, + description: None, + r#type: SchemaTypeId::void(), + default_value: None, + } + } + + /// Sets the description of the [`FeltSchema`] and returns `self`. + pub fn with_description(self, description: impl Into) -> Self { + FeltSchema { + description: Some(description.into()), + ..self + } + } + + /// Returns the felt type. + pub fn felt_type(&self) -> SchemaTypeId { + self.r#type.clone() + } + + pub fn name(&self) -> Option<&str> { + self.name.as_deref() + } + + pub fn description(&self) -> Option<&String> { + self.description.as_ref() + } + + pub fn default_value(&self) -> Option { + self.default_value + } + + fn collect_init_value_requirements( + &self, + slot_prefix: StorageValueName, + requirements: &mut BTreeMap, + ) -> Result<(), AccountComponentTemplateError> { + if self.r#type == SchemaTypeId::void() { + return Ok(()); + } + + let Some(name) = self.name.as_deref() else { + return Err(AccountComponentTemplateError::InvalidSchema( + "non-void felt elements must be named".into(), + )); + }; + let value_name = slot_prefix + .clone() + .with_suffix(name) + .map_err(|err| AccountComponentTemplateError::InvalidSchema(err.to_string()))?; + + let default_value = self + .default_value + .map(|felt| SCHEMA_TYPE_REGISTRY.display_felt(&self.r#type, felt)); + + if requirements + .insert( + value_name.clone(), + SchemaRequirement { + description: self.description.clone(), + r#type: self.r#type.clone(), + default_value, + }, + ) + .is_some() + { + return Err(AccountComponentTemplateError::DuplicateInitValueName(value_name)); + } + + Ok(()) + } + + /// Attempts to convert the [`FeltSchema`] into a [`Felt`]. + /// + /// If the schema variant is typed, the value is retrieved from `init_storage_data`, + /// identified by its key. Otherwise, the returned value is just the inner element. + pub(crate) fn try_build_felt( + &self, + init_storage_data: &InitStorageData, + value_prefix: StorageValueName, + ) -> Result { + let value_name = + match self.name.as_deref() { + Some(name) => Some(value_prefix.with_suffix(name).map_err(|err| { + AccountComponentTemplateError::InvalidSchema(err.to_string()) + })?), + None => None, + }; + + if let Some(value_name) = value_name.clone() { + match init_storage_data.get(&value_name) { + Some(WordValue::Atomic(raw)) => { + let felt = SCHEMA_TYPE_REGISTRY + .try_parse_felt(&self.r#type, raw) + .map_err(AccountComponentTemplateError::StorageValueParsingError)?; + return Ok(felt); + }, + Some(WordValue::Elements(_)) => { + return Err(AccountComponentTemplateError::InvalidInitStorageValue( + value_name, + "expected an atomic value, got a 4-element array".into(), + )); + }, + None => {}, + } + } + + if self.r#type == SchemaTypeId::void() { + return Ok(Felt::ZERO); + } + + if let Some(default_value) = self.default_value { + return Ok(default_value); + } + + let Some(value_name) = value_name else { + return Err(AccountComponentTemplateError::InvalidSchema( + "non-void felt elements must be named".into(), + )); + }; + + Err(AccountComponentTemplateError::InitValueNotProvided(value_name)) + } + + /// Validates that the defined felt type exists. + fn validate(&self) -> Result<(), AccountComponentTemplateError> { + let type_exists = SCHEMA_TYPE_REGISTRY.contains_felt_type(&self.felt_type()); + if !type_exists { + return Err(AccountComponentTemplateError::InvalidType( + self.felt_type().to_string(), + "Felt".into(), + )); + } + + if self.r#type == SchemaTypeId::void() { + if self.name.is_some() { + return Err(AccountComponentTemplateError::InvalidSchema( + "void felt elements must be unnamed".into(), + )); + } + if self.default_value.is_some() { + return Err(AccountComponentTemplateError::InvalidSchema( + "void felt elements cannot define `default-value`".into(), + )); + } + return Ok(()); + } + + if self.name.is_none() { + return Err(AccountComponentTemplateError::InvalidSchema( + "non-void felt elements must be named".into(), + )); + } + + if let Some(value) = self.default_value { + validate_felt_value(&self.felt_type(), value) + .map_err(AccountComponentTemplateError::StorageValueParsingError)?; + } + Ok(()) + } +} + +impl Serializable for FeltSchema { + fn write_into(&self, target: &mut W) { + target.write(&self.name); + target.write(&self.description); + target.write(&self.r#type); + target.write(self.default_value); + } +} + +impl Deserializable for FeltSchema { + fn read_from(source: &mut R) -> Result { + let name = Option::::read_from(source)?; + let description = Option::::read_from(source)?; + let r#type = SchemaTypeId::read_from(source)?; + let default_value = Option::::read_from(source)?; + Ok(FeltSchema { name, description, r#type, default_value }) + } +} + +// VALUE VALIDATION HELPERS +// ================================================================================================ + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum WordTypeKind { + Word, + Felt, +} + +fn word_type_kind(schema_type: &SchemaTypeId) -> WordTypeKind { + if SCHEMA_TYPE_REGISTRY.contains_felt_type(schema_type) { + WordTypeKind::Felt + } else { + WordTypeKind::Word + } +} + +fn validate_word_value( + kind: WordTypeKind, + schema_type: &SchemaTypeId, + word: Word, +) -> Result<(), super::SchemaTypeError> { + match kind { + WordTypeKind::Word => Ok(()), + WordTypeKind::Felt => { + if word[0] != Felt::ZERO || word[1] != Felt::ZERO || word[2] != Felt::ZERO { + return Err(super::SchemaTypeError::ConversionError(format!( + "expected a word of the form [0, 0, 0, ] for type `{schema_type}`" + ))); + } + validate_felt_value(schema_type, word[3]) + }, + } +} + +fn validate_felt_value( + schema_type: &SchemaTypeId, + felt: Felt, +) -> Result<(), super::SchemaTypeError> { + SCHEMA_TYPE_REGISTRY.validate_felt_value(schema_type, felt) +} + +/// Describes the schema for a storage value slot. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ValueSlotSchema { + description: Option, + word: WordSchema, +} + +impl ValueSlotSchema { + pub fn new(description: Option, word: WordSchema) -> Self { + Self { description, word } + } + + pub fn description(&self) -> Option<&String> { + self.description.as_ref() + } + + pub fn word(&self) -> &WordSchema { + &self.word + } + + fn collect_init_value_requirements( + &self, + slot_prefix: StorageValueName, + requirements: &mut BTreeMap, + ) -> Result<(), AccountComponentTemplateError> { + self.word.collect_init_value_requirements( + slot_prefix, + self.description.clone(), + requirements, + ) + } + + pub fn try_build_word( + &self, + init_storage_data: &InitStorageData, + value_prefix: StorageValueName, + ) -> Result { + self.word.try_build_word(init_storage_data, value_prefix) + } + + pub(crate) fn validate( + &self, + _slot_name: &StorageSlotName, + ) -> Result<(), AccountComponentTemplateError> { + self.word.validate()?; + Ok(()) + } +} + +impl Serializable for ValueSlotSchema { + fn write_into(&self, target: &mut W) { + target.write(&self.description); + target.write(&self.word); + } +} + +impl Deserializable for ValueSlotSchema { + fn read_from(source: &mut R) -> Result { + let description = Option::::read_from(source)?; + let word = WordSchema::read_from(source)?; + Ok(ValueSlotSchema::new(description, word)) + } +} + +/// Describes the schema for a storage map slot. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MapSlotSchema { + description: Option, + default_values: Option>, + key_schema: WordSchema, + value_schema: WordSchema, +} + +impl MapSlotSchema { + pub fn new( + description: Option, + default_values: Option>, + key_schema: WordSchema, + value_schema: WordSchema, + ) -> Self { + Self { + description, + default_values, + key_schema, + value_schema, + } + } + + pub fn description(&self) -> Option<&String> { + self.description.as_ref() + } + + pub fn try_build_map( + &self, + init_storage_data: &InitStorageData, + slot_prefix: StorageValueName, + ) -> Result { + let mut entries = self.default_values.clone().unwrap_or_default(); + + if init_storage_data.get(&slot_prefix).is_some() + && init_storage_data.map_entries(&slot_prefix).is_none() + { + return Err(AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix, + "expected a map, got a value".into(), + )); + } + + if let Some(init_entries) = init_storage_data.map_entries(&slot_prefix) { + let mut parsed_entries = Vec::with_capacity(init_entries.len()); + for (index, (raw_key, raw_value)) in init_entries.iter().enumerate() { + let key_label = format!("map entry[{index}].key"); + let value_label = format!("map entry[{index}].value"); + + let key = parse_word_value_with_schema( + &self.key_schema, + raw_key, + &slot_prefix, + key_label.as_str(), + )?; + let value = parse_word_value_with_schema( + &self.value_schema, + raw_value, + &slot_prefix, + value_label.as_str(), + )?; + + parsed_entries.push((key, value)); + } + + // Reject duplicate keys in init-provided entries. + let _ = StorageMap::with_entries(parsed_entries.iter().copied()).map_err(|err| { + AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err)) + })?; + + for (key, value) in parsed_entries.iter() { + entries.insert(*key, *value); + } + } + + if entries.is_empty() { + return Ok(StorageMap::new()); + } + + StorageMap::with_entries(entries) + .map_err(|err| AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err))) + } + + pub fn key_schema(&self) -> &WordSchema { + &self.key_schema + } + + pub fn value_schema(&self) -> &WordSchema { + &self.value_schema + } + + pub fn default_values(&self) -> Option> { + self.default_values.clone() + } + + fn validate(&self) -> Result<(), AccountComponentTemplateError> { + self.key_schema.validate()?; + self.value_schema.validate()?; + Ok(()) + } +} + +pub(super) fn parse_word_value_with_schema( + schema: &WordSchema, + raw_value: &WordValue, + slot_prefix: &StorageValueName, + label: &str, +) -> Result { + match schema { + WordSchema::Simple { r#type, .. } => match raw_value { + WordValue::Atomic(value) => { + SCHEMA_TYPE_REGISTRY.try_parse_word(r#type, value).map_err(|err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("failed to parse {label} as `{}`: {err}", r#type), + ) + }) + }, + WordValue::Elements(elements) => { + let felts: Vec = elements + .iter() + .map(|element| { + SCHEMA_TYPE_REGISTRY.try_parse_felt(&SchemaTypeId::native_felt(), element) + }) + .collect::>() + .map_err(|err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("failed to parse {label} element as `felt`: {err}"), + ) + })?; + let felts: [Felt; 4] = felts.try_into().expect("length is 4"); + let word = Word::from(felts); + schema.validate_word_value(slot_prefix, label, word)?; + Ok(word) + }, + }, + WordSchema::Composite { value } => match raw_value { + WordValue::Elements(elements) => { + let mut felts = [Felt::ZERO; 4]; + for index in 0..4 { + let felt_type = value[index].felt_type(); + felts[index] = SCHEMA_TYPE_REGISTRY + .try_parse_felt(&felt_type, &elements[index]) + .map_err(|err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("failed to parse {label}[{index}] as `{felt_type}`: {err}"), + ) + })?; + } + + Ok(Word::from(felts)) + }, + WordValue::Atomic(value) => { + Err(AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!( + "{label} must be an array of 4 elements for a composite schema, got atomic `{value}`" + ), + )) + }, + }, + } +} + +impl Serializable for MapSlotSchema { + fn write_into(&self, target: &mut W) { + target.write(&self.description); + target.write(&self.default_values); + target.write(&self.key_schema); + target.write(&self.value_schema); + } +} + +impl Deserializable for MapSlotSchema { + fn read_from(source: &mut R) -> Result { + let description = Option::::read_from(source)?; + let default_values = Option::>::read_from(source)?; + let key_schema = WordSchema::read_from(source)?; + let value_schema = WordSchema::read_from(source)?; + Ok(MapSlotSchema::new(description, default_values, key_schema, value_schema)) + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use alloc::collections::BTreeMap; + + use super::*; + + #[test] + fn map_slot_schema_default_values_returns_map() { + let word_schema = WordSchema::new_simple(SchemaTypeId::native_word()); + let mut default_values = BTreeMap::new(); + default_values.insert( + Word::from([Felt::new(1), Felt::new(0), Felt::new(0), Felt::new(0)]), + Word::from([Felt::new(10), Felt::new(11), Felt::new(12), Felt::new(13)]), + ); + let slot = MapSlotSchema::new( + Some("static map".into()), + Some(default_values), + word_schema.clone(), + word_schema, + ); + + let mut expected = BTreeMap::new(); + expected.insert( + Word::from([Felt::new(1), Felt::new(0), Felt::new(0), Felt::new(0)]), + Word::from([Felt::new(10), Felt::new(11), Felt::new(12), Felt::new(13)]), + ); + + assert_eq!(slot.default_values(), Some(expected)); + } + + #[test] + fn value_slot_schema_exposes_felt_schema_types() { + let felt_values = [ + FeltSchema::new_typed(SchemaTypeId::u8(), "a"), + FeltSchema::new_typed(SchemaTypeId::u16(), "b"), + FeltSchema::new_typed(SchemaTypeId::u32(), "c"), + FeltSchema::new_typed(SchemaTypeId::new("felt").unwrap(), "d"), + ]; + + let slot = ValueSlotSchema::new(None, WordSchema::new_value(felt_values)); + let WordSchema::Composite { value } = slot.word() else { + panic!("expected composite word schema"); + }; + + assert_eq!(value[0].felt_type(), SchemaTypeId::u8()); + assert_eq!(value[1].felt_type(), SchemaTypeId::u16()); + assert_eq!(value[2].felt_type(), SchemaTypeId::u32()); + assert_eq!(value[3].felt_type(), SchemaTypeId::new("felt").unwrap()); + } + + #[test] + fn map_slot_schema_key_and_value_types() { + let key_schema = WordSchema::new_simple(SchemaTypeId::new("sampling::Key").unwrap()); + + let value_schema = WordSchema::new_value([ + FeltSchema::new_typed(SchemaTypeId::native_felt(), "a"), + FeltSchema::new_typed(SchemaTypeId::native_felt(), "b"), + FeltSchema::new_typed(SchemaTypeId::native_felt(), "c"), + FeltSchema::new_typed(SchemaTypeId::native_felt(), "d"), + ]); + + let slot = MapSlotSchema::new(None, None, key_schema, value_schema); + + assert_eq!( + slot.key_schema(), + &WordSchema::new_simple(SchemaTypeId::new("sampling::Key").unwrap()) + ); + + let WordSchema::Composite { value } = slot.value_schema() else { + panic!("expected composite word schema for map values"); + }; + for felt in value.iter() { + assert_eq!(felt.felt_type(), SchemaTypeId::native_felt()); + } + } + + #[test] + fn value_slot_schema_accepts_typed_word_init_value() { + let slot = ValueSlotSchema::new(None, WordSchema::new_simple(SchemaTypeId::native_word())); + let slot_prefix: StorageValueName = "demo::slot".parse().unwrap(); + + let expected = Word::from([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]); + let init_data = + InitStorageData::new([(slot_prefix.clone(), expected.to_string().into())], []); + + let built = slot.try_build_word(&init_data, slot_prefix).unwrap(); + assert_eq!(built, expected); + } + + #[test] + fn value_slot_schema_accepts_felt_typed_word_init_value() { + let slot = ValueSlotSchema::new(None, WordSchema::new_simple(SchemaTypeId::u8())); + let slot_prefix: StorageValueName = "demo::u8_word".parse().unwrap(); + + let init_data = InitStorageData::new([(slot_prefix.clone(), "6".into())], []); + + let built = slot.try_build_word(&init_data, slot_prefix).unwrap(); + assert_eq!(built, Word::from([Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(6)])); + } + + #[test] + fn value_slot_schema_accepts_typed_felt_init_value_in_composed_word() { + let word = WordSchema::new_value([ + FeltSchema::new_typed(SchemaTypeId::u8(), "a"), + FeltSchema::new_typed_with_default(SchemaTypeId::native_felt(), "b", Felt::new(2)), + FeltSchema::new_typed_with_default(SchemaTypeId::native_felt(), "c", Felt::new(3)), + FeltSchema::new_typed_with_default(SchemaTypeId::native_felt(), "d", Felt::new(4)), + ]); + let slot = ValueSlotSchema::new(None, word); + + let init_data = InitStorageData::new([("demo::slot.a".parse().unwrap(), "1".into())], []); + + let built = slot.try_build_word(&init_data, "demo::slot".parse().unwrap()).unwrap(); + assert_eq!(built, Word::from([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])); + } + + #[test] + fn map_slot_schema_accepts_typed_map_init_value() { + let word_schema = WordSchema::new_simple(SchemaTypeId::native_word()); + let slot = MapSlotSchema::new(None, None, word_schema.clone(), word_schema); + let slot_prefix: StorageValueName = "demo::map".parse().unwrap(); + + let entries = vec![( + WordValue::Elements(["1".into(), "0".into(), "0".into(), "0".into()]), + WordValue::Elements(["10".into(), "11".into(), "12".into(), "13".into()]), + )]; + let init_data = InitStorageData::new([], [(slot_prefix.clone(), entries.clone())]); + + let built = slot.try_build_map(&init_data, slot_prefix).unwrap(); + let expected = StorageMap::with_entries([( + Word::from([Felt::new(1), Felt::new(0), Felt::new(0), Felt::new(0)]), + Word::from([Felt::new(10), Felt::new(11), Felt::new(12), Felt::new(13)]), + )]) + .unwrap(); + assert_eq!(built, expected); + } + + #[test] + fn map_slot_schema_missing_init_value_defaults_to_empty_map() { + let word_schema = WordSchema::new_simple(SchemaTypeId::native_word()); + let slot = MapSlotSchema::new(None, None, word_schema.clone(), word_schema); + let built = slot + .try_build_map(&InitStorageData::default(), "demo::map".parse().unwrap()) + .unwrap(); + assert_eq!(built, StorageMap::new()); + } +} diff --git a/crates/miden-objects/src/account/component/storage/toml/init_storage_data.rs b/crates/miden-objects/src/account/component/storage/toml/init_storage_data.rs new file mode 100644 index 0000000000..99e2e7af41 --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/toml/init_storage_data.rs @@ -0,0 +1,184 @@ +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +use serde::Deserialize; +use thiserror::Error; + +use super::super::{InitStorageData, StorageValueName, StorageValueNameError, WordValue}; +use super::RawMapEntrySchema; + +impl InitStorageData { + /// Creates an instance of [`InitStorageData`] from a TOML string. + /// + /// This method parses the provided TOML and flattens nested tables into + /// dot‑separated keys using [`StorageValueName`] as keys. + /// + /// Atomic values must be strings (e.g. `"0x1234"`, `"16"`, `"BTC"`). + /// + /// Arrays are supported for: + /// - storage map slots: an array of inline tables of the form `{ key = , value = + /// }`, + /// - word values: a 4-element array of field elements. + /// + /// # Errors + /// + /// - If the TOML string fails to parse + /// - If duplicate keys are found after parsing + /// - If empty tables are found in the string + /// - If the TOML string includes unsupported arrays + pub fn from_toml(toml_str: &str) -> Result { + // TOML documents are always parsed as a root table. + let table: toml::Table = toml::from_str(toml_str)?; + let mut value_entries = BTreeMap::new(); + let mut map_entries = BTreeMap::new(); + // Start at the root (no prefix yet). + Self::flatten_parse_toml_value( + None, + toml::Value::Table(table), + &mut value_entries, + &mut map_entries, + )?; + + Ok(InitStorageData::new(value_entries, map_entries)) + } + + /// Recursively flattens a TOML `Value` into a flat mapping. + /// + /// When recursing into nested tables, keys are combined using + /// [`StorageValueName::with_suffix`]. If an encountered table is empty (and not the top-level), + /// an error is returned. + fn flatten_parse_toml_value( + prefix: Option, + value: toml::Value, + value_entries: &mut BTreeMap, + map_entries: &mut BTreeMap>, + ) -> Result<(), InitStorageDataError> { + match value { + toml::Value::Table(table) => { + // If this is not the root and the table is empty, error + if let Some(prefix) = prefix.as_ref() + && table.is_empty() + { + return Err(InitStorageDataError::EmptyTable(prefix.to_string())); + } + for (key, val) in table { + let new_prefix = match prefix.as_ref() { + None => { + key.parse().map_err(InitStorageDataError::InvalidStorageValueName)? + }, + Some(prefix) => prefix + .clone() + .with_suffix(&key) + .map_err(InitStorageDataError::InvalidStorageValueName)?, + }; + Self::flatten_parse_toml_value( + Some(new_prefix), + val, + value_entries, + map_entries, + )?; + } + }, + toml::Value::Array(items) if items.is_empty() => { + let prefix = prefix.expect("arrays must have a key prefix"); + if value_entries.contains_key(&prefix) || map_entries.contains_key(&prefix) { + return Err(InitStorageDataError::DuplicateKey(prefix.to_string())); + } + map_entries.insert(prefix, Vec::new()); + }, + toml::Value::Array(items) => { + let prefix = prefix.expect("arrays must have a key prefix"); + // Arrays can be either: + // - map entries: an array of inline tables `{ key = ..., value = ... }` + // - a 4-element word value: an array of 4 field elements + if items.iter().all(|item| matches!(item, toml::Value::Table(_))) { + let entries = items.into_iter().map(parse_map_entry_value).collect::, + _, + >>( + )?; + if value_entries.contains_key(&prefix) || map_entries.contains_key(&prefix) { + return Err(InitStorageDataError::DuplicateKey(prefix.to_string())); + } + map_entries.insert(prefix, entries); + } else if items.len() == 4 + && items.iter().all(|item| matches!(item, toml::Value::String(_))) + { + let elements: [String; 4] = items + .into_iter() + .map(|value| match value { + toml::Value::String(s) => Ok(s), + _ => Err(InitStorageDataError::ArraysNotSupported { + key: prefix.to_string(), + len: 4, + }), + }) + .collect::, _>>()? + .try_into() + .expect("length was checked above"); + if value_entries.contains_key(&prefix) || map_entries.contains_key(&prefix) { + return Err(InitStorageDataError::DuplicateKey(prefix.to_string())); + } + value_entries.insert(prefix, WordValue::Elements(elements)); + } else { + return Err(InitStorageDataError::ArraysNotSupported { + key: prefix.to_string(), + len: items.len(), + }); + } + }, + toml_value => match toml_value { + toml::Value::String(s) => { + let prefix = prefix.expect("atomic values must have a key prefix"); + if value_entries.contains_key(&prefix) || map_entries.contains_key(&prefix) { + return Err(InitStorageDataError::DuplicateKey(prefix.to_string())); + } + value_entries.insert(prefix, WordValue::Atomic(s)); + }, + _ => { + let prefix = prefix.expect("atomic values must have a key prefix"); + return Err(InitStorageDataError::NonStringAtomic(prefix.to_string())); + }, + }, + } + Ok(()) + } +} + +#[derive(Debug, Error)] +pub enum InitStorageDataError { + #[error("failed to parse TOML: {0}")] + InvalidToml(#[from] toml::de::Error), + + #[error("empty table encountered for key `{0}`")] + EmptyTable(String), + + #[error("duplicate init key `{0}`")] + DuplicateKey(String), + + #[error( + "invalid input for `{key}`: unsupported array value (len {len}); expected either a map entry list (array of inline tables with `key` and `value`) or a 4-element word array of strings" + )] + ArraysNotSupported { key: String, len: usize }, + + #[error("invalid input for `{0}`: init values must be strings")] + NonStringAtomic(String), + + #[error("invalid storage value name")] + InvalidStorageValueName(#[source] StorageValueNameError), + + #[error("invalid map entry: {0}")] + InvalidMapEntrySchema(String), +} + +/// Parses a `{ key, value }` table into a `(Word, Word)` pair, rejecting typed fields. +fn parse_map_entry_value( + item: toml::Value, +) -> Result<(WordValue, WordValue), InitStorageDataError> { + // Try to deserialize the user input as a map entry + let entry: RawMapEntrySchema = RawMapEntrySchema::deserialize(item) + .map_err(|err| InitStorageDataError::InvalidMapEntrySchema(err.to_string()))?; + + Ok((entry.key, entry.value)) +} diff --git a/crates/miden-objects/src/account/component/storage/toml/mod.rs b/crates/miden-objects/src/account/component/storage/toml/mod.rs new file mode 100644 index 0000000000..130f57d954 --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/toml/mod.rs @@ -0,0 +1,497 @@ +use alloc::collections::{BTreeMap, BTreeSet}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +use miden_core::{Felt, Word}; +use semver::Version; +use serde::de::Error as _; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use super::super::{ + AccountStorageSchema, + FeltSchema, + MapSlotSchema, + StorageSlotSchema, + StorageValueName, + ValueSlotSchema, + WordSchema, + WordValue, +}; +use crate::account::component::storage::type_registry::SCHEMA_TYPE_REGISTRY; +use crate::account::component::{AccountComponentMetadata, SchemaTypeId}; +use crate::account::{AccountType, StorageSlotName}; +use crate::errors::AccountComponentTemplateError; + +mod init_storage_data; +mod serde_impls; + +#[cfg(test)] +mod tests; + +// ACCOUNT COMPONENT METADATA TOML FROM/TO +// ================================================================================================ + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +struct RawAccountComponentMetadata { + name: String, + description: String, + version: Version, + supported_types: BTreeSet, + #[serde(rename = "storage")] + #[serde(default)] + storage: RawStorageSchema, +} + +impl AccountComponentMetadata { + /// Deserializes `toml_string` and validates the resulting [AccountComponentMetadata] + /// + /// # Errors + /// + /// - If deserialization fails + /// - If the schema specifies storage slots with duplicates. + /// - If the schema contains invalid slot definitions. + pub fn from_toml(toml_string: &str) -> Result { + let raw: RawAccountComponentMetadata = toml::from_str(toml_string) + .map_err(AccountComponentTemplateError::TomlDeserializationError)?; + + let RawStorageSchema { slots } = raw.storage; + let mut fields = Vec::with_capacity(slots.len()); + + for slot in slots { + fields.push(slot.try_into_slot_schema()?); + } + + let storage_schema = AccountStorageSchema::new(fields)?; + Ok(Self::new( + raw.name, + raw.description, + raw.version, + raw.supported_types, + storage_schema, + )) + } + + /// Serializes the account component metadata into a TOML string. + pub fn to_toml(&self) -> Result { + let toml = + toml::to_string(self).map_err(AccountComponentTemplateError::TomlSerializationError)?; + Ok(toml) + } +} + +// ACCOUNT STORAGE SCHEMA SERIALIZATION +// ================================================================================================ + +/// Raw TOML storage schema: +/// +/// - `[[storage.slots]]` for both value and map slots. +/// +/// Slot kind is inferred by the shape of the `type` field: +/// - `type = "..."` or `type = [ ... ]` => value slot +/// - `type = { ... }` => map slot +#[derive(Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +struct RawStorageSchema { + #[serde(default)] + slots: Vec, +} + +/// Storage slot type descriptor. +/// +/// This field accepts either: +/// - a type identifier (e.g. `"word"`, `"u16"`, `"miden::standards::auth::rpo_falcon512::pub_key"`) +/// for simple word slots, +/// - an array of 4 [`FeltSchema`] descriptors for composite word slots, or +/// - a table `{ key = ..., value = ... }` for map slots. +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +enum RawSlotType { + Word(RawWordType), + Map(RawMapType), +} + +/// A word type descriptor. +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +enum RawWordType { + TypeIdentifier(SchemaTypeId), + FeltSchemaArray(Vec), +} + +/// A map type descriptor. +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +struct RawMapType { + key: RawWordType, + value: RawWordType, +} + +// ACCOUNT STORAGE SCHEMA SERDE +// ================================================================================================ + +impl Serialize for AccountStorageSchema { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let slots = self + .slots() + .iter() + .map(|(slot_name, schema)| RawStorageSlotSchema::from_slot(slot_name, schema)) + .collect(); + + RawStorageSchema { slots }.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for AccountStorageSchema { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // First, look at the raw representation + let raw = RawStorageSchema::deserialize(deserializer)?; + let mut fields = Vec::with_capacity(raw.slots.len()); + + for slot in raw.slots { + let (slot_name, schema) = slot.try_into_slot_schema().map_err(D::Error::custom)?; + fields.push((slot_name, schema)); + } + + AccountStorageSchema::new(fields).map_err(D::Error::custom) + } +} + +// ACCOUNT STORAGE SCHEMA SERDE HELPERS +// ================================================================================================ + +/// Raw storage slot schemas contain the raw representation that can get deserialized from TOML. +/// Specifically, it expresses the different combination of fields that expose the different types +/// of slots. +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +struct RawStorageSlotSchema { + /// The name of the storage slot, in `StorageSlotName` format (e.g. + /// `my_project::module::slot`). + name: String, + #[serde(default)] + description: Option, + /// Slot type descriptor. + /// + /// - If `type = { ... }`, this is a map slot. + /// - If `type = [ ... ]`, this is a composite word slot whose schema is described by 4 + /// [`FeltSchema`] descriptors. + /// - Otherwise, if `type = "..."`, this is a simple word slot whose value is supplied at + /// instantiation time unless `default-value` is set (or the type is `void`). + #[serde(rename = "type")] + r#type: RawSlotType, + /// The (overridable) default value for a simple word slot. + #[serde(default)] + default_value: Option, + /// Default map entries. + /// + /// These entries must be fully-specified values. If the map should be populated at + /// instantiation time, omit `default-values` and provide entries via init storage data. + #[serde(default)] + default_values: Option>, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +struct RawMapEntrySchema { + key: WordValue, + value: WordValue, +} + +impl RawStorageSlotSchema { + // SERIALIZATION + // -------------------------------------------------------------------------------------------- + + fn from_slot(slot_name: &StorageSlotName, schema: &StorageSlotSchema) -> Self { + match schema { + StorageSlotSchema::Value(schema) => Self::from_value_slot(slot_name, schema), + StorageSlotSchema::Map(schema) => Self::from_map_slot(slot_name, schema), + } + } + + fn from_value_slot(slot_name: &StorageSlotName, schema: &ValueSlotSchema) -> Self { + let word = schema.word(); + let (r#type, default_value) = match word { + WordSchema::Simple { r#type, default_value } => ( + RawSlotType::Word(RawWordType::TypeIdentifier(r#type.clone())), + default_value.map(|word| WordValue::from_word(r#type, word)), + ), + WordSchema::Composite { value } => { + (RawSlotType::Word(RawWordType::FeltSchemaArray(value.to_vec())), None) + }, + }; + + Self { + name: slot_name.as_str().to_string(), + description: schema.description().cloned(), + r#type, + default_value, + default_values: None, + } + } + + fn from_map_slot(slot_name: &StorageSlotName, schema: &MapSlotSchema) -> Self { + let default_values = schema.default_values().map(|default_values| { + default_values + .into_iter() + .map(|(key, value)| RawMapEntrySchema { + key: WordValue::from_word(&schema.key_schema().word_type(), key), + value: WordValue::from_word(&schema.value_schema().word_type(), value), + }) + .collect() + }); + + let key_type = match schema.key_schema() { + WordSchema::Simple { r#type, .. } => RawWordType::TypeIdentifier(r#type.clone()), + WordSchema::Composite { value } => RawWordType::FeltSchemaArray(value.to_vec()), + }; + + let value_type = match schema.value_schema() { + WordSchema::Simple { r#type, .. } => RawWordType::TypeIdentifier(r#type.clone()), + WordSchema::Composite { value } => RawWordType::FeltSchemaArray(value.to_vec()), + }; + + Self { + name: slot_name.as_str().to_string(), + description: schema.description().cloned(), + r#type: RawSlotType::Map(RawMapType { key: key_type, value: value_type }), + default_value: None, + default_values, + } + } + + // DESERIALIZATION + // -------------------------------------------------------------------------------------------- + + /// Converts the raw representation into a tuple of the storage slot name and its schema. + fn try_into_slot_schema( + self, + ) -> Result<(StorageSlotName, StorageSlotSchema), AccountComponentTemplateError> { + let RawStorageSlotSchema { + name, + description, + r#type, + default_value, + default_values, + } = self; + + let slot_name_raw = name; + let slot_name = StorageSlotName::new(slot_name_raw.clone()).map_err(|err| { + AccountComponentTemplateError::InvalidSchema(format!( + "invalid storage slot name `{slot_name_raw}`: {err}" + )) + })?; + + let description = + description.and_then(|d| if d.trim().is_empty() { None } else { Some(d) }); + + let slot_prefix = StorageValueName::from_slot_name(&slot_name); + + if default_value.is_some() && default_values.is_some() { + return Err(AccountComponentTemplateError::InvalidSchema( + "storage slot schema cannot define both `default-value` and `default-values`" + .into(), + )); + } + + match r#type { + RawSlotType::Map(map_type) => { + if default_value.is_some() { + return Err(AccountComponentTemplateError::InvalidSchema( + "map slots cannot define `default-value`".into(), + )); + } + + let RawMapType { key: key_type, value: value_type } = map_type; + let key_schema = Self::parse_word_schema(key_type, "`type.key`")?; + let value_schema = Self::parse_word_schema(value_type, "`type.value`")?; + + let default_values = default_values + .map(|entries| { + Self::parse_default_map_entries( + entries, + &key_schema, + &value_schema, + &slot_prefix, + ) + }) + .transpose()?; + + Ok(( + slot_name, + StorageSlotSchema::Map(MapSlotSchema::new( + description, + default_values, + key_schema, + value_schema, + )), + )) + }, + + RawSlotType::Word(word_type) => { + if default_values.is_some() { + return Err(AccountComponentTemplateError::InvalidSchema( + "`default-values` can be specified only for map slots (use `type = { ... }`)" + .into(), + )); + } + + match word_type { + RawWordType::TypeIdentifier(r#type) => { + if r#type.as_str() == "map" { + return Err(AccountComponentTemplateError::InvalidSchema( + "value slots cannot use `type = \"map\"`; use `type = { key = , value = }` instead" + .into(), + )); + } + + let word = default_value + .as_ref() + .map(|default_value| { + default_value.try_parse_as_typed_word( + &r#type, + &slot_prefix, + "default value", + ) + }) + .transpose()?; + + let word_schema = match word { + Some(word) => WordSchema::new_simple_with_default(r#type, word), + None => WordSchema::new_simple(r#type), + }; + + Ok(( + slot_name, + StorageSlotSchema::Value(ValueSlotSchema::new( + description, + word_schema, + )), + )) + }, + + RawWordType::FeltSchemaArray(elements) => { + if default_value.is_some() { + return Err(AccountComponentTemplateError::InvalidSchema( + "composite word slots cannot define `default-value`".into(), + )); + } + + let elements = Self::parse_felt_schema_array(elements, "word slot `type`")?; + Ok(( + slot_name, + StorageSlotSchema::Value(ValueSlotSchema::new( + description, + WordSchema::new_value(elements), + )), + )) + }, + } + }, + } + } + + fn parse_word_schema( + raw: RawWordType, + label: &str, + ) -> Result { + match raw { + RawWordType::TypeIdentifier(r#type) => Ok(WordSchema::new_simple(r#type)), + RawWordType::FeltSchemaArray(elements) => { + let elements = Self::parse_felt_schema_array(elements, label)?; + Ok(WordSchema::new_value(elements)) + }, + } + } + + fn parse_felt_schema_array( + elements: Vec, + label: &str, + ) -> Result<[FeltSchema; 4], AccountComponentTemplateError> { + if elements.len() != 4 { + return Err(AccountComponentTemplateError::InvalidSchema(format!( + "{label} must be an array of 4 elements, got {}", + elements.len() + ))); + } + Ok(elements.try_into().expect("length is 4")) + } + + fn parse_default_map_entries( + entries: Vec, + key_schema: &WordSchema, + value_schema: &WordSchema, + slot_prefix: &StorageValueName, + ) -> Result, AccountComponentTemplateError> { + let mut map = BTreeMap::new(); + + let parse = |schema: &WordSchema, raw: &WordValue, label: &str| { + super::schema::parse_word_value_with_schema(schema, raw, slot_prefix, label).map_err( + |err| { + AccountComponentTemplateError::InvalidSchema(format!( + "invalid map `{label}`: {err}" + )) + }, + ) + }; + + for (index, entry) in entries.into_iter().enumerate() { + let key_label = format!("default-values[{index}].key"); + let value_label = format!("default-values[{index}].value"); + + let key = parse(key_schema, &entry.key, &key_label)?; + let value = parse(value_schema, &entry.value, &value_label)?; + + if map.insert(key, value).is_some() { + return Err(AccountComponentTemplateError::InvalidSchema(format!( + "map storage slot `default-values[{index}]` contains a duplicate key" + ))); + } + } + + Ok(map) + } +} + +impl WordValue { + pub(super) fn try_parse_as_typed_word( + &self, + schema_type: &SchemaTypeId, + slot_prefix: &StorageValueName, + label: &str, + ) -> Result { + let word = match self { + WordValue::Atomic(value) => SCHEMA_TYPE_REGISTRY + .try_parse_word(schema_type, value) + .map_err(AccountComponentTemplateError::StorageValueParsingError)?, + WordValue::Elements(elements) => { + let felts = elements + .iter() + .map(|element| { + SCHEMA_TYPE_REGISTRY.try_parse_felt(&SchemaTypeId::native_felt(), element) + }) + .collect::, _>>() + .map_err(AccountComponentTemplateError::StorageValueParsingError)?; + let felts: [Felt; 4] = felts.try_into().expect("length is 4"); + Word::from(felts) + }, + }; + + WordSchema::new_simple(schema_type.clone()).validate_word_value( + slot_prefix, + label, + word, + )?; + Ok(word) + } + + pub(super) fn from_word(schema_type: &SchemaTypeId, word: Word) -> Self { + WordValue::Atomic(SCHEMA_TYPE_REGISTRY.display_word(schema_type, word)) + } +} diff --git a/crates/miden-objects/src/account/component/storage/toml/serde_impls.rs b/crates/miden-objects/src/account/component/storage/toml/serde_impls.rs new file mode 100644 index 0000000000..3dfb29551f --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/toml/serde_impls.rs @@ -0,0 +1,122 @@ +use alloc::string::String; + +use serde::de::Error as _; +use serde::ser::{Error as SerError, SerializeStruct}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use super::super::type_registry::SCHEMA_TYPE_REGISTRY; +use super::super::{FeltSchema, SchemaTypeId}; + +// FELT SCHEMA SERIALIZATION +// ================================================================================================ + +impl Serialize for FeltSchema { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if self.felt_type() == SchemaTypeId::void() { + let mut state = serializer.serialize_struct("FeltSchema", 2)?; + state.serialize_field("type", &SchemaTypeId::void())?; + if let Some(description) = self.description() { + state.serialize_field("description", description)?; + } + return state.end(); + } + + let name = self.name().ok_or_else(|| { + SerError::custom("invalid FeltSchema: non-void elements must have a name") + })?; + + let mut state = serializer.serialize_struct("FeltSchema", 4)?; + state.serialize_field("name", name)?; + if let Some(description) = self.description() { + state.serialize_field("description", description)?; + } + if self.felt_type() != SchemaTypeId::native_felt() { + state.serialize_field("type", &self.felt_type())?; + } + if let Some(default_value) = self.default_value() { + state.serialize_field( + "default-value", + &SCHEMA_TYPE_REGISTRY.display_felt(&self.felt_type(), default_value), + )?; + } + state.end() + } +} + +impl<'de> Deserialize<'de> for FeltSchema { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(rename_all = "kebab-case", deny_unknown_fields)] + struct RawFeltSchema { + #[serde(default)] + name: Option, + #[serde(default)] + description: Option, + #[serde(default, rename = "default-value")] + default_value: Option, + #[serde(default, rename = "type")] + r#type: Option, + } + + let raw = RawFeltSchema::deserialize(deserializer)?; + + let felt_type = raw.r#type.unwrap_or_else(SchemaTypeId::native_felt); + + let description = raw.description.and_then(|description| { + if description.trim().is_empty() { + None + } else { + Some(description) + } + }); + + if felt_type == SchemaTypeId::void() { + if raw.name.is_some() { + return Err(D::Error::custom("`type = \"void\"` elements must omit `name`")); + } + if raw.default_value.is_some() { + return Err(D::Error::custom( + "`type = \"void\"` elements cannot define `default-value`", + )); + } + + let schema = FeltSchema::new_void(); + return Ok(match description { + Some(description) => schema.with_description(description), + None => schema, + }); + } + + let Some(name) = raw.name else { + return Err(D::Error::custom("non-void elements must define `name`")); + }; + + let default_value = raw + .default_value + .map(|default_value| { + SCHEMA_TYPE_REGISTRY.try_parse_felt(&felt_type, &default_value).map_err(|err| { + D::Error::custom(format!( + "failed to parse {felt_type} as Felt for `default-value`: {err}" + )) + }) + }) + .transpose()?; + + let schema = match default_value { + Some(default_value) => { + FeltSchema::new_typed_with_default(felt_type, name, default_value) + }, + None => FeltSchema::new_typed(felt_type, name), + }; + Ok(match description { + Some(description) => schema.with_description(description), + None => schema, + }) + } +} diff --git a/crates/miden-objects/src/account/component/storage/toml/tests.rs b/crates/miden-objects/src/account/component/storage/toml/tests.rs new file mode 100644 index 0000000000..e277441a3f --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/toml/tests.rs @@ -0,0 +1,725 @@ +use alloc::string::ToString; +use core::error::Error; + +use miden_core::{Felt, FieldElement, Word}; + +use crate::account::component::toml::init_storage_data::InitStorageDataError; +use crate::account::component::{ + AccountComponentMetadata, + InitStorageData, + SchemaTypeId, + StorageSlotSchema, + StorageValueName, + WordSchema, + WordValue, +}; +use crate::account::{AccountStorage, StorageSlotContent, StorageSlotName}; +use crate::asset::TokenSymbol; +use crate::errors::AccountComponentTemplateError; + +#[test] +fn from_toml_str_with_nested_table_and_flattened() { + let toml_table = r#" + ["demo::token_metadata"] + max_supply = "1000000000" + symbol = "ETH" + decimals = "9" + "#; + + let toml_inline = r#" + "demo::token_metadata.max_supply" = "1000000000" + "demo::token_metadata.symbol" = "ETH" + "demo::token_metadata.decimals" = "9" + "#; + + let storage_table = InitStorageData::from_toml(toml_table).unwrap(); + let storage_inline = InitStorageData::from_toml(toml_inline).unwrap(); + + assert_eq!(storage_table.values(), storage_inline.values()); +} + +#[test] +fn from_toml_str_with_deeply_nested_tables_is_rejected() { + let toml_str = r#" + ["demo::token_metadata"] + [ "demo::token_metadata".nested ] + value = "42" + "#; + + assert_matches::assert_matches!( + InitStorageData::from_toml(toml_str), + Err(InitStorageDataError::InvalidStorageValueName(_)) + ); +} + +#[test] +fn from_toml_rejects_non_string_atomics() { + let toml_str = r#" + "demo::foo" = 42 + "#; + + let result = InitStorageData::from_toml(toml_str); + assert_matches::assert_matches!(result.unwrap_err(), InitStorageDataError::NonStringAtomic(_)); +} + +#[test] +fn test_error_on_array() { + let toml_str = r#" + "demo::token_metadata.v" = ["1", "2", "3", "4", "5"] + "#; + + let err = InitStorageData::from_toml(toml_str).unwrap_err(); + assert_matches::assert_matches!( + &err, + InitStorageDataError::ArraysNotSupported { key, len } + if key == "demo::token_metadata.v" && *len == 5 + ); + let msg = err.to_string(); + assert!(msg.contains("demo::token_metadata.v")); + assert!(msg.contains("len 5")); +} + +#[test] +fn parse_map_entries_from_array() { + let toml_str = r#" + "demo::my_map" = [ + { key = "0x0000000000000000000000000000000000000000000000000000000000000001", value = "0x0000000000000000000000000000000000000000000000000000000000000010" }, + { key = "0x0000000000000000000000000000000000000000000000000000000000000002", value = ["1", "2", "3", "4"] } + ] + "#; + + let storage = InitStorageData::from_toml(toml_str).expect("Failed to parse map entries"); + let map_name: StorageValueName = "demo::my_map".parse().unwrap(); + let entries = storage.map_entries(&map_name).expect("map entries missing"); + assert_eq!(entries.len(), 2); + + assert_matches::assert_matches!( + &entries[0].0, + WordValue::Atomic(v) + if v == "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + assert_matches::assert_matches!( + &entries[0].1, + WordValue::Atomic(v) + if v == "0x0000000000000000000000000000000000000000000000000000000000000010" + ); + assert_matches::assert_matches!(&entries[1].1, WordValue::Elements(elements) if elements == &[ + "1".to_string(), + "2".to_string(), + "3".to_string(), + "4".to_string(), + ]); +} + +#[test] +fn error_on_empty_subtable() { + let toml_str = r#" + ["demo::token_metadata"] + max_supply = {} + "#; + + let result = InitStorageData::from_toml(toml_str); + assert_matches::assert_matches!( + result.unwrap_err(), + InitStorageDataError::EmptyTable(key) if key == "demo::token_metadata.max_supply" + ); +} + +#[test] +fn error_on_duplicate_keys() { + let toml_str = r#" + "demo::token_metadata.max_supply" = "1000000000" + "demo::token_metadata.max_supply" = "500000000" + "#; + + let result = InitStorageData::from_toml(toml_str).unwrap_err(); + // TOML does not support duplicate keys + assert_matches::assert_matches!(result, InitStorageDataError::InvalidToml(_)); + assert!(result.source().unwrap().to_string().contains("duplicate")); +} + +#[test] +fn error_on_duplicate_keys_after_flattening() { + // `"slot.field"` should collide with `["slot"] field`. + let toml_str = r#" + "demo::token_metadata.max_supply" = "1" + + ["demo::token_metadata"] + max_supply = "2" + "#; + + let err = InitStorageData::from_toml(toml_str).unwrap_err(); + assert_matches::assert_matches!( + err, + InitStorageDataError::DuplicateKey(key) if key == "demo::token_metadata.max_supply" + ); +} + +#[test] +fn metadata_from_toml_parses_named_storage_schema() { + let toml_str = r#" + name = "Test Component" + description = "Test description" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::test_value" + description = "a demo slot" + type = "word" + + [[storage.slots]] + name = "demo::my_map" + type = { key = "word", value = "word" } + "#; + + let metadata = AccountComponentMetadata::from_toml(toml_str).unwrap(); + let requirements = metadata.schema_requirements(); + + assert!(requirements.contains_key(&"demo::test_value".parse::().unwrap())); + assert!(!requirements.contains_key(&"demo::my_map".parse::().unwrap())); +} + +#[test] +fn metadata_from_toml_rejects_typed_fields_in_static_map_values() { + let toml_str = r#" + name = "Test Component" + description = "Test description" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::my_map" + type = { key = "word", value = "word" } + default-values = [ + { key = "0x1", value = { type = "word" } }, + ] + "#; + + assert_matches::assert_matches!( + AccountComponentMetadata::from_toml(toml_str), + Err(AccountComponentTemplateError::TomlDeserializationError(_)) + ); +} + +#[test] +fn metadata_from_toml_rejects_reserved_slot_names() { + let reserved_slot = AccountStorage::faucet_sysdata_slot().as_str(); + + let toml_str = format!( + r#" + name = "Test Component" + description = "Test description" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "{reserved_slot}" + type = "word" + "# + ); + + assert_matches::assert_matches!( + AccountComponentMetadata::from_toml(&toml_str), + Err(AccountComponentTemplateError::ReservedSlotName(_)) + ); +} + +#[test] +fn metadata_toml_round_trip_value_and_map_slots() { + let toml_str = r#" + name = "round trip" + description = "test round-trip" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::single_value" + description = "single word slot" + type = "word" + default-value = "0x1" + + [[storage.slots]] + name = "demo::statemap" + type = { key = "word", value = "word" } + default-values = [ + { key = "0x000000000000ed5d", value = "0x10" }, + ] + "#; + + let original = + AccountComponentMetadata::from_toml(toml_str).expect("original metadata should parse"); + let round_trip_toml = original.to_toml().expect("serialize to toml"); + let round_trip = + AccountComponentMetadata::from_toml(&round_trip_toml).expect("round-trip parse"); + + assert_eq!(original, round_trip); +} + +#[test] +fn metadata_toml_round_trip_composed_slot_with_typed_fields() { + let toml_str = r#" + name = "round trip typed fields" + description = "test composed slot typed fields" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::composed" + description = "composed word with typed fields" + type = [ + { name = "a", type = "u16" }, + { name = "b", default-value = "0x2" }, + { name = "c", default-value = "0x3" }, + { name = "d", default-value = "0x4" }, + ] + "#; + + let original = + AccountComponentMetadata::from_toml(toml_str).expect("original metadata should parse"); + + let mut requirements = original.schema_requirements(); + assert_eq!( + requirements + .remove(&"demo::composed.a".parse::().unwrap()) + .unwrap() + .r#type, + SchemaTypeId::u16() + ); + + let round_trip_toml = original.to_toml().expect("serialize to toml"); + let round_trip = + AccountComponentMetadata::from_toml(&round_trip_toml).expect("round-trip parse"); + + assert_eq!(original, round_trip); +} + +#[test] +fn metadata_toml_round_trip_typed_slots() { + let toml_str = r#" + name = "typed components" + description = "test typed slots" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::typed_value" + type = "word" + + [[storage.slots]] + name = "demo::typed_map" + type = { key = "miden::standards::auth::rpo_falcon512::pub_key", value = "miden::standards::auth::rpo_falcon512::pub_key" } + "#; + + let metadata = + AccountComponentMetadata::from_toml(toml_str).expect("typed metadata should parse"); + let schema = metadata.storage_schema(); + + let value_slot = schema + .slots() + .get(&StorageSlotName::new("demo::typed_value").unwrap()) + .expect("value slot missing"); + let value_slot = match value_slot { + StorageSlotSchema::Value(slot) => slot, + _ => panic!("expected value slot"), + }; + + let typed_value = SchemaTypeId::native_word(); + assert_eq!(value_slot.word(), &WordSchema::new_simple(typed_value.clone())); + + let map_slot = schema + .slots() + .get(&StorageSlotName::new("demo::typed_map").unwrap()) + .expect("map slot missing"); + let map_slot = match map_slot { + StorageSlotSchema::Map(slot) => slot, + _ => panic!("expected map slot"), + }; + + let pub_key_type = SchemaTypeId::new("miden::standards::auth::rpo_falcon512::pub_key").unwrap(); + assert_eq!(map_slot.key_schema(), &WordSchema::new_simple(pub_key_type.clone())); + assert_eq!(map_slot.value_schema(), &WordSchema::new_simple(pub_key_type)); + + let mut requirements = metadata.schema_requirements(); + assert_eq!( + requirements + .remove(&"demo::typed_value".parse::().unwrap()) + .unwrap() + .r#type, + typed_value + ); + assert!(!requirements.contains_key(&"demo::typed_map".parse::().unwrap())); + + let round_trip = metadata.to_toml().expect("serialize"); + let parsed: toml::Value = toml::from_str(&round_trip).unwrap(); + let storage = parsed.get("storage").unwrap().as_table().unwrap(); + let storage_slots = storage.get("slots").unwrap().as_array().unwrap(); + + let typed_value_entry = storage_slots + .iter() + .find(|entry| entry.get("name").unwrap().as_str().unwrap() == "demo::typed_value") + .unwrap(); + assert_eq!(typed_value_entry.get("type").unwrap().as_str().unwrap(), "word"); + + let typed_map_entry = storage_slots + .iter() + .find(|entry| entry.get("name").unwrap().as_str().unwrap() == "demo::typed_map") + .unwrap(); + let map_type = typed_map_entry.get("type").unwrap().as_table().unwrap(); + assert_eq!( + map_type.get("key").unwrap().as_str().unwrap(), + "miden::standards::auth::rpo_falcon512::pub_key" + ); + assert_eq!( + map_type.get("value").unwrap().as_str().unwrap(), + "miden::standards::auth::rpo_falcon512::pub_key" + ); +} + +#[test] +fn extensive_schema_metadata_and_init_toml_example() { + let metadata_toml = r#" + name = "Extensive Example" + description = "Exercises composite slots, simple typed slots, static maps, optional init maps, and map typing." + version = "0.1.0" + supported-types = ["FungibleFaucet", "RegularAccountImmutableCode"] + + # composed slot schema expressed via `type = [...]` + [[storage.slots]] + name = "demo::token_metadata" + description = "Token metadata: max_supply, symbol, decimals, reserved." + type = [ + { type = "u32", name = "max_supply", description = "Maximum supply (base units)" }, + { type = "miden::standards::fungible_faucets::metadata::token_symbol", name = "symbol", default-value = "TST" }, + { type = "u8", name = "decimals", description = "Token decimals" }, + { type = "void" }, + ] + + # simple word-typed slot (must be passed at instantiation) + [[storage.slots]] + name = "demo::owner_pub_key" + description = "Owner public key" + type = "miden::standards::auth::rpo_falcon512::pub_key" + + # simple felt-typed word slot (parsed as felt, stored as [0,0,0,]) + [[storage.slots]] + name = "demo::protocol_version" + description = "Protocol version stored as u8 in the last felt" + type = "u8" + + # word slot with an overridable default + [[storage.slots]] + name = "demo::static_word" + description = "A fully specified word slot" + type = "word" + default-value = ["0x1", "0x2", "0x3", "0x4"] + + # Word slot with explicit `type = "word"` + [[storage.slots]] + name = "demo::legacy_word" + type = "word" + default-value = "0x123" + + # Static map defaults (fully concrete key/value words) + [[storage.slots]] + name = "demo::static_map" + description = "Static map with default entries" + type = { key = "word", value = "word" } + default-values = [ + { key = "0x1", value = "0x10" }, + { key = ["0", "0", "0", "2"], value = ["0", "0", "0", "32"] }, + ] + + # Word/word map (explicit key/value types). + [[storage.slots]] + name = "demo::default_typed_map" + description = "Defaults to key/value type => word/word" + type = { key = "word", value = "word" } + + # init-populated map with key/value types + [[storage.slots]] + name = "demo::typed_map_new" + type.key = [ + { type = "felt", name = "prefix" }, + { type = "felt", name = "suffix" }, + { type = "void" }, + { type = "void" }, + ] + type.value = "u16" + "#; + + let metadata = AccountComponentMetadata::from_toml(metadata_toml).unwrap(); + + // TOML round-trips + let round_trip_toml = metadata.to_toml().unwrap(); + let round_trip = AccountComponentMetadata::from_toml(&round_trip_toml).unwrap(); + assert_eq!(metadata, round_trip); + + // map typing is always explicit + let default_map_name = StorageSlotName::new("demo::default_typed_map").unwrap(); + let StorageSlotSchema::Map(default_map) = + metadata.storage_schema().slots().get(&default_map_name).unwrap() + else { + panic!("expected map slot schema"); + }; + assert_eq!(default_map.key_schema(), &WordSchema::new_simple(SchemaTypeId::native_word())); + assert_eq!(default_map.value_schema(), &WordSchema::new_simple(SchemaTypeId::native_word())); + + // `type.key`/`type.value` parse as schema/type descriptors (not literal words). + let typed_map_new_name = StorageSlotName::new("demo::typed_map_new").unwrap(); + let StorageSlotSchema::Map(typed_map_new) = + metadata.storage_schema().slots().get(&typed_map_new_name).unwrap() + else { + panic!("expected map slot schema"); + }; + assert_eq!(typed_map_new.value_schema(), &WordSchema::new_simple(SchemaTypeId::u16())); + assert!(matches!(typed_map_new.key_schema(), WordSchema::Composite { .. })); + + // used storage slots + let requirements = metadata.schema_requirements(); + assert!(requirements.contains_key(&"demo::owner_pub_key".parse::().unwrap())); + assert!( + requirements.contains_key(&"demo::protocol_version".parse::().unwrap()) + ); + assert!( + requirements + .contains_key(&"demo::token_metadata.max_supply".parse::().unwrap()) + ); + assert!( + requirements + .contains_key(&"demo::token_metadata.decimals".parse::().unwrap()) + ); + let symbol_requirement = requirements + .get(&"demo::token_metadata.symbol".parse::().unwrap()) + .expect("symbol should be reported with a default value"); + assert_eq!( + symbol_requirement.r#type, + SchemaTypeId::new("miden::standards::fungible_faucets::metadata::token_symbol").unwrap() + ); + assert_eq!(symbol_requirement.default_value.as_deref(), Some("TST")); + assert!( + !requirements.contains_key(&"demo::typed_map_new".parse::().unwrap()) + ); + assert!(!requirements.contains_key(&"demo::static_map".parse::().unwrap())); + + // Build storage without providing optional defaulted fields. + let init_toml_defaults = r#" + "demo::owner_pub_key" = "0x1234" + "demo::protocol_version" = "7" + + "demo::token_metadata.max_supply" = "1000000" + "demo::token_metadata.decimals" = "6" + "#; + let init_defaults = InitStorageData::from_toml(init_toml_defaults).unwrap(); + let slots = metadata.storage_schema().build_storage_slots(&init_defaults).unwrap(); + + let token_metadata_name = StorageSlotName::new("demo::token_metadata").unwrap(); + let token_metadata_slot = slots.iter().find(|s| s.name() == &token_metadata_name).unwrap(); + let StorageSlotContent::Value(token_metadata_word) = token_metadata_slot.content() else { + panic!("expected value slot for token_metadata"); + }; + let symbol_felt: Felt = TokenSymbol::new("TST").unwrap().into(); + let expected_token_metadata = + Word::from([Felt::from(1_000_000u32), symbol_felt, Felt::from(6u8), Felt::ZERO]); + assert_eq!(token_metadata_word, &expected_token_metadata); + + let owner_pub_key_name = StorageSlotName::new("demo::owner_pub_key").unwrap(); + let owner_pub_key_slot = slots.iter().find(|s| s.name() == &owner_pub_key_name).unwrap(); + let StorageSlotContent::Value(owner_pub_key_word) = owner_pub_key_slot.content() else { + panic!("expected value slot for owner_pub_key"); + }; + let expected_pub_key = + Word::parse("0x0000000000000000000000000000000000000000000000000000000000001234").unwrap(); + assert_eq!(owner_pub_key_word, &expected_pub_key); + + let protocol_version_name = StorageSlotName::new("demo::protocol_version").unwrap(); + let protocol_version_slot = slots.iter().find(|s| s.name() == &protocol_version_name).unwrap(); + let StorageSlotContent::Value(protocol_version_word) = protocol_version_slot.content() else { + panic!("expected value slot for protocol_version"); + }; + assert_eq!( + protocol_version_word, + &Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::from(7u8)]) + ); + + let static_word_name = StorageSlotName::new("demo::static_word").unwrap(); + let static_word_slot = slots.iter().find(|s| s.name() == &static_word_name).unwrap(); + let StorageSlotContent::Value(static_word) = static_word_slot.content() else { + panic!("expected value slot for static_word"); + }; + assert_eq!( + static_word, + &Word::from([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]) + ); + + let legacy_word_name = StorageSlotName::new("demo::legacy_word").unwrap(); + let legacy_word_slot = slots.iter().find(|s| s.name() == &legacy_word_name).unwrap(); + let StorageSlotContent::Value(legacy_word) = legacy_word_slot.content() else { + panic!("expected value slot for legacy_word"); + }; + assert_eq!(legacy_word, &Word::parse("0x123").unwrap()); + + let static_map_name = StorageSlotName::new("demo::static_map").unwrap(); + let static_map_slot = slots.iter().find(|s| s.name() == &static_map_name).unwrap(); + let StorageSlotContent::Map(static_map) = static_map_slot.content() else { + panic!("expected map slot for static_map"); + }; + assert_eq!(static_map.num_entries(), 2); + assert_eq!(static_map.get(&Word::parse("0x1").unwrap()), Word::parse("0x10").unwrap()); + assert_eq!( + static_map.get(&Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(2)])), + Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(32)]) + ); + + let typed_map_new_slot = slots.iter().find(|s| s.name() == &typed_map_new_name).unwrap(); + let StorageSlotContent::Map(typed_map_new_contents) = typed_map_new_slot.content() else { + panic!("expected map slot for typed_map_new"); + }; + assert_eq!(typed_map_new_contents.num_entries(), 0); + + // Provide init-populated multiple map entries and rebuild. + let init_toml_with_overrides = r#" + "demo::owner_pub_key" = "0x1234" + "demo::protocol_version" = "7" + "demo::legacy_word" = "0x456" + + "demo::typed_map_new" = [ + { key = ["1", "2", "0", "0"], value = "16" }, + { key = ["3", "4", "0", "0"], value = "32" } + ] + + "demo::static_map" = [ + { key = "0x1", value = "0x99" }, # overrides default + { key = "0x3", value = "0x30" } # adds a new key + ] + + ["demo::token_metadata"] + max_supply = "1000000" + decimals = "6" + symbol = "BTC" + "#; + let init_with_overrides = InitStorageData::from_toml(init_toml_with_overrides).unwrap(); + let parsed_entries = init_with_overrides + .map_entries(&"demo::typed_map_new".parse::().unwrap()) + .expect("demo::typed_map_new map entries missing"); + assert_eq!(parsed_entries.len(), 2); + let slots_with_maps = + metadata.storage_schema().build_storage_slots(&init_with_overrides).unwrap(); + + let typed_map_new_slot = + slots_with_maps.iter().find(|s| s.name() == &typed_map_new_name).unwrap(); + let StorageSlotContent::Map(typed_map_new_contents) = typed_map_new_slot.content() else { + panic!("expected map slot for typed_map_new"); + }; + assert_eq!(typed_map_new_contents.num_entries(), 2); + + let key1 = Word::from([Felt::new(1), Felt::new(2), Felt::ZERO, Felt::ZERO]); + assert_eq!( + typed_map_new_contents.get(&key1), + Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(16)]) + ); + + let token_metadata_slot = + slots_with_maps.iter().find(|s| s.name() == &token_metadata_name).unwrap(); + let StorageSlotContent::Value(token_metadata_word) = token_metadata_slot.content() else { + panic!("expected value slot for token_metadata"); + }; + let symbol_felt: Felt = TokenSymbol::new("BTC").unwrap().into(); + let expected_token_metadata_overridden = + Word::from([Felt::from(1_000_000u32), symbol_felt, Felt::from(6u8), Felt::ZERO]); + assert_eq!(token_metadata_word, &expected_token_metadata_overridden); + + let legacy_word_slot = slots_with_maps.iter().find(|s| s.name() == &legacy_word_name).unwrap(); + let StorageSlotContent::Value(legacy_word) = legacy_word_slot.content() else { + panic!("expected value slot for legacy_word"); + }; + assert_eq!(legacy_word, &Word::parse("0x456").unwrap()); + + let static_map_slot = slots_with_maps.iter().find(|s| s.name() == &static_map_name).unwrap(); + let StorageSlotContent::Map(static_map) = static_map_slot.content() else { + panic!("expected map slot for static_map"); + }; + assert_eq!(static_map.num_entries(), 3); + assert_eq!(static_map.get(&Word::parse("0x1").unwrap()), Word::parse("0x99").unwrap()); + assert_eq!( + static_map.get(&Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(2)])), + Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(32)]) + ); + assert_eq!(static_map.get(&Word::parse("0x3").unwrap()), Word::parse("0x30").unwrap()); +} + +#[test] +fn typed_map_init_entries_are_validated() { + let metadata_toml = r#" + name = "typed map validation" + description = "validates init-provided map entries against type.key/type.value" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::typed_map" + type.key = [ + { name = "prefix" }, + { name = "suffix" }, + { type = "void" }, + { type = "void" } + ] + type.value = "u16" + "#; + + let metadata = AccountComponentMetadata::from_toml(metadata_toml).unwrap(); + + // Key schema requires the last 2 elements to be `void` (0). This also tests parsing composite + // keys against a schema + let init_toml = r#" + "demo::typed_map" = [ + { key = ["1", "2", "3", "0"], value = ["0", "0", "0", "1"] } + ] + "#; + let init_data = InitStorageData::from_toml(init_toml).unwrap(); + + assert_matches::assert_matches!( + metadata.storage_schema().build_storage_slots(&init_data), + Err(AccountComponentTemplateError::InvalidInitStorageValue(name, msg)) + if &name.to_string() == "demo::typed_map" && msg.contains("void") + ); +} + +#[test] +fn typed_map_supports_non_numeric_value_types() { + let metadata_toml = r#" + name = "typed map token_symbol" + description = "parses typed map values using slot-level type.value" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::symbol_map" + type.key = "word" + type.value = "miden::standards::fungible_faucets::metadata::token_symbol" + "#; + + let metadata = AccountComponentMetadata::from_toml(metadata_toml).unwrap(); + + let init_toml = r#" + "demo::symbol_map" = [ + { key = "0x1", value = "BTC" } + ] + "#; + let init_data = InitStorageData::from_toml(init_toml).unwrap(); + + let slots = metadata.storage_schema().build_storage_slots(&init_data).unwrap(); + let slot_name = StorageSlotName::new("demo::symbol_map").unwrap(); + let slot = slots.iter().find(|s| s.name() == &slot_name).unwrap(); + + let StorageSlotContent::Map(map) = slot.content() else { + panic!("expected map slot"); + }; + + assert_eq!(map.num_entries(), 1); + + let key = Word::parse("0x1").unwrap(); + let symbol_felt: Felt = TokenSymbol::new("BTC").unwrap().into(); + let expected_value = Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, symbol_felt]); + assert_eq!(map.get(&key), expected_value); +} diff --git a/crates/miden-objects/src/account/component/storage/type_registry.rs b/crates/miden-objects/src/account/component/storage/type_registry.rs new file mode 100644 index 0000000000..4e0c58550b --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/type_registry.rs @@ -0,0 +1,652 @@ +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; +use core::error::Error; +use core::fmt::{self, Display}; + +use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use miden_core::{Felt, Word}; +use miden_crypto::dsa::{ecdsa_k256_keccak, rpo_falcon512}; +use miden_processor::DeserializationError; +use thiserror::Error; + +use crate::asset::TokenSymbol; +use crate::utils::sync::LazyLock; + +/// A global registry for schema type converters. +/// +/// It is used during component instantiation to convert init-provided values (typically provided +/// as strings) into their respective storage values. +pub static SCHEMA_TYPE_REGISTRY: LazyLock = LazyLock::new(|| { + let mut registry = SchemaTypeRegistry::new(); + registry.register_felt_type::(); + registry.register_felt_type::(); + registry.register_felt_type::(); + registry.register_felt_type::(); + registry.register_felt_type::(); + registry.register_felt_type::(); + registry.register_word_type::(); + registry.register_word_type::(); + registry.register_word_type::(); + registry +}); + +// SCHEMA TYPE ERROR +// ================================================================================================ + +/// Errors that can occur when parsing or converting schema types. +/// +/// This enum covers various failure cases including parsing errors, conversion errors, +/// unsupported conversions, and cases where a required type is not found in the registry. +#[derive(Debug, Error)] +pub enum SchemaTypeError { + #[error("conversion error: {0}")] + ConversionError(String), + #[error("felt type ` {0}` not found in the type registry")] + FeltTypeNotFound(SchemaTypeId), + #[error("invalid type name `{0}`: {1}")] + InvalidTypeName(String, String), + #[error("failed to parse input `{input}` as `{schema_type}`")] + ParseError { + input: String, + schema_type: SchemaTypeId, + source: Box, + }, + #[error("word type ` {0}` not found in the type registry")] + WordTypeNotFound(SchemaTypeId), +} + +impl SchemaTypeError { + /// Creates a [`SchemaTypeError::ParseError`]. + pub fn parse( + input: impl Into, + schema_type: SchemaTypeId, + source: impl Error + Send + Sync + 'static, + ) -> Self { + SchemaTypeError::ParseError { + input: input.into(), + schema_type, + source: Box::new(source), + } + } +} + +// SCHEMA TYPE +// ================================================================================================ + +/// A newtype wrapper around a `String`, representing a schema type identifier. +/// +/// A valid schema identifier is a name in the style of Rust namespaces, composed of one or more +/// non-empty segments separated by `::`. Each segment can contain only ASCII alphanumerics or `_`. +/// +/// Some examples: +/// - `u32` +/// - `felt` +/// - `miden::standards::auth::rpo_falcon512::pub_key` +#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] +#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))] +#[cfg_attr(feature = "std", serde(transparent))] +pub struct SchemaTypeId(String); + +impl SchemaTypeId { + /// Creates a new [`SchemaTypeId`] from a `String`. + /// + /// The name must follow a Rust-style namespace format, consisting of one or more segments + /// (non-empty, and alphanumerical) separated by double-colon (`::`) delimiters. + /// + /// # Errors + /// + /// - If the identifier is empty. + /// - If any segment is empty or contains something other than alphanumerical + /// characters/underscores. + pub fn new(s: impl Into) -> Result { + let s = s.into(); + if s.is_empty() { + return Err(SchemaTypeError::InvalidTypeName( + s.clone(), + "schema type identifier is empty".to_string(), + )); + } + for segment in s.split("::") { + if segment.is_empty() { + return Err(SchemaTypeError::InvalidTypeName( + s.clone(), + "empty segment in schema type identifier".to_string(), + )); + } + if !segment.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + return Err(SchemaTypeError::InvalidTypeName( + s.clone(), + format!("segment '{segment}' contains invalid characters"), + )); + } + } + Ok(Self(s)) + } + + /// Returns the schema type identifier for the `void` type. + /// + /// The `void` type always parses to `0` and is intended to model reserved or padding felts. + pub fn void() -> SchemaTypeId { + SchemaTypeId::new("void").expect("type is well formed") + } + + /// Returns the schema type identifier for the native [`Felt`] type. + pub fn native_felt() -> SchemaTypeId { + SchemaTypeId::new("felt").expect("type is well formed") + } + + /// Returns the schema type identifier for the native [`Word`] type. + pub fn native_word() -> SchemaTypeId { + SchemaTypeId::new("word").expect("type is well formed") + } + + /// Returns the schema type identifier for the native `u8` type. + pub fn u8() -> SchemaTypeId { + SchemaTypeId::new("u8").expect("type is well formed") + } + + /// Returns the schema type identifier for the native `u16` type. + pub fn u16() -> SchemaTypeId { + SchemaTypeId::new("u16").expect("type is well formed") + } + + /// Returns the schema type identifier for the native `u32` type. + pub fn u32() -> SchemaTypeId { + SchemaTypeId::new("u32").expect("type is well formed") + } + + /// Returns a reference to the inner string. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl Display for SchemaTypeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl Serializable for SchemaTypeId { + fn write_into(&self, target: &mut W) { + target.write(self.0.clone()) + } +} + +impl Deserializable for SchemaTypeId { + fn read_from(source: &mut R) -> Result { + let id: String = source.read()?; + + SchemaTypeId::new(id).map_err(|err| DeserializationError::InvalidValue(err.to_string())) + } +} + +// SCHEMA REQUIREMENT +// ================================================================================================ + +/// Describes the expected type and additional metadata for an init-provided storage value. +/// +/// A schema requirement specifies the expected type identifier for an init value, along with +/// optional description and default value metadata. +/// +/// The `default_value` (when present) is the canonical string representation for this type, and +/// can be used directly in [`InitStorageData`](super::InitStorageData). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SchemaRequirement { + /// The expected type identifier. + pub r#type: SchemaTypeId, + /// An optional description providing additional context. + pub description: Option, + /// An optional default value, which can be overridden at component instantiation time. + pub default_value: Option, +} + +// SCHEMA TYPE TRAITS +// ================================================================================================ + +/// Trait for converting a string into a single `Felt`. +pub trait FeltType: Send + Sync { + /// Returns the type identifier. + fn type_name() -> SchemaTypeId + where + Self: Sized; + + /// Parses the input string into a `Felt`. + fn parse_str(input: &str) -> Result + where + Self: Sized; + + /// Displays a `Felt` in a canonical string representation for this type. + fn display_felt(value: Felt) -> Result + where + Self: Sized; +} + +/// Trait for converting a string into a single `Word`. +pub trait WordType: Send + Sync { + /// Returns the type identifier. + fn type_name() -> SchemaTypeId + where + Self: Sized; + + /// Parses the input string into a `Word`. + fn parse_str(input: &str) -> Result + where + Self: Sized; + + /// Displays a `Word` in a canonical string representation for this type. + fn display_word(value: Word) -> Result + where + Self: Sized; +} + +impl WordType for T +where + T: FeltType, +{ + fn type_name() -> SchemaTypeId { + ::type_name() + } + + fn parse_str(input: &str) -> Result { + let felt = ::parse_str(input)?; + Ok(Word::from([Felt::new(0), Felt::new(0), Felt::new(0), felt])) + } + + fn display_word(value: Word) -> Result { + if value[0] != Felt::new(0) || value[1] != Felt::new(0) || value[2] != Felt::new(0) { + return Err(SchemaTypeError::ConversionError(format!( + "expected a word of the form [0, 0, 0, ] for type `{}`", + Self::type_name() + ))); + } + ::display_felt(value[3]) + } +} + +// FELT IMPLS FOR NATIVE TYPES +// ================================================================================================ + +/// A felt type that represents irrelevant elements in a storage schema definition. +struct Void; + +impl FeltType for Void { + fn type_name() -> SchemaTypeId { + SchemaTypeId::void() + } + + fn parse_str(input: &str) -> Result { + let parsed = ::parse_str(input)?; + if parsed != Felt::new(0) { + return Err(SchemaTypeError::ConversionError("void values must be zero".to_string())); + } + Ok(Felt::new(0)) + } + + fn display_felt(value: Felt) -> Result { + if value != Felt::new(0) { + return Err(SchemaTypeError::ConversionError("void values must be zero".to_string())); + } + Ok("0".into()) + } +} + +impl FeltType for u8 { + fn type_name() -> SchemaTypeId { + SchemaTypeId::u8() + } + + fn parse_str(input: &str) -> Result { + let native: u8 = input.parse().map_err(|err| { + SchemaTypeError::parse(input.to_string(), ::type_name(), err) + })?; + Ok(Felt::from(native)) + } + + fn display_felt(value: Felt) -> Result { + let native = u8::try_from(value.as_int()).map_err(|_| { + SchemaTypeError::ConversionError(format!("value `{}` is out of range for u8", value)) + })?; + Ok(native.to_string()) + } +} + +impl FeltType for u16 { + fn type_name() -> SchemaTypeId { + SchemaTypeId::u16() + } + + fn parse_str(input: &str) -> Result { + let native: u16 = input.parse().map_err(|err| { + SchemaTypeError::parse(input.to_string(), ::type_name(), err) + })?; + Ok(Felt::from(native)) + } + + fn display_felt(value: Felt) -> Result { + let native = u16::try_from(value.as_int()).map_err(|_| { + SchemaTypeError::ConversionError(format!("value `{}` is out of range for u16", value)) + })?; + Ok(native.to_string()) + } +} + +impl FeltType for u32 { + fn type_name() -> SchemaTypeId { + SchemaTypeId::u32() + } + + fn parse_str(input: &str) -> Result { + let native: u32 = input.parse().map_err(|err| { + SchemaTypeError::parse(input.to_string(), ::type_name(), err) + })?; + Ok(Felt::from(native)) + } + + fn display_felt(value: Felt) -> Result { + let native = u32::try_from(value.as_int()).map_err(|_| { + SchemaTypeError::ConversionError(format!("value `{}` is out of range for u32", value)) + })?; + Ok(native.to_string()) + } +} + +impl FeltType for Felt { + fn type_name() -> SchemaTypeId { + SchemaTypeId::new("felt").expect("type is well formed") + } + + fn parse_str(input: &str) -> Result { + let n = if let Some(hex) = input.strip_prefix("0x").or_else(|| input.strip_prefix("0X")) { + u64::from_str_radix(hex, 16) + } else { + input.parse::() + } + .map_err(|err| { + SchemaTypeError::parse(input.to_string(), ::type_name(), err) + })?; + Felt::try_from(n).map_err(|_| SchemaTypeError::ConversionError(input.to_string())) + } + + fn display_felt(value: Felt) -> Result { + Ok(format!("0x{:x}", value.as_int())) + } +} + +impl FeltType for TokenSymbol { + fn type_name() -> SchemaTypeId { + SchemaTypeId::new("miden::standards::fungible_faucets::metadata::token_symbol") + .expect("type is well formed") + } + fn parse_str(input: &str) -> Result { + let token = TokenSymbol::new(input).map_err(|err| { + SchemaTypeError::parse(input.to_string(), ::type_name(), err) + })?; + Ok(Felt::from(token)) + } + + fn display_felt(value: Felt) -> Result { + let token = TokenSymbol::try_from(value).map_err(|err| { + SchemaTypeError::ConversionError(format!( + "invalid token_symbol value `{}`: {err}", + value.as_int() + )) + })?; + token.to_string().map_err(|err| { + SchemaTypeError::ConversionError(format!( + "failed to display token_symbol value `{}`: {err}", + value.as_int() + )) + }) + } +} + +// WORD IMPLS FOR NATIVE TYPES +// ================================================================================================ + +#[derive(Debug, Error)] +#[error("error parsing word: {0}")] +struct WordParseError(String); + +/// Pads a hex string to 64 characters (excluding the 0x prefix). +/// +/// If the input starts with "0x" and has fewer than 64 hex characters after the prefix, +/// it will be left-padded with zeros. Otherwise, returns the input unchanged. +fn pad_hex_string(input: &str) -> String { + if input.starts_with("0x") && input.len() < 66 { + // 66 = "0x" + 64 hex chars + let hex_part = &input[2..]; + let padding = "0".repeat(64 - hex_part.len()); + format!("0x{}{}", padding, hex_part) + } else { + input.to_string() + } +} + +impl WordType for Word { + fn type_name() -> SchemaTypeId { + SchemaTypeId::native_word() + } + fn parse_str(input: &str) -> Result { + Word::parse(input).map_err(|err| { + SchemaTypeError::parse( + input.to_string(), + Self::type_name(), + WordParseError(err.to_string()), + ) + }) + } + + fn display_word(value: Word) -> Result { + Ok(value.to_string()) + } +} + +impl WordType for rpo_falcon512::PublicKey { + fn type_name() -> SchemaTypeId { + SchemaTypeId::new("miden::standards::auth::rpo_falcon512::pub_key") + .expect("type is well formed") + } + fn parse_str(input: &str) -> Result { + let padded_input = pad_hex_string(input); + + Word::try_from(padded_input.as_str()).map_err(|err| { + SchemaTypeError::parse( + input.to_string(), // Use original input in error + Self::type_name(), + WordParseError(err.to_string()), + ) + }) + } + + fn display_word(value: Word) -> Result { + Ok(value.to_string()) + } +} + +impl WordType for ecdsa_k256_keccak::PublicKey { + fn type_name() -> SchemaTypeId { + SchemaTypeId::new("miden::standards::auth::ecdsa_k256_keccak::pub_key") + .expect("type is well formed") + } + fn parse_str(input: &str) -> Result { + let padded_input = pad_hex_string(input); + + Word::try_from(padded_input.as_str()).map_err(|err| { + SchemaTypeError::parse( + input.to_string(), + Self::type_name(), + WordParseError(err.to_string()), + ) + }) + } + + fn display_word(value: Word) -> Result { + Ok(value.to_string()) + } +} + +// TYPE ALIASES FOR CONVERTER CLOSURES +// ================================================================================================ + +/// Type alias for a function that converts a string into a [`Felt`] value. +type FeltTypeConverter = fn(&str) -> Result; + +/// Type alias for a function that converts a string into a [`Word`]. +type WordTypeConverter = fn(&str) -> Result; + +/// Type alias for a function that converts a [`Felt`] into a canonical string representation. +type FeltTypeDisplayer = fn(Felt) -> Result; + +/// Type alias for a function that converts a [`Word`] into a canonical string representation. +type WordTypeDisplayer = fn(Word) -> Result; + +// SCHEMA TYPE REGISTRY +// ================================================================================================ + +/// Registry for schema type converters. +/// +/// This registry maintains mappings from type identifiers (as strings) to conversion functions for +/// [`Felt`] and [`Word`] types. It is used to dynamically parse init-provided inputs into their +/// corresponding storage values. +#[derive(Clone, Debug, Default)] +pub struct SchemaTypeRegistry { + felt: BTreeMap, + word: BTreeMap, + felt_display: BTreeMap, + word_display: BTreeMap, +} + +impl SchemaTypeRegistry { + /// Creates a new, empty [`SchemaTypeRegistry`]. + /// + /// The registry is initially empty and conversion functions can be registered using the + /// `register_*_type` methods. + pub fn new() -> Self { + Self::default() + } + + /// Registers a `FeltType` converter, to interpret a string as a [`Felt``]. + pub fn register_felt_type(&mut self) { + let key = ::type_name(); + self.felt.insert(key.clone(), T::parse_str); + self.felt_display.insert(key, T::display_felt); + } + + /// Registers a `WordType` converter, to interpret a string as a [`Word`]. + pub fn register_word_type(&mut self) { + let key = ::type_name(); + self.word.insert(key.clone(), T::parse_str); + self.word_display.insert(key, T::display_word); + } + + /// Attempts to parse a string into a `Felt` using the registered converter for the given type + /// name. + /// + /// # Arguments + /// + /// - type_name: A string that acts as the type identifier. + /// - value: The string input that should be parsed. + /// + /// # Errors + /// + /// - If the type is not registered or if the conversion fails. + pub fn try_parse_felt( + &self, + type_name: &SchemaTypeId, + value: &str, + ) -> Result { + let converter = self + .felt + .get(type_name) + .ok_or(SchemaTypeError::FeltTypeNotFound(type_name.clone()))?; + converter(value) + } + + /// Validates that the given [`Felt`] conforms to the specified schema type. + pub fn validate_felt_value( + &self, + type_name: &SchemaTypeId, + felt: Felt, + ) -> Result<(), SchemaTypeError> { + let display = self + .felt_display + .get(type_name) + .ok_or(SchemaTypeError::FeltTypeNotFound(type_name.clone()))?; + display(felt).map(|_| ()) + } + + /// Converts a [`Felt`] into a canonical string representation for the given schema type. + /// + /// This is intended for serializing schemas to TOML (e.g. default values). + #[allow(dead_code)] + pub fn display_felt(&self, type_name: &SchemaTypeId, felt: Felt) -> String { + self.felt_display + .get(type_name) + .and_then(|display| display(felt).ok()) + .unwrap_or_else(|| format!("0x{:x}", felt.as_int())) + } + + /// Converts a [`Word`] into a canonical string representation for the given schema type. + /// + /// Tries displaying as word if the converter is known, otherwise tests as a felt type, and + /// if it doesn't work, the word as hex is returned + // TODO: This should return richer information (how it was converted, if it succededs as word, + // etc.) + #[allow(dead_code)] + pub fn display_word(&self, type_name: &SchemaTypeId, word: Word) -> String { + if let Some(display) = self.word_display.get(type_name) { + return display(word).unwrap_or_else(|_| word.to_string()); + } + + // Treat any registered felt type as a word type by zero-padding the remaining felts. + if self.contains_felt_type(type_name) { + return self.display_felt(type_name, word[3]); + } + + word.to_hex() + } + + /// Attempts to parse a string into a `Word` using the registered converter for the given type + /// name. + /// + /// # Arguments + /// + /// - type_name: A string that acts as the type identifier. + /// - value: The string input that should be parsed. + /// + /// # Errors + /// + /// - If the type is not registered or if the conversion fails. + pub fn try_parse_word( + &self, + type_name: &SchemaTypeId, + value: &str, + ) -> Result { + if let Some(converter) = self.word.get(type_name) { + return converter(value); + } + + // Treat any registered felt type as a word type by zero-padding the remaining felts. + if let Some(converter) = self.felt.get(type_name) { + let felt = converter(value)?; + return Ok(Word::from([Felt::new(0), Felt::new(0), Felt::new(0), felt])); + } + + Err(SchemaTypeError::WordTypeNotFound(type_name.clone())) + } + + /// Returns `true` if a `FeltType` is registered for the given type. + pub fn contains_felt_type(&self, type_name: &SchemaTypeId) -> bool { + self.felt.contains_key(type_name) + } + + /// Returns `true` if a `WordType` is registered for the given type. + /// + /// This also returns `true` for any registered felt type (as those can be embedded into a word + /// with zero-padding). + pub fn contains_word_type(&self, type_name: &SchemaTypeId) -> bool { + self.word.contains_key(type_name) || self.felt.contains_key(type_name) + } +} diff --git a/crates/miden-objects/src/account/component/storage/value_name.rs b/crates/miden-objects/src/account/component/storage/value_name.rs new file mode 100644 index 0000000000..2d3a418576 --- /dev/null +++ b/crates/miden-objects/src/account/component/storage/value_name.rs @@ -0,0 +1,219 @@ +use alloc::string::{String, ToString}; +use core::cmp::Ordering; +use core::fmt::{self, Display}; +use core::str::FromStr; + +use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use miden_processor::DeserializationError; +use thiserror::Error; + +use crate::account::StorageSlotName; +use crate::errors::StorageSlotNameError; + +/// A simple wrapper type around a string key that identifies init-provided values. +/// +/// A storage value name is a string that identifies values supplied during component +/// instantiation (via [`InitStorageData`](super::InitStorageData)). +#[derive(Clone, Debug)] +#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))] +#[cfg_attr(feature = "std", serde(try_from = "String", into = "String"))] +pub struct StorageValueName { + slot_name: StorageSlotName, + element_field: Option, +} + +impl StorageValueName { + /// Creates a [`StorageValueName`] for the given storage slot. + pub fn from_slot_name(slot_name: &StorageSlotName) -> Self { + StorageValueName { + slot_name: slot_name.clone(), + element_field: None, + } + } + + /// Adds a field-name suffix to a slot-name key, separated by a period. + pub fn with_suffix(self, suffix: &str) -> Result { + let mut key = self; + + // `StorageValueName` keys are either `slot` or `slot.field`. Appending to a key that is + // already suffixed is create an invalid name. + if key.element_field.is_some() { + return Err(StorageValueNameError::InvalidCharacter { + part: key.to_string(), + character: '.', + }); + } + + Self::validate_field_segment(suffix)?; + key.element_field = Some(suffix.to_string()); + Ok(key) + } + + fn validate_slot_prefix(prefix: &str) -> Result { + match StorageSlotName::new(prefix) { + Ok(slot_name) => Ok(slot_name), + Err(err) => { + let invalid_char = match err { + StorageSlotNameError::UnexpectedColon + | StorageSlotNameError::TooShort + | StorageSlotNameError::TooLong => ':', + StorageSlotNameError::UnexpectedUnderscore => '_', + StorageSlotNameError::InvalidCharacter => prefix + .chars() + .find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == ':')) + .unwrap_or(':'), + }; + + Err(StorageValueNameError::InvalidCharacter { + part: prefix.to_string(), + character: invalid_char, + }) + }, + } + } + + fn validate_field_segment(segment: &str) -> Result<(), StorageValueNameError> { + if segment.is_empty() { + return Err(StorageValueNameError::EmptySegment); + } + + if let Some(offending_char) = + segment.chars().find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == '-')) + { + return Err(StorageValueNameError::InvalidCharacter { + part: segment.to_string(), + character: offending_char, + }); + } + + Ok(()) + } +} + +impl PartialEq for StorageValueName { + fn eq(&self, other: &Self) -> bool { + self.slot_name.as_str() == other.slot_name.as_str() + && self.element_field.as_deref() == other.element_field.as_deref() + } +} + +impl Eq for StorageValueName {} + +impl PartialOrd for StorageValueName { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for StorageValueName { + fn cmp(&self, other: &Self) -> Ordering { + let slot_cmp = self.slot_name.as_str().cmp(other.slot_name.as_str()); + if slot_cmp != Ordering::Equal { + return slot_cmp; + } + + match (self.element_field.as_deref(), other.element_field.as_deref()) { + (None, None) => Ordering::Equal, + + // "" is a prefix of ".", so it sorts first. + (None, Some(_)) => Ordering::Less, + (Some(_), None) => Ordering::Greater, + + (Some(a), Some(b)) => a.cmp(b), + } + } +} + +impl FromStr for StorageValueName { + type Err = StorageValueNameError; + + fn from_str(value: &str) -> Result { + if value.is_empty() { + return Err(StorageValueNameError::EmptySegment); + } + + // `StorageValueName` represents: + // - a storage slot name (`StorageSlotName`), or + // - a fully-qualified storage slot field key (`named::slot.field`). + let (slot, field) = match value.split_once('.') { + Some((slot, field)) => { + Self::validate_field_segment(field)?; + + if slot.is_empty() || field.is_empty() { + return Err(StorageValueNameError::EmptySegment); + } + + (slot, Some(field)) + }, + None => (value, None), + }; + + let slot_name = Self::validate_slot_prefix(slot)?; + let field = match field { + Some(field) => { + Self::validate_field_segment(field)?; + Some(field.to_string()) + }, + None => None, + }; + + Ok(Self { slot_name, element_field: field }) + } +} + +impl TryFrom for StorageValueName { + type Error = StorageValueNameError; + + fn try_from(value: String) -> Result { + value.parse() + } +} + +impl From for String { + fn from(value: StorageValueName) -> Self { + value.to_string() + } +} + +impl From<&StorageSlotName> for StorageValueName { + fn from(value: &StorageSlotName) -> Self { + StorageValueName::from_slot_name(value) + } +} + +impl Display for StorageValueName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.element_field { + None => f.write_str(self.slot_name.as_str()), + Some(field) => { + f.write_str(self.slot_name.as_str())?; + f.write_str(".")?; + f.write_str(field) + }, + } + } +} + +impl Serializable for StorageValueName { + fn write_into(&self, target: &mut W) { + let key = self.to_string(); + target.write(&key); + } +} + +impl Deserializable for StorageValueName { + fn read_from(source: &mut R) -> Result { + let key: String = source.read()?; + key.parse().map_err(|err: StorageValueNameError| { + DeserializationError::InvalidValue(err.to_string()) + }) + } +} + +#[derive(Debug, Error)] +pub enum StorageValueNameError { + #[error("key segment is empty")] + EmptySegment, + #[error("key segment '{part}' contains invalid character '{character}'")] + InvalidCharacter { part: String, character: char }, +} diff --git a/crates/miden-objects/src/account/component/template/mod.rs b/crates/miden-objects/src/account/component/template/mod.rs deleted file mode 100644 index 5a75e41584..0000000000 --- a/crates/miden-objects/src/account/component/template/mod.rs +++ /dev/null @@ -1,521 +0,0 @@ -use alloc::collections::{BTreeMap, BTreeSet}; -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use core::str::FromStr; - -use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_mast_package::{Package, SectionId}; -use miden_processor::DeserializationError; -use semver::Version; - -use super::AccountType; -use crate::AccountError; -use crate::errors::AccountComponentTemplateError; - -mod storage; -pub use storage::*; - -// ACCOUNT COMPONENT METADATA -// ================================================================================================ - -/// Represents the full component metadata configuration. -/// -/// An account component metadata describes the component alongside its storage layout. -/// On the storage layout, placeholders can be utilized to identify values that should be provided -/// at the moment of instantiation. -/// -/// When the `std` feature is enabled, this struct allows for serialization and deserialization to -/// and from a TOML file. -/// -/// # Guarantees -/// -/// - The metadata's storage layout does not contain duplicate slots, and it always starts at slot -/// index 0. -/// - Storage slots are laid out in a contiguous manner. -/// - Each placeholder represents a single value. The expected placeholders can be retrieved with -/// [AccountComponentMetadata::get_placeholder_requirements()], which returns a map from keys to -/// [PlaceholderTypeRequirement] (which, in turn, indicates the expected value type for the -/// placeholder). -/// -/// # Example -/// -/// ``` -/// # use semver::Version; -/// # use std::collections::{BTreeMap, BTreeSet}; -/// # use std::sync::Arc; -/// # use miden_mast_package::{MastArtifact, Package, PackageManifest, Section, SectionId}; -/// # use miden_objects::{testing::account_code::CODE, account::{ -/// # AccountComponent, AccountComponentMetadata, StorageEntry, -/// # StorageValueName, FeltRepresentation, WordRepresentation, TemplateType}, -/// # assembly::Assembler, Felt}; -/// # use miden_objects::account::InitStorageData; -/// # use miden_objects::utils::Serializable; -/// # fn main() -> Result<(), Box> { -/// let first_felt = FeltRepresentation::from(Felt::new(0u64)); -/// let second_felt = FeltRepresentation::from(Felt::new(1u64)); -/// let third_felt = FeltRepresentation::from(Felt::new(2u64)); -/// // Templated element: -/// let last_element = -/// FeltRepresentation::new_template(TemplateType::new("felt")?, StorageValueName::new("foo")?); -/// -/// let word_representation = WordRepresentation::new_value( -/// [first_felt, second_felt, third_felt, last_element], -/// Some(StorageValueName::new("test_value")?.into()), -/// ) -/// .with_description("this is the first entry in the storage layout"); -/// let storage_entry = StorageEntry::new_value(0, word_representation); -/// -/// let init_storage_data = InitStorageData::new( -/// [(StorageValueName::new("test_value.foo")?, "300".to_string())], -/// BTreeMap::new(), -/// ); -/// -/// let component_template = AccountComponentMetadata::new( -/// "test name".into(), -/// "description of the component".into(), -/// Version::parse("0.1.0")?, -/// BTreeSet::new(), -/// vec![storage_entry], -/// )?; -/// -/// let library = Assembler::default().assemble_library([CODE]).unwrap(); -/// let package = Package { -/// name: "test".into(), -/// mast: MastArtifact::Library(Arc::new(library)), -/// manifest: PackageManifest::new(None), -/// sections: vec![Section::new( -/// SectionId::ACCOUNT_COMPONENT_METADATA, -/// component_template.to_bytes(), -/// )], -/// version: Default::default(), -/// description: None, -/// }; -/// let component = AccountComponent::from_package(&package, &init_storage_data)?; -/// # Ok(()) -/// # } -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "std", serde(rename_all = "kebab-case"))] -pub struct AccountComponentMetadata { - /// The human-readable name of the component. - name: String, - - /// A brief description of what this component is and how it works. - description: String, - - /// The version of the component using semantic versioning. - /// This can be used to track and manage component upgrades. - version: Version, - - /// A set of supported target account types for this component. - supported_types: BTreeSet, - - /// A list of storage entries defining the component's storage layout and initialization - /// values. - storage: Vec, -} - -impl AccountComponentMetadata { - /// Create a new [AccountComponentMetadata]. - /// - /// # Errors - /// - /// - If the specified storage slots contain duplicates. - /// - If the slot numbers do not start at zero. - /// - If the slots are not contiguous. - pub fn new( - name: String, - description: String, - version: Version, - targets: BTreeSet, - storage: Vec, - ) -> Result { - let component = Self { - name, - description, - version, - supported_types: targets, - storage, - }; - component.validate()?; - Ok(component) - } - - /// Retrieves a map of unique storage placeholder names mapped to their expected type that - /// require a value at the moment of component instantiation. - /// - /// These values will be used for initializing storage slot values, or storage map entries. - /// For a full example on how a placeholder may be utilized, please refer to the docs for - /// [AccountComponentMetadata]. - /// - /// Types for the returned storage placeholders are inferred based on their location in the - /// storage layout structure. - pub fn get_placeholder_requirements( - &self, - ) -> BTreeMap { - let mut templates = BTreeMap::new(); - for entry in self.storage_entries() { - for (name, requirement) in entry.template_requirements() { - templates.insert(name, requirement); - } - } - - templates - } - - /// Returns the name of the account component. - pub fn name(&self) -> &str { - &self.name - } - - /// Returns the description of the account component. - pub fn description(&self) -> &str { - &self.description - } - - /// Returns the semantic version of the account component. - pub fn version(&self) -> &Version { - &self.version - } - - /// Returns the account types supported by the component. - pub fn supported_types(&self) -> &BTreeSet { - &self.supported_types - } - - /// Returns the list of storage entries of the component. - pub fn storage_entries(&self) -> &Vec { - &self.storage - } - - /// Validate the [AccountComponentMetadata]. - /// - /// # Errors - /// - /// - If the specified storage entries contain duplicate names. - /// - If the template contains duplicate placeholder names. - /// - If the slot numbers do not start at zero. - /// - If the slots are not contiguous. - fn validate(&self) -> Result<(), AccountComponentTemplateError> { - let mut all_slots: Vec = - self.storage.iter().flat_map(|entry| entry.slot_indices()).collect(); - - // Check that slots start at 0 and are contiguous - all_slots.sort_unstable(); - if let Some(&first_slot) = all_slots.first() - && first_slot != 0 - { - return Err(AccountComponentTemplateError::StorageSlotsDoNotStartAtZero(first_slot)); - } - - for slots in all_slots.windows(2) { - if slots[1] == slots[0] { - return Err(AccountComponentTemplateError::DuplicateSlot(slots[0])); - } - - if slots[1] != slots[0] + 1 { - return Err(AccountComponentTemplateError::NonContiguousSlots(slots[0], slots[1])); - } - } - - // Check for duplicate storage entry names - let mut seen_names = BTreeSet::new(); - for entry in self.storage_entries() { - entry.validate()?; - if let Some(name) = entry.name() { - let name_existed = !seen_names.insert(name.as_str()); - if name_existed { - return Err(AccountComponentTemplateError::DuplicateEntryNames(name.clone())); - } - } - } - - // Check for duplicate storage placeholder names - let mut seen_placeholder_names = BTreeSet::new(); - for entry in self.storage_entries() { - for (name, _) in entry.template_requirements() { - if !seen_placeholder_names.insert(name.clone()) { - return Err(AccountComponentTemplateError::DuplicatePlaceholderName(name)); - } - } - } - - Ok(()) - } -} - -// CONVERSIONS -// ================================================================================================ - -impl TryFrom<&Package> for AccountComponentMetadata { - type Error = AccountError; - - fn try_from(package: &Package) -> Result { - package - .sections - .iter() - .find_map(|section| { - (section.id == SectionId::ACCOUNT_COMPONENT_METADATA).then(|| { - AccountComponentMetadata::read_from_bytes(§ion.data).map_err(|err| { - AccountError::other_with_source( - "failed to deserialize account component metadata", - err, - ) - }) - }) - }) - .transpose()? - .ok_or_else(|| { - AccountError::other( - "package does not contain account component metadata section - packages without explicit metadata may be intended for other purposes (e.g., note scripts, transaction scripts)", - ) - }) - } -} - -// SERIALIZATION -// ================================================================================================ - -impl Serializable for AccountComponentMetadata { - fn write_into(&self, target: &mut W) { - self.name.write_into(target); - self.description.write_into(target); - self.version.to_string().write_into(target); - self.supported_types.write_into(target); - self.storage.write_into(target); - } -} - -impl Deserializable for AccountComponentMetadata { - fn read_from(source: &mut R) -> Result { - Ok(Self { - name: String::read_from(source)?, - description: String::read_from(source)?, - version: semver::Version::from_str(&String::read_from(source)?).map_err( - |err: semver::Error| DeserializationError::InvalidValue(err.to_string()), - )?, - supported_types: BTreeSet::::read_from(source)?, - storage: Vec::::read_from(source)?, - }) - } -} - -#[cfg(test)] -pub(crate) fn test_package_with_metadata( - name: impl Into, - library: &miden_assembly::Library, - metadata: &AccountComponentMetadata, -) -> miden_mast_package::Package { - use std::sync::Arc; - - use miden_mast_package::{MastArtifact, Package, PackageManifest, Section, SectionId}; - - Package { - name: name.into(), - mast: MastArtifact::Library(Arc::new(library.clone())), - manifest: PackageManifest::new(None), - sections: vec![Section::new(SectionId::ACCOUNT_COMPONENT_METADATA, metadata.to_bytes())], - version: Default::default(), - description: None, - } -} - -// TESTS -// ================================================================================================ - -#[cfg(test)] -mod tests { - use std::collections::{BTreeMap, BTreeSet}; - use std::string::ToString; - - use assert_matches::assert_matches; - use miden_assembly::Assembler; - use miden_core::utils::{Deserializable, Serializable}; - use miden_core::{Felt, FieldElement}; - use semver::Version; - - use super::{FeltRepresentation, test_package_with_metadata}; - use crate::AccountError; - use crate::account::component::FieldIdentifier; - use crate::account::component::template::storage::StorageEntry; - use crate::account::component::template::{AccountComponentMetadata, InitStorageData}; - use crate::account::{AccountComponent, StorageValueName}; - use crate::errors::AccountComponentTemplateError; - use crate::testing::account_code::CODE; - - fn default_felt_array() -> [FeltRepresentation; 4] { - [ - FeltRepresentation::from(Felt::ZERO), - FeltRepresentation::from(Felt::ZERO), - FeltRepresentation::from(Felt::ZERO), - FeltRepresentation::from(Felt::ZERO), - ] - } - - #[test] - fn contiguous_value_slots() { - let storage = vec![ - StorageEntry::new_value(0, default_felt_array()), - StorageEntry::new_multislot( - FieldIdentifier::with_name(StorageValueName::new("slot1").unwrap()), - 1..3, - vec![default_felt_array(), default_felt_array()], - ), - ]; - - let original_config = AccountComponentMetadata { - name: "test".into(), - description: "desc".into(), - version: Version::parse("0.1.0").unwrap(), - supported_types: BTreeSet::new(), - storage, - }; - - let serialized = original_config.to_toml().unwrap(); - let deserialized = AccountComponentMetadata::from_toml(&serialized).unwrap(); - assert_eq!(deserialized, original_config); - } - - #[test] - fn new_non_contiguous_value_slots() { - let storage = vec![ - StorageEntry::new_value(0, default_felt_array()), - StorageEntry::new_value(2, default_felt_array()), - ]; - - let result = AccountComponentMetadata::new( - "test".into(), - "desc".into(), - Version::parse("0.1.0").unwrap(), - BTreeSet::new(), - storage, - ); - assert_matches!(result, Err(AccountComponentTemplateError::NonContiguousSlots(0, 2))); - } - - #[test] - fn metadata_binary_serde_roundtrip() { - let storage = vec![ - StorageEntry::new_multislot( - FieldIdentifier::with_name(StorageValueName::new("slot1").unwrap()), - 1..3, - vec![default_felt_array(), default_felt_array()], - ), - StorageEntry::new_value(0, default_felt_array()), - ]; - - let component_metadata = AccountComponentMetadata { - name: "test".into(), - description: "desc".into(), - version: Version::parse("0.1.0").unwrap(), - supported_types: BTreeSet::new(), - storage, - }; - - let library = Assembler::default().assemble_library([CODE]).unwrap(); - let package = test_package_with_metadata("test_package", &library, &component_metadata); - let _ = AccountComponent::from_package(&package, &InitStorageData::default()).unwrap(); - - let serialized = component_metadata.to_bytes(); - let deserialized = AccountComponentMetadata::read_from_bytes(&serialized).unwrap(); - - assert_eq!(deserialized, component_metadata); - } - - #[test] - pub fn fail_on_duplicate_key() { - let toml_text = r#" - name = "Test Component" - description = "This is a test component" - version = "1.0.1" - supported-types = ["FungibleFaucet"] - - [[storage]] - name = "map" - description = "A storage map entry" - slot = 0 - values = [ - { key = "0x1", value = ["0x3", "0x1", "0x2", "0x3"] }, - { key = "0x1", value = ["0x1", "0x2", "0x3", "0x10"] } - ] - "#; - - let result = AccountComponentMetadata::from_toml(toml_text); - assert_matches!(result, Err(AccountComponentTemplateError::StorageMapHasDuplicateKeys(_))); - } - - #[test] - pub fn fail_on_duplicate_placeholder_name() { - let toml_text = r#" - name = "Test Component" - description = "tests for two duplicate placeholders" - version = "1.0.1" - supported-types = ["FungibleFaucet"] - - [[storage]] - name = "map" - slot = 0 - values = [ - { key = "0x1", value = [{type = "felt", name = "test"}, "0x1", "0x2", "0x3"] }, - { key = "0x2", value = ["0x1", "0x2", "0x3", {type = "token_symbol", name = "test"}] } - ] - "#; - - let result = AccountComponentMetadata::from_toml(toml_text).unwrap_err(); - assert_matches::assert_matches!( - result, - AccountComponentTemplateError::DuplicatePlaceholderName(_) - ); - } - - #[test] - pub fn fail_duplicate_key_instance() { - let _ = color_eyre::install(); - - let toml_text = r#" - name = "Test Component" - description = "This is a test component" - version = "1.0.1" - supported-types = ["FungibleFaucet"] - - [[storage]] - name = "map" - description = "A storage map entry" - slot = 0 - values = [ - { key = ["0", "0", "0", "1"], value = ["0x9", "0x12", "0x31", "0x18"] }, - { key = { name="duplicate_key" }, value = ["0x1", "0x2", "0x3", "0x4"] } - ] - "#; - - let metadata = AccountComponentMetadata::from_toml(toml_text).unwrap(); - let library = Assembler::default().assemble_library([CODE]).unwrap(); - - let package = test_package_with_metadata("test_package", &library, &metadata); - - // Fail to instantiate on a duplicate key - - let init_storage_data = InitStorageData::new( - [( - StorageValueName::new("map.duplicate_key").unwrap(), - "0x0000000000000000000000000000000000000000000000000100000000000000".to_string(), - )], - BTreeMap::new(), - ); - let account_component = AccountComponent::from_package(&package, &init_storage_data); - assert_matches!( - account_component, - Err(AccountError::AccountComponentTemplateInstantiationError( - AccountComponentTemplateError::StorageMapHasDuplicateKeys(_) - )) - ); - - // Successfully instantiate a map (keys are not duplicate) - let valid_init_storage_data = InitStorageData::new( - [(StorageValueName::new("map.duplicate_key").unwrap(), "0x30".to_string())], - BTreeMap::new(), - ); - AccountComponent::from_package(&package, &valid_init_storage_data).unwrap(); - } -} diff --git a/crates/miden-objects/src/account/component/template/storage/entry_content.rs b/crates/miden-objects/src/account/component/template/storage/entry_content.rs deleted file mode 100644 index 54780435b3..0000000000 --- a/crates/miden-objects/src/account/component/template/storage/entry_content.rs +++ /dev/null @@ -1,748 +0,0 @@ -use alloc::boxed::Box; -use alloc::collections::BTreeSet; -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use core::iter; - -use super::placeholder::{PlaceholderTypeRequirement, TEMPLATE_REGISTRY, TemplateType}; -use super::{ - FieldIdentifier, - InitStorageData, - MapEntry, - StorageValueName, - TemplateRequirementsIter, -}; -use crate::account::StorageMap; -use crate::account::component::template::AccountComponentTemplateError; -use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; -use crate::{Felt, FieldElement, Word}; - -// WORDS -// ================================================================================================ - -/// Defines how a word is represented within the component's storage description. -/// -/// Each word representation can be: -/// - A template that defines a type but does not carry a value. -/// - A predefined value that may contain a hardcoded word or a mix of fixed and templated felts. -#[derive(Debug, Clone, PartialEq, Eq)] -#[allow(clippy::large_enum_variant)] -pub enum WordRepresentation { - /// A templated value that serves as a placeholder for instantiation. - /// - /// This variant defines a type but does not store a value. The actual value is provided at the - /// time of instantiation. The name is required to identify this template externally. - Template { - /// The type associated with this templated word. - r#type: TemplateType, - identifier: FieldIdentifier, - }, - - /// A predefined value that can be used directly within storage. - /// - /// This variant may contain either a fully hardcoded word or a structured set of felts, some - /// of which may themselves be templates. - Value { - identifier: Option, - /// The 4-felt representation of the stored word. - value: [FeltRepresentation; 4], - }, -} - -impl WordRepresentation { - /// Constructs a new `Template` variant. - pub fn new_template(r#type: TemplateType, identifier: FieldIdentifier) -> Self { - WordRepresentation::Template { r#type, identifier } - } - - /// Constructs a new `Value` variant. - pub fn new_value( - value: impl Into<[FeltRepresentation; 4]>, - identifier: Option, - ) -> Self { - WordRepresentation::Value { identifier, value: value.into() } - } - - /// Sets the description of the [`WordRepresentation`] and returns `self`. - pub fn with_description(self, description: impl Into) -> Self { - match self { - WordRepresentation::Template { r#type, identifier } => WordRepresentation::Template { - r#type, - identifier: FieldIdentifier { - name: identifier.name, - description: Some(description.into()), - }, - }, - WordRepresentation::Value { identifier, value } => WordRepresentation::Value { - identifier: identifier.map(|id| FieldIdentifier { - name: id.name, - description: Some(description.into()), - }), - value, - }, - } - } - - /// Returns the name associated with the word representation. - /// - For the `Template` variant, it always returns a reference to the name. - /// - For the `Value` variant, it returns `Some` if a name is present, or `None` otherwise. - pub fn name(&self) -> Option<&StorageValueName> { - match self { - WordRepresentation::Template { identifier, .. } => Some(&identifier.name), - WordRepresentation::Value { identifier, .. } => identifier.as_ref().map(|id| &id.name), - } - } - - /// Returns the description associated with the word representation. - /// Both variants store an `Option`, which is converted to an `Option<&str>`. - pub fn description(&self) -> Option<&str> { - match self { - WordRepresentation::Template { identifier, .. } => identifier.description.as_deref(), - WordRepresentation::Value { identifier, .. } => { - identifier.as_ref().and_then(|id| id.description.as_deref()) - }, - } - } - - /// Returns the type name. - pub fn word_type(&self) -> TemplateType { - match self { - WordRepresentation::Template { r#type, .. } => r#type.clone(), - WordRepresentation::Value { .. } => TemplateType::native_word(), - } - } - - /// Returns the value (an array of 4 `FeltRepresentation`s) if this is a `Value` - /// variant; otherwise, returns `None`. - pub fn value(&self) -> Option<&[FeltRepresentation; 4]> { - match self { - WordRepresentation::Value { value, .. } => Some(value), - WordRepresentation::Template { .. } => None, - } - } - - /// Returns an iterator over the word's placeholders. - /// - /// For [`WordRepresentation::Value`], it corresponds to the inner iterators (since inner - /// elements can be templated as well). - /// For [`WordRepresentation::Template`] it returns the words's placeholder requirements - /// as defined. - pub fn template_requirements( - &self, - placeholder_prefix: StorageValueName, - ) -> TemplateRequirementsIter<'_> { - let placeholder_key = - placeholder_prefix.with_suffix(self.name().unwrap_or(&StorageValueName::empty())); - match self { - WordRepresentation::Template { identifier, r#type } => Box::new(iter::once(( - placeholder_key, - PlaceholderTypeRequirement { - description: identifier.description.clone(), - r#type: r#type.clone(), - }, - ))), - WordRepresentation::Value { value, .. } => Box::new( - value - .iter() - .flat_map(move |felt| felt.template_requirements(placeholder_key.clone())), - ), - } - } - - /// Attempts to convert the [WordRepresentation] into a [Word]. - /// - /// If the representation is a template, the value is retrieved from - /// `init_storage_data`, identified by its key. If any of the inner elements - /// within the value are a template, they are retrieved in the same way. - pub(crate) fn try_build_word( - &self, - init_storage_data: &InitStorageData, - placeholder_prefix: StorageValueName, - ) -> Result { - match self { - WordRepresentation::Template { identifier, r#type } => { - let placeholder_path = placeholder_prefix.with_suffix(&identifier.name); - let maybe_value = init_storage_data.get(&placeholder_path); - if let Some(value) = maybe_value { - let parsed_value = TEMPLATE_REGISTRY - .try_parse_word(r#type, value) - .map_err(AccountComponentTemplateError::StorageValueParsingError)?; - - Ok(parsed_value) - } else { - Err(AccountComponentTemplateError::PlaceholderValueNotProvided( - placeholder_path, - )) - } - }, - WordRepresentation::Value { value, identifier } => { - let mut result = [Felt::ZERO; 4]; - - for (index, felt_repr) in value.iter().enumerate() { - let placeholder = placeholder_prefix.clone().with_suffix( - identifier - .as_ref() - .map(|id| &id.name) - .unwrap_or(&StorageValueName::empty()), - ); - result[index] = felt_repr.try_build_felt(init_storage_data, placeholder)?; - } - // SAFETY: result is guaranteed to have all its 4 indices rewritten - Ok(Word::from(result)) - }, - } - } - - /// Validates that the defined type exists and all the inner felt types exist as well - pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> { - // Check that type exists in registry - let type_exists = TEMPLATE_REGISTRY.contains_word_type(&self.word_type()); - if !type_exists { - return Err(AccountComponentTemplateError::InvalidType( - self.word_type().to_string(), - "Word".into(), - )); - } - - if let Some(felts) = self.value() { - for felt in felts { - felt.validate()?; - } - } - - Ok(()) - } -} - -impl Serializable for WordRepresentation { - fn write_into(&self, target: &mut W) { - match self { - WordRepresentation::Template { identifier, r#type } => { - target.write_u8(0); - target.write(identifier); - target.write(r#type); - }, - WordRepresentation::Value { identifier, value } => { - target.write_u8(1); - target.write(identifier); - target.write(value); - }, - } - } -} - -impl Deserializable for WordRepresentation { - fn read_from(source: &mut R) -> Result { - let tag = source.read_u8()?; - match tag { - 0 => { - let identifier = FieldIdentifier::read_from(source)?; - let r#type = TemplateType::read_from(source)?; - Ok(WordRepresentation::Template { identifier, r#type }) - }, - 1 => { - let identifier = Option::::read_from(source)?; - let value = <[FeltRepresentation; 4]>::read_from(source)?; - Ok(WordRepresentation::Value { identifier, value }) - }, - other => Err(DeserializationError::InvalidValue(format!( - "unknown tag '{other}' for WordRepresentation" - ))), - } - } -} - -impl From<[FeltRepresentation; 4]> for WordRepresentation { - fn from(value: [FeltRepresentation; 4]) -> Self { - WordRepresentation::new_value(value, Option::::None) - } -} - -impl From<[Felt; 4]> for WordRepresentation { - fn from(value: [Felt; 4]) -> Self { - WordRepresentation::new_value( - value.map(FeltRepresentation::from), - Option::::None, - ) - } -} - -// FELTS -// ================================================================================================ - -/// Supported element representations for a component's storage entries. -/// -/// Each felt element in a storage entry can either be: -/// - A concrete value that holds a predefined felt. -/// - A template that specifies the type of felt expected, with the actual value to be provided -/// later. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum FeltRepresentation { - /// A concrete felt value. - /// - /// This variant holds a felt that is part of the component's storage. - /// The optional name allows for identification, and the description offers additional context. - Value { - /// An optional identifier for this felt value. - /// An optional explanation of the felt's purpose. - identifier: Option, - /// The actual felt value. - value: Felt, - }, - - /// A templated felt element. - /// - /// This variant specifies the expected type of the felt without providing a concrete value. - /// The name is required to uniquely identify the template, and an optional description can - /// further clarify its intended use. - Template { - /// The expected type for this felt element. - r#type: TemplateType, - /// A unique name for the felt template. - /// An optional description that explains the purpose of this template. - identifier: FieldIdentifier, - }, -} - -impl FeltRepresentation { - /// Creates a new [`FeltRepresentation::Value`] variant. - pub fn new_value(value: impl Into, name: Option) -> Self { - FeltRepresentation::Value { - value: value.into(), - identifier: name.map(FieldIdentifier::with_name), - } - } - - /// Creates a new [`FeltRepresentation::Template`] variant. - /// - /// The name will be used for identification at the moment of instantiating the componentn. - pub fn new_template(r#type: TemplateType, name: StorageValueName) -> Self { - FeltRepresentation::Template { - r#type, - identifier: FieldIdentifier::with_name(name), - } - } - - /// Sets the description of the [`FeltRepresentation`] and returns `self`. - pub fn with_description(self, description: impl Into) -> Self { - match self { - FeltRepresentation::Template { r#type, identifier } => FeltRepresentation::Template { - r#type, - identifier: FieldIdentifier { - name: identifier.name, - description: Some(description.into()), - }, - }, - FeltRepresentation::Value { identifier, value } => FeltRepresentation::Value { - identifier: identifier.map(|id| FieldIdentifier { - name: id.name, - description: Some(description.into()), - }), - value, - }, - } - } - - /// Returns the felt type. - pub fn felt_type(&self) -> TemplateType { - match self { - FeltRepresentation::Template { r#type, .. } => r#type.clone(), - FeltRepresentation::Value { .. } => TemplateType::native_felt(), - } - } - - /// Attempts to convert the [FeltRepresentation] into a [Felt]. - /// - /// If the representation is a template, the value is retrieved from `init_storage_data`, - /// identified by its key. Otherwise, the returned value is just the inner element. - pub(crate) fn try_build_felt( - &self, - init_storage_data: &InitStorageData, - placeholder_prefix: StorageValueName, - ) -> Result { - match self { - FeltRepresentation::Template { identifier, r#type } => { - let placeholder_key = placeholder_prefix.with_suffix(&identifier.name); - let raw_value = init_storage_data.get(&placeholder_key).ok_or( - AccountComponentTemplateError::PlaceholderValueNotProvided(placeholder_key), - )?; - - Ok(TEMPLATE_REGISTRY - .try_parse_felt(r#type, raw_value) - .map_err(AccountComponentTemplateError::StorageValueParsingError)?) - }, - FeltRepresentation::Value { value, .. } => Ok(*value), - } - } - - /// Returns an iterator over the felt's template. - /// - /// For [`FeltRepresentation::Value`], these is an empty set; for - /// [`FeltRepresentation::Template`] it returns the felt's placeholder key based on the - /// felt's name within the component description. - pub fn template_requirements( - &self, - placeholder_prefix: StorageValueName, - ) -> TemplateRequirementsIter<'_> { - match self { - FeltRepresentation::Template { identifier, r#type } => Box::new(iter::once(( - placeholder_prefix.with_suffix(&identifier.name), - PlaceholderTypeRequirement { - description: identifier.description.clone(), - r#type: r#type.clone(), - }, - ))), - _ => Box::new(iter::empty()), - } - } - - /// Validates that the defined Felt type exists - pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> { - // Check that type exists in registry - let type_exists = TEMPLATE_REGISTRY.contains_felt_type(&self.felt_type()); - if !type_exists { - return Err(AccountComponentTemplateError::InvalidType( - self.felt_type().to_string(), - "Felt".into(), - )); - } - Ok(()) - } -} - -impl From for FeltRepresentation { - fn from(value: Felt) -> Self { - FeltRepresentation::new_value(value, Option::::None) - } -} - -impl Default for FeltRepresentation { - fn default() -> Self { - FeltRepresentation::new_value(Felt::default(), Option::::None) - } -} - -impl Serializable for FeltRepresentation { - fn write_into(&self, target: &mut W) { - match self { - FeltRepresentation::Value { identifier, value } => { - target.write_u8(0); - target.write(identifier); - target.write(value); - }, - FeltRepresentation::Template { identifier, r#type } => { - target.write_u8(1); - target.write(identifier); - target.write(r#type); - }, - } - } -} - -impl Deserializable for FeltRepresentation { - fn read_from(source: &mut R) -> Result { - let tag = source.read_u8()?; - match tag { - 0 => { - let identifier = Option::::read_from(source)?; - let value = Felt::read_from(source)?; - Ok(FeltRepresentation::Value { value, identifier }) - }, - 1 => { - let identifier = FieldIdentifier::read_from(source)?; - let r#type = TemplateType::read_from(source)?; - Ok(FeltRepresentation::Template { r#type, identifier }) - }, - other => Err(DeserializationError::InvalidValue(format!( - "unknown tag '{other}' for FeltRepresentation" - ))), - } - } -} - -// MAP REPRESENTATION -// ================================================================================================ - -/// Supported map representations for a component's storage entries. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))] -pub enum MapRepresentation { - /// A map whose contents are provided during instantiation via placeholders. - Template { - /// The human-readable identifier of the map slot. - identifier: FieldIdentifier, - }, - /// A map with statically defined key/value pairs. - Value { - /// The human-readable identifier of the map slot. - identifier: FieldIdentifier, - /// Storage map entries, consisting of a list of keys associated with their values. - entries: Vec, - }, -} - -impl MapRepresentation { - /// Creates a new `MapRepresentation` from a vector of map entries. - pub fn new_value(entries: Vec, name: impl Into) -> Self { - MapRepresentation::Value { - entries, - identifier: FieldIdentifier::with_name(name.into()), - } - } - - /// Creates a new templated map representation. - pub fn new_template(name: impl Into) -> Self { - MapRepresentation::Template { - identifier: FieldIdentifier::with_name(name.into()), - } - } - - /// Sets the description of the [`MapRepresentation`] and returns `self`. - pub fn with_description(self, description: impl Into) -> Self { - match self { - MapRepresentation::Template { identifier } => MapRepresentation::Template { - identifier: FieldIdentifier { - name: identifier.name, - description: Some(description.into()), - }, - }, - MapRepresentation::Value { identifier, entries } => MapRepresentation::Value { - entries, - identifier: FieldIdentifier { - name: identifier.name, - description: Some(description.into()), - }, - }, - } - } - - /// Returns an iterator over all of the storage entries' placeholder keys, alongside their - /// expected type. - pub fn template_requirements(&self) -> TemplateRequirementsIter<'_> { - match self { - MapRepresentation::Template { identifier } => Box::new(iter::once(( - identifier.name.clone(), - PlaceholderTypeRequirement { - description: identifier.description.clone(), - r#type: TemplateType::storage_map(), - }, - ))), - MapRepresentation::Value { identifier, entries } => Box::new( - entries - .iter() - .flat_map(move |entry| entry.template_requirements(identifier.name.clone())), - ), - } - } - - /// Returns a reference to map entries. - pub fn entries(&self) -> &[MapEntry] { - match self { - MapRepresentation::Value { entries, .. } => entries, - MapRepresentation::Template { .. } => &[], - } - } - - /// Returns a reference to the map's name within the storage metadata. - pub fn name(&self) -> &StorageValueName { - match self { - MapRepresentation::Template { identifier } - | MapRepresentation::Value { identifier, .. } => &identifier.name, - } - } - - /// Returns a reference to the field's description. - pub fn description(&self) -> Option<&String> { - match self { - MapRepresentation::Template { identifier } - | MapRepresentation::Value { identifier, .. } => identifier.description.as_ref(), - } - } - - /// Returns the number of statically defined key-value pairs in the map. - pub fn len(&self) -> usize { - match self { - MapRepresentation::Value { entries, .. } => entries.len(), - MapRepresentation::Template { .. } => 0, - } - } - - /// Returns `true` if there are no statically defined entries in the map. - pub fn is_empty(&self) -> bool { - match self { - MapRepresentation::Value { entries, .. } => entries.is_empty(), - MapRepresentation::Template { .. } => true, - } - } - - /// Attempts to convert the [MapRepresentation] into a [StorageMap]. - /// - /// If any of the inner elements are templates, their values are retrieved from - /// `init_storage_data`, identified by their key. - pub fn try_build_map( - &self, - init_storage_data: &InitStorageData, - ) -> Result { - match self { - MapRepresentation::Value { identifier, entries } => { - let entries = entries - .iter() - .map(|map_entry| { - let key = map_entry - .key() - .try_build_word(init_storage_data, identifier.name.clone())?; - let value = map_entry - .value() - .try_build_word(init_storage_data, identifier.name.clone())?; - Ok((key, value)) - }) - .collect::, _>>()?; - - StorageMap::with_entries(entries).map_err(|err| { - AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err)) - }) - }, - MapRepresentation::Template { identifier } => { - if let Some(entries) = init_storage_data.map_entries(&identifier.name) { - return StorageMap::with_entries(entries.clone()).map_err(|err| { - AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err)) - }); - } - - Err(AccountComponentTemplateError::PlaceholderValueNotProvided( - identifier.name.clone(), - )) - }, - } - } - - /// Validates the map representation by checking for duplicate keys and placeholder validity. - pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> { - match self { - MapRepresentation::Template { .. } => Ok(()), - MapRepresentation::Value { entries, .. } => { - let mut seen_keys = BTreeSet::new(); - for entry in entries.iter() { - entry.key().validate()?; - entry.value().validate()?; - if let Ok(key) = entry - .key() - .try_build_word(&InitStorageData::default(), StorageValueName::empty()) - && !seen_keys.insert(key) - { - return Err(AccountComponentTemplateError::StorageMapHasDuplicateKeys( - Box::from(format!("key `{key}` is duplicated")), - )); - } - } - - Ok(()) - }, - } - } -} - -impl Serializable for MapRepresentation { - fn write_into(&self, target: &mut W) { - match self { - MapRepresentation::Value { identifier, entries } => { - target.write_u8(0u8); - target.write(identifier); - target.write(entries); - }, - MapRepresentation::Template { identifier } => { - target.write_u8(1u8); - target.write(identifier); - }, - } - } -} - -impl Deserializable for MapRepresentation { - fn read_from(source: &mut R) -> Result { - let tag = source.read_u8()?; - match tag { - 0 => { - let identifier = FieldIdentifier::read_from(source)?; - let entries = Vec::::read_from(source)?; - Ok(MapRepresentation::Value { entries, identifier }) - }, - 1 => { - let identifier = FieldIdentifier::read_from(source)?; - Ok(MapRepresentation::Template { identifier }) - }, - other => Err(DeserializationError::InvalidValue(format!( - "unknown tag '{other}' for MapRepresentation" - ))), - } - } -} - -// MULTI-WORD VALUE -// ================================================================================================ - -/// Defines how multi-slot values are represented within the component's storage description. -/// -/// Each multi-word value representation can be: -/// - A predefined value that may contain a hardcoded word or a mix of fixed and templated felts. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum MultiWordRepresentation { - // TODO: Once there are multi-slot template types, add a MultiWordRepresentation::Template - // here - Value { - /// The human-readable name of this multi-slot entry. - identifier: FieldIdentifier, - /// A list of values to fill the logical slot, with a length equal to the number of slots. - values: Vec<[FeltRepresentation; 4]>, - }, -} - -impl MultiWordRepresentation { - /// Returns the number of words in this representation. - pub fn num_words(&self) -> usize { - match self { - MultiWordRepresentation::Value { values, .. } => values.len(), - } - } - - /// Validates the multi-slot value. - pub fn validate(&self) -> Result<(), AccountComponentTemplateError> { - match self { - MultiWordRepresentation::Value { values, .. } => { - for slot_word in values { - for felt_in_slot in slot_word { - felt_in_slot.validate()?; - } - } - }, - } - Ok(()) - } -} - -impl Serializable for MultiWordRepresentation { - fn write_into(&self, target: &mut W) { - match self { - MultiWordRepresentation::Value { identifier, values } => { - target.write_u8(0u8); - target.write(identifier); - target.write(values); - }, - } - } -} -impl Deserializable for MultiWordRepresentation { - fn read_from(source: &mut R) -> Result { - let variant_tag = source.read_u8()?; - match variant_tag { - 0 => { - let identifier: FieldIdentifier = source.read()?; - let values: Vec<[FeltRepresentation; 4]> = source.read()?; - Ok(MultiWordRepresentation::Value { identifier, values }) - }, - _ => Err(DeserializationError::InvalidValue(format!( - "unknown variant tag '{variant_tag}' for MultiWordRepresentation" - ))), - } - } -} diff --git a/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs b/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs deleted file mode 100644 index e1cf1b856b..0000000000 --- a/crates/miden-objects/src/account/component/template/storage/init_storage_data.rs +++ /dev/null @@ -1,63 +0,0 @@ -use alloc::collections::BTreeMap; -use alloc::string::String; -use alloc::vec::Vec; - -use super::StorageValueName; -use crate::Word; - -/// Represents the data required to initialize storage entries when instantiating an -/// [AccountComponent](crate::account::AccountComponent) from component metadata (either provided -/// directly or extracted from a package). -/// -/// An [`InitStorageData`] can be created from a TOML string when the `std` feature flag is set. -#[derive(Clone, Debug, Default)] -pub struct InitStorageData { - // TODO: Both the below fields could be a single field with a two variant enum - // (eg, BTreeMap) - /// A mapping of storage placeholder names to their corresponding storage values. - value_entries: BTreeMap, - /// A mapping of map placeholder names to their corresponding key/value entries. - map_entries: BTreeMap>, -} - -impl InitStorageData { - /// Creates a new instance of [InitStorageData]. - /// - /// A [`BTreeMap`] is constructed from the passed iterator, so duplicate keys will cause - /// overridden values. - /// - /// # Parameters - /// - /// - `entries`: An iterable collection of key-value pairs. - /// - `map_entries`: An iterable collection of storage map entries keyed by placeholder. - pub fn new( - entries: impl IntoIterator, - map_entries: impl IntoIterator)>, - ) -> Self { - let value_entries = entries - .into_iter() - .filter(|(entry_name, _)| !entry_name.as_str().is_empty()) - .collect::>(); - - InitStorageData { - value_entries, - map_entries: map_entries.into_iter().collect(), - } - } - - /// Retrieves a reference to the storage placeholders. - pub fn placeholders(&self) -> &BTreeMap { - &self.value_entries - } - - /// Returns a reference to the name corresponding to the placeholder, or - /// [`Option::None`] if the placeholder is not present. - pub fn get(&self, key: &StorageValueName) -> Option<&String> { - self.value_entries.get(key) - } - - /// Returns the map entries associated with the given placeholder name, if any. - pub fn map_entries(&self, key: &StorageValueName) -> Option<&Vec<(Word, Word)>> { - self.map_entries.get(key) - } -} diff --git a/crates/miden-objects/src/account/component/template/storage/mod.rs b/crates/miden-objects/src/account/component/template/storage/mod.rs deleted file mode 100644 index 4b4fb1d0e7..0000000000 --- a/crates/miden-objects/src/account/component/template/storage/mod.rs +++ /dev/null @@ -1,1130 +0,0 @@ -use alloc::boxed::Box; -use alloc::string::String; -use alloc::vec::Vec; -use core::ops::Range; - -use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_core::{Felt, FieldElement}; -use miden_processor::DeserializationError; - -mod entry_content; -pub use entry_content::*; - -use super::AccountComponentTemplateError; -use crate::Word; -use crate::account::StorageSlot; - -mod placeholder; -pub use placeholder::{ - PlaceholderTypeRequirement, - StorageValueName, - StorageValueNameError, - TemplateType, - TemplateTypeError, -}; - -mod init_storage_data; -pub use init_storage_data::InitStorageData; - -#[cfg(feature = "std")] -pub mod toml; - -/// Alias used for iterators that collect all placeholders and their types within a component -/// template. -pub type TemplateRequirementsIter<'a> = - Box + 'a>; - -// IDENTIFIER -// ================================================================================================ - -/// An identifier for a storage entry field. -/// -/// An identifier consists of a name that identifies the field, and an optional description. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FieldIdentifier { - /// A human-readable identifier for the template. - pub name: StorageValueName, - /// An optional description explaining the purpose of this template. - pub description: Option, -} - -impl FieldIdentifier { - /// Creates a new `FieldIdentifier` with the given name and no description. - pub fn with_name(name: StorageValueName) -> Self { - Self { name, description: None } - } - - /// Creates a new `FieldIdentifier` with the given name and description. - pub fn with_description(name: StorageValueName, description: impl Into) -> Self { - Self { - name, - description: Some(description.into()), - } - } - - /// Returns the identifier name. - pub fn name(&self) -> &StorageValueName { - &self.name - } - - /// Returns the identifier description. - pub fn description(&self) -> Option<&String> { - self.description.as_ref() - } -} - -impl From for FieldIdentifier { - fn from(value: StorageValueName) -> Self { - FieldIdentifier::with_name(value) - } -} - -impl Serializable for FieldIdentifier { - fn write_into(&self, target: &mut W) { - target.write(&self.name); - target.write(&self.description); - } -} - -impl Deserializable for FieldIdentifier { - fn read_from(source: &mut R) -> Result { - let name = StorageValueName::read_from(source)?; - let description = Option::::read_from(source)?; - Ok(FieldIdentifier { name, description }) - } -} - -// STORAGE ENTRY -// ================================================================================================ - -/// Represents a single entry in the component's storage layout. -/// -/// Each entry can describe: -/// - A value slot with a single word. -/// - A map slot with a key-value map that occupies one storage slot. -/// - A multi-slot entry spanning multiple contiguous slots with multiple words (but not maps) that -/// represent a single logical value. -#[derive(Debug, Clone, PartialEq, Eq)] -#[allow(clippy::large_enum_variant)] -pub enum StorageEntry { - /// A value slot, which can contain one word. - Value { - /// The numeric index of this map slot in the component's storage. - slot: u8, - /// A description of a word, representing either a predefined value or a templated one. - word_entry: WordRepresentation, - }, - - /// A map slot, containing multiple key-value pairs. Keys and values are hex-encoded strings. - Map { - /// The numeric index of this map slot in the component's storage. - slot: u8, - /// A list of key-value pairs to initialize in this map slot. - map: MapRepresentation, - }, - - /// A multi-slot entry, representing a single logical value across multiple slots. - MultiSlot { - /// The indices of the slots that form this multi-slot entry. - slots: Range, - /// A description of the values. - word_entries: MultiWordRepresentation, - }, -} - -impl StorageEntry { - pub fn new_value(slot: u8, word_entry: impl Into) -> Self { - StorageEntry::Value { slot, word_entry: word_entry.into() } - } - - pub fn new_map(slot: u8, map: MapRepresentation) -> Self { - StorageEntry::Map { slot, map } - } - - pub fn new_multislot( - identifier: FieldIdentifier, - slots: Range, - values: Vec<[FeltRepresentation; 4]>, - ) -> Self { - StorageEntry::MultiSlot { - slots, - word_entries: MultiWordRepresentation::Value { identifier, values }, - } - } - - pub fn name(&self) -> Option<&StorageValueName> { - match self { - StorageEntry::Value { word_entry, .. } => word_entry.name(), - StorageEntry::Map { map, .. } => Some(map.name()), - StorageEntry::MultiSlot { word_entries, .. } => match word_entries { - MultiWordRepresentation::Value { identifier, .. } => Some(&identifier.name), - }, - } - } - - /// Returns the slot indices that the storage entry covers. - pub fn slot_indices(&self) -> Range { - match self { - StorageEntry::MultiSlot { slots, .. } => slots.clone(), - StorageEntry::Value { slot, .. } | StorageEntry::Map { slot, .. } => *slot..*slot + 1, - } - } - - /// Returns an iterator over all of the storage entries's value names, alongside their - /// expected type. - pub fn template_requirements(&self) -> TemplateRequirementsIter<'_> { - match self { - StorageEntry::Value { word_entry, .. } => { - word_entry.template_requirements(StorageValueName::empty()) - }, - StorageEntry::Map { map, .. } => map.template_requirements(), - StorageEntry::MultiSlot { word_entries, .. } => match word_entries { - MultiWordRepresentation::Value { identifier, values } => { - Box::new(values.iter().flat_map(move |word| { - word.iter() - .flat_map(move |f| f.template_requirements(identifier.name.clone())) - })) - }, - }, - } - } - - /// Attempts to convert the storage entry into a list of [`StorageSlot`]. - /// - /// - [`StorageEntry::Value`] would convert to a [`StorageSlot::Value`] - /// - [`StorageEntry::MultiSlot`] would convert to as many [`StorageSlot::Value`] as required by - /// the defined type - /// - [`StorageEntry::Map`] would convert to a [`StorageSlot::Map`] - /// - /// Each of the entry's values could be templated. These values are replaced for values found - /// in `init_storage_data`, identified by its key. - pub fn try_build_storage_slots( - &self, - init_storage_data: &InitStorageData, - ) -> Result, AccountComponentTemplateError> { - match self { - StorageEntry::Value { word_entry, .. } => { - let slot = - word_entry.try_build_word(init_storage_data, StorageValueName::empty())?; - Ok(vec![StorageSlot::Value(slot)]) - }, - StorageEntry::Map { map, .. } => { - let storage_map = map.try_build_map(init_storage_data)?; - Ok(vec![StorageSlot::Map(storage_map)]) - }, - StorageEntry::MultiSlot { word_entries, .. } => { - match word_entries { - MultiWordRepresentation::Value { identifier, values } => { - Ok(values - .iter() - .map(|word_repr| { - let mut result = [Felt::ZERO; 4]; - - for (index, felt_repr) in word_repr.iter().enumerate() { - result[index] = felt_repr.try_build_felt( - init_storage_data, - identifier.name.clone(), - )?; - } - // SAFETY: result is guaranteed to have all its 4 indices rewritten - Ok(StorageSlot::Value(Word::from(result))) - }) - .collect::, _>>()?) - }, - } - }, - } - } - - /// Validates the storage entry for internal consistency. - pub(super) fn validate(&self) -> Result<(), AccountComponentTemplateError> { - match self { - StorageEntry::Map { map, .. } => map.validate(), - StorageEntry::MultiSlot { slots, word_entries, .. } => { - if slots.len() == 1 { - return Err(AccountComponentTemplateError::MultiSlotSpansOneSlot); - } - - if slots.len() != word_entries.num_words() { - return Err(AccountComponentTemplateError::MultiSlotArityMismatch); - } - - word_entries.validate() - }, - StorageEntry::Value { word_entry, .. } => Ok(word_entry.validate()?), - } - } -} - -// SERIALIZATION -// ================================================================================================ - -impl Serializable for StorageEntry { - fn write_into(&self, target: &mut W) { - match self { - StorageEntry::Value { slot, word_entry } => { - target.write_u8(0u8); - target.write_u8(*slot); - target.write(word_entry); - }, - StorageEntry::Map { slot, map } => { - target.write_u8(1u8); - target.write_u8(*slot); - target.write(map); - }, - StorageEntry::MultiSlot { word_entries, slots } => { - target.write_u8(2u8); - target.write(word_entries); - target.write(slots.start); - target.write(slots.end); - }, - } - } -} - -impl Deserializable for StorageEntry { - fn read_from(source: &mut R) -> Result { - let variant_tag = source.read_u8()?; - match variant_tag { - 0 => { - let slot = source.read_u8()?; - let word_entry: WordRepresentation = source.read()?; - Ok(StorageEntry::Value { slot, word_entry }) - }, - 1 => { - let slot = source.read_u8()?; - let map: MapRepresentation = source.read()?; - Ok(StorageEntry::Map { slot, map }) - }, - 2 => { - let word_entries: MultiWordRepresentation = source.read()?; - let slots_start: u8 = source.read()?; - let slots_end: u8 = source.read()?; - Ok(StorageEntry::MultiSlot { - slots: slots_start..slots_end, - word_entries, - }) - }, - _ => Err(DeserializationError::InvalidValue(format!( - "unknown variant tag '{variant_tag}' for StorageEntry" - ))), - } - } -} - -// MAP ENTRY -// ================================================================================================ - -/// Key-value entry for storage maps. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -pub struct MapEntry { - key: WordRepresentation, - value: WordRepresentation, -} - -impl MapEntry { - pub fn new(key: impl Into, value: impl Into) -> Self { - Self { key: key.into(), value: value.into() } - } - - pub fn key(&self) -> &WordRepresentation { - &self.key - } - - pub fn value(&self) -> &WordRepresentation { - &self.value - } - - pub fn into_parts(self) -> (WordRepresentation, WordRepresentation) { - let MapEntry { key, value } = self; - (key, value) - } - - pub fn template_requirements( - &self, - placeholder_prefix: StorageValueName, - ) -> TemplateRequirementsIter<'_> { - let key_iter = self.key.template_requirements(placeholder_prefix.clone()); - let value_iter = self.value.template_requirements(placeholder_prefix); - - Box::new(key_iter.chain(value_iter)) - } -} - -impl Serializable for MapEntry { - fn write_into(&self, target: &mut W) { - self.key.write_into(target); - self.value.write_into(target); - } -} - -impl Deserializable for MapEntry { - fn read_from(source: &mut R) -> Result { - let key = WordRepresentation::read_from(source)?; - let value = WordRepresentation::read_from(source)?; - Ok(MapEntry { key, value }) - } -} - -// TESTS -// ================================================================================================ - -#[cfg(test)] -mod tests { - use alloc::collections::{BTreeMap, BTreeSet}; - use alloc::string::ToString; - use core::error::Error; - use core::panic; - - use miden_assembly::Assembler; - use miden_core::utils::{Deserializable, Serializable}; - use miden_core::{EMPTY_WORD, Felt, Word}; - use semver::Version; - - use crate::account::component::FieldIdentifier; - use crate::account::component::template::storage::placeholder::TemplateType; - use crate::account::component::template::{ - AccountComponentMetadata, - InitStorageData, - MapEntry, - MapRepresentation, - StorageValueName, - test_package_with_metadata, - }; - use crate::account::{ - AccountComponent, - AccountType, - FeltRepresentation, - StorageEntry, - StorageSlot, - TemplateTypeError, - WordRepresentation, - }; - use crate::errors::AccountComponentTemplateError; - use crate::testing::account_code::CODE; - use crate::{AccountError, word}; - - #[test] - fn test_storage_entry_serialization() { - let felt_array: [FeltRepresentation; 4] = [ - FeltRepresentation::from(Felt::new(0xabc)), - FeltRepresentation::from(Felt::new(1218)), - FeltRepresentation::from(Felt::new(0xdba3)), - FeltRepresentation::new_template( - TemplateType::native_felt(), - StorageValueName::new("slot3").unwrap(), - ) - .with_description("dummy description"), - ]; - - let test_word: Word = word!("0x000001"); - let test_word = test_word.map(FeltRepresentation::from); - - let map_representation = MapRepresentation::new_value( - vec![ - MapEntry { - key: WordRepresentation::new_template( - TemplateType::native_word(), - StorageValueName::new("foo").unwrap().into(), - ), - value: WordRepresentation::new_value(test_word.clone(), None), - }, - MapEntry { - key: WordRepresentation::new_value(test_word.clone(), None), - value: WordRepresentation::new_template( - TemplateType::native_word(), - StorageValueName::new("bar").unwrap().into(), - ), - }, - MapEntry { - key: WordRepresentation::new_template( - TemplateType::native_word(), - StorageValueName::new("baz").unwrap().into(), - ), - value: WordRepresentation::new_value(test_word, None), - }, - ], - StorageValueName::new("map").unwrap(), - ) - .with_description("a storage map description"); - - let storage = vec![ - StorageEntry::new_value(0, felt_array.clone()), - StorageEntry::new_map(1, map_representation), - StorageEntry::new_multislot( - FieldIdentifier::with_description( - StorageValueName::new("multi").unwrap(), - "Multi slot entry", - ), - 2..4, - vec![ - [ - FeltRepresentation::new_template( - TemplateType::native_felt(), - StorageValueName::new("test").unwrap(), - ), - FeltRepresentation::new_template( - TemplateType::native_felt(), - StorageValueName::new("test2").unwrap(), - ), - FeltRepresentation::new_template( - TemplateType::native_felt(), - StorageValueName::new("test3").unwrap(), - ), - FeltRepresentation::new_template( - TemplateType::native_felt(), - StorageValueName::new("test4").unwrap(), - ), - ], - felt_array, - ], - ), - StorageEntry::new_value( - 4, - WordRepresentation::new_template( - TemplateType::native_word(), - StorageValueName::new("single").unwrap().into(), - ), - ), - ]; - - let config = AccountComponentMetadata { - name: "Test Component".into(), - description: "This is a test component".into(), - version: Version::parse("1.0.0").unwrap(), - supported_types: BTreeSet::from([AccountType::FungibleFaucet]), - storage, - }; - let toml = config.to_toml().unwrap(); - let deserialized = AccountComponentMetadata::from_toml(&toml).unwrap(); - - assert_eq!(deserialized, config); - } - - #[test] - pub fn toml_serde_roundtrip() { - let toml_text = r#" - name = "Test Component" - description = "This is a test component" - version = "1.0.1" - supported-types = ["FungibleFaucet", "RegularAccountImmutableCode"] - - [[storage]] - name = "map_entry" - slot = 0 - values = [ - { key = "0x1", value = ["0x1","0x2","0x3","0"]}, - { key = "0x3", value = "0x123" }, - { key = { name = "map_key_template", description = "this tests that the default type is correctly set"}, value = "0x3" }, - ] - - [[storage]] - name = "token_metadata" - description = "Contains metadata about the token associated to the faucet account" - slot = 1 - value = [ - { type = "felt", name = "max_supply", description = "Maximum supply of the token in base units" }, # placeholder - { type = "token_symbol", value = "TST" }, # hardcoded non-felt type - { type = "u8", name = "decimals", description = "Number of decimal places" }, # placeholder - { value = "0" }, - ] - - [[storage]] - name = "default_recallable_height" - slot = 2 - type = "word" - "#; - - let component_metadata = AccountComponentMetadata::from_toml(toml_text).unwrap(); - let requirements = component_metadata.get_placeholder_requirements(); - - assert_eq!(requirements.len(), 4); - - let supply = requirements - .get(&StorageValueName::new("token_metadata.max_supply").unwrap()) - .unwrap(); - assert_eq!(supply.r#type.as_str(), "felt"); - - let decimals = requirements - .get(&StorageValueName::new("token_metadata.decimals").unwrap()) - .unwrap(); - assert_eq!(decimals.r#type.as_str(), "u8"); - - let default_recallable_height = requirements - .get(&StorageValueName::new("default_recallable_height").unwrap()) - .unwrap(); - assert_eq!(default_recallable_height.r#type.as_str(), "word"); - - let map_key_template = requirements - .get(&StorageValueName::new("map_entry.map_key_template").unwrap()) - .unwrap(); - assert_eq!(map_key_template.r#type.as_str(), "word"); - - let library = Assembler::default().assemble_library([CODE]).unwrap(); - let metadata_bytes = component_metadata.to_bytes(); - let metadata_deserialized = - AccountComponentMetadata::read_from_bytes(&metadata_bytes).unwrap(); - assert_eq!(component_metadata, metadata_deserialized); - - // Fail to parse because 2800 > u8 - let storage_placeholders = InitStorageData::new( - [ - ( - StorageValueName::new("map_entry.map_key_template").unwrap(), - "0x123".to_string(), - ), - ( - StorageValueName::new("token_metadata.max_supply").unwrap(), - 20_000u64.to_string(), - ), - (StorageValueName::new("token_metadata.decimals").unwrap(), "2800".into()), - (StorageValueName::new("default_recallable_height").unwrap(), "0".into()), - ], - BTreeMap::new(), - ); - - let package = - test_package_with_metadata("component_with_templates", &library, &component_metadata); - - let component = AccountComponent::from_package(&package, &storage_placeholders); - assert_matches::assert_matches!( - component, - Err(AccountError::AccountComponentTemplateInstantiationError( - AccountComponentTemplateError::StorageValueParsingError( - TemplateTypeError::ParseError { .. } - ) - )) - ); - - // Instantiate successfully - let storage_placeholders = InitStorageData::new( - [ - ( - StorageValueName::new("map_entry.map_key_template").unwrap(), - "0x123".to_string(), - ), - ( - StorageValueName::new("token_metadata.max_supply").unwrap(), - 20_000u64.to_string(), - ), - (StorageValueName::new("token_metadata.decimals").unwrap(), "128".into()), - (StorageValueName::new("default_recallable_height").unwrap(), "0x0".into()), - ], - BTreeMap::new(), - ); - - let component = AccountComponent::from_package(&package, &storage_placeholders).unwrap(); - assert_eq!( - component.supported_types(), - &[AccountType::FungibleFaucet, AccountType::RegularAccountImmutableCode] - .into_iter() - .collect() - ); - - let named_map_slot = component.storage_slots.first().unwrap(); - match named_map_slot.storage_slot() { - StorageSlot::Map(storage_map) => assert_eq!(storage_map.entries().count(), 3), - _ => panic!("should be map"), - } - - let named_value_slot = component.storage_slots().get(2).unwrap(); - match named_value_slot.storage_slot() { - StorageSlot::Value(v) => { - assert_eq!(v, &EMPTY_WORD) - }, - _ => panic!("should be value"), - } - - let failed_instantiation = - AccountComponent::from_package(&package, &InitStorageData::default()); - - assert_matches::assert_matches!( - failed_instantiation, - Err(AccountError::AccountComponentTemplateInstantiationError( - AccountComponentTemplateError::PlaceholderValueNotProvided(_) - )) - ); - } - - #[test] - fn test_no_duplicate_slot_names() { - let toml_text = r#" - name = "Test Component" - description = "This is a test component" - version = "1.0.1" - supported-types = ["FungibleFaucet", "RegularAccountImmutableCode"] - - [[storage]] - name = "test_duplicate" - slot = 0 - type = "felt" # Felt is not a valid type for word slots - "#; - - let err = AccountComponentMetadata::from_toml(toml_text).unwrap_err(); - assert_matches::assert_matches!(err, AccountComponentTemplateError::InvalidType(_, _)) - } - - #[test] - fn parses_and_instantiates_ecdsa_template() { - let toml = r#" - name = "ecdsa_auth" - description = "Ecdsa authentication component, for verifying ECDSA K256 Keccak signatures." - version = "0.1.0" - supported-types = ["RegularAccountUpdatableCode", "RegularAccountImmutableCode", "FungibleFaucet", "NonFungibleFaucet"] - - [[storage]] - name = "ecdsa_pubkey" - description = "ecdsa public key" - slot = 0 - type = "auth::ecdsa_k256_keccak::pub_key" - "#; - - let metadata = AccountComponentMetadata::from_toml(toml).unwrap(); - assert_eq!(metadata.storage_entries().len(), 1); - assert_eq!(metadata.storage_entries()[0].name().unwrap().as_str(), "ecdsa_pubkey"); - - let library = Assembler::default().assemble_library([CODE]).unwrap(); - let package = test_package_with_metadata("ecdsa_auth_package", &library, &metadata); - - let init_storage = InitStorageData::new( - [(StorageValueName::new("ecdsa_pubkey").unwrap(), "0x1234".into())], - BTreeMap::new(), - ); - - let component = AccountComponent::from_package(&package, &init_storage).unwrap(); - let slot = component.storage_slots().first().expect("missing storage slot"); - match slot { - StorageSlot::Value(word) => { - let expected = Word::parse( - "0x0000000000000000000000000000000000000000000000000000000000001234", - ) - .unwrap(); - assert_eq!(word, &expected); - }, - _ => panic!("expected value storage slot"), - } - } - - #[test] - fn map_template_can_build_from_entries() { - let map_name = StorageValueName::new("procedure_thresholds").unwrap(); - let map_entry = StorageEntry::new_map(0, MapRepresentation::new_template(map_name.clone())); - - let init_data = InitStorageData::from_toml( - r#" - procedure_thresholds = [ - { key = "0x0000000000000000000000000000000000000000000000000000000000000001", value = "0x0000000000000000000000000000000000000000000000000000000000000010" }, - { key = "0x0000000000000000000000000000000000000000000000000000000000000002", value = "0x0000000000000000000000000000000000000000000000000000000000000020" } - ] - "#, - ) - .unwrap(); - - let entries = init_data.map_entries(&map_name).expect("map entries missing"); - assert_eq!(entries.len(), 2); - assert_eq!( - entries[0], - ( - Word::parse("0x0000000000000000000000000000000000000000000000000000000000000001",) - .unwrap(), - Word::parse("0x0000000000000000000000000000000000000000000000000000000000000010",) - .unwrap(), - ) - ); - - let slots = map_entry.try_build_storage_slots(&init_data).unwrap(); - assert_eq!(slots.len(), 1); - - match &slots[0] { - StorageSlot::Map(storage_map) => { - assert_eq!(storage_map.num_entries(), 2); - let main_key = Word::parse( - "0x0000000000000000000000000000000000000000000000000000000000000001", - ) - .unwrap(); - let main_value_expected = Word::parse( - "0x0000000000000000000000000000000000000000000000000000000000000010", - ) - .unwrap(); - assert_eq!(storage_map.get(&main_key), main_value_expected); - }, - _ => panic!("expected map storage slot"), - } - } - - #[test] - fn map_template_requires_entries() { - let map_name = StorageValueName::new("procedure_thresholds").unwrap(); - let map_entry = StorageEntry::new_map(0, MapRepresentation::new_template(map_name.clone())); - - let result = map_entry.try_build_storage_slots(&InitStorageData::default()); - - assert_matches::assert_matches!( - result, - Err(AccountComponentTemplateError::PlaceholderValueNotProvided(name)) - if name.as_str() == "procedure_thresholds" - ); - - // try with an empty list - - let init_data = InitStorageData::from_toml( - r#" - procedure_thresholds = [] - "#, - ) - .unwrap(); - - let result = map_entry.try_build_storage_slots(&init_data).unwrap(); - - assert_eq!(result.len(), 1); - match &result[0] { - StorageSlot::Map(storage_map) => assert_eq!(storage_map.num_entries(), 0), - _ => panic!("expected map storage slot"), - } - } - - #[test] - fn map_placeholder_requirement_is_reported() { - let targets = [AccountType::RegularAccountImmutableCode].into_iter().collect(); - let map = - MapRepresentation::new_template(StorageValueName::new("procedure_thresholds").unwrap()) - .with_description("Configures procedure thresholds"); - - let metadata = AccountComponentMetadata::new( - "test".into(), - "desc".into(), - Version::new(1, 0, 0), - targets, - vec![StorageEntry::new_map(0, map)], - ) - .unwrap(); - - let requirements = metadata.get_placeholder_requirements(); - let requirement = requirements - .get(&StorageValueName::new("procedure_thresholds").unwrap()) - .expect("map placeholder should be reported"); - - assert_eq!(requirement.r#type.as_str(), "map"); - assert_eq!(requirement.description.as_deref(), Some("Configures procedure thresholds"),); - } - - #[test] - fn toml_template_map_roundtrip() { - let toml_text = r#" - name = "Test Component" - description = "Component with templated map" - version = "1.0.0" - supported-types = ["RegularAccountImmutableCode"] - - [[storage]] - name = "my_map" - description = "Some description" - slot = 0 - type = "map" - "#; - - let metadata = AccountComponentMetadata::from_toml(toml_text).unwrap(); - assert_eq!(metadata.storage_entries().len(), 1); - match metadata.storage_entries().first().unwrap() { - StorageEntry::Map { map, .. } => match map { - MapRepresentation::Template { identifier } => { - assert_eq!(identifier.name.as_str(), "my_map"); - assert_eq!(identifier.description.as_deref(), Some("Some description")); - }, - MapRepresentation::Value { .. } => panic!("expected template map"), - }, - _ => panic!("expected map storage entry"), - } - - let toml_roundtrip = metadata.to_toml().unwrap(); - assert!(toml_roundtrip.contains("type = \"map\"")); - } - - #[test] - fn toml_map_with_empty_values_creates_value_map() { - // Test that when type = "map" and values = [] is specified, - // it creates a MapRepresentation::Value with empty entries, - // not a MapRepresentation::Template - let toml_text = r#" - name = "Test Component" - description = "Component with map having empty values" - version = "1.0.0" - supported-types = ["RegularAccountImmutableCode"] - - [[storage]] - name = "executed_transactions" - description = "Map which stores executed transactions" - slot = 0 - type = "map" - values = [] - "#; - - let metadata = AccountComponentMetadata::from_toml(toml_text).unwrap(); - assert_eq!(metadata.storage_entries().len(), 1); - match metadata.storage_entries().first().unwrap() { - StorageEntry::Map { map, .. } => match map { - MapRepresentation::Value { identifier, entries } => { - assert_eq!(identifier.name.as_str(), "executed_transactions"); - assert_eq!( - identifier.description.as_deref(), - Some("Map which stores executed transactions") - ); - assert!(entries.is_empty(), "Expected empty entries for map with values = []"); - }, - MapRepresentation::Template { .. } => { - panic!("expected value map with empty entries, not template map") - }, - }, - _ => panic!("expected map storage entry"), - } - } - - #[test] - fn map_placeholder_populated_via_toml_array() { - let storage_entry = StorageEntry::new_map( - 0, - MapRepresentation::new_template(StorageValueName::new("my_map").unwrap()), - ); - - let init_data = InitStorageData::from_toml( - r#" - my_map = [ - { key = "0x0000000000000000000000000000000000000000000000000000000000000001", value = "0x0000000000000000000000000000000000000000000000000000000000000090" }, - { key = "0x0000000000000000000000000000000000000000000000000000000000000002", value = ["1", "2", "3", "4"] } - ] - other_placeholder = "0xAB" - "#, - ) - .unwrap(); - - assert_eq!( - init_data.get(&StorageValueName::new("other_placeholder").unwrap()).unwrap(), - "0xAB" - ); - - let slots = storage_entry.try_build_storage_slots(&init_data).unwrap(); - assert_eq!(slots.len(), 1); - match &slots[0] { - StorageSlot::Map(storage_map) => { - assert_eq!(storage_map.num_entries(), 2); - let second_value = Word::from([ - Felt::new(1u64), - Felt::new(2u64), - Felt::new(3u64), - Felt::new(4u64), - ]); - let second_key = Word::try_from( - "0x0000000000000000000000000000000000000000000000000000000000000002", - ) - .unwrap(); - assert_eq!(storage_map.get(&second_key), second_value); - }, - _ => panic!("expected map storage slot"), - } - } - - #[test] - fn toml_map_type_with_values_is_invalid() { - let toml_text = r#" - name = "Invalid" - description = "Invalid map" - version = "1.0.0" - supported-types = ["RegularAccountImmutableCode"] - - [[storage]] - name = "bad_map" - slot = 0 - type = "map" - values = [ { key = "0x1", value = "0x2" } ] - "#; - - let metadata = AccountComponentMetadata::from_toml(toml_text).unwrap(); - match metadata.storage_entries().first().unwrap() { - StorageEntry::Map { map, .. } => match map { - MapRepresentation::Value { entries, .. } => { - assert_eq!(entries.len(), 1); - }, - _ => panic!("expected static map"), - }, - _ => panic!("expected map storage entry"), - } - } - - #[test] - fn toml_map_values_with_non_map_type_is_invalid() { - let toml_text = r#" - name = "Invalid" - description = "Invalid map" - version = "1.0.0" - supported-types = ["RegularAccountImmutableCode"] - - [[storage]] - name = "bad_map" - slot = 0 - type = "word" - values = [ { key = "0x1", value = "0x2" } ] - "#; - - let result = AccountComponentMetadata::from_toml(toml_text); - assert_matches::assert_matches!( - result, - Err(AccountComponentTemplateError::TomlDeserializationError(_)) - ); - } - - #[test] - fn toml_fail_multislot_arity_mismatch() { - let toml_text = r#" - name = "Test Component" - description = "Test multislot arity mismatch" - version = "1.0.1" - supported-types = ["FungibleFaucet"] - - [[storage]] - name = "multislot_test" - slots = [0, 1] - values = [ - [ "0x1", "0x2", "0x3", "0x4" ] - ] - "#; - - let err = AccountComponentMetadata::from_toml(toml_text).unwrap_err(); - assert_matches::assert_matches!(err, AccountComponentTemplateError::MultiSlotArityMismatch); - } - - #[test] - fn toml_fail_multislot_duplicate_slot() { - let toml_text = r#" - name = "Test Component" - description = "Test multislot duplicate slot" - version = "1.0.1" - supported-types = ["FungibleFaucet"] - - [[storage]] - name = "multislot_duplicate" - slots = [0, 1] - values = [ - [ "0x1", "0x2", "0x3", "0x4" ], - [ "0x5", "0x6", "0x7", "0x8" ] - ] - - [[storage]] - name = "multislot_duplicate" - slots = [1, 2] - values = [ - [ "0x1", "0x2", "0x3", "0x4" ], - [ "0x5", "0x6", "0x7", "0x8" ] - ] - "#; - - let err = AccountComponentMetadata::from_toml(toml_text).unwrap_err(); - assert_matches::assert_matches!(err, AccountComponentTemplateError::DuplicateSlot(1)); - } - - #[test] - fn toml_fail_multislot_non_contiguous_slots() { - let toml_text = r#" - name = "Test Component" - description = "Test multislot non contiguous" - version = "1.0.1" - supported-types = ["FungibleFaucet"] - - [[storage]] - name = "multislot_non_contiguous" - slots = [0, 2] - values = [ - [ "0x1", "0x2", "0x3", "0x4" ], - [ "0x5", "0x6", "0x7", "0x8" ] - ] - "#; - - let err = AccountComponentMetadata::from_toml(toml_text).unwrap_err(); - // validate inner serde error - assert!(err.source().unwrap().to_string().contains("are not contiguous")); - } - - #[test] - fn toml_fail_duplicate_storage_entry_names() { - let toml_text = r#" - name = "Test Component" - description = "Component with duplicate storage entry names" - version = "1.0.1" - supported-types = ["FungibleFaucet"] - - [[storage]] - # placeholder - name = "duplicate" - slot = 0 - type = "word" - - [[storage]] - name = "duplicate" - slot = 1 - value = [ "0x1", "0x1", "0x1", "0x1" ] - "#; - - let result = AccountComponentMetadata::from_toml(toml_text); - assert_matches::assert_matches!( - result.unwrap_err(), - AccountComponentTemplateError::DuplicateEntryNames(_) - ); - } - - #[test] - fn toml_fail_multislot_spans_one_slot() { - let toml_text = r#" - name = "Test Component" - description = "Test multislot spans one slot" - version = "1.0.1" - supported-types = ["RegularAccountImmutableCode"] - - [[storage]] - name = "multislot_one_slot" - slots = [0] - values = [ - [ "0x1", "0x2", "0x3", "0x4" ], - ] - "#; - - let result = AccountComponentMetadata::from_toml(toml_text); - assert_matches::assert_matches!( - result.unwrap_err(), - AccountComponentTemplateError::MultiSlotSpansOneSlot - ); - } - - #[test] - fn test_toml_multislot_success() { - let toml_text = r#" - name = "Test Component" - description = "A multi-slot success scenario" - version = "1.0.1" - supported-types = ["FungibleFaucet"] - - [[storage]] - name = "multi_slot_example" - slots = [0, 1, 2] - values = [ - ["0x1", "0x2", "0x3", "0x4"], - ["0x5", "0x6", "0x7", "0x8"], - ["0x9", "0xa", "0xb", "0xc"] - ] - "#; - - let metadata = AccountComponentMetadata::from_toml(toml_text).unwrap(); - match &metadata.storage_entries()[0] { - StorageEntry::MultiSlot { slots, word_entries } => match word_entries { - crate::account::component::template::MultiWordRepresentation::Value { - identifier, - values, - } => { - assert_eq!(identifier.name.as_str(), "multi_slot_example"); - assert_eq!(slots, &(0..3)); - assert_eq!(values.len(), 3); - }, - }, - _ => panic!("expected multislot"), - } - } -} diff --git a/crates/miden-objects/src/account/component/template/storage/placeholder.rs b/crates/miden-objects/src/account/component/template/storage/placeholder.rs deleted file mode 100644 index 8acbbfeeda..0000000000 --- a/crates/miden-objects/src/account/component/template/storage/placeholder.rs +++ /dev/null @@ -1,549 +0,0 @@ -use alloc::boxed::Box; -use alloc::collections::BTreeMap; -use alloc::string::{String, ToString}; -use core::error::Error; -use core::fmt::{self, Display}; - -use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_core::{Felt, Word}; -use miden_crypto::dsa::{ecdsa_k256_keccak, rpo_falcon512}; -use miden_processor::DeserializationError; -use thiserror::Error; - -use crate::asset::TokenSymbol; -use crate::utils::sync::LazyLock; - -/// A global registry for template converters. -/// -/// It is used during component instantiation to dynamically convert template placeholders into -/// their respective storage values. -pub static TEMPLATE_REGISTRY: LazyLock = LazyLock::new(|| { - let mut registry = TemplateRegistry::new(); - registry.register_felt_type::(); - registry.register_felt_type::(); - registry.register_felt_type::(); - registry.register_felt_type::(); - registry.register_felt_type::(); - registry.register_word_type::(); - registry.register_word_type::(); - registry.register_word_type::(); - registry -}); - -// STORAGE VALUE NAME -// ================================================================================================ - -/// A simple wrapper type around a string key that identifies values. -/// -/// A storage value name is a string that identifies dynamic values within a component's metadata -/// storage entries. -/// -/// These names can be chained together, in a way that allows composing unique keys for inner -/// templated elements. -/// -/// At component instantiation, a map of names to values must be provided to dynamically -/// replace these placeholders with the instance's actual values. -#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))] -#[cfg_attr(feature = "std", serde(transparent))] -pub struct StorageValueName { - fully_qualified_name: String, -} - -impl StorageValueName { - /// Creates a new [`StorageValueName`] from the provided string. - /// - /// A [`StorageValueName`] serves as an identifier for storage values that are determined at - /// instantiation time of an [`AccountComponent`](crate::account::AccountComponent). - /// - /// The key can consist of one or more segments separated by dots (`.`). - /// Each segment must be non-empty and may contain only alphanumeric characters, underscores - /// (`_`), or hyphens (`-`). - /// - /// # Errors - /// - /// This method returns an error if: - /// - Any segment (or the whole key) is empty. - /// - Any segment contains invalid characters. - pub fn new(base: impl Into) -> Result { - let base: String = base.into(); - for segment in base.split('.') { - Self::validate_segment(segment)?; - } - Ok(Self { fully_qualified_name: base }) - } - - /// Creates an empty [`StorageValueName`]. - pub(crate) fn empty() -> Self { - StorageValueName { fully_qualified_name: String::default() } - } - - /// Adds a suffix to the storage value name, separated by a period. - #[must_use] - pub fn with_suffix(self, suffix: &StorageValueName) -> StorageValueName { - let mut key = self; - if !suffix.as_str().is_empty() { - if !key.as_str().is_empty() { - key.fully_qualified_name.push('.'); - } - key.fully_qualified_name.push_str(suffix.as_str()); - } - - key - } - - /// Returns the fully qualified name as a string slice. - pub fn as_str(&self) -> &str { - &self.fully_qualified_name - } - - fn validate_segment(segment: &str) -> Result<(), StorageValueNameError> { - if segment.is_empty() { - return Err(StorageValueNameError::EmptySegment); - } - if let Some(offending_char) = - segment.chars().find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == '-')) - { - return Err(StorageValueNameError::InvalidCharacter { - part: segment.to_string(), - character: offending_char, - }); - } - - Ok(()) - } -} - -impl Display for StorageValueName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -impl Serializable for StorageValueName { - fn write_into(&self, target: &mut W) { - target.write(&self.fully_qualified_name); - } -} - -impl Deserializable for StorageValueName { - fn read_from(source: &mut R) -> Result { - let key: String = source.read()?; - Ok(StorageValueName { fully_qualified_name: key }) - } -} - -#[derive(Debug, Error)] -pub enum StorageValueNameError { - #[error("key segment is empty")] - EmptySegment, - #[error("key segment '{part}' contains invalid character '{character}'")] - InvalidCharacter { part: String, character: char }, -} - -// TEMPLATE TYPE ERROR -// ================================================================================================ - -/// Errors that can occur when parsing or converting template types. -/// -/// This enum covers various failure cases including parsing errors, conversion errors, -/// unsupported conversions, and cases where a required type is not found in the registry. -#[derive(Debug, Error)] -pub enum TemplateTypeError { - #[error("conversion error: {0}")] - ConversionError(String), - #[error("felt type ` {0}` not found in the type registry")] - FeltTypeNotFound(TemplateType), - #[error("invalid type name `{0}`: {1}")] - InvalidTypeName(String, String), - #[error("failed to parse input `{input}` as `{template_type}`")] - ParseError { - input: String, - template_type: TemplateType, - source: Box, - }, - #[error("word type ` {0}` not found in the type registry")] - WordTypeNotFound(TemplateType), -} - -impl TemplateTypeError { - /// Creates a [`TemplateTypeError::ParseError`]. - pub fn parse( - input: impl Into, - template_type: TemplateType, - source: impl Error + Send + Sync + 'static, - ) -> Self { - TemplateTypeError::ParseError { - input: input.into(), - template_type, - source: Box::new(source), - } - } -} - -// TEMPLATE TYPE -// ================================================================================================ - -/// A newtype wrapper around a `String`, representing a template's type identifier. -#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] -#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))] -#[cfg_attr(feature = "std", serde(transparent))] -pub struct TemplateType(String); - -impl TemplateType { - /// Creates a new [`TemplateType`] from a `String`. - /// - /// The name must follow a Rust-style namespace format, consisting of one or more segments - /// (non-empty, and alphanumerical) separated by double-colon (`::`) delimiters. - /// - /// # Errors - /// - /// - If the identifier is empty. - /// - If it is composed of one or more segments separated by `::`. - /// - If any segment is empty or contains something other than alphanumerical - /// characters/underscores. - pub fn new(s: impl Into) -> Result { - let s = s.into(); - if s.is_empty() { - return Err(TemplateTypeError::InvalidTypeName( - s.clone(), - "template type identifier is empty".to_string(), - )); - } - for segment in s.split("::") { - if segment.is_empty() { - return Err(TemplateTypeError::InvalidTypeName( - s.clone(), - "empty segment in template type identifier".to_string(), - )); - } - if !segment.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { - return Err(TemplateTypeError::InvalidTypeName( - s.clone(), - format!("segment '{segment}' contains invalid characters"), - )); - } - } - Ok(Self(s)) - } - - /// Returns the [`TemplateType`] for the native [`Felt`] type. - pub fn native_felt() -> TemplateType { - TemplateType::new("felt").expect("type is well formed") - } - - /// Returns the [`TemplateType`] for the native [`Word`] type. - pub fn native_word() -> TemplateType { - TemplateType::new("word").expect("type is well formed") - } - - /// Returns the [`TemplateType`] for storage map placeholders. - pub fn storage_map() -> TemplateType { - TemplateType::new("map").expect("type is well formed") - } - - /// Returns a reference to the inner string. - pub fn as_str(&self) -> &str { - &self.0 - } -} - -impl Display for TemplateType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -impl Serializable for TemplateType { - fn write_into(&self, target: &mut W) { - target.write(self.0.clone()) - } -} - -impl Deserializable for TemplateType { - fn read_from(source: &mut R) -> Result { - let id: String = source.read()?; - - TemplateType::new(id).map_err(|err| DeserializationError::InvalidValue(err.to_string())) - } -} - -// TEMPLATE REQUIREMENT -// ================================================================================================ - -/// Describes the expected type and additional metadata for a templated storage entry. -/// -/// A `PlaceholderTypeRequirement` specifies the expected type identifier for a storage entry as -/// well as an optional description. This information is used to validate and provide context for -/// dynamic storage values. -#[derive(Debug)] -pub struct PlaceholderTypeRequirement { - /// The expected type identifier. - pub r#type: TemplateType, - /// An optional description providing additional context. - pub description: Option, -} - -// TEMPLATE TRAITS -// ================================================================================================ - -/// Trait for converting a string into a single `Felt`. -pub trait TemplateFelt { - /// Returns the type identifier. - fn type_name() -> TemplateType; - /// Parses the input string into a `Felt`. - fn parse_felt(input: &str) -> Result; -} - -/// Trait for converting a string into a single `Word`. -pub trait TemplateWord { - /// Returns the type identifier. - fn type_name() -> TemplateType; - /// Parses the input string into a `Word`. - fn parse_word(input: &str) -> Result; -} - -// FELT IMPLS FOR NATIVE TYPES -// ================================================================================================ - -impl TemplateFelt for u8 { - fn type_name() -> TemplateType { - TemplateType::new("u8").expect("type is well formed") - } - - fn parse_felt(input: &str) -> Result { - let native: u8 = input - .parse() - .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?; - Ok(Felt::from(native)) - } -} - -impl TemplateFelt for u16 { - fn type_name() -> TemplateType { - TemplateType::new("u16").expect("type is well formed") - } - - fn parse_felt(input: &str) -> Result { - let native: u16 = input - .parse() - .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?; - Ok(Felt::from(native)) - } -} - -impl TemplateFelt for u32 { - fn type_name() -> TemplateType { - TemplateType::new("u32").expect("type is well formed") - } - - fn parse_felt(input: &str) -> Result { - let native: u32 = input - .parse() - .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?; - Ok(Felt::from(native)) - } -} - -impl TemplateFelt for Felt { - fn type_name() -> TemplateType { - TemplateType::new("felt").expect("type is well formed") - } - - fn parse_felt(input: &str) -> Result { - let n = if let Some(hex) = input.strip_prefix("0x").or_else(|| input.strip_prefix("0X")) { - u64::from_str_radix(hex, 16) - } else { - input.parse::() - } - .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?; - Felt::try_from(n).map_err(|_| TemplateTypeError::ConversionError(input.to_string())) - } -} - -impl TemplateFelt for TokenSymbol { - fn type_name() -> TemplateType { - TemplateType::new("token_symbol").expect("type is well formed") - } - fn parse_felt(input: &str) -> Result { - let token = TokenSymbol::new(input) - .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?; - Ok(Felt::from(token)) - } -} - -// WORD IMPLS FOR NATIVE TYPES -// ================================================================================================ - -#[derive(Debug, Error)] -#[error("error parsing word: {0}")] -struct WordParseError(String); - -/// Pads a hex string to 64 characters (excluding the 0x prefix). -/// -/// If the input starts with "0x" and has fewer than 64 hex characters after the prefix, -/// it will be left-padded with zeros. Otherwise, returns the input unchanged. -fn pad_hex_string(input: &str) -> String { - if input.starts_with("0x") && input.len() < 66 { - // 66 = "0x" + 64 hex chars - let hex_part = &input[2..]; - let padding = "0".repeat(64 - hex_part.len()); - format!("0x{}{}", padding, hex_part) - } else { - input.to_string() - } -} - -impl TemplateWord for Word { - fn type_name() -> TemplateType { - TemplateType::native_word() - } - fn parse_word(input: &str) -> Result { - let padded_input = pad_hex_string(input); - - Word::try_from(padded_input.as_str()).map_err(|err| { - TemplateTypeError::parse( - input.to_string(), // Use original input in error - Self::type_name(), - WordParseError(err.to_string()), - ) - }) - } -} - -impl TemplateWord for rpo_falcon512::PublicKey { - fn type_name() -> TemplateType { - TemplateType::new("auth::rpo_falcon512::pub_key").expect("type is well formed") - } - fn parse_word(input: &str) -> Result { - let padded_input = pad_hex_string(input); - - Word::try_from(padded_input.as_str()).map_err(|err| { - TemplateTypeError::parse( - input.to_string(), // Use original input in error - Self::type_name(), - WordParseError(err.to_string()), - ) - }) - } -} - -impl TemplateWord for ecdsa_k256_keccak::PublicKey { - fn type_name() -> TemplateType { - TemplateType::new("auth::ecdsa_k256_keccak::pub_key").expect("type is well formed") - } - fn parse_word(input: &str) -> Result { - let padded_input = pad_hex_string(input); - - Word::try_from(padded_input.as_str()).map_err(|err| { - TemplateTypeError::parse( - input.to_string(), - Self::type_name(), - WordParseError(err.to_string()), - ) - }) - } -} - -// TYPE ALIASES FOR CONVERTER CLOSURES -// ================================================================================================ - -/// Type alias for a function that converts a string into a [`Felt`] value. -type TemplateFeltConverter = fn(&str) -> Result; - -/// Type alias for a function that converts a string into a [`Word`]. -type TemplateWordConverter = fn(&str) -> Result; - -// TODO: Implement converting to list of words for multi-slot values - -// TEMPLATE REGISTRY -// ================================================================================================ - -/// Registry for template converters. -/// -/// This registry maintains mappings from type identifiers (as strings) to conversion functions for -/// [`Felt`], [`Word`], and [`Vec`] types. It is used to dynamically parse template inputs -/// into their corresponding storage representations. -#[derive(Clone, Debug, Default)] -pub struct TemplateRegistry { - felt: BTreeMap, - word: BTreeMap, -} - -impl TemplateRegistry { - /// Creates a new, empty `TemplateRegistry`. - /// - /// The registry is initially empty and conversion functions can be registered using the - /// `register_*_type` methods. - pub fn new() -> Self { - Self { ..Default::default() } - } - - /// Registers a `TemplateFelt` converter, to interpret a string as a [`Felt``]. - pub fn register_felt_type(&mut self) { - let key = T::type_name(); - self.felt.insert(key, T::parse_felt); - } - - /// Registers a `TemplateWord` converter, to interpret a string as a [`Word`]. - pub fn register_word_type(&mut self) { - let key = T::type_name(); - self.word.insert(key, T::parse_word); - } - - /// Attempts to parse a string into a `Felt` using the registered converter for the given type - /// name. - /// - /// # Arguments - /// - /// - type_name: A string that acts as the type identifier. - /// - value: The string representation of the value to be parsed. - /// - /// # Errors - /// - /// - If the type is not registered or if the conversion fails. - pub fn try_parse_felt( - &self, - type_name: &TemplateType, - value: &str, - ) -> Result { - let converter = self - .felt - .get(type_name) - .ok_or(TemplateTypeError::FeltTypeNotFound(type_name.clone()))?; - converter(value) - } - - /// Attempts to parse a string into a `Word` using the registered converter for the given type - /// name. - /// - /// # Arguments - /// - /// - type_name: A string that acts as the type identifier. - /// - value: The string representation of the value to be parsed. - /// - /// # Errors - /// - /// - If the type is not registered or if the conversion fails. - pub fn try_parse_word( - &self, - type_name: &TemplateType, - value: &str, - ) -> Result { - let converter = self - .word - .get(type_name) - .ok_or(TemplateTypeError::WordTypeNotFound(type_name.clone()))?; - converter(value) - } - - /// Returns `true` if a `TemplateFelt` is registered for the given type. - pub fn contains_felt_type(&self, type_name: &TemplateType) -> bool { - self.felt.contains_key(type_name) - } - - /// Returns `true` if a `TemplateWord` is registered for the given type. - pub fn contains_word_type(&self, type_name: &TemplateType) -> bool { - self.word.contains_key(type_name) - } -} diff --git a/crates/miden-objects/src/account/component/template/storage/toml.rs b/crates/miden-objects/src/account/component/template/storage/toml.rs deleted file mode 100644 index 396c88f0c7..0000000000 --- a/crates/miden-objects/src/account/component/template/storage/toml.rs +++ /dev/null @@ -1,789 +0,0 @@ -use alloc::collections::BTreeMap; -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use core::fmt; - -use miden_core::{Felt, Word}; -use serde::de::value::MapAccessDeserializer; -use serde::de::{self, Error, MapAccess, SeqAccess, Visitor}; -use serde::ser::{SerializeMap, SerializeStruct}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use thiserror::Error; - -use super::placeholder::TemplateType; -use super::{ - FeltRepresentation, - InitStorageData, - MapEntry, - MapRepresentation, - MultiWordRepresentation, - StorageEntry, - StorageValueNameError, - WordRepresentation, -}; -use crate::account::component::FieldIdentifier; -use crate::account::component::template::storage::placeholder::{TEMPLATE_REGISTRY, TemplateFelt}; -use crate::account::{AccountComponentMetadata, StorageValueName}; -use crate::errors::AccountComponentTemplateError; - -// ACCOUNT COMPONENT METADATA TOML FROM/TO -// ================================================================================================ - -impl AccountComponentMetadata { - /// Deserializes `toml_string` and validates the resulting [AccountComponentMetadata] - /// - /// # Errors - /// - /// - If deserialization fails - /// - If the template specifies storage slots with duplicates. - /// - If the template includes slot numbers that do not start at zero. - /// - If storage slots in the template are not contiguous. - pub fn from_toml(toml_string: &str) -> Result { - let component: AccountComponentMetadata = toml::from_str(toml_string) - .map_err(AccountComponentTemplateError::TomlDeserializationError)?; - - component.validate()?; - Ok(component) - } - - /// Serializes the account component template into a TOML string. - pub fn to_toml(&self) -> Result { - let toml = - toml::to_string(self).map_err(AccountComponentTemplateError::TomlSerializationError)?; - Ok(toml) - } -} - -// WORD REPRESENTATION SERIALIZATION -// ================================================================================================ - -impl Serialize for WordRepresentation { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - WordRepresentation::Template { identifier, r#type } => { - let mut state = serializer.serialize_struct("WordRepresentation", 3)?; - state.serialize_field("name", &identifier.name())?; - state.serialize_field("description", &identifier.description())?; - state.serialize_field("type", r#type)?; - state.end() - }, - WordRepresentation::Value { identifier, value } => { - let mut state = serializer.serialize_struct("WordRepresentation", 3)?; - - state.serialize_field("name", &identifier.as_ref().map(|id| id.name()))?; - state.serialize_field( - "description", - &identifier.as_ref().map(|id| id.description()), - )?; - state.serialize_field("value", value)?; - state.end() - }, - } - } -} - -impl<'de> Deserialize<'de> for WordRepresentation { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct WordRepresentationVisitor; - - impl<'de> Visitor<'de> for WordRepresentationVisitor { - type Value = WordRepresentation; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string or a map representing a WordRepresentation") - } - - // A bare string is interpreted it as a Value variant. - fn visit_str(self, value: &str) -> Result - where - E: Error, - { - let parsed_value = Word::parse(value).map_err(|_err| { - E::invalid_value( - serde::de::Unexpected::Str(value), - &"a valid hexadecimal string", - ) - })?; - Ok(<[Felt; _]>::from(&parsed_value).into()) - } - - fn visit_string(self, value: String) -> Result - where - E: Error, - { - self.visit_str(&value) - } - - fn visit_seq(self, seq: A) -> Result - where - A: SeqAccess<'de>, - { - // Deserialize as a list of felt representations - let elements: Vec = - Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))?; - if elements.len() != 4 { - return Err(Error::invalid_length( - elements.len(), - &"expected an array of 4 elements", - )); - } - let value: [FeltRepresentation; 4] = - elements.try_into().expect("length was checked"); - Ok(WordRepresentation::new_value(value, None)) - } - - fn visit_map(self, map: M) -> Result - where - M: MapAccess<'de>, - { - #[derive(Deserialize, Debug)] - struct WordRepresentationHelper { - name: Option, - description: Option, - // The "value" field (if present) must be an array of 4 FeltRepresentations. - value: Option<[FeltRepresentation; 4]>, - #[serde(rename = "type")] - r#type: Option, - } - - let helper = - WordRepresentationHelper::deserialize(MapAccessDeserializer::new(map))?; - - if let Some(value) = helper.value { - let identifier = helper - .name - .map(|n| parse_field_identifier::(n, helper.description.clone())) - .transpose()?; - Ok(WordRepresentation::Value { value, identifier }) - } else { - // Otherwise, we expect a Template variant (name is required for identification) - let identifier = expect_parse_field_identifier::( - helper.name, - helper.description, - "word template", - )?; - let r#type = helper.r#type.unwrap_or_else(TemplateType::native_word); - Ok(WordRepresentation::Template { r#type, identifier }) - } - } - } - - deserializer.deserialize_any(WordRepresentationVisitor) - } -} - -// FELT REPRESENTATION SERIALIZATION -// ================================================================================================ - -impl Serialize for FeltRepresentation { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - FeltRepresentation::Value { identifier, value } => { - let hex = value.to_string(); - if identifier.is_none() { - serializer.serialize_str(&hex) - } else { - let mut state = serializer.serialize_struct("FeltRepresentation", 3)?; - if let Some(id) = identifier { - state.serialize_field("name", &id.name)?; - state.serialize_field("description", &id.description)?; - } - state.serialize_field("value", &hex)?; - state.end() - } - }, - FeltRepresentation::Template { identifier, r#type } => { - let mut state = serializer.serialize_struct("FeltRepresentation", 3)?; - state.serialize_field("name", &identifier.name)?; - state.serialize_field("description", &identifier.description)?; - state.serialize_field("type", r#type)?; - state.end() - }, - } - } -} - -impl<'de> Deserialize<'de> for FeltRepresentation { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - // Felts can be deserialized as either: - // - // - Scalars (parsed from strings) - // - A table object that can or cannot hardcode a value. If not present, this is a - // placeholder type - #[derive(Deserialize)] - #[serde(untagged)] - enum Intermediate { - Map { - name: Option, - description: Option, - #[serde(default)] - value: Option, - #[serde(rename = "type")] - r#type: Option, - }, - Scalar(String), - } - - let intermediate = Intermediate::deserialize(deserializer)?; - match intermediate { - Intermediate::Scalar(s) => { - let felt = Felt::parse_felt(&s) - .map_err(|e| D::Error::custom(format!("failed to parse Felt: {e}")))?; - Ok(FeltRepresentation::Value { identifier: None, value: felt }) - }, - Intermediate::Map { name, description, value, r#type } => { - // Get the defined type, or the default if it was not specified - let felt_type = r#type.unwrap_or_else(TemplateType::native_felt); - if let Some(val_str) = value { - // Parse into felt from the input string - let felt = - TEMPLATE_REGISTRY.try_parse_felt(&felt_type, &val_str).map_err(|e| { - D::Error::custom(format!("failed to parse {felt_type} as Felt: {e}")) - })?; - let identifier = name - .map(|n| parse_field_identifier::(n, description.clone())) - .transpose()?; - Ok(FeltRepresentation::Value { identifier, value: felt }) - } else { - // No value provided, so this is a placeholder - let identifier = expect_parse_field_identifier::( - name, - description, - "map template", - )?; - Ok(FeltRepresentation::Template { r#type: felt_type, identifier }) - } - }, - } - } -} - -// STORAGE VALUES -// ================================================================================================ - -/// Represents the type of values that can be found in a storage slot's `values` field. -#[derive(Debug, Deserialize, Serialize)] -#[serde(untagged)] -enum StorageValues { - /// List of individual words (for multi-slot entries). - Words(Vec<[FeltRepresentation; 4]>), - /// List of key-value entries (for map storage slots). - MapEntries(Vec), -} - -// STORAGE ENTRY SERIALIZATION -// ================================================================================================ - -#[derive(Default, Debug, Deserialize, Serialize)] -struct RawStorageEntry { - #[serde(flatten)] - identifier: Option, - slot: Option, - slots: Option>, - #[serde(rename = "type")] - word_type: Option, - value: Option<[FeltRepresentation; 4]>, - values: Option, -} - -impl From for RawStorageEntry { - fn from(entry: StorageEntry) -> Self { - match entry { - StorageEntry::Value { slot, word_entry } => match word_entry { - WordRepresentation::Value { identifier, value } => RawStorageEntry { - slot: Some(slot), - identifier, - value: Some(value), - ..Default::default() - }, - WordRepresentation::Template { identifier, r#type } => RawStorageEntry { - slot: Some(slot), - identifier: Some(identifier), - word_type: Some(r#type), - ..Default::default() - }, - }, - StorageEntry::Map { slot, map } => match map { - MapRepresentation::Value { identifier, entries } => RawStorageEntry { - slot: Some(slot), - identifier: Some(FieldIdentifier { - name: identifier.name, - description: identifier.description, - }), - values: Some(StorageValues::MapEntries(entries)), - ..Default::default() - }, - MapRepresentation::Template { identifier } => RawStorageEntry { - slot: Some(slot), - identifier: Some(FieldIdentifier { - name: identifier.name, - description: identifier.description, - }), - word_type: Some(TemplateType::storage_map()), - ..Default::default() - }, - }, - StorageEntry::MultiSlot { slots, word_entries } => match word_entries { - MultiWordRepresentation::Value { identifier, values } => RawStorageEntry { - slot: None, - identifier: Some(identifier), - slots: Some(slots.collect()), - values: Some(StorageValues::Words(values)), - ..Default::default() - }, - }, - } - } -} - -impl Serialize for StorageEntry { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let raw_storage_entry: RawStorageEntry = self.clone().into(); - raw_storage_entry.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for StorageEntry { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let raw = RawStorageEntry::deserialize(deserializer)?; - - if let Some(word_entry) = raw.value { - // If a value was provided, this is a WordRepresentation::Value entry - let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "value entry"))?; - let identifier = raw.identifier; - Ok(StorageEntry::Value { - slot, - word_entry: WordRepresentation::Value { value: word_entry, identifier }, - }) - } else if let Some(StorageValues::MapEntries(map_entries)) = raw.values { - // If `values` field contains key/value pairs, deserialize as map - let identifier = - raw.identifier.ok_or_else(|| missing_field_for("identifier", "map entry"))?; - let name = identifier.name; - let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "map entry"))?; - if let Some(word_type) = raw.word_type.clone() - && word_type != TemplateType::storage_map() - { - return Err(D::Error::custom( - "map storage entries with `values` must have `type = \"map\"`", - )); - } - let mut map = MapRepresentation::new_value(map_entries, name); - if let Some(desc) = identifier.description { - map = map.with_description(desc); - } - Ok(StorageEntry::Map { slot, map }) - } else if let Some(word_type) = raw.word_type.clone() - && word_type == TemplateType::storage_map() - { - let identifier = - raw.identifier.ok_or_else(|| missing_field_for("identifier", "map entry"))?; - let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "map entry"))?; - let FieldIdentifier { name, description } = identifier; - - // If values is specified (even if empty), create a value map. - // Due to #[serde(untagged)] on StorageValues, values = [] gets deserialized - // as StorageValues::Words(vec![]), so we need to treat it as an empty map. - // Otherwise, create a template map. - let mut map = if raw.values.is_some() { - MapRepresentation::new_value(Vec::new(), name) - } else { - MapRepresentation::new_template(name) - }; - - if let Some(desc) = description { - map = map.with_description(desc); - } - Ok(StorageEntry::Map { slot, map }) - } else if let Some(StorageValues::Words(values)) = raw.values { - let identifier = raw - .identifier - .ok_or_else(|| missing_field_for("identifier", "multislot entry"))?; - - let mut slots = - raw.slots.ok_or_else(|| missing_field_for("slots", "multislot entry"))?; - - // Sort so we can check contiguity - slots.sort_unstable(); - for pair in slots.windows(2) { - if pair[1] != pair[0] + 1 { - return Err(serde::de::Error::custom(format!( - "`slots` in the `{}` storage entry are not contiguous", - identifier.name - ))); - } - } - let start = slots[0]; - let end = slots.last().expect("checked validity") + 1; - Ok(StorageEntry::new_multislot(identifier, start..end, values)) - } else if let Some(word_type) = raw.word_type { - // If a type was provided instead, this is a WordRepresentation::Template entry - let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "single-slot entry"))?; - let identifier = raw - .identifier - .ok_or_else(|| missing_field_for("identifier", "single-slot entry"))?; - let word_entry = WordRepresentation::Template { r#type: word_type, identifier }; - Ok(StorageEntry::Value { slot, word_entry }) - } else { - Err(D::Error::custom("placeholder storage entries require the `type` field")) - } - } -} - -// INIT STORAGE DATA -// ================================================================================================ - -impl InitStorageData { - /// Creates an instance of [`InitStorageData`] from a TOML string. - /// - /// This method parses the provided TOML and flattens nested tables into - /// dot‑separated keys using [`StorageValueName`] as keys. All values are converted to plain - /// strings (so that, for example, `key = 10` and `key = "10"` both yield - /// `String::from("10")` as the value). - /// - /// # Errors - /// - /// - If duplicate keys or empty tables are found in the string - /// - If the TOML string includes arrays - pub fn from_toml(toml_str: &str) -> Result { - let value: toml::Value = toml::from_str(toml_str)?; - let mut value_entries = BTreeMap::new(); - let mut map_entries = BTreeMap::new(); - // Start with an empty prefix (i.e. the default, which is an empty string) - Self::flatten_parse_toml_value( - StorageValueName::empty(), - value, - &mut value_entries, - &mut map_entries, - )?; - - Ok(InitStorageData::new(value_entries, map_entries)) - } - - /// Recursively flattens a TOML `Value` into a flat mapping. - /// - /// When recursing into nested tables, keys are combined using - /// [`StorageValueName::with_suffix`]. If an encountered table is empty (and not the top-level), - /// an error is returned. Arrays are not supported. - fn flatten_parse_toml_value( - prefix: StorageValueName, - value: toml::Value, - value_entries: &mut BTreeMap, - map_entries: &mut BTreeMap>, - ) -> Result<(), InitStorageDataError> { - match value { - toml::Value::Table(table) => { - // If this is not the root and the table is empty, error - if !prefix.as_str().is_empty() && table.is_empty() { - return Err(InitStorageDataError::EmptyTable(prefix.as_str().into())); - } - for (key, val) in table { - // Create a new key and combine it with the current prefix. - let new_key = StorageValueName::new(key.to_string()) - .map_err(InitStorageDataError::InvalidStorageValueName)?; - let new_prefix = prefix.clone().with_suffix(&new_key); - Self::flatten_parse_toml_value(new_prefix, val, value_entries, map_entries)?; - } - }, - toml::Value::Array(items) if items.is_empty() => { - if prefix.as_str().is_empty() { - return Err(InitStorageDataError::ArraysNotSupported); - } - map_entries.insert(prefix, Vec::new()); - }, - toml::Value::Array(items) => { - if prefix.as_str().is_empty() - || !items.iter().all(|item| matches!(item, toml::Value::Table(_))) - { - return Err(InitStorageDataError::ArraysNotSupported); - } - - let entries = items - .into_iter() - .map(parse_map_entry_value) - .collect::, _>>()?; - map_entries.insert(prefix, entries); - }, - toml_value => { - // Get the string value, or convert to string if it's some other type - let value = match toml_value { - toml::Value::String(s) => s.clone(), - _ => toml_value.to_string(), - }; - value_entries.insert(prefix, value); - }, - } - Ok(()) - } -} - -#[derive(Debug, Error)] -pub enum InitStorageDataError { - #[error("failed to parse TOML")] - InvalidToml(#[from] toml::de::Error), - - #[error("empty table encountered for key `{0}`")] - EmptyTable(String), - - #[error("invalid input: arrays are not supported")] - ArraysNotSupported, - - #[error("invalid storage value name")] - InvalidStorageValueName(#[source] StorageValueNameError), - - #[error("invalid map entry: {0}")] - InvalidMapEntry(String), -} - -impl Serialize for FieldIdentifier { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(2))?; - map.serialize_entry("name", &self.name)?; - map.serialize_entry("description", &self.description)?; - map.end() - } -} - -struct FieldIdentifierVisitor; - -impl<'de> Visitor<'de> for FieldIdentifierVisitor { - type Value = FieldIdentifier; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map with 'name' and optionally 'description'") - } - - fn visit_map(self, mut map: M) -> Result - where - M: MapAccess<'de>, - { - let mut name = None; - let mut description = None; - while let Some(key) = map.next_key::()? { - match key.as_str() { - "name" => { - name = Some(map.next_value()?); - }, - "description" => { - let d: String = map.next_value()?; - // Normalize empty or whitespace-only strings into None - description = if d.trim().is_empty() { None } else { Some(d) }; - }, - _ => { - // Ignore other values as FieldIdentifiers are flattened within other structs - let _: de::IgnoredAny = map.next_value()?; - }, - } - } - let name = name.ok_or_else(|| de::Error::missing_field("name"))?; - Ok(FieldIdentifier { name, description }) - } -} - -impl<'de> Deserialize<'de> for FieldIdentifier { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_map(FieldIdentifierVisitor) - } -} - -// UTILS / HELPERS -// ================================================================================================ - -fn missing_field_for(field: &str, context: &str) -> E { - E::custom(format!("missing '{field}' field for {context}")) -} - -/// Checks than an optional (but expected) name field has been defined and is correct. -fn expect_parse_field_identifier( - n: Option, - description: Option, - context: &str, -) -> Result { - let name = n.ok_or_else(|| missing_field_for("name", context))?; - parse_field_identifier(name, description) -} - -/// Tries to parse a string into a [FieldIdentifier]. -fn parse_field_identifier( - n: String, - description: Option, -) -> Result { - StorageValueName::new(n) - .map_err(|err| E::custom(format!("invalid `name`: {err}"))) - .map(|storage_name| { - if let Some(desc) = description { - FieldIdentifier::with_description(storage_name, desc) - } else { - FieldIdentifier::with_name(storage_name) - } - }) -} - -/// Parses a `{ key, value }` TOML table into a `(Word, Word)` pair, rejecting templates. -fn parse_map_entry_value(item: toml::Value) -> Result<(Word, Word), InitStorageDataError> { - // Try to deserialize the user input as a map entry - let entry: MapEntry = MapEntry::deserialize(item) - .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?; - - // Make sure the entry does not contain templates, only static - if entry.key().template_requirements(StorageValueName::empty()).next().is_some() - || entry.value().template_requirements(StorageValueName::empty()).next().is_some() - { - return Err(InitStorageDataError::InvalidMapEntry( - "map entries cannot contain templates".into(), - )); - } - - // Interpret the user input as static words - let key = entry - .key() - .try_build_word(&InitStorageData::default(), StorageValueName::empty()) - .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?; - let value = entry - .value() - .try_build_word(&InitStorageData::default(), StorageValueName::empty()) - .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?; - - Ok((key, value)) -} - -// TESTS -// ================================================================================================ - -#[cfg(test)] -mod tests { - use alloc::string::ToString; - use core::error::Error; - - use super::*; - use crate::account::component::toml::InitStorageDataError; - - #[test] - fn from_toml_str_with_nested_table_and_flattened() { - let toml_table = r#" - [token_metadata] - max_supply = "1000000000" - symbol = "ETH" - decimals = "9" - "#; - - let toml_inline = r#" - token_metadata.max_supply = "1000000000" - token_metadata.symbol = "ETH" - token_metadata.decimals = "9" - "#; - - let storage_table = InitStorageData::from_toml(toml_table).unwrap(); - let storage_inline = InitStorageData::from_toml(toml_inline).unwrap(); - - assert_eq!(storage_table.placeholders(), storage_inline.placeholders()); - } - - #[test] - fn from_toml_str_with_deeply_nested_tables() { - let toml_str = r#" - [a] - b = "0xb" - - [a.c] - d = "0xd" - - [x.y.z] - w = 42 # NOTE: This gets parsed as string - "#; - - let storage = InitStorageData::from_toml(toml_str).expect("Failed to parse TOML"); - let key1 = StorageValueName::new("a.b".to_string()).unwrap(); - let key2 = StorageValueName::new("a.c.d".to_string()).unwrap(); - let key3 = StorageValueName::new("x.y.z.w".to_string()).unwrap(); - - assert_eq!(storage.get(&key1).unwrap(), "0xb"); - assert_eq!(storage.get(&key2).unwrap(), "0xd"); - assert_eq!(storage.get(&key3).unwrap(), "42"); - } - - #[test] - fn test_error_on_array() { - let toml_str = r#" - token_metadata.v = [1, 2, 3] - "#; - - let result = InitStorageData::from_toml(toml_str); - assert_matches::assert_matches!( - result.unwrap_err(), - InitStorageDataError::ArraysNotSupported - ); - } - - #[test] - fn parse_map_entries_from_array() { - let toml_str = r#" - my_map = [ - { key = "0x0000000000000000000000000000000000000000000000000000000000000001", value = "0x0000000000000000000000000000000000000000000000000000000000000010" }, - { key = "0x0000000000000000000000000000000000000000000000000000000000000002", value = ["1", "2", "3", "4"] } - ] - "#; - - let storage = InitStorageData::from_toml(toml_str).expect("Failed to parse map entries"); - let map_name = StorageValueName::new("my_map").unwrap(); - let entries = storage.map_entries(&map_name).expect("map entries missing"); - assert_eq!(entries.len(), 2); - - let first_key = - Word::try_from("0x0000000000000000000000000000000000000000000000000000000000000001") - .unwrap(); - assert_eq!(entries[0].0, first_key); - - let second_value = - Word::from([Felt::new(1u64), Felt::new(2u64), Felt::new(3u64), Felt::new(4u64)]); - assert_eq!(entries[1].1, second_value); - } - - #[test] - fn error_on_empty_subtable() { - let toml_str = r#" - [a] - b = {} - "#; - - let result = InitStorageData::from_toml(toml_str); - assert_matches::assert_matches!(result.unwrap_err(), InitStorageDataError::EmptyTable(_)); - } - - #[test] - fn error_on_duplicate_keys() { - let toml_str = r#" - token_metadata.max_supply = "1000000000" - token_metadata.max_supply = "500000000" - "#; - - let result = InitStorageData::from_toml(toml_str).unwrap_err(); - // TOML does not support duplicate keys - assert_matches::assert_matches!(result, InitStorageDataError::InvalidToml(_)); - assert!(result.source().unwrap().to_string().contains("duplicate")); - } -} diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 2f38dab713..12ed0c282a 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -32,23 +32,7 @@ pub use code::AccountCode; pub use code::procedure::AccountProcedureRoot; pub mod component; -pub use component::{ - AccountComponent, - AccountComponentCode, - // TODO(named_slots): Uncomment when refactored. - // AccountComponentMetadata, - // FeltRepresentation, - // InitStorageData, - // MapEntry, - // MapRepresentation, - // PlaceholderTypeRequirement, - // StorageEntry, - // StorageValueName, - // StorageValueNameError, - // TemplateType, - // TemplateTypeError, - // WordRepresentation, -}; +pub use component::{AccountComponent, AccountComponentCode, AccountComponentMetadata}; pub mod delta; pub use delta::{ diff --git a/crates/miden-objects/src/asset/nonfungible.rs b/crates/miden-objects/src/asset/nonfungible.rs index b0fa5f5ddd..d48b26602b 100644 --- a/crates/miden-objects/src/asset/nonfungible.rs +++ b/crates/miden-objects/src/asset/nonfungible.rs @@ -83,7 +83,7 @@ impl NonFungibleAsset { /// Creates a new [NonFungibleAsset] without checking its validity. /// /// # Safety - /// This function required that the provided value is a valid word representation of a + /// This function requires that the provided value is a valid word encoding of a /// [NonFungibleAsset]. pub unsafe fn new_unchecked(value: Word) -> NonFungibleAsset { NonFungibleAsset(value) diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index bbe4eba16e..985335c7d6 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -17,6 +17,7 @@ use super::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; use super::crypto::merkle::MerkleError; use super::note::NoteId; use super::{MAX_BATCHES_PER_BLOCK, MAX_OUTPUT_NOTES_PER_BATCH, Word}; +use crate::account::component::{SchemaTypeError, StorageValueName, StorageValueNameError}; use crate::account::{ AccountCode, AccountIdPrefix, @@ -46,35 +47,32 @@ use crate::{ // ACCOUNT COMPONENT TEMPLATE ERROR // ================================================================================================ -/* #[derive(Debug, Error)] pub enum AccountComponentTemplateError { #[error("storage slot name `{0}` is duplicate")] - DuplicateEntryNames(StorageValueName), - #[error("storage placeholder name `{0}` is duplicate")] - DuplicatePlaceholderName(StorageValueName), - #[error("slot {0} is defined multiple times")] - DuplicateSlot(u8), + DuplicateSlotName(StorageSlotName), + #[error("storage init value name `{0}` is duplicate")] + DuplicateInitValueName(StorageValueName), #[error("storage value name is incorrect: {0}")] IncorrectStorageValueName(#[source] StorageValueNameError), + #[error("invalid storage schema: {0}")] + InvalidSchema(String), #[error("type `{0}` is not valid for `{1}` slots")] InvalidType(String, String), #[error("error deserializing component metadata: {0}")] MetadataDeserializationError(String), - #[error("multi-slot entry should contain as many values as storage slot indices")] - MultiSlotArityMismatch, - #[error("multi-slot entry slot range should occupy more than one storage slot")] - MultiSlotSpansOneSlot, - #[error("component storage slots are not contiguous ({0} is followed by {1})")] - NonContiguousSlots(u8, u8), - #[error("storage value for placeholder `{0}` was not provided in the init storage data")] - PlaceholderValueNotProvided(StorageValueName), - #[error("error converting value into expected type: ")] - StorageValueParsingError(#[source] TemplateTypeError), + #[error("init storage value `{0}` was not provided")] + InitValueNotProvided(StorageValueName), + #[error("invalid init storage value for `{0}`: {1}")] + InvalidInitStorageValue(StorageValueName, String), + #[error( + "account component storage schema cannot contain a slot with name `{0}` as it is reserved by the protocol" + )] + ReservedSlotName(StorageSlotName), + #[error("error converting value into expected type: {0}")] + StorageValueParsingError(#[source] SchemaTypeError), #[error("storage map contains duplicate keys")] StorageMapHasDuplicateKeys(#[source] Box), - #[error("component storage slots have to start at 0, but they start at {0}")] - StorageSlotsDoNotStartAtZero(u8), #[cfg(feature = "std")] #[error("error trying to deserialize from toml")] TomlDeserializationError(#[source] toml::de::Error), @@ -82,7 +80,6 @@ pub enum AccountComponentTemplateError { #[error("error trying to deserialize from toml")] TomlSerializationError(#[source] toml::ser::Error), } -*/ // ACCOUNT ERROR // ================================================================================================ diff --git a/docs/src/account/components.md b/docs/src/account/components.md index b304910423..e7963d5f91 100644 --- a/docs/src/account/components.md +++ b/docs/src/account/components.md @@ -9,25 +9,28 @@ Account components are reusable units of functionality that define a part of an As an example, consider a typical wallet account, capable of holding a user's assets and requiring authentication whenever assets are added or removed. Such an account can be created by merging a `BasicWallet` component with an `RpoFalcon512` authentication component. The basic wallet does not need any storage, but contains the code to move assets in and out of the account vault. The authentication component holds a user's public key in storage and additionally contains the code to verify a signature against that public key. Together, these components form a fully functional wallet account. -## Account Component templates +## Account Component schemas -An account component template is a description of an account component and contains all the -information needed to initialize it. +An account component schema describes a reusable piece of account functionality and captures +everything required to initialize it. The schema encapsulates the component's **metadata**, its +code, and how its storage should be laid out and typed. -Specifically, a template specifies a component's **metadata** and its **code**. - -Once defined, a component template can be instantiated to an account component, which can then be +Once defined, a component schema can be instantiated to an account component. Multiple components can then be merged to form the account's `Code` and `Storage`. ## Component code -The template's code defines a library of functions that can read and write to the storage slots of the component. +The component's code defines a library of functions that can perform arbitrary computations, as well as read and write to account storage. ## Component metadata The component metadata describes the account component entirely: its name, description, version, and storage layout. -The storage layout must specify a contiguous list of slot values that starts at index `0`, and can optionally specify initial values for each of the slots. Alternatively, placeholders can be utilized to identify values that should be provided at the moment of instantiation. +The storage layout is described as a set of named storage slots. Each slot name must be a valid `StorageSlotName`, and its slot ID is +derived deterministically from the name. + +Each slot has a type and an optional default value. If there is no default value defined as part of the schema, the value needs to be +provided at instantiation time. Default values can also be overridden ### TOML specification @@ -35,154 +38,172 @@ The component metadata can be defined using TOML. Below is an example specificat ```toml name = "Fungible Faucet" -description = "This component showcases the component template format, and the different ways of -providing valid values to it." +description = "This component showcases the component schema format, and the different ways of providing valid values to it." version = "1.0.0" supported-types = ["FungibleFaucet"] -[[storage]] -name = "token_metadata" -description = "Contains metadata about the token associated to the faucet account. The metadata -is formed by three fields: max supply, the token symbol and the asset's decimals" -slot = 0 -value = [ - { type = "felt", name = "max_supply", description = "Maximum supply of the token in base units" }, - { type = "token_symbol", value = "TST" }, - { type = "u8", name = "decimals", description = "Number of decimal places for converting to absolute units", value = "10" }, - { value = "0x0" } +[[storage.slots]] +name = "demo::token_metadata" +description = "Contains token metadata (max supply, symbol, decimals)." +type = [ + { type = "u32", name = "max_supply", description = "Maximum supply of the token in base units" }, + { type = "miden::standards::fungible_faucets::metadata::token_symbol", name = "symbol", description = "Token symbol", default-value = "TST" }, + { type = "u8", name = "decimals", description = "Number of decimal places for converting to absolute units" }, + { type = "void" } ] -[[storage]] -name = "owner_public_key" -description = "This is a value placeholder that will be interpreted as a Falcon public key" -slot = 1 -type = "auth::rpo_falcon512::pub_key" - -[[storage]] -name = "map_storage_entry" -slot = 2 -values = [ - { key = "0x1", value = ["0x0", "249381274", "998123581", "124991023478"] }, - { - key = "0xDE0B1140012A9FD912F18AD9EC85E40F4CB697AE", - value = { - name = "value_placeholder", - description = "This value will be defined at the moment of instantiation" - } - } +[[storage.slots]] +name = "demo::owner_public_key" +description = "This is a typed value supplied at instantiation and interpreted as a Falcon public key" +type = "miden::standards::auth::rpo_falcon512::pub_key" + +[[storage.slots]] +name = "demo::protocol_version" +description = "A whole-word init-supplied value typed as a felt (stored as [0,0,0,])." +type = "u8" + +[[storage.slots]] +name = "demo::static_map" +description = "A map slot with statically defined entries" +type = { key = "word", value = "word" } +default-values = [ + { key = "0x0000000000000000000000000000000000000000000000000000000000000001", value = ["0x0", "249381274", "998123581", "124991023478"] }, + { key = ["0", "0", "0", "2"], value = "0x0000000000000000000000000000000000000000000000000000000000000010" } ] -[[storage]] -name = "procedure_thresholds" +[[storage.slots]] +name = "demo::procedure_thresholds" description = "Map which stores procedure thresholds (PROC_ROOT -> signature threshold)" -slot = 3 -type = "map" - -[[storage]] -name = "multislot_entry" -slots = [4,5] -values = [ - ["0x1","0x2","0x3","0x4"], - ["50000","60000","70000","80000"] +type = { key = "word", value = "u16" } +``` + +#### Header + +The metadata header specifies four fields: + +- `name`: The component schema's name +- `description` (optional): A brief description of the component schema and its functionality +- `version`: A semantic version of this component schema +- `supported-types`: Specifies the types of accounts on which the component can be used. Valid values are `FungibleFaucet`, `NonFungibleFaucet`, `RegularAccountUpdatableCode` and `RegularAccountImmutableCode` + +##### Word schema example + +```toml +[[storage.slots]] +name = "demo::faucet_id" +description = "Account ID of the registered faucet" +type = [ + { type = "felt", name = "prefix", description = "Faucet ID prefix" }, + { type = "felt", name = "suffix", description = "Faucet ID suffix" }, + { type = "void" }, + { type = "void" }, ] ``` -#### Specifying values and their types +#### Storage entries -In the TOML format, any value that is one word long can be written as a single value, or as exactly four field elements. In turn, a field element is a number within Miden's finite field. +An account component schema can contain multiple storage entries, each describing either a +**single-slot value** or a **storage map**. -A word can be written as a hexadecimal value, and field elements can be written either as hexadecimal or decimal numbers. In all cases, numbers should be input as strings. +In TOML, these are declared using dotted array keys: -In our example, the `token_metadata` single-slot entry is defined as four elements, where the first element is a placeholder, and the second, third and fourth are hardcoded values. +- **Value slots**: `[[storage.slots]]` with `type = "..."` or `type = [ ... ]` +- **Map slots**: `[[storage.slots]]` with `type = { ... }` -##### Word types +**Value-slot** entries describe their schema via `WordSchema`. A value type can be either: -Valid word types are `word` (default type) and `auth::rpo_falcon512::pub_key` (represents a Falcon public key). Both can be written and interpreted as hexadecimal strings. +- **Simple**: defined through the `type = ""` field, indicating the expected `SchemaTypeId` for the entire word. The value is supplied at instantiation time via `InitStorageData`. +- **Composite**: provided through `type = [ ... ]`, which contains exactly four `FeltSchema` descriptors. Each element is either a named typed field (optionally with `default-value`) or a `void` element for reserved/padding zeros. -##### Felt types +Composite schema entries reuse the existing TOML structure for four-element words, while simple schemas rely on `type`. In our example, the `token_metadata` slot uses a composite schema (`type = [...]`) mixing typed fields (`max_supply`, `decimals`) with defaults (`symbol`) and a reserved/padding `void` element. -Valid field element types are `u8`, `u16`, `u32`, `felt` (default type) and `token_symbol`: +Every entry carries: -- `u8`, `u16` and `u32` values can be parsed as decimal numbers and represent 8-bit, 16-bit and 32-bit unsigned integers -- `felt` values represent a field element, and can be parsed as decimal or hexadecimal values -- `token_symbol` values represent the symbol for basic fungible tokens, and are parsed as strings made of four uppercase characters +- `name`: Identifies the storage entry. +- `description` (optional): Explains the entry's purpose within the component. -#### Header +The remaining fields depend on whether the entry is a value slot or a map slot, as inferred by the +shape of the `type` field. -The metadata header specifies four fields: +##### Word types -- `name`: The component template's name -- `description` (optional): A brief description of the component template and its functionality -- `version`: A semantic version of this component template -- `supported-types`: Specifies the types of accounts on which the component can be used. Valid values are `FungibleFaucet`, `NonFungibleFaucet`, `RegularAccountUpdatableCode` and `RegularAccountImmutableCode` +Simple schemas accept `word` (default) and word-shaped types such as `miden::standards::auth::rpo_falcon512::pub_key` or `miden::standards::auth::ecdsa_k256_keccak::pub_key` (parsed from hexadecimal strings). -#### Storage entries +Simple schemas can also use any felt type (e.g. `u8`, `u16`, `u32`, `felt`, `miden::standards::fungible_faucets::metadata::token_symbol`, `void`). The value is parsed as a felt and stored as a word with the parsed felt in the last element and the remaining elements set to `0`. -An account component template can have multiple storage entries. A storage entry can specify either a **single-slot value**, a **multi-slot value**, or a **storage map**. -Each of these storage entries contain the following fields: +##### Felt types -- `name`: A name for identifying the storage entry -- `description` (optional): Describes the intended function of the storage slot within the component definition +Valid field element types are `void`, `u8`, `u16`, `u32`, `felt` (default) and `miden::standards::fungible_faucets::metadata::token_symbol`: -Additionally, based on the type of the storage entry, there are specific fields that should be specified. +- `void` is a special type which always evaluates to `0` and does not produce an init requirement; it is intended for reserved or padding elements. +- `u8`, `u16` and `u32` values can be parsed as decimal numbers and represent 8-bit, 16-bit and 32-bit unsigned integers. +- `felt` values represent a field element, and can be parsed as decimal or hexadecimal numbers. +- `miden::standards::fungible_faucets::metadata::token_symbol` values represent basic fungible token symbols, parsed as 1–6 uppercase ASCII characters. -##### Single-slot value +##### Value slots -A single-slot value fits within one slot (i.e., one word). +Single-slot entries are represented by `ValueSlotSchema` and occupy one slot (one word). They use the fields: -For a single-slot entry, the following fields are expected: +- `type` (required): Describes the schema for this slot. It can be either: + - a string type identifier (simple init-supplied slot), or + - an array of 4 felt schema descriptors (composite slot schema). +- `default-value` (optional): An overridable default for simple slots. If omitted, the slot is required at instantiation (unless `type = "void"`). -- `slot`: Specifies the slot index in which the value will be placed -- `value` (optional): Contains the initial storage value for this slot. Will be interpreted as a `word` unless another `type` is specified -- `type` (optional): Describes the expected type for the slot +In our TOML example, the first entry defines a composite schema, while the second is an init-supplied value typed as `miden::standards::auth::rpo_falcon512::pub_key`. -If no `value` is provided, the entry acts as a placeholder, requiring a value to be passed at instantiation. In this case, specifying a `type` is mandatory to ensure the input is correctly parsed. So the rule is that at least one of `value` and `type` has to be specified. -Valid types for a single-slot value are `word` or `auth::rpo_falcon512::pub_key`. +##### Storage map slots -In the above example, the first and second storage entries are single-slot values. +[Storage maps](./storage#map-slots) use `MapSlotSchema` and describe key-value pairs where each key and value is itself a `WordSchema`. Map slots support: -##### Storage map entries +- `type` (required): Declares the slot as a map via a map type table (`type = { ... }`), with: + - `type.key` (required): Declares the schema/type of keys stored in the map. + - `type.value` (required): Declares the schema/type of values stored in the map. +- `default-values` (optional): Lists default map entries defined by nested `key` and `value` descriptors. Each entry must be fully specified and cannot contain typed fields. -[Storage maps](./storage#map-slots) consist of key-value pairs, where both keys and values are single words. +`type.key` / `type.value` accept either a string type identifier (e.g. `"word"`) or a 4-element array of felt schema descriptors. -Storage map entries can specify the following fields: +If `default-values` is omitted, the map is populated at instantiation via [`InitStorageData`](#providing-init-values). When `default-values` are present, they act as defaults: init data can override existing values and optionally add new key-value pairs. -- `slot`: Specifies the slot index in which the root of the map will be placed -- `values` (optional): Contains a list of map entries, defined by a `key` and `value`. Each entry is - interpreted as a word, and keys or values may themselves be expressed via placeholders. -- `type = "map"` (optional): When provided without `values`, the entry is treated as a templated map - whose contents must be provided at instantiation time through [`InitStorageData`](#initializing-placeholder-values). - If `values` are present, the entry is interpreted as a static map regardless of the `type` field, so - specifying `type = "map"` becomes purely descriptive in that case. +In the example, the third storage entry defines a static map and the fourth entry (`procedure_thresholds`) is populated at instantiation. -In the example, the third storage entry defines a static storage map with two initial entries, while -the fourth entry (`procedure_thresholds`) is a templated map whose contents are supplied at -instantiation time. +##### Typed map -##### Multi-slot value +You can type maps at the slot level via `type.key` and `type.value` (each a `WordSchema`): -Multi-slot values are composite values that exceed the size of a single slot (i.e., more than one `word`). +```toml +[[storage.slots]] +name = "demo::typed_map" +type = { key = "word", value = "miden::standards::auth::rpo_falcon512::pub_key" } +``` -For multi-slot values, the following fields are expected: +This declares that all keys are `word` and all values are `miden::standards::auth::rpo_falcon512::pub_key`, regardless of whether the map contents come from `default-values = [...]` (static) or are supplied at instantiation via `InitStorageData`. -- `slots`: Specifies the list of contiguous slots that the value comprises -- `values`: Contains the initial storage value for the specified slots +`type.key` / `type.value` are validated when building map entries from `InitStorageData` (and when validating `default-values`). + +##### Multi-slot value -Placeholders can currently not be defined for multi-slot values. In our example, the fifth entry defines a two-slot value. +Multi-slot values are currently unsupported by component schemas. -#### Initializing placeholder values +#### Providing init values -When a storage entry introduces placeholders, an implementation must provide their concrete values -at instantiation time. This is done through `InitStorageData` (available as `miden_objects::account::InitStorageData`), which can be created programmatically or loaded from TOML using `InitStorageData::from_toml()`. +When a storage entry requires init-supplied values, an implementation must provide their concrete values +at instantiation time. This is done through `InitStorageData` (available as `miden_objects::account::component::InitStorageData`), which can be created programmatically or loaded from TOML using `InitStorageData::from_toml()`. -For example, the templated map entry above can be populated from TOML as follows: +For example, the init-populated map entry above can be populated from TOML as follows: ```toml -procedure_thresholds = [ +"demo::owner_public_key" = "0x1234" +"demo::protocol_version" = "1" + +["demo::token_metadata"] +max_supply = "1000000000" +decimals = "10" + +"demo::procedure_thresholds" = [ { key = "0xd2d1b6229d7cfb9f2ada31c5cb61453cf464f91828e124437c708eec55b9cd07", - value = "0x00000000000000000000000000000000000000000000000000000000000001" + value = ["0", "0", "0", "1"] }, { key = "0x2217cd9963f742fc2d131d86df08f8a2766ed17b73f1519b8d3143ad1c71d32d", @@ -191,4 +212,6 @@ procedure_thresholds = [ ] ``` -Each element in the array is a fully specified key/value pair. Keys and values can be written either as hexadecimal words or as an array of four field elements (decimal or hexadecimal strings). This syntax complements the existing `values = [...]` form used for static maps, and mirrors how map entries are provided in component metadata. +All init values must be provided as TOML strings (including numeric values), and are parsed/validated against the schema at instantiation time. + +Each element in the array is a fully specified key/value pair. Note that slot names include `::`, so they must be quoted in TOML. This syntax complements the existing `default-values = [...]` form used for static maps, and mirrors how map entries are provided in component metadata. If an init-populated map slot is omitted from `InitStorageData`, it defaults to an empty map. From b9aea905857ffcdb8db721733da92ec5720d0fe8 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Sat, 20 Dec 2025 10:51:10 +0300 Subject: [PATCH 071/114] chore: migrate to the VM v0.20.0 (#2158) --- CHANGELOG.md | 1 + Cargo.lock | 315 ++++++------ Cargo.toml | 22 +- bin/bench-transaction/src/context_setups.rs | 4 +- crates/miden-lib/Cargo.toml | 11 +- .../basic_fungible_faucet.masm | 4 +- .../asm/account_components/basic_wallet.masm | 4 +- .../ecdsa_k256_keccak_acl.masm | 5 +- ...k.masm => ecdsa_k256_keccak_multisig.masm} | 9 +- .../network_fungible_faucet.masm | 4 +- .../asm/account_components/no_auth.masm | 2 +- .../rpo_falcon_512_acl.masm | 5 +- ..._512.masm => rpo_falcon_512_multisig.masm} | 9 +- .../asm/kernels/transaction/api.masm | 150 +++--- .../asm/kernels/transaction/lib/account.masm | 249 +++++----- .../transaction/lib/account_delta.masm | 66 +-- .../asm/kernels/transaction/lib/asset.masm | 35 +- .../kernels/transaction/lib/asset_vault.masm | 57 +-- .../kernels/transaction/lib/constants.masm | 42 +- .../asm/kernels/transaction/lib/epilogue.masm | 51 +- .../asm/kernels/transaction/lib/faucet.masm | 36 +- .../kernels/transaction/lib/input_note.masm | 10 +- .../asm/kernels/transaction/lib/link_map.masm | 106 ++-- .../asm/kernels/transaction/lib/memory.masm | 463 +++++++++--------- .../asm/kernels/transaction/lib/note.masm | 28 +- .../kernels/transaction/lib/output_note.masm | 60 +-- .../asm/kernels/transaction/lib/prologue.masm | 117 ++--- .../asm/kernels/transaction/lib/tx.masm | 28 +- .../asm/kernels/transaction/main.masm | 33 +- .../kernels/transaction/tx_script_main.masm | 10 +- .../miden-lib/asm/miden/active_account.masm | 40 +- crates/miden-lib/asm/miden/active_note.masm | 31 +- crates/miden-lib/asm/miden/asset.masm | 14 +- .../asm/miden/auth/ecdsa_k256_keccak.masm | 31 +- crates/miden-lib/asm/miden/auth/mod.masm | 15 +- .../asm/miden/auth/rpo_falcon512.masm | 31 +- .../contracts/faucets/basic_fungible.masm | 12 +- .../asm/miden/contracts/faucets/mod.masm | 20 +- .../contracts/faucets/network_fungible.masm | 20 +- .../asm/miden/contracts/wallets/basic.masm | 10 +- crates/miden-lib/asm/miden/faucet.masm | 18 +- crates/miden-lib/asm/miden/input_note.masm | 20 +- .../asm/miden/kernel_proc_offsets.masm | 208 ++++---- .../miden-lib/asm/miden/native_account.masm | 18 +- crates/miden-lib/asm/miden/note.masm | 38 +- crates/miden-lib/asm/miden/output_note.masm | 16 +- crates/miden-lib/asm/miden/tx.masm | 23 +- crates/miden-lib/asm/note_scripts/BURN.masm | 2 +- crates/miden-lib/asm/note_scripts/MINT.masm | 20 +- crates/miden-lib/asm/note_scripts/P2ID.masm | 10 +- crates/miden-lib/asm/note_scripts/P2IDE.masm | 22 +- crates/miden-lib/asm/note_scripts/SWAP.masm | 12 +- .../asm/shared_modules/account_id.masm | 48 +- .../asm/shared_utils/util/asset.masm | 6 +- .../miden-lib/asm/shared_utils/util/note.masm | 4 +- crates/miden-lib/build.rs | 85 ++-- .../src/account/auth/ecdsa_k256_keccak.rs | 12 +- .../src/account/auth/rpo_falcon_512.rs | 14 +- .../miden-lib/src/account/components/mod.rs | 23 +- .../src/account/faucets/basic_fungible.rs | 4 +- .../src/account/faucets/network_fungible.rs | 4 +- crates/miden-lib/src/account/interface/mod.rs | 3 +- .../miden-lib/src/account/interface/test.rs | 52 +- crates/miden-lib/src/account/mod.rs | 8 +- crates/miden-lib/src/account/wallets/mod.rs | 4 +- .../src/errors/note_script_errors.rs | 2 +- .../miden-lib/src/errors/tx_kernel_errors.rs | 2 +- crates/miden-lib/src/lib.rs | 8 +- .../account_component/conditional_auth.rs | 6 +- .../testing/account_component/incr_nonce.rs | 4 +- .../src/testing/mock_account_code.rs | 40 +- crates/miden-lib/src/testing/mock_util_lib.rs | 8 +- crates/miden-lib/src/transaction/inputs.rs | 3 +- .../src/transaction/kernel_procedures.rs | 34 +- crates/miden-lib/src/transaction/mod.rs | 8 +- crates/miden-lib/src/utils/code_builder.rs | 115 ++--- crates/miden-objects/Cargo.toml | 2 +- crates/miden-objects/src/account/auth.rs | 47 +- .../miden-objects/src/account/builder/mod.rs | 8 +- crates/miden-objects/src/account/code/mod.rs | 6 +- .../src/account/component/mod.rs | 24 +- .../account/component/storage/toml/tests.rs | 10 +- .../component/storage/type_registry.rs | 10 +- crates/miden-objects/src/account/file.rs | 4 +- crates/miden-objects/src/account/mod.rs | 2 +- .../src/account/storage/map/mod.rs | 3 +- .../src/account/storage/map/partial.rs | 11 +- .../src/account/storage/map/witness.rs | 9 +- .../src/account/storage/partial.rs | 3 +- crates/miden-objects/src/address/mod.rs | 4 +- .../src/address/routing_parameters.rs | 10 +- .../src/asset/vault/asset_witness.rs | 5 +- crates/miden-objects/src/asset/vault/mod.rs | 2 +- .../miden-objects/src/asset/vault/partial.rs | 5 +- .../src/asset/vault/vault_key.rs | 2 +- crates/miden-objects/src/batch/note_tree.rs | 3 +- .../miden-objects/src/batch/proposed_batch.rs | 2 +- .../src/block/account_tree/backend.rs | 15 +- .../src/block/account_tree/mod.rs | 11 +- .../src/block/account_tree/partial.rs | 5 +- .../src/block/account_tree/witness.rs | 11 +- crates/miden-objects/src/block/blockchain.rs | 2 +- crates/miden-objects/src/block/note_tree.rs | 5 +- .../src/block/nullifier_tree/backend.rs | 10 +- .../src/block/nullifier_tree/mod.rs | 11 +- .../src/block/nullifier_tree/partial.rs | 4 +- .../src/block/nullifier_tree/witness.rs | 2 +- crates/miden-objects/src/errors.rs | 2 +- crates/miden-objects/src/lib.rs | 4 +- .../miden-objects/src/testing/account_code.rs | 4 +- .../src/testing/add_component.rs | 2 +- crates/miden-objects/src/testing/block.rs | 2 +- .../src/testing/noop_auth_component.rs | 2 +- .../src/transaction/inputs/account.rs | 2 +- .../src/transaction/partial_blockchain.rs | 5 +- .../block/proven_block_success.rs | 2 +- .../src/kernel_tests/block/utils.rs | 2 +- .../miden-testing/src/kernel_tests/tx/mod.rs | 2 +- .../src/kernel_tests/tx/test_account.rs | 140 +++--- .../src/kernel_tests/tx/test_account_delta.rs | 28 +- .../src/kernel_tests/tx/test_active_note.rs | 44 +- .../src/kernel_tests/tx/test_asset.rs | 10 +- .../src/kernel_tests/tx/test_asset_vault.rs | 55 +-- .../src/kernel_tests/tx/test_epilogue.rs | 56 +-- .../src/kernel_tests/tx/test_faucet.rs | 92 ++-- .../src/kernel_tests/tx/test_fpi.rs | 160 +++--- .../src/kernel_tests/tx/test_input_note.rs | 14 +- .../src/kernel_tests/tx/test_lazy_loading.rs | 16 +- .../src/kernel_tests/tx/test_link_map.rs | 6 +- .../src/kernel_tests/tx/test_note.rs | 32 +- .../src/kernel_tests/tx/test_output_note.rs | 54 +- .../src/kernel_tests/tx/test_prologue.rs | 12 +- .../src/kernel_tests/tx/test_tx.rs | 40 +- crates/miden-testing/src/mock_chain/auth.rs | 4 +- .../src/mock_chain/chain_builder.rs | 2 +- crates/miden-testing/src/mock_host.rs | 8 +- .../miden-testing/src/tx_context/builder.rs | 4 +- .../miden-testing/src/tx_context/context.rs | 1 - crates/miden-testing/src/utils.rs | 11 +- crates/miden-testing/tests/auth/ecdsa_acl.rs | 14 +- .../tests/auth/ecdsa_multisig.rs | 15 +- crates/miden-testing/tests/auth/multisig.rs | 17 +- .../tests/auth/rpo_falcon_acl.rs | 14 +- crates/miden-testing/tests/scripts/faucet.rs | 2 +- crates/miden-testing/tests/scripts/p2id.rs | 2 +- .../miden-testing/tests/scripts/send_note.rs | 14 +- crates/miden-testing/tests/scripts/swap.rs | 2 +- crates/miden-testing/tests/wallet/mod.rs | 2 +- crates/miden-tx/src/auth/tx_authenticator.rs | 2 +- crates/miden-tx/src/errors/mod.rs | 2 +- crates/miden-tx/src/executor/exec_host.rs | 12 +- crates/miden-tx/src/host/mod.rs | 20 +- crates/miden-tx/src/prover/mast_store.rs | 10 +- crates/miden-tx/src/prover/prover_host.rs | 2 +- crates/miden-tx/src/verifier/mod.rs | 4 +- deny.toml | 3 + deny.toml~ | 69 --- 157 files changed, 2124 insertions(+), 2212 deletions(-) rename crates/miden-lib/asm/account_components/{multisig_ecdsa_k256_keccak.masm => ecdsa_k256_keccak_multisig.masm} (98%) rename crates/miden-lib/asm/account_components/{multisig_rpo_falcon_512.masm => rpo_falcon_512_multisig.masm} (98%) delete mode 100644 deny.toml~ diff --git a/CHANGELOG.md b/CHANGELOG.md index 39b7695bf5..47e7c209d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). +- [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). diff --git a/Cargo.lock b/Cargo.lock index 52914dd77c..5f77a0ea10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,7 +175,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -216,15 +216,15 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bech32" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" [[package]] name = "bench-note-checker" @@ -331,9 +331,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.46" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -411,18 +411,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstyle", "clap_lex", @@ -626,7 +626,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -650,22 +650,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ "proc-macro2", "quote", - "syn", + "rustc_version 0.4.1", + "syn 2.0.111", ] [[package]] @@ -759,18 +760,6 @@ dependencies = [ "log", ] -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "env_filter" version = "0.1.4" @@ -811,7 +800,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -957,7 +946,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1081,15 +1070,16 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", "foldhash", "rayon", "serde", + "serde_core", ] [[package]] @@ -1124,9 +1114,9 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", @@ -1236,7 +1226,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1251,9 +1241,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -1320,9 +1310,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libm" @@ -1353,9 +1343,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loom" @@ -1367,7 +1357,7 @@ dependencies = [ "generator", "scoped-tls", "tracing", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", ] [[package]] @@ -1396,9 +1386,9 @@ dependencies = [ [[package]] name = "miden-air" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06acfd2ddc25b68f9d23d2add3f15c0ec3f9890ce6418409d71bea9dc6590bd0" +checksum = "e663337017ed028dff8c18a0ce1db64aad0e850996e3214f137f98317533c2e1" dependencies = [ "miden-core", "miden-utils-indexing", @@ -1409,10 +1399,11 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1219b9e48bb286b58a23bb65cf74baa1b24ddbcb462ca625b38186674571047" +checksum = "001249195c227624695529c82ebf51c390ec1c28e99a567549ce3a272a2aedf3" dependencies = [ + "env_logger", "log", "miden-assembly-syntax", "miden-core", @@ -1423,11 +1414,12 @@ dependencies = [ [[package]] name = "miden-assembly-syntax" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eeaef2853061c54527bb2664c0c832ce3d1f80847c79512455fec3b93057f2a" +checksum = "1963cfa667aa6a157c99982df340a7bd42b054652e6f33d5e3513217531eca73" dependencies = [ "aho-corasick", + "env_logger", "lalrpop", "lalrpop-util", "log", @@ -1436,6 +1428,7 @@ dependencies = [ "miden-utils-diagnostics", "midenc-hir-type", "proptest", + "proptest-derive", "regex", "rustc_version 0.4.1", "semver 1.0.27", @@ -1453,31 +1446,53 @@ dependencies = [ [[package]] name = "miden-core" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452a00429d05c416001ec0578291eb88e115cf94fc22b3308267abfdcd813440" +checksum = "136debf5474190dc584df3252710dac07a0e45315740c9538a7fc0b72c596365" dependencies = [ - "enum_dispatch", + "derive_more", + "itertools 0.14.0", "miden-crypto", "miden-debug-types", "miden-formatting", + "miden-utils-core-derive", "miden-utils-indexing", "num-derive", "num-traits", + "proptest", + "proptest-derive", "thiserror", "winter-math", "winter-utils", ] +[[package]] +name = "miden-core-lib" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcec9fb9a256d2fae347162d9a94653a1790dd33b4af73ad29686475b63deb34" +dependencies = [ + "env_logger", + "fs-err", + "miden-assembly", + "miden-core", + "miden-crypto", + "miden-processor", + "miden-utils-sync", + "sha2", + "thiserror", +] + [[package]] name = "miden-crypto" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395e5cc76b64e24533ee55c8d1ff90305b8cad372bdbea4f4f324239e36a895f" +checksum = "dc7981c1d907bb9864e24f2bd6304c4fca03a41fc4606c09edd6a7f5a8fc80fc" dependencies = [ "blake3", "cc", "chacha20poly1305", + "curve25519-dalek", "ed25519-dalek", "flume", "glob", @@ -1492,6 +1507,7 @@ dependencies = [ "rand_core 0.9.3", "rand_hc", "rayon", + "sha2", "sha3", "subtle", "thiserror", @@ -1503,19 +1519,19 @@ dependencies = [ [[package]] name = "miden-crypto-derive" -version = "0.18.2" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2222f37355ea975f40acd3c098a437574a31a4d8a2c193cf4e9fead2beede577" +checksum = "83479e7af490784c6f2d2e02cec5210fd6e5bc6ce3d4427734e36a773bca72d2" dependencies = [ "quote", - "syn", + "syn 2.0.111", ] [[package]] name = "miden-debug-types" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97eed62ac0ca7420e49148fd306c74786b23a8d31df6da6277c671ba3e5c619a" +checksum = "6dc25083822c3d582c42ad10aeee0138dec15a130f3017b05495bb91e31fde4a" dependencies = [ "memchr", "miden-crypto", @@ -1547,10 +1563,10 @@ dependencies = [ "fs-err", "miden-assembly", "miden-core", + "miden-core-lib", "miden-lib", "miden-objects", "miden-processor", - "miden-stdlib", "rand", "regex", "thiserror", @@ -1559,9 +1575,9 @@ dependencies = [ [[package]] name = "miden-mast-package" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d13e6ba2b357551598f13396ed52f8f21aa99979aa3b338bb5521feeda19c8a" +checksum = "da35f2fc1eacbfd0b6b995e888c2b778bd646acebf34dab27f9f7ed9b3effaa2" dependencies = [ "derive_more", "miden-assembly-syntax", @@ -1592,7 +1608,7 @@ dependencies = [ "supports-color", "supports-hyperlinks", "supports-unicode", - "syn", + "syn 2.0.111", "terminal_size", "textwrap", "thiserror", @@ -1608,7 +1624,7 @@ checksum = "86a905f3ea65634dd4d1041a4f0fd0a3e77aa4118341d265af1a94339182222f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1625,12 +1641,12 @@ dependencies = [ "miden-assembly", "miden-assembly-syntax", "miden-core", + "miden-core-lib", "miden-crypto", "miden-mast-package", "miden-objects", "miden-processor", "miden-protocol-macros", - "miden-stdlib", "miden-utils-sync", "miden-verifier", "pprof", @@ -1649,9 +1665,9 @@ dependencies = [ [[package]] name = "miden-processor" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ef77929651b8755965cde8f589bd38e2345a619d54cab6427f91aa23c47f6a" +checksum = "2eb298dbdda739080497c18eace4d56c58f3e8d257676c9b2f407be441131ecd" dependencies = [ "itertools 0.14.0", "miden-air", @@ -1674,14 +1690,14 @@ dependencies = [ "miden-objects", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] name = "miden-prover" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c30a5d10baeec17b9336de8544cb7f9b96b32de757c4cfb8d95ee0521bb5cd" +checksum = "8506c8eb4d980134c0145887af50bd4631df4010eb23d6e454764cb1ee28836c" dependencies = [ "miden-air", "miden-debug-types", @@ -1691,22 +1707,6 @@ dependencies = [ "winter-prover", ] -[[package]] -name = "miden-stdlib" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e90a5de45a1e6213ff17b66fff8accde0bbc64264e2c22bbcb9a895f8f3b767" -dependencies = [ - "env_logger", - "fs-err", - "miden-assembly", - "miden-core", - "miden-crypto", - "miden-processor", - "miden-utils-sync", - "thiserror", -] - [[package]] name = "miden-testing" version = "0.13.0" @@ -1753,11 +1753,22 @@ dependencies = [ "miden-tx", ] +[[package]] +name = "miden-utils-core-derive" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0807840c07a4491a292153258cfae27914333e1a7240777a77c22d8ca3b55873" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "miden-utils-diagnostics" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3ff4c019d96539a7066626efb4dce5c9fb7b0e44e961b0c2571e78f34236d5" +checksum = "1b28b1b29e300b471b0f1cbc286997a1326c900814a73b0b28338d5926ce192c" dependencies = [ "miden-crypto", "miden-debug-types", @@ -1768,18 +1779,18 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c798250bee4e856d4f18c161e91cdcbef1906f6614d00cf0063b47031c0f8cc6" +checksum = "f8bd0c1966de07d48a4ed0b2821466919c061f4866296be87afc56970a49716a" dependencies = [ "thiserror", ] [[package]] name = "miden-utils-sync" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feebe7d896c013ea74dbc98de978836606356a044d4ed3b61ded54d3b319d89f" +checksum = "a1fa7e37db2fbf2dee6ba6e411b3570ef48d52ec780b9c8125623f9ddca30da3" dependencies = [ "lock_api", "loom", @@ -1788,9 +1799,9 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f8e47b78bba1fe1b31faee8f12aafd95385f6d6a8b108b03e92f5d743bb29f" +checksum = "383c934eed92f89be4c1e3dbc97ccf37b48433a0b33727c92a5abbfa2d45f420" dependencies = [ "miden-air", "miden-core", @@ -1895,7 +1906,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2189,6 +2200,17 @@ dependencies = [ "unarray", ] +[[package]] +name = "proptest-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "quick-xml" version = "0.26.0" @@ -2386,7 +2408,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn", + "syn 2.0.111", "unicode-ident", ] @@ -2539,7 +2561,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2725,9 +2747,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "symbolic-common" -version = "12.16.3" +version = "12.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03f433c9befeea460a01d750e698aa86caf86dcfbd77d552885cd6c89d52f50" +checksum = "b3d8046c5674ab857104bc4559d505f4809b8060d57806e45d49737c97afeb60" dependencies = [ "debugid", "memmap2", @@ -2737,9 +2759,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.16.3" +version = "12.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d359ef6192db1760a34321ec4f089245ede4342c27e59be99642f12a859de8" +checksum = "1accb6e5c4b0f682de907623912e616b44be1c9e725775155546669dbff720ec" dependencies = [ "rustc-demangle", "symbolic-common", @@ -2747,9 +2769,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.110" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -2777,9 +2810,9 @@ dependencies = [ [[package]] name = "term" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2111ef44dae28680ae9752bb89409e7310ca33a8c621ebe7b106cf5c928b3ac0" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ "windows-sys 0.61.2", ] @@ -2831,7 +2864,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2871,7 +2904,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2924,9 +2957,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" dependencies = [ "indexmap", "toml_datetime", @@ -2951,9 +2984,9 @@ checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -2962,20 +2995,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -3015,9 +3048,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -3107,9 +3140,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "js-sys", "wasm-bindgen", @@ -3163,9 +3196,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -3176,9 +3209,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3186,31 +3219,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -3301,7 +3334,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3312,7 +3345,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3514,9 +3547,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -3573,7 +3606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d31a19dae58475d019850e25b0170e94b16d382fbf6afee9c0e80fdc935e73e" dependencies = [ "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3652,22 +3685,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 18222c2b59..05700ab01b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,17 +50,17 @@ miden-tx = { default-features = false, path = "crates/miden-tx", ve miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.13" } # Miden dependencies -miden-air = { default-features = false, version = "0.19" } -miden-assembly = { default-features = false, version = "0.19" } -miden-assembly-syntax = { default-features = false, version = "0.19" } -miden-core = { default-features = false, version = "0.19" } -miden-crypto = { default-features = false, version = "0.18.5" } -miden-mast-package = { default-features = false, version = "0.19" } -miden-processor = { default-features = false, version = "0.19" } -miden-prover = { default-features = false, version = "0.19" } -miden-stdlib = { default-features = false, version = "0.19" } -miden-utils-sync = { default-features = false, version = "0.19" } -miden-verifier = { default-features = false, version = "0.19" } +miden-air = { default-features = false, version = "0.20" } +miden-assembly = { default-features = false, version = "0.20" } +miden-assembly-syntax = { default-features = false, version = "0.20" } +miden-core = { default-features = false, version = "0.20" } +miden-core-lib = { default-features = false, version = "0.20" } +miden-crypto = { default-features = false, version = "0.19" } +miden-mast-package = { default-features = false, version = "0.20" } +miden-processor = { default-features = false, version = "0.20" } +miden-prover = { default-features = false, version = "0.20" } +miden-utils-sync = { default-features = false, version = "0.20" } +miden-verifier = { default-features = false, version = "0.20" } # External dependencies anyhow = { default-features = false, features = ["backtrace", "std"], version = "1.0" } diff --git a/bin/bench-transaction/src/context_setups.rs b/bin/bench-transaction/src/context_setups.rs index f08b376e90..625aab5473 100644 --- a/bin/bench-transaction/src/context_setups.rs +++ b/bin/bench-transaction/src/context_setups.rs @@ -25,8 +25,8 @@ pub fn tx_create_single_p2id_note() -> Result { let tx_note_creation_script = format!( " - use.miden::output_note - use.std::sys + use miden::output_note + use miden::core::sys begin # create an output note with fungible asset diff --git a/crates/miden-lib/Cargo.toml b/crates/miden-lib/Cargo.toml index 325f64deb1..f82bb251aa 100644 --- a/crates/miden-lib/Cargo.toml +++ b/crates/miden-lib/Cargo.toml @@ -15,17 +15,16 @@ version.workspace = true [lib] [features] -default = ["std"] -std = ["miden-assembly/std", "miden-objects/std", "miden-processor/std", "miden-stdlib/std"] -testing = ["dep:rand", "miden-objects/testing"] -with-debug-info = ["miden-stdlib/with-debug-info"] +default = ["std"] +std = ["miden-assembly/std", "miden-core-lib/std", "miden-objects/std", "miden-processor/std"] +testing = ["dep:rand", "miden-assembly/testing", "miden-objects/testing"] [dependencies] # Miden dependencies miden-core = { workspace = true } +miden-core-lib = { workspace = true } miden-objects = { workspace = true } miden-processor = { workspace = true } -miden-stdlib = { workspace = true } # External dependencies rand = { optional = true, workspace = true } @@ -35,7 +34,7 @@ thiserror = { workspace = true } fs-err = { version = "3" } miden-assembly = { workspace = true } miden-core = { workspace = true } -miden-stdlib = { workspace = true } +miden-core-lib = { workspace = true } regex = { version = "1.11" } walkdir = { version = "2.5" } diff --git a/crates/miden-lib/asm/account_components/basic_fungible_faucet.masm b/crates/miden-lib/asm/account_components/basic_fungible_faucet.masm index f436d1b541..17e78f9990 100644 --- a/crates/miden-lib/asm/account_components/basic_fungible_faucet.masm +++ b/crates/miden-lib/asm/account_components/basic_fungible_faucet.masm @@ -2,5 +2,5 @@ # # See the `BasicFungibleFaucet` Rust type's documentation for more details. -pub proc ::miden::contracts::faucets::basic_fungible::distribute -pub proc ::miden::contracts::faucets::basic_fungible::burn +pub use ::miden::contracts::faucets::basic_fungible::distribute +pub use ::miden::contracts::faucets::basic_fungible::burn diff --git a/crates/miden-lib/asm/account_components/basic_wallet.masm b/crates/miden-lib/asm/account_components/basic_wallet.masm index 36fbc7d7b9..0670f44c75 100644 --- a/crates/miden-lib/asm/account_components/basic_wallet.masm +++ b/crates/miden-lib/asm/account_components/basic_wallet.masm @@ -2,5 +2,5 @@ # # See the `BasicWallet` Rust type's documentation for more details. -pub proc ::miden::contracts::wallets::basic::receive_asset -pub proc ::miden::contracts::wallets::basic::move_asset_to_note +pub use ::miden::contracts::wallets::basic::receive_asset +pub use ::miden::contracts::wallets::basic::move_asset_to_note diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm index ade3c9c9f8..79215b9db8 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm +++ b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm @@ -5,7 +5,7 @@ use miden::active_account use miden::native_account use miden::tx -use std::word +use miden::core::word type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -35,7 +35,8 @@ const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::ecdsa_k256_kec #! Outputs: [pad(16)] #! #! Invocation: call -pub proc auth_tx_ecdsa_k256_keccak_acl.2(auth_args: BeWord) +@locals(2) +pub proc auth_tx_ecdsa_k256_keccak_acl(auth_args: BeWord) dropw # => [pad(16)] diff --git a/crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_multisig.masm similarity index 98% rename from crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm rename to crates/miden-lib/asm/account_components/ecdsa_k256_keccak_multisig.masm index abd148ab1e..f1d107edce 100644 --- a/crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm +++ b/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_multisig.masm @@ -151,7 +151,8 @@ end #! Locals: #! 0: new_num_of_approvers #! 1: init_num_of_approvers -pub proc update_signers_and_threshold.2(multisig_config_hash: BeWord) +@locals(2) +pub proc update_signers_and_threshold(multisig_config_hash: BeWord) adv.push_mapval # => [MULTISIG_CONFIG_HASH, pad(12)] @@ -238,7 +239,8 @@ end # #! Inputs: [default_threshold] #! Outputs: [transaction_threshold] -proc compute_transaction_threshold.1(default_threshold: u32) -> u32 +@locals(1) +proc compute_transaction_threshold(default_threshold: u32) -> u32 # 1. initialize transaction_threshold = 0 # 2. iterate through all account procedures # a. check if the procedure was called during the transaction @@ -353,7 +355,8 @@ end #! - the same transaction has already been executed (replay protection). #! #! Invocation: call -pub proc auth_tx_ecdsa_k256_keccak_multisig.1(salt: BeWord) +@locals(1) +pub proc auth_tx_ecdsa_k256_keccak_multisig(salt: BeWord) exec.native_account::incr_nonce drop # => [SALT] diff --git a/crates/miden-lib/asm/account_components/network_fungible_faucet.masm b/crates/miden-lib/asm/account_components/network_fungible_faucet.masm index b88db104ce..1b3cd76d31 100644 --- a/crates/miden-lib/asm/account_components/network_fungible_faucet.masm +++ b/crates/miden-lib/asm/account_components/network_fungible_faucet.masm @@ -2,5 +2,5 @@ # # See the `NetworkFungibleFaucet` Rust type's documentation for more details. -export.::miden::contracts::faucets::network_fungible::distribute -export.::miden::contracts::faucets::network_fungible::burn +pub use ::miden::contracts::faucets::network_fungible::distribute +pub use ::miden::contracts::faucets::network_fungible::burn diff --git a/crates/miden-lib/asm/account_components/no_auth.masm b/crates/miden-lib/asm/account_components/no_auth.masm index 7726b6c183..1ec18155b2 100644 --- a/crates/miden-lib/asm/account_components/no_auth.masm +++ b/crates/miden-lib/asm/account_components/no_auth.masm @@ -1,6 +1,6 @@ use miden::active_account use miden::native_account -use std::word +use miden::core::word #! Increment the nonce only if the account commitment has changed #! diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm b/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm index d4d7e42e07..635349116e 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm +++ b/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm @@ -5,7 +5,7 @@ use miden::active_account use miden::native_account use miden::tx -use std::word +use miden::core::word type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -35,7 +35,8 @@ const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::rpo_falcon512_ #! Outputs: [pad(16)] #! #! Invocation: call -pub proc auth_tx_rpo_falcon512_acl.2(auth_args: BeWord) +@locals(2) +pub proc auth_tx_rpo_falcon512_acl(auth_args: BeWord) dropw # => [pad(16)] diff --git a/crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm b/crates/miden-lib/asm/account_components/rpo_falcon_512_multisig.masm similarity index 98% rename from crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm rename to crates/miden-lib/asm/account_components/rpo_falcon_512_multisig.masm index 0b5f037d52..5896eefadb 100644 --- a/crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm +++ b/crates/miden-lib/asm/account_components/rpo_falcon_512_multisig.masm @@ -151,7 +151,8 @@ end #! Locals: #! 0: new_num_of_approvers #! 1: init_num_of_approvers -pub proc update_signers_and_threshold.2(multisig_config_hash: BeWord) +@locals(2) +pub proc update_signers_and_threshold(multisig_config_hash: BeWord) adv.push_mapval # => [MULTISIG_CONFIG_HASH, pad(12)] @@ -238,7 +239,8 @@ end # #! Inputs: [default_threshold] #! Outputs: [transaction_threshold] -proc compute_transaction_threshold.1(default_threshold: u32) -> u32 +@locals(1) +proc compute_transaction_threshold(default_threshold: u32) -> u32 # 1. initialize transaction_threshold = 0 # 2. iterate through all account procedures # a. check if the procedure was called during the transaction @@ -353,7 +355,8 @@ end #! - the same transaction has already been executed (replay protection). #! #! Invocation: call -pub proc auth_tx_rpo_falcon512_multisig.1(salt: BeWord) +@locals(1) +pub proc auth_tx_rpo_falcon512_multisig(salt: BeWord) exec.native_account::incr_nonce drop # => [SALT] diff --git a/crates/miden-lib/asm/kernels/transaction/api.masm b/crates/miden-lib/asm/kernels/transaction/api.masm index 5138dc37e1..e3a5edd3cc 100644 --- a/crates/miden-lib/asm/kernels/transaction/api.masm +++ b/crates/miden-lib/asm/kernels/transaction/api.masm @@ -1,12 +1,12 @@ -use.$kernel::account -use.$kernel::account_delta -use.$kernel::account_id -use.$kernel::faucet -use.$kernel::input_note -use.$kernel::memory -use.$kernel::note -use.$kernel::output_note -use.$kernel::tx +use $kernel::account +use $kernel::account_delta +use $kernel::account_id +use $kernel::faucet +use $kernel::input_note +use $kernel::memory +use $kernel::note +use $kernel::output_note +use $kernel::tx # NOTE # ================================================================================================= @@ -20,25 +20,25 @@ use.$kernel::tx # ERRORS # ================================================================================================= -const.ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED="for faucets the FAUCET_STORAGE_DATA_SLOT storage slot is reserved and can not be used with set_account_item" +const ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED="for faucets the FAUCET_STORAGE_DATA_SLOT storage slot is reserved and can not be used with set_account_item" -const.ERR_FAUCET_TOTAL_ISSUANCE_PROC_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_FAUCET="the faucet_get_total_fungible_asset_issuance procedure can only be called on a fungible faucet" +const ERR_FAUCET_TOTAL_ISSUANCE_PROC_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_FAUCET="the faucet_get_total_fungible_asset_issuance procedure can only be called on a fungible faucet" -const.ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet" +const ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet" -const.ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS="provided kernel procedure offset is out of bounds" +const ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS="provided kernel procedure offset is out of bounds" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ASSETS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note assets of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ASSETS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note assets of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_RECIPIENT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note recipient of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_RECIPIENT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note recipient of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note metadata of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note metadata of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_INPUTS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note inputs of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_INPUTS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note inputs of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note script root of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note script root of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note serial number of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note serial number of active note because no note is currently being processed" # AUTHENTICATION # ================================================================================================= @@ -52,7 +52,7 @@ const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSE #! - the invocation of the kernel procedure does not originate from the account context. #! #! Invocation: exec -proc.authenticate_account_origin +proc authenticate_account_origin # get the hash of the caller padw caller # => [CALLER] @@ -73,7 +73,7 @@ end #! of the account. #! #! Invocation: exec -proc.assert_auth_procedure_origin +proc assert_auth_procedure_origin # get the hash of the caller padw caller # => [CALLER] @@ -98,7 +98,7 @@ end #! - INIT_COMMITMENT is the initial account commitment. #! #! Invocation: dynexec -export.account_get_initial_commitment +pub proc account_get_initial_commitment # get the initial account commitment exec.account::get_initial_commitment # => [INIT_COMMITMENT, pad(16)] @@ -117,7 +117,7 @@ end #! - ACCOUNT_COMMITMENT is the commitment of the account data. #! #! Invocation: dynexec -export.account_compute_commitment +pub proc account_compute_commitment # compute the active account commitment exec.account::compute_commitment # => [ACCOUNT_COMMITMENT, pad(16)] @@ -148,7 +148,7 @@ end #! - the vault or storage delta is not empty but the nonce increment is zero. #! #! Invocation: dynexec -export.account_compute_delta_commitment +pub proc account_compute_delta_commitment # compute the account delta commitment exec.account_delta::compute_commitment # => [DELTA_COMMITMENT, pad(16)] @@ -169,7 +169,7 @@ end #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. #! #! Invocation: dynexec -export.account_get_id +pub proc account_get_id # get the native account ID exec.memory::get_native_account_id # => [native_account_id_prefix, native_account_id_suffix, is_native, pad(15)] @@ -215,7 +215,7 @@ end #! - nonce is the active account's nonce. #! #! Invocation: dynexec -export.account_get_nonce +pub proc account_get_nonce # get the account nonce exec.account::get_nonce # => [nonce, pad(16)] @@ -241,7 +241,7 @@ end #! - the nonce has already been incremented. #! #! Invocation: dynexec -export.account_incr_nonce +pub proc account_incr_nonce # check that this procedure was executed against the native account exec.memory::assert_native_account # => [pad(16)] @@ -268,7 +268,7 @@ end #! - CODE_COMMITMENT is the commitment of the account code. #! #! Invocation: dynexec -export.account_get_code_commitment +pub proc account_get_code_commitment # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [pad(16)] @@ -291,7 +291,7 @@ end #! - INIT_STORAGE_COMMITMENT is the initial account storage commitment. #! #! Invocation: dynexec -export.account_get_initial_storage_commitment +pub proc account_get_initial_storage_commitment # get the initial account storage commitment exec.account::get_initial_storage_commitment # => [INIT_STORAGE_COMMITMENT, pad(16)] @@ -310,7 +310,7 @@ end #! - STORAGE_COMMITMENT is the commitment of the account storage. #! #! Invocation: dynexec -export.account_compute_storage_commitment +pub proc account_compute_storage_commitment # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [pad(16)] @@ -338,7 +338,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: dynexec -export.account_get_item +pub proc account_get_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [slot_id_prefix, slot_id_suffix, pad(14)] @@ -369,7 +369,7 @@ end #! - the native account is a faucet and the provided slot ID points to the reserved faucet storage slot. #! #! Invocation: dynexec -export.account_set_item +pub proc account_set_item # check that this procedure was executed against the native account exec.memory::assert_native_account # => [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] @@ -410,7 +410,7 @@ end #! - the requested storage slot type is not map. #! #! Invocation: dynexec -export.account_get_map_item +pub proc account_get_map_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [slot_id_prefix, slot_id_suffix, KEY, pad(10)] @@ -434,7 +434,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: dynexec -export.account_get_initial_item +pub proc account_get_initial_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [slot_id_prefix, slot_id_suffix, pad(14)] @@ -465,7 +465,7 @@ end #! - the requested storage slot type is not map. #! #! Invocation: dynexec -export.account_get_initial_map_item +pub proc account_get_initial_map_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [slot_id_prefix, slot_id_suffix, KEY, pad(10)] @@ -497,7 +497,7 @@ end #! - the invocation of this procedure does not originate from the native account. #! #! Invocation: dynexec -export.account_set_map_item +pub proc account_set_map_item # check that this procedure was executed against the native account exec.memory::assert_native_account # => [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] @@ -524,7 +524,7 @@ end #! - INIT_VAULT_ROOT is the initial account vault root. #! #! Invocation: dynexec -export.account_get_initial_vault_root +pub proc account_get_initial_vault_root # get the initial account vault root exec.account::get_initial_vault_root # => [INIT_VAULT_ROOT, pad(16)] @@ -543,7 +543,7 @@ end #! - VAULT_ROOT is the root of the account vault. #! #! Invocation: dynexec -export.account_get_vault_root +pub proc account_get_vault_root # fetch the account vault root exec.memory::get_account_vault_root # => [VAULT_ROOT, pad(16)] @@ -573,7 +573,7 @@ end #! - the invocation of this procedure does not originate from the native account. #! #! Invocation: dynexec -export.account_add_asset +pub proc account_add_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [ASSET, pad(12)] @@ -602,7 +602,7 @@ end #! - the invocation of this procedure does not originate from the native account. #! #! Invocation: dynexec -export.account_remove_asset +pub proc account_remove_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [ASSET, pad(12)] @@ -631,7 +631,7 @@ end #! - the provided faucet ID is not an ID of a fungible faucet. #! #! Invocation: dynexec -export.account_get_balance +pub proc account_get_balance exec.account::get_balance # => [balance, pad(15)] end @@ -651,7 +651,7 @@ end #! - the provided faucet ID is not an ID of a fungible faucet. #! #! Invocation: dynexec -export.account_get_initial_balance +pub proc account_get_initial_balance exec.account::get_initial_balance # => [init_balance, pad(15)] end @@ -670,7 +670,7 @@ end #! - the ASSET is a fungible asset. #! #! Invocation: dynexec -export.account_has_non_fungible_asset +pub proc account_has_non_fungible_asset exec.account::has_non_fungible_asset # => [has_asset, pad(15)] end @@ -688,7 +688,7 @@ end #! - the procedure root is not part of the account code. #! #! Invocation: dynexec -export.account_was_procedure_called +pub proc account_was_procedure_called # check that this procedure was executed against the native account exec.memory::assert_native_account # => [PROC_ROOT, pad(12)] @@ -707,7 +707,7 @@ end #! - num_procedures is the number of procedures in the active account. #! #! Invocation: dynexec -export.account_get_num_procedures +pub proc account_get_num_procedures # get the number of procedures exec.memory::get_num_account_procedures # => [num_procedures, pad(16)] @@ -730,7 +730,7 @@ end #! - the procedure index is out of bounds. #! #! Invocation: dynexec -export.account_get_procedure_root +pub proc account_get_procedure_root # get the procedure root exec.account::get_procedure_root # => [PROC_ROOT, pad(15)] @@ -754,7 +754,7 @@ end #! available on the active account. #! #! Invocation: dynexec -export.account_has_procedure +pub proc account_has_procedure # check if the procedure was called exec.account::has_procedure # => [is_procedure_available, pad(15)] @@ -783,7 +783,7 @@ end #! - if the non-fungible asset being minted already exists. #! #! Invocation: dynexec -export.faucet_mint_asset +pub proc faucet_mint_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [ASSET, pad(12)] @@ -818,7 +818,7 @@ end #! transaction via a note or the accounts vault. #! #! Invocation: dynexec -export.faucet_burn_asset +pub proc faucet_burn_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [ASSET, pad(12)] @@ -845,7 +845,7 @@ end #! - the transaction is not being executed against a fungible faucet. #! #! Invocation: dynexec -export.faucet_get_total_fungible_asset_issuance +pub proc faucet_get_total_fungible_asset_issuance # assert that we are executing a transaction against a fungible faucet (access checks) exec.account::get_id swap drop exec.account_id::is_fungible_faucet assert.err=ERR_FAUCET_TOTAL_ISSUANCE_PROC_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_FAUCET @@ -875,7 +875,7 @@ end #! - the ASSET is not associated with the faucet the transaction is being executed against. #! #! Invocation: dynexec -export.faucet_is_non_fungible_asset_issued +pub proc faucet_is_non_fungible_asset_issued # assert that we are executing a transaction against a non-fungible faucet (access checks) exec.account::get_id swap drop exec.account_id::is_non_fungible_faucet assert.err=ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET @@ -912,7 +912,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_assets_info +pub proc input_note_get_assets_info # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -950,7 +950,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_recipient +pub proc input_note_get_recipient # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -988,7 +988,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_metadata +pub proc input_note_get_metadata # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -1026,7 +1026,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_serial_number +pub proc input_note_get_serial_number # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -1067,7 +1067,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_inputs_info +pub proc input_note_get_inputs_info # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -1111,7 +1111,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_script_root +pub proc input_note_get_script_root # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -1150,7 +1150,7 @@ end #! - note_idx is the index of the created note. #! #! Invocation: dynexec -export.output_note_create +pub proc output_note_create # check that this procedure was executed against the native account exec.memory::assert_native_account # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] @@ -1172,7 +1172,7 @@ end #! - the procedure is called when the active account is not the native one. #! #! Invocation: dynexec -export.output_note_add_asset +pub proc output_note_add_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [note_idx, ASSET, pad(11)] @@ -1195,7 +1195,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: dynexec -export.output_note_get_assets_info +pub proc output_note_get_assets_info # assert that the provided note index is less than the total number of output notes exec.output_note::assert_note_index_in_bounds # => [note_index, pad(15)] @@ -1224,7 +1224,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: dynexec -export.output_note_get_recipient +pub proc output_note_get_recipient # assert that the provided note index is less than the total number of output notes exec.output_note::assert_note_index_in_bounds # => [note_index, pad(15)] @@ -1255,7 +1255,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: dynexec -export.output_note_get_metadata +pub proc output_note_get_metadata # assert that the provided note index is less than the total number of output notes exec.output_note::assert_note_index_in_bounds # => [note_index, pad(15)] @@ -1291,7 +1291,7 @@ end #! - INPUT_NOTES_COMMITMENT is the input notes commitment hash. #! #! Invocation: dynexec -export.tx_get_input_notes_commitment +pub proc tx_get_input_notes_commitment exec.tx::get_input_notes_commitment # => [INPUT_NOTES_COMMITMENT, pad(16)] @@ -1310,7 +1310,7 @@ end #! - OUTPUT_NOTES_COMMITMENT is the output notes commitment. #! #! Invocation: dynexec -export.tx_get_output_notes_commitment +pub proc tx_get_output_notes_commitment # get the output notes commitment exec.tx::get_output_notes_commitment # => [OUTPUT_NOTES_COMMITMENT, pad(16)] @@ -1329,7 +1329,7 @@ end #! - num_input_notes is the total number of input notes consumed by this transaction. #! #! Invocation: dynexec -export.tx_get_num_input_notes +pub proc tx_get_num_input_notes # get the number of input notes exec.tx::get_num_input_notes # => [num_input_notes, pad(16)] @@ -1348,7 +1348,7 @@ end #! - num_output_notes is the number of output notes created in this transaction so far. #! #! Invocation: dynexec -export.tx_get_num_output_notes +pub proc tx_get_num_output_notes # get the number of input notes exec.tx::get_num_output_notes # => [num_output_notes, pad(16)] @@ -1367,7 +1367,7 @@ end #! - BLOCK_COMMITMENT is the commitment of the transaction reference block. #! #! Invocation: dynexec -export.tx_get_block_commitment +pub proc tx_get_block_commitment exec.tx::get_block_commitment # => [BLOCK_COMMITMENT, pad(16)] @@ -1385,7 +1385,7 @@ end #! - num is the transaction reference block number. #! #! Invocation: dynexec -export.tx_get_block_number +pub proc tx_get_block_number # get the block number exec.tx::get_block_number # => [num, pad(16)] @@ -1402,7 +1402,7 @@ end #! #! Where: #! - timestamp is the timestamp of the reference block for this transaction. -export.tx_get_block_timestamp +pub proc tx_get_block_timestamp # get the transaction reference block timestamp exec.tx::get_block_timestamp # => [timestamp, pad(16)] @@ -1446,7 +1446,7 @@ end #! - foreign context is created against the native account. #! #! Invocation: dynexec -export.tx_start_foreign_context +pub proc tx_start_foreign_context # get the memory address and a flag whether this account was already loaded. exec.account::get_account_data_ptr # OS => [was_loaded, ptr, foreign_account_id_prefix, foreign_account_id_suffix, pad(14)] @@ -1479,7 +1479,7 @@ end #! - the active account is the native account. #! #! Invocation: dynexec -export.tx_end_foreign_context +pub proc tx_end_foreign_context exec.memory::pop_ptr_from_account_stack # => [pad(16)] end @@ -1498,7 +1498,7 @@ end #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). #! #! Invocation: dynexec -export.tx_update_expiration_block_delta +pub proc tx_update_expiration_block_delta exec.tx::update_expiration_block_delta # => [pad(16)] end @@ -1512,7 +1512,7 @@ end #! - block_height_delta is the stored expiration time delta (1 to 0xFFFF). #! #! Invocation: dynexec -export.tx_get_expiration_delta +pub proc tx_get_expiration_delta exec.tx::get_expiration_delta # => [block_height_delta, pad(16)] @@ -1538,7 +1538,7 @@ end #! - the provided procedure offset exceeds the number of kernel procedures. #! #! Invocation: syscall -export.exec_kernel_proc +pub proc exec_kernel_proc # check that the provided procedure offset is within expected bounds dup exec.memory::get_num_kernel_procedures lt assert.err=ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS @@ -1574,7 +1574,7 @@ end #! #! Panics if: #! - the note index is greater or equal to the total number of input notes. -proc.get_requested_note_ptr +proc get_requested_note_ptr # get the memory pointer to the note with the specified index and verify it is valid swap exec.input_note::get_input_note_ptr swap # => [is_active_note, indexed_input_note_ptr] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index 2d2a361ea9..1a13d31343 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -1,62 +1,62 @@ -use.$kernel::account_delta -use.$kernel::account_id -use.$kernel::asset_vault -use.$kernel::constants -use.$kernel::memory -use.std::collections::smt -use.std::collections::sorted_array -use.std::crypto::hashes::rpo -use.std::mem -use.std::word +use $kernel::account_delta +use $kernel::account_id +use $kernel::asset_vault +use $kernel::constants +use $kernel::memory +use miden::core::collections::smt +use miden::core::collections::sorted_array +use miden::core::crypto::hashes::rpo256 +use miden::core::mem +use miden::core::word # ERRORS # ================================================================================================= -const.ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE="account nonce can only be incremented once" +const ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE="account nonce can only be incremented once" -const.ERR_ACCOUNT_NONCE_AT_MAX="account nonce is already at its maximum possible value" +const ERR_ACCOUNT_NONCE_AT_MAX="account nonce is already at its maximum possible value" -const.ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE="account code must be updatable for it to be possible to set new code" +const ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE="account code must be updatable for it to be possible to set new code" -const.ERR_ACCOUNT_SEED_AND_COMMITMENT_DIGEST_MISMATCH="ID of the new account does not match the ID computed from the seed and commitments" +const ERR_ACCOUNT_SEED_AND_COMMITMENT_DIGEST_MISMATCH="ID of the new account does not match the ID computed from the seed and commitments" -const.ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT="failed to write an account value item to a non-value storage slot" +const ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT="failed to write an account value item to a non-value storage slot" -const.ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT="failed to write an account map item to a non-map storage slot" +const ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT="failed to write an account map item to a non-map storage slot" -const.ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE="procedure is not part of the account code" +const ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE="procedure is not part of the account code" -const.ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS="provided procedure index is out of bounds" +const ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS="provided procedure index is out of bounds" -const.ERR_ACCOUNT_PROC_NOT_AUTH_PROC="account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure" +const ERR_ACCOUNT_PROC_NOT_AUTH_PROC="account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure" -const.ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE="slot IDs must be unique and sorted in ascending order" +const ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE="slot IDs must be unique and sorted in ascending order" -const.ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" +const ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" -const.ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH="computed account code commitment does not match recorded account code commitment" +const ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH="computed account code commitment does not match recorded account code commitment" -const.ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES="number of account procedures must be at least 2" +const ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES="number of account procedures must be at least 2" -const.ERR_ACCOUNT_TOO_MANY_PROCEDURES="number of account procedures exceeds the maximum limit of 256" +const ERR_ACCOUNT_TOO_MANY_PROCEDURES="number of account procedures exceeds the maximum limit of 256" -const.ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS="number of account storage slots exceeds the maximum limit of 255" +const ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS="number of account storage slots exceeds the maximum limit of 255" -const.ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH="computed account storage commitment does not match recorded account storage commitment" +const ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH="computed account storage commitment does not match recorded account storage commitment" -const.ERR_ACCOUNT_STORAGE_MAP_ENTRIES_DO_NOT_MATCH_MAP_ROOT="storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to" +const ERR_ACCOUNT_STORAGE_MAP_ENTRIES_DO_NOT_MATCH_MAP_ROOT="storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to" -const.ERR_FOREIGN_ACCOUNT_ID_IS_ZERO="ID of the provided foreign account equals zero" +const ERR_FOREIGN_ACCOUNT_ID_IS_ZERO="ID of the provided foreign account equals zero" -const.ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED="maximum allowed number of foreign account to be loaded (64) was exceeded" +const ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED="maximum allowed number of foreign account to be loaded (64) was exceeded" -const.ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT="commitment of the foreign account in the advice provider does not match the commitment in the account tree" +const ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT="commitment of the foreign account in the advice provider does not match the commitment in the account tree" -const.ERR_ACCOUNT_ID_UNKNOWN_VERSION="unknown version in account ID" +const ERR_ACCOUNT_ID_UNKNOWN_VERSION="unknown version in account ID" -const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in account ID" +const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in account ID" -const.ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account map item from a non-map storage slot" +const ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account map item from a non-map storage slot" # CONSTANTS # ================================================================================================= @@ -64,120 +64,120 @@ const.ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account # The name of the account storage slot at which faucet data is stored. # Fungible faucet: The faucet data consists of [0, 0, 0, total_issuance] # Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. -const.FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") +const FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") # The maximum storage slot index -const.MAX_STORAGE_SLOT_INDEX=254 +const MAX_STORAGE_SLOT_INDEX=254 # The maximum number of account storage slots. -const.MAX_NUM_STORAGE_SLOTS=MAX_STORAGE_SLOT_INDEX+1 +const MAX_NUM_STORAGE_SLOTS=MAX_STORAGE_SLOT_INDEX+1 # The minimum number of account interface procedures. -const.MIN_NUM_PROCEDURES=2 +const MIN_NUM_PROCEDURES=2 # The maximum number of account interface procedures. -const.MAX_NUM_PROCEDURES=256 +const MAX_NUM_PROCEDURES=256 # Given the least significant 32 bits of an account ID's prefix, this mask defines the bits used # to determine the account version. -const.ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 +const ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 # Given the least significant 32 bits of an account ID's prefix, this mask defines the bits used # to determine the account type. -const.ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 +const ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 # Given the least significant 32 bits of an account ID's first felt, this mask defines the bits used # to determine the account storage mode. -const.ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 +const ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 # Given the least significant 32 bits of an account ID's first felt with the storage mode mask # applied, this value defines the public storage mode. -const.ACCOUNT_ID_STORAGE_MODE_PUBLIC_U32=0 # 0b0000_0000 +const ACCOUNT_ID_STORAGE_MODE_PUBLIC_U32=0 # 0b0000_0000 # Given the least significant 32 bits of an account ID's first felt with the storage mode mask # applied, this value defines the private storage mode. -const.ACCOUNT_ID_STORAGE_MODE_PRIVATE_U32=0x80 # 0b1000_0000 +const ACCOUNT_ID_STORAGE_MODE_PRIVATE_U32=0x80 # 0b1000_0000 # Bit pattern for an account w/ immutable code, after the account type mask has been applied. -const.REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 +const REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 # Bit pattern for an account w/ updatable code, after the account type mask has been applied. -const.REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 +const REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 # Bit pattern for a fungible faucet w/ immutable code, after the account type mask has been applied. -const.FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 +const FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 # Bit pattern for a non-fungible faucet w/ immutable code, after the account type mask has been # applied. -const.NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 +const NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 # Bit pattern for a faucet account, after the account type mask has been applied. -const.FAUCET_ACCOUNT=0x20 # 0b10_0000 +const FAUCET_ACCOUNT=0x20 # 0b10_0000 # Depth of the account database tree. -const.ACCOUNT_TREE_DEPTH=64 +const ACCOUNT_TREE_DEPTH=64 # The number of field elements it takes to store one account storage slot. -const.ACCOUNT_STORAGE_SLOT_DATA_LENGTH=8 +const ACCOUNT_STORAGE_SLOT_DATA_LENGTH=8 # The number of field elements it takes to store one account procedure. -const.ACCOUNT_PROCEDURE_DATA_LENGTH=4 +const ACCOUNT_PROCEDURE_DATA_LENGTH=4 # The offset of the slot type in the storage slot. -const.ACCOUNT_SLOT_TYPE_OFFSET=1 +const ACCOUNT_SLOT_TYPE_OFFSET=1 # The offset of the slot's ID suffix in the storage slot. -const.ACCOUNT_SLOT_ID_SUFFIX_OFFSET=2 +const ACCOUNT_SLOT_ID_SUFFIX_OFFSET=2 # The offset of the slot's ID prefix in the storage slot. -const.ACCOUNT_SLOT_ID_PREFIX_OFFSET=3 +const ACCOUNT_SLOT_ID_PREFIX_OFFSET=3 # The offset of the slot value in the storage slot. -const.ACCOUNT_SLOT_VALUE_OFFSET=4 +const ACCOUNT_SLOT_VALUE_OFFSET=4 # EVENTS # ================================================================================================= # Event emitted before a foreign account is loaded from the advice inputs. -const.ACCOUNT_BEFORE_FOREIGN_LOAD_EVENT=event("miden::account::before_foreign_load") +const ACCOUNT_BEFORE_FOREIGN_LOAD_EVENT=event("miden::account::before_foreign_load") # Event emitted before an asset is added to the account vault. -const.ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT=event("miden::account::vault_before_add_asset") +const ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT=event("miden::account::vault_before_add_asset") # Event emitted after an asset is added to the account vault. -const.ACCOUNT_VAULT_AFTER_ADD_ASSET_EVENT=event("miden::account::vault_after_add_asset") +const ACCOUNT_VAULT_AFTER_ADD_ASSET_EVENT=event("miden::account::vault_after_add_asset") # Event emitted before an asset is removed from the account vault. -const.ACCOUNT_VAULT_BEFORE_REMOVE_ASSET_EVENT=event("miden::account::vault_before_remove_asset") +const ACCOUNT_VAULT_BEFORE_REMOVE_ASSET_EVENT=event("miden::account::vault_before_remove_asset") # Event emitted after an asset is removed from the account vault. -const.ACCOUNT_VAULT_AFTER_REMOVE_ASSET_EVENT=event("miden::account::vault_after_remove_asset") +const ACCOUNT_VAULT_AFTER_REMOVE_ASSET_EVENT=event("miden::account::vault_after_remove_asset") # Event emitted before a fungible asset's balance is fetched from the account vault. -const.ACCOUNT_VAULT_BEFORE_GET_BALANCE_EVENT=event("miden::account::vault_before_get_balance") +const ACCOUNT_VAULT_BEFORE_GET_BALANCE_EVENT=event("miden::account::vault_before_get_balance") # Event emitted before it is checked whether a non-fungible asset exists in the account vault. -const.ACCOUNT_VAULT_BEFORE_HAS_NON_FUNGIBLE_ASSET_EVENT=event("miden::account::vault_before_has_non_fungible_asset") +const ACCOUNT_VAULT_BEFORE_HAS_NON_FUNGIBLE_ASSET_EVENT=event("miden::account::vault_before_has_non_fungible_asset") # Event emitted before an account storage item is updated. -const.ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT=event("miden::account::storage_before_set_item") +const ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT=event("miden::account::storage_before_set_item") # Event emitted after an account storage item is updated. -const.ACCOUNT_STORAGE_AFTER_SET_ITEM_EVENT=event("miden::account::storage_after_set_item") +const ACCOUNT_STORAGE_AFTER_SET_ITEM_EVENT=event("miden::account::storage_after_set_item") # Event emitted before an account storage map item is accessed. -const.ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM_EVENT=event("miden::account::storage_before_get_map_item") +const ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM_EVENT=event("miden::account::storage_before_get_map_item") # Event emitted before an account storage map item is updated. -const.ACCOUNT_STORAGE_BEFORE_SET_MAP_ITEM_EVENT=event("miden::account::storage_before_set_map_item") +const ACCOUNT_STORAGE_BEFORE_SET_MAP_ITEM_EVENT=event("miden::account::storage_before_set_map_item") # Event emitted after an account storage map item is updated. -const.ACCOUNT_STORAGE_AFTER_SET_MAP_ITEM_EVENT=event("miden::account::storage_after_set_map_item") +const ACCOUNT_STORAGE_AFTER_SET_MAP_ITEM_EVENT=event("miden::account::storage_after_set_map_item") # Event emitted before an account nonce is incremented. -const.ACCOUNT_BEFORE_INCREMENT_NONCE_EVENT=event("miden::account::before_increment_nonce") +const ACCOUNT_BEFORE_INCREMENT_NONCE_EVENT=event("miden::account::before_increment_nonce") # Event emitted after an account nonce is incremented. -const.ACCOUNT_AFTER_INCREMENT_NONCE_EVENT=event("miden::account::after_increment_nonce") +const ACCOUNT_AFTER_INCREMENT_NONCE_EVENT=event("miden::account::after_increment_nonce") # Event emitted to push the index of the account procedure at the top of the operand stack onto # the advice stack. -const.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_index") +const ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_index") # CONSTANT ACCESSORS # ================================================================================================= @@ -192,7 +192,7 @@ const.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_i #! Where: #! - faucet_slot_id{prefix,suffix} are the prefix and suffix felts of the slot identifier, at which #! faucet data is stored. -export.get_faucet_sysdata_slot_id +pub proc get_faucet_sysdata_slot_id push.FAUCET_SYSDATA_SLOT[0..2] end @@ -203,7 +203,7 @@ end #! #! Where: #! - max_num_storage_slots is the maximum number of account storage slots. -export.get_max_num_storage_slots +pub proc get_max_num_storage_slots push.MAX_NUM_STORAGE_SLOTS end @@ -214,7 +214,7 @@ end #! #! Where: #! - max_num_procedures is the maximum number of account interface procedures. -export.get_max_num_procedures +pub proc get_max_num_procedures push.MAX_NUM_PROCEDURES end @@ -231,7 +231,7 @@ end #! #! Where: #! - act_acct_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. -export.memory::get_account_id->get_id +pub use memory::get_account_id->get_id #! Returns the nonce of the active account. #! @@ -240,7 +240,7 @@ export.memory::get_account_id->get_id #! #! Where: #! - nonce is the account nonce. -export.memory::get_account_nonce->get_nonce +pub use memory::get_account_nonce->get_nonce #! Increments the account nonce by one and returns the new nonce. #! @@ -251,7 +251,7 @@ export.memory::get_account_nonce->get_nonce #! #! Panics if: #! - the nonce has already been incremented. -export.incr_nonce +pub proc incr_nonce exec.account_delta::was_nonce_incremented # => [was_nonce_incremented] @@ -292,7 +292,7 @@ end #! #! Where: #! - INIT_COMMITMENT is the initial account commitment. -export.get_initial_commitment +pub proc get_initial_commitment # determine whether the active account is native exec.memory::is_native_account # => [is_native_account] @@ -319,7 +319,7 @@ end #! #! Where: #! - ACCOUNT_COMMITMENT is the commitment of the account data. -export.compute_commitment +pub proc compute_commitment # if outdated, recompute the storage commitment and store it in the memory exec.refresh_storage_commitment # => [] @@ -334,7 +334,7 @@ export.compute_commitment # => [RATE, RATE, PERM, account_data_ptr'] # extract account commitment - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [ACCOUNT_COMMITMENT, account_data_ptr'] # drop account_data_ptr @@ -351,7 +351,7 @@ end #! #! Where: #! - CODE_COMMITMENT is the commitment of the account code. -export.memory::get_account_code_commitment->get_code_commitment +pub use memory::get_account_code_commitment->get_code_commitment ### STORAGE COMMITMENT ################################################### @@ -362,7 +362,7 @@ export.memory::get_account_code_commitment->get_code_commitment #! #! Where: #! - INIT_ACCOUNT_STORAGE_COMMITMENT is the initial account storage commitment. -export.get_initial_storage_commitment +pub proc get_initial_storage_commitment # Get the storage commitment of the active account. For the foreign account this commitment will # be initial. exec.memory::get_account_storage_commitment @@ -389,7 +389,7 @@ end #! #! Where: #! - STORAGE_COMMITMENT is the commitment of the active account storage. -export.compute_storage_commitment +pub proc compute_storage_commitment # if outdated, recompute the storage commitment and store it in the memory exec.refresh_storage_commitment @@ -407,7 +407,7 @@ end #! #! Where: #! - INIT_ACCOUNT_VAULT_ROOT is the initial account vault root. -export.get_initial_vault_root +pub proc get_initial_vault_root # Get the vault root of the active account. For the foreign account this root will be equal to # the initial one. exec.memory::get_account_vault_root @@ -442,7 +442,7 @@ end #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. -export.get_item +pub proc get_item # get account storage slots section offset exec.memory::get_account_storage_slots_section_ptr # => [acct_storage_slots_section_offset, slot_id_prefix, slot_id_suffix] @@ -468,7 +468,7 @@ end #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. -export.get_typed_item +pub proc get_typed_item # get account storage slots section offset exec.memory::get_account_storage_slots_section_ptr # => [acct_storage_slots_section_offset, slot_id_prefix, slot_id_suffix] @@ -496,7 +496,7 @@ end #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. -export.get_initial_item +pub proc get_initial_item # get account initial storage slots section offset exec.memory::get_account_initial_storage_slots_ptr # => [account_initial_storage_slots_ptr, slot_id_prefix, slot_id_suffix] @@ -523,7 +523,7 @@ end #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. #! - the storage slot type is not value. -export.set_item +pub proc set_item emit.ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT # => [slot_id_prefix, slot_id_suffix, VALUE] @@ -574,7 +574,7 @@ end #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. -export.get_map_item +pub proc get_map_item exec.memory::get_account_storage_slots_section_ptr # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY] @@ -595,7 +595,7 @@ end #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. -export.get_initial_map_item +pub proc get_initial_map_item exec.memory::get_account_initial_storage_slots_ptr # => [initial_storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY] @@ -620,7 +620,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! - the storage slot type is not map. #! - no map with the root of the slot is found. -export.set_map_item +pub proc set_map_item exec.memory::get_account_storage_slots_section_ptr # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE] @@ -651,7 +651,7 @@ end #! Where: #! - index is the location in memory of the storage slot. #! - slot_type is the type of the storage slot. -export.get_storage_slot_type +pub proc get_storage_slot_type # convert the index into a memory offset mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH # => [offset] @@ -686,7 +686,7 @@ end #! - the total value of the fungible asset is greater than or equal to 2^63 after the new asset was #! added. #! - the vault already contains the same non-fungible asset. -export.add_asset_to_vault +pub proc add_asset_to_vault # duplicate the ASSET to be able to emit an event after an asset is being added dupw # => [ASSET, ASSET] @@ -726,7 +726,7 @@ end #! - the fungible asset is not found in the vault. #! - the amount of the fungible asset in the vault is less than the amount to be removed. #! - the non-fungible asset is not found in the vault. -export.remove_asset_from_vault +pub proc remove_asset_from_vault # fetch the vault root exec.memory::get_account_vault_root_ptr movdn.4 # => [ASSET, acct_vault_root_ptr] @@ -759,7 +759,7 @@ end #! #! Panics if: #! - the provided faucet ID is not an ID of a fungible faucet. -export.get_balance +pub proc get_balance # get the vault root exec.memory::get_account_vault_root_ptr movdn.2 # => [faucet_id_prefix, faucet_id_suffix, vault_root_ptr] @@ -786,7 +786,7 @@ end #! #! Panics if: #! - the provided faucet ID is not an ID of a fungible faucet. -export.get_initial_balance +pub proc get_initial_balance # get the vault root associated with the initial vault root of the native account exec.memory::get_account_initial_vault_root_ptr movdn.2 # => [faucet_id_prefix, faucet_id_suffix, init_native_vault_root_ptr] @@ -812,7 +812,7 @@ end #! #! Panics if: #! - the ASSET is a fungible asset. -export.has_non_fungible_asset +pub proc has_non_fungible_asset # get the vault root exec.memory::get_account_vault_root_ptr movdn.4 # => [ASSET, vault_root_ptr] @@ -840,7 +840,7 @@ end #! #! Panics if: #! - the procedure root is not part of the account code. -export.authenticate_and_track_procedure +pub proc authenticate_and_track_procedure # load procedure index emit.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT adv_push.1 # => [index, PROC_ROOT] @@ -868,7 +868,7 @@ end #! #! Panics if: #! - the procedure root is not the authentication procedure. -export.assert_auth_procedure +pub proc assert_auth_procedure # authentication procedure is always at index 0 push.0 # => [index, PROC_ROOT] @@ -898,7 +898,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.validate_seed +pub proc validate_seed # Compute the hash of (SEED, CODE_COMMITMENT, STORAGE_COMMITMENT, EMPTY_WORD). # --------------------------------------------------------------------------------------------- @@ -939,7 +939,7 @@ export.validate_seed # => [RATE, RATE, CAP] # extract digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [DIGEST] # Shape suffix to set the lower 8 bits to zero and compare the computed and provided ID. @@ -1094,7 +1094,7 @@ end #! - the computed account code commitment does not match the provided account code commitment. #! - the number of account storage slots exceeded the maximum limit of 255. #! - the computed account storage commitment does not match the provided account storage commitment. -export.load_foreign_account +pub proc load_foreign_account emit.ACCOUNT_BEFORE_FOREIGN_LOAD_EVENT # => [account_id_prefix, account_id_suffix] @@ -1163,7 +1163,7 @@ end #! Panics if: #! - the number of account storage slots exceeded the maximum limit of 255. #! - the computed account storage commitment does not match the provided account storage commitment. -export.save_account_storage_data +pub proc save_account_storage_data # move storage slot data from the advice map to the advice stack adv.push_mapvaln # OS => [STORAGE_COMMITMENT] @@ -1202,7 +1202,7 @@ export.save_account_storage_data # AS => [] # extract the digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # OS => [DIGEST, end_ptr', STORAGE_COMMITMENT] # drop end_ptr @@ -1233,7 +1233,7 @@ end #! Panics if: #! - the number of account procedures exceeded the maximum limit of 256. #! - the computed account code commitment does not match the provided account code commitment. -export.save_account_procedure_data +pub proc save_account_procedure_data # move procedure data from the advice map to the advice stack adv.push_mapvaln # OS => [CODE_COMMITMENT] @@ -1276,7 +1276,7 @@ export.save_account_procedure_data # AS => [] # extract the digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # OS => [DIGEST, end_ptr', CODE_COMMITMENT] # drop end_ptr @@ -1296,7 +1296,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.insert_new_storage +pub proc insert_new_storage exec.memory::get_num_storage_slots # => [num_slots] @@ -1344,7 +1344,7 @@ end #! Operand stack: [slot_idx] #! Advice map: { MAP_ROOT: [MAP_ENTRIES] } #! Outputs: [] -proc.insert_and_validate_storage_map +proc insert_and_validate_storage_map mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH exec.memory::get_account_storage_slots_section_ptr add @@ -1438,7 +1438,7 @@ end #! the first two felts of the hashed slot name. #! - is_faucet_storage_data_slot is a boolean value indicating whether the provided slot is the #! reserved faucet data slot. -export.is_faucet_storage_data_slot +pub proc is_faucet_storage_data_slot exec.get_faucet_sysdata_slot_id # => [faucet_slot_id_prefix, faucet_slot_id_suffix, slot_id_prefix, slot_id_suffix] @@ -1466,7 +1466,7 @@ end #! the first two felts of the hashed slot name. #! - INITIAL_VALUE is the initial value of the item at the beginning of the transaction. #! - CURRENT_VALUE is the current value of the item. -export.get_item_delta +pub proc get_item_delta # convert the index into a memory offset mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH # => [offset] @@ -1511,7 +1511,7 @@ end #! - index is the index of the slot. #! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. -export.get_slot_id +pub proc get_slot_id # convert the index into a memory offset mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH # => [offset] @@ -1540,7 +1540,7 @@ end #! Where: #! - slot_ptr is the pointer to a slot. #! - VALUE is the new value of the item. -proc.set_item_raw +proc set_item_raw add.ACCOUNT_SLOT_VALUE_OFFSET mem_storew_be dropw # => [] @@ -1573,7 +1573,8 @@ end #! - 0: slot_ptr #! - 4..8: OLD_MAP_VALUE #! - 8..12: OLD_MAP_ROOT -proc set_map_item_raw.12 +@locals(12) +proc set_map_item_raw # store slot_ptr until the end of the procedure dup loc_store.0 # => [slot_ptr, KEY, NEW_VALUE] @@ -1667,7 +1668,7 @@ end #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. -proc.get_map_item_raw +proc get_map_item_raw exec.find_storage_slot # => [slot_ptr, KEY] @@ -1714,7 +1715,7 @@ end #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. -proc.find_storage_slot +proc find_storage_slot # construct the start and end pointers of the storage slot section in which we will search dup exec.memory::get_num_storage_slots mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH add # => [storage_slots_end_ptr, storage_slots_start_ptr, slot_id_prefix, slot_id_suffix] @@ -1766,7 +1767,7 @@ end #! #! Panics if: #! - the procedure index is out of bounds. -proc.get_procedure_root +proc get_procedure_root dup exec.memory::get_num_account_procedures u32assert2.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS u32lt assert.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS @@ -1798,7 +1799,7 @@ end #! Panics if: #! - the prefix or suffix of the provided foreign account ID equal zero. #! - the maximum allowed number of foreign account to be loaded (64) was exceeded. -export.get_account_data_ptr +pub proc get_account_data_ptr # check that foreign account ID is not equal zero dup.1 eq.0 dup.1 eq.0 and not assert.err=ERR_FOREIGN_ACCOUNT_ID_IS_ZERO # => [foreign_account_id_prefix, foreign_account_id_suffix] @@ -1859,7 +1860,7 @@ end #! #! Panics if: #! - the hash of the active account is not represented in the account database. -export.validate_active_foreign_account +pub proc validate_active_foreign_account # get the account database root exec.memory::get_account_db_root # => [ACCOUNT_DB_ROOT] @@ -1890,7 +1891,7 @@ end #! #! Inputs: [KEY] #! Outputs: [HASHED_KEY] -proc.hash_map_key +proc hash_map_key hash # => [HASHED_KEY] end @@ -1904,7 +1905,7 @@ end #! #! Inputs: [] #! Outputs: [] -proc.refresh_storage_commitment +proc refresh_storage_commitment # First we should determine whether the storage commitment should be recomputed. We should do so # if the active account is native and the storage commitment is outdated (the dirty flag equals # 1). Otherwise the commitment value is guaranteed to be up-to-date. @@ -1929,11 +1930,11 @@ proc.refresh_storage_commitment # => [PAD, PAD, PAD, start_ptr, end_ptr] # hash elements from memory - exec.rpo::absorb_double_words_from_memory + exec.rpo256::absorb_double_words_from_memory # => [PERM, PERM, PERM, start_ptr, end_ptr] # extract the digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [DIGEST, end_ptr, end_ptr] # clean stack @@ -1966,7 +1967,7 @@ end #! #! Panics if: #! - the procedure root is not part of the account code. -export.was_procedure_called +pub proc was_procedure_called # load procedure index emit.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT adv_push.1 # => [index, PROC_ROOT] @@ -1999,7 +2000,7 @@ end #! #! Inputs: [proc_idx] #! Outputs: [] -export.set_was_procedure_called +pub proc set_was_procedure_called exec.memory::get_account_procedures_call_tracking_ptr # => [was_called_offset, proc_idx] @@ -2019,7 +2020,7 @@ end #! - PROC_ROOT is the hash of the procedure of interest. #! - is_procedure_available is the binary flag indicating whether the procedure with PROC_ROOT is #! available on the active account. -export.has_procedure +pub proc has_procedure # get the end pointer of the procedure section (where we should stop iterating) exec.memory::get_num_account_procedures exec.memory::get_account_procedure_ptr diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm index f264825098..220b431df5 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm @@ -1,29 +1,29 @@ -use.$kernel::account -use.$kernel::asset -use.$kernel::asset_vault -use.$kernel::constants -use.$kernel::link_map -use.$kernel::memory -use.std::crypto::hashes::rpo -use.std::word +use $kernel::account +use $kernel::asset +use $kernel::asset_vault +use $kernel::constants +use $kernel::link_map +use $kernel::memory +use miden::core::crypto::hashes::rpo256 +use miden::core::word # ERRORS # ================================================================================================= -const.ERR_ACCOUNT_DELTA_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED="nonce must be incremented if account vault or account storage changed" +const ERR_ACCOUNT_DELTA_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED="nonce must be incremented if account vault or account storage changed" # CONSTANTS # ================================================================================================= # The domain of an asset in the delta commitment. -const.DOMAIN_ASSET=1 +const DOMAIN_ASSET = 1 # The domain of a value storage slot in the delta commitment. -const.DOMAIN_VALUE=2 +const DOMAIN_VALUE = 2 # The domain of a map storage slot in the delta commitment. -const.DOMAIN_MAP=3 +const DOMAIN_MAP = 3 # The maximum value a felt can represent. -const.FELT_MAX=0xffffffff00000000 +const FELT_MAX = 0xffffffff00000000 # PROCEDURES # ================================================================================================= @@ -43,7 +43,7 @@ const.FELT_MAX=0xffffffff00000000 #! #! Panics if: #! - the vault or storage delta is not empty but the nonce increment is zero. -export.compute_commitment +pub proc compute_commitment # pad capacity element of the hasher padw # => [CAPACITY] @@ -76,7 +76,7 @@ export.compute_commitment exec.update_storage_delta # => [RATE, RATE, PERM, ID_AND_NONCE_DIGEST] - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [DELTA_COMMITMENT, ID_AND_NONCE_DIGEST] exec.was_nonce_incremented not @@ -103,7 +103,7 @@ end #! #! Inputs: [RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_storage_delta +proc update_storage_delta exec.memory::get_num_storage_slots movdn.12 # => [RATE, RATE, PERM, num_storage_slots] @@ -144,7 +144,7 @@ end #! #! Inputs: [slot_idx, RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_slot_delta +proc update_slot_delta dup exec.account::get_storage_slot_type # => [storage_slot_type, slot_idx, RATE, RATE, PERM] @@ -164,7 +164,7 @@ end #! #! Inputs: [slot_idx, RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_value_slot_delta +proc update_value_slot_delta exec.account::get_item_delta # => [INIT_VALUE, CURRENT_VALUE, slot_id_prefix, slot_id_suffix, RATE, RATE, PERM] @@ -220,7 +220,8 @@ end #! - 2: has_next #! - 3: iter #! - 4: num_changed_entries -proc.update_map_slot_delta.5 +@locals(5) +proc update_map_slot_delta # initialize num_changed_entries = 0 # this is necessary because this procedure can be called multiple times and the second # invocation shouldn't reuse the first invocation's value @@ -321,7 +322,8 @@ end #! #! Inputs: [RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_fungible_asset_delta.2 +@locals(2) +proc update_fungible_asset_delta exec.memory::get_account_delta_fungible_asset_ptr # => [account_delta_fungible_asset_ptr, RATE, RATE, PERM] @@ -401,7 +403,8 @@ end #! #! Inputs: [RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_non_fungible_asset_delta.2 +@locals(2) +proc update_non_fungible_asset_delta exec.memory::get_account_delta_non_fungible_asset_ptr # => [account_delta_non_fungible_asset_ptr, RATE, RATE, PERM] @@ -478,7 +481,7 @@ end #! #! Where: #! - was_nonce_incremented is the boolean flag indicating whether the account nonce was incremented. -export.was_nonce_incremented +pub proc was_nonce_incremented exec.memory::get_init_nonce # => [init_nonce] @@ -498,7 +501,7 @@ end #! #! Where: #! - ASSET is the asset. -export.add_asset +pub proc add_asset # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET] @@ -528,7 +531,7 @@ end #! #! Where: #! - ASSET is the asset. -export.remove_asset +pub proc remove_asset # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET, vault_root_ptr] @@ -556,7 +559,7 @@ end #! Where: #! - ASSET_KEY is the asset key of the fungible asset. #! - amount is the amount by which the fungible asset's amount increases. -export.add_fungible_asset +pub proc add_fungible_asset dupw exec.memory::get_account_delta_fungible_asset_ptr # => [fungible_delta_map_ptr, ASSET_KEY, ASSET_KEY, amount] @@ -593,7 +596,7 @@ end #! Where: #! - ASSET_KEY is the asset key of the fungible asset. #! - amount is the amount by which the fungible asset's amount decreases. -export.remove_fungible_asset +pub proc remove_fungible_asset dupw exec.memory::get_account_delta_fungible_asset_ptr # => [fungible_delta_map_ptr, ASSET_KEY, ASSET_KEY, amount] @@ -643,7 +646,7 @@ end #! #! Where: #! - ASSET is the non-fungible asset to be added. -export.add_non_fungible_asset +pub proc add_non_fungible_asset dupw exec.memory::get_account_delta_non_fungible_asset_ptr # => [non_fungible_delta_map_ptr, ASSET, ASSET] @@ -677,7 +680,7 @@ end #! #! Where: #! - ASSET is the non-fungible asset to be removed. -export.remove_non_fungible_asset +pub proc remove_non_fungible_asset dupw exec.memory::get_account_delta_non_fungible_asset_ptr # => [non_fungible_delta_map_ptr, ASSET, ASSET] @@ -715,7 +718,8 @@ end #! - KEY is the key in the storage map that is being updated. #! - PREV_VALUE is the previous value of the key in the storage map that is being updated. #! - NEW_VALUE is the new value of the key in the storage map that is being updated. -export.set_map_item.8 +@locals(8) +pub proc set_map_item # retrieve the link map ptr to the storage map delta for the provided index exec.memory::get_account_delta_storage_map_ptr # => [account_delta_storage_map_ptr, KEY, PREV_VALUE, NEW_VALUE] @@ -808,7 +812,7 @@ end #! - is_delta_amount_positive indicates whether the delta amount is positive. #! - delta_amount_abs is the absolute value of the delta_amount, meaning negative values are #! remapped to their positive equivalent value. -proc.delta_amount_absolute +proc delta_amount_absolute dup exec.is_delta_amount_negative not # => [is_positive, delta_amount] @@ -829,7 +833,7 @@ end #! Where: #! - delta_amount is the delta amount that can span the entire felt range. #! - is_delta_amount_negative indicates whether the delta amount represents a negative value. -proc.is_delta_amount_negative +proc is_delta_amount_negative # delta_amount represents a negative number if it is greater than the max amount exec.asset::get_fungible_asset_max_amount gt # => [is_delta_amount_negative] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/asset.masm b/crates/miden-lib/asm/kernels/transaction/lib/asset.masm index df64d68e36..6429f273d0 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/asset.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/asset.masm @@ -1,22 +1,21 @@ -use.$kernel::account -use.$kernel::account_id +use $kernel::account_id # ERRORS # ================================================================================================= -const.ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ONE_MUST_BE_ZERO="malformed fungible asset: `ASSET[1]` must be 0" +const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ONE_MUST_BE_ZERO="malformed fungible asset: `ASSET[1]` must be 0" -const.ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_TWO_AND_THREE_MUST_BE_FUNGIBLE_FAUCET_ID="malformed fungible asset: `ASSET[2]` and `ASSET[3]` must be a valid fungible faucet id" +const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_TWO_AND_THREE_MUST_BE_FUNGIBLE_FAUCET_ID="malformed fungible asset: `ASSET[2]` and `ASSET[3]` must be a valid fungible faucet id" -const.ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS="malformed fungible asset: `ASSET[0]` exceeds the maximum allowed amount" +const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS="malformed fungible asset: `ASSET[0]` exceeds the maximum allowed amount" -const.ERR_NON_FUNGIBLE_ASSET_FORMAT_ELEMENT_THREE_MUST_BE_FUNGIBLE_FAUCET_ID="malformed non-fungible asset: `ASSET[3]` is not a valid non-fungible faucet id" +const ERR_NON_FUNGIBLE_ASSET_FORMAT_ELEMENT_THREE_MUST_BE_FUNGIBLE_FAUCET_ID="malformed non-fungible asset: `ASSET[3]` is not a valid non-fungible faucet id" -const.ERR_NON_FUNGIBLE_ASSET_FORMAT_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO="malformed non-fungible asset: the most significant bit must be 0" +const ERR_NON_FUNGIBLE_ASSET_FORMAT_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO="malformed non-fungible asset: the most significant bit must be 0" -const.ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the fungible asset is not this faucet" +const ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the fungible asset is not this faucet" -const.ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the non-fungible asset is not this faucet" +const ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the non-fungible asset is not this faucet" # CONSTANT ACCESSORS # ================================================================================================= @@ -28,7 +27,7 @@ const.ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the non-fungibl #! #! Where: #! - fungible_asset_max_amount is the maximum amount of a fungible asset. -export.::$kernel::util::asset::get_fungible_asset_max_amount +pub use ::$kernel::util::asset::get_fungible_asset_max_amount # PROCEDURES # ================================================================================================= @@ -43,7 +42,7 @@ export.::$kernel::util::asset::get_fungible_asset_max_amount #! #! Panics if: #! - the asset is not well formed. -export.validate_fungible_asset +pub proc validate_fungible_asset # assert that ASSET[1] == ZERO dup.2 not assert.err=ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ONE_MUST_BE_ZERO # => [ASSET] @@ -58,7 +57,7 @@ export.validate_fungible_asset # => [ASSET] # assert that the max amount (ASSET[0]) of a fungible asset is not exceeded - dup.3 exec.get_fungible_asset_max_amount lte + dup.3 exec.::$kernel::util::asset::get_fungible_asset_max_amount lte assert.err=ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS # => [ASSET] end @@ -71,7 +70,7 @@ end #! Where: #! - ASSET is the asset to check. #! - is_fungible_asset is a boolean indicating whether the asset is fungible. -export.is_fungible_asset +pub proc is_fungible_asset # check the first element, it will be: # - zero for a fungible asset # - non zero for a non-fungible asset @@ -89,7 +88,7 @@ end #! #! Panics if: #! - the asset is not well formed. -export.validate_non_fungible_asset +pub proc validate_non_fungible_asset # assert that ASSET[3] is a valid account ID prefix # hack: because we only have the prefix we add a 0 as the suffix which is always valid push.0 dup.1 exec.account_id::validate @@ -109,7 +108,7 @@ end #! Where: #! - ASSET is the asset to check. #! - is_non_fungible_asset is a boolean indicating whether the asset is non-fungible. -export.is_non_fungible_asset +pub proc is_non_fungible_asset # check the first element, it will be: # - zero for a fungible asset # - non zero for a non-fungible asset @@ -127,7 +126,7 @@ end #! #! Panics if: #! - the asset is not well formed. -export.validate_asset +pub proc validate_asset # check if the asset is fungible exec.is_fungible_asset # => [is_fungible_asset, ASSET] @@ -150,7 +149,7 @@ end #! Where: #! - faucet_id_prefix is the prefix of the faucet's account ID. #! - ASSET is the asset to validate. -export.validate_fungible_asset_origin +pub proc validate_fungible_asset_origin # assert the origin of the asset is the faucet_id provided via the stack dup.3 dup.3 # => [asset_id_prefix, asset_id_suffix, faucet_id_prefix, faucet_id_suffix, ASSET] @@ -171,7 +170,7 @@ end #! Where: #! - faucet_id_prefix is the prefix of the faucet's account ID. #! - ASSET is the asset to validate. -export.validate_non_fungible_asset_origin +pub proc validate_non_fungible_asset_origin # assert the origin of the asset is the faucet_id prefix provided via the stack dup.1 assert_eq.err=ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN # => [ASSET] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm b/crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm index 6d439c8e71..111224287f 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm @@ -1,30 +1,30 @@ -use.std::collections::smt -use.std::word +use miden::core::collections::smt +use miden::core::word -use.$kernel::account_id -use.$kernel::asset -use.$kernel::memory +use $kernel::account_id +use $kernel::asset +use $kernel::memory # ERRORS # ================================================================================================= -const.ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="get_balance can only be called on a fungible asset" +const ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="get_balance can only be called on a fungible asset" -const.ERR_VAULT_PEEK_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="peek_balance can only be called on a fungible asset" +const ERR_VAULT_PEEK_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="peek_balance can only be called on a fungible asset" -const.ERR_VAULT_HAS_NON_FUNGIBLE_ASSET_PROC_CAN_BE_CALLED_ONLY_WITH_NON_FUNGIBLE_ASSET="the has_non_fungible_asset procedure can only be called on a non-fungible faucet" +const ERR_VAULT_HAS_NON_FUNGIBLE_ASSET_PROC_CAN_BE_CALLED_ONLY_WITH_NON_FUNGIBLE_ASSET="the has_non_fungible_asset procedure can only be called on a non-fungible faucet" -const.ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding the fungible asset to the vault would exceed the max amount of 9223372036854775807" +const ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding the fungible asset to the vault would exceed the max amount of 9223372036854775807" -const.ERR_VAULT_ADD_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to add fungible asset to the asset vault due to the initial value being invalid" +const ERR_VAULT_ADD_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to add fungible asset to the asset vault due to the initial value being invalid" -const.ERR_VAULT_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="the non-fungible asset already exists in the asset vault" +const ERR_VAULT_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="the non-fungible asset already exists in the asset vault" -const.ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW="failed to remove the fungible asset from the vault since the amount of the asset in the vault is less than the amount to remove" +const ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW="failed to remove the fungible asset from the vault since the amount of the asset in the vault is less than the amount to remove" -const.ERR_VAULT_REMOVE_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to remove fungible asset from the asset vault due to the initial value being invalid" +const ERR_VAULT_REMOVE_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to remove fungible asset from the asset vault due to the initial value being invalid" -const.ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND="failed to remove non-existent non-fungible asset from the vault" +const ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND="failed to remove non-existent non-fungible asset from the vault" # EVENTS # ================================================================================================= @@ -32,13 +32,13 @@ const.ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND="failed to remove non-exi # The event is from `stdlib`, visible through the prefix and is _not_ a `TransactionEvent`. # Care must be taken it stays in sync with the `miden-stdlib` definition. Note that generally speaking # we should not emit `"stdlib"` events, consider this "hijacking", tread carefully. -const.SMT_PEEK_EVENT=event("stdlib::collections::smt::smt_peek") +const SMT_PEEK_EVENT=event("miden::core::collections::smt::smt_peek") # CONSTANTS # ================================================================================================= # The bitmask that when applied will set the fungible bit to zero. -const.INVERSE_FUNGIBLE_BITMASK_U32=0xffffffdf # last byte: 0b1101_1111 +const INVERSE_FUNGIBLE_BITMASK_U32=0xffffffdf # last byte: 0b1101_1111 # ACCESSORS # ================================================================================================= @@ -56,7 +56,7 @@ const.INVERSE_FUNGIBLE_BITMASK_U32=0xffffffdf # last byte: 0b1101_1111 #! #! Panics if: #! - the provided faucet ID is not an ID of a fungible faucet. -export.get_balance +pub proc get_balance # assert that the faucet id is a fungible faucet dup exec.account_id::is_fungible_faucet assert.err=ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET @@ -102,7 +102,7 @@ end #! #! Panics if: #! - the asset is not a fungible asset. -export.peek_balance +pub proc peek_balance # assert that the faucet id is a fungible faucet dup exec.account_id::is_fungible_faucet assert.err=ERR_VAULT_PEEK_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET @@ -148,7 +148,7 @@ end #! #! Panics if: #! - the ASSET is a fungible asset. -export.has_non_fungible_asset +pub proc has_non_fungible_asset # check if the asset is a non-fungible asset exec.asset::is_non_fungible_asset assert.err=ERR_VAULT_HAS_NON_FUNGIBLE_ASSET_PROC_CAN_BE_CALLED_ONLY_WITH_NON_FUNGIBLE_ASSET @@ -190,7 +190,7 @@ end #! #! Panics if: #! - the total value of assets is greater than or equal to 2^63. -export.add_fungible_asset +pub proc add_fungible_asset # Create the asset key from the asset. # --------------------------------------------------------------------------------------------- @@ -294,7 +294,7 @@ end #! #! Panics if: #! - the vault already contains the same non-fungible asset. -export.add_non_fungible_asset +pub proc add_non_fungible_asset # Build the asset key from the non-fungible asset. # --------------------------------------------------------------------------------------------- @@ -341,7 +341,7 @@ end #! - the asset is not valid. #! - the total value of two fungible assets is greater than or equal to 2^63. #! - the vault already contains the same non-fungible asset. -export.add_asset +pub proc add_asset # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET] @@ -381,7 +381,8 @@ end #! #! Panics if: #! - the amount of the asset in the vault is less than the amount to be removed. -export.remove_fungible_asset.4 +@locals(4) +pub proc remove_fungible_asset exec.build_fungible_asset_vault_key # => [ASSET_KEY, ASSET, vault_root_ptr] @@ -465,7 +466,7 @@ end #! #! Panics if: #! - the non-fungible asset is not found in the vault. -export.remove_non_fungible_asset +pub proc remove_non_fungible_asset # build non-fungible asset key dupw exec.build_non_fungible_asset_vault_key padw # => [pad(4), ASSET_KEY, ASSET, vault_root_ptr] @@ -504,7 +505,7 @@ end #! - the fungible asset is not found in the vault. #! - the amount of the fungible asset in the vault is less than the amount to be removed. #! - the non-fungible asset is not found in the vault. -export.remove_asset +pub proc remove_asset # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET, vault_root_ptr] @@ -534,7 +535,7 @@ end #! - faucet_id_{prefix, suffix} are the prefix and suffix felts of the faucet id of the fungible #! asset of interest. #! - asset is the peeked asset from the vault. -proc.peek_asset +proc peek_asset # load the asset vault root from memory padw movup.8 mem_loadw_be # => [ASSET_VAULT_ROOT, ASSET_KEY] @@ -566,7 +567,7 @@ end #! Where: #! - ASSET is the non-fungible asset for which the vault key is built. #! - ASSET_KEY is the vault key of the non-fungible asset. -export.build_non_fungible_asset_vault_key +pub proc build_non_fungible_asset_vault_key # create the asset key from the non-fungible asset by swapping hash0 with the faucet id # => [faucet_id_prefix, hash2, hash1, hash0] swap.3 @@ -596,7 +597,7 @@ end #! Where: #! - ASSET is the fungible asset for which the vault key is built. #! - ASSET_KEY is the vault key of the fungible asset. -export.build_fungible_asset_vault_key +pub proc build_fungible_asset_vault_key # => [faucet_id_prefix, faucet_id_suffix, 0, amount] push.0.0 diff --git a/crates/miden-lib/asm/kernels/transaction/lib/constants.masm b/crates/miden-lib/asm/kernels/transaction/lib/constants.masm index 7c1da10d20..fb0dfb08f9 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/constants.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/constants.masm @@ -2,33 +2,33 @@ # ================================================================================================= # The number of elements in a Word -const.WORD_SIZE=4 +const WORD_SIZE=4 # The maximum number of input values associated with a single note. -const.MAX_INPUTS_PER_NOTE=1024 +const MAX_INPUTS_PER_NOTE=1024 # The maximum number of assets that can be stored in a single note. -const.MAX_ASSETS_PER_NOTE=256 +const MAX_ASSETS_PER_NOTE=256 # The maximum number of notes that can be consumed in a single transaction. -const.MAX_INPUT_NOTES_PER_TX=1024 +const MAX_INPUT_NOTES_PER_TX=1024 # The size of the memory segment allocated to each note. -const.NOTE_MEM_SIZE=2048 +const NOTE_MEM_SIZE=2048 # The depth of the Merkle tree used to commit to notes produced in a block. -const.NOTE_TREE_DEPTH=16 +const NOTE_TREE_DEPTH=16 # The maximum number of notes that can be created in a single transaction. -const.MAX_OUTPUT_NOTES_PER_TX=1024 +const MAX_OUTPUT_NOTES_PER_TX=1024 # TYPES # ================================================================================================= # Type of storage slot item in the account storage -const.STORAGE_SLOT_TYPE_VALUE=0 -const.STORAGE_SLOT_TYPE_MAP=1 -const.STORAGE_SLOT_TYPE_ARRAY=2 +const STORAGE_SLOT_TYPE_VALUE=0 +const STORAGE_SLOT_TYPE_MAP=1 +const STORAGE_SLOT_TYPE_ARRAY=2 # PROCEDURES # ================================================================================================= @@ -40,7 +40,7 @@ const.STORAGE_SLOT_TYPE_ARRAY=2 #! #! Where: #! - word_size is the number of elements in a Word. -export.get_word_size +pub proc get_word_size push.WORD_SIZE end @@ -51,7 +51,7 @@ end #! #! Where: #! - max_inputs_per_note is the max inputs per note. -export.::$kernel::util::note::get_max_inputs_per_note +pub use ::$kernel::util::note::get_max_inputs_per_note #! Returns the max allowed number of assets per note. #! @@ -60,7 +60,7 @@ export.::$kernel::util::note::get_max_inputs_per_note #! #! Where: #! - max_assets_per_note is the max assets per note. -export.get_max_assets_per_note +pub proc get_max_assets_per_note push.MAX_ASSETS_PER_NOTE end @@ -71,7 +71,7 @@ end #! #! Where: #! - max_num_input_notes is the max number of input notes. -export.get_max_num_input_notes +pub proc get_max_num_input_notes push.MAX_INPUT_NOTES_PER_TX end @@ -82,7 +82,7 @@ end #! #! Where: #! - note_mem_size is the size of the memory segment allocated to each note. -export.get_note_mem_size +pub proc get_note_mem_size push.NOTE_MEM_SIZE end @@ -93,7 +93,7 @@ end #! #! Where: #! - note_tree_depth is the depth of the Merkle tree used to commit to notes produced in a block. -export.get_note_tree_depth +pub proc get_note_tree_depth push.NOTE_TREE_DEPTH end @@ -104,7 +104,7 @@ end #! #! Where: #! - max_num_output_notes is the max number of notes that can be created in a single transaction. -export.get_max_num_output_notes +pub proc get_max_num_output_notes push.MAX_OUTPUT_NOTES_PER_TX end @@ -115,7 +115,7 @@ end #! #! Where: #! - EMPTY_SMT_ROOT is the root of an empty Sparse Merkle Tree. -export.get_empty_smt_root +pub proc get_empty_smt_root push.15321474589252129342.17373224439259377994.15071539326562317628.3312677166725950353 end @@ -126,7 +126,7 @@ end #! #! Where: #! - type_storage_value is the type of storage slot item in the account storage. -export.get_storage_slot_type_value +pub proc get_storage_slot_type_value push.STORAGE_SLOT_TYPE_VALUE end @@ -137,7 +137,7 @@ end #! #! Where: #! - type_storage_map is the type of storage slot item in the account storage. -export.get_storage_slot_type_map +pub proc get_storage_slot_type_map push.STORAGE_SLOT_TYPE_MAP end @@ -148,6 +148,6 @@ end #! #! Where: #! - type_storage_array is the type of storage slot item in the account storage. -export.get_storage_slot_type_array +pub proc get_storage_slot_type_array push.STORAGE_SLOT_TYPE_ARRAY end diff --git a/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm b/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm index f7281e5be9..1a6c7ef5f4 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm @@ -1,54 +1,54 @@ -use.$kernel::account -use.$kernel::account_delta -use.$kernel::asset_vault -use.$kernel::constants -use.$kernel::memory -use.$kernel::note +use $kernel::account +use $kernel::account_delta +use $kernel::asset_vault +use $kernel::constants +use $kernel::memory +use $kernel::note -use.std::word +use miden::core::word # ERRORS # ================================================================================================= -const.ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME="total number of assets in the account and all involved notes must stay the same" +const ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME="total number of assets in the account and all involved notes must stay the same" -const.ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY="executed transaction neither changed the account state, nor consumed any notes" +const ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY="executed transaction neither changed the account state, nor consumed any notes" -const.ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT="auth procedure had been called from outside the epilogue" +const ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT="auth procedure had been called from outside the epilogue" -const.ERR_EPILOGUE_NONCE_CANNOT_BE_0="nonce cannot be 0 after an account-creating transaction" +const ERR_EPILOGUE_NONCE_CANNOT_BE_0="nonce cannot be 0 after an account-creating transaction" # CONSTANTS # ================================================================================================= # Event emitted to signal that the compute_fee procedure has obtained the current number of cycles. -const.EPILOGUE_AFTER_TX_CYCLES_OBTAINED_EVENT=event("miden::epilogue::after_tx_cycles_obtained") +const EPILOGUE_AFTER_TX_CYCLES_OBTAINED_EVENT=event("miden::epilogue::after_tx_cycles_obtained") # Event emitted to signal that the fee was computed. -const.EPILOGUE_BEFORE_TX_FEE_REMOVED_FROM_ACCOUNT_EVENT=event("miden::epilogue::before_tx_fee_removed_from_account") +const EPILOGUE_BEFORE_TX_FEE_REMOVED_FROM_ACCOUNT_EVENT=event("miden::epilogue::before_tx_fee_removed_from_account") # Event emitted to signal that an execution of the authentication procedure has started. -const.EPILOGUE_AUTH_PROC_START_EVENT=event("miden::epilogue::auth_proc_start") +const EPILOGUE_AUTH_PROC_START_EVENT=event("miden::epilogue::auth_proc_start") # Event emitted to signal that an execution of the authentication procedure has ended. -const.EPILOGUE_AUTH_PROC_END_EVENT=event("miden::epilogue::auth_proc_end") +const EPILOGUE_AUTH_PROC_END_EVENT=event("miden::epilogue::auth_proc_end") # An additional number of cyclces to account for the number of cycles that smt::set will take when # removing the computed fee from the asset vault. # Theoretically, this can safely be set to worst_case_cycles - best_case_cycles of smt::set. That's # because we can assume that the measured number of cycles already contains at least the best case # number of cycles, and so we only need to add the difference. -const.SMT_SET_ADDITIONAL_CYCLES=250 +const SMT_SET_ADDITIONAL_CYCLES=250 # The number of cycles the epilogue is estimated to take after compute_fee has been executed, # including an unknown cycle number of the above-mentioned call to smt::set. It is safe to assume # that this includes at least smt::set's best case number of cycles. # This can be _estimated_ using the transaction measurements on ExecutedTransaction and can be set # to the lowest observed value. -const.NUM_POST_COMPUTE_FEE_CYCLES=500 +const NUM_POST_COMPUTE_FEE_CYCLES=500 # The number of cycles the epilogue is estimated to take after compute_fee has been executed. -const.ESTIMATED_AFTER_COMPUTE_FEE_CYCLES=NUM_POST_COMPUTE_FEE_CYCLES+SMT_SET_ADDITIONAL_CYCLES +const ESTIMATED_AFTER_COMPUTE_FEE_CYCLES=NUM_POST_COMPUTE_FEE_CYCLES+SMT_SET_ADDITIONAL_CYCLES # OUTPUT NOTES PROCEDURES # ================================================================================================= @@ -72,7 +72,7 @@ const.ESTIMATED_AFTER_COMPUTE_FEE_CYCLES=NUM_POST_COMPUTE_FEE_CYCLES+SMT_SET_ADD #! - output_note_ptr is the start boundary of the output notes section. #! - output_notes_end_ptr is the end boundary of the output notes section. #! - mem[i] is the memory value stored at some address i. -proc.copy_output_notes_to_advice_map +proc copy_output_notes_to_advice_map # get the number of notes created by the transaction exec.memory::get_num_output_notes # => [num_notes, OUTPUT_NOTES_COMMITMENT] @@ -117,7 +117,7 @@ end #! #! Inputs: [] #! Outputs: [] -proc.build_output_vault +proc build_output_vault # copy final account vault root to output account vault root exec.memory::get_account_vault_root exec.memory::set_output_vault_root dropw # => [] @@ -203,7 +203,7 @@ end #! #! Inputs: [] #! Outputs: [] -proc.execute_auth_procedure +proc execute_auth_procedure emit.EPILOGUE_AUTH_PROC_START_EVENT padw padw padw @@ -247,7 +247,7 @@ end #! #! Where: #! - fee_amount is the computed fee amount of the transaction in the native asset. -proc.compute_fee +proc compute_fee # get the number of cycles the transaction has taken to execute up this point clk # => [num_current_cycles] @@ -282,7 +282,7 @@ end #! - fee_amount is the computed fee amount of the transaction in the native asset. #! - FEE_ASSET is the fungible asset with amount set to fee_amount and the faucet ID set to the #! native asset. -proc.build_native_fee_asset +proc build_native_fee_asset exec.memory::get_native_asset_id # => [native_asset_id_prefix, native_asset_id_suffix, fee_amount] @@ -308,7 +308,7 @@ end #! #! Panics if: #! - the account vault does not contain the computed fee. -proc.compute_and_remove_fee +proc compute_and_remove_fee # compute the fee the tx needs to pay exec.compute_fee # => [fee_amount] @@ -377,7 +377,8 @@ end #! changing the account's state. The latter is validated as part of computing the account delta #! commitment. #! - the account vault does not contain the computed fee. -export.finalize_transaction.12 +@locals(12) +pub proc finalize_transaction # make sure that the context was switched back to the native account exec.memory::assert_native_account diff --git a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm b/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm index ef55eba543..d1b1fd2407 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm @@ -1,21 +1,21 @@ -use.$kernel::account -use.$kernel::account_id -use.$kernel::asset -use.$kernel::asset_vault -use.$kernel::memory +use $kernel::account +use $kernel::account_id +use $kernel::asset +use $kernel::asset_vault +use $kernel::memory # ERRORS # ================================================================================================= -const.ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT="asset mint operation would cause the new total supply to exceed the maximum allowed asset amount" +const ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT="asset mint operation would cause the new total supply to exceed the maximum allowed asset amount" -const.ERR_FAUCET_BURN_CANNOT_EXCEED_EXISTING_TOTAL_SUPPLY="asset amount to burn can not exceed the existing total supply" +const ERR_FAUCET_BURN_CANNOT_EXCEED_EXISTING_TOTAL_SUPPLY="asset amount to burn can not exceed the existing total supply" -const.ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED="failed to mint new non-fungible asset because it was already issued" +const ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED="failed to mint new non-fungible asset because it was already issued" -const.ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the burn_non_fungible_asset procedure can only be called on a non-fungible faucet" +const ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the burn_non_fungible_asset procedure can only be called on a non-fungible faucet" -const.ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND="failed to burn non-existent non-fungible asset in the vault" +const ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND="failed to burn non-existent non-fungible asset in the vault" # FUNGIBLE ASSETS # ================================================================================================== @@ -36,7 +36,7 @@ const.ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND="failed to burn non-existe #! executed against. #! - the asset is not well formed. #! - the total issuance after minting is greater than the maximum amount allowed. -export.mint_fungible_asset +pub proc mint_fungible_asset # assert that the asset is associated with the faucet the transaction is being executed against # and that the asset is valid exec.account::get_id exec.asset::validate_fungible_asset_origin @@ -78,7 +78,7 @@ end #! executed against. #! - the asset is not well formed. #! - the amount being burned is greater than the total input to the transaction. -proc.burn_fungible_asset +proc burn_fungible_asset # assert that the asset is associated with the faucet the transaction is being executed against # and that the asset is valid exec.account::get_id exec.asset::validate_fungible_asset_origin @@ -109,7 +109,7 @@ end #! Where: #! - total_issuance is the total issuance of the fungible faucet the transaction is being executed #! against. -export.get_total_issuance +pub proc get_total_issuance # fetch the TOTAL_ISSUANCE from storage exec.account::get_faucet_sysdata_slot_id exec.account::get_item # => [TOTAL_ISSUANCE] @@ -136,7 +136,7 @@ end #! - the non-fungible asset being minted is not associated with the faucet the transaction is being #! executed against. #! - the non-fungible asset being minted already exists. -proc.mint_non_fungible_asset +proc mint_non_fungible_asset # assert that the asset is associated with the faucet the transaction is being executed against # and that the asset is valid exec.account::get_id swap drop exec.asset::validate_non_fungible_asset_origin @@ -180,7 +180,7 @@ end #! executed against. #! - the non-fungible asset being burned does not exist or was not provided as input to the #! transaction via a note or the accounts vault. -proc.burn_non_fungible_asset +proc burn_non_fungible_asset # assert that we are executing a transaction against the non-fungible faucet (access checks) exec.account::get_id swap drop exec.account_id::is_non_fungible_faucet assert.err=ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET @@ -225,7 +225,7 @@ end #! Panics if: #! - the ASSET is a fungible asset. #! - the ASSET is not associated with the faucet the transaction is being executed against. -export.is_non_fungible_asset_issued +pub proc is_non_fungible_asset_issued # assert that the asset is associated with the faucet the transaction is being executed against # and that the asset is valid exec.account::get_id swap drop exec.asset::validate_non_fungible_asset_origin @@ -270,7 +270,7 @@ end #! - For fungible faucets if the total issuance after minting is greater than the maximum amount #! allowed. #! - For non-fungible faucets if the non-fungible asset being minted already exists. -export.mint +pub proc mint # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET] @@ -303,7 +303,7 @@ end #! transaction. #! - For non-fungible faucets if the non-fungible asset being burned does not exist or was not #! provided as input to the transaction via a note or the accounts vault. -export.burn +pub proc burn # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/input_note.masm b/crates/miden-lib/asm/kernels/transaction/lib/input_note.masm index 717bc67ee0..dd247047ce 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/input_note.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/input_note.masm @@ -1,9 +1,9 @@ -use.$kernel::memory +use $kernel::memory # ERRORS # ================================================================================================= -const.ERR_INPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested input note index should be less than the total number of input notes" +const ERR_INPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested input note index should be less than the total number of input notes" # INPUT NOTE PROCEDURES # ================================================================================================= @@ -17,7 +17,7 @@ const.ERR_INPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested input note index should be l #! - input_note_ptr is the memory address of the data segment for the requested input note. #! - num_assets is the number of assets in the specified note. #! - ASSETS_COMMITMENT is a sequential hash of the assets in the specified note. -export.get_assets_info +pub proc get_assets_info # get the number of assets in the note dup exec.memory::get_input_note_num_assets # => [num_assets, input_note_ptr] @@ -31,7 +31,7 @@ end #! #! Inputs: [note_index] #! Outputs: [note_index] -export.assert_note_index_in_bounds +pub proc assert_note_index_in_bounds # assert that the provided note index is less than the total number of notes dup exec.memory::get_num_input_notes # => [input_notes_num, note_index, note_index] @@ -53,7 +53,7 @@ end #! #! Panics if: #! - the note index is greater or equal to the total number of input notes. -export.get_input_note_ptr +pub proc get_input_note_ptr # asset that the provided input note index is valid exec.assert_note_index_in_bounds # => [idx] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/link_map.masm b/crates/miden-lib/asm/kernels/transaction/lib/link_map.masm index bef4faffff..caa4323e13 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/link_map.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/link_map.masm @@ -1,6 +1,6 @@ -use.std::collections::smt -use.std::word -use.$kernel::memory +use miden::core::collections::smt +use miden::core::word +use $kernel::memory # A link map is a map data structure based on a sorted linked list. # @@ -119,58 +119,58 @@ use.$kernel::memory # ERRORS # ================================================================================================= -const.ERR_LINK_MAP_CANNOT_BE_EMPTY_ON_ABSENCE_AFTER_ENTRY="map cannot be empty when proving absence after an entry" +const ERR_LINK_MAP_CANNOT_BE_EMPTY_ON_ABSENCE_AFTER_ENTRY="map cannot be empty when proving absence after an entry" -const.ERR_LINK_MAP_PROVIDED_KEY_NOT_EQUAL_TO_ENTRY_KEY="provided key does not match key in map entry" +const ERR_LINK_MAP_PROVIDED_KEY_NOT_EQUAL_TO_ENTRY_KEY="provided key does not match key in map entry" -const.ERR_LINK_MAP_PROVIDED_KEY_NOT_GREATER_THAN_ENTRY_KEY="provided key is not greater than the entry key" +const ERR_LINK_MAP_PROVIDED_KEY_NOT_GREATER_THAN_ENTRY_KEY="provided key is not greater than the entry key" -const.ERR_LINK_MAP_PROVIDED_KEY_NOT_LESS_THAN_ENTRY_KEY="provided key is not less than the entry key" +const ERR_LINK_MAP_PROVIDED_KEY_NOT_LESS_THAN_ENTRY_KEY="provided key is not less than the entry key" -const.ERR_LINK_MAP_ENTRY_PTR_IS_OUTSIDE_VALID_MEMORY_REGION="host-provided entry ptr is outside the valid memory region" +const ERR_LINK_MAP_ENTRY_PTR_IS_OUTSIDE_VALID_MEMORY_REGION="host-provided entry ptr is outside the valid memory region" -const.ERR_LINK_MAP_ENTRY_PTR_IS_NOT_ENTRY_ALIGNED="host-provided entry ptr is not 'link map entry'-aligned" +const ERR_LINK_MAP_ENTRY_PTR_IS_NOT_ENTRY_ALIGNED="host-provided entry ptr is not 'link map entry'-aligned" -const.ERR_LINK_MAP_MAP_PTR_IN_ENTRY_DOES_NOT_MATCH_EXPECTED_MAP_PTR="map ptr stored in host-provided entry does not match actual pointer of the map" +const ERR_LINK_MAP_MAP_PTR_IN_ENTRY_DOES_NOT_MATCH_EXPECTED_MAP_PTR="map ptr stored in host-provided entry does not match actual pointer of the map" # CONSTANTS # ================================================================================================= # The offset of the prev_entry_ptr in an entry's metadata. -const.PREV_ENTRY_PTR_OFFSET=1 +const PREV_ENTRY_PTR_OFFSET=1 # The offset of the next_entry_ptr in an entry's metadata. -const.NEXT_ENTRY_PTR_OFFSET=2 +const NEXT_ENTRY_PTR_OFFSET=2 # The offset of the KEY in an entry. -const.KEY_OFFSET=4 +const KEY_OFFSET=4 # The offset of the VALUE0 in an entry. -const.VALUE0_OFFSET=8 +const VALUE0_OFFSET=8 # The offset of the VALUE1 in an entry. -const.VALUE1_OFFSET=12 +const VALUE1_OFFSET=12 # The value of the Update operation for a set event. -const.INSERT_OPERATION_UPDATE=0 +const INSERT_OPERATION_UPDATE=0 # The value of the InsertAtHead operation for a set event. -const.INSERT_OPERATION_AT_HEAD=1 +const INSERT_OPERATION_AT_HEAD=1 # The value of the Found operation for a get event. -const.GET_OPERATION_FOUND=0 +const GET_OPERATION_FOUND=0 # The value of the AbsentAtHead operation for a get event. -const.GET_OPERATION_ABSENT_AT_HEAD=1 +const GET_OPERATION_ABSENT_AT_HEAD=1 # EVENTS # ================================================================================================= # Event emitted when an entry is set. -const.LINK_MAP_SET_EVENT=event("miden::link_map::set") +const LINK_MAP_SET_EVENT=event("miden::link_map::set") # Event emitted when an entry is fetched. -const.LINK_MAP_GET_EVENT=event("miden::link_map::get") +const LINK_MAP_GET_EVENT=event("miden::link_map::get") # LINK MAP PROCEDURES # ================================================================================================= @@ -198,7 +198,7 @@ const.LINK_MAP_GET_EVENT=event("miden::link_map::get") #! Panics if: #! - the host provides faulty advice. See panic sections of assert_entry_ptr_is_valid, #! update_entry, insert_at_head, insert_after_entry. -export.set +pub proc set emit.LINK_MAP_SET_EVENT adv_push.2 # => [operation, entry_ptr, map_ptr, KEY, VALUE0, VALUE1] @@ -265,7 +265,7 @@ end #! Panics if: #! - the host provides faulty advice. See panic sections of assert_entry_ptr_is_valid, #! get_existing_value, assert_absent_at_head, assert_absent_after_entry. -export.get +pub proc get emit.LINK_MAP_GET_EVENT adv_push.2 # => [get_operation, entry_ptr, map_ptr, KEY] @@ -318,7 +318,7 @@ end #! #! Inputs: [map_ptr] #! Outputs: [is_empty] -export.is_empty +pub proc is_empty mem_load eq.0 end @@ -328,7 +328,7 @@ end #! #! Inputs: [map_ptr] #! Outputs: [has_next, iter] -export.iter +pub proc iter exec.get_head # => [entry_ptr] @@ -349,7 +349,7 @@ end #! #! Inputs: [iter] #! Outputs: [KEY, VALUE0, VALUE1, has_next, next_iter] -export.next_key_double_value +pub proc next_key_double_value dup exec.next_key # => [KEY, has_next, next_entry_ptr, entry_ptr = iter] @@ -367,7 +367,7 @@ end #! #! Inputs: [iter] #! Outputs: [KEY, VALUE0, has_next, next_iter] -export.next_key_value +pub proc next_key_value dup exec.next_key # => [KEY, has_next, next_entry_ptr, entry_ptr = iter] @@ -385,7 +385,7 @@ end #! #! Inputs: [iter] #! Outputs: [KEY, has_next, next_iter] -export.next_key +pub proc next_key dup exec.get_next_entry_ptr # => [next_entry_ptr, entry_ptr = iter] @@ -406,7 +406,7 @@ end #! #! Panics if: #! - the KEY is not less than the key in the head of the map, unless the map is empty. -proc.insert_at_head +proc insert_at_head exec.memory::link_map_malloc # => [entry_ptr, map_ptr, KEY, VALUE0, VALUE1] @@ -465,7 +465,7 @@ end #! #! Panics if: #! - the key in the entry does not match the provided KEY. -proc.update_entry +proc update_entry dup movdn.5 # => [entry_ptr, KEY, entry_ptr, VALUE0, VALUE1] @@ -485,7 +485,7 @@ end #! - the KEY is not greater than the key in the prev entry. #! - the KEY is not less than the key in prev_entry.next_entry, unless the prev entry is the last #! one in the map. -proc.insert_after_entry +proc insert_after_entry movdn.5 # => [map_ptr, KEY, prev_entry_ptr, VALUE0, VALUE1] @@ -567,7 +567,7 @@ end #! #! Inputs: [entry_ptr, map_ptr, KEY, VALUE0, VALUE1] #! Outputs: [] -proc.insert_pair +proc insert_pair swap dup.1 # => [entry_ptr, map_ptr, entry_ptr, KEY, VALUE0, VALUE1] @@ -597,7 +597,7 @@ end #! #! Panics if: #! - the KEY is not less than the key in the head of the map, unless the map is empty. -proc.assert_absent_at_head +proc assert_absent_at_head dup exec.is_empty # => [is_empty, map_ptr, KEY] @@ -627,7 +627,7 @@ end #! - the map is empty. #! - the KEY is not greater than the key in the entry. #! - the KEY is not less than the key in entry.next_entry, unless the entry is the last one in the map. -proc.assert_absent_after_entry +proc assert_absent_after_entry swap exec.is_empty assertz.err=ERR_LINK_MAP_CANNOT_BE_EMPTY_ON_ABSENCE_AFTER_ENTRY # => [entry_ptr, KEY] @@ -666,7 +666,7 @@ end #! #! Panics if: #! - the key in the entry does not match KEY. -proc.get_existing_value +proc get_existing_value movdn.4 dup.4 # => [entry_ptr, KEY, entry_ptr] @@ -684,7 +684,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [has_next] -proc.has_next +proc has_next exec.get_next_entry_ptr eq.0 not end @@ -692,7 +692,7 @@ end #! #! Inputs: [entry_ptr, next_entry_ptr] #! Outputs: [] -proc.set_next_entry_ptr +proc set_next_entry_ptr add.NEXT_ENTRY_PTR_OFFSET mem_store # => [] end @@ -701,7 +701,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [next_entry_ptr] -proc.get_next_entry_ptr +proc get_next_entry_ptr add.NEXT_ENTRY_PTR_OFFSET mem_load # => [next_entry_ptr] end @@ -710,7 +710,7 @@ end #! #! Inputs: [entry_ptr, prev_entry_ptr] #! Outputs: [] -proc.set_prev_entry_ptr +proc set_prev_entry_ptr add.PREV_ENTRY_PTR_OFFSET mem_store # => [] end @@ -719,7 +719,7 @@ end #! #! Inputs: [entry_ptr, VALUE0, VALUE1] #! Outputs: [] -proc.set_value +proc set_value dup movdn.5 # => [entry_ptr, VALUE0, entry_ptr, VALUE1] @@ -734,7 +734,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [VALUE0, VALUE1] -proc.get_values +proc get_values dup exec.get_value1 # => [VALUE1, entry_ptr] @@ -746,7 +746,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [VALUE0] -proc.get_value0 +proc get_value0 padw movup.4 # => [entry_ptr, pad(4)] @@ -758,7 +758,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [VALUE1] -proc.get_value1 +proc get_value1 padw movup.4 # => [entry_ptr, pad(4)] @@ -770,7 +770,7 @@ end #! #! Inputs: [entry_ptr, KEY] #! Outputs: [] -proc.set_key +proc set_key add.KEY_OFFSET mem_storew_be dropw end @@ -778,7 +778,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [KEY] -proc.get_key +proc get_key padw movup.4 # => [entry_ptr, pad(4)] @@ -790,7 +790,7 @@ end #! #! Inputs: [map_ptr, entry_ptr] #! Outputs: [] -proc.set_head +proc set_head mem_store end @@ -798,7 +798,7 @@ end #! #! Inputs: [map_ptr] #! Outputs: [entry_ptr] -proc.get_head +proc get_head mem_load end @@ -806,7 +806,7 @@ end #! #! Inputs: [entry_ptr, map_ptr] #! Outputs: [] -proc.set_map_ptr +proc set_map_ptr mem_store end @@ -814,7 +814,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [map_ptr] -proc.get_map_ptr +proc get_map_ptr mem_load end @@ -825,7 +825,7 @@ end #! #! Inputs: [entry_ptr, KEY] #! Outputs: [] -proc.assert_key_is_equal +proc assert_key_is_equal exec.get_key swapw # => [KEY, ENTRY_KEY] @@ -837,7 +837,7 @@ end #! #! Inputs: [entry_ptr, KEY] #! Outputs: [] -proc.assert_key_is_greater +proc assert_key_is_greater exec.get_key # => [ENTRY_KEY, KEY] @@ -848,7 +848,7 @@ end #! #! Inputs: [entry_ptr, KEY] #! Outputs: [] -proc.assert_key_is_less +proc assert_key_is_less exec.get_key # => [ENTRY_KEY, KEY] @@ -867,7 +867,7 @@ end #! - entry ptr is "link map entry"-aligned, i.e. entry_ptr % LINK_MAP_ENTRY_SIZE == 0. This #! works because every entry ptr is a multiple of LINK_MAP_ENTRY_SIZE. #! - entry's map ptr is equal to the given map_ptr. -export.assert_entry_ptr_is_valid +pub proc assert_entry_ptr_is_valid # Check entry pointer is in valid memory range. # ------------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm index 6f103d1295..b1d93d415f 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/memory.masm @@ -1,21 +1,20 @@ -use.$kernel::constants -use.$kernel::account -use.std::mem +use $kernel::constants +use miden::core::mem # ERRORS # ================================================================================================= -const.ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255" +const ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255" -const.ERR_ACCOUNT_IS_NOT_NATIVE="the active account is not native" +const ERR_ACCOUNT_IS_NOT_NATIVE="the active account is not native" -const.ERR_ACCOUNT_STACK_OVERFLOW="depth of the nested FPI calls exceeded 64" +const ERR_ACCOUNT_STACK_OVERFLOW="depth of the nested FPI calls exceeded 64" -const.ERR_ACCOUNT_STACK_UNDERFLOW="failed to end foreign context because the active account is the native account" +const ERR_ACCOUNT_STACK_UNDERFLOW="failed to end foreign context because the active account is the native account" -const.ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT="creation of a foreign context against the native account is forbidden" +const ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT="creation of a foreign context against the native account is forbidden" -const.ERR_LINK_MAP_MAX_ENTRIES_EXCEEDED="number of link map entries exceeds maximum" +const ERR_LINK_MAP_MAX_ENTRIES_EXCEEDED="number of link map entries exceeds maximum" # MEMORY ADDRESS CONSTANTS # ================================================================================================= @@ -24,134 +23,134 @@ const.ERR_LINK_MAP_MAX_ENTRIES_EXCEEDED="number of link map entries exceeds maxi # ------------------------------------------------------------------------------------------------- # The memory address at which a pointer to the currently active input note is stored. -const.ACTIVE_INPUT_NOTE_PTR=0 +const ACTIVE_INPUT_NOTE_PTR=0 # The memory address at which the number of output notes is stored. -const.NUM_OUTPUT_NOTES_PTR=4 +const NUM_OUTPUT_NOTES_PTR=4 # The memory address at which the input vault root is stored. -const.INPUT_VAULT_ROOT_PTR=8 +const INPUT_VAULT_ROOT_PTR=8 # The memory address at which the output vault root is stored. -const.OUTPUT_VAULT_ROOT_PTR=12 +const OUTPUT_VAULT_ROOT_PTR=12 # The memory address at which the dirty flag of the storage commitment of the native account is # stored. # # This binary flag specifies whether the commitment is outdated: it holds 1 if some changes were # made to the account storage since the last re-computation, and 0 otherwise. -const.NATIVE_ACCT_STORAGE_COMMITMENT_DIRTY_FLAG_PTR=16 +const NATIVE_ACCT_STORAGE_COMMITMENT_DIRTY_FLAG_PTR=16 # The memory address at which the absolute expiration block number is stored. -const.TX_EXPIRATION_BLOCK_NUM_PTR=20 +const TX_EXPIRATION_BLOCK_NUM_PTR=20 # The memory address at which the pointer to the account stack element containing the pointer to the # currently accessing account (active account) data is stored. -const.ACCOUNT_STACK_TOP_PTR=24 +const ACCOUNT_STACK_TOP_PTR=24 # Pointer to the first element on the account stack. -const.MIN_ACCOUNT_STACK_PTR=25 +const MIN_ACCOUNT_STACK_PTR=25 # Pointer to the last element on the account stack. -const.MAX_ACCOUNT_STACK_PTR=88 +const MAX_ACCOUNT_STACK_PTR=88 # GLOBAL INPUTS # ------------------------------------------------------------------------------------------------- # The memory address at which the global inputs section begins. -const.GLOBAL_INPUTS_SECTION_OFFSET=400 +const GLOBAL_INPUTS_SECTION_OFFSET=400 # The memory address at which the transaction reference block's commitment is stored. -const.BLOCK_COMMITMENT_PTR=400 +const BLOCK_COMMITMENT_PTR=400 # The memory address at which the native account ID is stored. -const.NATIVE_ACCT_ID_PTR=404 +const NATIVE_ACCT_ID_PTR=404 # The memory address at which the initial account commitment is stored. -const.INIT_ACCOUNT_COMMITMENT_PTR=408 +const INIT_ACCOUNT_COMMITMENT_PTR=408 # The memory address at which the initial nonce is stored. -const.INIT_NONCE_PTR=412 +const INIT_NONCE_PTR=412 # The memory address at which the initial vault root of the native account is stored. -const.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR=416 +const INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR=416 # The memory address at which the initial storage commitment of the native account is stored. -const.INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT_PTR=420 +const INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT_PTR=420 # The memory address at which the input notes commitment is stored. -const.INPUT_NOTES_COMMITMENT_PTR=424 +const INPUT_NOTES_COMMITMENT_PTR=424 # The memory address at which the transaction script mast root is stored. -const.TX_SCRIPT_ROOT_PTR=428 +const TX_SCRIPT_ROOT_PTR=428 # The memory address at which the transaction script arguments are stored. -const.TX_SCRIPT_ARGS_PTR=432 +const TX_SCRIPT_ARGS_PTR=432 # The memory address at which the auth procedure arguments are stored. -const.AUTH_ARGS_PTR=436 +const AUTH_ARGS_PTR=436 # GLOBAL BLOCK DATA # ------------------------------------------------------------------------------------------------- # The memory address at which the block data section begins -const.BLOCK_DATA_SECTION_OFFSET=800 +const BLOCK_DATA_SECTION_OFFSET=800 # The memory address at which the previous block commitment is stored -const.PREV_BLOCK_COMMITMENT_PTR=800 +const PREV_BLOCK_COMMITMENT_PTR=800 # The memory address at which the chain commitment is stored -const.CHAIN_COMMITMENT_PTR=804 +const CHAIN_COMMITMENT_PTR=804 # The memory address at which the account root is stored -const.ACCT_DB_ROOT_PTR=808 +const ACCT_DB_ROOT_PTR=808 # The memory address at which the nullifier root is stored -const.NULLIFIER_ROOT_PTR=812 +const NULLIFIER_ROOT_PTR=812 # The memory address at which the tx commitment is stored -const.TX_COMMITMENT_PTR=816 +const TX_COMMITMENT_PTR=816 # The memory address at which the kernel commitment is stored -const.TX_KERNEL_COMMITMENT_PTR=820 +const TX_KERNEL_COMMITMENT_PTR=820 # The memory address at which the proof commitment is stored -const.VALIDATOR_KEY_COMMITMENT_PTR=824 +const VALIDATOR_KEY_COMMITMENT_PTR=824 # The memory address at which the block metadata is stored [block_number, version, timestamp, 0] -const.BLOCK_METADATA_PTR=828 +const BLOCK_METADATA_PTR=828 # The memory address at which the fee parameters are stored. These occupy a double word. # [native_asset_id_suffix, native_asset_id_prefix, verification_base_fee, 0] # [0, 0, 0, 0] -const.FEE_PARAMETERS_PTR=832 +const FEE_PARAMETERS_PTR=832 # The memory address at which the verification base fee is stored. -const.VERIFICATION_BASE_FEE_PTR=FEE_PARAMETERS_PTR+2 +const VERIFICATION_BASE_FEE_PTR=FEE_PARAMETERS_PTR+2 # The memory address at which the note root is stored -const.NOTE_ROOT_PTR=840 +const NOTE_ROOT_PTR=840 # PARTIAL BLOCKCHAIN # ------------------------------------------------------------------------------------------------- # The memory address at which the chain data section begins -const.PARTIAL_BLOCKCHAIN_PTR=1200 +const PARTIAL_BLOCKCHAIN_PTR=1200 # The memory address at which the total number of leaves in the partial blockchain is stored -const.PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR=1200 +const PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR=1200 # The memory address at which the partial blockchain peaks are stored -const.PARTIAL_BLOCKCHAIN_PEAKS_PTR=1204 +const PARTIAL_BLOCKCHAIN_PEAKS_PTR=1204 # KERNEL DATA # ------------------------------------------------------------------------------------------------- # The memory address at which the number of the kernel procedures is stored. -const.NUM_KERNEL_PROCEDURES_PTR=1600 +const NUM_KERNEL_PROCEDURES_PTR=1600 # The memory address at which the hashes of kernel procedures begin. -const.KERNEL_PROCEDURES_PTR=1604 +const KERNEL_PROCEDURES_PTR=1604 # ACCOUNT DATA # ------------------------------------------------------------------------------------------------- @@ -159,94 +158,94 @@ const.KERNEL_PROCEDURES_PTR=1604 # The largest memory address which can be used to load the foreign account data. # It is computed as `2048 * 64 * 4` -- this is the memory address where the data block of the 64th # account starts. -const.MAX_FOREIGN_ACCOUNT_PTR=524288 +const MAX_FOREIGN_ACCOUNT_PTR=524288 # The memory address at which the native account data is stored. -const.NATIVE_ACCOUNT_DATA_PTR=8192 +const NATIVE_ACCOUNT_DATA_PTR=8192 # The length of the memory interval that the account data occupies. -const.ACCOUNT_DATA_LENGTH=8192 +const ACCOUNT_DATA_LENGTH=8192 # The number of field elements it takes to store one account procedure. # TODO: Duplicated in account.masm, can we remove that? -const.ACCOUNT_PROCEDURE_DATA_LENGTH=4 +const ACCOUNT_PROCEDURE_DATA_LENGTH=4 # The offsets at which the account data is stored relative to the start of the account data segment. -const.ACCT_ID_AND_NONCE_OFFSET=0 -const.ACCT_NONCE_OFFSET=3 -const.ACCT_VAULT_ROOT_OFFSET=4 -const.ACCT_STORAGE_COMMITMENT_OFFSET=8 -const.ACCT_CODE_COMMITMENT_OFFSET=12 -const.ACCT_CORE_DATA_SECTION_END_OFFSET=16 -const.NUM_ACCT_PROCEDURES_OFFSET=28 -const.ACCT_PROCEDURES_SECTION_OFFSET=32 -const.ACCT_PROCEDURES_CALL_TRACKING_OFFSET=2084 -const.NUM_ACCT_STORAGE_SLOTS_OFFSET=2340 -const.ACCT_STORAGE_SLOTS_SECTION_OFFSET=2344 +const ACCT_ID_AND_NONCE_OFFSET=0 +const ACCT_NONCE_OFFSET=3 +const ACCT_VAULT_ROOT_OFFSET=4 +const ACCT_STORAGE_COMMITMENT_OFFSET=8 +const ACCT_CODE_COMMITMENT_OFFSET=12 +const ACCT_CORE_DATA_SECTION_END_OFFSET=16 +const NUM_ACCT_PROCEDURES_OFFSET=28 +const ACCT_PROCEDURES_SECTION_OFFSET=32 +const ACCT_PROCEDURES_CALL_TRACKING_OFFSET=2084 +const NUM_ACCT_STORAGE_SLOTS_OFFSET=2340 +const ACCT_STORAGE_SLOTS_SECTION_OFFSET=2344 # Offset at which an exact copy of the account's storage slot section starting at # ACCT_STORAGE_SLOTS_SECTION_OFFSET is kept. This keeps the initial values of the account storage # slots accessible to enable computing a diff between the initial and current account storage slots # for use in the account delta. This section is only present for the native account. -const.ACCT_INITIAL_STORAGE_SLOTS_SECTION_OFFSET=4384 +const ACCT_INITIAL_STORAGE_SLOTS_SECTION_OFFSET=4384 # NATIVE ACCOUNT DELTA # ------------------------------------------------------------------------------------------------- # The link map pointer at which the delta of the fungible asset vault is stored. -const.ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR=532480 +const ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR=532480 # The link map pointer at which the delta of the non-fungible asset vault is stored. -const.ACCOUNT_DELTA_NON_FUNGIBLE_ASSET_PTR=ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR+4 +const ACCOUNT_DELTA_NON_FUNGIBLE_ASSET_PTR=ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR+4 # The section of link map pointers where storage map deltas are stored. # This section is offset by `slot index` to get the link map ptr for the storage map # delta at that slot index. Slot indices that are not map slots are simply unused. -const.ACCOUNT_DELTA_STORAGE_MAP_SECTION=ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR+8 +const ACCOUNT_DELTA_STORAGE_MAP_SECTION=ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR+8 # INPUT NOTES DATA # ------------------------------------------------------------------------------------------------- # The memory address at which the input note section begins. -const.INPUT_NOTE_SECTION_OFFSET=4194304 +const INPUT_NOTE_SECTION_OFFSET=4194304 # The memory address at which the nullifier section of the input notes begins. -const.INPUT_NOTE_NULLIFIER_SECTION_PTR=4194308 +const INPUT_NOTE_NULLIFIER_SECTION_PTR=4194308 # The memory address at which the input note data section begins. -const.INPUT_NOTE_DATA_SECTION_OFFSET=4259840 +const INPUT_NOTE_DATA_SECTION_OFFSET=4259840 # The memory address at which the number of input notes is stored. -const.NUM_INPUT_NOTES_PTR=INPUT_NOTE_SECTION_OFFSET +const NUM_INPUT_NOTES_PTR=INPUT_NOTE_SECTION_OFFSET # The offsets at which data of an input note is stored relative to the start of its data segment -const.INPUT_NOTE_ID_OFFSET=0 -const.INPUT_NOTE_CORE_DATA_OFFSET=4 -const.INPUT_NOTE_SERIAL_NUM_OFFSET=4 -const.INPUT_NOTE_SCRIPT_ROOT_OFFSET=8 -const.INPUT_NOTE_INPUTS_COMMITMENT_OFFSET=12 -const.INPUT_NOTE_ASSETS_COMMITMENT_OFFSET=16 -const.INPUT_NOTE_RECIPIENT_OFFSET=20 -const.INPUT_NOTE_METADATA_OFFSET=24 -const.INPUT_NOTE_ARGS_OFFSET=28 -const.INPUT_NOTE_NUM_INPUTS_OFFSET=32 -const.INPUT_NOTE_NUM_ASSETS_OFFSET=36 -const.INPUT_NOTE_ASSETS_OFFSET=40 +const INPUT_NOTE_ID_OFFSET=0 +const INPUT_NOTE_CORE_DATA_OFFSET=4 +const INPUT_NOTE_SERIAL_NUM_OFFSET=4 +const INPUT_NOTE_SCRIPT_ROOT_OFFSET=8 +const INPUT_NOTE_INPUTS_COMMITMENT_OFFSET=12 +const INPUT_NOTE_ASSETS_COMMITMENT_OFFSET=16 +const INPUT_NOTE_RECIPIENT_OFFSET=20 +const INPUT_NOTE_METADATA_OFFSET=24 +const INPUT_NOTE_ARGS_OFFSET=28 +const INPUT_NOTE_NUM_INPUTS_OFFSET=32 +const INPUT_NOTE_NUM_ASSETS_OFFSET=36 +const INPUT_NOTE_ASSETS_OFFSET=40 # OUTPUT NOTES # ------------------------------------------------------------------------------------------------- # The memory address at which the output notes section begins. -const.OUTPUT_NOTE_SECTION_OFFSET=16777216 +const OUTPUT_NOTE_SECTION_OFFSET=16777216 # The offsets at which data of an output note is stored relative to the start of its data segment. -const.OUTPUT_NOTE_ID_OFFSET=0 -const.OUTPUT_NOTE_METADATA_OFFSET=4 -const.OUTPUT_NOTE_RECIPIENT_OFFSET=8 -const.OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=12 -const.OUTPUT_NOTE_NUM_ASSETS_OFFSET=16 -const.OUTPUT_NOTE_DIRTY_FLAG_OFFSET=17 -const.OUTPUT_NOTE_ASSETS_OFFSET=20 +const OUTPUT_NOTE_ID_OFFSET=0 +const OUTPUT_NOTE_METADATA_OFFSET=4 +const OUTPUT_NOTE_RECIPIENT_OFFSET=8 +const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=12 +const OUTPUT_NOTE_NUM_ASSETS_OFFSET=16 +const OUTPUT_NOTE_DIRTY_FLAG_OFFSET=17 +const OUTPUT_NOTE_ASSETS_OFFSET=20 # LINK MAP MEMORY # ------------------------------------------------------------------------------------------------- @@ -254,21 +253,21 @@ const.OUTPUT_NOTE_ASSETS_OFFSET=20 # The inclusive start of the link map dynamic memory region. # Chosen as a number greater than 2^25 such that all entry pointers are multiples of # LINK_MAP_ENTRY_SIZE. That enables a simpler check in assert_entry_ptr_is_valid. -const.LINK_MAP_REGION_START_PTR=33554448 +const LINK_MAP_REGION_START_PTR=33554448 # The non-inclusive end of the link map dynamic memory region. # This happens to be 2^26, but if it is changed, it should be chosen as a number such that # LINK_MAP_REGION_END_PTR - LINK_MAP_REGION_START_PTR is a multiple of LINK_MAP_ENTRY_SIZE, # because that enables checking whether a newly allocated entry pointer is at the end of the range # using equality rather than lt/gt in link_map_malloc. -const.LINK_MAP_REGION_END_PTR=67108864 +const LINK_MAP_REGION_END_PTR=67108864 # LINK_MAP_REGION_START_PTR + the currently used size stored at this pointer defines the next # entry pointer that will be allocated. -const.LINK_MAP_USED_MEMORY_SIZE=33554432 +const LINK_MAP_USED_MEMORY_SIZE=33554432 # The size of each map entry, i.e. four words. -const.LINK_MAP_ENTRY_SIZE=16 +const LINK_MAP_ENTRY_SIZE=16 # MEMORY PROCEDURES # ================================================================================================= @@ -283,7 +282,7 @@ const.LINK_MAP_ENTRY_SIZE=16 #! #! Where: #! - num_output_notes is the number of output notes created in this transaction so far. -export.get_num_output_notes +pub proc get_num_output_notes mem_load.NUM_OUTPUT_NOTES_PTR end @@ -294,7 +293,7 @@ end #! #! Where: #! - num_output_notes is the number of output notes. -export.set_num_output_notes +pub proc set_num_output_notes mem_store.NUM_OUTPUT_NOTES_PTR end @@ -305,7 +304,7 @@ end #! #! Where: #! - note_ptr is the memory address of the data segment for the active note. -export.get_active_input_note_ptr +pub proc get_active_input_note_ptr mem_load.ACTIVE_INPUT_NOTE_PTR end @@ -316,7 +315,7 @@ end #! #! Where: #! - note_ptr is the new memory address of the data segment for the input note. -export.set_active_input_note_ptr +pub proc set_active_input_note_ptr mem_store.ACTIVE_INPUT_NOTE_PTR end @@ -328,7 +327,7 @@ end #! Where: #! - input_vault_root_ptr is a pointer to the memory address at which the input vault root is #! stored. -export.get_input_vault_root_ptr +pub proc get_input_vault_root_ptr push.INPUT_VAULT_ROOT_PTR end @@ -339,7 +338,7 @@ end #! #! Where: #! - INPUT_VAULT_ROOT is the input vault root. -export.get_input_vault_root +pub proc get_input_vault_root padw mem_loadw_be.INPUT_VAULT_ROOT_PTR end @@ -350,7 +349,7 @@ end #! #! Where: #! - INPUT_VAULT_ROOT is the input vault root. -export.set_input_vault_root +pub proc set_input_vault_root mem_storew_be.INPUT_VAULT_ROOT_PTR end @@ -362,7 +361,7 @@ end #! Where: #! - output_vault_root_ptr is the pointer to the memory address at which the output vault root is #! stored. -export.get_output_vault_root_ptr +pub proc get_output_vault_root_ptr push.OUTPUT_VAULT_ROOT_PTR end @@ -373,7 +372,7 @@ end #! #! Where: #! - OUTPUT_VAULT_ROOT is the output vault root. -export.get_output_vault_root +pub proc get_output_vault_root padw mem_loadw_be.OUTPUT_VAULT_ROOT_PTR end @@ -384,7 +383,7 @@ end #! #! Where: #! - OUTPUT_VAULT_ROOT is the output vault root. -export.set_output_vault_root +pub proc set_output_vault_root mem_storew_be.OUTPUT_VAULT_ROOT_PTR end @@ -398,7 +397,7 @@ end #! #! Where: #! - BLOCK_COMMITMENT is the commitment of the transaction reference block. -export.set_block_commitment +pub proc set_block_commitment mem_storew_be.BLOCK_COMMITMENT_PTR end @@ -409,7 +408,7 @@ end #! #! Where: #! - BLOCK_COMMITMENT is the commitment of the transaction reference block. -export.get_block_commitment +pub proc get_block_commitment padw mem_loadw_be.BLOCK_COMMITMENT_PTR end @@ -420,7 +419,7 @@ end #! #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the account ID. -export.set_global_account_id +pub proc set_global_account_id push.0.0 # => [0, 0, account_id_prefix, account_id_suffix] mem_storew_be.NATIVE_ACCT_ID_PTR @@ -435,7 +434,7 @@ end #! #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the account ID. -export.get_global_account_id +pub proc get_global_account_id padw mem_loadw_be.NATIVE_ACCT_ID_PTR # => [0, 0, account_id_prefix, account_id_suffix] drop drop @@ -448,7 +447,7 @@ end #! #! Where: #! - INIT_ACCOUNT_COMMITMENT is the initial account commitment. -export.set_init_account_commitment +pub proc set_init_account_commitment mem_storew_be.INIT_ACCOUNT_COMMITMENT_PTR end @@ -459,7 +458,7 @@ end #! #! Where: #! - INIT_ACCOUNT_COMMITMENT is the initial account commitment. -export.get_init_account_commitment +pub proc get_init_account_commitment padw mem_loadw_be.INIT_ACCOUNT_COMMITMENT_PTR end @@ -470,7 +469,7 @@ end #! #! Where: #! - init_nonce is the initial account nonce. -export.set_init_nonce +pub proc set_init_nonce mem_store.INIT_NONCE_PTR end @@ -481,7 +480,7 @@ end #! #! Where: #! - init_nonce is the initial account nonce. -export.get_init_nonce +pub proc get_init_nonce mem_load.INIT_NONCE_PTR end @@ -492,7 +491,7 @@ end #! #! Where: #! - INIT_NATIVE_ACCOUNT_VAULT_ROOT is the initial vault root of the native account. -export.set_init_native_account_vault_root +pub proc set_init_native_account_vault_root mem_storew_be.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR end @@ -503,7 +502,7 @@ end #! #! Where: #! - INIT_NATIVE_ACCOUNT_VAULT_ROOT is the initial vault root of the native account. -export.get_init_native_account_vault_root +pub proc get_init_native_account_vault_root padw mem_loadw_be.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR end @@ -516,7 +515,7 @@ end #! Where: #! - native_account_initial_vault_root_ptr is the memory pointer to the initial vault root of the #! native account. -export.get_init_native_account_vault_root_ptr +pub proc get_init_native_account_vault_root_ptr push.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR end @@ -527,7 +526,7 @@ end #! #! Where: #! - INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT is the initial storage commitment of the native account. -export.set_init_account_storage_commitment +pub proc set_init_account_storage_commitment mem_storew_be.INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT_PTR end @@ -538,7 +537,7 @@ end #! #! Where: #! - INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT is the initial storage commitment of the native account. -export.get_init_account_storage_commitment +pub proc get_init_account_storage_commitment padw mem_loadw_be.INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT_PTR end @@ -551,7 +550,7 @@ end #! #! Where: #! - INPUT_NOTES_COMMITMENT is the input notes commitment. -export.get_input_notes_commitment +pub proc get_input_notes_commitment padw mem_loadw_be.INPUT_NOTES_COMMITMENT_PTR end @@ -562,7 +561,7 @@ end #! #! Where: #! - INPUT_NOTES_COMMITMENT is the notes' commitment. -export.set_nullifier_commitment +pub proc set_nullifier_commitment mem_storew_be.INPUT_NOTES_COMMITMENT_PTR end @@ -573,7 +572,7 @@ end #! #! Where: #! - tx_script_root_ptr is the pointer to the memory where transaction script root is stored. -export.get_tx_script_root_ptr +pub proc get_tx_script_root_ptr push.TX_SCRIPT_ROOT_PTR end @@ -584,7 +583,7 @@ end #! #! Where: #! - TX_SCRIPT_ROOT is the transaction script root. -export.set_tx_script_root +pub proc set_tx_script_root mem_storew_be.TX_SCRIPT_ROOT_PTR end @@ -596,7 +595,7 @@ end #! Where: #! - TX_SCRIPT_ARGS is the word of values which could be used directly or could be used to obtain #! some values associated with it from the advice map. -export.get_tx_script_args +pub proc get_tx_script_args padw mem_loadw_be.TX_SCRIPT_ARGS_PTR end @@ -608,7 +607,7 @@ end #! Where: #! - TX_SCRIPT_ARGS is the word of values which could be used directly or could be used to obtain #! some values associated with it from the advice map. -export.set_tx_script_args +pub proc set_tx_script_args mem_storew_be.TX_SCRIPT_ARGS_PTR end @@ -619,7 +618,7 @@ end #! #! Where: #! - AUTH_ARGS is the argument passed to the auth procedure. -export.get_auth_args +pub proc get_auth_args padw mem_loadw_be.AUTH_ARGS_PTR end @@ -630,7 +629,7 @@ end #! #! Where: #! - AUTH_ARGS is the argument passed to the auth procedure. -export.set_auth_args +pub proc set_auth_args mem_storew_be.AUTH_ARGS_PTR end @@ -644,7 +643,7 @@ end #! #! Where: #! - ptr is a pointer to the block data section. -export.get_block_data_ptr +pub proc get_block_data_ptr push.BLOCK_DATA_SECTION_OFFSET end @@ -655,7 +654,7 @@ end #! #! Where: #! - PREV_BLOCK_COMMITMENT_PTR is the block commitment of the transaction reference block. -export.get_prev_block_commitment +pub proc get_prev_block_commitment padw mem_loadw_be.PREV_BLOCK_COMMITMENT_PTR end @@ -666,7 +665,7 @@ end #! #! Where: #! - blk_num is the block number of the transaction reference block. -export.get_blk_num +pub proc get_blk_num mem_load.BLOCK_METADATA_PTR end @@ -677,8 +676,8 @@ end #! #! Where: #! - version is the protocol version of the transaction reference block. -const.BLOCK_METADATA_VERSION_PTR=BLOCK_METADATA_PTR+1 -export.get_blk_version +const BLOCK_METADATA_VERSION_PTR=BLOCK_METADATA_PTR+1 +pub proc get_blk_version mem_load.BLOCK_METADATA_VERSION_PTR end @@ -689,8 +688,8 @@ end #! #! Where: #! - timestamp is the timestamp of the reference block for this transaction. -const.BLOCK_METADATA_TIMESTAMP_PTR=BLOCK_METADATA_PTR+2 -export.get_blk_timestamp +const BLOCK_METADATA_TIMESTAMP_PTR=BLOCK_METADATA_PTR+2 +pub proc get_blk_timestamp mem_load.BLOCK_METADATA_TIMESTAMP_PTR end @@ -702,7 +701,7 @@ end #! Where: #! - native_asset_id_{prefix,suffix} are the prefix and suffix felts of the faucet ID that defines #! the native asset. -export.get_native_asset_id +pub proc get_native_asset_id padw mem_loadw_be.FEE_PARAMETERS_PTR drop drop # => [native_asset_id_prefix, native_asset_id_suffix] end @@ -715,7 +714,7 @@ end #! Where: #! - verification_base_fee is the base fee capturing the cost for the verification of a #! transaction. -export.get_verification_base_fee +pub proc get_verification_base_fee mem_load.VERIFICATION_BASE_FEE_PTR end @@ -726,7 +725,7 @@ end #! #! Where: #! - CHAIN_COMMITMENT is the chain commitment of the transaction reference block. -export.get_chain_commitment +pub proc get_chain_commitment padw mem_loadw_be.CHAIN_COMMITMENT_PTR end @@ -737,7 +736,7 @@ end #! #! Where: #! - ACCT_DB_ROOT is the account database root of the transaction reference block. -export.get_account_db_root +pub proc get_account_db_root padw mem_loadw_be.ACCT_DB_ROOT_PTR end @@ -748,7 +747,7 @@ end #! #! Where: #! - NULLIFIER_ROOT is the nullifier root of the transaction reference block. -export.get_nullifier_db_root +pub proc get_nullifier_db_root padw mem_loadw_be.NULLIFIER_ROOT_PTR end @@ -759,7 +758,7 @@ end #! #! Where: #! - TX_COMMITMENT is the tx commitment of the transaction reference block. -export.get_tx_commitment +pub proc get_tx_commitment padw mem_loadw_be.TX_COMMITMENT_PTR end @@ -770,7 +769,7 @@ end #! #! Where: #! - TX_KERNEL_COMMITMENT is the sequential hash of the kernel procedures. -export.get_tx_kernel_commitment +pub proc get_tx_kernel_commitment padw mem_loadw_be.TX_KERNEL_COMMITMENT_PTR end @@ -781,7 +780,7 @@ end #! #! Where: #! - VALIDATOR_KEY_COMMITMENT is the public key commitment of the transaction reference block. -export.get_validator_key_commitment +pub proc get_validator_key_commitment padw mem_loadw_be.VALIDATOR_KEY_COMMITMENT_PTR end @@ -792,7 +791,7 @@ end #! #! Where: #! - NOTE_ROOT is the note root of the transaction reference block. -export.get_note_root +pub proc get_note_root padw mem_loadw_be.NOTE_ROOT_PTR end @@ -803,7 +802,7 @@ end #! #! Where: #! - NOTE_ROOT is the note root of the transaction reference block. -export.set_note_root +pub proc set_note_root mem_storew_be.NOTE_ROOT_PTR end @@ -817,7 +816,7 @@ end #! #! Where: #! - ptr is the pointer to the partial blockchain section. -export.get_partial_blockchain_ptr +pub proc get_partial_blockchain_ptr push.PARTIAL_BLOCKCHAIN_PTR end @@ -828,7 +827,7 @@ end #! #! Where: #! - num_leaves is the number of leaves in the partial blockchain. -export.set_partial_blockchain_num_leaves +pub proc set_partial_blockchain_num_leaves mem_store.PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR end @@ -839,7 +838,7 @@ end #! #! Where: #! - ptr is the pointer to the start of the partial blockchain peaks section. -export.get_partial_blockchain_peaks_ptr +pub proc get_partial_blockchain_peaks_ptr push.PARTIAL_BLOCKCHAIN_PEAKS_PTR end @@ -853,7 +852,7 @@ end #! #! Where: #! - ptr is the memory address at which the native account data is stored. -export.get_native_account_data_ptr +pub proc get_native_account_data_ptr push.NATIVE_ACCOUNT_DATA_PTR end @@ -864,7 +863,7 @@ end #! #! Where: #! - account_data_length is the length of the memory interval that the account data occupies. -export.get_account_data_length +pub proc get_account_data_length push.ACCOUNT_DATA_LENGTH end @@ -876,7 +875,7 @@ end #! Where: #! - max_foreign_account_ptr is the largest memory address which can be used to load the foreign #! account data. -export.get_max_foreign_account_ptr +pub proc get_max_foreign_account_ptr push.MAX_FOREIGN_ACCOUNT_PTR end @@ -884,7 +883,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.set_active_account_data_ptr_to_native_account +pub proc set_active_account_data_ptr_to_native_account # store the native account data pointer into the first account stack element. push.NATIVE_ACCOUNT_DATA_PTR mem_store.MIN_ACCOUNT_STACK_PTR # => [native_acct_stack_ptr, account_stack_top_ptr] @@ -901,7 +900,7 @@ end #! #! Where: #! - active_account_data_ptr is the memory address at which the data of the active account begins. -export.get_active_account_data_ptr +pub proc get_active_account_data_ptr mem_load.ACCOUNT_STACK_TOP_PTR # => [account_stack_top_ptr] @@ -923,7 +922,7 @@ end #! Panics if: #! - the account stack is full, containing 64 accounts total. #! - the provided account data pointer is equal to the native account data pointer. -export.push_ptr_to_account_stack +pub proc push_ptr_to_account_stack # check that the account stack is not full mem_load.ACCOUNT_STACK_TOP_PTR dup # => [account_stack_top_ptr, account_stack_top_ptr, curr_account_data_ptr] @@ -952,7 +951,7 @@ end #! #! Panics if: #! - the account stack contains only native account. -export.pop_ptr_from_account_stack +pub proc pop_ptr_from_account_stack # check that the account stack always is at least of size 1, that is, it contains at least the # native account mem_load.ACCOUNT_STACK_TOP_PTR dup @@ -975,7 +974,7 @@ end #! #! Panics if: #! - the active account data pointer is not equal to native account data pointer (8192). -export.assert_native_account +pub proc assert_native_account exec.is_native_account assert.err=ERR_ACCOUNT_IS_NOT_NATIVE end @@ -984,7 +983,7 @@ end #! #! Inputs: [] #! Outputs: [is_native_account] -export.is_native_account +pub proc is_native_account exec.get_active_account_data_ptr # => [active_account_data_ptr] @@ -996,7 +995,7 @@ end #! #! Inputs: [] #! Outputs: [is_new_account] -export.is_new_account +pub proc is_new_account # the account is new if its nonce is zero # use get_init_nonce so this procedure works correctly even after the account's nonce was # incremented @@ -1010,7 +1009,7 @@ end #! #! Where: #! - ptr is the memory address at which the core account data ends. -export.get_core_account_data_end_ptr +pub proc get_core_account_data_end_ptr exec.get_active_account_data_ptr add.ACCT_CORE_DATA_SECTION_END_OFFSET end @@ -1023,7 +1022,7 @@ end #! #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. -export.get_account_id +pub proc get_account_id padw exec.get_active_account_data_ptr add.ACCT_ID_AND_NONCE_OFFSET mem_loadw_be # => [nonce, 0, account_id_prefix, account_id_suffix] drop drop @@ -1038,7 +1037,7 @@ end #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the native account #! of the transaction. -export.get_native_account_id +pub proc get_native_account_id padw push.NATIVE_ACCOUNT_DATA_PTR add.ACCT_ID_AND_NONCE_OFFSET mem_loadw_be # => [nonce, 0, account_id_prefix, account_id_suffix] drop drop @@ -1053,7 +1052,7 @@ end #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. #! - nonce is the nonce of the active account. -export.set_account_id_and_nonce +pub proc set_account_id_and_nonce exec.get_active_account_data_ptr add.ACCT_ID_AND_NONCE_OFFSET mem_storew_be end @@ -1065,7 +1064,7 @@ end #! #! Where: #! - nonce is the nonce of the active account. -export.get_account_nonce +pub proc get_account_nonce exec.get_active_account_data_ptr add.ACCT_NONCE_OFFSET mem_load end @@ -1077,7 +1076,7 @@ end #! #! Where: #! - nonce is the nonce of the native account of the transaction. -export.get_native_account_nonce +pub proc get_native_account_nonce push.NATIVE_ACCOUNT_DATA_PTR add.ACCT_NONCE_OFFSET mem_load end @@ -1089,7 +1088,7 @@ end #! #! Where: #! - nonce is the nonce of the active account. -export.set_account_nonce +pub proc set_account_nonce exec.get_active_account_data_ptr add.ACCT_ID_AND_NONCE_OFFSET padw # => [0, 0, 0, 0, account_id_and_nonce_ptr, new_nonce] dup.4 mem_loadw_be @@ -1107,7 +1106,7 @@ end #! #! Where: #! - account_vault_root_ptr is the memory pointer to the account asset vault root. -export.get_account_vault_root_ptr +pub proc get_account_vault_root_ptr exec.get_active_account_data_ptr add.ACCT_VAULT_ROOT_OFFSET end @@ -1118,7 +1117,7 @@ end #! #! Where: #! - ACCT_VAULT_ROOT is the account asset vault root. -export.get_account_vault_root +pub proc get_account_vault_root padw exec.get_active_account_data_ptr add.ACCT_VAULT_ROOT_OFFSET mem_loadw_be @@ -1131,7 +1130,7 @@ end #! #! Where: #! - ACCT_VAULT_ROOT is the account vault root to be set. -export.set_account_vault_root +pub proc set_account_vault_root exec.get_active_account_data_ptr add.ACCT_VAULT_ROOT_OFFSET mem_storew_be end @@ -1147,7 +1146,7 @@ end #! #! Where: #! - account_initial_vault_root_ptr is the memory pointer to the initial vault root. -export.get_account_initial_vault_root_ptr +pub proc get_account_initial_vault_root_ptr # For foreign account, use the regular vault root pointer since foreign accounts are read-only # and initial == current exec.get_account_vault_root_ptr @@ -1175,7 +1174,7 @@ end #! #! Where: #! - CODE_COMMITMENT is the code commitment of the account. -export.get_account_code_commitment +pub proc get_account_code_commitment padw exec.get_active_account_data_ptr add.ACCT_CODE_COMMITMENT_OFFSET mem_loadw_be @@ -1188,7 +1187,7 @@ end #! #! Where: #! - CODE_COMMITMENT is the code commitment to be set. -export.set_account_code_commitment +pub proc set_account_code_commitment exec.get_active_account_data_ptr add.ACCT_CODE_COMMITMENT_OFFSET mem_storew_be end @@ -1200,7 +1199,7 @@ end #! #! Where: #! - tx_expiration_block_num is the number of the transaction expiration block. -export.set_expiration_block_num +pub proc set_expiration_block_num mem_store.TX_EXPIRATION_BLOCK_NUM_PTR end @@ -1211,7 +1210,7 @@ end #! #! Where: #! - tx_expiration_block_num is the number of the transaction expiration block. -export.get_expiration_block_num +pub proc get_expiration_block_num mem_load.TX_EXPIRATION_BLOCK_NUM_PTR end @@ -1222,7 +1221,7 @@ end #! #! Where: #! - num_procedures is the number of procedures contained in the account code. -export.get_num_account_procedures +pub proc get_num_account_procedures exec.get_active_account_data_ptr add.NUM_ACCT_PROCEDURES_OFFSET mem_load end @@ -1234,7 +1233,7 @@ end #! #! Where: #! - num_procedures is the number of procedures contained in the account code. -export.set_num_account_procedures +pub proc set_num_account_procedures exec.get_active_account_data_ptr add.NUM_ACCT_PROCEDURES_OFFSET mem_store end @@ -1246,7 +1245,7 @@ end #! #! Where: #! - account_procedures_section_ptr is the memory pointer to the account procedures section. -export.get_account_procedures_section_ptr +pub proc get_account_procedures_section_ptr exec.get_active_account_data_ptr add.ACCT_PROCEDURES_SECTION_OFFSET end @@ -1257,7 +1256,7 @@ end #! #! Where: #! - procedures_call_tracking_ptr is the memory pointer to the procedure call tracking section. -export.get_account_procedures_call_tracking_ptr +pub proc get_account_procedures_call_tracking_ptr exec.get_active_account_data_ptr add.ACCT_PROCEDURES_CALL_TRACKING_OFFSET end @@ -1269,7 +1268,7 @@ end #! Where: #! - proc_idx is the index of the account procedure. #! - proc_ptr is the memory pointer to the account procedure at the specified index. -export.get_account_procedure_ptr +pub proc get_account_procedure_ptr mul.ACCOUNT_PROCEDURE_DATA_LENGTH exec.get_account_procedures_section_ptr add end @@ -1282,7 +1281,7 @@ end #! #! Where: #! - STORAGE_COMMITMENT is the account storage commitment. -export.get_account_storage_commitment +pub proc get_account_storage_commitment padw exec.get_active_account_data_ptr add.ACCT_STORAGE_COMMITMENT_OFFSET mem_loadw_be @@ -1295,7 +1294,7 @@ end #! #! Where: #! - STORAGE_COMMITMENT is the account storage commitment. -export.set_account_storage_commitment +pub proc set_account_storage_commitment exec.get_active_account_data_ptr add.ACCT_STORAGE_COMMITMENT_OFFSET mem_storew_be end @@ -1310,7 +1309,7 @@ end #! #! Where: #! - dirty_flag is the flag indicating whether the storage commitment is outdated. -export.set_native_account_storage_commitment_dirty_flag +pub proc set_native_account_storage_commitment_dirty_flag push.NATIVE_ACCT_STORAGE_COMMITMENT_DIRTY_FLAG_PTR mem_store # => [] end @@ -1325,7 +1324,7 @@ end #! Where: #! - should_recompute_storage_commitment is the flag indicating whether the storage commitment #! should be recomputed. -export.get_recompute_storage_commitment_flag +pub proc get_recompute_storage_commitment_flag # get the is_native_account flag exec.is_native_account # => [is_native_account] @@ -1346,7 +1345,7 @@ end #! #! Where: #! - num_storage_slots is the number of storage slots contained in the account storage. -export.get_num_storage_slots +pub proc get_num_storage_slots exec.get_active_account_data_ptr add.NUM_ACCT_STORAGE_SLOTS_OFFSET mem_load end @@ -1358,7 +1357,7 @@ end #! #! Where: #! - num_storage_slots is the number of storage slots contained in the account storage. -export.set_num_storage_slots +pub proc set_num_storage_slots exec.get_active_account_data_ptr add.NUM_ACCT_STORAGE_SLOTS_OFFSET mem_store end @@ -1370,7 +1369,7 @@ end #! #! Where: #! - storage_slots_section_ptr is the memory pointer to the account storage slots section. -export.get_account_storage_slots_section_ptr +pub proc get_account_storage_slots_section_ptr exec.get_active_account_data_ptr add.ACCT_STORAGE_SLOTS_SECTION_OFFSET end @@ -1381,7 +1380,7 @@ end #! #! Where: #! - storage_slots_section_ptr is the memory pointer to the native account's storage slots section. -export.get_native_account_storage_slots_ptr +pub proc get_native_account_storage_slots_ptr exec.get_native_account_data_ptr add.ACCT_STORAGE_SLOTS_SECTION_OFFSET end @@ -1392,7 +1391,7 @@ end #! #! Where: #! - account_initial_storage_slots_ptr is the memory pointer to the initial storage slot values. -export.get_native_account_initial_storage_slots_ptr +pub proc get_native_account_initial_storage_slots_ptr exec.get_native_account_data_ptr add.ACCT_INITIAL_STORAGE_SLOTS_SECTION_OFFSET end @@ -1407,7 +1406,7 @@ end #! #! Where: #! - account_initial_storage_slots_ptr is the memory pointer to the initial storage slot values. -export.get_account_initial_storage_slots_ptr +pub proc get_account_initial_storage_slots_ptr # For foreign account, use the regular storage slots pointer since foreign accounts are # read-only and initial == current exec.get_account_storage_slots_section_ptr @@ -1435,7 +1434,7 @@ end #! #! Where: #! - account_delta_fungible_asset_ptr is the link map pointer to the fungible asset vault delta. -export.get_account_delta_fungible_asset_ptr +pub proc get_account_delta_fungible_asset_ptr push.ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR end @@ -1446,7 +1445,7 @@ end #! #! Where: #! - account_delta_non_fungible_asset_ptr is the link map pointer to the non-fungible asset vault delta. -export.get_account_delta_non_fungible_asset_ptr +pub proc get_account_delta_non_fungible_asset_ptr push.ACCOUNT_DELTA_NON_FUNGIBLE_ASSET_PTR end @@ -1458,7 +1457,7 @@ end #! Where: #! - account_delta_storage_map_ptr is the link map pointer to the storage map delta for the #! requested slot index. -export.get_account_delta_storage_map_ptr +pub proc get_account_delta_storage_map_ptr add.ACCOUNT_DELTA_STORAGE_MAP_SECTION end @@ -1466,7 +1465,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.mem_copy_native_account_initial_storage_slots +pub proc mem_copy_native_account_initial_storage_slots exec.get_native_account_initial_storage_slots_ptr exec.get_native_account_storage_slots_ptr # => [storage_slots_section_ptr, initial_storage_slots_ptr] @@ -1489,7 +1488,7 @@ end #! #! Where: #! - num_input_notes is the total number of input notes consumed by this transaction. -export.get_num_input_notes +pub proc get_num_input_notes mem_load.NUM_INPUT_NOTES_PTR end @@ -1500,7 +1499,7 @@ end #! #! Where: #! - num_input_notes is the total number of input notes consumed by this transaction. -export.set_num_input_notes +pub proc set_num_input_notes mem_store.NUM_INPUT_NOTES_PTR end @@ -1513,7 +1512,7 @@ end #! Where: #! - idx is the index of the input note. #! - note_ptr is the memory address of the data segment for the input note with `idx`. -export.get_input_note_ptr +pub proc get_input_note_ptr exec.constants::get_note_mem_size mul add.INPUT_NOTE_DATA_SECTION_OFFSET end @@ -1525,7 +1524,7 @@ end #! Where: #! - note_ptr is the input note's the memory address. #! - NOTE_ID is the note's id. -export.set_input_note_id +pub proc set_input_note_id mem_storew_be end @@ -1538,7 +1537,7 @@ end #! Where: #! - idx is the index of the input note. #! - nullifier_ptr is the memory address of the nullifier for note idx. -export.get_input_note_nullifier_ptr +pub proc get_input_note_nullifier_ptr mul.4 add.INPUT_NOTE_NULLIFIER_SECTION_PTR end @@ -1550,7 +1549,7 @@ end #! Where: #! - idx is the index of the input note. #! - nullifier is the nullifier of the input note. -export.get_input_note_nullifier +pub proc get_input_note_nullifier mul.4 padw movup.4 add.INPUT_NOTE_NULLIFIER_SECTION_PTR mem_loadw_be end @@ -1563,7 +1562,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - note_data_ptr is the memory address at which the input note core data begins. -export.get_input_note_core_ptr +pub proc get_input_note_core_ptr add.INPUT_NOTE_CORE_DATA_OFFSET end @@ -1575,7 +1574,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - SCRIPT_ROOT is the script root of the input note. -export.get_input_note_script_root +pub proc get_input_note_script_root padw movup.4 add.INPUT_NOTE_SCRIPT_ROOT_OFFSET mem_loadw_be @@ -1589,7 +1588,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - script_root_ptr is the memory address where script root of the input note is stored. -export.get_input_note_script_root_ptr +pub proc get_input_note_script_root_ptr add.INPUT_NOTE_SCRIPT_ROOT_OFFSET end @@ -1601,7 +1600,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - INPUTS_COMMITMENT is the inputs commitment of the input note. -export.get_input_note_inputs_commitment +pub proc get_input_note_inputs_commitment padw movup.4 add.INPUT_NOTE_INPUTS_COMMITMENT_OFFSET mem_loadw_be @@ -1615,7 +1614,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - METADATA is the metadata of the input note. -export.get_input_note_metadata +pub proc get_input_note_metadata padw movup.4 add.INPUT_NOTE_METADATA_OFFSET mem_loadw_be @@ -1629,7 +1628,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - NOTE_METADATA is the metadata of the input note. -export.set_input_note_metadata +pub proc set_input_note_metadata add.INPUT_NOTE_METADATA_OFFSET mem_storew_be end @@ -1642,7 +1641,7 @@ end #! Where: #! - note_ptr is the start memory address of the note. #! - NOTE_ARGS are the note's args. -export.get_input_note_args +pub proc get_input_note_args padw movup.4 add.INPUT_NOTE_ARGS_OFFSET mem_loadw_be @@ -1656,7 +1655,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - NOTE_ARGS are optional note args of the input note. -export.set_input_note_args +pub proc set_input_note_args add.INPUT_NOTE_ARGS_OFFSET mem_storew_be end @@ -1669,7 +1668,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - num_inputs is the number of inputs in in the input note. -export.get_input_note_num_inputs +pub proc get_input_note_num_inputs add.INPUT_NOTE_NUM_INPUTS_OFFSET mem_load end @@ -1682,7 +1681,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - num_inputs is the number of inputs in the input note. -export.set_input_note_num_inputs +pub proc set_input_note_num_inputs add.INPUT_NOTE_NUM_INPUTS_OFFSET mem_store end @@ -1695,7 +1694,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - num_assets is the number of assets in the input note. -export.get_input_note_num_assets +pub proc get_input_note_num_assets add.INPUT_NOTE_NUM_ASSETS_OFFSET mem_load end @@ -1708,7 +1707,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - num_assets is the number of assets in the input note. -export.set_input_note_num_assets +pub proc set_input_note_num_assets add.INPUT_NOTE_NUM_ASSETS_OFFSET mem_store end @@ -1722,7 +1721,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - assets_ptr is the memory address at which the assets segment for the input note begins. -export.get_input_note_assets_ptr +pub proc get_input_note_assets_ptr add.INPUT_NOTE_ASSETS_OFFSET end @@ -1734,7 +1733,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - RECIPIENT is the commitment to the note's script, inputs and the serial number. -export.get_input_note_recipient +pub proc get_input_note_recipient padw movup.4 add.INPUT_NOTE_RECIPIENT_OFFSET mem_loadw_be @@ -1748,7 +1747,7 @@ end #! Where: #! - note_ptr is the memory address at which the output note data begins. #! - RECIPIENT is the commitment to the note's script, inputs and the serial number. -export.set_input_note_recipient +pub proc set_input_note_recipient add.INPUT_NOTE_RECIPIENT_OFFSET mem_storew_be end @@ -1761,7 +1760,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - ASSET_COMMITMENT is the sequential hash of the padded assets of an input note. -export.get_input_note_assets_commitment +pub proc get_input_note_assets_commitment padw movup.4 add.INPUT_NOTE_ASSETS_COMMITMENT_OFFSET mem_loadw_be @@ -1775,7 +1774,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - SERIAL_NUMBER is the input note's serial number. -export.get_input_note_serial_num +pub proc get_input_note_serial_num padw movup.4 add.INPUT_NOTE_SERIAL_NUM_OFFSET mem_loadw_be @@ -1789,7 +1788,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - sender is the sender for the input note. -export.get_input_note_sender +pub proc get_input_note_sender padw movup.4 add.INPUT_NOTE_METADATA_OFFSET mem_loadw_be @@ -1821,7 +1820,7 @@ end #! #! Where: #! - offset is the offset of the output note data segment. -export.get_output_note_data_offset +pub proc get_output_note_data_offset push.OUTPUT_NOTE_SECTION_OFFSET end @@ -1834,7 +1833,7 @@ end #! Where: #! - i is the index of the output note. #! - ptr is the memory address of the data segment for output note i. -export.get_output_note_ptr +pub proc get_output_note_ptr exec.constants::get_note_mem_size mul add.OUTPUT_NOTE_SECTION_OFFSET end @@ -1846,7 +1845,7 @@ end #! Where: #! - note_ptr is the memory address at which the output note data begins. #! - RECIPIENT is the commitment to the note's script, inputs and the serial number. -export.get_output_note_recipient +pub proc get_output_note_recipient padw movup.4 add.OUTPUT_NOTE_RECIPIENT_OFFSET mem_loadw_be @@ -1860,7 +1859,7 @@ end #! Where: #! - note_ptr is the memory address at which the output note data begins. #! - RECIPIENT is the commitment to the note's script, inputs and the serial number. -export.set_output_note_recipient +pub proc set_output_note_recipient add.OUTPUT_NOTE_RECIPIENT_OFFSET mem_storew_be end @@ -1873,7 +1872,7 @@ end #! Where: #! - METADATA is the note metadata. #! - note_ptr is the memory address at which the output note data begins. -export.get_output_note_metadata +pub proc get_output_note_metadata padw # => [0, 0, 0, 0, note_ptr] movup.4 add.OUTPUT_NOTE_METADATA_OFFSET @@ -1890,7 +1889,7 @@ end #! Where: #! - METADATA is the note metadata. #! - note_ptr is the memory address at which the output note data begins. -export.set_output_note_metadata +pub proc set_output_note_metadata add.OUTPUT_NOTE_METADATA_OFFSET mem_storew_be end @@ -1903,7 +1902,7 @@ end #! Where: #! - note_ptr is a pointer to the memory address at which the output note is stored. #! - num_assets is the number of assets in the output note. -export.get_output_note_num_assets +pub proc get_output_note_num_assets add.OUTPUT_NOTE_NUM_ASSETS_OFFSET mem_load end @@ -1918,7 +1917,7 @@ end #! #! Panics if: #! - the number of assets exceeds the maximum allowed number of assets per note. -export.set_output_note_num_assets +pub proc set_output_note_num_assets add.OUTPUT_NOTE_NUM_ASSETS_OFFSET # => [note_ptr + offset, num_assets] @@ -1939,7 +1938,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - dirty_flag is the flag indicating whether the assets commitment is outdated. -export.get_output_note_dirty_flag +pub proc get_output_note_dirty_flag add.OUTPUT_NOTE_DIRTY_FLAG_OFFSET mem_load end @@ -1954,7 +1953,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - dirty_flag is the flag indicating whether the assets commitment is outdated. -export.set_output_note_dirty_flag +pub proc set_output_note_dirty_flag add.OUTPUT_NOTE_DIRTY_FLAG_OFFSET mem_store end @@ -1966,7 +1965,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - asset_data_ptr is the memory address at which the output note asset data begins. -export.get_output_note_asset_data_ptr +pub proc get_output_note_asset_data_ptr add.OUTPUT_NOTE_ASSETS_OFFSET end @@ -1978,7 +1977,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - ASSETS_COMMITMENT is the sequential hash of the padded assets of an output note. -export.get_output_note_assets_commitment +pub proc get_output_note_assets_commitment padw movup.4 add.OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET mem_loadw_be @@ -1993,7 +1992,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - ASSETS_COMMITMENT is the sequential hash of the padded assets of an output note. -export.set_output_note_assets_commitment +pub proc set_output_note_assets_commitment add.OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET mem_storew_be end @@ -2008,7 +2007,7 @@ end #! #! Where: #! - num_kernel_procedures is the number of the procedures of the selected kernel. -export.set_num_kernel_procedures +pub proc set_num_kernel_procedures mem_store.NUM_KERNEL_PROCEDURES_PTR end @@ -2019,7 +2018,7 @@ end #! #! Where: #! - num_kernel_procedures is the number of the procedures of the selected kernel. -export.get_num_kernel_procedures +pub proc get_num_kernel_procedures mem_load.NUM_KERNEL_PROCEDURES_PTR end @@ -2031,7 +2030,7 @@ end #! Where: #! - kernel_procedures_ptr is the memory address where the hashes of the kernel procedures are #! stored. -export.get_kernel_procedures_ptr +pub proc get_kernel_procedures_ptr push.KERNEL_PROCEDURES_PTR end @@ -2042,7 +2041,7 @@ end #! #! Inputs: [] #! Outputs: [start_ptr] -export.get_link_map_region_start_ptr +pub proc get_link_map_region_start_ptr push.LINK_MAP_REGION_START_PTR end @@ -2050,7 +2049,7 @@ end #! #! Inputs: [] #! Outputs: [end_ptr] -export.get_link_map_region_end_ptr +pub proc get_link_map_region_end_ptr push.LINK_MAP_REGION_END_PTR end @@ -2058,7 +2057,7 @@ end #! #! Inputs: [] #! Outputs: [entry_size] -export.get_link_map_entry_size +pub proc get_link_map_entry_size push.LINK_MAP_ENTRY_SIZE end @@ -2069,7 +2068,7 @@ end #! #! Panics if: #! - the allocation exceeds the maximum possible number of link map entries. -export.link_map_malloc +pub proc link_map_malloc # retrieve the current memory size mem_load.LINK_MAP_USED_MEMORY_SIZE dup # => [current_mem_size, current_mem_size] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/note.masm b/crates/miden-lib/asm/kernels/transaction/lib/note.masm index b884a3728f..a1917db667 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/note.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/note.masm @@ -1,19 +1,19 @@ -use.std::crypto::hashes::rpo +use miden::core::crypto::hashes::rpo256 -use.$kernel::constants -use.$kernel::memory +use $kernel::constants +use $kernel::memory # ERRORS # ================================================================================================= -const.ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255" +const ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255" # CONSTANTS # ================================================================================================= # The diff between the memory address after first mem_stream operation and the next target when # generating the output notes commitment. Must be NOTE_MEM_SIZE - 8; -const.OUTPUT_NOTE_HASHING_MEM_DIFF=2040 +const OUTPUT_NOTE_HASHING_MEM_DIFF=2040 # ACTIVE NOTE PROCEDURES # ================================================================================================= @@ -25,7 +25,7 @@ const.OUTPUT_NOTE_HASHING_MEM_DIFF=2040 #! #! Where: #! - active_input_note_ptr is the pointer to the next note to be processed. -export.increment_active_input_note_ptr +pub proc increment_active_input_note_ptr # get the active input note pointer exec.memory::get_active_input_note_ptr # => [orig_input_note_ptr] @@ -44,7 +44,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.note_processing_teardown +pub proc note_processing_teardown # set the active input note pointer to 0 push.0 exec.memory::set_active_input_note_ptr # => [] @@ -60,7 +60,7 @@ end #! Where: #! - note_script_root_ptr is the memory address where note's script root is stored. #! - NOTE_ARGS is the note's arguments. -export.prepare_note +pub proc prepare_note padw padw push.0.0.0 # => [pad(11)] @@ -92,7 +92,7 @@ end #! Where: #! - note_data_ptr is a pointer to the data section of the output note. #! - ASSETS_COMMITMENT is the commitment of the assets of the output note located at note_data_ptr. -export.compute_output_note_assets_commitment +pub proc compute_output_note_assets_commitment # get the assets commitment dirty flag and decide whether we need to recompute the commitment dup exec.memory::get_output_note_dirty_flag # => [dirty_flag, note_data_ptr] @@ -138,7 +138,7 @@ export.compute_output_note_assets_commitment end # extract digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [ASSETS_COMMITMENT, asset_data_ptr, asset_counter, num_asset_pairs, note_data_ptr] # drop accessory variables from stack @@ -176,7 +176,7 @@ end #! Where: #! - note_data_ptr is a pointer to the data section of the output note. #! - NOTE_ID is the ID of the output note located at note_data_ptr. -proc.compute_output_note_id +proc compute_output_note_id # pad capacity elements of hasher padw # => [EMPTY_WORD, note_data_ptr] @@ -190,7 +190,7 @@ proc.compute_output_note_id # => [ASSETS_COMMITMENT, RECIPIENT, EMPTY_WORD, note_data_ptr] # compute output note commitment (which is also note ID) and extract digest - hperm exec.rpo::squeeze_digest + hperm exec.rpo256::squeeze_digest # => [NOTE_ID, note_data_ptr] # save the output note commitment (note ID) to memory @@ -206,7 +206,7 @@ end #! #! Where: #! - OUTPUT_NOTES_COMMITMENT is the commitment to the notes output by the transaction. -export.compute_output_notes_commitment +pub proc compute_output_notes_commitment # get the number of output notes from memory exec.memory::get_num_output_notes # => [num_notes, ...] @@ -251,7 +251,7 @@ export.compute_output_notes_commitment end # extract digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [OUTPUT_NOTES_COMMITMENT, end_ptr, end_ptr, ...] # drop accessory variables from stack diff --git a/crates/miden-lib/asm/kernels/transaction/lib/output_note.masm b/crates/miden-lib/asm/kernels/transaction/lib/output_note.masm index 902ea9768f..b9c66a633e 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/output_note.masm @@ -1,35 +1,35 @@ -use.$kernel::account -use.$kernel::memory -use.$kernel::note -use.$kernel::asset -use.$kernel::constants -use.std::word +use $kernel::account +use $kernel::memory +use $kernel::note +use $kernel::asset +use $kernel::constants +use miden::core::word # CONSTANTS # ================================================================================================= # Constants for different note types -const.PUBLIC_NOTE=1 # 0b01 -const.PRIVATE_NOTE=2 # 0b10 -const.ENCRYPTED_NOTE=3 # 0b11 +const PUBLIC_NOTE=1 # 0b01 +const PRIVATE_NOTE=2 # 0b10 +const ENCRYPTED_NOTE=3 # 0b11 # The note type must be PUBLIC, unless the high bits are `0b11`. (See the table below.) -const.LOCAL_ANY_PREFIX=3 # 0b11 +const LOCAL_ANY_PREFIX=3 # 0b11 # ERRORS # ================================================================================================= -const.ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT="number of output notes in the transaction exceeds the maximum limit of 1024" +const ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT="number of output notes in the transaction exceeds the maximum limit of 1024" -const.ERR_NOTE_INVALID_TYPE="invalid note type" +const ERR_NOTE_INVALID_TYPE="invalid note type" -const.ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested output note index should be less than the total number of created output notes" +const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested output note index should be less than the total number of created output notes" -const.ERR_NOTE_INVALID_INDEX="failed to find note at the given index; index must be within [0, num_of_notes]" +const ERR_NOTE_INVALID_INDEX="failed to find note at the given index; index must be within [0, num_of_notes]" -const.ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807" +const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807" -const.ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="non-fungible asset that already exists in the note cannot be added again" +const ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="non-fungible asset that already exists in the note cannot be added again" # The 2 highest bits in the u32 tag have the following meaning: # @@ -51,22 +51,22 @@ const.ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="non-fungible asset that already exi # users won't see the note 2. generate slightly more load as extra validation is performed for the # invalid tags. None of these scenarios have any significant impact. -const.ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX="invalid note type for the given note tag prefix" +const ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX="invalid note type for the given note tag prefix" -const.ERR_NOTE_TAG_MUST_BE_U32="the note's tag must fit into a u32 so the 32 most significant bits must be zero" +const ERR_NOTE_TAG_MUST_BE_U32="the note's tag must fit into a u32 so the 32 most significant bits must be zero" # EVENTS # ================================================================================================= # Event emitted before a new note is created. -const.NOTE_BEFORE_CREATED_EVENT=event("miden::note::before_created") +const NOTE_BEFORE_CREATED_EVENT=event("miden::note::before_created") # Event emitted after a new note is created. -const.NOTE_AFTER_CREATED_EVENT=event("miden::note::after_created") +const NOTE_AFTER_CREATED_EVENT=event("miden::note::after_created") # Event emitted before an ASSET is added to a note -const.NOTE_BEFORE_ADD_ASSET_EVENT=event("miden::note::before_add_asset") +const NOTE_BEFORE_ADD_ASSET_EVENT=event("miden::note::before_add_asset") # Event emitted after an ASSET is added to a note -const.NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") +const NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") # OUTPUT NOTE PROCEDURES # ================================================================================================= @@ -90,7 +90,7 @@ const.NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") #! - the note_tag is not an u32. #! - the note_tag starts with anything but 0b11 and note_type is not public. #! - the number of output notes exceeds the maximum limit of 1024. -export.create +pub proc create emit.NOTE_BEFORE_CREATED_EVENT exec.build_metadata @@ -132,7 +132,7 @@ end #! - note_index is the index of the output note whose assets info should be returned. #! - num_assets is the number of assets in the specified note. #! - ASSETS_COMMITMENT is a sequential hash of the assets in the specified note. -export.get_assets_info +pub proc get_assets_info # get the note data pointer based on the index of the requested note exec.memory::get_output_note_ptr # => [note_data_ptr] @@ -188,7 +188,7 @@ end #! - the max amount of fungible assets is exceeded. #! - the non-fungible asset already exists in the note. #! - the total number of ASSETs exceeds the maximum of 256. -export.add_asset +pub proc add_asset # check if the note exists, it must be within [0, num_of_notes] dup exec.memory::get_num_output_notes lte assert.err=ERR_NOTE_INVALID_INDEX # => [note_idx, ASSET] @@ -242,7 +242,7 @@ end #! #! Inputs: [note_index] #! Outputs: [note_index] -export.assert_note_index_in_bounds +pub proc assert_note_index_in_bounds # assert that the provided note index is less than the total number of notes dup exec.memory::get_num_output_notes # => [output_notes_num, note_index, note_index] @@ -270,7 +270,7 @@ end #! or off-chain). #! - execution_hint is the hint which specifies when a note is ready to be consumed. #! - NOTE_METADATA is the metadata associated with a note. -export.build_metadata +pub proc build_metadata # Validate the note type. # -------------------------------------------------------------------------------------------- @@ -366,7 +366,7 @@ end #! #! Where: #! - note_idx is the index of the next note to be created. -proc.increment_num_output_notes +proc increment_num_output_notes # get the current number of output notes exec.memory::get_num_output_notes # => [note_idx] @@ -397,7 +397,7 @@ end #! #! Panics if #! - the summed amounts exceed the maximum amount of fungible assets. -proc.add_fungible_asset +proc add_fungible_asset dup.4 exec.memory::get_output_note_asset_data_ptr # => [asset_ptr, ASSET, note_ptr, num_of_assets, note_idx] @@ -487,7 +487,7 @@ end #! #! Panics if: #! - the non-fungible asset already exists in the note. -proc.add_non_fungible_asset +proc add_non_fungible_asset dup.4 exec.memory::get_output_note_asset_data_ptr # => [asset_ptr, ASSET, note_ptr, num_of_assets, note_idx] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm index c3226643a8..9dc1a86926 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -1,71 +1,71 @@ -use.std::mem -use.std::collections::mmr -use.std::crypto::hashes::rpo -use.std::word - -use.$kernel::account -use.$kernel::account_delta -use.$kernel::account_id -use.$kernel::asset_vault -use.$kernel::constants -use.$kernel::memory +use miden::core::mem +use miden::core::collections::mmr +use miden::core::crypto::hashes::rpo256 +use miden::core::word + +use $kernel::account +use $kernel::account_delta +use $kernel::account_id +use $kernel::asset_vault +use $kernel::constants +use $kernel::memory # CONSTS # ================================================================================================= # Max U32 value, used for initializing the expiration block number -const.MAX_BLOCK_NUM=0xFFFFFFFF +const MAX_BLOCK_NUM=0xFFFFFFFF # EVENTS #================================================================================================= # Emission of an equivalent to `ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT`, use in `add_input_note_assets_to_vault` -const.ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT=event("miden::account::vault_before_add_asset") +const ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT=event("miden::account::vault_before_add_asset") # ERRORS # ================================================================================================= -const.ERR_PROLOGUE_GLOBAL_INPUTS_PROVIDED_DO_NOT_MATCH_BLOCK_COMMITMENT="the provided global inputs do not match the block commitment" +const ERR_PROLOGUE_GLOBAL_INPUTS_PROVIDED_DO_NOT_MATCH_BLOCK_COMMITMENT="the provided global inputs do not match the block commitment" -const.ERR_PROLOGUE_GLOBAL_INPUTS_PROVIDED_DO_NOT_MATCH_BLOCK_NUMBER_COMMITMENT="the provided global inputs do not match the block number commitment" +const ERR_PROLOGUE_GLOBAL_INPUTS_PROVIDED_DO_NOT_MATCH_BLOCK_NUMBER_COMMITMENT="the provided global inputs do not match the block number commitment" -const.ERR_PROLOGUE_NATIVE_ASSET_ID_IS_NOT_FUNGIBLE="native asset account ID in reference block is not of type fungible faucet" +const ERR_PROLOGUE_NATIVE_ASSET_ID_IS_NOT_FUNGIBLE="native asset account ID in reference block is not of type fungible faucet" -const.ERR_PROLOGUE_VERIFICATION_BASE_FEE_MUST_BE_U32="verification base fee must fit into a u32" +const ERR_PROLOGUE_VERIFICATION_BASE_FEE_MUST_BE_U32="verification base fee must fit into a u32" -const.ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" +const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" -const.ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY="reserved slot for new fungible faucet is not empty" +const ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY="reserved slot for new fungible faucet is not empty" -const.ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE="reserved slot for new fungible faucet has an invalid type" +const ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE="reserved slot for new fungible faucet has an invalid type" -const.ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT="reserved slot for non-fungible faucet is not a valid empty SMT" +const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT="reserved slot for non-fungible faucet is not a valid empty SMT" -const.ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE="reserved slot for new non-fungible faucet has an invalid type" +const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE="reserved slot for new non-fungible faucet has an invalid type" -const.ERR_PROLOGUE_PROVIDED_ACCOUNT_DATA_DOES_NOT_MATCH_ON_CHAIN_COMMITMENT="account data provided does not match the commitment recorded on-chain" +const ERR_PROLOGUE_PROVIDED_ACCOUNT_DATA_DOES_NOT_MATCH_ON_CHAIN_COMMITMENT="account data provided does not match the commitment recorded on-chain" -const.ERR_PROLOGUE_EXISTING_ACCOUNT_MUST_HAVE_NON_ZERO_NONCE="existing accounts must have a non-zero nonce" +const ERR_PROLOGUE_EXISTING_ACCOUNT_MUST_HAVE_NON_ZERO_NONCE="existing accounts must have a non-zero nonce" -const.ERR_PROLOGUE_MISMATCH_OF_ACCOUNT_IDS_FROM_GLOBAL_INPUTS_AND_ADVICE_PROVIDER="account IDs provided via global inputs and advice provider do not match" +const ERR_PROLOGUE_MISMATCH_OF_ACCOUNT_IDS_FROM_GLOBAL_INPUTS_AND_ADVICE_PROVIDER="account IDs provided via global inputs and advice provider do not match" -const.ERR_PROLOGUE_MISMATCH_OF_REFERENCE_BLOCK_MMR_AND_NOTE_AUTHENTICATION_MMR="reference block MMR and note's authentication MMR must match" +const ERR_PROLOGUE_MISMATCH_OF_REFERENCE_BLOCK_MMR_AND_NOTE_AUTHENTICATION_MMR="reference block MMR and note's authentication MMR must match" -const.ERR_PROLOGUE_NUMBER_OF_NOTE_ASSETS_EXCEEDS_LIMIT="number of note assets exceeds the maximum limit of 256" +const ERR_PROLOGUE_NUMBER_OF_NOTE_ASSETS_EXCEEDS_LIMIT="number of note assets exceeds the maximum limit of 256" -const.ERR_PROLOGUE_PROVIDED_INPUT_ASSETS_INFO_DOES_NOT_MATCH_ITS_COMMITMENT="provided info about assets of an input does not match its commitment" +const ERR_PROLOGUE_PROVIDED_INPUT_ASSETS_INFO_DOES_NOT_MATCH_ITS_COMMITMENT="provided info about assets of an input does not match its commitment" -const.ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT="number of input notes exceeds the kernel's maximum limit of 1024" +const ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT="number of input notes exceeds the kernel's maximum limit of 1024" -const.ERR_PROLOGUE_INPUT_NOTES_COMMITMENT_MISMATCH="note commitment computed from the input note data does not match given note commitment" +const ERR_PROLOGUE_INPUT_NOTES_COMMITMENT_MISMATCH="note commitment computed from the input note data does not match given note commitment" -const.ERR_PROLOGUE_NEW_ACCOUNT_NONCE_MUST_BE_ZERO="new account must have a zero nonce" +const ERR_PROLOGUE_NEW_ACCOUNT_NONCE_MUST_BE_ZERO="new account must have a zero nonce" -const.ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" +const ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" -const.ERR_PROLOGUE_NOTE_AUTHENTICATION_FAILED="failed to authenticate note inclusion in block" +const ERR_PROLOGUE_NOTE_AUTHENTICATION_FAILED="failed to authenticate note inclusion in block" -const.ERR_PROLOGUE_KERNEL_PROCEDURE_COMMITMENT_MISMATCH="sequential hash over kernel procedures does not match kernel commitment from block" +const ERR_PROLOGUE_KERNEL_PROCEDURE_COMMITMENT_MISMATCH="sequential hash over kernel procedures does not match kernel commitment from block" # PUBLIC INPUTS # ================================================================================================= @@ -89,7 +89,7 @@ const.ERR_PROLOGUE_KERNEL_PROCEDURE_COMMITMENT_MISMATCH="sequential hash over ke #! accounts. #! - INPUT_NOTES_COMMITMENT is the commitment to the input notes. See the #! `tx_get_input_notes_commitment` kernel procedure for details. -proc.process_global_inputs +proc process_global_inputs exec.memory::set_block_commitment dropw exec.memory::set_init_account_commitment dropw exec.memory::set_nullifier_commitment dropw @@ -114,7 +114,7 @@ end #! Where: #! - TX_KERNEL_COMMITMENT is the sequential hash of the kernel procedures. #! - [KERNEL_PROCEDURE_ROOTS] is the array of the kernel procedure roots. -proc.process_kernel_data +proc process_kernel_data # load the transaction kernel commitment from memory exec.memory::get_tx_kernel_commitment # OS => [TX_KERNEL_COMMITMENT] @@ -147,7 +147,7 @@ proc.process_kernel_data # AS => [] # extract the resulting hash - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # OS => [SEQ_KERNEL_PROC_HASH, kernel_procs_ptr', TX_KERNEL_COMMITMENT] # AS => [] @@ -197,7 +197,7 @@ end #! - verification_base_fee is the base fee capturing the cost for the verification of a #! transaction. #! - NOTE_ROOT is the root of the tree with all notes created in the block. -proc.process_block_data +proc process_block_data exec.memory::get_block_data_ptr # => [block_data_ptr, block_num] @@ -209,7 +209,7 @@ proc.process_block_data adv_pipe hperm adv_pipe hperm adv_pipe hperm - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [SUB_COMMITMENT, block_data_ptr', block_num] # store the note root in memory @@ -258,7 +258,7 @@ end #! - PARTIAL_BLOCKCHAIN_COMMITMENT is the sequential hash of the padded MMR peaks. #! - num_blocks is the number of blocks in the MMR. #! - PEAK_1 .. PEAK_N are the MMR peaks. -proc.process_chain_data +proc process_chain_data exec.memory::get_partial_blockchain_ptr dup # => [partial_blockchain_ptr, partial_blockchain_ptr] @@ -291,7 +291,7 @@ end #! #! Inputs: [] #! Outputs: [] -proc.validate_new_account +proc validate_new_account # Assert the account ID of the account is valid exec.memory::get_account_id exec.account_id::validate # => [] @@ -397,7 +397,7 @@ end #! - ACCOUNT_VAULT_ROOT is the account's vault root. #! - ACCOUNT_STORAGE_COMMITMENT is the account's storage commitment. #! - ACCOUNT_CODE_COMMITMENT is the account's code commitment. -proc.process_account_data +proc process_account_data # Initialize the active account data pointer in the bookkeeping section with the native offset # (2048) exec.memory::set_active_account_data_ptr_to_native_account @@ -412,7 +412,7 @@ proc.process_account_data padw padw padw adv_pipe hperm adv_pipe hperm - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [ACCOUNT_COMMITMENT, acct_data_ptr'] movup.4 drop @@ -518,7 +518,8 @@ end #! - BLOCK_SUB_COMMITMENT is the sub_commitment of the block which created the input note. #! - NOTE_ROOT is the merkle root of the notes tree containing the input note. #! - note_index is the input note's position in the notes tree. -proc.authenticate_note.8 +@locals(8) +proc authenticate_note # Load the BLOCK_COMMITMENT from the PARTIAL_BLOCKCHAIN # --------------------------------------------------------------------------------------------- @@ -592,7 +593,7 @@ end #! - ASSETS_COMMITMENT is the sequential hash of the padded note's assets. #! - NULLIFIER is the result of #! `hash(SERIAL_NUMBER || SCRIPT_ROOT || INPUTS_COMMITMENT || ASSETS_COMMITMENT)`. -proc.process_input_note_details +proc process_input_note_details exec.memory::get_input_note_core_ptr # => [note_data_ptr] @@ -600,7 +601,7 @@ proc.process_input_note_details padw padw padw adv_pipe hperm adv_pipe hperm - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [NULLIFIER, note_data_ptr + 16] movup.4 drop @@ -628,7 +629,7 @@ end #! - note_ptr is the memory location for the input note. #! - NOTE_ARGS are the user arguments passed to the note. #! - NOTE_METADATA is the note's metadata. -proc.process_note_args_and_metadata +proc process_note_args_and_metadata padw adv_loadw dup.4 exec.memory::set_input_note_args dropw # => [note_ptr] @@ -648,7 +649,7 @@ end #! Where: #! - note_ptr is the memory location for the input note. #! - inputs_len is the note's input count. -proc.process_note_inputs_length +proc process_note_inputs_length # move the inputs length from the advice stack to the operand stack adv_push.1 # => [inputs_len, note_ptr] @@ -676,7 +677,7 @@ end #! - note_ptr is the memory location for the input note. #! - assets_count is the note's assets count. #! - ASSET_0, ..., ASSET_N are the padded note's assets. -proc.process_note_assets +proc process_note_assets # verify and save the assets count # --------------------------------------------------------------------------------------------- @@ -735,7 +736,7 @@ proc.process_note_assets end # => [PERM, PERM, PERM, assets_ptr+8n, note_ptr, counter+2n, rounded_num_assets] - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [ASSET_COMMITMENT_COMPUTED, assets_ptr+8n, note_ptr, counter+2n, rounded_num_assets] swapw drop movdn.2 drop drop @@ -754,7 +755,7 @@ end #! #! Where: #! - note_ptr is the memory location for the input note. -proc.add_input_note_assets_to_vault +proc add_input_note_assets_to_vault # prepare the stack # --------------------------------------------------------------------------------------------- @@ -806,7 +807,7 @@ end #! Where: #! - note_ptr is the memory location for the input note. #! - NOTE_ID is the note's id, i.e. `hash(RECIPIENT || ASSET_COMMITMENT)`. -proc.compute_input_note_id +proc compute_input_note_id # compute SERIAL_COMMITMENT: hash(SERIAL_NUMBER || EMPTY_WORD) dup exec.memory::get_input_note_serial_num padw hmerge # => [SERIAL_COMMITMENT, note_ptr] @@ -874,7 +875,7 @@ end #! - block_num is the note's creation block number. #! - BLOCK_SUB_COMMITMENT is the block's sub_commitment for which the note was created. #! - NOTE_ROOT is the merkle root of the note's tree. -proc.process_input_note +proc process_input_note # note details # --------------------------------------------------------------------------------------------- @@ -969,7 +970,7 @@ end #! - num_notes is the number of input notes. #! - INPUT_NOTES_COMMITMENT, see `transaction::api::get_input_notes_commitment`. #! - NOTE_DATA is the input notes' details, for format see `prologue::process_input_note`. -proc.process_input_notes_data +proc process_input_notes_data # get the number of input notes from the advice stack adv_push.1 # => [num_notes] @@ -1036,7 +1037,7 @@ proc.process_input_notes_data # => [has_more_notes, PERM, PERM, PERM, idx+1, num_notes] end - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [INPUT_NOTES_COMMITMENT, idx+1, num_notes] # assert the input notes and the commitment matches @@ -1070,7 +1071,7 @@ end #! - TX_SCRIPT_ROOT is the transaction's script root. #! - TX_SCRIPT_ARGS is the word of values which could be used directly or could be used to obtain #! some values associated with it from the advice map. -proc.process_tx_script_data +proc process_tx_script_data # read the transaction script root from the advice stack padw adv_loadw # => [TX_SCRIPT_ROOT] @@ -1101,7 +1102,7 @@ end #! #! Where: #! - AUTH_ARGS is the argument passed to the auth procedure. -proc.process_auth_procedure_data +proc process_auth_procedure_data # read the auth procedure args from the advice stack padw adv_loadw # => [AUTH_ARGS] @@ -1196,7 +1197,7 @@ end #! - data provided by the advice provider does not match global inputs. #! - the account data is invalid. #! - any of the input notes do note exist in the note db. -export.prepare_transaction +pub proc prepare_transaction exec.process_global_inputs # => [block_num] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/tx.masm b/crates/miden-lib/asm/kernels/transaction/lib/tx.masm index e7e137c885..df4c0e9d42 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -1,19 +1,19 @@ -use.$kernel::memory -use.$kernel::note +use $kernel::memory +use $kernel::note # CONSTANTS # ================================================================================================= # Max value for U16, used as the upper limit for expiration block delta -const.EXPIRY_UPPER_LIMIT=0xFFFF+1 +const EXPIRY_UPPER_LIMIT=0xFFFF+1 # Max U32 value, used for initializing the expiration block number -const.MAX_BLOCK_NUM=0xFFFFFFFF +const MAX_BLOCK_NUM=0xFFFFFFFF # ERRORS # ================================================================================================= -const.ERR_TX_INVALID_EXPIRATION_DELTA="transaction expiration block delta must be within 0x1 and 0xFFFF" +const ERR_TX_INVALID_EXPIRATION_DELTA="transaction expiration block delta must be within 0x1 and 0xFFFF" # PROCEDURES # ================================================================================================= @@ -25,7 +25,7 @@ const.ERR_TX_INVALID_EXPIRATION_DELTA="transaction expiration block delta must b #! #! Where: #! - BLOCK_COMMITMENT is the commitment of the transaction reference block. -export.memory::get_block_commitment +pub use memory::get_block_commitment #! Returns the block number of the transaction reference block. #! @@ -34,7 +34,7 @@ export.memory::get_block_commitment #! #! Where: #! - num is the transaction reference block number. -export.memory::get_blk_num->get_block_number +pub use memory::get_blk_num->get_block_number #! Returns the block timestamp of the reference block for this transaction. #! @@ -43,7 +43,7 @@ export.memory::get_blk_num->get_block_number #! #! Where: #! - timestamp is the timestamp of the reference block for this transaction. -export.memory::get_blk_timestamp->get_block_timestamp +pub use memory::get_blk_timestamp->get_block_timestamp #! Returns the input notes commitment hash. #! @@ -54,7 +54,7 @@ export.memory::get_blk_timestamp->get_block_timestamp #! #! Where: #! - INPUT_NOTES_COMMITMENT is the input notes commitment hash. -export.memory::get_input_notes_commitment +pub use memory::get_input_notes_commitment #! Returns the output notes commitment hash. This is computed as a sequential hash of #! (note_id, note_metadata) tuples over all output notes. @@ -64,7 +64,7 @@ export.memory::get_input_notes_commitment #! #! Where: #! - OUTPUT_NOTES_COMMITMENT is the output notes commitment. -export.note::compute_output_notes_commitment->get_output_notes_commitment +pub use note::compute_output_notes_commitment->get_output_notes_commitment #! Returns the total number of input notes consumed by this transaction. #! @@ -73,7 +73,7 @@ export.note::compute_output_notes_commitment->get_output_notes_commitment #! #! Where: #! - num_input_notes is the total number of input notes consumed by this transaction. -export.memory::get_num_input_notes +pub use memory::get_num_input_notes #! Returns the current number of output notes created in this transaction. #! @@ -82,7 +82,7 @@ export.memory::get_num_input_notes #! #! Where: #! - num_output_notes is the number of output notes created in this transaction so far. -export.memory::get_num_output_notes +pub use memory::get_num_output_notes #! Updates the transaction expiration block delta. #! @@ -95,7 +95,7 @@ export.memory::get_num_output_notes #! #! Where: #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). -export.update_expiration_block_delta +pub proc update_expiration_block_delta # Ensure block_height_delta is between 1 and 0xFFFF (inclusive) dup neq.0 assert.err=ERR_TX_INVALID_EXPIRATION_DELTA # => [block_height_delta] @@ -130,7 +130,7 @@ end #! #! Where: #! - block_height_delta is the stored expiration time delta (1 to 0xFFFF). -export.get_expiration_delta +pub proc get_expiration_delta exec.memory::get_expiration_block_num # => [stored_expiration_block_num] diff --git a/crates/miden-lib/asm/kernels/transaction/main.masm b/crates/miden-lib/asm/kernels/transaction/main.masm index 959f882d83..61a0763a06 100644 --- a/crates/miden-lib/asm/kernels/transaction/main.masm +++ b/crates/miden-lib/asm/kernels/transaction/main.masm @@ -1,37 +1,37 @@ -use.std::word +use miden::core::word -use.$kernel::epilogue -use.$kernel::memory -use.$kernel::note -use.$kernel::prologue +use $kernel::epilogue +use $kernel::memory +use $kernel::note +use $kernel::prologue # EVENTS # ================================================================================================= # Event emitted to signal that an execution of the transaction prologue has started. -const.PROLOGUE_START_EVENT=event("miden::tx::prologue_start") +const PROLOGUE_START_EVENT=event("miden::tx::prologue_start") # Event emitted to signal that an execution of the transaction prologue has ended. -const.PROLOGUE_END_EVENT=event("miden::tx::prologue_end") +const PROLOGUE_END_EVENT=event("miden::tx::prologue_end") # Event emitted to signal that the notes processing has started. -const.NOTES_PROCESSING_START_EVENT=event("miden::tx::notes_processing_start") +const NOTES_PROCESSING_START_EVENT=event("miden::tx::notes_processing_start") # Event emitted to signal that the notes processing has ended. -const.NOTES_PROCESSING_END_EVENT=event("miden::tx::notes_processing_end") +const NOTES_PROCESSING_END_EVENT=event("miden::tx::notes_processing_end") # Event emitted to signal that the note consuming has started. -const.NOTE_EXECUTION_START_EVENT=event("miden::tx::note_execution_start") +const NOTE_EXECUTION_START_EVENT=event("miden::tx::note_execution_start") # Event emitted to signal that the note consuming has ended. -const.NOTE_EXECUTION_END_EVENT=event("miden::tx::note_execution_end") +const NOTE_EXECUTION_END_EVENT=event("miden::tx::note_execution_end") # Event emitted to signal that the transaction script processing has started. -const.TX_SCRIPT_PROCESSING_START_EVENT=event("miden::tx::tx_script_processing_start") +const TX_SCRIPT_PROCESSING_START_EVENT=event("miden::tx::tx_script_processing_start") # Event emitted to signal that the transaction script processing has ended. -const.TX_SCRIPT_PROCESSING_END_EVENT=event("miden::tx::tx_script_processing_end") +const TX_SCRIPT_PROCESSING_END_EVENT=event("miden::tx::tx_script_processing_end") # Event emitted to signal that an execution of the transaction epilogue has started. -const.EPILOGUE_START_EVENT=event("miden::tx::epilogue_start") +const EPILOGUE_START_EVENT=event("miden::tx::epilogue_start") # Event emitted to signal that an execution of the transaction epilogue has ended. -const.EPILOGUE_END_EVENT=event("miden::tx::epilogue_end") +const EPILOGUE_END_EVENT=event("miden::tx::epilogue_end") # MAIN # ================================================================================================= @@ -66,7 +66,8 @@ const.EPILOGUE_END_EVENT=event("miden::tx::epilogue_end") #! - ACCOUNT_UPDATE_COMMITMENT is the hash of the the final account commitment and account #! delta commitment. #! - FEE_ASSET is the fungible asset used as the transaction fee. -proc.main.1 +@locals(1) +proc main # Prologue # --------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/asm/kernels/transaction/tx_script_main.masm b/crates/miden-lib/asm/kernels/transaction/tx_script_main.masm index 79c99f8549..b0b12ea8cf 100644 --- a/crates/miden-lib/asm/kernels/transaction/tx_script_main.masm +++ b/crates/miden-lib/asm/kernels/transaction/tx_script_main.masm @@ -1,12 +1,12 @@ -use.std::word +use miden::core::word -use.$kernel::memory -use.$kernel::prologue +use $kernel::memory +use $kernel::prologue # ERRORS # ================================================================================================= -const.ERR_TX_TRANSACTION_SCRIPT_IS_MISSING="the transaction script is missing" +const ERR_TX_TRANSACTION_SCRIPT_IS_MISSING="the transaction script is missing" # MAIN # ================================================================================================= @@ -34,7 +34,7 @@ const.ERR_TX_TRANSACTION_SCRIPT_IS_MISSING="the transaction script is missing" #! - account_id is the account that the transaction is being executed against. #! - INITIAL_ACCOUNT_COMMITMENT is the account state prior to the transaction, EMPTY_WORD for new accounts. #! - INPUT_NOTES_COMMITMENT, see `transaction::api::get_input_notes_commitment`. -proc.main +proc main # Prologue # --------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/asm/miden/active_account.masm b/crates/miden-lib/asm/miden/active_account.masm index 6c45d7d80e..58b69c0214 100644 --- a/crates/miden-lib/asm/miden/active_account.masm +++ b/crates/miden-lib/asm/miden/active_account.masm @@ -1,4 +1,4 @@ -use.miden::kernel_proc_offsets +use miden::kernel_proc_offsets # ACTIVE ACCOUNT PROCEDURES # ================================================================================================= @@ -15,7 +15,7 @@ use.miden::kernel_proc_offsets #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. #! #! Invocation: exec -export.get_id +pub proc get_id # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -48,7 +48,7 @@ end #! - nonce is the active account's nonce. #! #! Invocation: exec -export.get_nonce +pub proc get_nonce # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -76,7 +76,7 @@ end #! - INIT_COMMITMENT is the initial account commitment. #! #! Invocation: exec -export.get_initial_commitment +pub proc get_initial_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -101,7 +101,7 @@ end #! - ACCOUNT_COMMITMENT is the commitment of the account data. #! #! Invocation: exec -export.compute_commitment +pub proc compute_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -139,7 +139,7 @@ end #! - CODE_COMMITMENT is the commitment of the account code. #! #! Invocation: exec -export.get_code_commitment +pub proc get_code_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -169,7 +169,7 @@ end #! - INIT_STORAGE_COMMITMENT is the initial account storage commitment. #! #! Invocation: exec -export.get_initial_storage_commitment +pub proc get_initial_storage_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -203,7 +203,7 @@ end #! - STORAGE_COMMITMENT is the commitment of the account storage. #! #! Invocation: exec -export.compute_storage_commitment +pub proc compute_storage_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -228,7 +228,7 @@ end #! - INIT_VAULT_ROOT is the initial account vault root. #! #! Invocation: exec -export.get_initial_vault_root +pub proc get_initial_vault_root # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -253,7 +253,7 @@ end #! - VAULT_ROOT is the root of the account vault. #! #! Invocation: exec -export.get_vault_root +pub proc get_vault_root # pad the stack for syscall invocation padw padw padw push.0.0.0 # => [pad(15)] @@ -286,7 +286,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: exec -export.get_item +pub proc get_item push.0 movdn.2 # => [slot_id_prefix, slot_id_suffix, 0] @@ -320,7 +320,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: exec -export.get_initial_item +pub proc get_initial_item push.0 movdn.2 # => [slot_id_prefix, slot_id_suffix, 0] @@ -356,7 +356,7 @@ end #! - the slot item at index is not a map. #! #! Invocation: exec -export.get_map_item +pub proc get_map_item exec.kernel_proc_offsets::account_get_map_item_offset # => [offset, slot_id_prefix, slot_id_suffix, KEY] @@ -389,7 +389,7 @@ end #! - the slot item at index is not a map. #! #! Invocation: exec -export.get_initial_map_item +pub proc get_initial_map_item exec.kernel_proc_offsets::account_get_initial_map_item_offset # => [offset, slot_id_prefix, slot_id_suffix, KEY] @@ -422,7 +422,7 @@ end #! - the provided faucet ID is not an ID of a fungible faucet. #! #! Invocation: exec -export.get_balance +pub proc get_balance exec.kernel_proc_offsets::account_get_balance_offset # => [offset, faucet_id_prefix, faucet_id_suffix] @@ -453,7 +453,7 @@ end #! - the provided faucet ID is not an ID of a fungible faucet. #! #! Invocation: exec -export.get_initial_balance +pub proc get_initial_balance exec.kernel_proc_offsets::account_get_initial_balance_offset # => [offset, faucet_id_prefix, faucet_id_suffix] @@ -483,7 +483,7 @@ end #! - the ASSET is a fungible asset. #! #! Invocation: exec -export.has_non_fungible_asset +pub proc has_non_fungible_asset exec.kernel_proc_offsets::account_has_non_fungible_asset_offset # => [offset, ASSET] @@ -508,7 +508,7 @@ end #! - num_procedures is the number of procedures in the active account. #! #! Invocation: exec -export.get_num_procedures +pub proc get_num_procedures # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -537,7 +537,7 @@ end #! - the procedure index is out of bounds. #! #! Invocation: exec -export.get_procedure_root +pub proc get_procedure_root # => [index] push.0.0 movup.2 @@ -570,7 +570,7 @@ end #! available on the active account. #! #! Invocation: exec -export.has_procedure +pub proc has_procedure exec.kernel_proc_offsets::account_has_procedure_offset # => [offset, PROC_ROOT] diff --git a/crates/miden-lib/asm/miden/active_note.masm b/crates/miden-lib/asm/miden/active_note.masm index 69d2e6753b..dba1e4a65d 100644 --- a/crates/miden-lib/asm/miden/active_note.masm +++ b/crates/miden-lib/asm/miden/active_note.masm @@ -1,15 +1,15 @@ -use.std::mem +use miden::core::mem -use.miden::kernel_proc_offsets -use.miden::note -use.miden::contracts::wallets::basic->wallet +use miden::kernel_proc_offsets +use miden::note +use miden::contracts::wallets::basic->wallet # ERRORS # ================================================================================================= -const.ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT="note data does not match the commitment" +const ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT="note data does not match the commitment" -const.ERR_NOTE_INVALID_NUMBER_OF_INPUTS="the specified number of note inputs does not match the actual number" +const ERR_NOTE_INVALID_NUMBER_OF_INPUTS="the specified number of note inputs does not match the actual number" # ACTIVE NOTE PROCEDURES # ================================================================================================= @@ -30,7 +30,7 @@ const.ERR_NOTE_INVALID_NUMBER_OF_INPUTS="the specified number of note inputs doe #! - no note is currently active. #! #! Invocation: exec -export.get_assets +pub proc get_assets # pad the stack padw padw padw push.0.0 # => [pad(14), dest_ptr] @@ -66,7 +66,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_recipient +pub proc get_recipient # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -103,7 +103,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_inputs +pub proc get_inputs # pad the stack padw padw padw push.0.0 # => [pad(14), dest_ptr] @@ -140,7 +140,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_metadata +pub proc get_metadata # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -172,7 +172,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_sender +pub proc get_sender # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -208,7 +208,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_serial_number +pub proc get_serial_number # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -240,7 +240,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_script_root +pub proc get_script_root # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -264,7 +264,8 @@ end #! #! Inputs: [] #! Outputs: [] -export.add_assets_to_account.1024 +@locals(1024) +pub proc add_assets_to_account # write assets to local memory starting at offset 0 # we have allocated 4 * MAX_ASSETS_PER_NOTE number of locals so all assets should fit # since the asset memory will be overwritten, we don't have to initialize the locals to zero @@ -330,7 +331,7 @@ end #! } #! Outputs: #! Operand stack: [num_inputs, dest_ptr] -proc.write_inputs_to_memory +proc write_inputs_to_memory # load the inputs from the advice map to the advice stack adv.push_mapvaln # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] diff --git a/crates/miden-lib/asm/miden/asset.masm b/crates/miden-lib/asm/miden/asset.masm index 6c6d8f8d6b..4a7e612fdd 100644 --- a/crates/miden-lib/asm/miden/asset.masm +++ b/crates/miden-lib/asm/miden/asset.masm @@ -1,13 +1,13 @@ -use.miden::account_id +use miden::account_id # ERRORS # ================================================================================================= -const.ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the fungible asset because the provided faucet id is not from a fungible faucet" +const ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the fungible asset because the provided faucet id is not from a fungible faucet" -const.ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT="fungible asset build operation called with amount that exceeds the maximum allowed asset amount" +const ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT="fungible asset build operation called with amount that exceeds the maximum allowed asset amount" -const.ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet" +const ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet" # PROCEDURES # ================================================================================================= @@ -24,7 +24,7 @@ const.ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the #! - ASSET is the built fungible asset. #! #! Invocation: exec -export.build_fungible_asset +pub proc build_fungible_asset # assert the faucet is a fungible faucet dup exec.account_id::is_fungible_faucet assert.err=ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID # => [faucet_id_prefix, faucet_id_suffix, amount] @@ -51,7 +51,7 @@ end #! - ASSET is the built non-fungible asset. #! #! Invocation: exec -export.build_non_fungible_asset +pub proc build_non_fungible_asset # assert the faucet is a non-fungible faucet dup exec.account_id::is_non_fungible_faucet assert.err=ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID @@ -69,4 +69,4 @@ end #! Outputs: [fungible_asset_max_amount] #! #! fungible_asset_max_amount is the maximum amount of a fungible asset. -export.::miden::util::asset::get_fungible_asset_max_amount +pub use ::miden::util::asset::get_fungible_asset_max_amount diff --git a/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm b/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm index 3505028a21..d220bd1c32 100644 --- a/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm +++ b/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm @@ -1,21 +1,21 @@ -use.miden::active_account -use.miden::native_account -use.miden::auth -use.miden::tx -use.std::crypto::dsa::ecdsa::secp256k1 +use miden::active_account +use miden::native_account +use miden::auth +use miden::tx +use miden::core::crypto::dsa::ecdsa_k256_keccak # CONSTANTS # ================================================================================================= # The event to request an authentication signature. -const.AUTH_REQUEST_EVENT=event("miden::auth::request") +const AUTH_REQUEST_EVENT=event("miden::auth::request") # Local Memory Addresses for multisig operations -const.NUM_OF_APPROVERS_LOC=0 -const.PUB_KEY_SLOT_SUFFIX_LOC=4 -const.PUB_KEY_SLOT_PREFIX_LOC=5 -const.CURRENT_PK_LOC=8 -const.SUCCESSFUL_VERIFICATIONS_LOC=12 +const NUM_OF_APPROVERS_LOC=0 +const PUB_KEY_SLOT_SUFFIX_LOC=4 +const PUB_KEY_SLOT_PREFIX_LOC=5 +const CURRENT_PK_LOC=8 +const SUCCESSFUL_VERIFICATIONS_LOC=12 #! Authenticate a transaction using the ECDSA signature scheme. #! @@ -32,7 +32,7 @@ const.SUCCESSFUL_VERIFICATIONS_LOC=12 #! Outputs: [] #! #! Invocation: exec -export.authenticate_transaction +pub proc authenticate_transaction # Increment the account's nonce. # --------------------------------------------------------------------------------------------- # This has to happen before computing the delta commitment, otherwise that procedure will abort @@ -63,7 +63,7 @@ export.authenticate_transaction # Verify the signature against the public key and the message. The procedure gets as inputs the # hash of the public key and the message via the operand stack. The signature is provided via # the advice stack. The signature is valid if and only if the procedure returns. - exec.secp256k1::verify_ecdsa_k256_keccak + exec.ecdsa_k256_keccak::verify # OS => [] # AS => [] end @@ -81,7 +81,8 @@ end #! #! Inputs: [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, MSG] #! Outputs: [num_verified_signatures, MSG] -export.verify_signatures.16 +@locals(16) +pub proc verify_signatures loc_store.PUB_KEY_SLOT_PREFIX_LOC loc_store.PUB_KEY_SLOT_SUFFIX_LOC # => [num_of_approvers, MSG] @@ -155,7 +156,7 @@ export.verify_signatures.16 # OS => [PUB_KEY, MSG, MSG, i-1] # AS => [SIGNATURE] - exec.secp256k1::verify_ecdsa_k256_keccak + exec.ecdsa_k256_keccak::verify # => [MSG, i-1] loc_load.SUCCESSFUL_VERIFICATIONS_LOC diff --git a/crates/miden-lib/asm/miden/auth/mod.masm b/crates/miden-lib/asm/miden/auth/mod.masm index a62b6141cc..0cfd10e644 100644 --- a/crates/miden-lib/asm/miden/auth/mod.masm +++ b/crates/miden-lib/asm/miden/auth/mod.masm @@ -1,10 +1,11 @@ -use.miden::native_account -use.miden::tx -use.std::crypto::hashes::rpo +use miden::native_account +use miden::tx +use miden::core::crypto::hashes::rpo256 #! Inputs: [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT] #! Outputs: [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT] -export.adv_insert_hqword.16 +@locals(16) +pub proc adv_insert_hqword loc_storew_be.0 movdnw.3 loc_storew_be.4 @@ -46,7 +47,7 @@ end #! - OUTPUT_NOTES_COMMITMENT is the commitment to the transaction's output notes. #! - INPUT_NOTES_COMMITMENT is the commitment to the transaction's inputs notes. #! - ACCOUNT_DELTA_COMMITMENT is the commitment to the transaction's account delta. -export.create_tx_summary +pub proc create_tx_summary exec.native_account::compute_delta_commitment # => [ACCOUNT_DELTA_COMMITMENT, SALT] @@ -70,7 +71,7 @@ end #! - OUTPUT_NOTES_COMMITMENT is the commitment to the transaction's output notes. #! - INPUT_NOTES_COMMITMENT is the commitment to the transaction's inputs notes. #! - ACCOUNT_DELTA_COMMITMENT is the commitment to the transaction's account delta. -export.hash_tx_summary +pub proc hash_tx_summary swapdw # => [INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT, SALT, OUTPUT_NOTES_COMMITMENT] @@ -91,6 +92,6 @@ export.hash_tx_summary hperm # => [RATE, RATE, PERM] - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [TX_SUMMARY_COMMITMENT] end diff --git a/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm b/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm index 16f4ef165b..7349e34337 100644 --- a/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm +++ b/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm @@ -1,21 +1,21 @@ -use.miden::active_account -use.miden::native_account -use.miden::auth -use.miden::tx -use.std::crypto::dsa::rpo_falcon512 +use miden::active_account +use miden::native_account +use miden::auth +use miden::tx +use miden::core::crypto::dsa::falcon512rpo # CONSTANTS # ================================================================================================= # The event to request an authentication signature. -const.AUTH_REQUEST_EVENT=event("miden::auth::request") +const AUTH_REQUEST_EVENT=event("miden::auth::request") # Local Memory Addresses for multisig operations -const.NUM_OF_APPROVERS_LOC=0 -const.PUB_KEY_SLOT_SUFFIX_LOC=4 -const.PUB_KEY_SLOT_PREFIX_LOC=5 -const.CURRENT_PK_LOC=8 -const.SUCCESSFUL_VERIFICATIONS_LOC=12 +const NUM_OF_APPROVERS_LOC=0 +const PUB_KEY_SLOT_SUFFIX_LOC=4 +const PUB_KEY_SLOT_PREFIX_LOC=5 +const CURRENT_PK_LOC=8 +const SUCCESSFUL_VERIFICATIONS_LOC=12 #! Authenticate a transaction using the Falcon signature scheme. #! @@ -32,7 +32,7 @@ const.SUCCESSFUL_VERIFICATIONS_LOC=12 #! Outputs: [] #! #! Invocation: exec -export.authenticate_transaction +pub proc authenticate_transaction # Increment the account's nonce. # --------------------------------------------------------------------------------------------- # This has to happen before computing the delta commitment, otherwise that procedure will abort @@ -63,7 +63,7 @@ export.authenticate_transaction # Verify the signature against the public key and the message. The procedure gets as inputs the # hash of the public key and the message via the operand stack. The signature is provided via # the advice stack. The signature is valid if and only if the procedure returns. - exec.rpo_falcon512::verify + exec.falcon512rpo::verify # OS => [] # AS => [] end @@ -81,7 +81,8 @@ end #! #! Inputs: [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, MSG] #! Outputs: [num_verified_signatures, MSG] -export.verify_signatures.16 +@locals(16) +pub proc verify_signatures loc_store.PUB_KEY_SLOT_PREFIX_LOC loc_store.PUB_KEY_SLOT_SUFFIX_LOC # => [num_of_approvers, MSG] @@ -155,7 +156,7 @@ export.verify_signatures.16 # OS => [PUB_KEY, MSG, MSG, i-1] # AS => [SIGNATURE] - exec.rpo_falcon512::verify + exec.falcon512rpo::verify # => [MSG, i-1] loc_load.SUCCESSFUL_VERIFICATIONS_LOC diff --git a/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm b/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm index 9930eda19c..bb5bea6559 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm +++ b/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm @@ -8,18 +8,18 @@ # - decimals are the decimals of the token. # - token_symbol as three chars encoded in a Felt. -use.miden::contracts::faucets +use miden::contracts::faucets # CONSTANTS # ================================================================================================= -const.PRIVATE_NOTE=2 +const PRIVATE_NOTE=2 # ERRORS # ================================================================================================= -const.ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED="distribute would cause the maximum supply to be exceeded" +const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED="distribute would cause the maximum supply to be exceeded" -const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 note asset" +const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 note asset" # CONSTANTS # ================================================================================================= @@ -43,7 +43,7 @@ const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no #! - the total issuance after minting is greater than the maximum allowed supply. #! #! Invocation: call -export.distribute +pub proc distribute exec.faucets::distribute # => [pad(16)] end @@ -64,4 +64,4 @@ end #! - the transaction is executed against an account which is not a fungible asset faucet. #! - the transaction is executed against a faucet which is not the origin of the specified asset. #! - the amount about to be burned is greater than the outstanding supply of the asset. -export.faucets::burn +pub use faucets::burn diff --git a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm b/crates/miden-lib/asm/miden/contracts/faucets/mod.masm index 45eb22f9fa..3c9018d909 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm +++ b/crates/miden-lib/asm/miden/contracts/faucets/mod.masm @@ -1,24 +1,24 @@ -use.miden::active_account -use.miden::active_note -use.miden::faucet -use.miden::output_note +use miden::active_account +use miden::active_note +use miden::faucet +use miden::output_note # CONSTANTS # ================================================================================================= -const.PRIVATE_NOTE=2 +const PRIVATE_NOTE=2 # ERRORS # ================================================================================================= -const.ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED="distribute would cause the maximum supply to be exceeded" +const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED="distribute would cause the maximum supply to be exceeded" -const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 note asset" +const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 note asset" # CONSTANTS # ================================================================================================= # The standard slot where fungible faucet metadata like token symbol or decimals are stored. -const.METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") +const METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") #! Distributes freshly minted fungible assets to the provided recipient by creating a note. #! @@ -39,7 +39,7 @@ const.METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") #! - the total issuance after minting is greater than the maximum allowed supply. #! #! Invocation: exec -export.distribute +pub proc distribute # get max supply of this faucet. We assume it is stored at pos 3 of slot 0 push.METADATA_SLOT[0..2] exec.active_account::get_item drop drop drop # => [max_supply, amount, tag, aux, note_type, execution_hint, RECIPIENT] @@ -96,7 +96,7 @@ end #! - the amount about to be burned is greater than the outstanding supply of the asset. #! #! Invocation: call -export.burn +pub proc burn # Get the assets from the note. This will fail if not called from a note context. push.0 exec.active_note::get_assets # => [num_assets, dest_ptr, pad(16)] diff --git a/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm b/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm index 41e0e90db1..4cf8dba38f 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm +++ b/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm @@ -1,17 +1,17 @@ -use.miden::active_account -use.miden::account_id -use.miden::active_note -use.miden::contracts::faucets -use.miden::contracts::faucets::basic_fungible +use miden::active_account +use miden::account_id +use miden::active_note +use miden::contracts::faucets +use miden::contracts::faucets::basic_fungible # CONSTANTS # ================================================================================================ # The slot in this component's storage layout where the owner config is stored. -const.OWNER_CONFIG_SLOT=word("miden::network_fungible_faucet::owner_config") +const OWNER_CONFIG_SLOT=word("miden::network_fungible_faucet::owner_config") # ERRORS -const.ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who can mint assets" +const ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who can mint assets" #! Checks if the note sender is the owner of this faucet. #! @@ -20,7 +20,7 @@ const.ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who ca #! #! Where: #! - is_owner is 1 if the sender is the owner, 0 otherwise. -proc.is_owner +proc is_owner push.OWNER_CONFIG_SLOT[0..2] exec.active_account::get_item # => [owner_prefix, owner_suffix, 0, 0] @@ -55,7 +55,7 @@ end #! - any of the validations in faucets::distribute fail. #! #! Invocation: call -export.distribute +pub proc distribute exec.is_owner # => [is_owner, amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] @@ -82,4 +82,4 @@ end #! - the amount about to be burned is greater than the outstanding supply of the asset. #! #! Invocation: call -export.faucets::burn +pub use faucets::burn diff --git a/crates/miden-lib/asm/miden/contracts/wallets/basic.masm b/crates/miden-lib/asm/miden/contracts/wallets/basic.masm index 0094eec5af..57c701cb94 100644 --- a/crates/miden-lib/asm/miden/contracts/wallets/basic.masm +++ b/crates/miden-lib/asm/miden/contracts/wallets/basic.masm @@ -1,9 +1,9 @@ -use.miden::native_account -use.miden::output_note +use miden::native_account +use miden::output_note # CONSTANTS # ================================================================================================= -const.PUBLIC_NOTE=1 +const PUBLIC_NOTE=1 #! Adds the provided asset to the active account. #! @@ -19,7 +19,7 @@ const.PUBLIC_NOTE=1 #! the total amount would be greater than 2^63. #! #! Invocation: call -export.receive_asset +pub proc receive_asset exec.native_account::add_asset # => [ASSET', pad(12)] @@ -48,7 +48,7 @@ end #! - the non-fungible asset is not found in the vault. #! #! Invocation: call -export.move_asset_to_note +pub proc move_asset_to_note # remove the asset from the account exec.native_account::remove_asset # => [ASSET, note_idx, pad(11)] diff --git a/crates/miden-lib/asm/miden/faucet.masm b/crates/miden-lib/asm/miden/faucet.masm index 1332f2c3ca..13230e7ca7 100644 --- a/crates/miden-lib/asm/miden/faucet.masm +++ b/crates/miden-lib/asm/miden/faucet.masm @@ -1,6 +1,6 @@ -use.miden::asset -use.miden::active_account -use.miden::kernel_proc_offsets +use miden::asset +use miden::active_account +use miden::kernel_proc_offsets #! Creates a fungible asset for the faucet the transaction is being executed against. #! @@ -15,7 +15,7 @@ use.miden::kernel_proc_offsets #! - the active account is not a fungible faucet. #! #! Invocation: exec -export.create_fungible_asset +pub proc create_fungible_asset # fetch the id of the faucet the transaction is being executed against. exec.active_account::get_id # => [id_prefix, id_suffix, amount] @@ -38,7 +38,7 @@ end #! - the active account is not a non-fungible faucet. #! #! Invocation: exec -export.create_non_fungible_asset +pub proc create_non_fungible_asset # get the id of the faucet the transaction is being executed against exec.active_account::get_id swap drop # => [faucet_id_prefix, DATA_HASH] @@ -66,7 +66,7 @@ end #! - for non-fungible faucets if the non-fungible asset being minted already exists. #! #! Invocation: exec -export.mint +pub proc mint exec.kernel_proc_offsets::faucet_mint_asset_offset # => [offset, ASSET] @@ -101,7 +101,7 @@ end #! provided as input to the transaction via a note or the accounts vault. #! #! Invocation: exec -export.burn +pub proc burn exec.kernel_proc_offsets::faucet_burn_asset_offset # => [offset, ASSET] @@ -130,7 +130,7 @@ end #! - the transaction is not being executed against a fungible faucet. #! #! Invocation: exec -export.get_total_issuance +pub proc get_total_issuance # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -161,7 +161,7 @@ end #! - the ASSET is not associated with the faucet the transaction is being executed against. #! #! Invocation: exec -export.is_non_fungible_asset_issued +pub proc is_non_fungible_asset_issued exec.kernel_proc_offsets::faucet_is_non_fungible_asset_issued_offset # => [offset, ASSET] diff --git a/crates/miden-lib/asm/miden/input_note.masm b/crates/miden-lib/asm/miden/input_note.masm index 228899e11e..722414e29c 100644 --- a/crates/miden-lib/asm/miden/input_note.masm +++ b/crates/miden-lib/asm/miden/input_note.masm @@ -1,5 +1,5 @@ -use.miden::kernel_proc_offsets -use.miden::note +use miden::kernel_proc_offsets +use miden::note # PROCEDURES # ================================================================================================= @@ -20,7 +20,7 @@ use.miden::note #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_assets_info +pub proc get_assets_info # start padding the stack push.0 swap # => [note_index, 0] @@ -71,7 +71,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_assets +pub proc get_assets # get the assets commitment and assets number dup.1 exec.get_assets_info # => [ASSETS_COMMITMENT, num_assets, dest_ptr, note_index] @@ -94,7 +94,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_recipient +pub proc get_recipient # start padding the stack push.0 swap # => [note_index, 0] @@ -132,7 +132,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_metadata +pub proc get_metadata # start padding the stack push.0 swap # => [note_index, 0] @@ -170,7 +170,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_sender +pub proc get_sender # start padding the stack push.0 swap # => [note_index, 0] @@ -213,7 +213,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_inputs_info +pub proc get_inputs_info # start padding the stack push.0 swap # => [note_index, 0] @@ -254,7 +254,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_script_root +pub proc get_script_root # start padding the stack push.0 swap # => [note_index, 0] @@ -292,7 +292,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_serial_number +pub proc get_serial_number # start padding the stack push.0 swap # => [note_index, 0] diff --git a/crates/miden-lib/asm/miden/kernel_proc_offsets.masm b/crates/miden-lib/asm/miden/kernel_proc_offsets.masm index 3f7215e494..be593b3ec1 100644 --- a/crates/miden-lib/asm/miden/kernel_proc_offsets.masm +++ b/crates/miden-lib/asm/miden/kernel_proc_offsets.masm @@ -4,92 +4,92 @@ ### Account ##################################### # Entire account commitment -const.ACCOUNT_GET_INITIAL_COMMITMENT_OFFSET=0 -const.ACCOUNT_COMPUTE_COMMITMENT_OFFSET=1 +const ACCOUNT_GET_INITIAL_COMMITMENT_OFFSET=0 +const ACCOUNT_COMPUTE_COMMITMENT_OFFSET=1 # ID -const.ACCOUNT_GET_ID_OFFSET=2 +const ACCOUNT_GET_ID_OFFSET=2 # Nonce -const.ACCOUNT_GET_NONCE_OFFSET=3 # accessor -const.ACCOUNT_INCR_NONCE_OFFSET=4 # mutator +const ACCOUNT_GET_NONCE_OFFSET=3 # accessor +const ACCOUNT_INCR_NONCE_OFFSET=4 # mutator # Code -const.ACCOUNT_GET_CODE_COMMITMENT_OFFSET=5 +const ACCOUNT_GET_CODE_COMMITMENT_OFFSET=5 # Storage -const.ACCOUNT_GET_INITIAL_STORAGE_COMMITMENT_OFFSET=6 -const.ACCOUNT_COMPUTE_STORAGE_COMMITMENT_OFFSET=7 -const.ACCOUNT_GET_ITEM_OFFSET=8 -const.ACCOUNT_GET_INITIAL_ITEM_OFFSET=9 -const.ACCOUNT_SET_ITEM_OFFSET=10 -const.ACCOUNT_GET_MAP_ITEM_OFFSET=11 -const.ACCOUNT_GET_INITIAL_MAP_ITEM_OFFSET=12 -const.ACCOUNT_SET_MAP_ITEM_OFFSET=13 +const ACCOUNT_GET_INITIAL_STORAGE_COMMITMENT_OFFSET=6 +const ACCOUNT_COMPUTE_STORAGE_COMMITMENT_OFFSET=7 +const ACCOUNT_GET_ITEM_OFFSET=8 +const ACCOUNT_GET_INITIAL_ITEM_OFFSET=9 +const ACCOUNT_SET_ITEM_OFFSET=10 +const ACCOUNT_GET_MAP_ITEM_OFFSET=11 +const ACCOUNT_GET_INITIAL_MAP_ITEM_OFFSET=12 +const ACCOUNT_SET_MAP_ITEM_OFFSET=13 # Vault -const.ACCOUNT_GET_INITIAL_VAULT_ROOT_OFFSET=14 -const.ACCOUNT_GET_VAULT_ROOT_OFFSET=15 -const.ACCOUNT_ADD_ASSET_OFFSET=16 -const.ACCOUNT_REMOVE_ASSET_OFFSET=17 -const.ACCOUNT_GET_BALANCE_OFFSET=18 -const.ACCOUNT_GET_INITIAL_BALANCE_OFFSET=19 -const.ACCOUNT_HAS_NON_FUNGIBLE_ASSET_OFFSET=20 +const ACCOUNT_GET_INITIAL_VAULT_ROOT_OFFSET=14 +const ACCOUNT_GET_VAULT_ROOT_OFFSET=15 +const ACCOUNT_ADD_ASSET_OFFSET=16 +const ACCOUNT_REMOVE_ASSET_OFFSET=17 +const ACCOUNT_GET_BALANCE_OFFSET=18 +const ACCOUNT_GET_INITIAL_BALANCE_OFFSET=19 +const ACCOUNT_HAS_NON_FUNGIBLE_ASSET_OFFSET=20 # Delta -const.ACCOUNT_COMPUTE_DELTA_COMMITMENT_OFFSET=21 +const ACCOUNT_COMPUTE_DELTA_COMMITMENT_OFFSET=21 # Procedure introspection -const.ACCOUNT_GET_NUM_PROCEDURES_OFFSET=22 -const.ACCOUNT_GET_PROCEDURE_ROOT_OFFSET=23 -const.ACCOUNT_WAS_PROCEDURE_CALLED_OFFSET=24 -const.ACCOUNT_HAS_PROCEDURE_OFFSET=25 +const ACCOUNT_GET_NUM_PROCEDURES_OFFSET=22 +const ACCOUNT_GET_PROCEDURE_ROOT_OFFSET=23 +const ACCOUNT_WAS_PROCEDURE_CALLED_OFFSET=24 +const ACCOUNT_HAS_PROCEDURE_OFFSET=25 ### Faucet ###################################### -const.FAUCET_MINT_ASSET_OFFSET=26 -const.FAUCET_BURN_ASSET_OFFSET=27 -const.FAUCET_GET_TOTAL_FUNGIBLE_ASSET_ISSUANCE_OFFSET=28 -const.FAUCET_IS_NON_FUNGIBLE_ASSET_ISSUED_OFFSET=29 +const FAUCET_MINT_ASSET_OFFSET=26 +const FAUCET_BURN_ASSET_OFFSET=27 +const FAUCET_GET_TOTAL_FUNGIBLE_ASSET_ISSUANCE_OFFSET=28 +const FAUCET_IS_NON_FUNGIBLE_ASSET_ISSUED_OFFSET=29 ### Note ######################################## # input notes -const.INPUT_NOTE_GET_METADATA_OFFSET=30 -const.INPUT_NOTE_GET_ASSETS_INFO_OFFSET=31 -const.INPUT_NOTE_GET_SCRIPT_ROOT_OFFSET=32 -const.INPUT_NOTE_GET_INPUTS_INFO_OFFSET=33 -const.INPUT_NOTE_GET_SERIAL_NUMBER_OFFSET=34 -const.INPUT_NOTE_GET_RECIPIENT_OFFSET=35 +const INPUT_NOTE_GET_METADATA_OFFSET=30 +const INPUT_NOTE_GET_ASSETS_INFO_OFFSET=31 +const INPUT_NOTE_GET_SCRIPT_ROOT_OFFSET=32 +const INPUT_NOTE_GET_INPUTS_INFO_OFFSET=33 +const INPUT_NOTE_GET_SERIAL_NUMBER_OFFSET=34 +const INPUT_NOTE_GET_RECIPIENT_OFFSET=35 # output notes -const.OUTPUT_NOTE_CREATE_OFFSET=36 -const.OUTPUT_NOTE_GET_METADATA_OFFSET=37 -const.OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET=38 -const.OUTPUT_NOTE_GET_RECIPIENT_OFFSET=39 -const.OUTPUT_NOTE_ADD_ASSET_OFFSET=40 +const OUTPUT_NOTE_CREATE_OFFSET=36 +const OUTPUT_NOTE_GET_METADATA_OFFSET=37 +const OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET=38 +const OUTPUT_NOTE_GET_RECIPIENT_OFFSET=39 +const OUTPUT_NOTE_ADD_ASSET_OFFSET=40 ### Tx ########################################## # input notes -const.TX_GET_NUM_INPUT_NOTES_OFFSET=41 -const.TX_GET_INPUT_NOTES_COMMITMENT_OFFSET=42 +const TX_GET_NUM_INPUT_NOTES_OFFSET=41 +const TX_GET_INPUT_NOTES_COMMITMENT_OFFSET=42 # output notes -const.TX_GET_NUM_OUTPUT_NOTES_OFFSET=43 -const.TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET=44 +const TX_GET_NUM_OUTPUT_NOTES_OFFSET=43 +const TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET=44 # block info -const.TX_GET_BLOCK_COMMITMENT_OFFSET=45 -const.TX_GET_BLOCK_NUMBER_OFFSET=46 -const.TX_GET_BLOCK_TIMESTAMP_OFFSET=47 +const TX_GET_BLOCK_COMMITMENT_OFFSET=45 +const TX_GET_BLOCK_NUMBER_OFFSET=46 +const TX_GET_BLOCK_TIMESTAMP_OFFSET=47 # foreign context -const.TX_START_FOREIGN_CONTEXT_OFFSET=48 -const.TX_END_FOREIGN_CONTEXT_OFFSET=49 +const TX_START_FOREIGN_CONTEXT_OFFSET=48 +const TX_END_FOREIGN_CONTEXT_OFFSET=49 # expiration data -const.TX_GET_EXPIRATION_DELTA_OFFSET=50 # accessor -const.TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=51 # mutator +const TX_GET_EXPIRATION_DELTA_OFFSET=50 # accessor +const TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=51 # mutator # ACCESSORS # ------------------------------------------------------------------------------------------------- @@ -104,7 +104,7 @@ const.TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=51 # mutator #! Where: #! - proc_offset is the offset of the `account_get_initial_commitment` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_initial_commitment_offset +pub proc account_get_initial_commitment_offset push.ACCOUNT_GET_INITIAL_COMMITMENT_OFFSET end @@ -116,7 +116,7 @@ end #! Where: #! - proc_offset is the offset of the `account_compute_commitment` kernel procedure required to get #! the address where this procedure is stored. -export.account_compute_commitment_offset +pub proc account_compute_commitment_offset push.ACCOUNT_COMPUTE_COMMITMENT_OFFSET end @@ -128,7 +128,7 @@ end #! Where: #! - proc_offset is the offset of the `account_compute_delta_commitment` kernel procedure required #! to get the address where this procedure is stored. -export.account_compute_delta_commitment_offset +pub proc account_compute_delta_commitment_offset push.ACCOUNT_COMPUTE_DELTA_COMMITMENT_OFFSET end @@ -140,7 +140,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_id` kernel procedure required to get the address #! where this procedure is stored. -export.account_get_id_offset +pub proc account_get_id_offset push.ACCOUNT_GET_ID_OFFSET end @@ -152,7 +152,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_nonce` kernel procedure required to get the #! address where this procedure is stored. -export.account_get_nonce_offset +pub proc account_get_nonce_offset push.ACCOUNT_GET_NONCE_OFFSET end @@ -164,7 +164,7 @@ end #! Where: #! - proc_offset is the offset of the `account_incr_nonce` kernel procedure required to get the #! address where this procedure is stored. -export.account_incr_nonce_offset +pub proc account_incr_nonce_offset push.ACCOUNT_INCR_NONCE_OFFSET end @@ -176,7 +176,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_code_commitment` kernel procedure required to get #! the address where this procedure is stored. -export.account_get_code_commitment_offset +pub proc account_get_code_commitment_offset push.ACCOUNT_GET_CODE_COMMITMENT_OFFSET end @@ -188,7 +188,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_storage_commitment` kernel procedure #! required to get the address where this procedure is stored. -export.account_get_initial_storage_commitment_offset +pub proc account_get_initial_storage_commitment_offset push.ACCOUNT_GET_INITIAL_STORAGE_COMMITMENT_OFFSET end @@ -200,7 +200,7 @@ end #! Where: #! - proc_offset is the offset of the `account_compute_storage_commitment` kernel procedure required #! to get the address where this procedure is stored. -export.account_compute_storage_commitment_offset +pub proc account_compute_storage_commitment_offset push.ACCOUNT_COMPUTE_STORAGE_COMMITMENT_OFFSET end @@ -212,7 +212,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_item` kernel procedure required to get the #! address where this procedure is stored. -export.account_get_item_offset +pub proc account_get_item_offset push.ACCOUNT_GET_ITEM_OFFSET end @@ -224,7 +224,7 @@ end #! Where: #! - proc_offset is the offset of the `account_set_item` kernel procedure required to get the #! address where this procedure is stored. -export.account_set_item_offset +pub proc account_set_item_offset push.ACCOUNT_SET_ITEM_OFFSET end @@ -236,7 +236,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_map_item` kernel procedure required to get the #! address where this procedure is stored. -export.account_get_map_item_offset +pub proc account_get_map_item_offset push.ACCOUNT_GET_MAP_ITEM_OFFSET end @@ -248,7 +248,7 @@ end #! Where: #! - proc_offset is the offset of the `account_set_map_item` kernel procedure required to get the #! address where this procedure is stored. -export.account_set_map_item_offset +pub proc account_set_map_item_offset push.ACCOUNT_SET_MAP_ITEM_OFFSET end @@ -260,7 +260,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_item` kernel procedure required to get #! the address where this procedure is stored. -export.account_get_initial_item_offset +pub proc account_get_initial_item_offset push.ACCOUNT_GET_INITIAL_ITEM_OFFSET end @@ -272,7 +272,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_map_item` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_initial_map_item_offset +pub proc account_get_initial_map_item_offset push.ACCOUNT_GET_INITIAL_MAP_ITEM_OFFSET end @@ -284,7 +284,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_vault_root` kernel procedure required #! to get the address where this procedure is stored. -export.account_get_initial_vault_root_offset +pub proc account_get_initial_vault_root_offset push.ACCOUNT_GET_INITIAL_VAULT_ROOT_OFFSET end @@ -296,7 +296,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_vault_root` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_vault_root_offset +pub proc account_get_vault_root_offset push.ACCOUNT_GET_VAULT_ROOT_OFFSET end @@ -308,7 +308,7 @@ end #! Where: #! - proc_offset is the offset of the `account_add_asset` kernel procedure required to get the #! address where this procedure is stored. -export.account_add_asset_offset +pub proc account_add_asset_offset push.ACCOUNT_ADD_ASSET_OFFSET end @@ -320,7 +320,7 @@ end #! Where: #! - proc_offset is the offset of the `account_remove_asset` kernel procedure required to get the #! address where this procedure is stored. -export.account_remove_asset_offset +pub proc account_remove_asset_offset push.ACCOUNT_REMOVE_ASSET_OFFSET end @@ -332,7 +332,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_balance` kernel procedure required to get the #! address where this procedure is stored. -export.account_get_balance_offset +pub proc account_get_balance_offset push.ACCOUNT_GET_BALANCE_OFFSET end @@ -344,7 +344,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_balance` kernel procedure required to get #! the address where this procedure is stored. -export.account_get_initial_balance_offset +pub proc account_get_initial_balance_offset push.ACCOUNT_GET_INITIAL_BALANCE_OFFSET end @@ -356,7 +356,7 @@ end #! Where: #! - proc_offset is the offset of the `account_has_non_fungible_asset` kernel procedure required to #! get the address where this procedure is stored. -export.account_has_non_fungible_asset_offset +pub proc account_has_non_fungible_asset_offset push.ACCOUNT_HAS_NON_FUNGIBLE_ASSET_OFFSET end @@ -368,7 +368,7 @@ end #! Where: #! - proc_offset is the offset of the `account_was_procedure_called` kernel procedure required to #! get the address where this procedure is stored. -export.account_was_procedure_called_offset +pub proc account_was_procedure_called_offset push.ACCOUNT_WAS_PROCEDURE_CALLED_OFFSET end @@ -380,7 +380,7 @@ end #! Where: #! - proc_offset is the offset of the `account_has_procedure` kernel procedure required to get the #! address where this procedure is stored. -export.account_has_procedure_offset +pub proc account_has_procedure_offset push.ACCOUNT_HAS_PROCEDURE_OFFSET end @@ -392,7 +392,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_num_procedures` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_num_procedures_offset +pub proc account_get_num_procedures_offset push.ACCOUNT_GET_NUM_PROCEDURES_OFFSET end @@ -404,7 +404,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_procedure_root` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_procedure_root_offset +pub proc account_get_procedure_root_offset push.ACCOUNT_GET_PROCEDURE_ROOT_OFFSET end @@ -418,7 +418,7 @@ end #! Where: #! - proc_offset is the offset of the `faucet_mint_asset` kernel procedure required to get the #! address where this procedure is stored. -export.faucet_mint_asset_offset +pub proc faucet_mint_asset_offset push.FAUCET_MINT_ASSET_OFFSET end @@ -430,7 +430,7 @@ end #! Where: #! - proc_offset is the offset of the `faucet_burn_asset` kernel procedure required to get the #! address where this procedure is stored. -export.faucet_burn_asset_offset +pub proc faucet_burn_asset_offset push.FAUCET_BURN_ASSET_OFFSET end @@ -442,7 +442,7 @@ end #! Where: #! - proc_offset is the offset of the `faucet_get_total_fungible_asset_issuance` kernel procedure #! required to get the address where this procedure is stored. -export.faucet_get_total_fungible_asset_issuance_offset +pub proc faucet_get_total_fungible_asset_issuance_offset push.FAUCET_GET_TOTAL_FUNGIBLE_ASSET_ISSUANCE_OFFSET end @@ -454,7 +454,7 @@ end #! Where: #! - proc_offset is the offset of the `faucet_is_non_fungible_asset_issued` kernel procedure #! required to get the address where this procedure is stored. -export.faucet_is_non_fungible_asset_issued_offset +pub proc faucet_is_non_fungible_asset_issued_offset push.FAUCET_IS_NON_FUNGIBLE_ASSET_ISSUED_OFFSET end @@ -468,7 +468,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_create` kernel procedure required to get the #! address where this procedure is stored. -export.output_note_create_offset +pub proc output_note_create_offset push.OUTPUT_NOTE_CREATE_OFFSET end @@ -480,7 +480,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_add_asset` kernel procedure required to get the #! address where this procedure is stored. -export.output_note_add_asset_offset +pub proc output_note_add_asset_offset push.OUTPUT_NOTE_ADD_ASSET_OFFSET end @@ -492,7 +492,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_get_assets_info` kernel procedure required to get #! the address where this procedure is stored. -export.output_note_get_assets_info_offset +pub proc output_note_get_assets_info_offset push.OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET end @@ -504,7 +504,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_get_recipient` kernel procedure required to get #! the address where this procedure is stored. -export.output_note_get_recipient_offset +pub proc output_note_get_recipient_offset push.OUTPUT_NOTE_GET_RECIPIENT_OFFSET end @@ -516,7 +516,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_get_metadata` kernel procedure required to get #! the address where this procedure is stored. -export.output_note_get_metadata_offset +pub proc output_note_get_metadata_offset push.OUTPUT_NOTE_GET_METADATA_OFFSET end @@ -530,7 +530,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_assets_info` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_assets_info_offset +pub proc input_note_get_assets_info_offset push.INPUT_NOTE_GET_ASSETS_INFO_OFFSET end @@ -542,7 +542,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_recipient` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_recipient_offset +pub proc input_note_get_recipient_offset push.INPUT_NOTE_GET_RECIPIENT_OFFSET end @@ -554,7 +554,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_metadata` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_metadata_offset +pub proc input_note_get_metadata_offset push.INPUT_NOTE_GET_METADATA_OFFSET end @@ -566,7 +566,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_serial_number` kernel procedure required to #! get the address where this procedure is stored. -export.input_note_get_serial_number_offset +pub proc input_note_get_serial_number_offset push.INPUT_NOTE_GET_SERIAL_NUMBER_OFFSET end @@ -578,7 +578,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_inputs_info` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_inputs_info_offset +pub proc input_note_get_inputs_info_offset push.INPUT_NOTE_GET_INPUTS_INFO_OFFSET end @@ -590,7 +590,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_script_root` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_script_root_offset +pub proc input_note_get_script_root_offset push.INPUT_NOTE_GET_SCRIPT_ROOT_OFFSET end @@ -604,7 +604,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_input_notes_commitment` kernel procedure required to #! get the address where this procedure is stored. -export.tx_get_input_notes_commitment_offset +pub proc tx_get_input_notes_commitment_offset push.TX_GET_INPUT_NOTES_COMMITMENT_OFFSET end @@ -616,7 +616,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_output_notes_commitment` kernel procedure required to #! get the address where this procedure is stored. -export.tx_get_output_notes_commitment_offset +pub proc tx_get_output_notes_commitment_offset push.TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET end @@ -628,7 +628,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_num_input_notes` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_num_input_notes_offset +pub proc tx_get_num_input_notes_offset push.TX_GET_NUM_INPUT_NOTES_OFFSET end @@ -640,7 +640,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_num_output_notes` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_num_output_notes_offset +pub proc tx_get_num_output_notes_offset push.TX_GET_NUM_OUTPUT_NOTES_OFFSET end @@ -652,7 +652,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_block_commitment` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_block_commitment_offset +pub proc tx_get_block_commitment_offset push.TX_GET_BLOCK_COMMITMENT_OFFSET end @@ -664,7 +664,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_block_number` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_block_number_offset +pub proc tx_get_block_number_offset push.TX_GET_BLOCK_NUMBER_OFFSET end @@ -676,7 +676,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_block_timestamp` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_block_timestamp_offset +pub proc tx_get_block_timestamp_offset push.TX_GET_BLOCK_TIMESTAMP_OFFSET end @@ -688,7 +688,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_start_foreign_context` kernel procedure required to get #! the address where this procedure is stored. -export.tx_start_foreign_context_offset +pub proc tx_start_foreign_context_offset push.TX_START_FOREIGN_CONTEXT_OFFSET end @@ -700,7 +700,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_end_foreign_context` kernel procedure required to get the #! address where this procedure is stored. -export.tx_end_foreign_context_offset +pub proc tx_end_foreign_context_offset push.TX_END_FOREIGN_CONTEXT_OFFSET end @@ -712,7 +712,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_update_expiration_block_delta` kernel procedure required #! to get the address where this procedure is stored. -export.tx_update_expiration_block_delta_offset +pub proc tx_update_expiration_block_delta_offset push.TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET end @@ -724,6 +724,6 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_expiration_delta` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_expiration_delta_offset +pub proc tx_get_expiration_delta_offset push.TX_GET_EXPIRATION_DELTA_OFFSET end diff --git a/crates/miden-lib/asm/miden/native_account.masm b/crates/miden-lib/asm/miden/native_account.masm index 6a15423752..4810682e06 100644 --- a/crates/miden-lib/asm/miden/native_account.masm +++ b/crates/miden-lib/asm/miden/native_account.masm @@ -1,4 +1,4 @@ -use.miden::kernel_proc_offsets +use miden::kernel_proc_offsets # NATIVE ACCOUNT PROCEDURES # ================================================================================================= @@ -16,7 +16,7 @@ use.miden::kernel_proc_offsets #! transaction. #! #! Invocation: exec -export.get_id +pub proc get_id # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -52,7 +52,7 @@ end #! - the nonce has already been incremented. #! #! Invocation: exec -export.incr_nonce +pub proc incr_nonce # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -93,7 +93,7 @@ end #! #! Panics if: #! - the vault or storage delta is not empty but the nonce increment is zero. -export.compute_delta_commitment +pub proc compute_delta_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -129,7 +129,7 @@ end #! - the native account is a faucet and the provided slot ID points to the reserved faucet storage slot. #! #! Invocation: exec -export.set_item +pub proc set_item exec.kernel_proc_offsets::account_set_item_offset # => [offset, slot_id_prefix, slot_id_suffix, VALUE] @@ -166,7 +166,7 @@ end #! - the invocation of this procedure does not originate from the native account. #! #! Invocation: exec -export.set_map_item +pub proc set_map_item exec.kernel_proc_offsets::account_set_map_item_offset # => [offset, slot_id_prefix, slot_id_suffix, KEY, VALUE] @@ -208,7 +208,7 @@ end #! - the vault already contains the same non-fungible asset. #! #! Invocation: exec -export.add_asset +pub proc add_asset exec.kernel_proc_offsets::account_add_asset_offset # => [offset, ASSET] @@ -238,7 +238,7 @@ end #! - the non-fungible asset is not found in the vault. #! #! Invocation: exec -export.remove_asset +pub proc remove_asset exec.kernel_proc_offsets::account_remove_asset_offset # => [offset, ASSET] @@ -271,7 +271,7 @@ end #! - was_called is 1 if the procedure was called, 0 otherwise. #! #! Invocation: exec -export.was_procedure_called +pub proc was_procedure_called exec.kernel_proc_offsets::account_was_procedure_called_offset # => [offset, PROC_ROOT] diff --git a/crates/miden-lib/asm/miden/note.masm b/crates/miden-lib/asm/miden/note.masm index a8097a70f2..e2b62646a6 100644 --- a/crates/miden-lib/asm/miden/note.masm +++ b/crates/miden-lib/asm/miden/note.masm @@ -1,12 +1,12 @@ -use.miden::account_id -use.std::crypto::hashes::rpo -use.std::math::u64 -use.std::mem +use miden::account_id +use miden::core::crypto::hashes::rpo256 +use miden::core::math::u64 +use miden::core::mem # ERRORS # ================================================================================================= -const.ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" +const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" # NOTE UTILITY PROCEDURES # ================================================================================================= @@ -32,19 +32,18 @@ const.ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceede #! - num_inputs is greater than 1024. #! #! Invocation: exec -export.compute_inputs_commitment +pub proc compute_inputs_commitment # check that number of inputs is less than or equal to 1024 dup.1 push.1024 u32assert2.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT u32lte assert.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT # => [inputs_ptr, num_inputs] - # push 1 as the pad_inputs flag: we should pad the stack while computing the note inputs - # commitment - push.1 movdn.2 - # => [inputs_ptr, num_inputs, pad_inputs_flag] - - exec.rpo::prepare_hasher_state - exec.rpo::hash_memory_with_state + # compute the inputs commitment + # + # TODO: refactor the hash computation process as part of the + # https://github.com/0xMiden/miden-base/issues/2036 and + # https://github.com/0xMiden/miden-base/issues/2129 + exec.rpo256::pad_and_hash_elements # => [INPUTS_COMMITMENT] end @@ -54,7 +53,7 @@ end #! Output: [max_inputs_per_note] #! #! - max_inputs_per_note is the max inputs per note. -export.::miden::util::note::get_max_inputs_per_note +pub use ::miden::util::note::get_max_inputs_per_note #! Writes the assets data stored in the advice map to the memory specified by the provided #! destination pointer. @@ -66,7 +65,7 @@ export.::miden::util::note::get_max_inputs_per_note #! } #! Outputs: #! Operand stack: [num_assets, dest_ptr] -export.write_assets_to_memory +pub proc write_assets_to_memory # load the asset data from the advice map to the advice stack adv.push_mapval # OS => [ASSETS_COMMITMENT, num_assets, dest_ptr] @@ -123,7 +122,8 @@ end #! - num_inputs is greater than 1024. #! #! Invocation: exec -export.build_recipient.1 +@locals(1) +pub proc build_recipient dup.1 dup.1 # => [inputs_ptr, num_inputs, inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT] @@ -191,7 +191,7 @@ end #! - RECIPIENT is the recipient of the note. #! #! Invocation: exec -export.build_recipient_hash +pub proc build_recipient_hash padw hmerge # => [SERIAL_NUM_HASH, SCRIPT_ROOT, INPUT_COMMITMENT] @@ -211,7 +211,7 @@ end #! - METADATA is the metadata of some note. #! - sender_{prefix,suffix} are the prefix and suffix felts of the sender ID of the note which #! metadata was provided. -export.extract_sender_from_metadata +pub proc extract_sender_from_metadata # => [aux, merged_tag_hint_payload, merged_sender_id_type_hint_tag, sender_id_prefix] # drop aux felt and the felt containing tag, execution hint and payload @@ -241,7 +241,7 @@ end #! - network_account_tag is the computed network note tag. #! #! Invocation: exec -export.build_note_tag_for_network_account +pub proc build_note_tag_for_network_account swap drop # => [account_id_prefix] diff --git a/crates/miden-lib/asm/miden/output_note.masm b/crates/miden-lib/asm/miden/output_note.masm index 39c900edfc..52f713137d 100644 --- a/crates/miden-lib/asm/miden/output_note.masm +++ b/crates/miden-lib/asm/miden/output_note.masm @@ -1,5 +1,5 @@ -use.miden::kernel_proc_offsets -use.miden::note +use miden::kernel_proc_offsets +use miden::note # PROCEDURES # ================================================================================================= @@ -18,7 +18,7 @@ use.miden::note #! - note_idx is the index of the created note. #! #! Invocation: exec -export.create +pub proc create # pad the stack before the syscall to prevent accidental modification of the deeper stack # elements padw padw swapdw movup.8 drop @@ -51,7 +51,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: exec -export.get_assets_info +pub proc get_assets_info # start padding the stack push.0.0 movup.2 # => [note_index, 0, 0] @@ -97,7 +97,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: exec -export.get_assets +pub proc get_assets # get the assets commitment and assets number dup.1 exec.get_assets_info # => [ASSETS_COMMITMENT, num_assets, dest_ptr, note_index] @@ -117,7 +117,7 @@ end #! - ASSET can be a fungible or non-fungible asset. #! #! Invocation: exec -export.add_asset +pub proc add_asset movup.4 exec.kernel_proc_offsets::output_note_add_asset_offset # => [offset, note_idx, ASSET] @@ -147,7 +147,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: exec -export.get_recipient +pub proc get_recipient # start padding the stack push.0.0 movup.2 # => [note_index, 0, 0] @@ -180,7 +180,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: exec -export.get_metadata +pub proc get_metadata # start padding the stack push.0.0 movup.2 # => [note_index, 0, 0] diff --git a/crates/miden-lib/asm/miden/tx.masm b/crates/miden-lib/asm/miden/tx.masm index a6890e1fe4..fd4031f0cf 100644 --- a/crates/miden-lib/asm/miden/tx.masm +++ b/crates/miden-lib/asm/miden/tx.masm @@ -1,4 +1,4 @@ -use.miden::kernel_proc_offsets +use miden::kernel_proc_offsets #! Returns the block number of the transaction reference block. #! @@ -9,7 +9,7 @@ use.miden::kernel_proc_offsets #! - num is the transaction reference block number. #! #! Invocation: exec -export.get_block_number +pub proc get_block_number # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -34,7 +34,7 @@ end #! - BLOCK_COMMITMENT is the commitment to the reference block of the transaction. #! #! Invocation: exec -export.get_block_commitment +pub proc get_block_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -77,7 +77,7 @@ end #! Where: #! - timestamp is the timestamp of the reference block for this transaction. The underlying value is #! of type u32, so u32 operations can be safely used on it. -export.get_block_timestamp +pub proc get_block_timestamp # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -104,7 +104,7 @@ end #! - INPUT_NOTES_COMMITMENT is the input notes commitment hash. #! #! Invocation: exec -export.get_input_notes_commitment +pub proc get_input_notes_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -130,7 +130,7 @@ end #! - OUTPUT_NOTES_COMMITMENT is the output notes commitment. #! #! Invocation: exec -export.get_output_notes_commitment +pub proc get_output_notes_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -155,7 +155,7 @@ end #! - num_input_notes is the total number of input notes consumed by this transaction. #! #! Invocation: exec -export.get_num_input_notes +pub proc get_num_input_notes # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -183,7 +183,7 @@ end #! - num_output_notes is the number of output notes created in this transaction so far. #! #! Invocation: exec -export.get_num_output_notes +pub proc get_num_output_notes # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -215,7 +215,8 @@ end #! moment of the foreign procedure execution (n = 16 - mem_addr_size - foreign_inputs_len). #! #! Invocation: exec -export.execute_foreign_procedure.4 +@locals(4) +pub proc execute_foreign_procedure # get the start_foreign_context procedure offset push.0 movup.2 movup.2 exec.kernel_proc_offsets::tx_start_foreign_context_offset # => [offset, foreign_account_id_prefix, foreign_account_id_suffix, 0, FOREIGN_PROC_ROOT, , pad(n)] @@ -268,7 +269,7 @@ end #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). #! #! Annotation hint: is not used anywhere -export.update_expiration_block_delta +pub proc update_expiration_block_delta exec.kernel_proc_offsets::tx_update_expiration_block_delta_offset # => [offset, expiration_delta, ...] @@ -291,7 +292,7 @@ end #! - block_height_delta is the stored expiration time delta (1 to 0xFFFF). #! #! Annotation hint: is not used anywhere -export.get_expiration_block_delta +pub proc get_expiration_block_delta # pad the stack padw padw padw push.0.0.0 # => [pad(15)] diff --git a/crates/miden-lib/asm/note_scripts/BURN.masm b/crates/miden-lib/asm/note_scripts/BURN.masm index 42b68ed11b..acd8b4399b 100644 --- a/crates/miden-lib/asm/note_scripts/BURN.masm +++ b/crates/miden-lib/asm/note_scripts/BURN.masm @@ -1,4 +1,4 @@ -use.miden::contracts::faucets +use miden::contracts::faucets #! BURN script: burns the asset from the note by calling the faucet's burn procedure. #! This note can be executed against any faucet account that exposes the faucets::burn procedure diff --git a/crates/miden-lib/asm/note_scripts/MINT.masm b/crates/miden-lib/asm/note_scripts/MINT.masm index 65f102cfa1..d62903cebc 100644 --- a/crates/miden-lib/asm/note_scripts/MINT.masm +++ b/crates/miden-lib/asm/note_scripts/MINT.masm @@ -1,22 +1,22 @@ -use.miden::active_note -use.miden::contracts::faucets::network_fungible->network_faucet -use.miden::note +use miden::active_note +use miden::contracts::faucets::network_fungible->network_faucet +use miden::note # CONSTANTS # ================================================================================================= -const.MINT_NOTE_NUM_INPUTS_PRIVATE=8 -const.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 +const MINT_NOTE_NUM_INPUTS_PRIVATE=8 +const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 -const.OUTPUT_NOTE_TYPE_PUBLIC=1 -const.OUTPUT_NOTE_TYPE_PRIVATE=2 +const OUTPUT_NOTE_TYPE_PUBLIC=1 +const OUTPUT_NOTE_TYPE_PRIVATE=2 -const.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 -const.OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 +const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 +const OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 # ERRORS # ================================================================================================= -const.ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" +const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" #! Network Faucet MINT script: mints assets by calling the network faucet's distribute function. #! This note is intended to be executed against a network fungible faucet account. diff --git a/crates/miden-lib/asm/note_scripts/P2ID.masm b/crates/miden-lib/asm/note_scripts/P2ID.masm index f5b565d223..0cab854848 100644 --- a/crates/miden-lib/asm/note_scripts/P2ID.masm +++ b/crates/miden-lib/asm/note_scripts/P2ID.masm @@ -1,13 +1,13 @@ -use.miden::active_account -use.miden::account_id -use.miden::active_note +use miden::active_account +use miden::account_id +use miden::active_note # ERRORS # ================================================================================================= -const.ERR_P2ID_WRONG_NUMBER_OF_INPUTS="P2ID note expects exactly 2 note inputs" +const ERR_P2ID_WRONG_NUMBER_OF_INPUTS="P2ID note expects exactly 2 note inputs" -const.ERR_P2ID_TARGET_ACCT_MISMATCH="P2ID's target account address and transaction address do not match" +const ERR_P2ID_TARGET_ACCT_MISMATCH="P2ID's target account address and transaction address do not match" #! Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account #! matches target account ID specified by the note inputs. diff --git a/crates/miden-lib/asm/note_scripts/P2IDE.masm b/crates/miden-lib/asm/note_scripts/P2IDE.masm index 8e885cc02d..02502d3758 100644 --- a/crates/miden-lib/asm/note_scripts/P2IDE.masm +++ b/crates/miden-lib/asm/note_scripts/P2IDE.masm @@ -1,20 +1,20 @@ -use.miden::active_account -use.miden::account_id -use.miden::active_note -use.miden::tx +use miden::active_account +use miden::account_id +use miden::active_note +use miden::tx # ERRORS # ================================================================================================= -const.ERR_P2IDE_WRONG_NUMBER_OF_INPUTS="P2IDE note expects exactly 4 note inputs" +const ERR_P2IDE_WRONG_NUMBER_OF_INPUTS="P2IDE note expects exactly 4 note inputs" -const.ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER="failed to reclaim P2IDE note because the reclaiming account is not the sender" +const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER="failed to reclaim P2IDE note because the reclaiming account is not the sender" -const.ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED="failed to reclaim P2IDE note because the reclaim block height is not reached yet" +const ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED="failed to reclaim P2IDE note because the reclaim block height is not reached yet" -const.ERR_P2IDE_RECLAIM_DISABLED="P2IDE reclaim is disabled" +const ERR_P2IDE_RECLAIM_DISABLED="P2IDE reclaim is disabled" -const.ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note because the note is still timelocked" +const ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note because the note is still timelocked" # HELPER PROCEDURES # ================================================================================================= @@ -23,7 +23,7 @@ const.ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note becaus #! #! Inputs: [current_block_height, timelock_block_height] #! Outputs: [current_block_height] -proc.verify_unlocked +proc verify_unlocked dup movdn.2 # => [current_block_height, timelock_block_height, current_block_height] @@ -43,7 +43,7 @@ end #! - the reclaim of the active note is disabled. #! - the reclaim block height is not reached yet. #! - the account attempting to reclaim the note is not the sender account. -proc.reclaim_note +proc reclaim_note # check that the reclaim of the active note is enabled movup.3 dup neq.0 assert.err=ERR_P2IDE_RECLAIM_DISABLED # => [reclaim_block_height, account_id_prefix, account_id_suffix, current_block_height] diff --git a/crates/miden-lib/asm/note_scripts/SWAP.masm b/crates/miden-lib/asm/note_scripts/SWAP.masm index 479e328593..75eebc0074 100644 --- a/crates/miden-lib/asm/note_scripts/SWAP.masm +++ b/crates/miden-lib/asm/note_scripts/SWAP.masm @@ -1,18 +1,18 @@ -use.miden::active_note -use.miden::output_note -use.miden::contracts::wallets::basic->wallet +use miden::active_note +use miden::output_note +use miden::contracts::wallets::basic->wallet # CONSTANTS # ================================================================================================= -const.SWAP_NOTE_INPUTS_NUMBER=12 +const SWAP_NOTE_INPUTS_NUMBER=12 # ERRORS # ================================================================================================= -const.ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" +const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" -const.ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" +const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" #! Swap script: adds an asset from the note into consumers account and #! creates a note consumable by note issuer containing requested ASSET. diff --git a/crates/miden-lib/asm/shared_modules/account_id.masm b/crates/miden-lib/asm/shared_modules/account_id.masm index 4158a6e04e..4dcf7d4e11 100644 --- a/crates/miden-lib/asm/shared_modules/account_id.masm +++ b/crates/miden-lib/asm/shared_modules/account_id.masm @@ -1,50 +1,50 @@ # ERRORS # ================================================================================================= -const.ERR_ACCOUNT_ID_UNKNOWN_VERSION="unknown version in account ID" +const ERR_ACCOUNT_ID_UNKNOWN_VERSION="unknown version in account ID" -const.ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO="most significant bit of the account ID suffix must be zero" +const ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO="most significant bit of the account ID suffix must be zero" -const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in account ID" +const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in account ID" -const.ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO="least significant byte of the account ID suffix must be zero" +const ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO="least significant byte of the account ID suffix must be zero" -const.ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT="the account ID must have storage mode public if the network flag is set" +const ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT="the account ID must have storage mode public if the network flag is set" # CONSTANTS # ================================================================================================= # Bit pattern for a faucet account, after the account type mask has been applied. -const.FAUCET_ACCOUNT=0x20 # 0b10_0000 +const FAUCET_ACCOUNT=0x20 # 0b10_0000 # Bit pattern for an account w/ updatable code, after the account type mask has been applied. -const.REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 +const REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 # Bit pattern for an account w/ immutable code, after the account type mask has been applied. -const.REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 +const REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 # Bit pattern for a fungible faucet w/ immutable code, after the account type mask has been applied. -const.FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 +const FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 # Bit pattern for a non-fungible faucet w/ immutable code, after the account type mask has been # applied. -const.NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 +const NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 # Given the least significant 32 bits of an account id's prefix, this mask defines the bits used # to determine the account type. -const.ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 +const ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 # Given the least significant 32 bits of an account id's prefix, this mask defines the bits used # to determine the account version. -const.ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 +const ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 # Given the least significant 32 bits of an account ID's prefix, this mask defines the bits used # to determine the account storage mode. -const.ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 +const ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 # Given the least significant 32 bits of an account ID's first felt with the storage mode mask # applied, this value defines the non-existent, invalid storage mode. -const.ACCOUNT_ID_STORAGE_MODE_INVALID_U32=0xc0 # 0b1100_0000 +const ACCOUNT_ID_STORAGE_MODE_INVALID_U32=0xc0 # 0b1100_0000 # PROCEDURES # ================================================================================================= @@ -57,7 +57,7 @@ const.ACCOUNT_ID_STORAGE_MODE_INVALID_U32=0xc0 # 0b1100_0000 #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_fungible_faucet is a boolean indicating whether the account is a fungible faucet. -export.is_fungible_faucet +pub proc is_fungible_faucet exec.id_type eq.FUNGIBLE_FAUCET_ACCOUNT # => [is_fungible_faucet] end @@ -70,7 +70,7 @@ end #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_non_fungible_faucet is a boolean indicating whether the account is a non-fungible faucet. -export.is_non_fungible_faucet +pub proc is_non_fungible_faucet exec.id_type eq.NON_FUNGIBLE_FAUCET_ACCOUNT # => [is_non_fungible_faucet] end @@ -85,7 +85,7 @@ end #! - other_acct_id_{prefix,suffix} are the prefix and suffix felts of the other account ID to #! compare against. #! - is_id_equal is a boolean indicating whether the account IDs are equal. -export.is_equal +pub proc is_equal movup.2 eq # => [is_prefix_equal, acct_id_suffix, other_acct_id_suffix] movdn.2 eq @@ -102,7 +102,7 @@ end #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_faucet is a boolean indicating whether the account is a faucet. -export.is_faucet +pub proc is_faucet u32split drop u32and.FAUCET_ACCOUNT neq.0 # => [is_faucet] end @@ -116,7 +116,7 @@ end #! - acct_id_prefix is the prefix of the account ID. #! - is_updatable_account is a boolean indicating whether the account is a regular updatable #! account. -export.is_updatable_account +pub proc is_updatable_account exec.id_type eq.REGULAR_ACCOUNT_UPDATABLE_CODE # => [is_updatable_account] end @@ -130,7 +130,7 @@ end #! - acct_id_prefix is the prefix of the account ID. #! - is_immutable_account is a boolean indicating whether the account is a regular immutable #! account. -export.is_immutable_account +pub proc is_immutable_account exec.id_type eq.REGULAR_ACCOUNT_IMMUTABLE_CODE # => [is_immutable_account] end @@ -149,7 +149,7 @@ end #! - account_id_prefix does not contain either the public, network or private storage mode. #! - account_id_suffix does not have its most significant bit set to zero. #! - account_id_suffix does not have its lower 8 bits set to zero. -export.validate +pub proc validate # Validate version in prefix. For now only version 0 is supported. # --------------------------------------------------------------------------------------------- @@ -201,7 +201,7 @@ end #! - seed_digest_suffix is the suffix of the digest that should be shaped into the suffix #! of an account ID. #! - account_id_suffix is the suffix of an account ID. -export.shape_suffix +pub proc shape_suffix u32split swap # => [seed_digest_suffix_lo, seed_digest_suffix_hi] @@ -225,7 +225,7 @@ end #! Where: #! - account_id_prefix is the prefix of an account ID. #! - id_version is the version number of the ID. -proc.id_version +proc id_version # extract the lower 32 bits u32split drop # => [account_id_prefix_lo] @@ -249,7 +249,7 @@ end #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - acct_type is the account type. -proc.id_type +proc id_type u32split drop u32and.ACCOUNT_ID_TYPE_MASK_U32 # => [acct_type] end diff --git a/crates/miden-lib/asm/shared_utils/util/asset.masm b/crates/miden-lib/asm/shared_utils/util/asset.masm index 9d16b342f2..225ed0366d 100644 --- a/crates/miden-lib/asm/shared_utils/util/asset.masm +++ b/crates/miden-lib/asm/shared_utils/util/asset.masm @@ -4,7 +4,7 @@ # Specifies the maximum amount a fungible asset can represent. # # This is 2^63 - 2^31. See account_delta.masm for more details. -const.FUNGIBLE_ASSET_MAX_AMOUNT=0x7fffffff80000000 +const FUNGIBLE_ASSET_MAX_AMOUNT=0x7fffffff80000000 # PROCEDURES # ================================================================================================= @@ -16,7 +16,7 @@ const.FUNGIBLE_ASSET_MAX_AMOUNT=0x7fffffff80000000 #! #! Where: #! - fungible_asset_max_amount is the maximum amount of a fungible asset. -export.get_fungible_asset_max_amount +pub proc get_fungible_asset_max_amount push.FUNGIBLE_ASSET_MAX_AMOUNT # => [fungible_asset_max_amount] end @@ -31,7 +31,7 @@ end #! Where: #! - ASSET is the fungible asset from which to extract the balance. #! - balance is the amount of the fungible asset. -export.get_balance_from_fungible_asset +pub proc get_balance_from_fungible_asset drop drop drop # => [balance] end diff --git a/crates/miden-lib/asm/shared_utils/util/note.masm b/crates/miden-lib/asm/shared_utils/util/note.masm index 1a425ce13b..00a4ea88cf 100644 --- a/crates/miden-lib/asm/shared_utils/util/note.masm +++ b/crates/miden-lib/asm/shared_utils/util/note.masm @@ -2,7 +2,7 @@ # ================================================================================================= # The maximum number of input values associated with a single note. -const.MAX_INPUTS_PER_NOTE=1024 +const MAX_INPUTS_PER_NOTE=1024 # PROCEDURES # ================================================================================================= @@ -14,6 +14,6 @@ const.MAX_INPUTS_PER_NOTE=1024 #! #! Where: #! - max_inputs_per_note is the max inputs per note. -export.get_max_inputs_per_note +pub proc get_max_inputs_per_note push.MAX_INPUTS_PER_NOTE end diff --git a/crates/miden-lib/build.rs b/crates/miden-lib/build.rs index 10254ae490..6843e1e791 100644 --- a/crates/miden-lib/build.rs +++ b/crates/miden-lib/build.rs @@ -6,16 +6,9 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use fs_err as fs; -use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr, miette}; +use miden_assembly::diagnostics::{IntoDiagnostic, NamedSource, Result, WrapErr, miette}; use miden_assembly::utils::Serializable; -use miden_assembly::{ - Assembler, - DefaultSourceManager, - KernelLibrary, - Library, - LibraryNamespace, - Report, -}; +use miden_assembly::{Assembler, DefaultSourceManager, KernelLibrary, Library, Report}; use regex::Regex; use walkdir::WalkDir; @@ -144,12 +137,12 @@ fn main() -> Result<()> { /// tx_script_main.masm. /// - src/transaction/procedures/kernel_v0.rs -> contains the kernel procedures table. fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result { - let shared_utils_path = Path::new(ASM_DIR).join(SHARED_UTILS_DIR); - let kernel_namespace = LibraryNamespace::Kernel; + let shared_utils_path = std::path::Path::new(ASM_DIR).join(SHARED_UTILS_DIR); + let kernel_path = miden_assembly::Path::kernel_path(); let mut assembler = build_assembler(None)?; - // add the shared util modules to the kernel lib under the kernel::util namespace - assembler.compile_and_statically_link_from_dir(kernel_namespace.clone(), &shared_utils_path)?; + // add the shared util modules to the kernel lib under the ::$kernel::util namespace + assembler.compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; // assemble the kernel library and write it to the "tx_kernel.masl" file let kernel_lib = assembler @@ -165,11 +158,9 @@ fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result // assemble the kernel program and write it to the "tx_kernel.masb" file let mut main_assembler = assembler.clone(); - // add the shared util modules to the kernel lib under the kernel::util namespace - main_assembler - .compile_and_statically_link_from_dir(kernel_namespace.clone(), &shared_utils_path)?; - main_assembler - .compile_and_statically_link_from_dir(kernel_namespace.clone(), source_dir.join("lib"))?; + // add the shared util modules to the kernel lib under the ::$kernel::util namespace + main_assembler.compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; + main_assembler.compile_and_statically_link_from_dir(source_dir.join("lib"), kernel_path)?; let main_file_path = source_dir.join("main.masm"); let kernel_main = main_assembler.clone().assemble_program(main_file_path)?; @@ -187,12 +178,12 @@ fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result // This is needed in test assemblers to access individual procedures which would otherwise // be hidden when using KernelLibrary (api.masm) - // add the shared util modules to the kernel lib under the kernel::util namespace + // add the shared util modules to the kernel lib under the ::$kernel::util namespace kernel_lib_assembler - .compile_and_statically_link_from_dir(kernel_namespace.clone(), &shared_utils_path)?; + .compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; let test_lib = kernel_lib_assembler - .assemble_library_from_dir(source_dir.join("lib"), kernel_namespace) + .assemble_library_from_dir(source_dir.join("lib"), kernel_path) .unwrap(); let masb_file_path = @@ -232,6 +223,7 @@ fn generate_kernel_proc_hash_file(kernel: KernelLibrary) -> Result<()> { let to_exclude = BTreeSet::from_iter(["exec_kernel_proc"]); let offsets_filename = Path::new(ASM_DIR).join(ASM_MIDEN_DIR).join("kernel_proc_offsets.masm"); let offsets = parse_proc_offsets(&offsets_filename)?; + let generated_procs: BTreeMap = module_info .procedures() .filter(|(_, proc_info)| !to_exclude.contains::(proc_info.name.as_ref())) @@ -276,7 +268,7 @@ pub const KERNEL_PROCEDURES: [Word; {proc_count}] = [ } fn parse_proc_offsets(filename: impl AsRef) -> Result> { - let regex: Regex = Regex::new(r"^const\.(?P\w+)_OFFSET\s*=\s*(?P\d+)").unwrap(); + let regex: Regex = Regex::new(r"^const\s*(?P\w+)_OFFSET\s*=\s*(?P\d+)").unwrap(); let mut result = BTreeMap::new(); for line in fs::read_to_string(filename).into_diagnostic()?.lines() { if let Some(captures) = regex.captures(line) { @@ -303,11 +295,10 @@ fn compile_miden_lib( let source_dir = source_dir.join(ASM_MIDEN_DIR); let shared_path = Path::new(ASM_DIR).join(SHARED_UTILS_DIR); - let miden_namespace = "miden".parse::().expect("invalid base namespace"); // add the shared modules to the kernel lib under the miden::util namespace - assembler.compile_and_statically_link_from_dir(miden_namespace.clone(), &shared_path)?; + assembler.compile_and_statically_link_from_dir(&shared_path, "miden")?; - let miden_lib = assembler.assemble_library_from_dir(source_dir, miden_namespace)?; + let miden_lib = assembler.assemble_library_from_dir(source_dir, "miden")?; let output_file = target_dir.join("miden").with_extension(Library::LIBRARY_EXTENSION); miden_lib.write_to_file(output_file).into_diagnostic()?; @@ -369,15 +360,16 @@ fn compile_account_components( .expect("file stem should be valid UTF-8") .to_owned(); - // Read the source code to string instead of passing it to assemble_library directly since - // that would attempt to interpret the path as a LibraryPath which would fail. let component_source_code = fs::read_to_string(masm_file_path) .expect("reading the component's MASM source code should succeed"); + let named_source = NamedSource::new(component_name.clone(), component_source_code); + let component_library = assembler .clone() - .assemble_library([component_source_code]) + .assemble_library([named_source]) .expect("library assembly should succeed"); + let component_file_path = target_dir.join(component_name).with_extension(Library::LIBRARY_EXTENSION); component_library.write_to_file(component_file_path).into_diagnostic()?; @@ -389,15 +381,12 @@ fn compile_account_components( // HELPER FUNCTIONS // ================================================================================================ -/// Returns a new [Assembler] loaded with miden-stdlib and the specified kernel, if provided. -/// -/// The returned assembler will be in the `debug` mode if the `with-debug-info` feature is enabled. +/// Returns a new [Assembler] loaded with miden-core-lib and the specified kernel, if provided. fn build_assembler(kernel: Option) -> Result { kernel .map(|kernel| Assembler::with_kernel(Arc::new(DefaultSourceManager::default()), kernel)) .unwrap_or_default() - .with_debug_mode(cfg!(feature = "with-debug-info")) - .with_dynamic_library(miden_stdlib::StdLibrary::default()) + .with_dynamic_library(miden_core_lib::CoreLibrary::default()) } /// Recursively copies `src` into `dst`. @@ -448,7 +437,7 @@ fn copy_directory, R: AsRef>(src: T, dst: R) -> Result<()> /// This is required to include the shared modules as APIs of the `kernel` and `miden` libraries. /// /// This is done to make it possible to import the modules in the `shared_modules` folder directly, -/// i.e. "use.$kernel::account_id". +/// i.e. "use $kernel::account_id". fn copy_shared_modules>(source_dir: T) -> Result<()> { // source is expected to be an `OUT_DIR/asm` folder let shared_modules_dir = source_dir.as_ref().join(SHARED_MODULES_DIR); @@ -517,7 +506,7 @@ fn is_masm_file(path: &Path) -> io::Result { /// For example: /// /// ```text -/// const.ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" +/// const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" /// ``` /// /// would generate a Rust file for transaction kernel errors (since the error belongs to that @@ -589,7 +578,7 @@ fn extract_masm_errors( errors: &mut BTreeMap, file_contents: &str, ) -> Result<()> { - let regex = Regex::new(r#"const(\.|\ )ERR_(?.*)\ ?=\ ?"(?.*)""#).unwrap(); + let regex = Regex::new(r#"const\s*ERR_(?.*)\s*=\s*"(?.*)""#).unwrap(); for capture in regex.captures_iter(file_contents) { let error_name = capture @@ -655,7 +644,7 @@ fn generate_error_file_content(category: ErrorCategory, errors: Vec) "// This file is generated by build.rs, do not modify manually. // It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. // -// To add a new error, define a constant in masm of the pattern `const.ERR__...`. +// To add a new error, define a constant in masm of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, // Non-Fungible-Asset, ...). " @@ -793,7 +782,7 @@ fn generate_event_constants(asm_source_dir: &Path, target_dir: &Path) -> Result< Ok(()) } -/// Extract all `const.X=event("x")` definitions from all MASM files +/// Extract all `const X=event("x")` definitions from all MASM files fn extract_all_event_definitions(asm_source_dir: &Path) -> Result> { // collect mappings event path to const variable name, we want a unique mapping // which we use to generate the constants and enum variant names @@ -812,19 +801,17 @@ fn extract_all_event_definitions(asm_source_dir: &Path) -> Result, file_contents: &str, file_path: &Path, ) -> Result<()> { - let regex = Regex::new(r#"const(\.|\ )(\w+)\ ?=\ ?event\("([^"]+)"\)"#).unwrap(); + let regex = Regex::new(r#"const\s*(\w+)\s*=\s*event\("([^"]+)"\)"#).unwrap(); for capture in regex.captures_iter(file_contents) { - let const_name = capture.get(2).expect("const name should be captured"); - let event_path = capture.get(3).expect("event path should be captured"); + let const_name = capture.get(1).expect("const name should be captured"); + let event_path = capture.get(2).expect("event path should be captured"); let event_path = event_path.as_str(); let const_name = const_name.as_str(); @@ -837,13 +824,7 @@ fn extract_event_definitions_from_file( }; if !event_path.starts_with("miden::") { - // we ignore any `stdlib::` prefixed ones - if !event_path.starts_with("stdlib::") { - return Err(miette::miette!( - "unhandled `event_path={event_path}`, doesn't with `stdlib::` nor with `miden::`." - )); - } - continue; + return Err(miette::miette!("unhandled `event_path={event_path}`")); } // Check for duplicates with different definitions @@ -876,7 +857,7 @@ fn generate_event_file_content( // Generate constants // - // Note: If we ever encounter two constants `const.X`, that are both named `X` we will error + // Note: If we ever encounter two constants `const X`, that are both named `X` we will error // when attempting to generate the rust code. Currently this is a side-effect, but we // want to error out as early as possible: // TODO: make the error out at build-time to be able to present better error hints diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs index 00c8078cc4..80029eb351 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs @@ -12,12 +12,14 @@ static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing the ECDSA K256 Keccak signature scheme for authentication /// of transactions. /// -/// It reexports the procedures from `miden::contracts::auth::basic`. When linking against this -/// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures +/// It reexports the procedures from `miden::contracts::auth::ecdsa_k256_keccak`. When linking +/// against this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be +/// available to the assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: -/// - `auth_tx_ecdsa_k256_keccak`, which can be used to verify a signature provided via the advice -/// stack to authenticate a transaction. +/// - `verify_signatures`, which can be used to verify a signature provided via the advice stack to +/// authenticate a transaction. +/// - `authenticate_transaction`, which can be used to authenticate a transaction using the ECDSA +/// signature scheme. /// /// This component supports all account types. /// diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs index 9d0795915d..1c0f5e0b01 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs @@ -12,12 +12,14 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing the RpoFalcon512 signature scheme for authentication of /// transactions. /// -/// It reexports the procedures from `miden::contracts::auth::basic`. When linking against this -/// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures -/// of this component are: -/// - `auth_tx_rpo_falcon512`, which can be used to verify a signature provided via the advice stack -/// to authenticate a transaction. +/// It reexports the procedures from `miden::contracts::auth::rpo_falcon512`. When linking against +/// this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to +/// the assembler which is the case when using [`CodeBuilder`][builder]. The procedures of this +/// component are: +/// - `verify_signatures`, which can be used to verify a signature provided via the advice stack to +/// authenticate a transaction. +/// - `authenticate_transaction`, which can be used to authenticate a transaction using the Falcon +/// signature scheme. /// /// This component supports all account types. /// diff --git a/crates/miden-lib/src/account/components/mod.rs b/crates/miden-lib/src/account/components/mod.rs index b16d1984b9..152e91a644 100644 --- a/crates/miden-lib/src/account/components/mod.rs +++ b/crates/miden-lib/src/account/components/mod.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use miden_objects::Word; use miden_objects::account::AccountProcedureRoot; -use miden_objects::assembly::Library; +use miden_objects::assembly::{Library, LibraryExport}; use miden_objects::utils::Deserializable; use miden_objects::utils::sync::LazyLock; use miden_processor::MastNodeExt; @@ -39,7 +39,7 @@ static ECDSA_K256_KECCAK_ACL_LIBRARY: LazyLock = LazyLock::new(|| { static ECDSA_K256_KECCAK_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/multisig_ecdsa_k256_keccak.masl" + "/assets/account_components/ecdsa_k256_keccak_multisig.masl" )); Library::read_from_bytes(bytes) .expect("Shipped Multisig Ecdsa K256 Keccak library is well-formed") @@ -89,7 +89,7 @@ static NO_AUTH_LIBRARY: LazyLock = LazyLock::new(|| { static RPO_FALCON_512_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/multisig_rpo_falcon_512.masl" + "/assets/account_components/rpo_falcon_512_multisig.masl" )); Library::read_from_bytes(bytes).expect("Shipped Multisig Rpo Falcon 512 library is well-formed") }); @@ -177,13 +177,16 @@ impl WellKnownComponent { Self::AuthNoAuth => NO_AUTH_LIBRARY.as_ref(), }; - library.exports().map(|export| { - library - .mast_forest() - .get_node_by_id(export.node) - .expect("export node not in the forest") - .digest() - }) + library + .exports() + .filter(|export| matches!(export, LibraryExport::Procedure(_))) + .map(|proc_export| { + library + .mast_forest() + .get_node_by_id(proc_export.unwrap_procedure().node) + .expect("export node not in the forest") + .digest() + }) } /// Checks whether procedures from the current component are present in the procedures map diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 481f131155..0c7d81cac5 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -74,8 +74,8 @@ impl BasicFungibleFaucet { /// The maximum number of decimals supported by the component. pub const MAX_DECIMALS: u8 = 12; - const DISTRIBUTE_PROC_NAME: &str = "distribute"; - const BURN_PROC_NAME: &str = "burn"; + const DISTRIBUTE_PROC_NAME: &str = "basic_fungible_faucet::distribute"; + const BURN_PROC_NAME: &str = "basic_fungible_faucet::burn"; // CONSTRUCTORS // -------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 25d88717f7..023091d144 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -72,8 +72,8 @@ impl NetworkFungibleFaucet { /// The maximum number of decimals supported by the component. pub const MAX_DECIMALS: u8 = 12; - const DISTRIBUTE_PROC_NAME: &str = "distribute"; - const BURN_PROC_NAME: &str = "burn"; + const DISTRIBUTE_PROC_NAME: &str = "network_fungible_faucet::distribute"; + const BURN_PROC_NAME: &str = "network_fungible_faucet::burn"; // CONSTRUCTORS // -------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-lib/src/account/interface/mod.rs index 2b51c9d17a..9094f43d23 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-lib/src/account/interface/mod.rs @@ -237,7 +237,6 @@ impl AccountInterface { &self, output_notes: &[PartialNote], expiration_delta: Option, - in_debug_mode: bool, ) -> Result { let note_creation_source = self.build_create_notes_section(output_notes)?; @@ -247,7 +246,7 @@ impl AccountInterface { note_creation_source, ); - let tx_script = CodeBuilder::new(in_debug_mode) + let tx_script = CodeBuilder::new() .compile_tx_script(script) .map_err(AccountInterfaceError::InvalidTransactionScript)?; diff --git a/crates/miden-lib/src/account/interface/test.rs b/crates/miden-lib/src/account/interface/test.rs index a574e03284..c9beb24218 100644 --- a/crates/miden-lib/src/account/interface/test.rs +++ b/crates/miden-lib/src/account/interface/test.rs @@ -143,9 +143,9 @@ fn test_basic_wallet_default_notes() { #[test] fn test_custom_account_default_note() { let account_custom_code_source = " - use.miden::contracts::wallets::basic + use miden::contracts::wallets::basic - export.basic::receive_asset + pub use basic::receive_asset "; let account_code = CodeBuilder::default() @@ -262,9 +262,9 @@ fn test_basic_wallet_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use.miden::tx - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::tx + use miden::contracts::wallets::basic->wallet + use miden::contracts::faucets::basic_fungible->fungible_faucet begin push.1 @@ -292,8 +292,8 @@ fn test_basic_wallet_custom_notes() { ); let incompatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::contracts::wallets::basic->wallet + use miden::contracts::faucets::basic_fungible->fungible_faucet begin push.1 @@ -352,8 +352,8 @@ fn test_basic_fungible_faucet_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::contracts::wallets::basic->wallet + use miden::contracts::faucets::basic_fungible->fungible_faucet begin push.1 @@ -380,8 +380,8 @@ fn test_basic_fungible_faucet_custom_notes() { ); let incompatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::contracts::wallets::basic->wallet + use miden::contracts::faucets::basic_fungible->fungible_faucet begin push.1 @@ -418,11 +418,11 @@ fn test_basic_fungible_faucet_custom_notes() { #[test] fn test_custom_account_custom_notes() { let account_custom_code_source = " - export.procedure_1 + pub proc procedure_1 push.1.2.3.4 dropw end - export.procedure_2 + pub proc procedure_2 push.5.6.7.8 dropw end "; @@ -462,8 +462,8 @@ fn test_custom_account_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.test::account::component_1->test_account + use miden::contracts::wallets::basic->wallet + use test::account::component_1->test_account begin push.1 @@ -493,8 +493,8 @@ fn test_custom_account_custom_notes() { ); let incompatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.test::account::component_1->test_account + use miden::contracts::wallets::basic->wallet + use test::account::component_1->test_account begin push.1 @@ -528,11 +528,11 @@ fn test_custom_account_custom_notes() { #[test] fn test_custom_account_multiple_components_custom_notes() { let account_custom_code_source = " - export.procedure_1 + pub proc procedure_1 push.1.2.3.4 dropw end - export.procedure_2 + pub proc procedure_2 push.5.6.7.8 dropw end "; @@ -573,10 +573,9 @@ fn test_custom_account_multiple_components_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::auth::basic->basic_auth - use.test::account::component_1->test_account - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::contracts::wallets::basic->wallet + use test::account::component_1->test_account + use miden::contracts::faucets::basic_fungible->fungible_faucet begin push.1 @@ -611,10 +610,9 @@ fn test_custom_account_multiple_components_custom_notes() { ); let incompatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::auth::basic->basic_auth - use.test::account::component_1->test_account - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::contracts::wallets::basic->wallet + use test::account::component_1->test_account + use miden::contracts::faucets::basic_fungible->fungible_faucet begin push.1 diff --git a/crates/miden-lib/src/account/mod.rs b/crates/miden-lib/src/account/mod.rs index 78afc9f8dd..4f4b0694a2 100644 --- a/crates/miden-lib/src/account/mod.rs +++ b/crates/miden-lib/src/account/mod.rs @@ -32,13 +32,7 @@ macro_rules! procedure_digest { ($name:ident, $proc_name:expr, $library_fn:expr) => { static $name: miden_objects::utils::sync::LazyLock = miden_objects::utils::sync::LazyLock::new(|| { - let qualified_name = miden_objects::assembly::QualifiedProcedureName::new( - ::core::default::Default::default(), - miden_objects::assembly::ProcedureName::new($proc_name).unwrap_or_else(|_| { - panic!("failed to create name for '{}' procedure", $proc_name) - }), - ); - $library_fn().get_procedure_root_by_name(qualified_name).unwrap_or_else(|| { + $library_fn().get_procedure_root_by_path($proc_name).unwrap_or_else(|| { panic!("{} should contain '{}' procedure", stringify!($library_fn), $proc_name) }) }); diff --git a/crates/miden-lib/src/account/wallets/mod.rs b/crates/miden-lib/src/account/wallets/mod.rs index 3fc7851efd..efd95a597e 100644 --- a/crates/miden-lib/src/account/wallets/mod.rs +++ b/crates/miden-lib/src/account/wallets/mod.rs @@ -60,8 +60,8 @@ pub struct BasicWallet; impl BasicWallet { // CONSTANTS // -------------------------------------------------------------------------------------------- - const RECEIVE_ASSET_PROC_NAME: &str = "receive_asset"; - const MOVE_ASSET_TO_NOTE_PROC_NAME: &str = "move_asset_to_note"; + const RECEIVE_ASSET_PROC_NAME: &str = "basic_wallet::receive_asset"; + const MOVE_ASSET_TO_NOTE_PROC_NAME: &str = "basic_wallet::move_asset_to_note"; // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/src/errors/note_script_errors.rs b/crates/miden-lib/src/errors/note_script_errors.rs index e64fa97c35..debd063d91 100644 --- a/crates/miden-lib/src/errors/note_script_errors.rs +++ b/crates/miden-lib/src/errors/note_script_errors.rs @@ -3,7 +3,7 @@ use crate::errors::MasmError; // This file is generated by build.rs, do not modify manually. // It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. // -// To add a new error, define a constant in masm of the pattern `const.ERR__...`. +// To add a new error, define a constant in masm of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, // Non-Fungible-Asset, ...). diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index 530c59315a..b1de5fa4cc 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -3,7 +3,7 @@ use crate::errors::MasmError; // This file is generated by build.rs, do not modify manually. // It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. // -// To add a new error, define a constant in masm of the pattern `const.ERR__...`. +// To add a new error, define a constant in masm of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, // Non-Fungible-Asset, ...). diff --git a/crates/miden-lib/src/lib.rs b/crates/miden-lib/src/lib.rs index 126df397a1..c04aeff6f5 100644 --- a/crates/miden-lib/src/lib.rs +++ b/crates/miden-lib/src/lib.rs @@ -28,7 +28,7 @@ pub mod testing; // RE-EXPORTS // ================================================================================================ -pub use miden_stdlib::StdLibrary; +pub use miden_core_lib::CoreLibrary; // CONSTANTS // ================================================================================================ @@ -77,18 +77,18 @@ impl Default for MidenLib { // NOTE: Most kernel-related tests can be found under /miden-tx/kernel_tests #[cfg(all(test, feature = "std"))] mod tests { - use miden_objects::assembly::LibraryPath; + use miden_objects::assembly::Path; use super::MidenLib; #[test] fn test_compile() { - let path = "miden::active_account::get_id".parse::().unwrap(); + let path = Path::new("::miden::active_account::get_id"); let miden = MidenLib::default(); let exists = miden.0.module_infos().any(|module| { module .procedures() - .any(|(_, proc)| module.path().clone().append(&proc.name).unwrap() == path) + .any(|(_, proc)| module.path().join(&proc.name).as_path() == path) }); assert!(exists); diff --git a/crates/miden-lib/src/testing/account_component/conditional_auth.rs b/crates/miden-lib/src/testing/account_component/conditional_auth.rs index fbc3a7d989..9a1e0ea8da 100644 --- a/crates/miden-lib/src/testing/account_component/conditional_auth.rs +++ b/crates/miden-lib/src/testing/account_component/conditional_auth.rs @@ -10,11 +10,11 @@ pub const ERR_WRONG_ARGS_MSG: &str = "auth procedure args are incorrect"; static CONDITIONAL_AUTH_CODE: LazyLock = LazyLock::new(|| { format!( r#" - use.miden::native_account + use miden::native_account - const.WRONG_ARGS="{ERR_WRONG_ARGS_MSG}" + const WRONG_ARGS="{ERR_WRONG_ARGS_MSG}" - export.auth_conditional + pub proc auth_conditional # => [AUTH_ARGS] # If [97, 98, 99] is passed as an argument, all good. diff --git a/crates/miden-lib/src/testing/account_component/incr_nonce.rs b/crates/miden-lib/src/testing/account_component/incr_nonce.rs index a505205dad..134f8f3bed 100644 --- a/crates/miden-lib/src/testing/account_component/incr_nonce.rs +++ b/crates/miden-lib/src/testing/account_component/incr_nonce.rs @@ -5,9 +5,9 @@ use miden_objects::utils::sync::LazyLock; use crate::utils::CodeBuilder; const INCR_NONCE_AUTH_CODE: &str = " - use.miden::native_account + use miden::native_account - export.auth_incr_nonce + pub proc auth_incr_nonce exec.native_account::incr_nonce drop end "; diff --git a/crates/miden-lib/src/testing/mock_account_code.rs b/crates/miden-lib/src/testing/mock_account_code.rs index 86a3d4f888..81d4b5a066 100644 --- a/crates/miden-lib/src/testing/mock_account_code.rs +++ b/crates/miden-lib/src/testing/mock_account_code.rs @@ -5,30 +5,30 @@ use miden_objects::utils::sync::LazyLock; use crate::utils::CodeBuilder; const MOCK_FAUCET_CODE: &str = " - use.miden::faucet + use miden::faucet #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET, pad(12)] - export.mint + pub proc mint exec.faucet::mint # => [ASSET, pad(12)] end #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET, pad(12)] - export.burn + pub proc burn exec.faucet::burn # => [ASSET, pad(12)] end "; const MOCK_ACCOUNT_CODE: &str = " - use.miden::active_account - use.miden::native_account - use.miden::tx + use miden::active_account + use miden::native_account + use miden::tx - export.::miden::contracts::wallets::basic::receive_asset - export.::miden::contracts::wallets::basic::move_asset_to_note + pub use ::miden::contracts::wallets::basic::receive_asset + pub use ::miden::contracts::wallets::basic::move_asset_to_note # Note: all account's export procedures below should be only called or dyncall'ed, so it # is assumed that the operand stack at the beginning of their execution is pad'ed and @@ -36,14 +36,14 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] #! Outputs: [OLD_VALUE, pad(12)] - export.set_item + pub proc set_item exec.native_account::set_item # => [OLD_VALUE, pad(12)] end #! Inputs: [slot_id_prefix, slot_id_suffix, pad(14)] #! Outputs: [VALUE, pad(12)] - export.get_item + pub proc get_item exec.active_account::get_item # => [VALUE, pad(14)] @@ -54,7 +54,7 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [slot_id_prefix, slot_id_suffix, pad(14)] #! Outputs: [VALUE, pad(12)] - export.get_initial_item + pub proc get_initial_item exec.active_account::get_initial_item # => [VALUE, pad(14)] @@ -65,28 +65,28 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] - export.set_map_item + pub proc set_map_item exec.native_account::set_map_item # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] end #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] #! Outputs: [VALUE, pad(12)] - export.get_map_item + pub proc get_map_item exec.active_account::get_map_item # => [VALUE, pad(12)] end #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] #! Outputs: [INIT_VALUE, pad(12)] - export.get_initial_map_item + pub proc get_initial_map_item exec.active_account::get_initial_map_item # => [INIT_VALUE, pad(12)] end #! Inputs: [pad(16)] #! Outputs: [CODE_COMMITMENT, pad(12)] - export.get_code_commitment + pub proc get_code_commitment exec.active_account::get_code_commitment # => [CODE_COMMITMENT, pad(16)] @@ -97,7 +97,7 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [pad(16)] #! Outputs: [CODE_COMMITMENT, pad(12)] - export.compute_storage_commitment + pub proc compute_storage_commitment exec.active_account::compute_storage_commitment # => [STORAGE_COMMITMENT, pad(16)] @@ -107,21 +107,21 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET', pad(12)] - export.add_asset + pub proc add_asset exec.native_account::add_asset # => [ASSET', pad(12)] end #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET, pad(12)] - export.remove_asset + pub proc remove_asset exec.native_account::remove_asset # => [ASSET, pad(12)] end #! Inputs: [pad(16)] #! Outputs: [3, pad(12)] - export.account_procedure_1 + pub proc account_procedure_1 push.1.2 add # truncate the stack @@ -130,7 +130,7 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [pad(16)] #! Outputs: [1, pad(12)] - export.account_procedure_2 + pub proc account_procedure_2 push.2.1 sub # truncate the stack diff --git a/crates/miden-lib/src/testing/mock_util_lib.rs b/crates/miden-lib/src/testing/mock_util_lib.rs index 7be47cb256..c9d9e2c951 100644 --- a/crates/miden-lib/src/testing/mock_util_lib.rs +++ b/crates/miden-lib/src/testing/mock_util_lib.rs @@ -4,11 +4,11 @@ use miden_objects::utils::sync::LazyLock; use crate::utils::CodeBuilder; const MOCK_UTIL_LIBRARY_CODE: &str = " - use.miden::output_note + use miden::output_note # Inputs: [] # Outputs: [note_idx] - export.create_random_note + pub proc create_random_note push.1.2.3.4 # = RECIPIENT push.1 # = NoteExecutionHint::Always push.2 # = NoteType::Private @@ -22,7 +22,7 @@ const MOCK_UTIL_LIBRARY_CODE: &str = " # Inputs: [ASSET] # Outputs: [] - export.create_random_note_with_asset + pub proc create_random_note_with_asset exec.create_random_note # => [note_idx, ASSET] @@ -35,7 +35,7 @@ const MOCK_UTIL_LIBRARY_CODE: &str = " "; static MOCK_UTIL_LIBRARY: LazyLock = LazyLock::new(|| { - CodeBuilder::new(false) + CodeBuilder::new() .compile_component_code("mock::util", MOCK_UTIL_LIBRARY_CODE) .expect("mock util library should be valid") .into_library() diff --git a/crates/miden-lib/src/transaction/inputs.rs b/crates/miden-lib/src/transaction/inputs.rs index d49641c942..79d6f4255b 100644 --- a/crates/miden-lib/src/transaction/inputs.rs +++ b/crates/miden-lib/src/transaction/inputs.rs @@ -4,7 +4,8 @@ use miden_objects::account::{AccountHeader, AccountId, PartialAccount}; use miden_objects::asset::AssetWitness; use miden_objects::block::account_tree::AccountWitness; use miden_objects::crypto::SequentialCommit; -use miden_objects::crypto::merkle::{InnerNodeInfo, SmtProof}; +use miden_objects::crypto::merkle::InnerNodeInfo; +use miden_objects::crypto::merkle::smt::SmtProof; use miden_objects::transaction::{AccountInputs, InputNote, PartialBlockchain, TransactionInputs}; use miden_objects::vm::AdviceInputs; use miden_objects::{EMPTY_WORD, Felt, FieldElement, Word, ZERO}; diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-lib/src/transaction/kernel_procedures.rs index 47cea98a15..03c50d542f 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-lib/src/transaction/kernel_procedures.rs @@ -24,33 +24,33 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_compute_storage_commitment word!("0xaa54d5c070ad5e2a39c9b8b3a17aaa3c9e9387c35c59f72c965060ba91e4f748"), // account_get_item - word!("0x6e3dc7fd548bfd6c802b6799be6e996eff247ecb2f16834a9363eca1b1751607"), + word!("0x9a4c4b33fde23e9fbe4c78b3430a058cc7ef8b5e487de19fa77d9d52a934e903"), // account_get_initial_item - word!("0x4835f7023bcafd46f28ece1e5c879f0f99191f78e4026c5c0590c01edc8ab598"), + word!("0x182573f7527df3b1e0b7cd28a7a2cb722776654d90219792806923bffe9d74df"), // account_set_item - word!("0x07737e8ce29230526bb76b64f64da0e45739363e11b11cb746657adaa749b37c"), + word!("0x1e1a7357928f22e54c4328d2ad611883c7bcd09c0e5368d52835f3cdb28f3411"), // account_get_map_item - word!("0xd816dc31953635af927407f3b1cd8f1ec6825522363a93c264cc36eaeb23d019"), + word!("0xba1f281fe8c8ef584bfc1615962cbde4573ac01173bb113a5c79bcecf4eb1c65"), // account_get_initial_map_item - word!("0x773b9197efb18287cc9f1009ea53dbd3945b79b7168717b6a56ff0292083d1ea"), + word!("0x7f00c7140a71d12d1162a9cf0143bdd64bbd7e8d63b115fc3a2b07338813f8ab"), // account_set_map_item - word!("0x5e8aafb3ad8a442e29c9850c372d391ffbfd5e55e90109d33e4017d7c2626e62"), + word!("0x77df8fd76b36f7750d968f138ae85ea880a6c0a3f21b6bfab88919a509c81880"), // account_get_initial_vault_root word!("0x46297d9ac95afd60c7ef1a065e024ad49aa4c019f6b3924191905449b244d4ec"), // account_get_vault_root word!("0x42a2bfb8eac4fce9bbf75ea15215b00729faeeaf7fff784692948d3f618a9bb7"), // account_add_asset - word!("0x222ae6f550bb09b675cd73acf2e8ab25e4b8f06afa3ef1a1f66d3372e843ff3e"), + word!("0xb0f56deca8a478de98114c0aaafd96732f9be5db6fa94c1d4e0cf71ed5958e53"), // account_remove_asset - word!("0xc927af9fb41cece5eeafa4cc05ad4d82ec7771e8bc94db677a357bfca1f92ca7"), + word!("0xe70870b0f7baca27f3c6311ed322159af037b2bb0c7e90c4ac5ac0b5feaabc8c"), // account_get_balance - word!("0x1ed792cc7775aa1ce2f32367a3d430561ec9bceb33f5bb222691c49a6bde8112"), + word!("0x6a5eb788fd2beec7555874f978a4dd2f2c4f5d8088cd33e148c61450e4510fe1"), // account_get_initial_balance - word!("0xdc1320d6f044c40d37e5e835a584b643d6e77e3fc1136f498815298e28c912b8"), + word!("0x2e0decbc35a10c15ba72c14ed3e32dc9d4a3866f66114c073b3fc7b127362b74"), // account_has_non_fungible_asset - word!("0xfaad11de0c026551df15231790c2364cc598e891444bf826da01b524b1a8ca8f"), + word!("0xffe57961158c8e5f8a3aaa773943ee208fac7ed4786a7c8b6fed04ba54f39111"), // account_compute_delta_commitment - word!("0xd7ced8e16079d9c775e7dfc6b9d4b7a946ccd973fcd0ccaa84095bf8e10561a2"), + word!("0x5152e37bf0e1b5e0673f129cc84eb97b1c4cfb85127002169e0a2d827145f872"), // account_get_num_procedures word!("0x53b5ec38b7841948762c258010e6e07ad93963bcaac2d83813f8edb6710dc720"), // account_get_procedure_root @@ -60,13 +60,13 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_procedure word!("0xb0b63fdd01af0bcb4aacb2412e934cdc7691308647152d416c7ae4fc909da076"), // faucet_mint_asset - word!("0xf44dbb74a478e3ac63eada9680c32d57890e481f7ade41382cbf2a98592320fe"), + word!("0xc9c3e3a69799564bfb0424a9a473ae4f9f5a2d4d5508a166cdecfaf27e9bdc5b"), // faucet_burn_asset - word!("0x522a0592e159ac3fa15bfc73bba8afd1b753fdb9f86a6526f3c02a25b42bacec"), + word!("0xac5a428ad9fdf8b1879bb0e4d6e24ad8bf3aee8bc3b6d1f43c85440d63820308"), // faucet_get_total_fungible_asset_issuance - word!("0x51cfb361598b5dd34ff93ae2c3788e7c1cd702e8831e2ea1d45dfc2ccd97876f"), + word!("0xdd8624380e31863946214a233afffde6529a56b196d0c2214b53b810a4a3251f"), // faucet_is_non_fungible_asset_issued - word!("0xb4e83658a2c6b89ce2562fed3a6dfa141f7ff00c3d36a89d0d06f70b6690f133"), + word!("0xe53348efccf43a1d0ef2b55ea688bdd930130568f3427871daf705737a7e5066"), // input_note_get_metadata word!("0x7ad3e94585e7a397ee27443c98b376ed8d4ba762122af6413fde9314c00a6219"), // input_note_get_assets_info @@ -104,7 +104,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // tx_get_block_timestamp word!("0x7903185b847517debb6c2072364e3e757b99ee623e97c2bd0a4661316c5c5418"), // tx_start_foreign_context - word!("0x923b99b2b14173d265dfdeda4375fb4195f1246a6c96b102b256bb1a6400d2e9"), + word!("0xa44d1bcc1c42ec52df448ebf9362aefc61223a374aa14cad6cf51d406c4b1c4c"), // tx_end_foreign_context word!("0xaa0018aa8da890b73511879487f65553753fb7df22de380dd84c11e6f77eec6f"), // tx_get_expiration_delta diff --git a/crates/miden-lib/src/transaction/mod.rs b/crates/miden-lib/src/transaction/mod.rs index 2c592597fb..76b28ef97e 100644 --- a/crates/miden-lib/src/transaction/mod.rs +++ b/crates/miden-lib/src/transaction/mod.rs @@ -2,6 +2,7 @@ use alloc::string::ToString; use alloc::sync::Arc; use alloc::vec::Vec; +use miden_core_lib::CoreLibrary; use miden_objects::account::AccountId; #[cfg(any(feature = "testing", test))] use miden_objects::assembly::Library; @@ -15,7 +16,6 @@ use miden_objects::utils::serde::Deserializable; use miden_objects::utils::sync::LazyLock; use miden_objects::vm::{AdviceInputs, Program, ProgramInfo, StackInputs, StackOutputs}; use miden_objects::{Felt, Hasher, TransactionOutputError, Word}; -use miden_stdlib::StdLibrary; use super::MidenLib; @@ -141,19 +141,19 @@ impl TransactionKernel { // -------------------------------------------------------------------------------------------- /// Returns a new Miden assembler instantiated with the transaction kernel and loaded with the - /// Miden stdlib as well as with miden-lib. + /// core lib as well as with miden-lib. pub fn assembler() -> Assembler { Self::assembler_with_source_manager(Arc::new(DefaultSourceManager::default())) } /// Returns a new assembler instantiated with the transaction kernel and loaded with the - /// Miden stdlib as well as with miden-lib. + /// core lib as well as with miden-lib. pub fn assembler_with_source_manager(source_manager: Arc) -> Assembler { #[cfg(all(any(feature = "testing", test), feature = "std"))] source_manager_ext::load_masm_source_files(&source_manager); Assembler::with_kernel(source_manager, Self::kernel()) - .with_dynamic_library(StdLibrary::default()) + .with_dynamic_library(CoreLibrary::default()) .expect("failed to load std-lib") .with_dynamic_library(MidenLib::default()) .expect("failed to load miden-lib") diff --git a/crates/miden-lib/src/utils/code_builder.rs b/crates/miden-lib/src/utils/code_builder.rs index 2e58cded31..bd3cf38329 100644 --- a/crates/miden-lib/src/utils/code_builder.rs +++ b/crates/miden-lib/src/utils/code_builder.rs @@ -5,9 +5,9 @@ use miden_objects::assembly::{ Assembler, DefaultSourceManager, Library, - LibraryPath, Parse, ParseOptions, + Path, SourceManagerSync, }; use miden_objects::note::NoteScript; @@ -57,13 +57,13 @@ use crate::transaction::TransactionKernel; /// # use anyhow::Context; /// # use miden_lib::utils::CodeBuilder; /// # use miden_objects::assembly::Library; -/// # use miden_stdlib::StdLibrary; +/// # use miden_core_lib::CoreLibrary; /// # fn example() -> anyhow::Result<()> { -/// # let module_code = "export.test push.1 add end"; +/// # let module_code = "pub proc test push.1 add end"; /// # let script_code = "begin nop end"; /// # // Create sample libraries for the example -/// # let my_lib: Library = StdLibrary::default().into(); // Convert StdLibrary to Library -/// # let fpi_lib: Library = StdLibrary::default().into(); +/// # let my_lib: Library = CoreLibrary::default().into(); // Convert CoreLibrary to Library +/// # let fpi_lib: Library = CoreLibrary::default().into(); /// let script = CodeBuilder::default() /// .with_linked_module("my::module", module_code).context("failed to link module")? /// .with_statically_linked_library(&my_lib).context("failed to link static library")? @@ -87,26 +87,19 @@ impl CodeBuilder { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new CodeBuilder with the specified debug mode. - /// - /// # Arguments - /// * `in_debug_mode` - Whether to enable debug mode in the assembler - pub fn new(in_debug_mode: bool) -> Self { + /// Creates a new CodeBuilder. + pub fn new() -> Self { let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()) - .with_debug_mode(in_debug_mode); + let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); Self { assembler, source_manager } } /// Creates a new CodeBuilder with the specified source manager. /// - /// The returned builder is instantiated with debug mode enabled. - /// /// # Arguments /// * `source_manager` - The source manager to use with the internal `Assembler` pub fn with_source_manager(source_manager: Arc) -> Self { - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()) - .with_debug_mode(true); + let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); Self { assembler, source_manager } } @@ -132,22 +125,12 @@ impl CodeBuilder { module_path: impl AsRef, module_code: impl Parse, ) -> Result<(), CodeBuilderError> { - // Parse the library path - let lib_path = LibraryPath::new(module_path.as_ref()).map_err(|err| { - CodeBuilderError::build_error_with_source( - format!("invalid module path: {}", module_path.as_ref()), - err, - ) - })?; - let mut parse_options = ParseOptions::for_library(); - parse_options.path = Some(lib_path); + parse_options.path = Some(Path::new(module_path.as_ref()).into()); - let module = module_code - .parse_with_options(self.source_manager.as_ref(), parse_options) - .map_err(|err| { - CodeBuilderError::build_error_with_report("failed to parse module code", err) - })?; + let module = module_code.parse_with_options(self.source_manager(), parse_options).map_err( + |err| CodeBuilderError::build_error_with_report("failed to parse module code", err), + )?; self.assembler.compile_and_statically_link(module).map_err(|err| { CodeBuilderError::build_error_with_report("failed to assemble module", err) @@ -258,29 +241,22 @@ impl CodeBuilder { /// # Errors /// Returns an error if: /// - Compiling the account component code fails - /// - If `component_path` is not a valid [`LibraryPath`] pub fn compile_component_code( self, component_path: impl AsRef, component_code: impl Parse, ) -> Result { let CodeBuilder { assembler, source_manager } = self; - let component_path = component_path.as_ref(); - let lib_path = LibraryPath::new(component_path).map_err(|err| { - CodeBuilderError::build_error_with_source( - format!("invalid component path: {component_path}"), - err, - ) - })?; let mut parse_options = ParseOptions::for_library(); - parse_options.path = Some(lib_path); + parse_options.path = Some(Path::new(component_path.as_ref()).into()); - let module = component_code - .parse_with_options(source_manager.as_ref(), parse_options) - .map_err(|err| { - CodeBuilderError::build_error_with_report("failed to parse component code", err) - })?; + let module = + component_code + .parse_with_options(source_manager, parse_options) + .map_err(|err| { + CodeBuilderError::build_error_with_report("failed to parse component code", err) + })?; let library = assembler.assemble_library([module]).map_err(|err| { CodeBuilderError::build_error_with_report("failed to parse component code", err) @@ -388,10 +364,9 @@ impl CodeBuilder { ) -> Self { use crate::testing::mock_util_lib::mock_util_library; - // Start from the full kernel-aware assembler (includes stdlib and miden-lib). + // Start from the full kernel-aware assembler (includes core lib and miden-lib). let mut assembler = - TransactionKernel::assembler_with_source_manager(source_manager.clone()) - .with_debug_mode(true); + TransactionKernel::assembler_with_source_manager(source_manager.clone()); // Expose kernel procedures under `$kernel` for testing. assembler @@ -414,7 +389,7 @@ impl CodeBuilder { impl Default for CodeBuilder { fn default() -> Self { - Self::new(true) + Self::new() } } @@ -452,7 +427,7 @@ mod tests { #[test] fn test_create_library_and_create_tx_script() -> anyhow::Result<()> { let script_code = " - use.external_contract::counter_contract + use external_contract::counter_contract begin call.counter_contract::increment @@ -460,11 +435,11 @@ mod tests { "; let account_code = " - use.miden::active_account - use.miden::native_account - use.std::sys + use miden::active_account + use miden::native_account + use miden::core::sys - export.increment + pub proc increment push.0 exec.active_account::get_item push.1 add @@ -490,7 +465,7 @@ mod tests { #[test] fn test_parse_library_and_add_to_builder() -> anyhow::Result<()> { let script_code = " - use.external_contract::counter_contract + use external_contract::counter_contract begin call.counter_contract::increment @@ -498,11 +473,11 @@ mod tests { "; let account_code = " - use.miden::active_account - use.miden::native_account - use.std::sys + use miden::active_account + use miden::native_account + use miden::core::sys - export.increment + pub proc increment push.0 exec.active_account::get_item push.1 add @@ -529,7 +504,7 @@ mod tests { .link_module(library_path, account_code) .context("failed to link first module")?; builder_with_libs - .link_module("test::lib", "export.test nop end") + .link_module("test::lib", "pub proc test nop end") .context("failed to link second module")?; builder_with_libs .compile_tx_script(script_code) @@ -541,7 +516,7 @@ mod tests { #[test] fn test_builder_style_chaining() -> anyhow::Result<()> { let script_code = " - use.external_contract::counter_contract + use external_contract::counter_contract begin call.counter_contract::increment @@ -549,11 +524,11 @@ mod tests { "; let account_code = " - use.miden::active_account - use.miden::native_account - use.std::sys + use miden::active_account + use miden::native_account + use miden::core::sys - export.increment + pub proc increment push.0 exec.active_account::get_item push.1 add @@ -576,13 +551,13 @@ mod tests { #[test] fn test_multiple_chained_modules() -> anyhow::Result<()> { let script_code = - "use.test::lib1 use.test::lib2 begin exec.lib1::test1 exec.lib2::test2 end"; + "use test::lib1 use test::lib2 begin exec.lib1::test1 exec.lib2::test2 end"; // Test chaining multiple modules let builder = CodeBuilder::default() - .with_linked_module("test::lib1", "export.test1 push.1 add end") + .with_linked_module("test::lib1", "pub proc test1 push.1 add end") .context("failed to link first module")? - .with_linked_module("test::lib2", "export.test2 push.2 add end") + .with_linked_module("test::lib2", "pub proc test2 push.2 add end") .context("failed to link second module")?; builder.compile_tx_script(script_code).context("failed to parse tx script")?; @@ -593,7 +568,7 @@ mod tests { #[test] fn test_static_and_dynamic_linking() -> anyhow::Result<()> { let script_code = " - use.contracts::static_contract + use contracts::static_contract begin call.static_contract::increment_1 @@ -601,13 +576,13 @@ mod tests { "; let account_code_1 = " - export.increment_1 + pub proc increment_1 push.0 drop end "; let account_code_2 = " - export.increment_2 + pub proc increment_2 push.0 drop end "; diff --git a/crates/miden-objects/Cargo.toml b/crates/miden-objects/Cargo.toml index 997c800f15..e62480a48c 100644 --- a/crates/miden-objects/Cargo.toml +++ b/crates/miden-objects/Cargo.toml @@ -38,11 +38,11 @@ testing = ["dep:rand_chacha", "dep:rand_xoshiro", "dep:winter-rand-utils", "mide miden-assembly = { workspace = true } miden-assembly-syntax = { workspace = true } miden-core = { workspace = true } +miden-core-lib = { workspace = true } miden-crypto = { workspace = true } miden-mast-package = { workspace = true } miden-processor = { workspace = true } miden-protocol-macros = { workspace = true } -miden-stdlib = { workspace = true } miden-utils-sync = { workspace = true } miden-verifier = { workspace = true } winter-rand-utils = { optional = true, version = "0.13" } diff --git a/crates/miden-objects/src/account/auth.rs b/crates/miden-objects/src/account/auth.rs index ea1c7f550c..7c0edc1cbb 100644 --- a/crates/miden-objects/src/account/auth.rs +++ b/crates/miden-objects/src/account/auth.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use rand::{CryptoRng, Rng}; -use crate::crypto::dsa::{ecdsa_k256_keccak, rpo_falcon512}; +use crate::crypto::dsa::{ecdsa_k256_keccak, falcon512_rpo}; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -95,20 +95,20 @@ impl Deserializable for AuthScheme { #[non_exhaustive] #[repr(u8)] pub enum AuthSecretKey { - RpoFalcon512(rpo_falcon512::SecretKey) = RPO_FALCON_512, + RpoFalcon512(falcon512_rpo::SecretKey) = RPO_FALCON_512, EcdsaK256Keccak(ecdsa_k256_keccak::SecretKey) = ECDSA_K256_KECCAK, } impl AuthSecretKey { /// Generates an RpoFalcon512 secret key from the OS-provided randomness. #[cfg(feature = "std")] - pub fn new_rpo_falcon512() -> Self { - Self::RpoFalcon512(rpo_falcon512::SecretKey::new()) + pub fn new_falcon512_rpo() -> Self { + Self::RpoFalcon512(falcon512_rpo::SecretKey::new()) } /// Generates an RpoFalcon512 secrete key using the provided random number generator. - pub fn new_rpo_falcon512_with_rng(rng: &mut R) -> Self { - Self::RpoFalcon512(rpo_falcon512::SecretKey::with_rng(rng)) + pub fn new_falcon512_rpo_with_rng(rng: &mut R) -> Self { + Self::RpoFalcon512(falcon512_rpo::SecretKey::with_rng(rng)) } /// Generates an EcdsaK256Keccak secret key from the OS-provided randomness. @@ -161,7 +161,7 @@ impl Deserializable for AuthSecretKey { fn read_from(source: &mut R) -> Result { match source.read::()? { AuthScheme::RpoFalcon512 => { - let secret_key = rpo_falcon512::SecretKey::read_from(source)?; + let secret_key = falcon512_rpo::SecretKey::read_from(source)?; Ok(AuthSecretKey::RpoFalcon512(secret_key)) }, AuthScheme::EcdsaK256Keccak => { @@ -185,8 +185,8 @@ impl core::fmt::Display for PublicKeyCommitment { } } -impl From for PublicKeyCommitment { - fn from(value: rpo_falcon512::PublicKey) -> Self { +impl From for PublicKeyCommitment { + fn from(value: falcon512_rpo::PublicKey) -> Self { Self(value.to_commitment()) } } @@ -207,7 +207,7 @@ impl From for PublicKeyCommitment { #[derive(Clone, Debug)] #[non_exhaustive] pub enum PublicKey { - RpoFalcon512(rpo_falcon512::PublicKey), + RpoFalcon512(falcon512_rpo::PublicKey), EcdsaK256Keccak(ecdsa_k256_keccak::PublicKey), } @@ -256,7 +256,7 @@ impl Deserializable for PublicKey { fn read_from(source: &mut R) -> Result { match source.read::()? { AuthScheme::RpoFalcon512 => { - let pub_key = rpo_falcon512::PublicKey::read_from(source)?; + let pub_key = falcon512_rpo::PublicKey::read_from(source)?; Ok(PublicKey::RpoFalcon512(pub_key)) }, AuthScheme::EcdsaK256Keccak => { @@ -277,7 +277,7 @@ impl Deserializable for PublicKey { /// provider. To prepare the signature, use the provided `to_prepared_signature` method: /// ```rust,no_run /// use miden_objects::account::auth::Signature; -/// use miden_objects::crypto::dsa::rpo_falcon512::SecretKey; +/// use miden_objects::crypto::dsa::falcon512_rpo::SecretKey; /// use miden_objects::{Felt, Word}; /// /// let secret_key = SecretKey::new(); @@ -288,7 +288,7 @@ impl Deserializable for PublicKey { #[derive(Clone, Debug)] #[repr(u8)] pub enum Signature { - RpoFalcon512(rpo_falcon512::Signature) = RPO_FALCON_512, + RpoFalcon512(falcon512_rpo::Signature) = RPO_FALCON_512, EcdsaK256Keccak(ecdsa_k256_keccak::Signature) = ECDSA_K256_KECCAK, } @@ -310,9 +310,12 @@ impl Signature { // TODO: the `expect()` should be changed to an error; but that will be a part of a bigger // refactoring let mut result = match self { - Signature::RpoFalcon512(sig) => prepare_rpo_falcon512_signature(sig), - Signature::EcdsaK256Keccak(sig) => miden_stdlib::prepare_ecdsa_signature(msg, sig) - .expect("inferring public key from signature and message should succeed"), + Signature::RpoFalcon512(sig) => prepare_falcon512_rpo_signature(sig), + Signature::EcdsaK256Keccak(sig) => { + let pk = ecdsa_k256_keccak::PublicKey::recover_from(msg, sig) + .expect("inferring public key from signature and message should succeed"); + miden_core_lib::dsa::ecdsa_k256_keccak::encode_signature(&pk, sig) + }, }; // reverse the signature data so that when it is pushed onto the advice stack, the first @@ -322,8 +325,8 @@ impl Signature { } } -impl From for Signature { - fn from(signature: rpo_falcon512::Signature) -> Self { +impl From for Signature { + fn from(signature: falcon512_rpo::Signature) -> Self { Signature::RpoFalcon512(signature) } } @@ -342,7 +345,7 @@ impl Deserializable for Signature { fn read_from(source: &mut R) -> Result { match source.read::()? { AuthScheme::RpoFalcon512 => { - let signature = rpo_falcon512::Signature::read_from(source)?; + let signature = falcon512_rpo::Signature::read_from(source)?; Ok(Signature::RpoFalcon512(signature)) }, AuthScheme::EcdsaK256Keccak => { @@ -356,7 +359,7 @@ impl Deserializable for Signature { // SIGNATURE PREPARATION // ================================================================================================ -/// Converts a Falcon [rpo_falcon512::Signature] to a vector of values to be pushed onto the +/// Converts a Falcon [falcon512_rpo::Signature] to a vector of values to be pushed onto the /// advice stack. The values are the ones required for a Falcon signature verification inside the VM /// and they are: /// @@ -367,8 +370,8 @@ impl Deserializable for Signature { /// 4. The product of the above two polynomials `pi` in the ring of polynomials with coefficients in /// the Miden field. /// 5. The nonce represented as 8 field elements. -fn prepare_rpo_falcon512_signature(sig: &rpo_falcon512::Signature) -> Vec { - use rpo_falcon512::Polynomial; +fn prepare_falcon512_rpo_signature(sig: &falcon512_rpo::Signature) -> Vec { + use falcon512_rpo::Polynomial; // The signature is composed of a nonce and a polynomial s2 // The nonce is represented as 8 field elements. diff --git a/crates/miden-objects/src/account/builder/mod.rs b/crates/miden-objects/src/account/builder/mod.rs index 1f43367d7d..e02aac4152 100644 --- a/crates/miden-objects/src/account/builder/mod.rs +++ b/crates/miden-objects/src/account/builder/mod.rs @@ -295,12 +295,12 @@ mod tests { use crate::testing::noop_auth_component::NoopAuthComponent; const CUSTOM_CODE1: &str = " - export.foo + pub proc foo push.2.2 add eq.4 end "; const CUSTOM_CODE2: &str = " - export.bar + pub proc bar push.4.4 add eq.8 end "; @@ -401,10 +401,10 @@ mod tests { assert_eq!(account.code.procedure_roots().count(), 3); let foo_root = CUSTOM_LIBRARY1.mast_forest() - [CUSTOM_LIBRARY1.get_export_node_id(&CUSTOM_LIBRARY1.exports().next().unwrap().name)] + [CUSTOM_LIBRARY1.get_export_node_id(CUSTOM_LIBRARY1.exports().next().unwrap().path())] .digest(); let bar_root = CUSTOM_LIBRARY2.mast_forest() - [CUSTOM_LIBRARY2.get_export_node_id(&CUSTOM_LIBRARY2.exports().next().unwrap().name)] + [CUSTOM_LIBRARY2.get_export_node_id(CUSTOM_LIBRARY2.exports().next().unwrap().path())] .digest(); assert!(account.code().procedures().contains(&AccountProcedureRoot::from_raw(foo_root))); diff --git a/crates/miden-objects/src/account/code/mod.rs b/crates/miden-objects/src/account/code/mod.rs index a548921b5a..0d541b70b8 100644 --- a/crates/miden-objects/src/account/code/mod.rs +++ b/crates/miden-objects/src/account/code/mod.rs @@ -470,13 +470,11 @@ mod tests { use miden_assembly::Assembler; let code_with_multiple_auth = " - use.miden::account - - export.auth_basic + pub proc auth_basic push.1 drop end - export.auth_secondary + pub proc auth_secondary push.0 drop end "; diff --git a/crates/miden-objects/src/account/component/mod.rs b/crates/miden-objects/src/account/component/mod.rs index 60dbef0a58..9f709b28d4 100644 --- a/crates/miden-objects/src/account/component/mod.rs +++ b/crates/miden-objects/src/account/component/mod.rs @@ -13,7 +13,7 @@ mod code; pub use code::AccountComponentCode; use crate::account::{AccountType, StorageSlot}; -use crate::assembly::QualifiedProcedureName; +use crate::assembly::Path; use crate::{AccountError, MastForest, Word}; // ACCOUNT COMPONENT @@ -194,13 +194,10 @@ impl AccountComponent { procedures } - /// Returns the digest of the procedure with the specified name, or `None` if it was not found + /// Returns the digest of the procedure with the specified path, or `None` if it was not found /// in this component's library or its library path is malformed. - pub fn get_procedure_root_by_name( - &self, - proc_name: impl TryInto, - ) -> Option { - self.code.as_library().get_procedure_root_by_name(proc_name) + pub fn get_procedure_root_by_path(&self, proc_name: impl AsRef) -> Option { + self.code.as_library().get_procedure_root_by_path(proc_name) } // MUTATORS @@ -250,7 +247,14 @@ mod tests { use miden_assembly::Assembler; use miden_core::utils::Serializable; - use miden_mast_package::{MastArtifact, Package, PackageManifest, Section, SectionId}; + use miden_mast_package::{ + MastArtifact, + Package, + PackageKind, + PackageManifest, + Section, + SectionId, + }; use semver::Version; use super::*; @@ -275,7 +279,7 @@ mod tests { name: "test_package".to_string(), mast: MastArtifact::Library(Arc::new(library.clone())), manifest: PackageManifest::new(None), - + kind: PackageKind::AccountComponent, sections: vec![Section::new( SectionId::ACCOUNT_COMPONENT_METADATA, metadata_bytes.clone(), @@ -298,6 +302,7 @@ mod tests { name: "test_package_no_metadata".to_string(), mast: MastArtifact::Library(Arc::new(library)), manifest: PackageManifest::new(None), + kind: PackageKind::AccountComponent, sections: vec![], // No metadata section version: Default::default(), description: None, @@ -343,6 +348,7 @@ mod tests { let package_without_metadata = Package { name: "test_package_no_metadata".to_string(), mast: MastArtifact::Library(Arc::new(library)), + kind: PackageKind::AccountComponent, manifest: PackageManifest::new(None), sections: vec![], // No metadata section version: Default::default(), diff --git a/crates/miden-objects/src/account/component/storage/toml/tests.rs b/crates/miden-objects/src/account/component/storage/toml/tests.rs index e277441a3f..5d491a53f3 100644 --- a/crates/miden-objects/src/account/component/storage/toml/tests.rs +++ b/crates/miden-objects/src/account/component/storage/toml/tests.rs @@ -308,7 +308,7 @@ fn metadata_toml_round_trip_typed_slots() { [[storage.slots]] name = "demo::typed_map" - type = { key = "miden::standards::auth::rpo_falcon512::pub_key", value = "miden::standards::auth::rpo_falcon512::pub_key" } + type = { key = "miden::standards::auth::falcon512_rpo::pub_key", value = "miden::standards::auth::falcon512_rpo::pub_key" } "#; let metadata = @@ -336,7 +336,7 @@ fn metadata_toml_round_trip_typed_slots() { _ => panic!("expected map slot"), }; - let pub_key_type = SchemaTypeId::new("miden::standards::auth::rpo_falcon512::pub_key").unwrap(); + let pub_key_type = SchemaTypeId::new("miden::standards::auth::falcon512_rpo::pub_key").unwrap(); assert_eq!(map_slot.key_schema(), &WordSchema::new_simple(pub_key_type.clone())); assert_eq!(map_slot.value_schema(), &WordSchema::new_simple(pub_key_type)); @@ -368,11 +368,11 @@ fn metadata_toml_round_trip_typed_slots() { let map_type = typed_map_entry.get("type").unwrap().as_table().unwrap(); assert_eq!( map_type.get("key").unwrap().as_str().unwrap(), - "miden::standards::auth::rpo_falcon512::pub_key" + "miden::standards::auth::falcon512_rpo::pub_key" ); assert_eq!( map_type.get("value").unwrap().as_str().unwrap(), - "miden::standards::auth::rpo_falcon512::pub_key" + "miden::standards::auth::falcon512_rpo::pub_key" ); } @@ -399,7 +399,7 @@ fn extensive_schema_metadata_and_init_toml_example() { [[storage.slots]] name = "demo::owner_pub_key" description = "Owner public key" - type = "miden::standards::auth::rpo_falcon512::pub_key" + type = "miden::standards::auth::falcon512_rpo::pub_key" # simple felt-typed word slot (parsed as felt, stored as [0,0,0,]) [[storage.slots]] diff --git a/crates/miden-objects/src/account/component/storage/type_registry.rs b/crates/miden-objects/src/account/component/storage/type_registry.rs index 4e0c58550b..fe94417574 100644 --- a/crates/miden-objects/src/account/component/storage/type_registry.rs +++ b/crates/miden-objects/src/account/component/storage/type_registry.rs @@ -6,7 +6,7 @@ use core::fmt::{self, Display}; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; use miden_core::{Felt, Word}; -use miden_crypto::dsa::{ecdsa_k256_keccak, rpo_falcon512}; +use miden_crypto::dsa::{ecdsa_k256_keccak, falcon512_rpo}; use miden_processor::DeserializationError; use thiserror::Error; @@ -26,7 +26,7 @@ pub static SCHEMA_TYPE_REGISTRY: LazyLock = LazyLock::new(|| registry.register_felt_type::(); registry.register_felt_type::(); registry.register_word_type::(); - registry.register_word_type::(); + registry.register_word_type::(); registry.register_word_type::(); registry }); @@ -82,7 +82,7 @@ impl SchemaTypeError { /// Some examples: /// - `u32` /// - `felt` -/// - `miden::standards::auth::rpo_falcon512::pub_key` +/// - `miden::standards::auth::falcon512_rpo::pub_key` #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] #[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))] #[cfg_attr(feature = "std", serde(transparent))] @@ -443,9 +443,9 @@ impl WordType for Word { } } -impl WordType for rpo_falcon512::PublicKey { +impl WordType for falcon512_rpo::PublicKey { fn type_name() -> SchemaTypeId { - SchemaTypeId::new("miden::standards::auth::rpo_falcon512::pub_key") + SchemaTypeId::new("miden::standards::auth::falcon512_rpo::pub_key") .expect("type is well formed") } fn parse_str(input: &str) -> Result { diff --git a/crates/miden-objects/src/account/file.rs b/crates/miden-objects/src/account/file.rs index f882f261e6..4b6f5d4287 100644 --- a/crates/miden-objects/src/account/file.rs +++ b/crates/miden-objects/src/account/file.rs @@ -119,8 +119,8 @@ mod tests { let storage = AccountStorage::new(vec![]).unwrap(); let nonce = Felt::new(1); let account = Account::new_existing(id, vault, storage, code, nonce); - let auth_secret_key = AuthSecretKey::new_rpo_falcon512(); - let auth_secret_key_2 = AuthSecretKey::new_rpo_falcon512(); + let auth_secret_key = AuthSecretKey::new_falcon512_rpo(); + let auth_secret_key_2 = AuthSecretKey::new_falcon512_rpo(); AccountFile::new(account, vec![auth_secret_key, auth_secret_key_2]) } diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-objects/src/account/mod.rs index 12ed0c282a..95d6eac982 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-objects/src/account/mod.rs @@ -828,7 +828,7 @@ mod tests { /// account type returns an error. #[test] fn test_account_unsupported_component_type() { - let code1 = "export.foo add end"; + let code1 = "pub proc foo add end"; let library1 = Assembler::default().assemble_library([code1]).unwrap(); // This component support all account types except the regular account with updatable code. diff --git a/crates/miden-objects/src/account/storage/map/mod.rs b/crates/miden-objects/src/account/storage/map/mod.rs index 4b262f88ed..2882496190 100644 --- a/crates/miden-objects/src/account/storage/map/mod.rs +++ b/crates/miden-objects/src/account/storage/map/mod.rs @@ -5,7 +5,8 @@ use miden_crypto::merkle::EmptySubtreeRoots; use super::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, Word}; use crate::account::StorageMapDelta; -use crate::crypto::merkle::{InnerNodeInfo, LeafIndex, SMT_DEPTH, Smt, SmtLeaf}; +use crate::crypto::merkle::InnerNodeInfo; +use crate::crypto::merkle::smt::{LeafIndex, SMT_DEPTH, Smt, SmtLeaf}; use crate::errors::StorageMapError; use crate::{AccountError, Felt, Hasher}; diff --git a/crates/miden-objects/src/account/storage/map/partial.rs b/crates/miden-objects/src/account/storage/map/partial.rs index 92fd97e868..55f1f408f7 100644 --- a/crates/miden-objects/src/account/storage/map/partial.rs +++ b/crates/miden-objects/src/account/storage/map/partial.rs @@ -2,15 +2,8 @@ use alloc::collections::BTreeMap; use miden_core::utils::{Deserializable, Serializable}; use miden_crypto::Word; -use miden_crypto::merkle::{ - InnerNodeInfo, - LeafIndex, - MerkleError, - PartialSmt, - SMT_DEPTH, - SmtLeaf, - SmtProof, -}; +use miden_crypto::merkle::smt::{LeafIndex, PartialSmt, SMT_DEPTH, SmtLeaf, SmtProof}; +use miden_crypto::merkle::{InnerNodeInfo, MerkleError}; use crate::account::{StorageMap, StorageMapWitness}; use crate::utils::serde::{ByteReader, DeserializationError}; diff --git a/crates/miden-objects/src/account/storage/map/witness.rs b/crates/miden-objects/src/account/storage/map/witness.rs index 401d8345a4..f70a8359af 100644 --- a/crates/miden-objects/src/account/storage/map/witness.rs +++ b/crates/miden-objects/src/account/storage/map/witness.rs @@ -1,6 +1,7 @@ use alloc::collections::BTreeMap; -use miden_crypto::merkle::{InnerNodeInfo, SmtProof}; +use miden_crypto::merkle::InnerNodeInfo; +use miden_crypto::merkle::smt::SmtProof; use crate::Word; use crate::account::StorageMap; @@ -15,9 +16,9 @@ use crate::errors::StorageMapError; /// This type guarantees that the raw key-value pairs it contains are all present in the /// contained SMT proof. Note that the inverse is not necessarily true. The proof may contain more /// entries than the map because to prove inclusion of a given raw key A an -/// [`SmtLeaf::Multiple`](miden_crypto::merkle::SmtLeaf::Multiple) may be present that contains both -/// keys hash(A) and hash(B). However, B may not be present in the key-value pairs and this is a -/// valid state. +/// [`SmtLeaf::Multiple`](miden_crypto::merkle::smt::SmtLeaf::Multiple) may be present that contains +/// both keys hash(A) and hash(B). However, B may not be present in the key-value pairs and this is +/// a valid state. #[derive(Debug, Clone, PartialEq, Eq)] pub struct StorageMapWitness { proof: SmtProof, diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-objects/src/account/storage/partial.rs index 755f867d4e..d7e0a5b015 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-objects/src/account/storage/partial.rs @@ -2,7 +2,8 @@ use alloc::collections::{BTreeMap, BTreeSet}; use miden_core::utils::{Deserializable, Serializable}; use miden_crypto::Word; -use miden_crypto::merkle::{InnerNodeInfo, SmtLeaf}; +use miden_crypto::merkle::InnerNodeInfo; +use miden_crypto::merkle::smt::SmtLeaf; use super::{AccountStorage, AccountStorageHeader, StorageSlotContent}; use crate::AccountError; diff --git a/crates/miden-objects/src/address/mod.rs b/crates/miden-objects/src/address/mod.rs index 142dd9e8b8..6e9c864a36 100644 --- a/crates/miden-objects/src/address/mod.rs +++ b/crates/miden-objects/src/address/mod.rs @@ -445,7 +445,7 @@ mod tests { /// Tests that an address with encryption key can be created and used. #[test] fn address_with_encryption_key() -> anyhow::Result<()> { - use crate::crypto::dsa::eddsa_25519::SecretKey; + use crate::crypto::dsa::eddsa_25519_sha512::SecretKey; use crate::crypto::ies::{SealingKey, UnsealingKey}; let rng = &mut rand::rng(); @@ -484,7 +484,7 @@ mod tests { /// Tests that an address with encryption key can be encoded/decoded. #[test] fn address_encryption_key_encode_decode() -> anyhow::Result<()> { - use crate::crypto::dsa::eddsa_25519::SecretKey; + use crate::crypto::dsa::eddsa_25519_sha512::SecretKey; let rng = &mut rand::rng(); // Use a local account type (RegularAccountImmutableCode) instead of network diff --git a/crates/miden-objects/src/address/routing_parameters.rs b/crates/miden-objects/src/address/routing_parameters.rs index 5d173f9ee6..5b5899efea 100644 --- a/crates/miden-objects/src/address/routing_parameters.rs +++ b/crates/miden-objects/src/address/routing_parameters.rs @@ -7,7 +7,7 @@ use bech32::{Bech32m, Hrp}; use crate::AddressError; use crate::address::AddressInterface; -use crate::crypto::dsa::{ecdsa_k256_keccak, eddsa_25519}; +use crate::crypto::dsa::{ecdsa_k256_keccak, eddsa_25519_sha512}; use crate::crypto::ies::SealingKey; use crate::errors::Bech32Error; use crate::note::NoteTag; @@ -363,7 +363,7 @@ fn decode_encryption_key( fn read_x25519_pub_key( byte_iter: &mut impl ExactSizeIterator, -) -> Result { +) -> Result { if byte_iter.len() < X25519_PUBLIC_KEY_LENGTH { return Err(AddressError::decode_error(format!( "expected {} bytes to decode X25519 public key", @@ -371,7 +371,7 @@ fn read_x25519_pub_key( ))); } let key_bytes: [u8; X25519_PUBLIC_KEY_LENGTH] = read_byte_array(byte_iter); - eddsa_25519::PublicKey::read_from_bytes(&key_bytes).map_err(|err| { + eddsa_25519_sha512::PublicKey::read_from_bytes(&key_bytes).map_err(|err| { AddressError::decode_error_with_source("failed to decode X25519 public key", err) }) } @@ -538,7 +538,7 @@ mod tests { // Test X25519XChaCha20Poly1305 { - use crate::crypto::dsa::eddsa_25519::SecretKey; + use crate::crypto::dsa::eddsa_25519_sha512::SecretKey; let secret_key = SecretKey::with_rng(&mut rand::rng()); let public_key = secret_key.public_key(); let encryption_key = SealingKey::X25519XChaCha20Poly1305(public_key); @@ -556,7 +556,7 @@ mod tests { // Test X25519AeadRpo { - use crate::crypto::dsa::eddsa_25519::SecretKey; + use crate::crypto::dsa::eddsa_25519_sha512::SecretKey; let secret_key = SecretKey::with_rng(&mut rand::rng()); let public_key = secret_key.public_key(); let encryption_key = SealingKey::X25519AeadRpo(public_key); diff --git a/crates/miden-objects/src/asset/vault/asset_witness.rs b/crates/miden-objects/src/asset/vault/asset_witness.rs index a1f79c4342..e894a56070 100644 --- a/crates/miden-objects/src/asset/vault/asset_witness.rs +++ b/crates/miden-objects/src/asset/vault/asset_witness.rs @@ -1,6 +1,7 @@ use alloc::string::ToString; -use miden_crypto::merkle::{InnerNodeInfo, SmtLeaf, SmtProof}; +use miden_crypto::merkle::InnerNodeInfo; +use miden_crypto::merkle::smt::{SmtLeaf, SmtProof}; use super::vault_key::AssetVaultKey; use crate::AssetError; @@ -112,7 +113,7 @@ impl Deserializable for AssetWitness { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_crypto::merkle::Smt; + use miden_crypto::merkle::smt::Smt; use super::*; use crate::Word; diff --git a/crates/miden-objects/src/asset/vault/mod.rs b/crates/miden-objects/src/asset/vault/mod.rs index 88b57e0dcf..bb3e1c046a 100644 --- a/crates/miden-objects/src/asset/vault/mod.rs +++ b/crates/miden-objects/src/asset/vault/mod.rs @@ -15,7 +15,7 @@ use super::{ Serializable, }; use crate::account::{AccountId, AccountVaultDelta, NonFungibleDeltaAction}; -use crate::crypto::merkle::Smt; +use crate::crypto::merkle::smt::Smt; use crate::{AssetVaultError, Word}; mod partial; diff --git a/crates/miden-objects/src/asset/vault/partial.rs b/crates/miden-objects/src/asset/vault/partial.rs index b303842cff..1427a8902c 100644 --- a/crates/miden-objects/src/asset/vault/partial.rs +++ b/crates/miden-objects/src/asset/vault/partial.rs @@ -1,6 +1,7 @@ use alloc::string::ToString; -use miden_crypto::merkle::{InnerNodeInfo, MerkleError, PartialSmt, SmtLeaf, SmtProof}; +use miden_crypto::merkle::smt::{PartialSmt, SmtLeaf, SmtProof}; +use miden_crypto::merkle::{InnerNodeInfo, MerkleError}; use super::{AssetVault, AssetVaultKey}; use crate::Word; @@ -191,7 +192,7 @@ impl Deserializable for PartialVault { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_crypto::merkle::Smt; + use miden_crypto::merkle::smt::Smt; use super::*; use crate::asset::FungibleAsset; diff --git a/crates/miden-objects/src/asset/vault/vault_key.rs b/crates/miden-objects/src/asset/vault/vault_key.rs index c68cf7be10..2cff63d04d 100644 --- a/crates/miden-objects/src/asset/vault/vault_key.rs +++ b/crates/miden-objects/src/asset/vault/vault_key.rs @@ -1,6 +1,6 @@ use core::fmt; -use miden_crypto::merkle::LeafIndex; +use miden_crypto::merkle::smt::LeafIndex; use miden_processor::SMT_DEPTH; use crate::Word; diff --git a/crates/miden-objects/src/batch/note_tree.rs b/crates/miden-objects/src/batch/note_tree.rs index 23ba6c7608..de473ee0f6 100644 --- a/crates/miden-objects/src/batch/note_tree.rs +++ b/crates/miden-objects/src/batch/note_tree.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; -use crate::crypto::merkle::{LeafIndex, MerkleError, SimpleSmt}; +use crate::crypto::merkle::MerkleError; +use crate::crypto::merkle::smt::{LeafIndex, SimpleSmt}; use crate::note::{NoteId, NoteMetadata, compute_note_commitment}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; use crate::{BATCH_NOTE_TREE_DEPTH, EMPTY_WORD, Word}; diff --git a/crates/miden-objects/src/batch/proposed_batch.rs b/crates/miden-objects/src/batch/proposed_batch.rs index 5a57cee094..5d5cb0f97b 100644 --- a/crates/miden-objects/src/batch/proposed_batch.rs +++ b/crates/miden-objects/src/batch/proposed_batch.rs @@ -425,7 +425,7 @@ impl Deserializable for ProposedBatch { #[cfg(test)] mod tests { use anyhow::Context; - use miden_crypto::merkle::{Mmr, PartialMmr}; + use miden_crypto::merkle::mmr::{Mmr, PartialMmr}; use miden_verifier::ExecutionProof; use winter_rand_utils::rand_value; diff --git a/crates/miden-objects/src/block/account_tree/backend.rs b/crates/miden-objects/src/block/account_tree/backend.rs index 9679d255cf..78dc989786 100644 --- a/crates/miden-objects/src/block/account_tree/backend.rs +++ b/crates/miden-objects/src/block/account_tree/backend.rs @@ -3,17 +3,10 @@ use alloc::vec::Vec; use super::{AccountId, AccountIdPrefix, AccountTree, AccountTreeError, account_id_to_smt_key}; use crate::Word; +use crate::crypto::merkle::MerkleError; #[cfg(feature = "std")] -use crate::crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; -use crate::crypto::merkle::{ - LeafIndex, - MerkleError, - MutationSet, - SMT_DEPTH, - Smt, - SmtLeaf, - SmtProof, -}; +use crate::crypto::merkle::smt::{LargeSmt, LargeSmtError, SmtStorage}; +use crate::crypto::merkle::smt::{LeafIndex, MutationSet, SMT_DEPTH, Smt, SmtLeaf, SmtProof}; // ACCOUNT TREE BACKEND // ================================================================================================ @@ -183,7 +176,7 @@ where } fn root(&self) -> Word { - LargeSmt::root(self).map_err(large_smt_error_to_merkle_error).unwrap() + LargeSmt::root(self) } } diff --git a/crates/miden-objects/src/block/account_tree/mod.rs b/crates/miden-objects/src/block/account_tree/mod.rs index 38efc26e82..f48ce04425 100644 --- a/crates/miden-objects/src/block/account_tree/mod.rs +++ b/crates/miden-objects/src/block/account_tree/mod.rs @@ -3,7 +3,8 @@ use alloc::vec::Vec; use crate::Word; use crate::account::{AccountId, AccountIdPrefix}; -use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt, SmtLeaf}; +use crate::crypto::merkle::MerkleError; +use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt, SmtLeaf}; use crate::errors::AccountTreeError; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -578,7 +579,7 @@ pub(super) mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_basic_operations() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; // Create test data let id0 = AccountIdBuilder::new().build_with_seed([5; 32]); @@ -624,7 +625,7 @@ pub(super) mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_duplicate_prefix_check() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids(); @@ -645,7 +646,7 @@ pub(super) mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_apply_mutations() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let id0 = AccountIdBuilder::new().build_with_seed([5; 32]); let id1 = AccountIdBuilder::new().build_with_seed([6; 32]); @@ -678,7 +679,7 @@ pub(super) mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_same_root_as_regular_smt() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let id0 = AccountIdBuilder::new().build_with_seed([5; 32]); let id1 = AccountIdBuilder::new().build_with_seed([6; 32]); diff --git a/crates/miden-objects/src/block/account_tree/partial.rs b/crates/miden-objects/src/block/account_tree/partial.rs index f92b8918f0..af84f725ae 100644 --- a/crates/miden-objects/src/block/account_tree/partial.rs +++ b/crates/miden-objects/src/block/account_tree/partial.rs @@ -1,9 +1,8 @@ -use miden_crypto::merkle::SmtLeaf; +use miden_crypto::merkle::smt::{PartialSmt, SmtLeaf}; use super::{AccountWitness, account_id_to_smt_key}; use crate::Word; use crate::account::AccountId; -use crate::crypto::merkle::PartialSmt; use crate::errors::AccountTreeError; /// The partial sparse merkle tree containing the state commitments of accounts in the chain. @@ -191,7 +190,7 @@ impl PartialAccountTree { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_crypto::merkle::Smt; + use miden_crypto::merkle::smt::Smt; use super::*; use crate::block::account_tree::AccountTree; diff --git a/crates/miden-objects/src/block/account_tree/witness.rs b/crates/miden-objects/src/block/account_tree/witness.rs index 16ca22f451..b6d6e5083b 100644 --- a/crates/miden-objects/src/block/account_tree/witness.rs +++ b/crates/miden-objects/src/block/account_tree/witness.rs @@ -1,14 +1,7 @@ use alloc::string::ToString; -use miden_crypto::merkle::{ - InnerNodeInfo, - LeafIndex, - SMT_DEPTH, - SmtLeaf, - SmtProof, - SmtProofError, - SparseMerklePath, -}; +use miden_crypto::merkle::smt::{LeafIndex, SMT_DEPTH, SmtLeaf, SmtProof, SmtProofError}; +use miden_crypto::merkle::{InnerNodeInfo, SparseMerklePath}; use crate::account::AccountId; use crate::block::account_tree::{account_id_to_smt_key, smt_key_to_account_id}; diff --git a/crates/miden-objects/src/block/blockchain.rs b/crates/miden-objects/src/block/blockchain.rs index 58046a630a..a70159fbd0 100644 --- a/crates/miden-objects/src/block/blockchain.rs +++ b/crates/miden-objects/src/block/blockchain.rs @@ -1,7 +1,7 @@ use alloc::collections::BTreeSet; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_crypto::merkle::{Forest, Mmr, MmrError, MmrPeaks, MmrProof, PartialMmr}; +use miden_crypto::merkle::mmr::{Forest, Mmr, MmrError, MmrPeaks, MmrProof, PartialMmr}; use miden_processor::DeserializationError; use crate::Word; diff --git a/crates/miden-objects/src/block/note_tree.rs b/crates/miden-objects/src/block/note_tree.rs index 5e6ee6d215..0e919073f1 100644 --- a/crates/miden-objects/src/block/note_tree.rs +++ b/crates/miden-objects/src/block/note_tree.rs @@ -3,7 +3,8 @@ use alloc::string::ToString; use miden_crypto::merkle::SparseMerklePath; use crate::batch::BatchNoteTree; -use crate::crypto::merkle::{LeafIndex, MerkleError, SimpleSmt}; +use crate::crypto::merkle::MerkleError; +use crate::crypto::merkle::smt::{LeafIndex, SimpleSmt}; use crate::note::{NoteId, NoteMetadata, compute_note_commitment}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; use crate::{ @@ -176,7 +177,7 @@ impl Deserializable for BlockNoteTree { #[cfg(test)] mod tests { - use miden_crypto::merkle::SimpleSmt; + use miden_crypto::merkle::smt::SimpleSmt; use miden_crypto::utils::{Deserializable, Serializable}; use super::BlockNoteTree; diff --git a/crates/miden-objects/src/block/nullifier_tree/backend.rs b/crates/miden-objects/src/block/nullifier_tree/backend.rs index 8f7f037769..603258ea0a 100644 --- a/crates/miden-objects/src/block/nullifier_tree/backend.rs +++ b/crates/miden-objects/src/block/nullifier_tree/backend.rs @@ -2,9 +2,10 @@ use alloc::boxed::Box; use super::{BlockNumber, Nullifier, NullifierBlock, NullifierTree, NullifierTreeError}; use crate::Word; +use crate::crypto::merkle::MerkleError; #[cfg(feature = "std")] -use crate::crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; -use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt, SmtProof}; +use crate::crypto::merkle::smt::{LargeSmt, LargeSmtError, SmtStorage}; +use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt, SmtProof}; // NULLIFIER TREE BACKEND // ================================================================================================ @@ -158,12 +159,7 @@ where } fn root(&self) -> Word { - // SAFETY: We expect here as storage errors are considered unrecoverable. This maintains - // API compatibility with the non-fallible Smt::root(). - // See issue #2010 for future improvements to error handling. LargeSmt::root(self) - .map_err(large_smt_error_to_merkle_error) - .expect("Storage I/O error accessing root") } } diff --git a/crates/miden-objects/src/block/nullifier_tree/mod.rs b/crates/miden-objects/src/block/nullifier_tree/mod.rs index ee8226a6b8..812e3906c2 100644 --- a/crates/miden-objects/src/block/nullifier_tree/mod.rs +++ b/crates/miden-objects/src/block/nullifier_tree/mod.rs @@ -2,7 +2,8 @@ use alloc::string::ToString; use alloc::vec::Vec; use crate::block::BlockNumber; -use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt}; +use crate::crypto::merkle::MerkleError; +use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt}; use crate::errors::NullifierTreeError; use crate::note::Nullifier; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -412,7 +413,7 @@ mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_basic_operations() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; // Create test data let nullifier1 = Nullifier::dummy(1); @@ -452,7 +453,7 @@ mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_nullifier_already_spent() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let nullifier1 = Nullifier::dummy(1); @@ -476,7 +477,7 @@ mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_apply_mutations() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let nullifier1 = Nullifier::dummy(1); let nullifier2 = Nullifier::dummy(2); @@ -507,7 +508,7 @@ mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_same_root_as_regular_smt() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let nullifier1 = Nullifier::dummy(1); let nullifier2 = Nullifier::dummy(2); diff --git a/crates/miden-objects/src/block/nullifier_tree/partial.rs b/crates/miden-objects/src/block/nullifier_tree/partial.rs index e745096e90..0bcb90c80d 100644 --- a/crates/miden-objects/src/block/nullifier_tree/partial.rs +++ b/crates/miden-objects/src/block/nullifier_tree/partial.rs @@ -1,7 +1,7 @@ use super::{NullifierBlock, NullifierWitness}; use crate::Word; use crate::block::BlockNumber; -use crate::crypto::merkle::PartialSmt; +use crate::crypto::merkle::smt::PartialSmt; use crate::errors::NullifierTreeError; use crate::note::Nullifier; @@ -110,7 +110,7 @@ impl PartialNullifierTree { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_crypto::merkle::Smt; + use miden_crypto::merkle::smt::Smt; use winter_rand_utils::rand_value; use super::*; diff --git a/crates/miden-objects/src/block/nullifier_tree/witness.rs b/crates/miden-objects/src/block/nullifier_tree/witness.rs index 019e5763dc..1b7df6db38 100644 --- a/crates/miden-objects/src/block/nullifier_tree/witness.rs +++ b/crates/miden-objects/src/block/nullifier_tree/witness.rs @@ -1,4 +1,4 @@ -use crate::crypto::merkle::SmtProof; +use crate::crypto::merkle::smt::SmtProof; use crate::utils::serde::{ ByteReader, ByteWriter, diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors.rs index 985335c7d6..151bdfadfc 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors.rs @@ -7,7 +7,7 @@ use miden_assembly::Report; use miden_assembly::diagnostics::reporting::PrintDiagnostic; use miden_core::Felt; use miden_core::mast::MastForestError; -use miden_crypto::merkle::MmrError; +use miden_crypto::merkle::mmr::MmrError; use miden_crypto::utils::HexParseError; use miden_processor::DeserializationError; use thiserror::Error; diff --git a/crates/miden-objects/src/lib.rs b/crates/miden-objects/src/lib.rs index d1ed2d7faf..6a803494af 100644 --- a/crates/miden-objects/src/lib.rs +++ b/crates/miden-objects/src/lib.rs @@ -61,15 +61,15 @@ pub use miden_crypto::word::{LexicographicWord, Word, WordError}; pub mod assembly { pub use miden_assembly::ast::{Module, ModuleKind, ProcedureName, QualifiedProcedureName}; pub use miden_assembly::debuginfo::SourceManagerSync; + pub use miden_assembly::library::LibraryExport; pub use miden_assembly::{ Assembler, DefaultSourceManager, KernelLibrary, Library, - LibraryNamespace, - LibraryPath, Parse, ParseOptions, + Path, SourceFile, SourceId, SourceManager, diff --git a/crates/miden-objects/src/testing/account_code.rs b/crates/miden-objects/src/testing/account_code.rs index 90e0afeb56..bd1df53194 100644 --- a/crates/miden-objects/src/testing/account_code.rs +++ b/crates/miden-objects/src/testing/account_code.rs @@ -7,11 +7,11 @@ use crate::account::{AccountCode, AccountComponent, AccountType}; use crate::testing::noop_auth_component::NoopAuthComponent; pub const CODE: &str = " - export.foo + pub proc foo push.1.2 mul end - export.bar + pub proc bar push.1.2 add end "; diff --git a/crates/miden-objects/src/testing/add_component.rs b/crates/miden-objects/src/testing/add_component.rs index eb4b79d79f..3415a70a16 100644 --- a/crates/miden-objects/src/testing/add_component.rs +++ b/crates/miden-objects/src/testing/add_component.rs @@ -6,7 +6,7 @@ use crate::utils::sync::LazyLock; // ================================================================================================ const ADD_CODE: &str = " - export.add5 + pub proc add5 add.5 end "; diff --git a/crates/miden-objects/src/testing/block.rs b/crates/miden-objects/src/testing/block.rs index e67ef5b21f..3ef1a2c624 100644 --- a/crates/miden-objects/src/testing/block.rs +++ b/crates/miden-objects/src/testing/block.rs @@ -1,4 +1,4 @@ -use miden_crypto::merkle::Smt; +use miden_crypto::merkle::smt::Smt; #[cfg(not(target_family = "wasm"))] use winter_rand_utils::rand_value; diff --git a/crates/miden-objects/src/testing/noop_auth_component.rs b/crates/miden-objects/src/testing/noop_auth_component.rs index 8ebc9fb800..35b7a79126 100644 --- a/crates/miden-objects/src/testing/noop_auth_component.rs +++ b/crates/miden-objects/src/testing/noop_auth_component.rs @@ -6,7 +6,7 @@ use crate::utils::sync::LazyLock; // ================================================================================================ const NOOP_AUTH_CODE: &str = " - export.auth_noop + pub proc auth_noop push.0 drop end "; diff --git a/crates/miden-objects/src/transaction/inputs/account.rs b/crates/miden-objects/src/transaction/inputs/account.rs index 848f33ef64..f0c2296323 100644 --- a/crates/miden-objects/src/transaction/inputs/account.rs +++ b/crates/miden-objects/src/transaction/inputs/account.rs @@ -2,7 +2,7 @@ use crate::Word; use crate::account::{AccountCode, AccountId, PartialAccount, PartialStorage}; use crate::asset::PartialVault; use crate::block::account_tree::AccountWitness; -use crate::crypto::merkle::{SmtProof, SmtProofError}; +use crate::crypto::merkle::smt::{SmtProof, SmtProofError}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; // ACCOUNT INPUTS diff --git a/crates/miden-objects/src/transaction/partial_blockchain.rs b/crates/miden-objects/src/transaction/partial_blockchain.rs index 4fcf3d0d94..0d63027fe8 100644 --- a/crates/miden-objects/src/transaction/partial_blockchain.rs +++ b/crates/miden-objects/src/transaction/partial_blockchain.rs @@ -4,7 +4,8 @@ use core::ops::RangeTo; use crate::PartialBlockchainError; use crate::block::{BlockHeader, BlockNumber}; -use crate::crypto::merkle::{InnerNodeInfo, MmrPeaks, PartialMmr}; +use crate::crypto::merkle::InnerNodeInfo; +use crate::crypto::merkle::mmr::{MmrPeaks, PartialMmr}; use crate::utils::serde::{Deserializable, Serializable}; // PARTIAL BLOCKCHAIN @@ -284,7 +285,7 @@ mod tests { use crate::alloc::vec::Vec; use crate::block::{BlockHeader, BlockNumber, FeeParameters}; use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey; - use crate::crypto::merkle::{Mmr, PartialMmr}; + use crate::crypto::merkle::mmr::{Mmr, PartialMmr}; use crate::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; use crate::{PartialBlockchainError, Word}; diff --git a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs index cf5d5718ee..fbe3855c08 100644 --- a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs @@ -9,7 +9,7 @@ use miden_objects::asset::FungibleAsset; use miden_objects::batch::BatchNoteTree; use miden_objects::block::account_tree::AccountTree; use miden_objects::block::{BlockInputs, BlockNoteIndex, BlockNoteTree, ProposedBlock}; -use miden_objects::crypto::merkle::Smt; +use miden_objects::crypto::merkle::smt::Smt; use miden_objects::note::NoteType; use miden_objects::transaction::InputNoteCommitment; diff --git a/crates/miden-testing/src/kernel_tests/block/utils.rs b/crates/miden-testing/src/kernel_tests/block/utils.rs index e2e35dc488..da42b7b821 100644 --- a/crates/miden-testing/src/kernel_tests/block/utils.rs +++ b/crates/miden-testing/src/kernel_tests/block/utils.rs @@ -101,7 +101,7 @@ impl MockChainBlockExt for MockChain { fn update_expiration_tx_script(expiration_delta: u16) -> TransactionScript { let code = format!( " - use.miden::tx + use miden::tx begin push.{expiration_delta} diff --git a/crates/miden-testing/src/kernel_tests/tx/mod.rs b/crates/miden-testing/src/kernel_tests/tx/mod.rs index 7c488b71b6..0c6eff44a8 100644 --- a/crates/miden-testing/src/kernel_tests/tx/mod.rs +++ b/crates/miden-testing/src/kernel_tests/tx/mod.rs @@ -125,7 +125,7 @@ pub fn create_mock_notes_procedure(notes: &[Note]) -> String { } let mut script = String::from( - "proc.create_mock_notes + "proc create_mock_notes # remove padding from prologue dropw dropw dropw dropw ", diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index bcf0b213e8..d69d0b485e 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -87,11 +87,10 @@ pub async fn compute_commitment() -> miette::Result<()> { let tx_script = format!( r#" - use.std::word + use miden::core::word - use.miden::prologue - use.miden::active_account - use.mock::account->mock_account + use miden::active_account + use mock::account->mock_account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -190,7 +189,7 @@ async fn test_account_type() -> miette::Result<()> { let code = format!( " - use.$kernel::account_id + use $kernel::account_id begin exec.account_id::{procedure} @@ -262,7 +261,7 @@ async fn test_account_validate_id() -> miette::Result<()> { let suffix = Felt::try_from((account_id % (1u128 << 64)) as u64).unwrap(); let code = " - use.$kernel::account_id + use $kernel::account_id begin exec.account_id::validate @@ -315,7 +314,7 @@ async fn test_is_faucet_procedure() -> miette::Result<()> { let code = format!( " - use.$kernel::account_id + use $kernel::account_id begin push.{prefix} @@ -356,8 +355,8 @@ pub async fn test_compute_code_commitment() -> miette::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::prologue + use mock::account->mock_account begin exec.prologue::prepare_transaction @@ -385,10 +384,10 @@ async fn test_get_item() -> miette::Result<()> { let code = format!( r#" - use.$kernel::account - use.$kernel::prologue + use $kernel::account + use $kernel::prologue - const.SLOT_NAME = word("{slot_name}") + const SLOT_NAME = word("{slot_name}") begin exec.prologue::prepare_transaction @@ -431,8 +430,8 @@ async fn test_get_map_item() -> miette::Result<()> { for (key, expected_value) in map.entries() { let code = format!( r#" - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account const SLOT_NAME = word("{slot_name}") @@ -448,7 +447,7 @@ async fn test_get_map_item() -> miette::Result<()> { push.{expected_value} assert_eqw.err="value did not match {expected_value}" - exec.::std::sys::truncate_stack + exec.::miden::core::sys::truncate_stack end "#, slot_name = slot.name(), @@ -479,8 +478,8 @@ async fn test_get_storage_slot_type() -> miette::Result<()> { let code = format!( " - use.$kernel::account - use.$kernel::prologue + use $kernel::account + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -545,9 +544,9 @@ async fn test_account_get_item_fails_on_unknown_slot() -> anyhow::Result<()> { let chain = builder.build()?; let code = r#" - use.mock::account + use mock::account - const.UNKNOWN_SLOT_NAME = word("unknown::slot::name") + const UNKNOWN_SLOT_NAME = word("unknown::slot::name") begin push.UNKNOWN_SLOT_NAME[0..2] @@ -580,9 +579,9 @@ async fn test_account_get_item_fails_on_unknown_slot() -> anyhow::Result<()> { #[tokio::test] async fn test_account_set_item_fails_on_reserved_faucet_metadata_slot() -> anyhow::Result<()> { let code = r#" - use.miden::native_account + use miden::native_account - const.FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") + const FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") begin push.FAUCET_SYSDATA_SLOT[0..2] @@ -640,7 +639,7 @@ async fn test_is_slot_id_lt() -> miette::Result<()> { for (prev_slot, curr_slot) in test_cases { let code = format!( r#" - use.$kernel::account + use $kernel::account begin push.{curr_suffix}.{curr_prefix}.{prev_suffix}.{prev_prefix} @@ -677,10 +676,10 @@ async fn test_set_item() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::account - use.$kernel::prologue + use $kernel::account + use $kernel::prologue - const.MOCK_VALUE_SLOT0 = word("{slot_name}") + const MOCK_VALUE_SLOT0 = word("{slot_name}") begin exec.prologue::prepare_transaction @@ -728,12 +727,12 @@ async fn test_set_map_item() -> miette::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::prologue + use mock::account->mock_account - const.SLOT_NAME=word("{slot_name}") + const SLOT_NAME=word("{slot_name}") begin exec.prologue::prepare_transaction @@ -809,8 +808,8 @@ async fn test_get_initial_storage_commitment() -> anyhow::Result<()> { let code = format!( r#" - use.miden::active_account - use.$kernel::prologue + use miden::active_account + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -860,12 +859,11 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { let code = format!( r#" - use.miden::account - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::prologue + use mock::account->mock_account - const.MOCK_VALUE_SLOT0=word("{mock_value_slot0}") - const.MOCK_MAP_SLOT=word("{mock_map_slot}") + const MOCK_VALUE_SLOT0=word("{mock_value_slot0}") + const MOCK_MAP_SLOT=word("{mock_map_slot}") begin exec.prologue::prepare_transaction @@ -1008,8 +1006,8 @@ async fn test_get_vault_root() -> anyhow::Result<()> { // get the initial vault root let code = format!( " - use.miden::active_account - use.$kernel::prologue + use miden::active_account + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -1029,9 +1027,9 @@ async fn test_get_vault_root() -> anyhow::Result<()> { let code = format!( r#" - use.miden::active_account - use.$kernel::prologue - use.mock::account->mock_account + use miden::active_account + use $kernel::prologue + use mock::account->mock_account begin exec.prologue::prepare_transaction @@ -1113,7 +1111,7 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { let add_existing_source = format!( r#" - use.miden::active_account + use miden::active_account begin # push faucet ID prefix and suffix @@ -1167,7 +1165,7 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { let add_new_source = format!( r#" - use.miden::active_account + use miden::active_account begin # push faucet ID prefix and suffix @@ -1244,13 +1242,13 @@ async fn test_get_init_balance_subtraction() -> anyhow::Result<()> { let remove_existing_source = format!( r#" - use.miden::active_account - use.miden::contracts::wallets::basic->wallet - use.mock::util + use miden::active_account + use miden::contracts::wallets::basic->wallet + use mock::util # Inputs: [ASSET, note_idx] # Outputs: [ASSET, note_idx] - proc.move_asset_to_note + proc move_asset_to_note # pad the stack before call push.0.0.0 movdn.7 movdn.7 movdn.7 padw padw swapdw # => [ASSET, note_idx, pad(11)] @@ -1339,8 +1337,8 @@ async fn test_authenticate_and_track_procedure() -> miette::Result<()> { let code = format!( " - use.$kernel::account - use.$kernel::prologue + use $kernel::account + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -1395,8 +1393,8 @@ async fn test_was_procedure_called() -> miette::Result<()> { // 5. Checks that `was_procedure_called` returns `true` let tx_script_code = format!( r#" - use.mock::account->mock_account - use.miden::native_account + use mock::account->mock_account + use miden::native_account const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") @@ -1452,11 +1450,11 @@ async fn test_was_procedure_called() -> miette::Result<()> { async fn transaction_executor_account_code_using_custom_library() -> miette::Result<()> { let external_library_code = format!( r#" - use.miden::native_account + use miden::native_account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") - export.external_setter + pub proc external_setter push.2.3.4.5 push.MOCK_VALUE_SLOT0[0..2] exec.native_account::set_item @@ -1466,9 +1464,9 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res ); const ACCOUNT_COMPONENT_CODE: &str = " - use.external_library::external_module + use external_library::external_module - export.custom_setter + pub proc custom_setter exec.external_module::external_setter end"; @@ -1490,7 +1488,7 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res assembler.clone().assemble_library([account_component_source]).unwrap(); let tx_script_src = "\ - use.account_component::account_module + use account_component::account_module begin call.account_module::custom_setter @@ -1537,9 +1535,9 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res #[tokio::test] async fn incrementing_nonce_twice_fails() -> anyhow::Result<()> { let source_code = " - use.miden::native_account + use miden::native_account - export.auth_incr_nonce_twice + pub proc auth_incr_nonce_twice exec.native_account::incr_nonce drop exec.native_account::incr_nonce drop end @@ -1573,8 +1571,8 @@ async fn test_has_procedure() -> miette::Result<()> { .unwrap(); let tx_script_code = r#" - use.mock::account->mock_account - use.miden::active_account + use mock::account->mock_account + use miden::active_account begin # check that get_item procedure is available on the mock account @@ -1625,9 +1623,9 @@ async fn test_get_initial_item() -> miette::Result<()> { // Test that get_initial_item returns the initial value before any changes let code = format!( r#" - use.$kernel::account - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::account + use $kernel::prologue + use mock::account->mock_account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -1691,8 +1689,8 @@ async fn test_get_initial_map_item() -> miette::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::prologue + use mock::account->mock_account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -1779,7 +1777,7 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> static COMPONENT_1_LIBRARY: LazyLock = LazyLock::new(|| { let code = format!( r#" - use.miden::active_account + use miden::active_account const TEST_SLOT_NAME = word("{test_slot_name}") @@ -1794,7 +1792,6 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> let source = NamedSource::new("component1::interface", code); TransactionKernel::assembler() - .with_debug_mode(true) .assemble_library([source]) .expect("mock account code should be valid") }); @@ -1802,8 +1799,8 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> static COMPONENT_2_LIBRARY: LazyLock = LazyLock::new(|| { let code = format!( r#" - use.miden::active_account - use.miden::native_account + use miden::active_account + use miden::native_account const TEST_SLOT_NAME = word("{test_slot_name}") @@ -1825,7 +1822,6 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> let source = NamedSource::new("component2::interface", code); TransactionKernel::assembler() - .with_debug_mode(true) .assemble_library([source]) .expect("mock account code should be valid") }); @@ -1862,8 +1858,8 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> .context("failed to build account")?; let tx_script = r#" - use.component1::interface->comp1_interface - use.component2::interface->comp2_interface + use component1::interface->comp1_interface + use component2::interface->comp2_interface begin call.comp1_interface::get_slot_content diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index fa7ae57a3d..df41aaaa36 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -659,8 +659,8 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { let tx_script_src = format!( r#" - use.mock::account - use.miden::output_note + use mock::account + use miden::output_note const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -848,9 +848,9 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: let code = format!( r#" - use.mock::account + use mock::account - const.MAP_SLOT=word("{map2_slot_name}") + const MAP_SLOT=word("{map2_slot_name}") begin # Update an existing key. @@ -860,7 +860,7 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] call.account::set_map_item - exec.::std::sys::truncate_stack + exec.::miden::core::sys::truncate_stack end "# ); @@ -1104,12 +1104,12 @@ fn parse_tx_script(code: impl AsRef) -> anyhow::Result { } const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " - use.mock::account - use.miden::output_note + use mock::account + use miden::output_note #! Inputs: [slot_id_prefix, slot_id_suffix, VALUE] #! Outputs: [] - proc.set_item + proc set_item repeat.10 push.0 movdn.6 end # => [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] @@ -1121,7 +1121,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, VALUE] #! Outputs: [] - proc.set_map_item + proc set_map_item repeat.6 push.0 movdn.10 end # => [index, KEY, VALUE, pad(6)] @@ -1134,7 +1134,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [ASSET] #! Outputs: [] - proc.create_note_with_asset + proc create_note_with_asset push.0.1.2.3 # recipient push.1 # note_execution_hint push.2 # note_type private @@ -1154,7 +1154,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] #! Outputs: [note_idx] - proc.create_note + proc create_note repeat.8 push.0 movdn.8 end # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] @@ -1167,7 +1167,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [ASSET, note_idx] #! Outputs: [] - proc.move_asset_to_note + proc move_asset_to_note repeat.11 push.0 movdn.5 end # => [ASSET, note_idx, pad(11)] @@ -1179,7 +1179,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [ASSET] #! Outputs: [ASSET'] - proc.add_asset + proc add_asset repeat.12 push.0 movdn.4 end # => [ASSET, pad(12)] @@ -1192,7 +1192,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [ASSET] #! Outputs: [ASSET] - proc.remove_asset + proc remove_asset repeat.12 push.0 movdn.4 end # => [ASSET, pad(12)] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index cbd725e035..f175f37d3a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -49,7 +49,7 @@ async fn test_active_note_get_sender_fails_from_tx_script() -> anyhow::Result<() mock_chain.prove_next_block()?; let code = " - use.miden::active_note + use miden::active_note begin # try to get the sender from transaction script @@ -90,9 +90,9 @@ async fn test_active_note_get_metadata() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.$kernel::note->note_internal - use.miden::active_note + use $kernel::prologue + use $kernel::note->note_internal + use miden::active_note begin exec.prologue::prepare_transaction @@ -135,9 +135,9 @@ async fn test_active_note_get_sender() -> anyhow::Result<()> { // calling get_sender should return sender of the active note let code = " - use.$kernel::prologue - use.$kernel::note->note_internal - use.miden::active_note + use $kernel::prologue + use $kernel::note->note_internal + use miden::active_note begin exec.prologue::prepare_transaction @@ -211,13 +211,13 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { // calling get_assets should return assets at the specified address let code = format!( " - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.$kernel::note->note_internal - use.miden::active_note + use $kernel::prologue + use $kernel::note->note_internal + use miden::active_note - proc.process_note_0 + proc process_note_0 # drop the note inputs dropw dropw dropw dropw @@ -240,7 +240,7 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { drop end - proc.process_note_1 + proc process_note_1 # drop the note inputs dropw dropw dropw dropw @@ -341,9 +341,9 @@ async fn test_active_note_get_inputs() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.$kernel::note->note_internal - use.miden::active_note + use $kernel::prologue + use $kernel::note->note_internal + use miden::active_note begin # => [BH, acct_id, IAH, NC] @@ -441,8 +441,8 @@ async fn test_active_note_get_exactly_8_inputs() -> anyhow::Result<()> { .build()?; let tx_code = " - use.$kernel::prologue - use.miden::active_note + use $kernel::prologue + use miden::active_note begin exec.prologue::prepare_transaction @@ -484,8 +484,8 @@ async fn test_active_note_get_serial_number() -> anyhow::Result<()> { // calling get_serial_number should return the serial number of the active note let code = " - use.$kernel::prologue - use.miden::active_note + use $kernel::prologue + use miden::active_note begin exec.prologue::prepare_transaction @@ -523,8 +523,8 @@ async fn test_active_note_get_script_root() -> anyhow::Result<()> { // calling get_script_root should return script root of the active note let code = " - use.$kernel::prologue - use.miden::active_note + use $kernel::prologue + use miden::active_note begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset.rs index 092e2608ac..4836d6620d 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset.rs @@ -21,8 +21,8 @@ async fn test_create_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.miden::faucet + use $kernel::prologue + use miden::faucet begin exec.prologue::prepare_transaction @@ -62,8 +62,8 @@ async fn test_create_non_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.miden::faucet + use $kernel::prologue + use miden::faucet begin exec.prologue::prepare_transaction @@ -95,7 +95,7 @@ async fn test_validate_non_fungible_asset() -> anyhow::Result<()> { let code = format!( " - use.$kernel::asset + use $kernel::asset begin push.{non_fungible_asset} diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs index 37b492d8cc..5f784d6ac0 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs @@ -28,8 +28,8 @@ async fn get_balance_returns_correct_amount() -> anyhow::Result<()> { let faucet_id: AccountId = ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET.try_into().unwrap(); let code = format!( r#" - use.$kernel::prologue - use.miden::active_account + use $kernel::prologue + use miden::active_account begin exec.prologue::prepare_transaction @@ -64,10 +64,9 @@ async fn peek_balance_returns_correct_amount() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.$kernel::memory - use.$kernel::asset_vault - use.miden::account + use $kernel::prologue + use $kernel::memory + use $kernel::asset_vault begin exec.prologue::prepare_transaction @@ -113,8 +112,8 @@ async fn test_get_balance_non_fungible_fails() -> anyhow::Result<()> { let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET).unwrap(); let code = format!( " - use.$kernel::prologue - use.miden::active_account + use $kernel::prologue + use miden::active_account begin exec.prologue::prepare_transaction @@ -144,8 +143,8 @@ async fn test_has_non_fungible_asset() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.miden::active_account + use $kernel::prologue + use miden::active_account begin exec.prologue::prepare_transaction @@ -182,8 +181,8 @@ async fn test_add_fungible_asset_success() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -229,8 +228,8 @@ async fn test_add_non_fungible_asset_fail_overflow() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -260,8 +259,8 @@ async fn test_add_non_fungible_asset_success() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -302,8 +301,8 @@ async fn test_add_non_fungible_asset_fail_duplicate() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -339,8 +338,8 @@ async fn test_remove_fungible_asset_success_no_balance_remaining() -> anyhow::Re let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -384,8 +383,8 @@ async fn test_remove_fungible_asset_fail_remove_too_much() -> anyhow::Result<()> let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -423,8 +422,8 @@ async fn test_remove_fungible_asset_success_balance_remaining() -> anyhow::Resul let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -472,8 +471,8 @@ async fn test_remove_inexisting_non_fungible_asset_fails() -> anyhow::Result<()> let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -508,8 +507,8 @@ async fn test_remove_non_fungible_asset_success() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 42a8c7aa9b..673555212b 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -69,9 +69,9 @@ async fn test_epilogue() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.$kernel::account - use.$kernel::epilogue + use $kernel::prologue + use $kernel::account + use $kernel::epilogue {output_notes_data_procedure} @@ -169,8 +169,8 @@ async fn test_compute_output_note_id() -> anyhow::Result<()> { for (note, i) in tx_context.expected_output_notes().iter().zip(0u32..) { let code = format!( " - use.$kernel::prologue - use.$kernel::epilogue + use $kernel::prologue + use $kernel::epilogue {output_notes_data_procedure} @@ -227,8 +227,8 @@ async fn epilogue_fails_when_num_output_assets_exceed_num_input_assets() -> anyh let code = format!( " - use.mock::account - use.mock::util + use mock::account + use mock::util begin # create a note with the output asset @@ -280,8 +280,8 @@ async fn epilogue_fails_when_num_input_assets_exceed_num_output_assets() -> anyh let code = format!( " - use.mock::account - use.mock::util + use mock::account + use mock::util begin # create a note with the output asset @@ -318,10 +318,10 @@ async fn test_block_expiration_height_monotonically_decreases() -> anyhow::Resul let test_pairs: [(u64, u64); 3] = [(9, 12), (18, 3), (20, 20)]; let code_template = " - use.$kernel::prologue - use.$kernel::tx - use.$kernel::epilogue - use.$kernel::account + use $kernel::prologue + use $kernel::tx + use $kernel::epilogue + use $kernel::account begin exec.prologue::prepare_transaction @@ -366,7 +366,7 @@ async fn test_invalid_expiration_deltas() -> anyhow::Result<()> { let test_values = [0u64, u16::MAX as u64 + 1, u32::MAX as u64]; let code_template = " - use.$kernel::tx + use $kernel::tx begin push.{value_1} @@ -389,10 +389,10 @@ async fn test_no_expiration_delta_set() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code_template = " - use.$kernel::prologue - use.$kernel::epilogue - use.$kernel::tx - use.$kernel::account + use $kernel::prologue + use $kernel::epilogue + use $kernel::tx + use $kernel::account begin exec.prologue::prepare_transaction @@ -425,10 +425,10 @@ async fn test_epilogue_increment_nonce_success() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.mock::account - use.$kernel::epilogue - use.$kernel::memory + use $kernel::prologue + use mock::account + use $kernel::epilogue + use $kernel::memory const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -461,7 +461,7 @@ async fn test_epilogue_increment_nonce_success() -> anyhow::Result<()> { async fn epilogue_fails_on_account_state_change_without_nonce_increment() -> anyhow::Result<()> { let code = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -534,11 +534,11 @@ async fn test_epilogue_empty_transaction_with_empty_output_note() -> anyhow::Res // create an empty output note in the transaction script let tx_script_source = format!( r#" - use.std::word - use.miden::output_note - use.$kernel::prologue - use.$kernel::epilogue - use.$kernel::note + use miden::core::word + use miden::output_note + use $kernel::prologue + use $kernel::epilogue + use $kernel::note begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index 7a091aab88..e485ca1d0c 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -50,11 +50,11 @@ async fn test_mint_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.mock::faucet->mock_faucet - use.miden::faucet - use.$kernel::asset_vault - use.$kernel::memory - use.$kernel::prologue + use mock::faucet->mock_faucet + use miden::faucet + use $kernel::asset_vault + use $kernel::memory + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -100,7 +100,7 @@ async fn mint_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result<()> let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -131,8 +131,8 @@ async fn test_mint_fungible_asset_inconsistent_faucet_id() -> anyhow::Result<()> let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -153,7 +153,7 @@ async fn test_mint_fungible_asset_inconsistent_faucet_id() -> anyhow::Result<()> async fn test_mint_fungible_asset_fails_saturate_max_amount() -> anyhow::Result<()> { let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -194,13 +194,13 @@ async fn test_mint_non_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.std::collections::smt + use miden::core::collections::smt - use.$kernel::account - use.$kernel::asset_vault - use.$kernel::memory - use.$kernel::prologue - use.mock::faucet->mock_faucet + use $kernel::account + use $kernel::asset_vault + use $kernel::memory + use $kernel::prologue + use mock::faucet->mock_faucet const FAUCET_SYSDATA_SLOT_NAME = word("{faucet_sysdata_slot_name}") @@ -250,8 +250,8 @@ async fn test_mint_non_fungible_asset_fails_inconsistent_faucet_id() -> anyhow:: let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -275,7 +275,7 @@ async fn mint_non_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -306,8 +306,8 @@ async fn test_mint_non_fungible_asset_fails_asset_already_exists() -> anyhow::Re let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -347,11 +347,11 @@ async fn test_burn_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.mock::faucet->mock_faucet - use.miden::faucet - use.$kernel::asset_vault - use.$kernel::memory - use.$kernel::prologue + use mock::faucet->mock_faucet + use miden::faucet + use $kernel::asset_vault + use $kernel::memory + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -395,7 +395,7 @@ async fn burn_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result<()> let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -428,8 +428,8 @@ async fn test_burn_fungible_asset_inconsistent_faucet_id() -> anyhow::Result<()> let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -459,8 +459,8 @@ async fn test_burn_fungible_asset_insufficient_input_amount() -> anyhow::Result< let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -496,11 +496,11 @@ async fn test_burn_non_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::account - use.$kernel::asset_vault - use.$kernel::memory - use.$kernel::prologue - use.mock::faucet->mock_faucet + use $kernel::account + use $kernel::asset_vault + use $kernel::memory + use $kernel::prologue + use mock::faucet->mock_faucet const FAUCET_SYSDATA_SLOT_NAME = word("{faucet_sysdata_slot_name}") @@ -567,8 +567,8 @@ async fn test_burn_non_fungible_asset_fails_does_not_exist() -> anyhow::Result<( let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin # burn asset @@ -593,7 +593,7 @@ async fn burn_non_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -626,8 +626,8 @@ async fn test_burn_non_fungible_asset_fails_inconsistent_faucet_id() -> anyhow:: let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin # burn asset @@ -661,8 +661,8 @@ async fn test_is_non_fungible_asset_issued_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.miden::faucet + use $kernel::prologue + use miden::faucet begin exec.prologue::prepare_transaction @@ -703,8 +703,8 @@ async fn test_get_total_issuance_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.miden::faucet + use $kernel::prologue + use miden::faucet begin exec.prologue::prepare_transaction @@ -737,8 +737,8 @@ fn setup_non_faucet_account() -> anyhow::Result { )) .compile_component_code( "test::non_faucet_component", - "export.::miden::faucet::mint - export.::miden::faucet::burn", + "pub use ::miden::faucet::mint + pub use ::miden::faucet::burn", )?; let faucet_component = AccountComponent::new(faucet_code, vec![])? .with_supported_type(AccountType::RegularAccountUpdatableCode); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 81416446ec..2037bfac8a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -60,9 +60,9 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let mock_value_slot0 = AccountStorage::mock_value_slot0(); let mock_map_slot = AccountStorage::mock_map_slot(); let foreign_account_code_source = " - use.miden::active_account + use miden::active_account - export.get_item_foreign + pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.1 drop @@ -72,7 +72,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { movup.6 movup.6 drop drop end - export.get_map_item_foreign + pub proc get_map_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.2 drop @@ -122,10 +122,10 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -176,10 +176,10 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::tx const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -236,10 +236,10 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -316,9 +316,9 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { let mock_value_slot1 = AccountStorage::mock_value_slot1(); let foreign_account_code_source_1 = " - use.miden::active_account + use miden::active_account - export.get_item_foreign_1 + pub proc get_item_foreign_1 # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.1 drop @@ -329,9 +329,9 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { end "; let foreign_account_code_source_2 = " - use.miden::active_account + use miden::active_account - export.get_item_foreign_2 + pub proc get_item_foreign_2 # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.2 drop @@ -400,10 +400,10 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") @@ -535,9 +535,9 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { let mock_map_slot = AccountStorage::mock_map_slot(); let foreign_account_code_source = " - use.miden::active_account + use miden::active_account - export.get_item_foreign + pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.1 drop @@ -547,7 +547,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { movup.6 movup.6 drop drop end - export.get_map_item_foreign + pub proc get_map_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.2 drop @@ -581,9 +581,9 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.miden::tx + use miden::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -686,9 +686,9 @@ async fn foreign_account_can_get_balance_and_presence_of_asset() -> anyhow::Resu let foreign_account_code_source = format!( " - use.miden::active_account + use miden::active_account - export.get_asset_balance + pub proc get_asset_balance # get balance of first asset push.{fungible_faucet_id_suffix} push.{fungible_faucet_id_prefix} exec.active_account::get_balance @@ -740,9 +740,9 @@ async fn foreign_account_can_get_balance_and_presence_of_asset() -> anyhow::Resu let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx + use miden::tx begin # Get the added balance of two assets from foreign account @@ -799,9 +799,9 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { let foreign_account_code_source = format!( " - use.miden::active_account + use miden::active_account - export.get_initial_balance + pub proc get_initial_balance # push the faucet ID on the stack push.{fungible_faucet_id_suffix} push.{fungible_faucet_id_prefix} @@ -845,9 +845,9 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx + use miden::tx begin # Get the initial balance of the fungible asset from the foreign account @@ -914,15 +914,15 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { let second_foreign_account_code_source = format!( r#" - use.miden::tx - use.miden::active_account + use miden::tx + use miden::active_account - use.std::sys + use miden::core::sys const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") - export.second_account_foreign_proc + pub proc second_account_foreign_proc # get the storage item at value1 # pad the stack for the `execute_foreign_procedure` execution padw padw @@ -979,14 +979,14 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { // ------ FIRST FOREIGN ACCOUNT --------------------------------------------------------------- let first_foreign_account_code_source = format!( r#" - use.miden::tx - use.miden::active_account + use miden::tx + use miden::active_account - use.std::sys + use miden::core::sys const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") - export.first_account_foreign_proc + pub proc first_account_foreign_proc # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 # => [pad(15)] @@ -1013,7 +1013,7 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { exec.sys::truncate_stack end - export.get_item_foreign + pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.1 drop @@ -1082,10 +1082,8 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys - - use.miden::tx - use.miden::account + use miden::core::sys + use miden::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1147,9 +1145,9 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { // ------ SECOND FOREIGN ACCOUNT --------------------------------------------------------------- // unique procedure which just leaves a constant on the stack let second_foreign_account_code_source = r#" - use.std::sys + use miden::core::sys - export.second_account_foreign_proc + pub proc second_account_foreign_proc # leave a constant result on the stack push.3 @@ -1175,10 +1173,10 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { // unique procedure which calls the second foreign account via FPI and then returns let first_foreign_account_code_source = format!( r#" - use.miden::tx - use.std::sys + use miden::tx + use miden::core::sys - export.first_account_foreign_proc + pub proc first_account_foreign_proc # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 # => [pad(15)] @@ -1242,8 +1240,8 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { // Call the first foreign account's procedure. It will call into the second FA via FPI. let code = format!( r#" - use.std::sys - use.miden::tx + use miden::core::sys + use miden::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1302,11 +1300,11 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let last_foreign_account_code_source = format!( r#" - use.miden::active_account + use miden::active_account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") - export.get_item_foreign + pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure # of the foreign account, not the native one push.1 drop @@ -1347,10 +1345,10 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let foreign_account_code_source = format!( " - use.miden::tx - use.std::sys + use miden::tx + use miden::core::sys - export.read_first_foreign_storage_slot_{foreign_account_index} + pub proc read_first_foreign_storage_slot_{foreign_account_index} # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 # => [pad(15)] @@ -1420,9 +1418,9 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx + use miden::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1467,11 +1465,11 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { // ------ FIRST FOREIGN ACCOUNT --------------------------------------------------------------- let foreign_account_code_source = " - use.miden::tx + use miden::tx - use.std::sys + use miden::core::sys - export.first_account_foreign_proc + pub proc first_account_foreign_proc # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 # => [pad(15)] @@ -1516,9 +1514,9 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx + use miden::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1579,10 +1577,10 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { async fn test_fpi_stale_account() -> anyhow::Result<()> { // Prepare the test data let foreign_account_code_source = " - use.miden::native_account + use miden::native_account # code is not used in this test - export.set_some_item_foreign + pub proc set_some_item_foreign push.34.1 exec.native_account::set_item end @@ -1641,10 +1639,10 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::tx begin exec.prologue::prepare_transaction @@ -1679,10 +1677,10 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { #[tokio::test] async fn test_fpi_get_account_id() -> anyhow::Result<()> { let foreign_account_code_source = " - use.miden::active_account - use.miden::native_account + use miden::active_account + use miden::native_account - export.get_current_and_native_ids + pub proc get_current_and_native_ids # get the ID of the current (foreign) account exec.active_account::get_id # => [acct_id_prefix, acct_id_suffix, pad(16)] @@ -1722,10 +1720,10 @@ async fn test_fpi_get_account_id() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.miden::tx - use.miden::account_id + use miden::tx + use miden::account_id begin # get the IDs of the foreign and native accounts @@ -1878,18 +1876,18 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - // Create foreign procedures that test get_initial_item and get_initial_map_item let foreign_account_code_source = format!( r#" - use.miden::active_account - use.std::sys + use miden::active_account + use miden::core::sys const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") - export.test_get_initial_item + pub proc test_get_initial_item push.MOCK_VALUE_SLOT0[0..2] exec.active_account::get_initial_item exec.sys::truncate_stack end - export.test_get_initial_map_item + pub proc test_get_initial_map_item exec.active_account::get_initial_map_item exec.sys::truncate_stack end @@ -1919,8 +1917,8 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - let code = format!( r#" - use.std::sys - use.miden::tx + use miden::core::sys + use miden::tx const MOCK_MAP_SLOT = word("{mock_map_slot}") diff --git a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs index e0b9fb9f12..6165978995 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs @@ -47,7 +47,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { let code = format!( " - use.miden::input_note + use miden::input_note begin {check_note_0} @@ -104,7 +104,7 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::input_note begin # get the recipient from the input note @@ -158,7 +158,7 @@ async fn test_get_sender() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::input_note begin # get the sender from the input note @@ -257,7 +257,7 @@ async fn test_get_assets() -> anyhow::Result<()> { let code = format!( " - use.miden::input_note + use miden::input_note begin {check_note_0} @@ -302,7 +302,7 @@ async fn test_get_inputs_info() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::input_note begin # get the inputs commitment and length from the input note with index 0 (the only one @@ -352,7 +352,7 @@ async fn test_get_script_root() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::input_note begin # get the script root from the input note with index 0 (the only one we have) @@ -395,7 +395,7 @@ async fn test_get_serial_number() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::input_note begin # get the serial number from the input note with index 0 (the only one we have) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index a48cfcbd0f..9ebd66d5be 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -40,7 +40,7 @@ async fn adding_fungible_assets_with_lazy_loading_succeeds() -> anyhow::Result<( let code = format!( " - use.mock::account + use mock::account begin push.{FUNGIBLE_ASSET1} @@ -87,8 +87,8 @@ async fn removing_fungible_assets_with_lazy_loading_succeeds() -> anyhow::Result let code = format!( " - use.mock::account - use.mock::util + use mock::account + use mock::util begin push.{FUNGIBLE_ASSET1} @@ -185,7 +185,7 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.mock::account + use mock::account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -204,7 +204,7 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] call.account::set_map_item - exec.::std::sys::truncate_stack + exec.::miden::core::sys::truncate_stack end "# ); @@ -253,8 +253,8 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.std::word - use.mock::account + use miden::core::word + use mock::account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -276,7 +276,7 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { padw assert_eqw.err="non-existent value should be the empty word" - exec.::std::sys::truncate_stack + exec.::miden::core::sys::truncate_stack end "# ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs b/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs index 69979d73d3..044cc209d2 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs @@ -32,9 +32,9 @@ async fn insertion() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::link_map + use $kernel::link_map - const.MAP_PTR={map_ptr} + const MAP_PTR={map_ptr} begin # Insert key {entry1_key} into an empty map. @@ -534,7 +534,7 @@ async fn execute_link_map_test(operations: Vec) -> anyhow::Result let code = format!( r#" - use.$kernel::link_map + use $kernel::link_map begin {test_code} end diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index a439f64e8a..aac0489914 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -12,7 +12,7 @@ use miden_objects::account::{AccountBuilder, AccountId}; use miden_objects::assembly::DefaultSourceManager; use miden_objects::assembly::diagnostics::miette::{self, miette}; use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::dsa::rpo_falcon512::SecretKey; +use miden_objects::crypto::dsa::falcon512_rpo::SecretKey; use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; use miden_objects::note::{ Note, @@ -66,8 +66,8 @@ async fn test_note_setup() -> anyhow::Result<()> { }; let code = " - use.$kernel::prologue - use.$kernel::note + use $kernel::prologue + use $kernel::note begin exec.prologue::prepare_transaction @@ -124,9 +124,9 @@ async fn test_note_script_and_note_args() -> miette::Result<()> { }; let code = " - use.$kernel::prologue - use.$kernel::memory - use.$kernel::note + use $kernel::prologue + use $kernel::memory + use $kernel::note begin exec.prologue::prepare_transaction @@ -204,9 +204,9 @@ async fn test_build_recipient() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::note + use miden::note begin # put the values that will be hashed into the memory @@ -293,9 +293,9 @@ async fn test_compute_inputs_commitment() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::note + use miden::note begin # put the values that will be hashed into the memory @@ -399,8 +399,8 @@ async fn test_build_metadata() -> miette::Result<()> { for (iteration, test_metadata) in [test_metadata1, test_metadata2].into_iter().enumerate() { let code = format!( " - use.$kernel::prologue - use.$kernel::output_note + use $kernel::prologue + use $kernel::output_note begin exec.prologue::prepare_transaction @@ -434,8 +434,8 @@ pub async fn test_timelock() -> anyhow::Result<()> { let code = format!( r#" - use.miden::active_note - use.miden::tx + use miden::active_note + use miden::tx begin # store the note inputs to memory starting at address 0 @@ -567,8 +567,8 @@ async fn test_build_note_tag_for_network_account() -> anyhow::Result<()> { let code = format!( " - use.std::sys - use.miden::note + use miden::core::sys + use miden::note begin push.{suffix}.{prefix} diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index d4256a7182..535531618c 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -62,9 +62,9 @@ async fn test_create_note() -> anyhow::Result<()> { let code = format!( " - use.miden::output_note + use miden::output_note - use.$kernel::prologue + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -143,8 +143,8 @@ async fn test_create_note_with_invalid_tag() -> anyhow::Result<()> { fn note_creation_script(tag: Felt) -> String { format!( " - use.miden::output_note - use.$kernel::prologue + use miden::output_note + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -174,10 +174,10 @@ async fn test_create_note_too_many_notes() -> anyhow::Result<()> { let code = format!( " - use.miden::output_note - use.$kernel::constants - use.$kernel::memory - use.$kernel::prologue + use miden::output_note + use $kernel::constants + use $kernel::memory + use $kernel::prologue begin exec.constants::get_max_num_output_notes @@ -292,12 +292,12 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx - use.miden::output_note + use miden::tx + use miden::output_note - use.$kernel::prologue + use $kernel::prologue begin # => [BH, acct_id, IAH, NC] @@ -397,9 +397,9 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { let code = format!( " - use.miden::output_note + use miden::output_note - use.$kernel::prologue + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -466,8 +466,8 @@ async fn test_create_note_and_add_multiple_assets() -> anyhow::Result<()> { let code = format!( " - use.miden::output_note - use.$kernel::prologue + use miden::output_note + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -547,8 +547,8 @@ async fn test_create_note_and_add_same_nft_twice() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.miden::output_note + use $kernel::prologue + use miden::output_note begin exec.prologue::prepare_transaction @@ -639,9 +639,9 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { let recipient = NoteRecipient::new(output_serial_no, input_note_1.script().clone(), inputs); let code = format!( " - use.miden::output_note - use.miden::note - use.$kernel::prologue + use miden::output_note + use miden::note + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -754,8 +754,8 @@ async fn test_get_asset_info() -> anyhow::Result<()> { let tx_script_src = &format!( r#" - use.miden::output_note - use.std::sys + use miden::output_note + use miden::core::sys begin # create an output note with fungible asset 0 @@ -870,8 +870,8 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { let tx_script_src = &format!( r#" - use.miden::output_note - use.std::sys + use miden::output_note + use miden::core::sys begin # create an output note with one asset @@ -984,8 +984,8 @@ async fn test_get_assets() -> anyhow::Result<()> { let tx_script_src = &format!( " - use.miden::output_note - use.std::sys + use miden::output_note + use miden::core::sys begin {create_note_0} diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 6eb4ca1334..ed27cf835e 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -124,7 +124,7 @@ async fn test_transaction_prologue() -> anyhow::Result<()> { }; let code = " - use.$kernel::prologue + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -743,7 +743,7 @@ pub async fn create_account_invalid_seed() -> anyhow::Result<()> { .build()?; let code = " - use.$kernel::prologue + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -761,8 +761,8 @@ pub async fn create_account_invalid_seed() -> anyhow::Result<()> { async fn test_get_blk_version() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code = " - use.$kernel::memory - use.$kernel::prologue + use $kernel::memory + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -787,8 +787,8 @@ async fn test_get_blk_version() -> anyhow::Result<()> { async fn test_get_blk_timestamp() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code = " - use.$kernel::memory - use.$kernel::prologue + use $kernel::memory + use $kernel::prologue begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index b8cb5480ae..b184ef1fbb 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -133,8 +133,8 @@ async fn test_block_procedures() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code = " - use.miden::tx - use.$kernel::prologue + use miden::tx + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -245,12 +245,12 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let tx_script_src = format!( "\ - use.miden::contracts::wallets::basic->wallet - use.miden::output_note + use miden::contracts::wallets::basic->wallet + use miden::output_note # Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] # Outputs: [note_idx] - proc.create_note + proc create_note # pad the stack before the call to prevent accidental modification of the deeper stack # elements padw padw swapdw @@ -266,7 +266,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { # Inputs: [ASSET, note_idx] # Outputs: [ASSET, note_idx] - proc.move_asset_to_note + proc move_asset_to_note # pad the stack before call push.0.0.0 movdn.7 movdn.7 movdn.7 padw padw swapdw # => [ASSET, note_idx, pad(11)] @@ -414,12 +414,12 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { #[tokio::test] async fn user_code_can_abort_transaction_with_summary() -> anyhow::Result<()> { let source_code = r#" - use.miden::auth - use.miden::tx - const.AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized") + use miden::auth + use miden::tx + const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized") #! Inputs: [AUTH_ARGS, pad(12)] #! Outputs: [pad(16)] - export.auth_abort_tx + pub proc auth_abort_tx dropw # => [pad(16)] @@ -624,7 +624,7 @@ async fn tx_summary_commitment_is_signed_by_ecdsa_auth() -> anyhow::Result<()> { #[tokio::test] async fn execute_tx_view_script() -> anyhow::Result<()> { let test_module_source = " - export.foo + pub proc foo push.3.4 add swapw dropw @@ -638,8 +638,8 @@ async fn execute_tx_view_script() -> anyhow::Result<()> { let library = assembler.assemble_library([source]).unwrap(); let source = " - use.test::module_1 - use.std::sys + use test::module_1 + use miden::core::sys begin push.1.2 @@ -648,7 +648,7 @@ async fn execute_tx_view_script() -> anyhow::Result<()> { end "; - let tx_script = CodeBuilder::new(false) + let tx_script = CodeBuilder::new() .with_statically_linked_library(&library)? .compile_tx_script(source)?; let tx_context = TransactionContextBuilder::with_existing_mock_account() @@ -681,8 +681,6 @@ async fn test_tx_script_inputs() -> anyhow::Result<()> { let tx_script_input_value = Word::from([9, 8, 7, 6u32]); let tx_script_src = format!( " - use.miden::account - begin # push the tx script input key onto the stack push.{tx_script_input_key} @@ -714,8 +712,6 @@ async fn test_tx_script_args() -> anyhow::Result<()> { let tx_script_args = Word::from([1, 2, 3, 4u32]); let tx_script_src = r#" - use.miden::account - begin # => [TX_SCRIPT_ARGS] # `TX_SCRIPT_ARGS` value is a user provided word, which could be used during the @@ -759,9 +755,9 @@ async fn test_tx_script_args() -> anyhow::Result<()> { #[tokio::test] async fn inputs_created_correctly() -> anyhow::Result<()> { let account_component_masm = r#" - adv_map.A([6,7,8,9])=[10,11,12,13] + adv_map A([6,7,8,9]) = [10,11,12,13] - export.assert_adv_map + pub proc assert_adv_map # test tx script advice map push.[1,2,3,4] adv.push_mapval adv_loadw @@ -784,9 +780,7 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { )?; let script = r#" - use.miden::account - - adv_map.A([1,2,3,4])=[5,6,7,8] + adv_map A([1,2,3,4]) = [5,6,7,8] begin call.::test::adv_map_component::assert_adv_map diff --git a/crates/miden-testing/src/mock_chain/auth.rs b/crates/miden-testing/src/mock_chain/auth.rs index 7de10857f8..2fa5fc0bb1 100644 --- a/crates/miden-testing/src/mock_chain/auth.rs +++ b/crates/miden-testing/src/mock_chain/auth.rs @@ -88,7 +88,7 @@ impl Auth { match self { Auth::BasicAuth => { let mut rng = ChaCha20Rng::from_seed(Default::default()); - let sec_key = AuthSecretKey::new_rpo_falcon512_with_rng(&mut rng); + let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); let component = AuthRpoFalcon512::new(pub_key).into(); @@ -138,7 +138,7 @@ impl Auth { allow_unauthorized_input_notes, } => { let mut rng = ChaCha20Rng::from_seed(Default::default()); - let sec_key = AuthSecretKey::new_rpo_falcon512_with_rng(&mut rng); + let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); let component = AuthRpoFalcon512Acl::new( diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index f3adace37b..196b96dd5a 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -45,7 +45,7 @@ use miden_objects::block::{ ProvenBlock, }; use miden_objects::crypto::dsa::ecdsa_k256_keccak::SecretKey; -use miden_objects::crypto::merkle::Smt; +use miden_objects::crypto::merkle::smt::Smt; use miden_objects::note::{Note, NoteDetails, NoteType}; use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; use miden_objects::testing::random_signer::RandomBlockSigner; diff --git a/crates/miden-testing/src/mock_host.rs b/crates/miden-testing/src/mock_host.rs index 92b08ecc9b..6273af9e1f 100644 --- a/crates/miden-testing/src/mock_host.rs +++ b/crates/miden-testing/src/mock_host.rs @@ -2,7 +2,7 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::StdLibrary; +use miden_lib::CoreLibrary; use miden_lib::transaction::{EventId, TransactionEventId}; use miden_objects::Word; use miden_processor::{ @@ -50,12 +50,12 @@ impl<'store> MockHost<'store> { pub fn new( exec_host: TransactionExecutorHost<'store, 'static, TransactionContext, UnreachableAuth>, ) -> Self { - // StdLibrary events are always handled. - let stdlib_handlers = StdLibrary::default() + // CoreLibrary events are always handled. + let core_lib_handlers = CoreLibrary::default() .handlers() .into_iter() .map(|(handler_event_name, _)| handler_event_name.to_event_id()); - let mut handled_events = BTreeSet::from_iter(stdlib_handlers); + let mut handled_events = BTreeSet::from_iter(core_lib_handlers); // The default set of transaction events that are always handled. handled_events.extend( diff --git a/crates/miden-testing/src/tx_context/builder.rs b/crates/miden-testing/src/tx_context/builder.rs index ba6e20f708..f52012b99a 100644 --- a/crates/miden-testing/src/tx_context/builder.rs +++ b/crates/miden-testing/src/tx_context/builder.rs @@ -51,8 +51,8 @@ use crate::{MockChain, MockChainNote}; /// let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; /// /// let code = " -/// use.$kernel::prologue -/// use.mock::account +/// use $kernel::prologue +/// use mock::account /// /// begin /// exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index 4f2db63216..304b05c55f 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -133,7 +133,6 @@ impl TransactionContext { .into(); let program = assembler - .with_debug_mode(true) .assemble_program(virtual_source_file) .expect("code was not well formed"); diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index 4b430f47cd..4cb36c1a75 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -20,7 +20,7 @@ use rand::rngs::SmallRng; macro_rules! assert_execution_error { ($execution_result:expr, $expected_err:expr) => { match $execution_result { - Err(miden_processor::ExecutionError::FailedAssertion { label: _, source_file: _, clk: _, err_code, err_msg }) => { + Err(miden_processor::ExecutionError::FailedAssertion { label: _, source_file: _, clk: _, err_code, err_msg, err: _ }) => { if let Some(ref msg) = err_msg { assert_eq!(msg.as_ref(), $expected_err.message(), "error messages did not match"); } @@ -48,6 +48,7 @@ macro_rules! assert_transaction_executor_error { clk: _, err_code, err_msg, + err: _, }, )) => { if let Some(ref msg) = err_msg { @@ -128,9 +129,9 @@ pub fn create_p2any_note( let code = format!( " - use.mock::account - use.miden::active_note - use.miden::contracts::wallets::basic->wallet + use mock::account + use miden::active_note + use miden::contracts::wallets::basic->wallet begin # fetch pointer & number of assets @@ -198,7 +199,7 @@ fn note_script_that_creates_notes<'note>( sender_id: AccountId, output_notes: impl Iterator, ) -> anyhow::Result { - let mut out = String::from("use.miden::output_note\n\nbegin\n"); + let mut out = String::from("use miden::output_note\n\nbegin\n"); for (idx, note) in output_notes.into_iter().enumerate() { anyhow::ensure!( diff --git a/crates/miden-testing/tests/auth/ecdsa_acl.rs b/crates/miden-testing/tests/auth/ecdsa_acl.rs index 0a5d1980ec..415530ff66 100644 --- a/crates/miden-testing/tests/auth/ecdsa_acl.rs +++ b/crates/miden-testing/tests/auth/ecdsa_acl.rs @@ -26,7 +26,7 @@ use crate::prove_and_verify_transaction; // ================================================================================================ const TX_SCRIPT_NO_TRIGGER: &str = r#" - use.mock::account + use mock::account begin call.account::account_procedure_1 drop @@ -46,10 +46,10 @@ fn setup_ecdsa_acl_test( MockAccountComponent::with_slots(AccountStorage::mock_storage_slots()).into(); let get_item_proc_root = component - .get_procedure_root_by_name("mock::account::get_item") + .get_procedure_root_by_path("mock::account::get_item") .expect("get_item procedure should exist"); let set_item_proc_root = component - .get_procedure_root_by_name("mock::account::set_item") + .get_procedure_root_by_path("mock::account::set_item") .expect("set_item procedure should exist"); let auth_trigger_procedures = vec![get_item_proc_root, set_item_proc_root]; @@ -88,10 +88,10 @@ async fn test_ecdsa_acl() -> anyhow::Result<()> { MockAccountComponent::with_slots(AccountStorage::mock_storage_slots()).into(); let get_item_proc_root = component - .get_procedure_root_by_name("mock::account::get_item") + .get_procedure_root_by_path("mock::account::get_item") .expect("get_item procedure should exist"); let set_item_proc_root = component - .get_procedure_root_by_name("mock::account::set_item") + .get_procedure_root_by_path("mock::account::set_item") .expect("set_item procedure should exist"); let auth_trigger_procedures = vec![get_item_proc_root, set_item_proc_root]; @@ -104,7 +104,7 @@ async fn test_ecdsa_acl() -> anyhow::Result<()> { let tx_script_with_trigger_1 = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -119,7 +119,7 @@ async fn test_ecdsa_acl() -> anyhow::Result<()> { let tx_script_with_trigger_2 = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") diff --git a/crates/miden-testing/tests/auth/ecdsa_multisig.rs b/crates/miden-testing/tests/auth/ecdsa_multisig.rs index 35e716e087..58bf8dcd72 100644 --- a/crates/miden-testing/tests/auth/ecdsa_multisig.rs +++ b/crates/miden-testing/tests/auth/ecdsa_multisig.rs @@ -399,7 +399,7 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::update_signers_and_threshold + call.::ecdsa_k256_keccak_multisig::update_signers_and_threshold end "; @@ -644,7 +644,9 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { // Create transaction script let tx_script = CodeBuilder::default() .with_dynamically_linked_library(ecdsa_k256_keccak_multisig_library())? - .compile_tx_script("begin\n call.::update_signers_and_threshold\nend")?; + .compile_tx_script( + "begin\n call.::ecdsa_k256_keccak_multisig::update_signers_and_threshold\nend", + )?; let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; @@ -846,7 +848,7 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::update_signers_and_threshold + call.::ecdsa_k256_keccak_multisig::update_signers_and_threshold end "; @@ -1000,11 +1002,8 @@ async fn test_multisig_proc_threshold_overrides() -> anyhow::Result<()> { &mut RpoRandomCoin::new(Word::from([Felt::new(42); 4])), )?; let multisig_account_interface = AccountInterface::from(&multisig_account); - let send_note_transaction_script = multisig_account_interface.build_send_notes_script( - &[output_note.clone().into()], - None, - false, - )?; + let send_note_transaction_script = + multisig_account_interface.build_send_notes_script(&[output_note.clone().into()], None)?; // Execute transaction without signatures to get tx summary let tx_context_init = mock_chain diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index c35ffcd880..08ba34f17d 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -45,7 +45,7 @@ fn setup_keys_and_authenticators( let mut authenticators = Vec::new(); for _ in 0..num_approvers { - let sec_key = AuthSecretKey::new_rpo_falcon512_with_rng(&mut rng); + let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key(); secret_keys.push(sec_key); @@ -395,7 +395,7 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::update_signers_and_threshold + call.::rpo_falcon_512_multisig::update_signers_and_threshold end "; @@ -639,7 +639,9 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { // Create transaction script let tx_script = CodeBuilder::default() .with_dynamically_linked_library(rpo_falcon_512_multisig_library())? - .compile_tx_script("begin\n call.::update_signers_and_threshold\nend")?; + .compile_tx_script( + "begin\n call.::rpo_falcon_512_multisig::update_signers_and_threshold\nend", + )?; let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; @@ -836,7 +838,7 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::update_signers_and_threshold + call.::rpo_falcon_512_multisig::update_signers_and_threshold end "; @@ -990,11 +992,8 @@ async fn test_multisig_proc_threshold_overrides() -> anyhow::Result<()> { &mut RpoRandomCoin::new(Word::from([Felt::new(42); 4])), )?; let multisig_account_interface = AccountInterface::from(&multisig_account); - let send_note_transaction_script = multisig_account_interface.build_send_notes_script( - &[output_note.clone().into()], - None, - false, - )?; + let send_note_transaction_script = + multisig_account_interface.build_send_notes_script(&[output_note.clone().into()], None)?; // Execute transaction without signatures to get tx summary let tx_context_init = mock_chain diff --git a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs index 791b33ffc4..ca1bb2f8f8 100644 --- a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs +++ b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs @@ -25,7 +25,7 @@ use miden_tx::TransactionExecutorError; // ================================================================================================ const TX_SCRIPT_NO_TRIGGER: &str = r#" - use.mock::account + use mock::account begin call.account::account_procedure_1 drop @@ -45,10 +45,10 @@ fn setup_rpo_falcon_acl_test( MockAccountComponent::with_slots(AccountStorage::mock_storage_slots()).into(); let get_item_proc_root = component - .get_procedure_root_by_name("mock::account::get_item") + .get_procedure_root_by_path("mock::account::get_item") .expect("get_item procedure should exist"); let set_item_proc_root = component - .get_procedure_root_by_name("mock::account::set_item") + .get_procedure_root_by_path("mock::account::set_item") .expect("set_item procedure should exist"); let auth_trigger_procedures = vec![get_item_proc_root, set_item_proc_root]; @@ -87,10 +87,10 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { MockAccountComponent::with_slots(AccountStorage::mock_storage_slots()).into(); let get_item_proc_root = component - .get_procedure_root_by_name("mock::account::get_item") + .get_procedure_root_by_path("mock::account::get_item") .expect("get_item procedure should exist"); let set_item_proc_root = component - .get_procedure_root_by_name("mock::account::set_item") + .get_procedure_root_by_path("mock::account::set_item") .expect("set_item procedure should exist"); let auth_trigger_procedures = vec![get_item_proc_root, set_item_proc_root]; @@ -103,7 +103,7 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { let tx_script_with_trigger_1 = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -118,7 +118,7 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { let tx_script_with_trigger_2 = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 11b4b1c96a..a45dabe03a 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -358,7 +358,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let trigger_note_script_code = format!( " - use.miden::note + use miden::note begin # Build recipient hash from SERIAL_NUM, SCRIPT_ROOT, and INPUTS_COMMITMENT diff --git a/crates/miden-testing/tests/scripts/p2id.rs b/crates/miden-testing/tests/scripts/p2id.rs index 7e4b7a5874..0a9aecf3bd 100644 --- a/crates/miden-testing/tests/scripts/p2id.rs +++ b/crates/miden-testing/tests/scripts/p2id.rs @@ -222,7 +222,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { let tx_script_src = &format!( " - use.miden::output_note + use miden::output_note begin push.{recipient_1} push.{note_execution_hint_1} diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index bc50fc1e93..a0b2b7fefb 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -52,11 +52,8 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let partial_note: PartialNote = note.clone().into(); let expiration_delta = 10u16; - let send_note_transaction_script = sender_account_interface.build_send_notes_script( - slice::from_ref(&partial_note), - Some(expiration_delta), - false, - )?; + let send_note_transaction_script = sender_account_interface + .build_send_notes_script(slice::from_ref(&partial_note), Some(expiration_delta))?; let executed_transaction = mock_chain .build_tx_context(sender_basic_wallet_account.id(), &[], &[]) @@ -116,11 +113,8 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { let partial_note: PartialNote = note.clone().into(); let expiration_delta = 10u16; - let send_note_transaction_script = sender_account_interface.build_send_notes_script( - slice::from_ref(&partial_note), - Some(expiration_delta), - false, - )?; + let send_note_transaction_script = sender_account_interface + .build_send_notes_script(slice::from_ref(&partial_note), Some(expiration_delta))?; let _executed_transaction = mock_chain .build_tx_context(sender_basic_fungible_faucet_account.id(), &[], &[]) diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index 748b641421..abce6229f4 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -40,7 +40,7 @@ pub async fn prove_send_swap_note() -> anyhow::Result<()> { let tx_script_src = &format!( " - use.miden::output_note + use miden::output_note begin push.{recipient} push.{note_execution_hint} diff --git a/crates/miden-testing/tests/wallet/mod.rs b/crates/miden-testing/tests/wallet/mod.rs index 75f6da77f8..fe50b9a24d 100644 --- a/crates/miden-testing/tests/wallet/mod.rs +++ b/crates/miden-testing/tests/wallet/mod.rs @@ -16,7 +16,7 @@ fn wallet_creation() { let seed = [0_u8; 32]; let mut rng = ChaCha20Rng::from_seed(seed); - let sec_key = AuthSecretKey::new_rpo_falcon512_with_rng(&mut rng); + let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); let auth_scheme: AuthScheme = AuthScheme::RpoFalcon512 { pub_key }; diff --git a/crates/miden-tx/src/auth/tx_authenticator.rs b/crates/miden-tx/src/auth/tx_authenticator.rs index 86a9febb70..af1617412e 100644 --- a/crates/miden-tx/src/auth/tx_authenticator.rs +++ b/crates/miden-tx/src/auth/tx_authenticator.rs @@ -284,7 +284,7 @@ mod test { #[test] fn serialize_auth_key() { - let auth_key = AuthSecretKey::new_rpo_falcon512(); + let auth_key = AuthSecretKey::new_falcon512_rpo(); let serialized = auth_key.to_bytes(); let deserialized = AuthSecretKey::read_from_bytes(&serialized).unwrap(); diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 3b51d5ee1d..5cf283c035 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -8,7 +8,7 @@ use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic; use miden_objects::asset::AssetVaultKey; use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::SmtProofError; +use miden_objects::crypto::merkle::smt::SmtProofError; use miden_objects::note::{NoteId, NoteMetadata}; use miden_objects::transaction::TransactionSummary; use miden_objects::{ diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 77e0e9507a..f75562b069 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -10,7 +10,7 @@ use miden_objects::assembly::debuginfo::Location; use miden_objects::assembly::{SourceFile, SourceManagerSync, SourceSpan}; use miden_objects::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::SmtProof; +use miden_objects::crypto::merkle::smt::SmtProof; use miden_objects::note::{NoteInputs, NoteMetadata, NoteRecipient}; use miden_objects::transaction::{InputNote, InputNotes, OutputNote, TransactionSummary}; use miden_objects::vm::AdviceMap; @@ -451,22 +451,22 @@ where &mut self, process: &ProcessState, ) -> impl FutureMaybeSend, EventError>> { - let stdlib_event_result = self.base_host.handle_stdlib_events(process); + let core_lib_event_result = self.base_host.handle_core_lib_events(process); - // If the event was handled by a stdlib handler (Ok(Some)), we will return the result from + // If the event was handled by a core lib handler (Ok(Some)), we will return the result from // within the async block below. So, we only need to extract th tx event if the event was // not yet handled (Ok(None)). - let tx_event_result = match stdlib_event_result { + let tx_event_result = match core_lib_event_result { Ok(None) => Some(TransactionEvent::extract(&self.base_host, process)), _ => None, }; async move { - if let Some(mutations) = stdlib_event_result? { + if let Some(mutations) = core_lib_event_result? { return Ok(mutations); } - // The outer None means the event was handled by stdlib handlers. + // The outer None means the event was handled by core lib handlers. let Some(tx_event_result) = tx_event_result else { return Ok(Vec::new()); }; diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index fc39d3b517..a65aaf5bd6 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -10,7 +10,7 @@ mod account_procedures; pub use account_procedures::AccountProcedureIndexMap; pub(crate) mod note_builder; -use miden_lib::StdLibrary; +use miden_lib::CoreLibrary; use miden_lib::transaction::EventId; use note_builder::OutputNoteBuilder; @@ -101,7 +101,7 @@ pub struct TransactionBaseHost<'store, STORE> { output_notes: BTreeMap, /// Handle the VM default events _before_ passing it to user defined ones. - stdlib_handlers: EventHandlerRegistry, + core_lib_handlers: EventHandlerRegistry, } impl<'store, STORE> TransactionBaseHost<'store, STORE> { @@ -116,14 +116,14 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { scripts_mast_store: ScriptMastForestStore, acct_procedure_index_map: AccountProcedureIndexMap, ) -> Self { - let stdlib_handlers = { + let core_lib_handlers = { let mut registry = EventHandlerRegistry::new(); - let stdlib = StdLibrary::default(); - for (event_id, handler) in stdlib.handlers() { + let core_lib = CoreLibrary::default(); + for (event_id, handler) in core_lib.handlers() { registry .register(event_id, handler) - .expect("There are no duplicates in the stdlibrary handlers"); + .expect("There are no duplicates in the core library handlers"); } registry }; @@ -136,7 +136,7 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { acct_procedure_index_map, output_notes: BTreeMap::default(), input_notes, - stdlib_handlers, + core_lib_handlers, } } @@ -263,16 +263,16 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { // EVENT HANDLERS // -------------------------------------------------------------------------------------------- - /// Handles the event if the stdlib event handler registry contains a handler with the emitted + /// Handles the event if the core lib event handler registry contains a handler with the emitted /// event ID. /// /// Returns `Some` if the event was handled, `None` otherwise. - pub fn handle_stdlib_events( + pub fn handle_core_lib_events( &self, process: &ProcessState, ) -> Result>, EventError> { let event_id = EventId::from_felt(process.get_stack_item(0)); - if let Some(mutations) = self.stdlib_handlers.handle_event(event_id, process)? { + if let Some(mutations) = self.core_lib_handlers.handle_event(event_id, process)? { Ok(Some(mutations)) } else { Ok(None) diff --git a/crates/miden-tx/src/prover/mast_store.rs b/crates/miden-tx/src/prover/mast_store.rs index 3ee9d7d5dc..722aea587d 100644 --- a/crates/miden-tx/src/prover/mast_store.rs +++ b/crates/miden-tx/src/prover/mast_store.rs @@ -2,7 +2,7 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; use miden_lib::transaction::TransactionKernel; -use miden_lib::{MidenLib, StdLibrary}; +use miden_lib::{CoreLibrary, MidenLib}; use miden_objects::Word; use miden_objects::account::AccountCode; use miden_objects::assembly::mast::MastForest; @@ -28,7 +28,7 @@ impl TransactionMastStore { /// Returns a new [TransactionMastStore] instantiated with the default libraries. /// /// The default libraries include: - /// - Miden standard library (miden-stdlib). + /// - Miden core library (miden-core-lib). /// - Miden protocol library (miden-lib). /// - Transaction kernel. pub fn new() -> Self { @@ -39,9 +39,9 @@ impl TransactionMastStore { let kernels_forest = TransactionKernel::kernel().mast_forest().clone(); store.insert(kernels_forest); - // load miden-stdlib MAST forest - let miden_stdlib_forest = StdLibrary::default().mast_forest().clone(); - store.insert(miden_stdlib_forest); + // load miden-core-lib MAST forest + let miden_core_lib_forest = CoreLibrary::default().mast_forest().clone(); + store.insert(miden_core_lib_forest); // load miden lib MAST forest let miden_lib_forest = MidenLib::default().mast_forest().clone(); diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index 3572a8eb4e..7bf33dfa03 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -91,7 +91,7 @@ where } fn on_event(&mut self, process: &ProcessState) -> Result, EventError> { - if let Some(advice_mutations) = self.base_host.handle_stdlib_events(process)? { + if let Some(advice_mutations) = self.base_host.handle_core_lib_events(process)? { return Ok(advice_mutations); } diff --git a/crates/miden-tx/src/verifier/mod.rs b/crates/miden-tx/src/verifier/mod.rs index 062b3f8ccc..4e0778a8c0 100644 --- a/crates/miden-tx/src/verifier/mod.rs +++ b/crates/miden-tx/src/verifier/mod.rs @@ -1,4 +1,4 @@ -use miden_lib::StdLibrary; +use miden_lib::CoreLibrary; use miden_lib::transaction::TransactionKernel; use miden_objects::transaction::ProvenTransaction; use miden_objects::vm::ProgramInfo; @@ -50,7 +50,7 @@ impl TransactionVerifier { ); // verify transaction proof - let precompile_verifiers = StdLibrary::default().verifier_registry(); + let precompile_verifiers = CoreLibrary::default().verifier_registry(); let proof_security_level = verify_with_precompiles( self.tx_program_info.clone(), stack_inputs, diff --git a/deny.toml b/deny.toml index 799128b16d..3679142018 100644 --- a/deny.toml +++ b/deny.toml @@ -59,6 +59,9 @@ skip-tree = [ # Allow windows-sys v0.48.x/v0.59.x - multiple Windows system libraries { name = "windows-sys", version = "=0.48.*" }, { name = "windows-sys", version = "=0.59.*" }, + # Allow syn v1.x and v2.x - our derive macros need v1.x while ecosystem uses v2.x + { name = "syn", version = "=1.0.109" }, + { name = "syn", version = "=2.0.111" }, ] wildcards = "allow" diff --git a/deny.toml~ b/deny.toml~ deleted file mode 100644 index 1d91acb183..0000000000 --- a/deny.toml~ +++ /dev/null @@ -1,69 +0,0 @@ -# cargo-deny configuration for DeepProve workspace - -# Graph configuration -[graph] -targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"] - -# Advisory database configuration -[advisories] -db-path = "~/.cargo/advisory-db" -db-urls = ["https://github.com/rustsec/advisory-db"] -ignore = [ - "RUSTSEC-2024-0436", # paste is unmaintained but no alternative available - "RUSTSEC-2025-0055", # tracing-subscriber vulnerability - will be fixed by upgrade - "RUSTSEC-2025-0056", # adler is unmaintained but used by miniz_oxide -] -yanked = "warn" - -# License configuration -[licenses] -allow = [ - "Apache-2.0 WITH LLVM-exception", - "Apache-2.0", - "BSD-2-Clause", - "BSD-3-Clause", - "ISC", - "MIT", - "Unicode-3.0", - "Zlib", -] -exceptions = [ - # Each entry is the crate and version constraint, and its specific allowed licenses - #{ allow = ["Zlib"], name = "adler32", version = "*" }, -] - -# Bans configuration -[bans] -allow = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -deny = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -highlight = "all" -multiple-versions = "deny" -skip = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -skip-tree = [ - # Allow getrandom v0.2.x - legacy version used by nanorand - { name = "getrandom", version = "=0.2.*" }, - # Allow rand_core v0.6.x - legacy version used by winterfell crates - { name = "rand_core", version = "=0.6.*" }, - # Allow rustc_version v0.2.x - build dependency version - { name = "rustc_version", version = "=0.2.*" }, - # Allow unicode-width v0.1.x - used by miden-formatting vs textwrap conflict - { name = "unicode-width", version = "=0.1.*" }, - # Allow windows-targets v0.48.x - older Windows target version - { name = "windows-targets", version = "=0.48.*" }, - # Allow windows-sys v0.48.x/v0.59.x - multiple Windows system libraries - { name = "windows-sys", version = "=0.48.*" }, - { name = "windows-sys", version = "=0.59.*" }, -] -wildcards = "allow" - -# Sources configuration -[sources] -allow-registry = ["https://github.com/rust-lang/crates.io-index"] -unknown-git = "deny" -unknown-registry = "deny" From 63697be43fd8926074464ade56ec507deabcb53a Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Sat, 20 Dec 2025 15:11:09 +0700 Subject: [PATCH 072/114] chore: prepare split into protocol and standards (#2184) --- CHANGELOG.md | 1 + .../asm/kernels/transaction/lib/epilogue.masm | 4 +- crates/miden-lib/asm/miden/active_note.masm | 59 ---- .../asm/miden/contracts/wallets/basic.masm | 59 ++++ crates/miden-lib/asm/note_scripts/P2ID.masm | 3 +- crates/miden-lib/asm/note_scripts/P2IDE.masm | 5 +- crates/miden-lib/build.rs | 215 +++++++-------- .../src/account/faucets/basic_fungible.rs | 6 +- .../src/account/faucets/network_fungible.rs | 6 +- .../src/account/interface/component.rs | 29 -- .../src/account/interface/extension.rs | 261 ++++++++++++++++++ crates/miden-lib/src/account/interface/mod.rs | 210 +------------- .../miden-lib/src/account/interface/test.rs | 29 +- crates/miden-lib/src/errors/mod.rs | 11 +- crates/miden-lib/src/errors/protocol.rs | 45 +++ .../{note_script_errors.rs => standards.rs} | 27 +- .../{tx_kernel_errors.rs => tx_kernel.rs} | 25 +- crates/miden-lib/src/note/well_known_note.rs | 2 +- .../src/testing/account_interface.rs | 4 +- .../src/kernel_tests/tx/test_account.rs | 2 +- .../src/kernel_tests/tx/test_active_note.rs | 2 +- .../src/kernel_tests/tx/test_asset_vault.rs | 2 +- .../src/kernel_tests/tx/test_auth.rs | 4 +- .../src/kernel_tests/tx/test_epilogue.rs | 2 +- .../src/kernel_tests/tx/test_faucet.rs | 2 +- .../src/kernel_tests/tx/test_fpi.rs | 2 +- .../src/kernel_tests/tx/test_output_note.rs | 2 +- .../src/kernel_tests/tx/test_prologue.rs | 2 +- .../src/kernel_tests/tx/test_tx.rs | 6 +- .../tests/auth/ecdsa_multisig.rs | 6 +- crates/miden-testing/tests/auth/multisig.rs | 6 +- crates/miden-testing/tests/scripts/faucet.rs | 2 +- crates/miden-testing/tests/scripts/p2id.rs | 2 +- crates/miden-testing/tests/scripts/p2ide.rs | 2 +- .../miden-testing/tests/scripts/send_note.rs | 7 +- docs/src/protocol_library.md | 1 - 36 files changed, 560 insertions(+), 493 deletions(-) create mode 100644 crates/miden-lib/src/account/interface/extension.rs create mode 100644 crates/miden-lib/src/errors/protocol.rs rename crates/miden-lib/src/errors/{note_script_errors.rs => standards.rs} (77%) rename crates/miden-lib/src/errors/{tx_kernel_errors.rs => tx_kernel.rs} (92%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47e7c209d0..69f790fdec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). +- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184)). - [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). diff --git a/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm b/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm index 1a6c7ef5f4..196f4aeaa6 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm @@ -14,7 +14,7 @@ const ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME="total number of as const ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY="executed transaction neither changed the account state, nor consumed any notes" -const ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT="auth procedure had been called from outside the epilogue" +const ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT="auth procedure has been called from outside the epilogue" const ERR_EPILOGUE_NONCE_CANNOT_BE_0="nonce cannot be 0 after an account-creating transaction" @@ -220,7 +220,7 @@ proc execute_auth_procedure # if auth procedure was called already, it must have been called by a user, which is disallowed exec.account::was_procedure_called - assertz.err=ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT + assertz.err=ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT # => [auth_procedure_ptr, AUTH_ARGS, pad(12)] # execute the auth procedure diff --git a/crates/miden-lib/asm/miden/active_note.masm b/crates/miden-lib/asm/miden/active_note.masm index dba1e4a65d..533f248077 100644 --- a/crates/miden-lib/asm/miden/active_note.masm +++ b/crates/miden-lib/asm/miden/active_note.masm @@ -2,7 +2,6 @@ use miden::core::mem use miden::kernel_proc_offsets use miden::note -use miden::contracts::wallets::basic->wallet # ERRORS # ================================================================================================= @@ -260,64 +259,6 @@ pub proc get_script_root # => [SCRIPT_ROOT] end -#! Adds all assets from the active note to the native account's vault. -#! -#! Inputs: [] -#! Outputs: [] -@locals(1024) -pub proc add_assets_to_account - # write assets to local memory starting at offset 0 - # we have allocated 4 * MAX_ASSETS_PER_NOTE number of locals so all assets should fit - # since the asset memory will be overwritten, we don't have to initialize the locals to zero - locaddr.0 exec.get_assets - # => [num_of_assets, ptr = 0] - - # compute the pointer at which we should stop iterating - mul.4 dup.1 add - # => [end_ptr, ptr] - - # pad the stack and move the pointer to the top - padw movup.5 - # => [ptr, EMPTY_WORD, end_ptr] - - # loop if the amount of assets is non-zero - dup dup.6 neq - # => [should_loop, ptr, EMPTY_WORD, end_ptr] - - while.true - # => [ptr, EMPTY_WORD, end_ptr] - - # save the pointer so that we can use it later - dup movdn.5 - # => [ptr, EMPTY_WORD, ptr, end_ptr] - - # load the asset - mem_loadw_be - # => [ASSET, ptr, end_ptr] - - # pad the stack before call - padw swapw padw padw swapdw - # => [ASSET, pad(12), ptr, end_ptr] - - # add asset to the account - call.wallet::receive_asset - # => [pad(16), ptr, end_ptr] - - # clean the stack after call - dropw dropw dropw - # => [EMPTY_WORD, ptr, end_ptr] - - # increment the pointer and continue looping if ptr != end_ptr - movup.4 add.4 dup dup.6 neq - # => [should_loop, ptr+4, EMPTY_WORD, end_ptr] - end - # => [ptr', EMPTY_WORD, end_ptr] - - # clear the stack - drop dropw drop - # => [] -end - # HELPER PROCEDURES # ================================================================================================= diff --git a/crates/miden-lib/asm/miden/contracts/wallets/basic.masm b/crates/miden-lib/asm/miden/contracts/wallets/basic.masm index 57c701cb94..c1fccdd0b9 100644 --- a/crates/miden-lib/asm/miden/contracts/wallets/basic.masm +++ b/crates/miden-lib/asm/miden/contracts/wallets/basic.masm @@ -1,5 +1,6 @@ use miden::native_account use miden::output_note +use miden::active_note # CONSTANTS # ================================================================================================= @@ -59,3 +60,61 @@ pub proc move_asset_to_note exec.output_note::add_asset # => [ASSET, note_idx, pad(11)] end + +#! Adds all assets from the active note to the native account's vault. +#! +#! Inputs: [] +#! Outputs: [] +@locals(1024) +pub proc add_assets_to_account + # write assets to local memory starting at offset 0 + # we have allocated 4 * MAX_ASSETS_PER_NOTE number of locals so all assets should fit + # since the asset memory will be overwritten, we don't have to initialize the locals to zero + locaddr.0 exec.active_note::get_assets + # => [num_of_assets, ptr = 0] + + # compute the pointer at which we should stop iterating + mul.4 dup.1 add + # => [end_ptr, ptr] + + # pad the stack and move the pointer to the top + padw movup.5 + # => [ptr, EMPTY_WORD, end_ptr] + + # loop if the amount of assets is non-zero + dup dup.6 neq + # => [should_loop, ptr, EMPTY_WORD, end_ptr] + + while.true + # => [ptr, EMPTY_WORD, end_ptr] + + # save the pointer so that we can use it later + dup movdn.5 + # => [ptr, EMPTY_WORD, ptr, end_ptr] + + # load the asset + mem_loadw_be + # => [ASSET, ptr, end_ptr] + + # pad the stack before call + padw swapw padw padw swapdw + # => [ASSET, pad(12), ptr, end_ptr] + + # add asset to the account + call.receive_asset + # => [pad(16), ptr, end_ptr] + + # clean the stack after call + dropw dropw dropw + # => [EMPTY_WORD, ptr, end_ptr] + + # increment the pointer and continue looping if ptr != end_ptr + movup.4 add.4 dup dup.6 neq + # => [should_loop, ptr+4, EMPTY_WORD, end_ptr] + end + # => [ptr', EMPTY_WORD, end_ptr] + + # clear the stack + drop dropw drop + # => [] +end diff --git a/crates/miden-lib/asm/note_scripts/P2ID.masm b/crates/miden-lib/asm/note_scripts/P2ID.masm index 0cab854848..a6eec2e964 100644 --- a/crates/miden-lib/asm/note_scripts/P2ID.masm +++ b/crates/miden-lib/asm/note_scripts/P2ID.masm @@ -1,6 +1,7 @@ use miden::active_account use miden::account_id use miden::active_note +use miden::contracts::wallets::basic->basic_wallet # ERRORS # ================================================================================================= @@ -47,6 +48,6 @@ begin exec.account_id::is_equal assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH # => [] - exec.active_note::add_assets_to_account + exec.basic_wallet::add_assets_to_account # => [] end diff --git a/crates/miden-lib/asm/note_scripts/P2IDE.masm b/crates/miden-lib/asm/note_scripts/P2IDE.masm index 02502d3758..5a73ec1074 100644 --- a/crates/miden-lib/asm/note_scripts/P2IDE.masm +++ b/crates/miden-lib/asm/note_scripts/P2IDE.masm @@ -2,6 +2,7 @@ use miden::active_account use miden::account_id use miden::active_note use miden::tx +use miden::contracts::wallets::basic->basic_wallet # ERRORS # ================================================================================================= @@ -64,7 +65,7 @@ proc reclaim_note # => [] # add note assets to account - exec.active_note::add_assets_to_account + exec.basic_wallet::add_assets_to_account # => [] end @@ -130,7 +131,7 @@ begin if.true # we can safely consume the note since the active account is the target of the note - dropw exec.active_note::add_assets_to_account + dropw exec.basic_wallet::add_assets_to_account # => [] else diff --git a/crates/miden-lib/build.rs b/crates/miden-lib/build.rs index 6843e1e791..a1a599f594 100644 --- a/crates/miden-lib/build.rs +++ b/crates/miden-lib/build.rs @@ -12,9 +12,6 @@ use miden_assembly::{Assembler, DefaultSourceManager, KernelLibrary, Library, Re use regex::Regex; use walkdir::WalkDir; -/// A map where the key is the error name and the value is the error code with the message. -type ErrorCategoryMap = BTreeMap>; - // CONSTANTS // ================================================================================================ @@ -33,27 +30,29 @@ const SHARED_MODULES_DIR: &str = "shared_modules"; const ASM_TX_KERNEL_DIR: &str = "kernels/transaction"; const KERNEL_PROCEDURES_RS_FILE: &str = "src/transaction/kernel_procedures.rs"; -const TX_KERNEL_ERRORS_FILE: &str = "src/errors/tx_kernel_errors.rs"; -const NOTE_SCRIPT_ERRORS_FILE: &str = "src/errors/note_script_errors.rs"; +const TX_KERNEL_ERRORS_FILE: &str = "src/errors/tx_kernel.rs"; +const PROTOCOL_LIB_ERRORS_FILE: &str = "src/errors/protocol.rs"; +const STANDARDS_ERRORS_FILE: &str = "src/errors/standards.rs"; const TX_KERNEL_ERRORS_ARRAY_NAME: &str = "TX_KERNEL_ERRORS"; -const NOTE_SCRIPT_ERRORS_ARRAY_NAME: &str = "NOTE_SCRIPT_ERRORS"; - -const TX_KERNEL_ERROR_CATEGORIES: [TxKernelErrorCategory; 14] = [ - TxKernelErrorCategory::Kernel, - TxKernelErrorCategory::Prologue, - TxKernelErrorCategory::Epilogue, - TxKernelErrorCategory::Tx, - TxKernelErrorCategory::Note, - TxKernelErrorCategory::Account, - TxKernelErrorCategory::ForeignAccount, - TxKernelErrorCategory::Faucet, - TxKernelErrorCategory::FungibleAsset, - TxKernelErrorCategory::NonFungibleAsset, - TxKernelErrorCategory::Vault, - TxKernelErrorCategory::LinkMap, - TxKernelErrorCategory::InputNote, - TxKernelErrorCategory::OutputNote, +const PROTOCOL_LIB_ERRORS_ARRAY_NAME: &str = "PROTOCOL_LIB_ERRORS"; +const STANDARDS_ERRORS_ARRAY_NAME: &str = "STANDARDS_ERRORS"; + +const TX_KERNEL_ERROR_CATEGORIES: [&str; 14] = [ + "KERNEL", + "PROLOGUE", + "EPILOGUE", + "TX", + "NOTE", + "ACCOUNT", + "FOREIGN_ACCOUNT", + "FAUCET", + "FUNGIBLE_ASSET", + "NON_FUNGIBLE_ASSET", + "VAULT", + "LINK_MAP", + "INPUT_NOTE", + "OUTPUT_NOTE", ]; // PRE-PROCESSING @@ -531,20 +530,61 @@ fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { return Ok(()); } - let categories = - extract_all_masm_errors(asm_source_dir).context("failed to extract all masm errors")?; + // Transaction kernel errors + // ------------------------------------------ - for (category, errors) in categories { - // Generate the errors file. - let error_file_content = generate_error_file_content(category, errors)?; - std::fs::write(category.error_file_name(), error_file_content).into_diagnostic()?; - } + let tx_kernel_dir = asm_source_dir.join(ASM_TX_KERNEL_DIR); + let errors = + extract_all_masm_errors(&tx_kernel_dir).context("failed to extract all masm errors")?; + validate_tx_kernel_category(&errors)?; + + generate_error_file( + ErrorModule { + file_name: TX_KERNEL_ERRORS_FILE, + array_name: TX_KERNEL_ERRORS_ARRAY_NAME, + }, + errors, + )?; + + // Miden protocol library errors + // ------------------------------------------ + + let miden_dir = asm_source_dir.join(ASM_MIDEN_DIR); + let errors = + extract_all_masm_errors(&miden_dir).context("failed to extract all masm errors")?; + + generate_error_file( + ErrorModule { + file_name: PROTOCOL_LIB_ERRORS_FILE, + array_name: PROTOCOL_LIB_ERRORS_ARRAY_NAME, + }, + errors, + )?; + + // Miden standards errors + // ------------------------------------------ + + let account_components_dir = asm_source_dir.join(ASM_ACCOUNT_COMPONENTS_DIR); + let note_scripts_dir = asm_source_dir.join(ASM_NOTE_SCRIPTS_DIR); + let mut errors = extract_all_masm_errors(&account_components_dir) + .context("failed to extract all masm errors")?; + errors.extend( + extract_all_masm_errors(¬e_scripts_dir).context("failed to extract all masm errors")?, + ); + + generate_error_file( + ErrorModule { + file_name: STANDARDS_ERRORS_FILE, + array_name: STANDARDS_ERRORS_ARRAY_NAME, + }, + errors, + )?; Ok(()) } /// Extract all masm errors from the given path and returns a map by error category. -fn extract_all_masm_errors(asm_source_dir: &Path) -> Result { +fn extract_all_masm_errors(asm_source_dir: &Path) -> Result> { // We use a BTree here to order the errors by their categories which is the first part after the // ERR_ prefix and to allow for the same error to be defined multiple times in different files // (as long as the constant name and error messages match). @@ -560,17 +600,12 @@ fn extract_all_masm_errors(asm_source_dir: &Path) -> Result { extract_masm_errors(&mut errors, &file_contents)?; } - let mut category_map: BTreeMap> = BTreeMap::new(); - - for (error_name, error) in errors.into_iter() { - let category = ErrorCategory::match_category(&error_name)?; - - let named_error = NamedError { name: error_name, message: error.message }; - - category_map.entry(category).or_default().push(named_error); - } + let errors = errors + .into_iter() + .map(|(error_name, error)| NamedError { name: error_name, message: error.message }) + .collect(); - Ok(category_map) + Ok(errors) } /// Extracts the errors from a single masm file and inserts them into the provided map. @@ -633,8 +668,9 @@ fn is_new_error_category<'a>(last_error: &mut Option<&'a str>, current_error: &' is_new } -/// Generates the content of an error file for the given category and the set of errors. -fn generate_error_file_content(category: ErrorCategory, errors: Vec) -> Result { +/// Generates the content of an error file for the given category and the set of errors and writes +/// it to the category's file. +fn generate_error_file(category: ErrorModule, errors: Vec) -> Result<()> { let mut output = String::new(); writeln!(output, "use crate::errors::MasmError;\n").unwrap(); @@ -642,11 +678,10 @@ fn generate_error_file_content(category: ErrorCategory, errors: Vec) writeln!( output, "// This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. // -// To add a new error, define a constant in masm of the pattern `const ERR__...`. -// Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, -// Non-Fungible-Asset, ...). +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). " ) .unwrap(); @@ -656,7 +691,7 @@ fn generate_error_file_content(category: ErrorCategory, errors: Vec) "// {} // ================================================================================================ ", - category.array_name().replace("_", " ") + category.array_name.replace("_", " ") ) .unwrap(); @@ -677,7 +712,9 @@ fn generate_error_file_content(category: ErrorCategory, errors: Vec) .into_diagnostic()?; } - Ok(output) + std::fs::write(category.file_name, output).into_diagnostic()?; + + Ok(()) } type ErrorName = String; @@ -693,76 +730,28 @@ struct NamedError { message: String, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum ErrorCategory { - TxKernel, - NoteScript, +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +struct ErrorModule { + pub file_name: &'static str, + pub array_name: &'static str, } -impl ErrorCategory { - pub const fn error_file_name(&self) -> &'static str { - match self { - ErrorCategory::TxKernel => TX_KERNEL_ERRORS_FILE, - ErrorCategory::NoteScript => NOTE_SCRIPT_ERRORS_FILE, - } - } - - pub const fn array_name(&self) -> &'static str { - match self { - ErrorCategory::TxKernel => TX_KERNEL_ERRORS_ARRAY_NAME, - ErrorCategory::NoteScript => NOTE_SCRIPT_ERRORS_ARRAY_NAME, - } - } - - pub fn match_category(error_name: &ErrorName) -> Result { - for kernel_category in TX_KERNEL_ERROR_CATEGORIES { - if error_name.starts_with(kernel_category.category_name()) { - return Ok(ErrorCategory::TxKernel); - } +/// Validates that all error names in the provided slice start with a known tx kernel error +/// category. +fn validate_tx_kernel_category(errors: &[NamedError]) -> Result<()> { + for error in errors { + if !TX_KERNEL_ERROR_CATEGORIES + .iter() + .any(|known_category| error.name.starts_with(known_category)) + { + return Err(miette::miette!( + "error `{}` does not start with a known tx kernel error category", + error.name + )); } - - // If the error is not a tx kernel error, consider it a note script error. - Ok(ErrorCategory::NoteScript) } -} -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum TxKernelErrorCategory { - Kernel, - Prologue, - Epilogue, - Tx, - Note, - Account, - ForeignAccount, - Faucet, - FungibleAsset, - NonFungibleAsset, - Vault, - LinkMap, - InputNote, - OutputNote, -} - -impl TxKernelErrorCategory { - pub const fn category_name(&self) -> &'static str { - match self { - TxKernelErrorCategory::Kernel => "KERNEL", - TxKernelErrorCategory::Prologue => "PROLOGUE", - TxKernelErrorCategory::Epilogue => "EPILOGUE", - TxKernelErrorCategory::Tx => "TX", - TxKernelErrorCategory::Note => "NOTE", - TxKernelErrorCategory::Account => "ACCOUNT", - TxKernelErrorCategory::ForeignAccount => "FOREIGN_ACCOUNT", - TxKernelErrorCategory::Faucet => "FAUCET", - TxKernelErrorCategory::FungibleAsset => "FUNGIBLE_ASSET", - TxKernelErrorCategory::NonFungibleAsset => "NON_FUNGIBLE_ASSET", - TxKernelErrorCategory::Vault => "VAULT", - TxKernelErrorCategory::LinkMap => "LINK_MAP", - TxKernelErrorCategory::InputNote => "INPUT_NOTE", - TxKernelErrorCategory::OutputNote => "OUTPUT_NOTE", - } - } + Ok(()) } // EVENT CONSTANTS FILE GENERATION diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 0c7d81cac5..2201504fde 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -20,7 +20,7 @@ use crate::account::auth::{ AuthRpoFalcon512AclConfig, }; use crate::account::components::basic_fungible_faucet_library; -use crate::account::interface::{AccountComponentInterface, AccountInterface}; +use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::procedure_digest; // BASIC FUNGIBLE FAUCET ACCOUNT COMPONENT @@ -208,7 +208,7 @@ impl TryFrom for BasicFungibleFaucet { type Error = FungibleFaucetError; fn try_from(account: Account) -> Result { - let account_interface = AccountInterface::from(&account); + let account_interface = AccountInterface::from_account(&account); BasicFungibleFaucet::try_from_interface(account_interface, account.storage()) } @@ -218,7 +218,7 @@ impl TryFrom<&Account> for BasicFungibleFaucet { type Error = FungibleFaucetError; fn try_from(account: &Account) -> Result { - let account_interface = AccountInterface::from(account); + let account_interface = AccountInterface::from_account(account); BasicFungibleFaucet::try_from_interface(account_interface, account.storage()) } diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 023091d144..809b1c04d2 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -16,7 +16,7 @@ use miden_objects::{Felt, FieldElement, Word}; use super::{BasicFungibleFaucet, FungibleFaucetError}; use crate::account::auth::NoAuth; use crate::account::components::network_fungible_faucet_library; -use crate::account::interface::{AccountComponentInterface, AccountInterface}; +use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::procedure_digest; // NETWORK FUNGIBLE FAUCET ACCOUNT COMPONENT @@ -243,7 +243,7 @@ impl TryFrom for NetworkFungibleFaucet { type Error = FungibleFaucetError; fn try_from(account: Account) -> Result { - let account_interface = AccountInterface::from(&account); + let account_interface = AccountInterface::from_account(&account); NetworkFungibleFaucet::try_from_interface(account_interface, account.storage()) } @@ -253,7 +253,7 @@ impl TryFrom<&Account> for NetworkFungibleFaucet { type Error = FungibleFaucetError; fn try_from(account: &Account) -> Result { - let account_interface = AccountInterface::from(account); + let account_interface = AccountInterface::from_account(account); NetworkFungibleFaucet::try_from_interface(account_interface, account.storage()) } diff --git a/crates/miden-lib/src/account/interface/component.rs b/crates/miden-lib/src/account/interface/component.rs index fc26de424d..0ce3bda505 100644 --- a/crates/miden-lib/src/account/interface/component.rs +++ b/crates/miden-lib/src/account/interface/component.rs @@ -1,4 +1,3 @@ -use alloc::collections::BTreeSet; use alloc::string::{String, ToString}; use alloc::vec::Vec; @@ -16,7 +15,6 @@ use crate::account::auth::{ AuthRpoFalcon512Acl, AuthRpoFalcon512Multisig, }; -use crate::account::components::WellKnownComponent; use crate::account::interface::AccountInterfaceError; // ACCOUNT COMPONENT INTERFACE @@ -175,33 +173,6 @@ impl AccountComponentInterface { } } - /// Creates a vector of [AccountComponentInterface] instances. This vector specifies the - /// components which were used to create an account with the provided procedures info array. - pub fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec { - let mut component_interface_vec = Vec::new(); - - let mut procedures = BTreeSet::from_iter(procedures.iter().copied()); - - // Well known component interfaces - // ---------------------------------------------------------------------------------------- - - // Get all available well known components which could be constructed from the `procedures` - // map and push them to the `component_interface_vec` - WellKnownComponent::extract_well_known_components( - &mut procedures, - &mut component_interface_vec, - ); - - // Custom component interfaces - // ---------------------------------------------------------------------------------------- - - // All remaining procedures are put into the custom bucket. - component_interface_vec - .push(AccountComponentInterface::Custom(procedures.into_iter().collect())); - - component_interface_vec - } - /// Generates a body for the note creation of the `send_note` transaction script. The resulting /// code could use different procedures for note creation, which depends on the used interface. /// diff --git a/crates/miden-lib/src/account/interface/extension.rs b/crates/miden-lib/src/account/interface/extension.rs new file mode 100644 index 0000000000..8e698b0b5c --- /dev/null +++ b/crates/miden-lib/src/account/interface/extension.rs @@ -0,0 +1,261 @@ +use alloc::collections::BTreeSet; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use miden_objects::Word; +use miden_objects::account::{Account, AccountCode, AccountId, AccountProcedureRoot}; +use miden_objects::assembly::mast::{MastForest, MastNode, MastNodeId}; +use miden_objects::note::{Note, NoteScript}; +use miden_processor::MastNodeExt; + +use crate::AuthScheme; +use crate::account::components::{ + WellKnownComponent, + basic_fungible_faucet_library, + basic_wallet_library, + ecdsa_k256_keccak_acl_library, + ecdsa_k256_keccak_library, + ecdsa_k256_keccak_multisig_library, + network_fungible_faucet_library, + no_auth_library, + rpo_falcon_512_acl_library, + rpo_falcon_512_library, + rpo_falcon_512_multisig_library, +}; +use crate::account::interface::{ + AccountComponentInterface, + AccountInterface, + NoteAccountCompatibility, +}; +use crate::note::WellKnownNote; + +// ACCOUNT INTERFACE EXTENSION TRAIT +// ================================================================================================ + +/// An extension for [`AccountInterface`] that allows instantiation from higher-level types. +pub trait AccountInterfaceExt { + /// Creates a new [`AccountInterface`] instance from the provided account ID, authentication + /// schemes and account code. + fn from_code(account_id: AccountId, auth: Vec, code: &AccountCode) -> Self; + + /// Creates a new [`AccountInterface`] instance from the provided [`Account`]. + fn from_account(account: &Account) -> Self; + + /// Returns [NoteAccountCompatibility::Maybe] if the provided note is compatible with the + /// current [AccountInterface], and [NoteAccountCompatibility::No] otherwise. + fn is_compatible_with(&self, note: &Note) -> NoteAccountCompatibility; + + /// Returns the set of digests of all procedures from all account component interfaces. + fn get_procedure_digests(&self) -> BTreeSet; +} + +impl AccountInterfaceExt for AccountInterface { + fn from_code(account_id: AccountId, auth: Vec, code: &AccountCode) -> Self { + let components = AccountComponentInterface::from_procedures(code.procedures()); + + Self::new(account_id, auth, components) + } + + fn from_account(account: &Account) -> Self { + let components = AccountComponentInterface::from_procedures(account.code().procedures()); + let mut auth = Vec::new(); + + // Find the auth component and extract all auth schemes from it + // An account should have only one auth component + for component in components.iter() { + if component.is_auth_component() { + auth = component.get_auth_schemes(account.storage()); + break; + } + } + + Self::new(account.id(), auth, components) + } + + /// Returns [NoteAccountCompatibility::Maybe] if the provided note is compatible with the + /// current [AccountInterface], and [NoteAccountCompatibility::No] otherwise. + fn is_compatible_with(&self, note: &Note) -> NoteAccountCompatibility { + if let Some(well_known_note) = WellKnownNote::from_note(note) { + if well_known_note.is_compatible_with(self) { + NoteAccountCompatibility::Maybe + } else { + NoteAccountCompatibility::No + } + } else { + verify_note_script_compatibility(note.script(), self.get_procedure_digests()) + } + } + + fn get_procedure_digests(&self) -> BTreeSet { + let mut component_proc_digests = BTreeSet::new(); + for component in self.components.iter() { + match component { + AccountComponentInterface::BasicWallet => { + component_proc_digests + .extend(basic_wallet_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::BasicFungibleFaucet => { + component_proc_digests + .extend(basic_fungible_faucet_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::NetworkFungibleFaucet => { + component_proc_digests.extend( + network_fungible_faucet_library().mast_forest().procedure_digests(), + ); + }, + AccountComponentInterface::AuthEcdsaK256Keccak => { + component_proc_digests + .extend(ecdsa_k256_keccak_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::AuthEcdsaK256KeccakAcl => { + component_proc_digests + .extend(ecdsa_k256_keccak_acl_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::AuthEcdsaK256KeccakMultisig => { + component_proc_digests.extend( + ecdsa_k256_keccak_multisig_library().mast_forest().procedure_digests(), + ); + }, + AccountComponentInterface::AuthRpoFalcon512 => { + component_proc_digests + .extend(rpo_falcon_512_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::AuthRpoFalcon512Acl => { + component_proc_digests + .extend(rpo_falcon_512_acl_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::AuthRpoFalcon512Multisig => { + component_proc_digests.extend( + rpo_falcon_512_multisig_library().mast_forest().procedure_digests(), + ); + }, + AccountComponentInterface::AuthNoAuth => { + component_proc_digests + .extend(no_auth_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::Custom(custom_procs) => { + component_proc_digests + .extend(custom_procs.iter().map(|info| *info.mast_root())); + }, + } + } + + component_proc_digests + } +} + +/// An extension for [`AccountComponentInterface`] that allows instantiation from a set of procedure +/// roots. +pub trait AccountComponentInterfaceExt { + /// Creates a vector of [`AccountComponentInterface`] instances from the provided set of + /// procedures. + fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec; +} + +impl AccountComponentInterfaceExt for AccountComponentInterface { + fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec { + let mut component_interface_vec = Vec::new(); + + let mut procedures = BTreeSet::from_iter(procedures.iter().copied()); + + // Well known component interfaces + // ---------------------------------------------------------------------------------------- + + // Get all available well known components which could be constructed from the + // `procedures` map and push them to the `component_interface_vec` + WellKnownComponent::extract_well_known_components( + &mut procedures, + &mut component_interface_vec, + ); + + // Custom component interfaces + // ---------------------------------------------------------------------------------------- + + // All remaining procedures are put into the custom bucket. + component_interface_vec + .push(AccountComponentInterface::Custom(procedures.into_iter().collect())); + + component_interface_vec + } +} + +// HELPER FUNCTIONS +// ------------------------------------------------------------------------------------------------ + +/// Verifies that the provided note script is compatible with the target account interfaces. +/// +/// This is achieved by checking that at least one execution branch in the note script is compatible +/// with the account procedures vector. +/// +/// This check relies on the fact that account procedures are the only procedures that are `call`ed +/// from note scripts, while kernel procedures are `sycall`ed. +fn verify_note_script_compatibility( + note_script: &NoteScript, + account_procedures: BTreeSet, +) -> NoteAccountCompatibility { + // collect call branches of the note script + let branches = collect_call_branches(note_script); + + // if none of the branches are compatible with the target account, return a `CheckResult::No` + if !branches.iter().any(|call_targets| call_targets.is_subset(&account_procedures)) { + return NoteAccountCompatibility::No; + } + + NoteAccountCompatibility::Maybe +} + +/// Collect call branches by recursively traversing through program execution branches and +/// accumulating call targets. +fn collect_call_branches(note_script: &NoteScript) -> Vec> { + let mut branches = vec![BTreeSet::new()]; + + let entry_node = note_script.entrypoint(); + recursively_collect_call_branches(entry_node, &mut branches, ¬e_script.mast()); + branches +} + +/// Generates a list of calls invoked in each execution branch of the provided code block. +fn recursively_collect_call_branches( + mast_node_id: MastNodeId, + branches: &mut Vec>, + note_script_forest: &Arc, +) { + let mast_node = ¬e_script_forest[mast_node_id]; + + match mast_node { + MastNode::Block(_) => {}, + MastNode::Join(join_node) => { + recursively_collect_call_branches(join_node.first(), branches, note_script_forest); + recursively_collect_call_branches(join_node.second(), branches, note_script_forest); + }, + MastNode::Split(split_node) => { + let current_branch = branches.last().expect("at least one execution branch").clone(); + recursively_collect_call_branches(split_node.on_false(), branches, note_script_forest); + + // If the previous branch had additional calls we need to create a new branch + if branches.last().expect("at least one execution branch").len() > current_branch.len() + { + branches.push(current_branch); + } + + recursively_collect_call_branches(split_node.on_true(), branches, note_script_forest); + }, + MastNode::Loop(loop_node) => { + recursively_collect_call_branches(loop_node.body(), branches, note_script_forest); + }, + MastNode::Call(call_node) => { + if call_node.is_syscall() { + return; + } + + let callee_digest = note_script_forest[call_node.callee()].digest(); + + branches + .last_mut() + .expect("at least one execution branch") + .insert(callee_digest); + }, + MastNode::Dyn(_) => {}, + MastNode::External(_) => {}, + } +} diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-lib/src/account/interface/mod.rs index 9094f43d23..d17901eefa 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-lib/src/account/interface/mod.rs @@ -1,31 +1,13 @@ -use alloc::collections::BTreeSet; use alloc::string::String; -use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{Account, AccountCode, AccountId, AccountIdPrefix, AccountType}; -use miden_objects::assembly::mast::{MastForest, MastNode, MastNodeId}; -use miden_objects::note::{Note, NoteScript, PartialNote}; +use miden_objects::account::{AccountId, AccountIdPrefix, AccountType}; +use miden_objects::note::PartialNote; use miden_objects::transaction::TransactionScript; -use miden_processor::MastNodeExt; use thiserror::Error; use crate::AuthScheme; -use crate::account::components::{ - basic_fungible_faucet_library, - basic_wallet_library, - ecdsa_k256_keccak_acl_library, - ecdsa_k256_keccak_library, - ecdsa_k256_keccak_multisig_library, - network_fungible_faucet_library, - no_auth_library, - rpo_falcon_512_acl_library, - rpo_falcon_512_library, - rpo_falcon_512_multisig_library, -}; use crate::errors::CodeBuilderError; -use crate::note::WellKnownNote; use crate::utils::CodeBuilder; #[cfg(test)] @@ -34,6 +16,9 @@ mod test; mod component; pub use component::AccountComponentInterface; +mod extension; +pub use extension::{AccountComponentInterfaceExt, AccountInterfaceExt}; + // ACCOUNT INTERFACE // ================================================================================================ @@ -54,10 +39,12 @@ impl AccountInterface { // -------------------------------------------------------------------------------------------- /// Creates a new [`AccountInterface`] instance from the provided account ID, authentication - /// schemes and account code. - pub fn new(account_id: AccountId, auth: Vec, code: &AccountCode) -> Self { - let components = AccountComponentInterface::from_procedures(code.procedures()); - + /// schemes and account component interfaces. + pub fn new( + account_id: AccountId, + auth: Vec, + components: Vec, + ) -> Self { Self { account_id, auth, components } } @@ -116,78 +103,6 @@ impl AccountInterface { pub fn components(&self) -> &Vec { &self.components } - - /// Returns [NoteAccountCompatibility::Maybe] if the provided note is compatible with the - /// current [AccountInterface], and [NoteAccountCompatibility::No] otherwise. - pub fn is_compatible_with(&self, note: &Note) -> NoteAccountCompatibility { - if let Some(well_known_note) = WellKnownNote::from_note(note) { - if well_known_note.is_compatible_with(self) { - NoteAccountCompatibility::Maybe - } else { - NoteAccountCompatibility::No - } - } else { - verify_note_script_compatibility(note.script(), self.get_procedure_digests()) - } - } - - /// Returns a digests set of all procedures from all account component interfaces. - pub(crate) fn get_procedure_digests(&self) -> BTreeSet { - let mut component_proc_digests = BTreeSet::new(); - for component in self.components.iter() { - match component { - AccountComponentInterface::BasicWallet => { - component_proc_digests - .extend(basic_wallet_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::BasicFungibleFaucet => { - component_proc_digests - .extend(basic_fungible_faucet_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::NetworkFungibleFaucet => { - component_proc_digests.extend( - network_fungible_faucet_library().mast_forest().procedure_digests(), - ); - }, - AccountComponentInterface::AuthEcdsaK256Keccak => { - component_proc_digests - .extend(ecdsa_k256_keccak_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::AuthEcdsaK256KeccakAcl => { - component_proc_digests - .extend(ecdsa_k256_keccak_acl_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::AuthEcdsaK256KeccakMultisig => { - component_proc_digests.extend( - ecdsa_k256_keccak_multisig_library().mast_forest().procedure_digests(), - ); - }, - AccountComponentInterface::AuthRpoFalcon512 => { - component_proc_digests - .extend(rpo_falcon_512_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::AuthRpoFalcon512Acl => { - component_proc_digests - .extend(rpo_falcon_512_acl_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::AuthRpoFalcon512Multisig => { - component_proc_digests.extend( - rpo_falcon_512_multisig_library().mast_forest().procedure_digests(), - ); - }, - AccountComponentInterface::AuthNoAuth => { - component_proc_digests - .extend(no_auth_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::Custom(custom_procs) => { - component_proc_digests - .extend(custom_procs.iter().map(|info| *info.mast_root())); - }, - } - } - - component_proc_digests - } } // ------------------------------------------------------------------------------------------------ @@ -298,28 +213,6 @@ impl AccountInterface { } } -impl From<&Account> for AccountInterface { - fn from(account: &Account) -> Self { - let components = AccountComponentInterface::from_procedures(account.code().procedures()); - let mut auth = Vec::new(); - - // Find the auth component and extract all auth schemes from it - // An account should have only one auth component - for component in components.iter() { - if component.is_auth_component() { - auth = component.get_auth_schemes(account.storage()); - break; - } - } - - Self { - account_id: account.id(), - auth, - components, - } - } -} - // NOTE ACCOUNT COMPATIBILITY // ================================================================================================ @@ -338,87 +231,6 @@ pub enum NoteAccountCompatibility { Yes, } -// HELPER FUNCTIONS -// ------------------------------------------------------------------------------------------------ - -/// Verifies that the provided note script is compatible with the target account interfaces. -/// -/// This is achieved by checking that at least one execution branch in the note script is compatible -/// with the account procedures vector. -/// -/// This check relies on the fact that account procedures are the only procedures that are `call`ed -/// from note scripts, while kernel procedures are `sycall`ed. -fn verify_note_script_compatibility( - note_script: &NoteScript, - account_procedures: BTreeSet, -) -> NoteAccountCompatibility { - // collect call branches of the note script - let branches = collect_call_branches(note_script); - - // if none of the branches are compatible with the target account, return a `CheckResult::No` - if !branches.iter().any(|call_targets| call_targets.is_subset(&account_procedures)) { - return NoteAccountCompatibility::No; - } - - NoteAccountCompatibility::Maybe -} - -/// Collect call branches by recursively traversing through program execution branches and -/// accumulating call targets. -fn collect_call_branches(note_script: &NoteScript) -> Vec> { - let mut branches = vec![BTreeSet::new()]; - - let entry_node = note_script.entrypoint(); - recursively_collect_call_branches(entry_node, &mut branches, ¬e_script.mast()); - branches -} - -/// Generates a list of calls invoked in each execution branch of the provided code block. -fn recursively_collect_call_branches( - mast_node_id: MastNodeId, - branches: &mut Vec>, - note_script_forest: &Arc, -) { - let mast_node = ¬e_script_forest[mast_node_id]; - - match mast_node { - MastNode::Block(_) => {}, - MastNode::Join(join_node) => { - recursively_collect_call_branches(join_node.first(), branches, note_script_forest); - recursively_collect_call_branches(join_node.second(), branches, note_script_forest); - }, - MastNode::Split(split_node) => { - let current_branch = branches.last().expect("at least one execution branch").clone(); - recursively_collect_call_branches(split_node.on_false(), branches, note_script_forest); - - // If the previous branch had additional calls we need to create a new branch - if branches.last().expect("at least one execution branch").len() > current_branch.len() - { - branches.push(current_branch); - } - - recursively_collect_call_branches(split_node.on_true(), branches, note_script_forest); - }, - MastNode::Loop(loop_node) => { - recursively_collect_call_branches(loop_node.body(), branches, note_script_forest); - }, - MastNode::Call(call_node) => { - if call_node.is_syscall() { - return; - } - - let callee_digest = note_script_forest[call_node.callee()].digest(); - - branches - .last_mut() - .expect("at least one execution branch") - .insert(callee_digest); - }, - MastNode::Dyn(_) => {}, - MastNode::External(_) => {}, - } -} - // ACCOUNT INTERFACE ERROR // ============================================================================================ diff --git a/crates/miden-lib/src/account/interface/test.rs b/crates/miden-lib/src/account/interface/test.rs index c9beb24218..b085508911 100644 --- a/crates/miden-lib/src/account/interface/test.rs +++ b/crates/miden-lib/src/account/interface/test.rs @@ -31,6 +31,7 @@ use crate::account::faucets::BasicFungibleFaucet; use crate::account::interface::{ AccountComponentInterface, AccountInterface, + AccountInterfaceExt, NoteAccountCompatibility, }; use crate::account::wallets::BasicWallet; @@ -51,7 +52,7 @@ fn test_basic_wallet_default_notes() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); let mock_seed = Word::from([Felt::new(4), Felt::new(5), Felt::new(6), Felt::new(7)]).as_bytes(); let faucet_account = AccountBuilder::new(mock_seed) @@ -67,7 +68,7 @@ fn test_basic_wallet_default_notes() { ) .build_existing() .expect("failed to create wallet account"); - let faucet_account_interface = AccountInterface::from(&faucet_account); + let faucet_account_interface = AccountInterface::from_account(&faucet_account); let p2id_note = create_p2id_note( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap(), @@ -160,7 +161,7 @@ fn test_custom_account_default_note() { .with_component(account_component.clone()) .build_existing() .unwrap(); - let target_account_interface = AccountInterface::from(&target_account); + let target_account_interface = AccountInterface::from_account(&target_account); let p2id_note = create_p2id_note( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap(), @@ -246,7 +247,7 @@ fn test_basic_wallet_custom_notes() { .with_assets(vec![FungibleAsset::mock(20)]) .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); let sender_account_id = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into().unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); @@ -336,7 +337,7 @@ fn test_basic_fungible_faucet_custom_notes() { ) .build_existing() .expect("failed to create wallet account"); - let faucet_account_interface = AccountInterface::from(&faucet_account); + let faucet_account_interface = AccountInterface::from_account(&faucet_account); let sender_account_id = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into().unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); @@ -439,7 +440,7 @@ fn test_custom_account_custom_notes() { .with_component(account_component.clone()) .build_existing() .unwrap(); - let target_account_interface = AccountInterface::from(&target_account); + let target_account_interface = AccountInterface::from_account(&target_account); let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let sender_account = AccountBuilder::new(mock_seed) @@ -550,7 +551,7 @@ fn test_custom_account_multiple_components_custom_notes() { .with_component(BasicWallet) .build_existing() .unwrap(); - let target_account_interface = AccountInterface::from(&target_account); + let target_account_interface = AccountInterface::from_account(&target_account); let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let sender_account = AccountBuilder::new(mock_seed) @@ -677,7 +678,7 @@ fn test_get_auth_scheme_ecdsa_k256_keccak() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); // Find the EcdsaK256Keccak component interface let ecdsa_k256_keccak_component = wallet_account_interface @@ -707,7 +708,7 @@ fn test_get_auth_scheme_rpo_falcon512() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); // Find the RpoFalcon512 component interface let rpo_falcon_component = wallet_account_interface @@ -737,7 +738,7 @@ fn test_get_auth_scheme_no_auth() { .build_existing() .expect("failed to create no-auth account"); - let no_auth_account_interface = AccountInterface::from(&no_auth_account); + let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); // Find the NoAuth component interface let no_auth_component = no_auth_account_interface @@ -781,7 +782,7 @@ fn test_account_interface_from_account_uses_get_auth_scheme() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); // Should have exactly one auth scheme assert_eq!(wallet_account_interface.auth().len(), 1); @@ -801,7 +802,7 @@ fn test_account_interface_from_account_uses_get_auth_scheme() { .build_existing() .expect("failed to create no-auth account"); - let no_auth_account_interface = AccountInterface::from(&no_auth_account); + let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); // Should have exactly one auth scheme assert_eq!(no_auth_account_interface.auth().len(), 1); @@ -822,7 +823,7 @@ fn test_account_interface_get_auth_scheme() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); // Test that auth() method provides the authentication schemes assert_eq!(wallet_account_interface.auth().len(), 1); @@ -840,7 +841,7 @@ fn test_account_interface_get_auth_scheme() { .build_existing() .expect("failed to create no-auth account"); - let no_auth_account_interface = AccountInterface::from(&no_auth_account); + let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); // Test that auth() method provides the authentication schemes assert_eq!(no_auth_account_interface.auth().len(), 1); diff --git a/crates/miden-lib/src/errors/mod.rs b/crates/miden-lib/src/errors/mod.rs index 32a5d0b629..4f7fbaa593 100644 --- a/crates/miden-lib/src/errors/mod.rs +++ b/crates/miden-lib/src/errors/mod.rs @@ -1,10 +1,17 @@ +/// The errors from the MASM code of the transaction kernel. #[cfg(any(feature = "testing", test))] #[rustfmt::skip] -pub mod tx_kernel_errors; +pub mod tx_kernel; +/// The errors from the MASM code of the Miden protocol library. #[cfg(any(feature = "testing", test))] #[rustfmt::skip] -pub mod note_script_errors; +pub mod protocol; + +/// The errors from the MASM code of the Miden standards. +#[cfg(any(feature = "testing", test))] +#[rustfmt::skip] +pub mod standards; mod masm_error; pub use masm_error::MasmError; diff --git a/crates/miden-lib/src/errors/protocol.rs b/crates/miden-lib/src/errors/protocol.rs new file mode 100644 index 0000000000..6bff1565c0 --- /dev/null +++ b/crates/miden-lib/src/errors/protocol.rs @@ -0,0 +1,45 @@ +use crate::errors::MasmError; + +// This file is generated by build.rs, do not modify manually. +// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. +// +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). + +// PROTOCOL LIB ERRORS +// ================================================================================================ + +/// Error Message: "the account ID must have storage mode public if the network flag is set" +pub const ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT: MasmError = MasmError::from_static_str("the account ID must have storage mode public if the network flag is set"); +/// Error Message: "least significant byte of the account ID suffix must be zero" +pub const ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO: MasmError = MasmError::from_static_str("least significant byte of the account ID suffix must be zero"); +/// Error Message: "most significant bit of the account ID suffix must be zero" +pub const ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO: MasmError = MasmError::from_static_str("most significant bit of the account ID suffix must be zero"); +/// Error Message: "unknown account storage mode in account ID" +pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE: MasmError = MasmError::from_static_str("unknown account storage mode in account ID"); +/// Error Message: "unknown version in account ID" +pub const ERR_ACCOUNT_ID_UNKNOWN_VERSION: MasmError = MasmError::from_static_str("unknown version in account ID"); + +/// Error Message: "burn requires exactly 1 note asset" +pub const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("burn requires exactly 1 note asset"); + +/// Error Message: "fungible asset build operation called with amount that exceeds the maximum allowed asset amount" +pub const ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT: MasmError = MasmError::from_static_str("fungible asset build operation called with amount that exceeds the maximum allowed asset amount"); +/// Error Message: "distribute would cause the maximum supply to be exceeded" +pub const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED: MasmError = MasmError::from_static_str("distribute would cause the maximum supply to be exceeded"); +/// Error Message: "failed to build the fungible asset because the provided faucet id is not from a fungible faucet" +pub const ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the fungible asset because the provided faucet id is not from a fungible faucet"); + +/// Error Message: "failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet" +pub const ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet"); + +/// Error Message: "note data does not match the commitment" +pub const ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT: MasmError = MasmError::from_static_str("note data does not match the commitment"); +/// Error Message: "the specified number of note inputs does not match the actual number" +pub const ERR_NOTE_INVALID_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("the specified number of note inputs does not match the actual number"); + +/// Error Message: "note sender is not the owner of the faucet who can mint assets" +pub const ERR_ONLY_OWNER_CAN_MINT: MasmError = MasmError::from_static_str("note sender is not the owner of the faucet who can mint assets"); + +/// Error Message: "number of note inputs exceeded the maximum limit of 1024" +pub const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 1024"); diff --git a/crates/miden-lib/src/errors/note_script_errors.rs b/crates/miden-lib/src/errors/standards.rs similarity index 77% rename from crates/miden-lib/src/errors/note_script_errors.rs rename to crates/miden-lib/src/errors/standards.rs index debd063d91..b02a414dca 100644 --- a/crates/miden-lib/src/errors/note_script_errors.rs +++ b/crates/miden-lib/src/errors/standards.rs @@ -1,30 +1,26 @@ use crate::errors::MasmError; // This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. // -// To add a new error, define a constant in masm of the pattern `const ERR__...`. -// Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, -// Non-Fungible-Asset, ...). +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). -// NOTE SCRIPT ERRORS +// STANDARDS ERRORS // ================================================================================================ -/// Error Message: "auth procedure had been called from outside the epilogue" -pub const ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT: MasmError = MasmError::from_static_str("auth procedure had been called from outside the epilogue"); - -/// Error Message: "burn requires exactly 1 note asset" -pub const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("burn requires exactly 1 note asset"); - /// Error Message: "number of approvers must be equal to or greater than threshold" pub const ERR_MALFORMED_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers must be equal to or greater than threshold"); +/// Error Message: "failed to approve multisig transaction as it was already executed" +pub const ERR_TX_ALREADY_EXECUTED: MasmError = MasmError::from_static_str("failed to approve multisig transaction as it was already executed"); + +/// Error Message: "number of approvers or threshold must not be zero" +pub const ERR_ZERO_IN_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers or threshold must not be zero"); + /// Error Message: "MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" pub const ERR_MINT_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes"); -/// Error Message: "note sender is not the owner of the faucet who can mint assets" -pub const ERR_ONLY_OWNER_CAN_MINT: MasmError = MasmError::from_static_str("note sender is not the owner of the faucet who can mint assets"); - /// Error Message: "failed to reclaim P2IDE note because the reclaiming account is not the sender" pub const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER: MasmError = MasmError::from_static_str("failed to reclaim P2IDE note because the reclaiming account is not the sender"); /// Error Message: "P2IDE reclaim is disabled" @@ -45,6 +41,3 @@ pub const ERR_P2ID_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_st pub const ERR_SWAP_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("SWAP script requires exactly 1 note asset"); /// Error Message: "SWAP script expects exactly 12 note inputs" pub const ERR_SWAP_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("SWAP script expects exactly 12 note inputs"); - -/// Error Message: "number of approvers or threshold must not be zero" -pub const ERR_ZERO_IN_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers or threshold must not be zero"); diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel.rs similarity index 92% rename from crates/miden-lib/src/errors/tx_kernel_errors.rs rename to crates/miden-lib/src/errors/tx_kernel.rs index b1de5fa4cc..cbfae11c6a 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel.rs @@ -1,11 +1,10 @@ use crate::errors::MasmError; // This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. // -// To add a new error, define a constant in masm of the pattern `const ERR__...`. -// Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, -// Non-Fungible-Asset, ...). +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). // TX KERNEL ERRORS // ================================================================================================ @@ -65,6 +64,8 @@ pub const ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS: MasmError = MasmError::from_static /// Error Message: "storage slot with the provided name does not exist" pub const ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME: MasmError = MasmError::from_static_str("storage slot with the provided name does not exist"); +/// Error Message: "auth procedure has been called from outside the epilogue" +pub const ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT: MasmError = MasmError::from_static_str("auth procedure has been called from outside the epilogue"); /// Error Message: "executed transaction neither changed the account state, nor consumed any notes" pub const ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY: MasmError = MasmError::from_static_str("executed transaction neither changed the account state, nor consumed any notes"); /// Error Message: "nonce cannot be 0 after an account-creating transaction" @@ -98,10 +99,6 @@ pub const ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT: MasmError = MasmError::from_st /// Error Message: "maximum allowed number of foreign account to be loaded (64) was exceeded" pub const ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED: MasmError = MasmError::from_static_str("maximum allowed number of foreign account to be loaded (64) was exceeded"); -/// Error Message: "fungible asset build operation called with amount that exceeds the maximum allowed asset amount" -pub const ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT: MasmError = MasmError::from_static_str("fungible asset build operation called with amount that exceeds the maximum allowed asset amount"); -/// Error Message: "distribute would cause the maximum supply to be exceeded" -pub const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED: MasmError = MasmError::from_static_str("distribute would cause the maximum supply to be exceeded"); /// Error Message: "the origin of the fungible asset is not this faucet" pub const ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN: MasmError = MasmError::from_static_str("the origin of the fungible asset is not this faucet"); /// Error Message: "malformed fungible asset: `ASSET[1]` must be 0" @@ -110,8 +107,6 @@ pub const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ONE_MUST_BE_ZERO: MasmError = MasmEr pub const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_TWO_AND_THREE_MUST_BE_FUNGIBLE_FAUCET_ID: MasmError = MasmError::from_static_str("malformed fungible asset: `ASSET[2]` and `ASSET[3]` must be a valid fungible faucet id"); /// Error Message: "malformed fungible asset: `ASSET[0]` exceeds the maximum allowed amount" pub const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS: MasmError = MasmError::from_static_str("malformed fungible asset: `ASSET[0]` exceeds the maximum allowed amount"); -/// Error Message: "failed to build the fungible asset because the provided faucet id is not from a fungible faucet" -pub const ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the fungible asset because the provided faucet id is not from a fungible faucet"); /// Error Message: "requested input note index should be less than the total number of input notes" pub const ERR_INPUT_NOTE_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("requested input note index should be less than the total number of input notes"); @@ -144,8 +139,6 @@ pub const ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN: MasmError = MasmError::fr pub const ERR_NON_FUNGIBLE_ASSET_FORMAT_ELEMENT_THREE_MUST_BE_FUNGIBLE_FAUCET_ID: MasmError = MasmError::from_static_str("malformed non-fungible asset: `ASSET[3]` is not a valid non-fungible faucet id"); /// Error Message: "malformed non-fungible asset: the most significant bit must be 0" pub const ERR_NON_FUNGIBLE_ASSET_FORMAT_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO: MasmError = MasmError::from_static_str("malformed non-fungible asset: the most significant bit must be 0"); -/// Error Message: "failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet" -pub const ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet"); /// Error Message: "failed to access note assets of active note because no note is currently being processed" pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ASSETS_WHILE_NO_NOTE_BEING_PROCESSED: MasmError = MasmError::from_static_str("failed to access note assets of active note because no note is currently being processed"); @@ -159,16 +152,12 @@ pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_RECIPIENT_WHILE_NO_NOTE_BEING_PROCESSE pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED: MasmError = MasmError::from_static_str("failed to access note script root of active note because no note is currently being processed"); /// Error Message: "failed to access note serial number of active note because no note is currently being processed" pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED: MasmError = MasmError::from_static_str("failed to access note serial number of active note because no note is currently being processed"); -/// Error Message: "note data does not match the commitment" -pub const ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT: MasmError = MasmError::from_static_str("note data does not match the commitment"); /// Error Message: "adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807" pub const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED: MasmError = MasmError::from_static_str("adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807"); /// Error Message: "failed to find note at the given index; index must be within [0, num_of_notes]" pub const ERR_NOTE_INVALID_INDEX: MasmError = MasmError::from_static_str("failed to find note at the given index; index must be within [0, num_of_notes]"); /// Error Message: "invalid note type for the given note tag prefix" pub const ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX: MasmError = MasmError::from_static_str("invalid note type for the given note tag prefix"); -/// Error Message: "the specified number of note inputs does not match the actual number" -pub const ERR_NOTE_INVALID_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("the specified number of note inputs does not match the actual number"); /// Error Message: "invalid note type" pub const ERR_NOTE_INVALID_TYPE: MasmError = MasmError::from_static_str("invalid note type"); /// Error Message: "number of assets in a note exceed 255" @@ -209,8 +198,6 @@ pub const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE: MasmE pub const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT: MasmError = MasmError::from_static_str("reserved slot for non-fungible faucet is not a valid empty SMT"); /// Error Message: "failed to authenticate note inclusion in block" pub const ERR_PROLOGUE_NOTE_AUTHENTICATION_FAILED: MasmError = MasmError::from_static_str("failed to authenticate note inclusion in block"); -/// Error Message: "number of note inputs exceeded the maximum limit of 1024" -pub const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 1024"); /// Error Message: "number of input notes exceeds the kernel's maximum limit of 1024" pub const ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT: MasmError = MasmError::from_static_str("number of input notes exceeds the kernel's maximum limit of 1024"); /// Error Message: "number of note assets exceeds the maximum limit of 256" @@ -224,8 +211,6 @@ pub const ERR_PROLOGUE_PROVIDED_INPUT_ASSETS_INFO_DOES_NOT_MATCH_ITS_COMMITMENT: /// Error Message: "verification base fee must fit into a u32" pub const ERR_PROLOGUE_VERIFICATION_BASE_FEE_MUST_BE_U32: MasmError = MasmError::from_static_str("verification base fee must fit into a u32"); -/// Error Message: "failed to approve multisig transaction as it was already executed" -pub const ERR_TX_ALREADY_EXECUTED: MasmError = MasmError::from_static_str("failed to approve multisig transaction as it was already executed"); /// Error Message: "transaction expiration block delta must be within 0x1 and 0xFFFF" pub const ERR_TX_INVALID_EXPIRATION_DELTA: MasmError = MasmError::from_static_str("transaction expiration block delta must be within 0x1 and 0xFFFF"); /// Error Message: "number of output notes in the transaction exceeds the maximum limit of 1024" diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index 92586e58a4..949b696708 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -11,7 +11,7 @@ use miden_objects::vm::Program; use miden_objects::{Felt, Word}; use crate::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; -use crate::account::interface::{AccountComponentInterface, AccountInterface}; +use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::account::wallets::BasicWallet; // WELL KNOWN NOTE SCRIPTS diff --git a/crates/miden-lib/src/testing/account_interface.rs b/crates/miden-lib/src/testing/account_interface.rs index dbac4986eb..d329b15a09 100644 --- a/crates/miden-lib/src/testing/account_interface.rs +++ b/crates/miden-lib/src/testing/account_interface.rs @@ -3,11 +3,11 @@ use alloc::vec::Vec; use miden_objects::Word; use miden_objects::account::Account; -use crate::account::interface::AccountInterface; +use crate::account::interface::{AccountInterface, AccountInterfaceExt}; /// Helper function to extract public keys from an account pub fn get_public_keys_from_account(account: &Account) -> Vec { - let interface: AccountInterface = account.into(); + let interface = AccountInterface::from_account(account); interface .auth() diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index d69d0b485e..56d885ad53 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -4,7 +4,7 @@ use std::collections::BTreeMap; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::errors::tx_kernel_errors::{ +use miden_lib::errors::tx_kernel::{ ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO, ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO, ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index f175f37d3a..dd666fb778 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -1,7 +1,7 @@ use alloc::string::String; use anyhow::Context; -use miden_lib::errors::tx_kernel_errors::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; +use miden_lib::errors::tx_kernel::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; use miden_lib::testing::mock_account::MockAccountExt; use miden_lib::utils::CodeBuilder; use miden_objects::account::Account; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs index 5f784d6ac0..9629f318b5 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs @@ -1,5 +1,5 @@ use assert_matches::assert_matches; -use miden_lib::errors::tx_kernel_errors::{ +use miden_lib::errors::tx_kernel::{ ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED, ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs index b6d0cf449b..64d93e5c0b 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs @@ -1,7 +1,7 @@ use anyhow::Context; use miden_lib::account::wallets::BasicWallet; use miden_lib::errors::MasmError; -use miden_lib::errors::note_script_errors::ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; +use miden_lib::errors::tx_kernel::ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; use miden_lib::testing::account_component::{ConditionalAuthComponent, ERR_WRONG_ARGS_MSG}; use miden_lib::testing::mock_account::MockAccountExt; use miden_lib::utils::CodeBuilder; @@ -91,7 +91,7 @@ async fn test_auth_procedure_called_from_wrong_context() -> anyhow::Result<()> { assert_transaction_executor_error!( execution_result, - ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT + ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT ); Ok(()) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 673555212b..3c009ff167 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -1,7 +1,7 @@ use alloc::string::ToString; use alloc::vec::Vec; -use miden_lib::errors::tx_kernel_errors::{ +use miden_lib::errors::tx_kernel::{ ERR_ACCOUNT_DELTA_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED, ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY, ERR_EPILOGUE_NONCE_CANNOT_BE_0, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index e485ca1d0c..605faec694 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -1,6 +1,6 @@ use alloc::sync::Arc; -use miden_lib::errors::tx_kernel_errors::{ +use miden_lib::errors::tx_kernel::{ ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT, ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED, ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 2037bfac8a..1d4c81020a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -2,7 +2,7 @@ use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; -use miden_lib::errors::tx_kernel_errors::{ +use miden_lib::errors::tx_kernel::{ ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT, ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT, ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index 535531618c..c0abe54d4f 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -2,7 +2,7 @@ use alloc::string::String; use alloc::vec::Vec; use anyhow::Context; -use miden_lib::errors::tx_kernel_errors::{ +use miden_lib::errors::tx_kernel::{ ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS, ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT, }; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index ed27cf835e..024bd98324 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use anyhow::Context; use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::tx_kernel_errors::{ +use miden_lib::errors::tx_kernel::{ ERR_ACCOUNT_SEED_AND_COMMITMENT_DIGEST_MISMATCH, ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY, ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index b184ef1fbb..b881592bfa 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -3,7 +3,7 @@ use alloc::sync::Arc; use anyhow::Context; use assert_matches::assert_matches; use miden_lib::AuthScheme; -use miden_lib::account::interface::AccountInterface; +use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_lib::account::wallets::BasicWallet; use miden_lib::note::create_p2id_note; use miden_lib::testing::account_component::IncrNonceAuthComponent; @@ -529,7 +529,7 @@ async fn tx_summary_commitment_is_signed_by_falcon_auth() -> anyhow::Result<()> ); let summary_commitment = summary.to_commitment(); - let account_interface = AccountInterface::from(&account); + let account_interface = AccountInterface::from_account(&account); let pub_key = match account_interface.auth().first().unwrap() { AuthScheme::RpoFalcon512 { pub_key } => pub_key, AuthScheme::NoAuth => panic!("Expected RpoFalcon512 auth scheme, got NoAuth"), @@ -593,7 +593,7 @@ async fn tx_summary_commitment_is_signed_by_ecdsa_auth() -> anyhow::Result<()> { ); let summary_commitment = summary.to_commitment(); - let account_interface = AccountInterface::from(&account); + let account_interface = AccountInterface::from_account(&account); let pub_key = match account_interface.auth().first().unwrap() { AuthScheme::EcdsaK256Keccak { pub_key } => pub_key, AuthScheme::EcdsaK256KeccakMultisig { .. } => { diff --git a/crates/miden-testing/tests/auth/ecdsa_multisig.rs b/crates/miden-testing/tests/auth/ecdsa_multisig.rs index 58bf8dcd72..2de07ace67 100644 --- a/crates/miden-testing/tests/auth/ecdsa_multisig.rs +++ b/crates/miden-testing/tests/auth/ecdsa_multisig.rs @@ -1,8 +1,8 @@ use miden_lib::account::auth::AuthEcdsaK256KeccakMultisig; use miden_lib::account::components::ecdsa_k256_keccak_multisig_library; -use miden_lib::account::interface::AccountInterface; +use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::tx_kernel_errors::ERR_TX_ALREADY_EXECUTED; +use miden_lib::errors::standards::ERR_TX_ALREADY_EXECUTED; use miden_lib::note::create_p2id_note; use miden_lib::testing::account_interface::get_public_keys_from_account; use miden_lib::utils::CodeBuilder; @@ -1001,7 +1001,7 @@ async fn test_multisig_proc_threshold_overrides() -> anyhow::Result<()> { Default::default(), &mut RpoRandomCoin::new(Word::from([Felt::new(42); 4])), )?; - let multisig_account_interface = AccountInterface::from(&multisig_account); + let multisig_account_interface = AccountInterface::from_account(&multisig_account); let send_note_transaction_script = multisig_account_interface.build_send_notes_script(&[output_note.clone().into()], None)?; diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index 08ba34f17d..67540653ba 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -1,8 +1,8 @@ use miden_lib::account::auth::AuthRpoFalcon512Multisig; use miden_lib::account::components::rpo_falcon_512_multisig_library; -use miden_lib::account::interface::AccountInterface; +use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::tx_kernel_errors::ERR_TX_ALREADY_EXECUTED; +use miden_lib::errors::standards::ERR_TX_ALREADY_EXECUTED; use miden_lib::note::create_p2id_note; use miden_lib::testing::account_interface::get_public_keys_from_account; use miden_lib::utils::CodeBuilder; @@ -991,7 +991,7 @@ async fn test_multisig_proc_threshold_overrides() -> anyhow::Result<()> { Default::default(), &mut RpoRandomCoin::new(Word::from([Felt::new(42); 4])), )?; - let multisig_account_interface = AccountInterface::from(&multisig_account); + let multisig_account_interface = AccountInterface::from_account(&multisig_account); let send_note_transaction_script = multisig_account_interface.build_send_notes_script(&[output_note.clone().into()], None)?; diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index a45dabe03a..b7ffd2c26a 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -4,7 +4,7 @@ use alloc::sync::Arc; use core::slice; use miden_lib::account::faucets::{BasicFungibleFaucet, FungibleFaucetExt, NetworkFungibleFaucet}; -use miden_lib::errors::tx_kernel_errors::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; +use miden_lib::errors::protocol::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; use miden_lib::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; use miden_lib::testing::note::NoteBuilder; use miden_lib::utils::CodeBuilder; diff --git a/crates/miden-testing/tests/scripts/p2id.rs b/crates/miden-testing/tests/scripts/p2id.rs index 0a9aecf3bd..2d3449e8c8 100644 --- a/crates/miden-testing/tests/scripts/p2id.rs +++ b/crates/miden-testing/tests/scripts/p2id.rs @@ -1,4 +1,4 @@ -use miden_lib::errors::note_script_errors::ERR_P2ID_TARGET_ACCT_MISMATCH; +use miden_lib::errors::standards::ERR_P2ID_TARGET_ACCT_MISMATCH; use miden_lib::note::create_p2id_note; use miden_lib::utils::CodeBuilder; use miden_objects::account::Account; diff --git a/crates/miden-testing/tests/scripts/p2ide.rs b/crates/miden-testing/tests/scripts/p2ide.rs index d149ced65f..29c982a02b 100644 --- a/crates/miden-testing/tests/scripts/p2ide.rs +++ b/crates/miden-testing/tests/scripts/p2ide.rs @@ -1,7 +1,7 @@ use core::slice; use anyhow::Context; -use miden_lib::errors::note_script_errors::{ +use miden_lib::errors::standards::{ ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER, ERR_P2IDE_RECLAIM_DISABLED, ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED, diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index a0b2b7fefb..58326232f3 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -1,7 +1,7 @@ use core::slice; use std::collections::BTreeMap; -use miden_lib::account::interface::AccountInterface; +use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_lib::utils::CodeBuilder; use miden_objects::Word; use miden_objects::asset::{Asset, FungibleAsset}; @@ -33,7 +33,7 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { builder.add_existing_wallet_with_assets(Auth::BasicAuth, [FungibleAsset::mock(100)])?; let mock_chain = builder.build()?; - let sender_account_interface = AccountInterface::from(&sender_basic_wallet_account); + let sender_account_interface = AccountInterface::from_account(&sender_basic_wallet_account); let tag = NoteTag::from_account_id(sender_basic_wallet_account.id()); let metadata = NoteMetadata::new( @@ -92,7 +92,8 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { builder.add_existing_basic_faucet(Auth::BasicAuth, "POL", 200, None)?; let mock_chain = builder.build()?; - let sender_account_interface = AccountInterface::from(&sender_basic_fungible_faucet_account); + let sender_account_interface = + AccountInterface::from_account(&sender_basic_fungible_faucet_account); let tag = NoteTag::from_account_id(sender_basic_fungible_faucet_account.id()); let metadata = NoteMetadata::new( diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index d17f09ed68..a3037cd63c 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -81,7 +81,6 @@ Active note procedures can be used to fetch data from the note that is currently | `get_sender` | Returns the sender of the active note.

**Inputs:** `[]`
**Outputs:** `[sender_id_prefix, sender_id_suffix]` | Note | | `get_serial_number` | Returns the [serial number](note.md#serial-number) of the active note.

**Inputs:** `[]`
**Outputs:** `[SERIAL_NUMBER]` | Note | | `get_script_root` | Returns the [script root](note.md#script) of the active note.

**Inputs:** `[]`
**Outputs:** `[SCRIPT_ROOT]` | Note | -| `add_assets_to_account` | Adds all assets from the active note to the account vault.

**Inputs:** `[]`
**Outputs:** `[]` | Note | ## Input Note Procedures (`miden::input_note`) From fb0b6e996ec5c16ea87aa1ebd85644f16540b1a2 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Sat, 20 Dec 2025 15:43:48 +0700 Subject: [PATCH 073/114] chore: move protocol components from `miden-lib` to `miden-objects` (#2191) --- CHANGELOG.md | 2 +- Cargo.lock | 3 + bin/bench-transaction/src/context_setups.rs | 2 +- crates/miden-lib/Cargo.toml | 3 +- crates/miden-lib/build.rs | 895 +++++------------- .../src/account/auth/ecdsa_k256_keccak.rs | 8 +- .../src/account/auth/rpo_falcon_512.rs | 8 +- .../src/account/faucets/basic_fungible.rs | 8 +- .../src/account/faucets/network_fungible.rs | 8 +- crates/miden-lib/src/account/interface/mod.rs | 2 +- .../miden-lib/src/account/interface/test.rs | 2 +- crates/miden-lib/src/account/wallets/mod.rs | 6 +- crates/miden-lib/src/block/mod.rs | 73 -- .../code_builder.rs => code_builder/mod.rs} | 40 +- crates/miden-lib/src/errors/mod.rs | 16 - crates/miden-lib/src/errors/standards.rs | 23 +- .../src/errors/transaction_errors.rs | 26 - crates/miden-lib/src/lib.rs | 83 +- crates/miden-lib/src/standards_lib.rs | 72 ++ .../account_component/conditional_auth.rs | 2 +- .../testing/account_component/incr_nonce.rs | 2 +- .../src/testing/mock_account_code.rs | 2 +- crates/miden-lib/src/testing/mod.rs | 3 +- crates/miden-lib/src/testing/note.rs | 2 +- crates/miden-lib/src/transaction/outputs.rs | 65 -- crates/miden-lib/src/utils/mod.rs | 6 - crates/miden-objects/Cargo.toml | 8 + .../asm/kernels/transaction/api.masm | 0 .../asm/kernels/transaction/lib/account.masm | 0 .../transaction/lib/account_delta.masm | 0 .../asm/kernels/transaction/lib/asset.masm | 0 .../kernels/transaction/lib/asset_vault.masm | 0 .../kernels/transaction/lib/constants.masm | 0 .../asm/kernels/transaction/lib/epilogue.masm | 0 .../asm/kernels/transaction/lib/faucet.masm | 0 .../kernels/transaction/lib/input_note.masm | 0 .../asm/kernels/transaction/lib/link_map.masm | 0 .../asm/kernels/transaction/lib/memory.masm | 0 .../asm/kernels/transaction/lib/note.masm | 0 .../kernels/transaction/lib/output_note.masm | 0 .../asm/kernels/transaction/lib/prologue.masm | 0 .../asm/kernels/transaction/lib/tx.masm | 0 .../asm/kernels/transaction/main.masm | 0 .../kernels/transaction/tx_script_main.masm | 0 .../asm/miden/active_account.masm | 0 .../asm/miden/active_note.masm | 0 .../asm/miden/asset.masm | 0 .../asm/miden/faucet.masm | 0 .../asm/miden/input_note.masm | 0 .../asm/miden/kernel_proc_offsets.masm | 0 .../asm/miden/native_account.masm | 0 .../asm/miden/note.masm | 0 .../asm/miden/output_note.masm | 0 .../asm/miden/tx.masm | 0 .../asm/shared_modules/account_id.masm | 0 .../asm/shared_utils/util/asset.masm | 0 .../asm/shared_utils/util/note.masm | 0 crates/miden-objects/build.rs | 813 ++++++++++++++++ crates/miden-objects/src/account/header.rs | 48 +- .../miden-objects/src/block/proposed_block.rs | 76 +- .../src/errors/masm_error.rs | 4 +- .../src/{errors.rs => errors/mod.rs} | 41 +- .../src/errors/protocol.rs | 8 - .../src/errors/tx_kernel.rs | 0 crates/miden-objects/src/lib.rs | 9 +- crates/miden-objects/src/protocol.rs | 70 ++ .../src/testing/mock_util_lib.rs | 12 +- crates/miden-objects/src/testing/mod.rs | 1 + .../src/transaction/kernel/advice_inputs.rs} | 27 +- .../src/transaction/kernel}/memory.rs | 0 .../src/transaction/kernel}/mod.rs | 68 +- .../src/transaction/kernel/procedures.rs} | 2 +- .../src/transaction/kernel}/tx_event_id.rs | 4 +- crates/miden-objects/src/transaction/mod.rs | 2 + .../miden-objects/src/transaction/outputs.rs | 17 + crates/miden-testing/src/executor.rs | 4 +- .../src/kernel_tests/block/header_errors.rs | 13 +- .../src/kernel_tests/block/utils.rs | 2 +- .../miden-testing/src/kernel_tests/tx/mod.rs | 20 +- .../src/kernel_tests/tx/test_account.rs | 25 +- .../src/kernel_tests/tx/test_account_delta.rs | 2 +- .../kernel_tests/tx/test_account_interface.rs | 3 +- .../src/kernel_tests/tx/test_active_note.rs | 4 +- .../src/kernel_tests/tx/test_asset_vault.rs | 8 +- .../src/kernel_tests/tx/test_auth.rs | 6 +- .../src/kernel_tests/tx/test_epilogue.rs | 35 +- .../src/kernel_tests/tx/test_faucet.rs | 18 +- .../src/kernel_tests/tx/test_fpi.rs | 36 +- .../src/kernel_tests/tx/test_input_note.rs | 2 +- .../src/kernel_tests/tx/test_lazy_loading.rs | 2 +- .../src/kernel_tests/tx/test_note.rs | 6 +- .../src/kernel_tests/tx/test_output_note.rs | 26 +- .../src/kernel_tests/tx/test_prologue.rs | 51 +- .../src/kernel_tests/tx/test_tx.rs | 4 +- crates/miden-testing/src/mock_chain/chain.rs | 3 +- .../src/mock_chain/chain_builder.rs | 3 +- crates/miden-testing/src/mock_host.rs | 6 +- .../miden-testing/src/tx_context/builder.rs | 2 +- .../miden-testing/src/tx_context/context.rs | 4 +- crates/miden-testing/src/utils.rs | 2 +- crates/miden-testing/tests/auth/ecdsa_acl.rs | 2 +- .../tests/auth/ecdsa_multisig.rs | 2 +- crates/miden-testing/tests/auth/multisig.rs | 2 +- .../tests/auth/rpo_falcon_acl.rs | 2 +- crates/miden-testing/tests/lib.rs | 2 +- crates/miden-testing/tests/scripts/faucet.rs | 4 +- crates/miden-testing/tests/scripts/p2id.rs | 2 +- .../miden-testing/tests/scripts/send_note.rs | 2 +- crates/miden-testing/tests/scripts/swap.rs | 2 +- crates/miden-tx/src/auth/tx_authenticator.rs | 2 +- crates/miden-tx/src/executor/exec_host.rs | 9 +- crates/miden-tx/src/executor/mod.rs | 2 +- crates/miden-tx/src/executor/notes_checker.rs | 9 +- crates/miden-tx/src/host/kernel_process.rs | 6 +- crates/miden-tx/src/host/mod.rs | 4 +- crates/miden-tx/src/host/tx_event.rs | 4 +- crates/miden-tx/src/prover/mast_store.rs | 16 +- crates/miden-tx/src/prover/mod.rs | 2 +- crates/miden-tx/src/verifier/mod.rs | 5 +- 119 files changed, 1713 insertions(+), 1225 deletions(-) delete mode 100644 crates/miden-lib/src/block/mod.rs rename crates/miden-lib/src/{utils/code_builder.rs => code_builder/mod.rs} (95%) delete mode 100644 crates/miden-lib/src/errors/transaction_errors.rs create mode 100644 crates/miden-lib/src/standards_lib.rs delete mode 100644 crates/miden-lib/src/transaction/outputs.rs delete mode 100644 crates/miden-lib/src/utils/mod.rs rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/api.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/account.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/account_delta.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/asset.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/asset_vault.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/constants.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/epilogue.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/faucet.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/input_note.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/link_map.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/memory.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/note.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/output_note.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/prologue.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/lib/tx.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/main.masm (100%) rename crates/{miden-lib => miden-objects}/asm/kernels/transaction/tx_script_main.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/active_account.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/active_note.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/asset.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/faucet.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/input_note.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/kernel_proc_offsets.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/native_account.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/note.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/output_note.masm (100%) rename crates/{miden-lib => miden-objects}/asm/miden/tx.masm (100%) rename crates/{miden-lib => miden-objects}/asm/shared_modules/account_id.masm (100%) rename crates/{miden-lib => miden-objects}/asm/shared_utils/util/asset.masm (100%) rename crates/{miden-lib => miden-objects}/asm/shared_utils/util/note.masm (100%) create mode 100644 crates/miden-objects/build.rs rename crates/{miden-lib => miden-objects}/src/errors/masm_error.rs (90%) rename crates/miden-objects/src/{errors.rs => errors/mod.rs} (96%) rename crates/{miden-lib => miden-objects}/src/errors/protocol.rs (82%) rename crates/{miden-lib => miden-objects}/src/errors/tx_kernel.rs (100%) create mode 100644 crates/miden-objects/src/protocol.rs rename crates/{miden-lib => miden-objects}/src/testing/mock_util_lib.rs (80%) rename crates/{miden-lib/src/transaction/inputs.rs => miden-objects/src/transaction/kernel/advice_inputs.rs} (96%) rename crates/{miden-lib/src/transaction => miden-objects/src/transaction/kernel}/memory.rs (100%) rename crates/{miden-lib/src/transaction => miden-objects/src/transaction/kernel}/mod.rs (91%) rename crates/{miden-lib/src/transaction/kernel_procedures.rs => miden-objects/src/transaction/kernel/procedures.rs} (99%) rename crates/{miden-lib/src/transaction => miden-objects/src/transaction/kernel}/tx_event_id.rs (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f790fdec..09628cb568 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). -- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184)). +- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191)). - [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). diff --git a/Cargo.lock b/Cargo.lock index 5f77a0ea10..a92450807e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1636,6 +1636,7 @@ dependencies = [ "bech32", "color-eyre", "criterion 0.5.1", + "fs-err", "getrandom 0.3.4", "miden-air", "miden-assembly", @@ -1653,12 +1654,14 @@ dependencies = [ "rand", "rand_chacha", "rand_xoshiro", + "regex", "rstest", "semver 1.0.27", "serde", "tempfile", "thiserror", "toml", + "walkdir", "winter-air", "winter-rand-utils", ] diff --git a/bin/bench-transaction/src/context_setups.rs b/bin/bench-transaction/src/context_setups.rs index 625aab5473..90f91a22f0 100644 --- a/bin/bench-transaction/src/context_setups.rs +++ b/bin/bench-transaction/src/context_setups.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use miden_lib::utils::CodeBuilder; +use miden_lib::code_builder::CodeBuilder; use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::note::NoteType; use miden_objects::testing::account_id::ACCOUNT_ID_SENDER; diff --git a/crates/miden-lib/Cargo.toml b/crates/miden-lib/Cargo.toml index f82bb251aa..623c5745bc 100644 --- a/crates/miden-lib/Cargo.toml +++ b/crates/miden-lib/Cargo.toml @@ -21,8 +21,6 @@ testing = ["dep:rand", "miden-assembly/testing", "miden-objects/testing"] [dependencies] # Miden dependencies -miden-core = { workspace = true } -miden-core-lib = { workspace = true } miden-objects = { workspace = true } miden-processor = { workspace = true } @@ -35,6 +33,7 @@ fs-err = { version = "3" } miden-assembly = { workspace = true } miden-core = { workspace = true } miden-core-lib = { workspace = true } +miden-objects = { workspace = true } regex = { version = "1.11" } walkdir = { version = "2.5" } diff --git a/crates/miden-lib/build.rs b/crates/miden-lib/build.rs index a1a599f594..a73b4e2eaa 100644 --- a/crates/miden-lib/build.rs +++ b/crates/miden-lib/build.rs @@ -1,16 +1,11 @@ -use std::collections::{BTreeMap, BTreeSet}; use std::env; -use std::fmt::Write; -use std::io::{self}; -use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::path::Path; use fs_err as fs; -use miden_assembly::diagnostics::{IntoDiagnostic, NamedSource, Result, WrapErr, miette}; +use miden_assembly::diagnostics::{IntoDiagnostic, NamedSource, Result, WrapErr}; use miden_assembly::utils::Serializable; -use miden_assembly::{Assembler, DefaultSourceManager, KernelLibrary, Library, Report}; -use regex::Regex; -use walkdir::WalkDir; +use miden_assembly::{Assembler, Library, Report}; +use miden_objects::transaction::TransactionKernel; // CONSTANTS // ================================================================================================ @@ -25,36 +20,11 @@ const ASM_DIR: &str = "asm"; const ASM_MIDEN_DIR: &str = "miden"; const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts"; const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components"; -const SHARED_UTILS_DIR: &str = "shared_utils"; -const SHARED_MODULES_DIR: &str = "shared_modules"; -const ASM_TX_KERNEL_DIR: &str = "kernels/transaction"; -const KERNEL_PROCEDURES_RS_FILE: &str = "src/transaction/kernel_procedures.rs"; -const TX_KERNEL_ERRORS_FILE: &str = "src/errors/tx_kernel.rs"; -const PROTOCOL_LIB_ERRORS_FILE: &str = "src/errors/protocol.rs"; const STANDARDS_ERRORS_FILE: &str = "src/errors/standards.rs"; -const TX_KERNEL_ERRORS_ARRAY_NAME: &str = "TX_KERNEL_ERRORS"; -const PROTOCOL_LIB_ERRORS_ARRAY_NAME: &str = "PROTOCOL_LIB_ERRORS"; const STANDARDS_ERRORS_ARRAY_NAME: &str = "STANDARDS_ERRORS"; -const TX_KERNEL_ERROR_CATEGORIES: [&str; 14] = [ - "KERNEL", - "PROLOGUE", - "EPILOGUE", - "TX", - "NOTE", - "ACCOUNT", - "FOREIGN_ACCOUNT", - "FAUCET", - "FUNGIBLE_ASSET", - "NON_FUNGIBLE_ASSET", - "VAULT", - "LINK_MAP", - "INPUT_NOTE", - "OUTPUT_NOTE", -]; - // PRE-PROCESSING // ================================================================================================ @@ -72,24 +42,20 @@ fn main() -> Result<()> { let build_dir = env::var("OUT_DIR").unwrap(); let src = Path::new(&crate_dir).join(ASM_DIR); let dst = Path::new(&build_dir).to_path_buf(); - copy_directory(src, &dst)?; + shared::copy_directory(src, &dst, ASM_DIR)?; // set source directory to {OUT_DIR}/asm let source_dir = dst.join(ASM_DIR); - // copy the shared modules to the kernel and miden library folders - copy_shared_modules(&source_dir)?; - // set target directory to {OUT_DIR}/assets let target_dir = Path::new(&build_dir).join(ASSETS_DIR); - // compile transaction kernel - let mut assembler = - compile_tx_kernel(&source_dir.join(ASM_TX_KERNEL_DIR), &target_dir.join("kernels"))?; + // compile standards library + let standards_lib = + compile_standards_lib(&source_dir, &target_dir, TransactionKernel::assembler())?; - // compile miden library - let miden_lib = compile_miden_lib(&source_dir, &target_dir, assembler.clone())?; - assembler.link_dynamic_library(miden_lib)?; + let mut assembler = TransactionKernel::assembler(); + assembler.link_static_library(standards_lib)?; // compile note scripts compile_note_scripts( @@ -107,202 +73,27 @@ fn main() -> Result<()> { generate_error_constants(&source_dir)?; - generate_event_constants(&source_dir, &target_dir)?; Ok(()) } -// COMPILE TRANSACTION KERNEL -// ================================================================================================ - -/// Reads the transaction kernel MASM source from the `source_dir`, compiles it, saves the results -/// to the `target_dir`, and returns an [Assembler] instantiated with the compiled kernel. -/// -/// Additionally it compiles the transaction script executor program, see the -/// [compile_tx_script_main] procedure for details. -/// -/// `source_dir` is expected to have the following structure: -/// -/// - {source_dir}/api.masm -> defines exported procedures from the transaction kernel. -/// - {source_dir}/main.masm -> defines the executable program of the transaction kernel. -/// - {source_dir}/tx_script_main -> defines the executable program of the arbitrary transaction -/// script. -/// - {source_dir}/lib -> contains common modules used by both api.masm and main.masm. -/// -/// The compiled files are written as follows: -/// -/// - {target_dir}/tx_kernel.masl -> contains kernel library compiled from api.masm. -/// - {target_dir}/tx_kernel.masb -> contains the executable compiled from main.masm. -/// - {target_dir}/tx_script_main.masb -> contains the executable compiled from -/// tx_script_main.masm. -/// - src/transaction/procedures/kernel_v0.rs -> contains the kernel procedures table. -fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result { - let shared_utils_path = std::path::Path::new(ASM_DIR).join(SHARED_UTILS_DIR); - let kernel_path = miden_assembly::Path::kernel_path(); - - let mut assembler = build_assembler(None)?; - // add the shared util modules to the kernel lib under the ::$kernel::util namespace - assembler.compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; - - // assemble the kernel library and write it to the "tx_kernel.masl" file - let kernel_lib = assembler - .assemble_kernel_from_dir(source_dir.join("api.masm"), Some(source_dir.join("lib")))?; - - // generate kernel `procedures.rs` file - generate_kernel_proc_hash_file(kernel_lib.clone())?; - - let output_file = target_dir.join("tx_kernel").with_extension(Library::LIBRARY_EXTENSION); - kernel_lib.write_to_file(output_file).into_diagnostic()?; - - let assembler = build_assembler(Some(kernel_lib))?; - - // assemble the kernel program and write it to the "tx_kernel.masb" file - let mut main_assembler = assembler.clone(); - // add the shared util modules to the kernel lib under the ::$kernel::util namespace - main_assembler.compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; - main_assembler.compile_and_statically_link_from_dir(source_dir.join("lib"), kernel_path)?; - - let main_file_path = source_dir.join("main.masm"); - let kernel_main = main_assembler.clone().assemble_program(main_file_path)?; - - let masb_file_path = target_dir.join("tx_kernel.masb"); - kernel_main.write_to_file(masb_file_path).into_diagnostic()?; - - // compile the transaction script main program - compile_tx_script_main(source_dir, target_dir, main_assembler)?; - - #[cfg(any(feature = "testing", test))] - { - let mut kernel_lib_assembler = assembler.clone(); - // Build kernel as a library and save it to file. - // This is needed in test assemblers to access individual procedures which would otherwise - // be hidden when using KernelLibrary (api.masm) - - // add the shared util modules to the kernel lib under the ::$kernel::util namespace - kernel_lib_assembler - .compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; - - let test_lib = kernel_lib_assembler - .assemble_library_from_dir(source_dir.join("lib"), kernel_path) - .unwrap(); - - let masb_file_path = - target_dir.join("kernel_library").with_extension(Library::LIBRARY_EXTENSION); - test_lib.write_to_file(masb_file_path).into_diagnostic()?; - } - - Ok(assembler) -} - -/// Reads the transaction script executor MASM source from the `source_dir/tx_script_main.masm`, -/// compiles it and saves the results to the `target_dir` as a `tx_script_main.masb` binary file. -fn compile_tx_script_main( - source_dir: &Path, - target_dir: &Path, - main_assembler: Assembler, -) -> Result<()> { - // assemble the transaction script executor program and write it to the "tx_script_main.masb" - // file. - let tx_script_main_file_path = source_dir.join("tx_script_main.masm"); - let tx_script_main = main_assembler.assemble_program(tx_script_main_file_path)?; - - let masb_file_path = target_dir.join("tx_script_main.masb"); - tx_script_main.write_to_file(masb_file_path).into_diagnostic() -} - -/// Generates kernel `procedures.rs` file based on the kernel library -fn generate_kernel_proc_hash_file(kernel: KernelLibrary) -> Result<()> { - // Because the kernel Rust file will be stored under ./src, this should be a no-op if we can't - // write there - if !BUILD_GENERATED_FILES_IN_SRC { - return Ok(()); - } - - let (_, module_info, _) = kernel.into_parts(); - - let to_exclude = BTreeSet::from_iter(["exec_kernel_proc"]); - let offsets_filename = Path::new(ASM_DIR).join(ASM_MIDEN_DIR).join("kernel_proc_offsets.masm"); - let offsets = parse_proc_offsets(&offsets_filename)?; - - let generated_procs: BTreeMap = module_info - .procedures() - .filter(|(_, proc_info)| !to_exclude.contains::(proc_info.name.as_ref())) - .map(|(_, proc_info)| { - let name = proc_info.name.to_string(); - - let Some(&offset) = offsets.get(&name) else { - panic!("Offset constant for function `{name}` not found in `{offsets_filename:?}`"); - }; - - (offset, format!(" // {name}\n word!(\"{}\"),", proc_info.digest)) - }) - .collect(); - - let proc_count = generated_procs.len(); - let generated_procs: String = generated_procs.into_iter().enumerate().map(|(index, (offset, txt))| { - if index != offset { - panic!("Offset constants in the file `{offsets_filename:?}` are not contiguous (missing offset: {index})"); - } - - txt - }).collect::>().join("\n"); - - fs::write( - KERNEL_PROCEDURES_RS_FILE, - format!( - r#"// This file is generated by build.rs, do not modify - -use miden_objects::{{Word, word}}; - -// KERNEL PROCEDURES -// ================================================================================================ - -/// Hashes of all dynamically executed kernel procedures. -pub const KERNEL_PROCEDURES: [Word; {proc_count}] = [ -{generated_procs} -]; -"#, - ), - ) - .into_diagnostic() -} - -fn parse_proc_offsets(filename: impl AsRef) -> Result> { - let regex: Regex = Regex::new(r"^const\s*(?P\w+)_OFFSET\s*=\s*(?P\d+)").unwrap(); - let mut result = BTreeMap::new(); - for line in fs::read_to_string(filename).into_diagnostic()?.lines() { - if let Some(captures) = regex.captures(line) { - result.insert( - captures["name"].to_string().to_lowercase(), - captures["offset"].parse().into_diagnostic()?, - ); - } - } - - Ok(result) -} - -// COMPILE MIDEN LIB +// COMPILE PROTOCOL LIB // ================================================================================================ /// Reads the MASM files from "{source_dir}/miden" directory, compiles them into a Miden assembly /// library, saves the library into "{target_dir}/miden.masl", and returns the compiled library. -fn compile_miden_lib( +fn compile_standards_lib( source_dir: &Path, target_dir: &Path, - mut assembler: Assembler, + assembler: Assembler, ) -> Result { let source_dir = source_dir.join(ASM_MIDEN_DIR); - let shared_path = Path::new(ASM_DIR).join(SHARED_UTILS_DIR); - - // add the shared modules to the kernel lib under the miden::util namespace - assembler.compile_and_statically_link_from_dir(&shared_path, "miden")?; - let miden_lib = assembler.assemble_library_from_dir(source_dir, "miden")?; + let standards_lib = assembler.assemble_library_from_dir(source_dir, "miden")?; - let output_file = target_dir.join("miden").with_extension(Library::LIBRARY_EXTENSION); - miden_lib.write_to_file(output_file).into_diagnostic()?; + let output_file = target_dir.join("standards").with_extension(Library::LIBRARY_EXTENSION); + standards_lib.write_to_file(output_file).into_diagnostic()?; - Ok(miden_lib) + Ok(standards_lib) } // COMPILE EXECUTABLE MODULES @@ -317,7 +108,7 @@ fn compile_note_scripts(source_dir: &Path, target_dir: &Path, assembler: Assembl .into_diagnostic() .wrap_err("failed to create note_scripts directory")?; - for masm_file_path in get_masm_files(source_dir).unwrap() { + for masm_file_path in shared::get_masm_files(source_dir).unwrap() { // read the MASM file, parse it, and serialize the parsed AST to bytes let code = assembler.clone().assemble_program(masm_file_path.clone())?; @@ -351,7 +142,7 @@ fn compile_account_components( fs::create_dir_all(target_dir).unwrap(); } - for masm_file_path in get_masm_files(source_dir).unwrap() { + for masm_file_path in shared::get_masm_files(source_dir).unwrap() { let component_name = masm_file_path .file_stem() .expect("masm file should have a file stem") @@ -377,126 +168,6 @@ fn compile_account_components( Ok(()) } -// HELPER FUNCTIONS -// ================================================================================================ - -/// Returns a new [Assembler] loaded with miden-core-lib and the specified kernel, if provided. -fn build_assembler(kernel: Option) -> Result { - kernel - .map(|kernel| Assembler::with_kernel(Arc::new(DefaultSourceManager::default()), kernel)) - .unwrap_or_default() - .with_dynamic_library(miden_core_lib::CoreLibrary::default()) -} - -/// Recursively copies `src` into `dst`. -/// -/// This function will overwrite the existing files if re-executed. -fn copy_directory, R: AsRef>(src: T, dst: R) -> Result<()> { - let mut prefix = src.as_ref().canonicalize().unwrap(); - // keep all the files inside the `asm` folder - prefix.pop(); - - let target_dir = dst.as_ref().join(ASM_DIR); - if target_dir.exists() { - // Clear existing asm files that were copied earlier which may no longer exist. - fs::remove_dir_all(&target_dir) - .into_diagnostic() - .wrap_err("failed to remove ASM directory")?; - } - - // Recreate the directory structure. - fs::create_dir_all(&target_dir) - .into_diagnostic() - .wrap_err("failed to create ASM directory")?; - - let dst = dst.as_ref(); - let mut todo = vec![src.as_ref().to_path_buf()]; - - while let Some(goal) = todo.pop() { - for entry in fs::read_dir(goal).unwrap() { - let path = entry.unwrap().path(); - if path.is_dir() { - let src_dir = path.canonicalize().unwrap(); - let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap()); - if !dst_dir.exists() { - fs::create_dir_all(&dst_dir).unwrap(); - } - todo.push(src_dir); - } else { - let dst_file = dst.join(path.strip_prefix(&prefix).unwrap()); - fs::copy(&path, dst_file).unwrap(); - } - } - } - - Ok(()) -} - -/// Copies the content of the build `shared_modules` folder to the `lib` and `miden` build folders. -/// This is required to include the shared modules as APIs of the `kernel` and `miden` libraries. -/// -/// This is done to make it possible to import the modules in the `shared_modules` folder directly, -/// i.e. "use $kernel::account_id". -fn copy_shared_modules>(source_dir: T) -> Result<()> { - // source is expected to be an `OUT_DIR/asm` folder - let shared_modules_dir = source_dir.as_ref().join(SHARED_MODULES_DIR); - - for module_path in get_masm_files(shared_modules_dir).unwrap() { - let module_name = module_path.file_name().unwrap(); - - // copy to kernel lib - let kernel_lib_folder = source_dir.as_ref().join(ASM_TX_KERNEL_DIR).join("lib"); - fs::copy(&module_path, kernel_lib_folder.join(module_name)).into_diagnostic()?; - - // copy to miden lib - let miden_lib_folder = source_dir.as_ref().join(ASM_MIDEN_DIR); - fs::copy(&module_path, miden_lib_folder.join(module_name)).into_diagnostic()?; - } - - Ok(()) -} - -/// Returns a vector with paths to all MASM files in the specified directory. -/// -/// All non-MASM files are skipped. -fn get_masm_files>(dir_path: P) -> Result> { - let mut files = Vec::new(); - - let path = dir_path.as_ref(); - if path.is_dir() { - let entries = fs::read_dir(path) - .into_diagnostic() - .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; - for entry in entries { - let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; - let file_path = file.path(); - if is_masm_file(&file_path).into_diagnostic()? { - files.push(file_path); - } - } - } else { - println!("cargo:warn=The specified path is not a directory."); - } - - Ok(files) -} - -/// Returns true if the provided path resolves to a file with `.masm` extension. -/// -/// # Errors -/// Returns an error if the path could not be converted to a UTF-8 string. -fn is_masm_file(path: &Path) -> io::Result { - if let Some(extension) = path.extension() { - let extension = extension - .to_str() - .ok_or_else(|| io::Error::other("invalid UTF-8 filename"))? - .to_lowercase(); - Ok(extension == "masm") - } else { - Ok(false) - } -} - // ERROR CONSTANTS FILE GENERATION // ================================================================================================ @@ -530,52 +201,16 @@ fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { return Ok(()); } - // Transaction kernel errors - // ------------------------------------------ - - let tx_kernel_dir = asm_source_dir.join(ASM_TX_KERNEL_DIR); - let errors = - extract_all_masm_errors(&tx_kernel_dir).context("failed to extract all masm errors")?; - validate_tx_kernel_category(&errors)?; - - generate_error_file( - ErrorModule { - file_name: TX_KERNEL_ERRORS_FILE, - array_name: TX_KERNEL_ERRORS_ARRAY_NAME, - }, - errors, - )?; - - // Miden protocol library errors - // ------------------------------------------ - - let miden_dir = asm_source_dir.join(ASM_MIDEN_DIR); - let errors = - extract_all_masm_errors(&miden_dir).context("failed to extract all masm errors")?; - - generate_error_file( - ErrorModule { - file_name: PROTOCOL_LIB_ERRORS_FILE, - array_name: PROTOCOL_LIB_ERRORS_ARRAY_NAME, - }, - errors, - )?; - // Miden standards errors // ------------------------------------------ - let account_components_dir = asm_source_dir.join(ASM_ACCOUNT_COMPONENTS_DIR); - let note_scripts_dir = asm_source_dir.join(ASM_NOTE_SCRIPTS_DIR); - let mut errors = extract_all_masm_errors(&account_components_dir) + let errors = shared::extract_all_masm_errors(asm_source_dir) .context("failed to extract all masm errors")?; - errors.extend( - extract_all_masm_errors(¬e_scripts_dir).context("failed to extract all masm errors")?, - ); - - generate_error_file( - ErrorModule { + shared::generate_error_file( + shared::ErrorModule { file_name: STANDARDS_ERRORS_FILE, array_name: STANDARDS_ERRORS_ARRAY_NAME, + is_crate_local: false, }, errors, )?; @@ -583,305 +218,267 @@ fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { Ok(()) } -/// Extract all masm errors from the given path and returns a map by error category. -fn extract_all_masm_errors(asm_source_dir: &Path) -> Result> { - // We use a BTree here to order the errors by their categories which is the first part after the - // ERR_ prefix and to allow for the same error to be defined multiple times in different files - // (as long as the constant name and error messages match). - let mut errors = BTreeMap::new(); - - // Walk all files of the kernel source directory. - for entry in WalkDir::new(asm_source_dir) { - let entry = entry.into_diagnostic()?; - if !is_masm_file(entry.path()).into_diagnostic()? { - continue; +/// This module should be kept in sync with the copy in miden-objects's build.rs. +mod shared { + use std::collections::BTreeMap; + use std::fmt::Write; + use std::io::{self}; + use std::path::{Path, PathBuf}; + + use fs_err as fs; + use miden_assembly::Report; + use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr}; + use regex::Regex; + use walkdir::WalkDir; + + /// Recursively copies `src` into `dst`. + /// + /// This function will overwrite the existing files if re-executed. + pub fn copy_directory, R: AsRef>( + src: T, + dst: R, + asm_dir: &str, + ) -> Result<()> { + let mut prefix = src.as_ref().canonicalize().unwrap(); + // keep all the files inside the `asm` folder + prefix.pop(); + + let target_dir = dst.as_ref().join(asm_dir); + if target_dir.exists() { + // Clear existing asm files that were copied earlier which may no longer exist. + fs::remove_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to remove ASM directory")?; } - let file_contents = std::fs::read_to_string(entry.path()).into_diagnostic()?; - extract_masm_errors(&mut errors, &file_contents)?; - } - let errors = errors - .into_iter() - .map(|(error_name, error)| NamedError { name: error_name, message: error.message }) - .collect(); + // Recreate the directory structure. + fs::create_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to create ASM directory")?; + + let dst = dst.as_ref(); + let mut todo = vec![src.as_ref().to_path_buf()]; + + while let Some(goal) = todo.pop() { + for entry in fs::read_dir(goal).unwrap() { + let path = entry.unwrap().path(); + if path.is_dir() { + let src_dir = path.canonicalize().unwrap(); + let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap()); + if !dst_dir.exists() { + fs::create_dir_all(&dst_dir).unwrap(); + } + todo.push(src_dir); + } else { + let dst_file = dst.join(path.strip_prefix(&prefix).unwrap()); + fs::copy(&path, dst_file).unwrap(); + } + } + } - Ok(errors) -} + Ok(()) + } -/// Extracts the errors from a single masm file and inserts them into the provided map. -fn extract_masm_errors( - errors: &mut BTreeMap, - file_contents: &str, -) -> Result<()> { - let regex = Regex::new(r#"const\s*ERR_(?.*)\s*=\s*"(?.*)""#).unwrap(); - - for capture in regex.captures_iter(file_contents) { - let error_name = capture - .name("name") - .expect("error name should be captured") - .as_str() - .trim() - .to_owned(); - let error_message = capture - .name("message") - .expect("error code should be captured") - .as_str() - .trim() - .to_owned(); + /// Returns a vector with paths to all MASM files in the specified directory. + /// + /// All non-MASM files are skipped. + pub fn get_masm_files>(dir_path: P) -> Result> { + let mut files = Vec::new(); + + let path = dir_path.as_ref(); + if path.is_dir() { + let entries = fs::read_dir(path) + .into_diagnostic() + .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; + for entry in entries { + let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; + let file_path = file.path(); + if is_masm_file(&file_path).into_diagnostic()? { + files.push(file_path); + } + } + } else { + println!("cargo:warn=The specified path is not a directory."); + } - if let Some(ExtractedError { message: existing_error_message, .. }) = - errors.get(&error_name) - && existing_error_message != &error_message - { - return Err(Report::msg(format!( - "Transaction kernel error constant ERR_{error_name} is already defined elsewhere but its error message is different" - ))); + Ok(files) + } + + /// Returns true if the provided path resolves to a file with `.masm` extension. + /// + /// # Errors + /// Returns an error if the path could not be converted to a UTF-8 string. + pub fn is_masm_file(path: &Path) -> io::Result { + if let Some(extension) = path.extension() { + let extension = extension + .to_str() + .ok_or_else(|| io::Error::other("invalid UTF-8 filename"))? + .to_lowercase(); + Ok(extension == "masm") + } else { + Ok(false) } + } - // Enforce the "no trailing punctuation" rule from the Rust error guidelines on MASM errors. - if error_message.ends_with(".") { - return Err(Report::msg(format!( - "Error messages should not end with a period: `ERR_{error_name}: {error_message}`" - ))); + /// Extract all masm errors from the given path and returns a map by error category. + pub fn extract_all_masm_errors(asm_source_dir: &Path) -> Result> { + // We use a BTree here to order the errors by their categories which is the first part after + // the ERR_ prefix and to allow for the same error to be defined multiple times in + // different files (as long as the constant name and error messages match). + let mut errors = BTreeMap::new(); + + // Walk all files of the kernel source directory. + for entry in WalkDir::new(asm_source_dir) { + let entry = entry.into_diagnostic()?; + if !is_masm_file(entry.path()).into_diagnostic()? { + continue; + } + let file_contents = std::fs::read_to_string(entry.path()).into_diagnostic()?; + extract_masm_errors(&mut errors, &file_contents)?; } - errors.insert(error_name, ExtractedError { message: error_message }); + let errors = errors + .into_iter() + .map(|(error_name, error)| NamedError { name: error_name, message: error.message }) + .collect(); + + Ok(errors) } - Ok(()) -} + /// Extracts the errors from a single masm file and inserts them into the provided map. + pub fn extract_masm_errors( + errors: &mut BTreeMap, + file_contents: &str, + ) -> Result<()> { + let regex = Regex::new(r#"const\s*ERR_(?.*)\s*=\s*"(?.*)""#).unwrap(); + + for capture in regex.captures_iter(file_contents) { + let error_name = capture + .name("name") + .expect("error name should be captured") + .as_str() + .trim() + .to_owned(); + let error_message = capture + .name("message") + .expect("error code should be captured") + .as_str() + .trim() + .to_owned(); + + if let Some(ExtractedError { message: existing_error_message, .. }) = + errors.get(&error_name) + && existing_error_message != &error_message + { + return Err(Report::msg(format!( + "Transaction kernel error constant ERR_{error_name} is already defined elsewhere but its error message is different" + ))); + } -fn is_new_error_category<'a>(last_error: &mut Option<&'a str>, current_error: &'a str) -> bool { - let is_new = match last_error { - Some(last_err) => { - let last_category = - last_err.split("_").next().expect("there should be at least one entry"); - let new_category = - current_error.split("_").next().expect("there should be at least one entry"); - last_category != new_category - }, - None => false, - }; + // Enforce the "no trailing punctuation" rule from the Rust error guidelines on MASM + // errors. + if error_message.ends_with(".") { + return Err(Report::msg(format!( + "Error messages should not end with a period: `ERR_{error_name}: {error_message}`" + ))); + } - last_error.replace(current_error); + errors.insert(error_name, ExtractedError { message: error_message }); + } - is_new -} + Ok(()) + } -/// Generates the content of an error file for the given category and the set of errors and writes -/// it to the category's file. -fn generate_error_file(category: ErrorModule, errors: Vec) -> Result<()> { - let mut output = String::new(); + pub fn is_new_error_category<'a>( + last_error: &mut Option<&'a str>, + current_error: &'a str, + ) -> bool { + let is_new = match last_error { + Some(last_err) => { + let last_category = + last_err.split("_").next().expect("there should be at least one entry"); + let new_category = + current_error.split("_").next().expect("there should be at least one entry"); + last_category != new_category + }, + None => false, + }; + + last_error.replace(current_error); + + is_new + } - writeln!(output, "use crate::errors::MasmError;\n").unwrap(); + /// Generates the content of an error file for the given category and the set of errors and + /// writes it to the category's file. + pub fn generate_error_file(module: ErrorModule, errors: Vec) -> Result<()> { + let mut output = String::new(); - writeln!( - output, - "// This file is generated by build.rs, do not modify manually. + if module.is_crate_local { + writeln!(output, "use crate::errors::MasmError;\n").unwrap(); + } else { + writeln!(output, "use miden_objects::errors::MasmError;\n").unwrap(); + } + + writeln!( + output, + "// This file is generated by build.rs, do not modify manually. // It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. // // To add a new error, define a constant in MASM of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). " - ) - .unwrap(); - - writeln!( - output, - "// {} -// ================================================================================================ -", - category.array_name.replace("_", " ") - ) - .unwrap(); - - let mut last_error = None; - for named_error in errors.iter() { - let NamedError { name, message } = named_error; - - // Group errors into blocks separate by newlines. - if is_new_error_category(&mut last_error, name) { - writeln!(output).into_diagnostic()?; - } + ) + .unwrap(); - writeln!(output, "/// Error Message: \"{message}\"").into_diagnostic()?; writeln!( output, - r#"pub const ERR_{name}: MasmError = MasmError::from_static_str("{message}");"# + "// {} +// ================================================================================================ +", + module.array_name.replace("_", " ") ) - .into_diagnostic()?; - } - - std::fs::write(category.file_name, output).into_diagnostic()?; - - Ok(()) -} + .unwrap(); -type ErrorName = String; + let mut last_error = None; + for named_error in errors.iter() { + let NamedError { name, message } = named_error; -#[derive(Debug, Clone)] -struct ExtractedError { - message: String, -} - -#[derive(Debug, Clone)] -struct NamedError { - name: ErrorName, - message: String, -} - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct ErrorModule { - pub file_name: &'static str, - pub array_name: &'static str, -} + // Group errors into blocks separate by newlines. + if is_new_error_category(&mut last_error, name) { + writeln!(output).into_diagnostic()?; + } -/// Validates that all error names in the provided slice start with a known tx kernel error -/// category. -fn validate_tx_kernel_category(errors: &[NamedError]) -> Result<()> { - for error in errors { - if !TX_KERNEL_ERROR_CATEGORIES - .iter() - .any(|known_category| error.name.starts_with(known_category)) - { - return Err(miette::miette!( - "error `{}` does not start with a known tx kernel error category", - error.name - )); + writeln!(output, "/// Error Message: \"{message}\"").into_diagnostic()?; + writeln!( + output, + r#"pub const ERR_{name}: MasmError = MasmError::from_static_str("{message}");"# + ) + .into_diagnostic()?; } - } - - Ok(()) -} - -// EVENT CONSTANTS FILE GENERATION -// ================================================================================================ -/// Reads all MASM files from the `asm_source_dir` and extracts event definitions, -/// then generates the transaction_events.rs file with constants. -fn generate_event_constants(asm_source_dir: &Path, target_dir: &Path) -> Result<()> { - // Extract all event definitions from MASM files - let events = extract_all_event_definitions(asm_source_dir)?; + std::fs::write(module.file_name, output).into_diagnostic()?; - // Generate the events file in OUT_DIR - let event_file_content = generate_event_file_content(&events).into_diagnostic()?; - let event_file_path = target_dir.join("transaction_events.rs"); - fs::write(event_file_path, event_file_content).into_diagnostic()?; - - Ok(()) -} - -/// Extract all `const X=event("x")` definitions from all MASM files -fn extract_all_event_definitions(asm_source_dir: &Path) -> Result> { - // collect mappings event path to const variable name, we want a unique mapping - // which we use to generate the constants and enum variant names - let mut events = BTreeMap::new(); - - // Walk all MASM files - for entry in WalkDir::new(asm_source_dir) { - let entry = entry.into_diagnostic()?; - if !is_masm_file(entry.path()).into_diagnostic()? { - continue; - } - let file_contents = fs::read_to_string(entry.path()).into_diagnostic()?; - extract_event_definitions_from_file(&mut events, &file_contents, entry.path())?; + Ok(()) } - Ok(events) -} - -/// Extract event definitions from a single MASM file in form of `const ${X} = event("${x::path}")`. -fn extract_event_definitions_from_file( - events: &mut BTreeMap, - file_contents: &str, - file_path: &Path, -) -> Result<()> { - let regex = Regex::new(r#"const\s*(\w+)\s*=\s*event\("([^"]+)"\)"#).unwrap(); - - for capture in regex.captures_iter(file_contents) { - let const_name = capture.get(1).expect("const name should be captured"); - let event_path = capture.get(2).expect("event path should be captured"); - - let event_path = event_path.as_str(); - let const_name = const_name.as_str(); - - let const_name_wo_suffix = - if let Some((const_name_wo_suffix, _)) = const_name.rsplit_once("_EVENT") { - const_name_wo_suffix.to_string() - } else { - const_name.to_owned() - }; + pub type ErrorName = String; - if !event_path.starts_with("miden::") { - return Err(miette::miette!("unhandled `event_path={event_path}`")); - } - - // Check for duplicates with different definitions - if let Some(existing_const_name) = events.get(event_path) { - if existing_const_name != &const_name_wo_suffix { - println!( - "cargo:warning=Duplicate event definition found {event_path} with different definitions names: - '{existing_const_name}' vs '{const_name}' in {}", - file_path.display() - ); - } - } else { - events.insert(event_path.to_owned(), const_name_wo_suffix.to_owned()); - } + #[derive(Debug, Clone)] + pub struct ExtractedError { + pub message: String, } - Ok(()) -} - -/// Generate the content of the transaction_events.rs file -fn generate_event_file_content( - events: &BTreeMap, -) -> std::result::Result { - use std::fmt::Write; - - let mut output = String::new(); - - writeln!(&mut output, "// This file is generated by build.rs, do not modify")?; - writeln!(&mut output)?; - - // Generate constants - // - // Note: If we ever encounter two constants `const X`, that are both named `X` we will error - // when attempting to generate the rust code. Currently this is a side-effect, but we - // want to error out as early as possible: - // TODO: make the error out at build-time to be able to present better error hints - for (event_path, event_name) in events { - let value = miden_core::EventId::from_name(event_path).as_felt().as_int(); - debug_assert!(!event_name.is_empty()); - writeln!(&mut output, "const {}: u64 = {};", event_name, value)?; + #[derive(Debug, Clone)] + pub struct NamedError { + pub name: ErrorName, + pub message: String, } - { - writeln!(&mut output)?; - - writeln!(&mut output)?; - - writeln!( - &mut output, - r###" -use alloc::collections::BTreeMap; - -pub(crate) static EVENT_NAME_LUT: ::miden_objects::utils::sync::LazyLock> = - ::miden_objects::utils::sync::LazyLock::new(|| {{ - BTreeMap::from_iter([ -"### - )?; - - for (event_path, const_name) in events { - writeln!(&mut output, " ({}, \"{}\"),", const_name, event_path)?; - } - - writeln!( - &mut output, - r###" ]) -}});"### - )?; + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + pub struct ErrorModule { + pub file_name: &'static str, + pub array_name: &'static str, + pub is_crate_local: bool, } - - Ok(output) } diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs index 80029eb351..bdab19c3a4 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs @@ -13,9 +13,9 @@ static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// of transactions. /// /// It reexports the procedures from `miden::contracts::auth::ecdsa_k256_keccak`. When linking -/// against this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be -/// available to the assembler which is the case when using [`CodeBuilder`][builder]. The procedures -/// of this component are: +/// against this component, the `miden` library (i.e. +/// [`ProtocolLib`](miden_objects::ProtocolLib)) must be available to the assembler which is the +/// case when using [`CodeBuilder`][builder]. The procedures of this component are: /// - `verify_signatures`, which can be used to verify a signature provided via the advice stack to /// authenticate a transaction. /// - `authenticate_transaction`, which can be used to authenticate a transaction using the ECDSA @@ -23,7 +23,7 @@ static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// This component supports all account types. /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct AuthEcdsaK256Keccak { pub_key: PublicKeyCommitment, } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs index 1c0f5e0b01..5bb476bc39 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-lib/src/account/auth/rpo_falcon_512.rs @@ -13,9 +13,9 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// transactions. /// /// It reexports the procedures from `miden::contracts::auth::rpo_falcon512`. When linking against -/// this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to -/// the assembler which is the case when using [`CodeBuilder`][builder]. The procedures of this -/// component are: +/// this component, the `miden` library (i.e. [`ProtocolLib`](miden_objects::ProtocolLib)) must +/// be available to the assembler which is the case when using [`CodeBuilder`][builder]. The +/// procedures of this component are: /// - `verify_signatures`, which can be used to verify a signature provided via the advice stack to /// authenticate a transaction. /// - `authenticate_transaction`, which can be used to authenticate a transaction using the Falcon @@ -27,7 +27,7 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// - [`Self::public_key_slot`]: Public key /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct AuthRpoFalcon512 { pub_key: PublicKeyCommitment, } diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-lib/src/account/faucets/basic_fungible.rs index 2201504fde..24b1b568b9 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-lib/src/account/faucets/basic_fungible.rs @@ -43,9 +43,9 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic fungible faucet. /// /// It reexports the procedures from `miden::contracts::faucets::basic_fungible`. When linking -/// against this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be -/// available to the assembler which is the case when using -/// [`CodeBuilder`][builder]. The procedures of this component are: +/// against this component, the `miden` library (i.e. +/// [`ProtocolLib`](miden_objects::ProtocolLib)) must be available to the assembler which is the +/// case when using [`CodeBuilder`][builder]. The procedures of this component are: /// - `distribute`, which mints an assets and create a note for the provided recipient. /// - `burn`, which burns the provided asset. /// @@ -60,7 +60,7 @@ procedure_digest!( /// /// - [`Self::metadata_slot`]: Fungible faucet metadata /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct BasicFungibleFaucet { symbol: TokenSymbol, decimals: u8, diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-lib/src/account/faucets/network_fungible.rs index 809b1c04d2..f6d7723c3d 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-lib/src/account/faucets/network_fungible.rs @@ -44,9 +44,9 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing a network fungible faucet. /// /// It reexports the procedures from `miden::contracts::faucets::network_fungible`. When linking -/// against this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be -/// available to the assembler which is the case when using -/// [`CodeBuilder`][builder]. The procedures of this component are: +/// against this component, the `miden` library (i.e. +/// [`ProtocolLib`](miden_objects::ProtocolLib)) must be available to the assembler which is the +/// case when using [`CodeBuilder`][builder]. The procedures of this component are: /// - `distribute`, which mints an assets and create a note for the provided recipient. /// - `burn`, which burns the provided asset. /// @@ -59,7 +59,7 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// - [`Self::metadata_slot`]: Fungible faucet metadata. /// - [`Self::owner_config_slot`]: The owner account of this network faucet. /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct NetworkFungibleFaucet { faucet: BasicFungibleFaucet, owner_account_id: AccountId, diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-lib/src/account/interface/mod.rs index d17901eefa..ac32cec99b 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-lib/src/account/interface/mod.rs @@ -7,8 +7,8 @@ use miden_objects::transaction::TransactionScript; use thiserror::Error; use crate::AuthScheme; +use crate::code_builder::CodeBuilder; use crate::errors::CodeBuilderError; -use crate::utils::CodeBuilder; #[cfg(test)] mod test; diff --git a/crates/miden-lib/src/account/interface/test.rs b/crates/miden-lib/src/account/interface/test.rs index b085508911..2abe1e3b7d 100644 --- a/crates/miden-lib/src/account/interface/test.rs +++ b/crates/miden-lib/src/account/interface/test.rs @@ -35,9 +35,9 @@ use crate::account::interface::{ NoteAccountCompatibility, }; use crate::account::wallets::BasicWallet; +use crate::code_builder::CodeBuilder; use crate::note::{create_p2id_note, create_p2ide_note, create_swap_note}; use crate::testing::account_interface::get_public_keys_from_account; -use crate::utils::CodeBuilder; // DEFAULT NOTES // ================================================================================================ diff --git a/crates/miden-lib/src/account/wallets/mod.rs b/crates/miden-lib/src/account/wallets/mod.rs index efd95a597e..06160377c9 100644 --- a/crates/miden-lib/src/account/wallets/mod.rs +++ b/crates/miden-lib/src/account/wallets/mod.rs @@ -42,8 +42,8 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic wallet. /// /// It reexports the procedures from `miden::contracts::wallets::basic`. When linking against this -/// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures +/// component, the `miden` library (i.e. [`ProtocolLib`](miden_objects::ProtocolLib)) must be +/// available to the assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: /// - `receive_asset`, which can be used to add an asset to the account. /// - `move_asset_to_note`, which can be used to remove the specified asset from the account and add @@ -54,7 +54,7 @@ procedure_digest!( /// /// This component supports all account types. /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct BasicWallet; impl BasicWallet { diff --git a/crates/miden-lib/src/block/mod.rs b/crates/miden-lib/src/block/mod.rs deleted file mode 100644 index d3db8b80e8..0000000000 --- a/crates/miden-lib/src/block/mod.rs +++ /dev/null @@ -1,73 +0,0 @@ -use miden_objects::ProposedBlockError; -use miden_objects::block::{BlockBody, BlockHeader, ProposedBlock}; - -use crate::transaction::TransactionKernel; - -/// Builds a [`BlockHeader`] and [`BlockBody`] by computing the following from the state -/// updates encapsulated by the provided [`ProposedBlock`]: -/// - the account root; -/// - the nullifier root; -/// - the note root; -/// - the transaction commitment; and -/// - the chain commitment. -/// -/// The returned block header contains the same validator public key as the previous block, as -/// provided by the proposed block. -/// -/// This functionality is handled here because the block header requires [`TransactionKernel`] for -/// its various commitment fields. -pub fn build_block( - proposed_block: ProposedBlock, -) -> Result<(BlockHeader, BlockBody), ProposedBlockError> { - // Get fields from the proposed block before it is consumed. - let block_num = proposed_block.block_num(); - let timestamp = proposed_block.timestamp(); - let prev_block_header = proposed_block.prev_block_header().clone(); - - // Insert the state commitments of updated accounts into the account tree to compute its new - // root. - let new_account_root = proposed_block.compute_account_root()?; - - // Insert the created nullifiers into the nullifier tree to compute its new root. - let new_nullifier_root = proposed_block.compute_nullifier_root()?; - - // Compute the root of the block note tree. - let note_tree = proposed_block.compute_block_note_tree(); - let note_root = note_tree.root(); - - // Insert the previous block header into the block partial blockchain to get the new chain - // commitment. - let new_chain_commitment = proposed_block.compute_chain_commitment(); - - // Construct the block body from the proposed block. - let body = BlockBody::from(proposed_block); - - // Construct the header. - let tx_commitment = body.transaction_commitment(); - let prev_block_commitment = prev_block_header.commitment(); - - // For now we copy the parameters of the previous header, which means the parameters set on - // the genesis block will be passed through. Eventually, the contained base fees will be - // updated based on the demand in the currently proposed block. - let fee_parameters = prev_block_header.fee_parameters().clone(); - - // Currently undefined and reserved for future use. - // See miden-base/1155. - let version = 0; - let tx_kernel_commitment = TransactionKernel.to_commitment(); - let header = BlockHeader::new( - version, - prev_block_commitment, - block_num, - new_chain_commitment, - new_account_root, - new_nullifier_root, - note_root, - tx_commitment, - tx_kernel_commitment, - prev_block_header.validator_key().clone(), - fee_parameters, - timestamp, - ); - Ok((header, body)) -} diff --git a/crates/miden-lib/src/utils/code_builder.rs b/crates/miden-lib/src/code_builder/mod.rs similarity index 95% rename from crates/miden-lib/src/utils/code_builder.rs rename to crates/miden-lib/src/code_builder/mod.rs index bd3cf38329..fc99422380 100644 --- a/crates/miden-lib/src/utils/code_builder.rs +++ b/crates/miden-lib/src/code_builder/mod.rs @@ -11,10 +11,10 @@ use miden_objects::assembly::{ SourceManagerSync, }; use miden_objects::note::NoteScript; -use miden_objects::transaction::TransactionScript; +use miden_objects::transaction::{TransactionKernel, TransactionScript}; use crate::errors::CodeBuilderError; -use crate::transaction::TransactionKernel; +use crate::standards_lib::StandardsLib; // CODE BUILDER // ================================================================================================ @@ -55,9 +55,9 @@ use crate::transaction::TransactionKernel; /// /// ```no_run /// # use anyhow::Context; -/// # use miden_lib::utils::CodeBuilder; +/// # use miden_lib::code_builder::CodeBuilder; /// # use miden_objects::assembly::Library; -/// # use miden_core_lib::CoreLibrary; +/// # use miden_objects::CoreLibrary; /// # fn example() -> anyhow::Result<()> { /// # let module_code = "pub proc test push.1 add end"; /// # let script_code = "begin nop end"; @@ -89,9 +89,7 @@ impl CodeBuilder { /// Creates a new CodeBuilder. pub fn new() -> Self { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - Self { assembler, source_manager } + Self::with_source_manager(Arc::new(DefaultSourceManager::default())) } /// Creates a new CodeBuilder with the specified source manager. @@ -99,7 +97,9 @@ impl CodeBuilder { /// # Arguments /// * `source_manager` - The source manager to use with the internal `Assembler` pub fn with_source_manager(source_manager: Arc) -> Self { - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); + let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()) + .with_dynamic_library(StandardsLib::default()) + .expect("linking std lib should work"); Self { assembler, source_manager } } @@ -342,7 +342,7 @@ impl CodeBuilder { /// /// [account_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_account_library /// [faucet_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_faucet_library - /// [util_lib]: crate::testing::mock_util_lib::mock_util_library + /// [util_lib]: miden_objects::testing::mock_util_lib::mock_util_library #[cfg(any(feature = "testing", test))] pub fn with_mock_libraries() -> Self { Self::with_mock_libraries_with_source_manager(Arc::new(DefaultSourceManager::default())) @@ -362,28 +362,28 @@ impl CodeBuilder { pub fn with_mock_libraries_with_source_manager( source_manager: Arc, ) -> Self { - use crate::testing::mock_util_lib::mock_util_library; + use miden_objects::testing::mock_util_lib::mock_util_library; - // Start from the full kernel-aware assembler (includes core lib and miden-lib). - let mut assembler = - TransactionKernel::assembler_with_source_manager(source_manager.clone()); + // Start with the builder linking against the transaction kernel, protocol library and + // standards library. + let mut builder = Self::with_source_manager(source_manager); // Expose kernel procedures under `$kernel` for testing. - assembler - .link_dynamic_library(TransactionKernel::library()) + builder + .link_dynamic_library(&TransactionKernel::library()) .expect("failed to link kernel library"); // Add mock account/faucet libs (built in debug mode) and mock util. for library in Self::mock_libraries() { - assembler - .link_dynamic_library(library) + builder + .link_dynamic_library(&library) .expect("failed to link mock account libraries"); } - assembler - .link_static_library(mock_util_library()) + builder + .link_static_library(&mock_util_library()) .expect("failed to link mock util library"); - Self { assembler, source_manager } + builder } } diff --git a/crates/miden-lib/src/errors/mod.rs b/crates/miden-lib/src/errors/mod.rs index 4f7fbaa593..2bf69e28e0 100644 --- a/crates/miden-lib/src/errors/mod.rs +++ b/crates/miden-lib/src/errors/mod.rs @@ -1,23 +1,7 @@ -/// The errors from the MASM code of the transaction kernel. -#[cfg(any(feature = "testing", test))] -#[rustfmt::skip] -pub mod tx_kernel; - -/// The errors from the MASM code of the Miden protocol library. -#[cfg(any(feature = "testing", test))] -#[rustfmt::skip] -pub mod protocol; - /// The errors from the MASM code of the Miden standards. #[cfg(any(feature = "testing", test))] #[rustfmt::skip] pub mod standards; -mod masm_error; -pub use masm_error::MasmError; - mod code_builder_errors; pub use code_builder_errors::CodeBuilderError; - -mod transaction_errors; -pub use transaction_errors::{TransactionEventError, TransactionTraceParsingError}; diff --git a/crates/miden-lib/src/errors/standards.rs b/crates/miden-lib/src/errors/standards.rs index b02a414dca..0f53b5fcd7 100644 --- a/crates/miden-lib/src/errors/standards.rs +++ b/crates/miden-lib/src/errors/standards.rs @@ -1,4 +1,4 @@ -use crate::errors::MasmError; +use miden_objects::errors::MasmError; // This file is generated by build.rs, do not modify manually. // It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. @@ -9,18 +9,21 @@ use crate::errors::MasmError; // STANDARDS ERRORS // ================================================================================================ -/// Error Message: "number of approvers must be equal to or greater than threshold" -pub const ERR_MALFORMED_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers must be equal to or greater than threshold"); +/// Error Message: "burn requires exactly 1 note asset" +pub const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("burn requires exactly 1 note asset"); -/// Error Message: "failed to approve multisig transaction as it was already executed" -pub const ERR_TX_ALREADY_EXECUTED: MasmError = MasmError::from_static_str("failed to approve multisig transaction as it was already executed"); +/// Error Message: "distribute would cause the maximum supply to be exceeded" +pub const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED: MasmError = MasmError::from_static_str("distribute would cause the maximum supply to be exceeded"); -/// Error Message: "number of approvers or threshold must not be zero" -pub const ERR_ZERO_IN_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers or threshold must not be zero"); +/// Error Message: "number of approvers must be equal to or greater than threshold" +pub const ERR_MALFORMED_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers must be equal to or greater than threshold"); /// Error Message: "MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" pub const ERR_MINT_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes"); +/// Error Message: "note sender is not the owner of the faucet who can mint assets" +pub const ERR_ONLY_OWNER_CAN_MINT: MasmError = MasmError::from_static_str("note sender is not the owner of the faucet who can mint assets"); + /// Error Message: "failed to reclaim P2IDE note because the reclaiming account is not the sender" pub const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER: MasmError = MasmError::from_static_str("failed to reclaim P2IDE note because the reclaiming account is not the sender"); /// Error Message: "P2IDE reclaim is disabled" @@ -41,3 +44,9 @@ pub const ERR_P2ID_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_st pub const ERR_SWAP_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("SWAP script requires exactly 1 note asset"); /// Error Message: "SWAP script expects exactly 12 note inputs" pub const ERR_SWAP_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("SWAP script expects exactly 12 note inputs"); + +/// Error Message: "failed to approve multisig transaction as it was already executed" +pub const ERR_TX_ALREADY_EXECUTED: MasmError = MasmError::from_static_str("failed to approve multisig transaction as it was already executed"); + +/// Error Message: "number of approvers or threshold must not be zero" +pub const ERR_ZERO_IN_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers or threshold must not be zero"); diff --git a/crates/miden-lib/src/errors/transaction_errors.rs b/crates/miden-lib/src/errors/transaction_errors.rs deleted file mode 100644 index d216c1342a..0000000000 --- a/crates/miden-lib/src/errors/transaction_errors.rs +++ /dev/null @@ -1,26 +0,0 @@ -use miden_core::EventId; -use thiserror::Error; - -use crate::transaction::TransactionEventId; - -// TRANSACTION EVENT PARSING ERROR -// ================================================================================================ - -#[derive(Debug, Error)] -pub enum TransactionEventError { - #[error("event id {0} is not a valid transaction event")] - InvalidTransactionEvent(EventId, Option<&'static str>), - #[error("event id {0} is not a transaction kernel event")] - NotTransactionEvent(EventId, Option<&'static str>), - #[error("event id {0} can only be emitted from the root context")] - NotRootContext(TransactionEventId), -} - -// TRANSACTION TRACE PARSING ERROR -// ================================================================================================ - -#[derive(Debug, Error)] -pub enum TransactionTraceParsingError { - #[error("trace id {0} is an unknown transaction kernel trace")] - UnknownTransactionTrace(u32), -} diff --git a/crates/miden-lib/src/lib.rs b/crates/miden-lib/src/lib.rs index c04aeff6f5..8818e57389 100644 --- a/crates/miden-lib/src/lib.rs +++ b/crates/miden-lib/src/lib.rs @@ -1,96 +1,21 @@ #![no_std] -use alloc::sync::Arc; - #[macro_use] extern crate alloc; #[cfg(feature = "std")] extern crate std; -use miden_objects::assembly::Library; -use miden_objects::assembly::mast::MastForest; -use miden_objects::utils::serde::Deserializable; -use miden_objects::utils::sync::LazyLock; - mod auth; pub use auth::AuthScheme; pub mod account; -pub mod block; +pub mod code_builder; pub mod errors; pub mod note; -pub mod transaction; -pub mod utils; +mod standards_lib; + +pub use standards_lib::StandardsLib; #[cfg(any(feature = "testing", test))] pub mod testing; - -// RE-EXPORTS -// ================================================================================================ -pub use miden_core_lib::CoreLibrary; - -// CONSTANTS -// ================================================================================================ - -const MIDEN_LIB_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/assets/miden.masl")); - -// MIDEN LIBRARY -// ================================================================================================ - -#[derive(Clone)] -pub struct MidenLib(Library); - -impl MidenLib { - /// Returns a reference to the [`MastForest`] of the inner [`Library`]. - pub fn mast_forest(&self) -> &Arc { - self.0.mast_forest() - } -} - -impl AsRef for MidenLib { - fn as_ref(&self) -> &Library { - &self.0 - } -} - -impl From for Library { - fn from(value: MidenLib) -> Self { - value.0 - } -} - -impl Default for MidenLib { - fn default() -> Self { - static MIDEN_LIB: LazyLock = LazyLock::new(|| { - let contents = - Library::read_from_bytes(MIDEN_LIB_BYTES).expect("failed to read miden lib masl!"); - MidenLib(contents) - }); - MIDEN_LIB.clone() - } -} - -// TESTS -// ================================================================================================ - -// NOTE: Most kernel-related tests can be found under /miden-tx/kernel_tests -#[cfg(all(test, feature = "std"))] -mod tests { - use miden_objects::assembly::Path; - - use super::MidenLib; - - #[test] - fn test_compile() { - let path = Path::new("::miden::active_account::get_id"); - let miden = MidenLib::default(); - let exists = miden.0.module_infos().any(|module| { - module - .procedures() - .any(|(_, proc)| module.path().join(&proc.name).as_path() == path) - }); - - assert!(exists); - } -} diff --git a/crates/miden-lib/src/standards_lib.rs b/crates/miden-lib/src/standards_lib.rs new file mode 100644 index 0000000000..2c832727a1 --- /dev/null +++ b/crates/miden-lib/src/standards_lib.rs @@ -0,0 +1,72 @@ +use alloc::sync::Arc; + +use miden_objects::assembly::Library; +use miden_objects::assembly::mast::MastForest; +use miden_objects::utils::serde::Deserializable; +use miden_objects::utils::sync::LazyLock; + +// CONSTANTS +// ================================================================================================ + +const STANDARDS_LIB_BYTES: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/assets/standards.masl")); + +// MIDEN STANDARDS LIBRARY +// ================================================================================================ + +#[derive(Clone)] +pub struct StandardsLib(Library); + +impl StandardsLib { + /// Returns a reference to the [`MastForest`] of the inner [`Library`]. + pub fn mast_forest(&self) -> &Arc { + self.0.mast_forest() + } +} + +impl AsRef for StandardsLib { + fn as_ref(&self) -> &Library { + &self.0 + } +} + +impl From for Library { + fn from(value: StandardsLib) -> Self { + value.0 + } +} + +impl Default for StandardsLib { + fn default() -> Self { + static STANDARDS_LIB: LazyLock = LazyLock::new(|| { + let contents = Library::read_from_bytes(STANDARDS_LIB_BYTES) + .expect("standards lib masl should be well-formed"); + StandardsLib(contents) + }); + STANDARDS_LIB.clone() + } +} + +// TESTS +// ================================================================================================ + +// NOTE: Most standards-related tests can be found in miden-testing. +#[cfg(all(test, feature = "std"))] +mod tests { + use miden_objects::assembly::Path; + + use super::StandardsLib; + + #[test] + fn test_compile() { + let path = Path::new("::miden::contracts::faucets::basic_fungible::distribute"); + let miden = StandardsLib::default(); + let exists = miden.0.module_infos().any(|module| { + module + .procedures() + .any(|(_, proc)| module.path().join(&proc.name).as_path() == path) + }); + + assert!(exists); + } +} diff --git a/crates/miden-lib/src/testing/account_component/conditional_auth.rs b/crates/miden-lib/src/testing/account_component/conditional_auth.rs index 9a1e0ea8da..df05ff3dab 100644 --- a/crates/miden-lib/src/testing/account_component/conditional_auth.rs +++ b/crates/miden-lib/src/testing/account_component/conditional_auth.rs @@ -3,7 +3,7 @@ use alloc::string::String; use miden_objects::account::{AccountComponent, AccountComponentCode}; use miden_objects::utils::sync::LazyLock; -use crate::utils::CodeBuilder; +use crate::code_builder::CodeBuilder; pub const ERR_WRONG_ARGS_MSG: &str = "auth procedure args are incorrect"; diff --git a/crates/miden-lib/src/testing/account_component/incr_nonce.rs b/crates/miden-lib/src/testing/account_component/incr_nonce.rs index 134f8f3bed..3956176d9c 100644 --- a/crates/miden-lib/src/testing/account_component/incr_nonce.rs +++ b/crates/miden-lib/src/testing/account_component/incr_nonce.rs @@ -2,7 +2,7 @@ use miden_objects::account::AccountComponent; use miden_objects::assembly::Library; use miden_objects::utils::sync::LazyLock; -use crate::utils::CodeBuilder; +use crate::code_builder::CodeBuilder; const INCR_NONCE_AUTH_CODE: &str = " use miden::native_account diff --git a/crates/miden-lib/src/testing/mock_account_code.rs b/crates/miden-lib/src/testing/mock_account_code.rs index 81d4b5a066..fe2e7472da 100644 --- a/crates/miden-lib/src/testing/mock_account_code.rs +++ b/crates/miden-lib/src/testing/mock_account_code.rs @@ -2,7 +2,7 @@ use miden_objects::account::AccountCode; use miden_objects::assembly::Library; use miden_objects::utils::sync::LazyLock; -use crate::utils::CodeBuilder; +use crate::code_builder::CodeBuilder; const MOCK_FAUCET_CODE: &str = " use miden::faucet diff --git a/crates/miden-lib/src/testing/mod.rs b/crates/miden-lib/src/testing/mod.rs index fa43a14205..f08811b562 100644 --- a/crates/miden-lib/src/testing/mod.rs +++ b/crates/miden-lib/src/testing/mod.rs @@ -1,6 +1,7 @@ pub mod account_component; pub mod account_interface; + pub mod mock_account; pub mod mock_account_code; -pub mod mock_util_lib; + pub mod note; diff --git a/crates/miden-lib/src/testing/note.rs b/crates/miden-lib/src/testing/note.rs index f6d590a008..a5e6dfc7f9 100644 --- a/crates/miden-lib/src/testing/note.rs +++ b/crates/miden-lib/src/testing/note.rs @@ -20,7 +20,7 @@ use miden_objects::testing::note::DEFAULT_NOTE_CODE; use miden_objects::{Felt, NoteError, Word, ZERO}; use rand::Rng; -use crate::utils::CodeBuilder; +use crate::code_builder::CodeBuilder; // NOTE BUILDER // ================================================================================================ diff --git a/crates/miden-lib/src/transaction/outputs.rs b/crates/miden-lib/src/transaction/outputs.rs deleted file mode 100644 index bb28b753a8..0000000000 --- a/crates/miden-lib/src/transaction/outputs.rs +++ /dev/null @@ -1,65 +0,0 @@ -use miden_objects::account::{AccountHeader, AccountId}; -use miden_objects::{AccountError, Felt, WORD_SIZE, Word, WordError}; - -use super::memory::{ - ACCT_CODE_COMMITMENT_OFFSET, - ACCT_DATA_MEM_SIZE, - ACCT_ID_AND_NONCE_OFFSET, - ACCT_NONCE_IDX, - ACCT_STORAGE_COMMITMENT_OFFSET, - ACCT_VAULT_ROOT_OFFSET, - MemoryOffset, -}; -use crate::transaction::memory::{ACCT_ID_PREFIX_IDX, ACCT_ID_SUFFIX_IDX}; - -// STACK OUTPUTS -// ================================================================================================ - -/// The index of the word at which the final account nonce is stored on the output stack. -pub const OUTPUT_NOTES_COMMITMENT_WORD_IDX: usize = 0; - -/// The index of the word at which the account update commitment is stored on the output stack. -pub const ACCOUNT_UPDATE_COMMITMENT_WORD_IDX: usize = 1; - -/// The index of the word at which the fee asset is stored on the output stack. -pub const FEE_ASSET_WORD_IDX: usize = 2; - -/// The index of the item at which the expiration block height is stored on the output stack. -pub const EXPIRATION_BLOCK_ELEMENT_IDX: usize = 12; - -// ACCOUNT HEADER EXTRACTOR -// ================================================================================================ - -/// Parses the account header data returned by the VM into individual account component commitments. -/// Returns a tuple of account ID, vault root, storage commitment, code commitment, and nonce. -pub fn parse_final_account_header(elements: &[Felt]) -> Result { - if elements.len() != ACCT_DATA_MEM_SIZE { - return Err(AccountError::HeaderDataIncorrectLength { - actual: elements.len(), - expected: ACCT_DATA_MEM_SIZE, - }); - } - - let id = AccountId::try_from([ - elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_PREFIX_IDX], - elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_SUFFIX_IDX], - ]) - .map_err(AccountError::FinalAccountHeaderIdParsingFailed)?; - let nonce = elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_NONCE_IDX]; - let vault_root = parse_word(elements, ACCT_VAULT_ROOT_OFFSET) - .expect("we should have sliced off exactly 4 bytes"); - let storage_commitment = parse_word(elements, ACCT_STORAGE_COMMITMENT_OFFSET) - .expect("we should have sliced off exactly 4 bytes"); - let code_commitment = parse_word(elements, ACCT_CODE_COMMITMENT_OFFSET) - .expect("we should have sliced off exactly 4 bytes"); - - Ok(AccountHeader::new(id, nonce, vault_root, storage_commitment, code_commitment)) -} - -// HELPER FUNCTIONS -// ================================================================================================ - -/// Creates a new `Word` instance from the slice of `Felt`s using provided offset. -fn parse_word(data: &[Felt], offset: MemoryOffset) -> Result { - Word::try_from(&data[offset as usize..offset as usize + WORD_SIZE]) -} diff --git a/crates/miden-lib/src/utils/mod.rs b/crates/miden-lib/src/utils/mod.rs deleted file mode 100644 index 408cd69922..0000000000 --- a/crates/miden-lib/src/utils/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod code_builder; - -pub use code_builder::CodeBuilder; -pub use miden_objects::utils::*; - -pub use crate::errors::CodeBuilderError; diff --git a/crates/miden-objects/Cargo.toml b/crates/miden-objects/Cargo.toml index e62480a48c..739f6a438d 100644 --- a/crates/miden-objects/Cargo.toml +++ b/crates/miden-objects/Cargo.toml @@ -73,3 +73,11 @@ winter-air = { version = "0.13" } # for HashFunction/ExecutionProof::new_dummy color-eyre = { version = "0.5" } miden-air = { features = ["std", "testing"], workspace = true } + +[build-dependencies] +fs-err = { version = "3" } +miden-assembly = { workspace = true } +miden-core = { workspace = true } +miden-core-lib = { workspace = true } +regex = { version = "1.11" } +walkdir = { version = "2.5" } diff --git a/crates/miden-lib/asm/kernels/transaction/api.masm b/crates/miden-objects/asm/kernels/transaction/api.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/api.masm rename to crates/miden-objects/asm/kernels/transaction/api.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-objects/asm/kernels/transaction/lib/account.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/account.masm rename to crates/miden-objects/asm/kernels/transaction/lib/account.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-objects/asm/kernels/transaction/lib/account_delta.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm rename to crates/miden-objects/asm/kernels/transaction/lib/account_delta.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/asset.masm b/crates/miden-objects/asm/kernels/transaction/lib/asset.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/asset.masm rename to crates/miden-objects/asm/kernels/transaction/lib/asset.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm b/crates/miden-objects/asm/kernels/transaction/lib/asset_vault.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm rename to crates/miden-objects/asm/kernels/transaction/lib/asset_vault.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/constants.masm b/crates/miden-objects/asm/kernels/transaction/lib/constants.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/constants.masm rename to crates/miden-objects/asm/kernels/transaction/lib/constants.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm b/crates/miden-objects/asm/kernels/transaction/lib/epilogue.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm rename to crates/miden-objects/asm/kernels/transaction/lib/epilogue.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm b/crates/miden-objects/asm/kernels/transaction/lib/faucet.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/faucet.masm rename to crates/miden-objects/asm/kernels/transaction/lib/faucet.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/input_note.masm b/crates/miden-objects/asm/kernels/transaction/lib/input_note.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/input_note.masm rename to crates/miden-objects/asm/kernels/transaction/lib/input_note.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/link_map.masm b/crates/miden-objects/asm/kernels/transaction/lib/link_map.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/link_map.masm rename to crates/miden-objects/asm/kernels/transaction/lib/link_map.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm b/crates/miden-objects/asm/kernels/transaction/lib/memory.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/memory.masm rename to crates/miden-objects/asm/kernels/transaction/lib/memory.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/note.masm b/crates/miden-objects/asm/kernels/transaction/lib/note.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/note.masm rename to crates/miden-objects/asm/kernels/transaction/lib/note.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/output_note.masm b/crates/miden-objects/asm/kernels/transaction/lib/output_note.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/output_note.masm rename to crates/miden-objects/asm/kernels/transaction/lib/output_note.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-objects/asm/kernels/transaction/lib/prologue.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/prologue.masm rename to crates/miden-objects/asm/kernels/transaction/lib/prologue.masm diff --git a/crates/miden-lib/asm/kernels/transaction/lib/tx.masm b/crates/miden-objects/asm/kernels/transaction/lib/tx.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/lib/tx.masm rename to crates/miden-objects/asm/kernels/transaction/lib/tx.masm diff --git a/crates/miden-lib/asm/kernels/transaction/main.masm b/crates/miden-objects/asm/kernels/transaction/main.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/main.masm rename to crates/miden-objects/asm/kernels/transaction/main.masm diff --git a/crates/miden-lib/asm/kernels/transaction/tx_script_main.masm b/crates/miden-objects/asm/kernels/transaction/tx_script_main.masm similarity index 100% rename from crates/miden-lib/asm/kernels/transaction/tx_script_main.masm rename to crates/miden-objects/asm/kernels/transaction/tx_script_main.masm diff --git a/crates/miden-lib/asm/miden/active_account.masm b/crates/miden-objects/asm/miden/active_account.masm similarity index 100% rename from crates/miden-lib/asm/miden/active_account.masm rename to crates/miden-objects/asm/miden/active_account.masm diff --git a/crates/miden-lib/asm/miden/active_note.masm b/crates/miden-objects/asm/miden/active_note.masm similarity index 100% rename from crates/miden-lib/asm/miden/active_note.masm rename to crates/miden-objects/asm/miden/active_note.masm diff --git a/crates/miden-lib/asm/miden/asset.masm b/crates/miden-objects/asm/miden/asset.masm similarity index 100% rename from crates/miden-lib/asm/miden/asset.masm rename to crates/miden-objects/asm/miden/asset.masm diff --git a/crates/miden-lib/asm/miden/faucet.masm b/crates/miden-objects/asm/miden/faucet.masm similarity index 100% rename from crates/miden-lib/asm/miden/faucet.masm rename to crates/miden-objects/asm/miden/faucet.masm diff --git a/crates/miden-lib/asm/miden/input_note.masm b/crates/miden-objects/asm/miden/input_note.masm similarity index 100% rename from crates/miden-lib/asm/miden/input_note.masm rename to crates/miden-objects/asm/miden/input_note.masm diff --git a/crates/miden-lib/asm/miden/kernel_proc_offsets.masm b/crates/miden-objects/asm/miden/kernel_proc_offsets.masm similarity index 100% rename from crates/miden-lib/asm/miden/kernel_proc_offsets.masm rename to crates/miden-objects/asm/miden/kernel_proc_offsets.masm diff --git a/crates/miden-lib/asm/miden/native_account.masm b/crates/miden-objects/asm/miden/native_account.masm similarity index 100% rename from crates/miden-lib/asm/miden/native_account.masm rename to crates/miden-objects/asm/miden/native_account.masm diff --git a/crates/miden-lib/asm/miden/note.masm b/crates/miden-objects/asm/miden/note.masm similarity index 100% rename from crates/miden-lib/asm/miden/note.masm rename to crates/miden-objects/asm/miden/note.masm diff --git a/crates/miden-lib/asm/miden/output_note.masm b/crates/miden-objects/asm/miden/output_note.masm similarity index 100% rename from crates/miden-lib/asm/miden/output_note.masm rename to crates/miden-objects/asm/miden/output_note.masm diff --git a/crates/miden-lib/asm/miden/tx.masm b/crates/miden-objects/asm/miden/tx.masm similarity index 100% rename from crates/miden-lib/asm/miden/tx.masm rename to crates/miden-objects/asm/miden/tx.masm diff --git a/crates/miden-lib/asm/shared_modules/account_id.masm b/crates/miden-objects/asm/shared_modules/account_id.masm similarity index 100% rename from crates/miden-lib/asm/shared_modules/account_id.masm rename to crates/miden-objects/asm/shared_modules/account_id.masm diff --git a/crates/miden-lib/asm/shared_utils/util/asset.masm b/crates/miden-objects/asm/shared_utils/util/asset.masm similarity index 100% rename from crates/miden-lib/asm/shared_utils/util/asset.masm rename to crates/miden-objects/asm/shared_utils/util/asset.masm diff --git a/crates/miden-lib/asm/shared_utils/util/note.masm b/crates/miden-objects/asm/shared_utils/util/note.masm similarity index 100% rename from crates/miden-lib/asm/shared_utils/util/note.masm rename to crates/miden-objects/asm/shared_utils/util/note.masm diff --git a/crates/miden-objects/build.rs b/crates/miden-objects/build.rs new file mode 100644 index 0000000000..2a28d2fd38 --- /dev/null +++ b/crates/miden-objects/build.rs @@ -0,0 +1,813 @@ +use std::collections::{BTreeMap, BTreeSet}; +use std::env; +use std::path::Path; +use std::sync::Arc; + +use fs_err as fs; +use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr, miette}; +use miden_assembly::{Assembler, DefaultSourceManager, KernelLibrary, Library}; +use regex::Regex; +use walkdir::WalkDir; + +// CONSTANTS +// ================================================================================================ + +/// Defines whether the build script should generate files in `/src`. +/// The docs.rs build pipeline has a read-only filesystem, so we have to avoid writing to `src`, +/// otherwise the docs will fail to build there. Note that writing to `OUT_DIR` is fine. +const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN_SRC").is_some(); + +const ASSETS_DIR: &str = "assets"; +const ASM_DIR: &str = "asm"; +const ASM_MIDEN_DIR: &str = "miden"; + +const SHARED_UTILS_DIR: &str = "shared_utils"; +const SHARED_MODULES_DIR: &str = "shared_modules"; +const ASM_TX_KERNEL_DIR: &str = "kernels/transaction"; +const KERNEL_PROCEDURES_RS_FILE: &str = "src/transaction/kernel/procedures.rs"; + +const TX_KERNEL_ERRORS_FILE: &str = "src/errors/tx_kernel.rs"; +const PROTOCOL_LIB_ERRORS_FILE: &str = "src/errors/protocol.rs"; + +const TX_KERNEL_ERRORS_ARRAY_NAME: &str = "TX_KERNEL_ERRORS"; +const PROTOCOL_LIB_ERRORS_ARRAY_NAME: &str = "PROTOCOL_LIB_ERRORS"; + +const TX_KERNEL_ERROR_CATEGORIES: [&str; 14] = [ + "KERNEL", + "PROLOGUE", + "EPILOGUE", + "TX", + "NOTE", + "ACCOUNT", + "FOREIGN_ACCOUNT", + "FAUCET", + "FUNGIBLE_ASSET", + "NON_FUNGIBLE_ASSET", + "VAULT", + "LINK_MAP", + "INPUT_NOTE", + "OUTPUT_NOTE", +]; + +// PRE-PROCESSING +// ================================================================================================ + +/// Read and parse the contents from `./asm`. +/// - Compiles contents of asm/miden directory into a Miden library file (.masl) under miden +/// namespace. +/// - Compiles contents of asm/scripts directory into individual .masb files. +fn main() -> Result<()> { + // re-build when the MASM code changes + println!("cargo::rerun-if-changed={ASM_DIR}/"); + println!("cargo::rerun-if-env-changed=BUILD_GENERATED_FILES_IN_SRC"); + + // Copies the MASM code to the build directory + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let build_dir = env::var("OUT_DIR").unwrap(); + let src = Path::new(&crate_dir).join(ASM_DIR); + let dst = Path::new(&build_dir).to_path_buf(); + shared::copy_directory(src, &dst, ASM_DIR)?; + + // set source directory to {OUT_DIR}/asm + let source_dir = dst.join(ASM_DIR); + + // copy the shared modules to the kernel and miden library folders + copy_shared_modules(&source_dir)?; + + // set target directory to {OUT_DIR}/assets + let target_dir = Path::new(&build_dir).join(ASSETS_DIR); + + // compile transaction kernel + let mut assembler = + compile_tx_kernel(&source_dir.join(ASM_TX_KERNEL_DIR), &target_dir.join("kernels"))?; + + // compile protocol library + let protocol_lib = compile_protocol_lib(&source_dir, &target_dir, assembler.clone())?; + assembler.link_dynamic_library(protocol_lib)?; + + generate_error_constants(&source_dir)?; + + generate_event_constants(&source_dir, &target_dir)?; + + Ok(()) +} + +// COMPILE TRANSACTION KERNEL +// ================================================================================================ + +/// Reads the transaction kernel MASM source from the `source_dir`, compiles it, saves the results +/// to the `target_dir`, and returns an [Assembler] instantiated with the compiled kernel. +/// +/// Additionally it compiles the transaction script executor program, see the +/// [compile_tx_script_main] procedure for details. +/// +/// `source_dir` is expected to have the following structure: +/// +/// - {source_dir}/api.masm -> defines exported procedures from the transaction kernel. +/// - {source_dir}/main.masm -> defines the executable program of the transaction kernel. +/// - {source_dir}/tx_script_main -> defines the executable program of the arbitrary transaction +/// script. +/// - {source_dir}/lib -> contains common modules used by both api.masm and main.masm. +/// +/// The compiled files are written as follows: +/// +/// - {target_dir}/tx_kernel.masl -> contains kernel library compiled from api.masm. +/// - {target_dir}/tx_kernel.masb -> contains the executable compiled from main.masm. +/// - {target_dir}/tx_script_main.masb -> contains the executable compiled from +/// tx_script_main.masm. +/// - src/transaction/procedures/kernel_v0.rs -> contains the kernel procedures table. +fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result { + let shared_utils_path = std::path::Path::new(ASM_DIR).join(SHARED_UTILS_DIR); + let kernel_path = miden_assembly::Path::kernel_path(); + + let mut assembler = build_assembler(None)?; + // add the shared util modules to the kernel lib under the ::$kernel::util namespace + assembler.compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; + + // assemble the kernel library and write it to the "tx_kernel.masl" file + let kernel_lib = assembler + .assemble_kernel_from_dir(source_dir.join("api.masm"), Some(source_dir.join("lib")))?; + + // generate kernel `procedures.rs` file + generate_kernel_proc_hash_file(kernel_lib.clone())?; + + let output_file = target_dir.join("tx_kernel").with_extension(Library::LIBRARY_EXTENSION); + kernel_lib.write_to_file(output_file).into_diagnostic()?; + + let assembler = build_assembler(Some(kernel_lib))?; + + // assemble the kernel program and write it to the "tx_kernel.masb" file + let mut main_assembler = assembler.clone(); + // add the shared util modules to the kernel lib under the ::$kernel::util namespace + main_assembler.compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; + main_assembler.compile_and_statically_link_from_dir(source_dir.join("lib"), kernel_path)?; + + let main_file_path = source_dir.join("main.masm"); + let kernel_main = main_assembler.clone().assemble_program(main_file_path)?; + + let masb_file_path = target_dir.join("tx_kernel.masb"); + kernel_main.write_to_file(masb_file_path).into_diagnostic()?; + + // compile the transaction script main program + compile_tx_script_main(source_dir, target_dir, main_assembler)?; + + #[cfg(any(feature = "testing", test))] + { + let mut kernel_lib_assembler = assembler.clone(); + // Build kernel as a library and save it to file. + // This is needed in test assemblers to access individual procedures which would otherwise + // be hidden when using KernelLibrary (api.masm) + + // add the shared util modules to the kernel lib under the ::$kernel::util namespace + kernel_lib_assembler + .compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; + + let test_lib = kernel_lib_assembler + .assemble_library_from_dir(source_dir.join("lib"), kernel_path) + .unwrap(); + + let masb_file_path = + target_dir.join("kernel_library").with_extension(Library::LIBRARY_EXTENSION); + test_lib.write_to_file(masb_file_path).into_diagnostic()?; + } + + Ok(assembler) +} + +/// Reads the transaction script executor MASM source from the `source_dir/tx_script_main.masm`, +/// compiles it and saves the results to the `target_dir` as a `tx_script_main.masb` binary file. +fn compile_tx_script_main( + source_dir: &Path, + target_dir: &Path, + main_assembler: Assembler, +) -> Result<()> { + // assemble the transaction script executor program and write it to the "tx_script_main.masb" + // file. + let tx_script_main_file_path = source_dir.join("tx_script_main.masm"); + let tx_script_main = main_assembler.assemble_program(tx_script_main_file_path)?; + + let masb_file_path = target_dir.join("tx_script_main.masb"); + tx_script_main.write_to_file(masb_file_path).into_diagnostic() +} + +/// Generates kernel `procedures.rs` file based on the kernel library +fn generate_kernel_proc_hash_file(kernel: KernelLibrary) -> Result<()> { + // Because the kernel Rust file will be stored under ./src, this should be a no-op if we can't + // write there + if !BUILD_GENERATED_FILES_IN_SRC { + return Ok(()); + } + + let (_, module_info, _) = kernel.into_parts(); + + let to_exclude = BTreeSet::from_iter(["exec_kernel_proc"]); + let offsets_filename = Path::new(ASM_DIR).join(ASM_MIDEN_DIR).join("kernel_proc_offsets.masm"); + let offsets = parse_proc_offsets(&offsets_filename)?; + + let generated_procs: BTreeMap = module_info + .procedures() + .filter(|(_, proc_info)| !to_exclude.contains::(proc_info.name.as_ref())) + .map(|(_, proc_info)| { + let name = proc_info.name.to_string(); + + let Some(&offset) = offsets.get(&name) else { + panic!("Offset constant for function `{name}` not found in `{offsets_filename:?}`"); + }; + + (offset, format!(" // {name}\n word!(\"{}\"),", proc_info.digest)) + }) + .collect(); + + let proc_count = generated_procs.len(); + let generated_procs: String = generated_procs.into_iter().enumerate().map(|(index, (offset, txt))| { + if index != offset { + panic!("Offset constants in the file `{offsets_filename:?}` are not contiguous (missing offset: {index})"); + } + + txt + }).collect::>().join("\n"); + + fs::write( + KERNEL_PROCEDURES_RS_FILE, + format!( + r#"// This file is generated by build.rs, do not modify + +use crate::{{Word, word}}; + +// KERNEL PROCEDURES +// ================================================================================================ + +/// Hashes of all dynamically executed kernel procedures. +pub const KERNEL_PROCEDURES: [Word; {proc_count}] = [ +{generated_procs} +]; +"#, + ), + ) + .into_diagnostic() +} + +fn parse_proc_offsets(filename: impl AsRef) -> Result> { + let regex: Regex = Regex::new(r"^const\s*(?P\w+)_OFFSET\s*=\s*(?P\d+)").unwrap(); + let mut result = BTreeMap::new(); + for line in fs::read_to_string(filename).into_diagnostic()?.lines() { + if let Some(captures) = regex.captures(line) { + result.insert( + captures["name"].to_string().to_lowercase(), + captures["offset"].parse().into_diagnostic()?, + ); + } + } + + Ok(result) +} + +// COMPILE PROTOCOL LIB +// ================================================================================================ + +/// Reads the MASM files from "{source_dir}/miden" directory, compiles them into a Miden assembly +/// library, saves the library into "{target_dir}/miden.masl", and returns the compiled library. +fn compile_protocol_lib( + source_dir: &Path, + target_dir: &Path, + mut assembler: Assembler, +) -> Result { + let source_dir = source_dir.join(ASM_MIDEN_DIR); + let shared_path = Path::new(ASM_DIR).join(SHARED_UTILS_DIR); + + // add the shared modules to the protocol lib under the miden::util namespace + // note that this module is not publicly exported, it is only available for linking the library + // itself + assembler.compile_and_statically_link_from_dir(&shared_path, "miden")?; + + let protocol_lib = assembler.assemble_library_from_dir(source_dir, "miden")?; + + let output_file = target_dir.join("protocol").with_extension(Library::LIBRARY_EXTENSION); + protocol_lib.write_to_file(output_file).into_diagnostic()?; + + Ok(protocol_lib) +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Returns a new [Assembler] loaded with miden-core-lib and the specified kernel, if provided. +fn build_assembler(kernel: Option) -> Result { + kernel + .map(|kernel| Assembler::with_kernel(Arc::new(DefaultSourceManager::default()), kernel)) + .unwrap_or_default() + .with_dynamic_library(miden_core_lib::CoreLibrary::default()) +} + +/// Copies the content of the build `shared_modules` folder to the `lib` and `miden` build folders. +/// This is required to include the shared modules as APIs of the `kernel` and `miden` libraries. +/// +/// This is done to make it possible to import the modules in the `shared_modules` folder directly, +/// i.e. "use $kernel::account_id". +fn copy_shared_modules>(source_dir: T) -> Result<()> { + // source is expected to be an `OUT_DIR/asm` folder + let shared_modules_dir = source_dir.as_ref().join(SHARED_MODULES_DIR); + + for module_path in shared::get_masm_files(shared_modules_dir).unwrap() { + let module_name = module_path.file_name().unwrap(); + + // copy to kernel lib + let kernel_lib_folder = source_dir.as_ref().join(ASM_TX_KERNEL_DIR).join("lib"); + fs::copy(&module_path, kernel_lib_folder.join(module_name)).into_diagnostic()?; + + // copy to miden lib + let miden_lib_folder = source_dir.as_ref().join(ASM_MIDEN_DIR); + fs::copy(&module_path, miden_lib_folder.join(module_name)).into_diagnostic()?; + } + + Ok(()) +} + +// ERROR CONSTANTS FILE GENERATION +// ================================================================================================ + +/// Reads all MASM files from the `asm_source_dir` and extracts its error constants and their +/// associated error message and generates a Rust file for each category of errors. +/// For example: +/// +/// ```text +/// const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" +/// ``` +/// +/// would generate a Rust file for transaction kernel errors (since the error belongs to that +/// category, identified by the category extracted from `ERR_`) with - roughly - the +/// following content: +/// +/// ```rust +/// pub const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY: MasmError = +/// MasmError::from_static_str("new account must have an empty vault"); +/// ``` +/// +/// and add the constant to the error constants array. +/// +/// The function ensures that a constant is not defined twice, except if their error message is +/// the same. This can happen across multiple files. +/// +/// Because the error files will be written to ./src/errors, this should be a no-op if ./src is +/// read-only. To enable writing to ./src, set the `BUILD_GENERATED_FILES_IN_SRC` environment +/// variable. +fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { + if !BUILD_GENERATED_FILES_IN_SRC { + return Ok(()); + } + + // Transaction kernel errors + // ------------------------------------------ + + let tx_kernel_dir = asm_source_dir.join(ASM_TX_KERNEL_DIR); + let errors = shared::extract_all_masm_errors(&tx_kernel_dir) + .context("failed to extract all masm errors")?; + validate_tx_kernel_category(&errors)?; + + shared::generate_error_file( + shared::ErrorModule { + file_name: TX_KERNEL_ERRORS_FILE, + array_name: TX_KERNEL_ERRORS_ARRAY_NAME, + is_crate_local: true, + }, + errors, + )?; + + // Miden protocol library errors + // ------------------------------------------ + + let miden_dir = asm_source_dir.join(ASM_MIDEN_DIR); + let errors = + shared::extract_all_masm_errors(&miden_dir).context("failed to extract all masm errors")?; + + shared::generate_error_file( + shared::ErrorModule { + file_name: PROTOCOL_LIB_ERRORS_FILE, + array_name: PROTOCOL_LIB_ERRORS_ARRAY_NAME, + is_crate_local: true, + }, + errors, + )?; + + Ok(()) +} + +/// Validates that all error names in the provided slice start with a known tx kernel error +/// category. +fn validate_tx_kernel_category(errors: &[shared::NamedError]) -> Result<()> { + for error in errors { + if !TX_KERNEL_ERROR_CATEGORIES + .iter() + .any(|known_category| error.name.starts_with(known_category)) + { + return Err(miette::miette!( + "error `{}` does not start with a known tx kernel error category", + error.name + )); + } + } + + Ok(()) +} + +// EVENT CONSTANTS FILE GENERATION +// ================================================================================================ + +/// Reads all MASM files from the `asm_source_dir` and extracts event definitions, +/// then generates the transaction_events.rs file with constants. +fn generate_event_constants(asm_source_dir: &Path, target_dir: &Path) -> Result<()> { + // Extract all event definitions from MASM files + let mut events = extract_all_event_definitions(asm_source_dir)?; + + // Add two additional events we want in `TransactionEventId` that do not appear in kernel or + // protocol lib modules. + events.insert("miden::auth::request".to_owned(), "AUTH_REQUEST".to_owned()); + events.insert("miden::auth::unauthorized".to_owned(), "AUTH_UNAUTHORIZED".to_owned()); + + // Generate the events file in OUT_DIR + let event_file_content = generate_event_file_content(&events).into_diagnostic()?; + let event_file_path = target_dir.join("transaction_events.rs"); + fs::write(event_file_path, event_file_content).into_diagnostic()?; + + Ok(()) +} + +/// Extract all `const X=event("x")` definitions from all MASM files +fn extract_all_event_definitions(asm_source_dir: &Path) -> Result> { + // collect mappings event path to const variable name, we want a unique mapping + // which we use to generate the constants and enum variant names + let mut events = BTreeMap::new(); + + // Walk all MASM files + for entry in WalkDir::new(asm_source_dir) { + let entry = entry.into_diagnostic()?; + if !shared::is_masm_file(entry.path()).into_diagnostic()? { + continue; + } + let file_contents = fs::read_to_string(entry.path()).into_diagnostic()?; + extract_event_definitions_from_file(&mut events, &file_contents, entry.path())?; + } + + Ok(events) +} + +/// Extract event definitions from a single MASM file in form of `const ${X} = event("${x::path}")`. +fn extract_event_definitions_from_file( + events: &mut BTreeMap, + file_contents: &str, + file_path: &Path, +) -> Result<()> { + let regex = Regex::new(r#"const\s*(\w+)\s*=\s*event\("([^"]+)"\)"#).unwrap(); + + for capture in regex.captures_iter(file_contents) { + let const_name = capture.get(1).expect("const name should be captured"); + let event_path = capture.get(2).expect("event path should be captured"); + + let event_path = event_path.as_str(); + let const_name = const_name.as_str(); + + let const_name_wo_suffix = + if let Some((const_name_wo_suffix, _)) = const_name.rsplit_once("_EVENT") { + const_name_wo_suffix.to_string() + } else { + const_name.to_owned() + }; + + if !event_path.starts_with("miden::") { + return Err(miette::miette!("unhandled `event_path={event_path}`")); + } + + // Check for duplicates with different definitions + if let Some(existing_const_name) = events.get(event_path) { + if existing_const_name != &const_name_wo_suffix { + println!( + "cargo:warning=Duplicate event definition found {event_path} with different definitions names: + '{existing_const_name}' vs '{const_name}' in {}", + file_path.display() + ); + } + } else { + events.insert(event_path.to_owned(), const_name_wo_suffix.to_owned()); + } + } + + Ok(()) +} + +/// Generate the content of the transaction_events.rs file +fn generate_event_file_content( + events: &BTreeMap, +) -> std::result::Result { + use std::fmt::Write; + + let mut output = String::new(); + + writeln!(&mut output, "// This file is generated by build.rs, do not modify")?; + writeln!(&mut output)?; + + // Generate constants + // + // Note: If we ever encounter two constants `const X`, that are both named `X` we will error + // when attempting to generate the rust code. Currently this is a side-effect, but we + // want to error out as early as possible: + // TODO: make the error out at build-time to be able to present better error hints + for (event_path, event_name) in events { + let value = miden_core::EventId::from_name(event_path).as_felt().as_int(); + debug_assert!(!event_name.is_empty()); + writeln!(&mut output, "const {}: u64 = {};", event_name, value)?; + } + + { + writeln!(&mut output)?; + + writeln!(&mut output)?; + + writeln!( + &mut output, + r###" +use alloc::collections::BTreeMap; + +pub(crate) static EVENT_NAME_LUT: ::miden_utils_sync::LazyLock> = + ::miden_utils_sync::LazyLock::new(|| {{ + BTreeMap::from_iter([ +"### + )?; + + for (event_path, const_name) in events { + writeln!(&mut output, " ({}, \"{}\"),", const_name, event_path)?; + } + + writeln!( + &mut output, + r###" ]) +}});"### + )?; + } + + Ok(output) +} + +/// This module should be kept in sync with the copy in miden-lib's build.rs. +mod shared { + use std::collections::BTreeMap; + use std::fmt::Write; + use std::io::{self}; + use std::path::{Path, PathBuf}; + + use fs_err as fs; + use miden_assembly::Report; + use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr}; + use regex::Regex; + use walkdir::WalkDir; + + /// Recursively copies `src` into `dst`. + /// + /// This function will overwrite the existing files if re-executed. + pub fn copy_directory, R: AsRef>( + src: T, + dst: R, + asm_dir: &str, + ) -> Result<()> { + let mut prefix = src.as_ref().canonicalize().unwrap(); + // keep all the files inside the `asm` folder + prefix.pop(); + + let target_dir = dst.as_ref().join(asm_dir); + if target_dir.exists() { + // Clear existing asm files that were copied earlier which may no longer exist. + fs::remove_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to remove ASM directory")?; + } + + // Recreate the directory structure. + fs::create_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to create ASM directory")?; + + let dst = dst.as_ref(); + let mut todo = vec![src.as_ref().to_path_buf()]; + + while let Some(goal) = todo.pop() { + for entry in fs::read_dir(goal).unwrap() { + let path = entry.unwrap().path(); + if path.is_dir() { + let src_dir = path.canonicalize().unwrap(); + let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap()); + if !dst_dir.exists() { + fs::create_dir_all(&dst_dir).unwrap(); + } + todo.push(src_dir); + } else { + let dst_file = dst.join(path.strip_prefix(&prefix).unwrap()); + fs::copy(&path, dst_file).unwrap(); + } + } + } + + Ok(()) + } + + /// Returns a vector with paths to all MASM files in the specified directory. + /// + /// All non-MASM files are skipped. + pub fn get_masm_files>(dir_path: P) -> Result> { + let mut files = Vec::new(); + + let path = dir_path.as_ref(); + if path.is_dir() { + let entries = fs::read_dir(path) + .into_diagnostic() + .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; + for entry in entries { + let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; + let file_path = file.path(); + if is_masm_file(&file_path).into_diagnostic()? { + files.push(file_path); + } + } + } else { + println!("cargo:warn=The specified path is not a directory."); + } + + Ok(files) + } + + /// Returns true if the provided path resolves to a file with `.masm` extension. + /// + /// # Errors + /// Returns an error if the path could not be converted to a UTF-8 string. + pub fn is_masm_file(path: &Path) -> io::Result { + if let Some(extension) = path.extension() { + let extension = extension + .to_str() + .ok_or_else(|| io::Error::other("invalid UTF-8 filename"))? + .to_lowercase(); + Ok(extension == "masm") + } else { + Ok(false) + } + } + + /// Extract all masm errors from the given path and returns a map by error category. + pub fn extract_all_masm_errors(asm_source_dir: &Path) -> Result> { + // We use a BTree here to order the errors by their categories which is the first part after + // the ERR_ prefix and to allow for the same error to be defined multiple times in + // different files (as long as the constant name and error messages match). + let mut errors = BTreeMap::new(); + + // Walk all files of the kernel source directory. + for entry in WalkDir::new(asm_source_dir) { + let entry = entry.into_diagnostic()?; + if !is_masm_file(entry.path()).into_diagnostic()? { + continue; + } + let file_contents = std::fs::read_to_string(entry.path()).into_diagnostic()?; + extract_masm_errors(&mut errors, &file_contents)?; + } + + let errors = errors + .into_iter() + .map(|(error_name, error)| NamedError { name: error_name, message: error.message }) + .collect(); + + Ok(errors) + } + + /// Extracts the errors from a single masm file and inserts them into the provided map. + pub fn extract_masm_errors( + errors: &mut BTreeMap, + file_contents: &str, + ) -> Result<()> { + let regex = Regex::new(r#"const\s*ERR_(?.*)\s*=\s*"(?.*)""#).unwrap(); + + for capture in regex.captures_iter(file_contents) { + let error_name = capture + .name("name") + .expect("error name should be captured") + .as_str() + .trim() + .to_owned(); + let error_message = capture + .name("message") + .expect("error code should be captured") + .as_str() + .trim() + .to_owned(); + + if let Some(ExtractedError { message: existing_error_message, .. }) = + errors.get(&error_name) + && existing_error_message != &error_message + { + return Err(Report::msg(format!( + "Transaction kernel error constant ERR_{error_name} is already defined elsewhere but its error message is different" + ))); + } + + // Enforce the "no trailing punctuation" rule from the Rust error guidelines on MASM + // errors. + if error_message.ends_with(".") { + return Err(Report::msg(format!( + "Error messages should not end with a period: `ERR_{error_name}: {error_message}`" + ))); + } + + errors.insert(error_name, ExtractedError { message: error_message }); + } + + Ok(()) + } + + pub fn is_new_error_category<'a>( + last_error: &mut Option<&'a str>, + current_error: &'a str, + ) -> bool { + let is_new = match last_error { + Some(last_err) => { + let last_category = + last_err.split("_").next().expect("there should be at least one entry"); + let new_category = + current_error.split("_").next().expect("there should be at least one entry"); + last_category != new_category + }, + None => false, + }; + + last_error.replace(current_error); + + is_new + } + + /// Generates the content of an error file for the given category and the set of errors and + /// writes it to the category's file. + pub fn generate_error_file(module: ErrorModule, errors: Vec) -> Result<()> { + let mut output = String::new(); + + if module.is_crate_local { + writeln!(output, "use crate::errors::MasmError;\n").unwrap(); + } else { + writeln!(output, "use miden_objects::errors::MasmError;\n").unwrap(); + } + + writeln!( + output, + "// This file is generated by build.rs, do not modify manually. +// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. +// +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). +" + ) + .unwrap(); + + writeln!( + output, + "// {} +// ================================================================================================ +", + module.array_name.replace("_", " ") + ) + .unwrap(); + + let mut last_error = None; + for named_error in errors.iter() { + let NamedError { name, message } = named_error; + + // Group errors into blocks separate by newlines. + if is_new_error_category(&mut last_error, name) { + writeln!(output).into_diagnostic()?; + } + + writeln!(output, "/// Error Message: \"{message}\"").into_diagnostic()?; + writeln!( + output, + r#"pub const ERR_{name}: MasmError = MasmError::from_static_str("{message}");"# + ) + .into_diagnostic()?; + } + + std::fs::write(module.file_name, output).into_diagnostic()?; + + Ok(()) + } + + pub type ErrorName = String; + + #[derive(Debug, Clone)] + pub struct ExtractedError { + pub message: String, + } + + #[derive(Debug, Clone)] + pub struct NamedError { + pub name: ErrorName, + pub message: String, + } + + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + pub struct ErrorModule { + pub file_name: &'static str, + pub array_name: &'static str, + pub is_crate_local: bool, + } +} diff --git a/crates/miden-objects/src/account/header.rs b/crates/miden-objects/src/account/header.rs index 1d6a14d802..8a2a19f9cd 100644 --- a/crates/miden-objects/src/account/header.rs +++ b/crates/miden-objects/src/account/header.rs @@ -1,8 +1,19 @@ use alloc::vec::Vec; use super::{Account, AccountId, Felt, PartialAccount, ZERO, hash_account}; -use crate::Word; +use crate::transaction::memory::{ + ACCT_CODE_COMMITMENT_OFFSET, + ACCT_DATA_MEM_SIZE, + ACCT_ID_AND_NONCE_OFFSET, + ACCT_ID_PREFIX_IDX, + ACCT_ID_SUFFIX_IDX, + ACCT_NONCE_IDX, + ACCT_STORAGE_COMMITMENT_OFFSET, + ACCT_VAULT_ROOT_OFFSET, + MemoryOffset, +}; use crate::utils::serde::{Deserializable, Serializable}; +use crate::{AccountError, WORD_SIZE, Word, WordError}; // ACCOUNT HEADER // ================================================================================================ @@ -45,6 +56,33 @@ impl AccountHeader { } } + /// Parses the account header data returned by the VM into individual account component + /// commitments. Returns a tuple of account ID, vault root, storage commitment, code + /// commitment, and nonce. + pub(crate) fn try_from_elements(elements: &[Felt]) -> Result { + if elements.len() != ACCT_DATA_MEM_SIZE { + return Err(AccountError::HeaderDataIncorrectLength { + actual: elements.len(), + expected: ACCT_DATA_MEM_SIZE, + }); + } + + let id = AccountId::try_from([ + elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_PREFIX_IDX], + elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_SUFFIX_IDX], + ]) + .map_err(AccountError::FinalAccountHeaderIdParsingFailed)?; + let nonce = elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_NONCE_IDX]; + let vault_root = parse_word(elements, ACCT_VAULT_ROOT_OFFSET) + .expect("we should have sliced off exactly 4 bytes"); + let storage_commitment = parse_word(elements, ACCT_STORAGE_COMMITMENT_OFFSET) + .expect("we should have sliced off exactly 4 bytes"); + let code_commitment = parse_word(elements, ACCT_CODE_COMMITMENT_OFFSET) + .expect("we should have sliced off exactly 4 bytes"); + + Ok(AccountHeader::new(id, nonce, vault_root, storage_commitment, code_commitment)) + } + // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- @@ -177,6 +215,14 @@ impl Deserializable for AccountHeader { } } +// HELPER FUNCTIONS +// ================================================================================================ + +/// Creates a new `Word` instance from the slice of `Felt`s using provided offset. +fn parse_word(data: &[Felt], offset: MemoryOffset) -> Result { + Word::try_from(&data[offset as usize..offset as usize + WORD_SIZE]) +} + // TESTS // ================================================================================================ diff --git a/crates/miden-objects/src/block/proposed_block.rs b/crates/miden-objects/src/block/proposed_block.rs index 7e4b6dbc62..800c4239a3 100644 --- a/crates/miden-objects/src/block/proposed_block.rs +++ b/crates/miden-objects/src/block/proposed_block.rs @@ -16,6 +16,7 @@ use crate::block::block_inputs::BlockInputs; use crate::block::nullifier_tree::{NullifierWitness, PartialNullifierTree}; use crate::block::{ AccountUpdateWitness, + BlockBody, BlockHeader, BlockNoteIndex, BlockNoteTree, @@ -24,7 +25,13 @@ use crate::block::{ }; use crate::errors::ProposedBlockError; use crate::note::{NoteId, Nullifier}; -use crate::transaction::{InputNoteCommitment, OutputNote, PartialBlockchain, TransactionHeader}; +use crate::transaction::{ + InputNoteCommitment, + OutputNote, + PartialBlockchain, + TransactionHeader, + TransactionKernel, +}; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -442,6 +449,73 @@ impl ProposedBlock { // STATE MUTATORS // -------------------------------------------------------------------------------------------- + /// Builds a [`BlockHeader`] and [`BlockBody`] by computing the following from the state + /// updates encapsulated by the provided [`ProposedBlock`]: + /// - the account root; + /// - the nullifier root; + /// - the note root; + /// - the transaction commitment; and + /// - the chain commitment. + /// + /// The returned block header contains the same validator public key as the previous block, as + /// provided by the proposed block. + pub fn into_header_and_body(self) -> Result<(BlockHeader, BlockBody), ProposedBlockError> { + // Get fields from the proposed block before it is consumed. + let block_num = self.block_num(); + let timestamp = self.timestamp(); + let prev_block_header = self.prev_block_header().clone(); + + // Insert the state commitments of updated accounts into the account tree to compute its new + // root. + let new_account_root = self.compute_account_root()?; + + // Insert the created nullifiers into the nullifier tree to compute its new root. + let new_nullifier_root = self.compute_nullifier_root()?; + + // Compute the root of the block note tree. + let note_tree = self.compute_block_note_tree(); + let note_root = note_tree.root(); + + // Insert the previous block header into the block partial blockchain to get the new chain + // commitment. + // TODO: Consider avoiding the partial blockchain clone by constructing `BlockBody` from its + // raw parts, which does not require the partial blockchain. + let new_chain_commitment = self.compute_chain_commitment(); + + // Construct the block body from the proposed block. + let body = BlockBody::from(self); + + // Construct the header. + let tx_commitment = body.transaction_commitment(); + let prev_block_commitment = prev_block_header.commitment(); + + // For now we copy the parameters of the previous header, which means the parameters set on + // the genesis block will be passed through. Eventually, the contained base fees will be + // updated based on the demand in the currently proposed block. + let fee_parameters = prev_block_header.fee_parameters().clone(); + + // Currently undefined and reserved for future use. + // See miden-base/1155. + let version = 0; + let tx_kernel_commitment = TransactionKernel.to_commitment(); + let header = BlockHeader::new( + version, + prev_block_commitment, + block_num, + new_chain_commitment, + new_account_root, + new_nullifier_root, + note_root, + tx_commitment, + tx_kernel_commitment, + prev_block_header.validator_key().clone(), + fee_parameters, + timestamp, + ); + + Ok((header, body)) + } + /// Consumes self and returns the non-[`Copy`] parts of the block. #[allow(clippy::type_complexity)] pub fn into_parts( diff --git a/crates/miden-lib/src/errors/masm_error.rs b/crates/miden-objects/src/errors/masm_error.rs similarity index 90% rename from crates/miden-lib/src/errors/masm_error.rs rename to crates/miden-objects/src/errors/masm_error.rs index 3f3606151d..43c55f8797 100644 --- a/crates/miden-lib/src/errors/masm_error.rs +++ b/crates/miden-objects/src/errors/masm_error.rs @@ -1,6 +1,6 @@ use alloc::borrow::Cow; -use miden_objects::Felt; +use crate::Felt; /// A convenience wrapper around an error extracted from Miden Assembly source files. pub struct MasmError { @@ -27,7 +27,7 @@ impl MasmError { /// Returns the code of this error. pub fn code(&self) -> Felt { - miden_objects::assembly::mast::error_code_from_msg(&self.message) + crate::assembly::mast::error_code_from_msg(&self.message) } } diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-objects/src/errors/mod.rs similarity index 96% rename from crates/miden-objects/src/errors.rs rename to crates/miden-objects/src/errors/mod.rs index 151bdfadfc..2dfd04eaa2 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-objects/src/errors/mod.rs @@ -5,8 +5,8 @@ use core::error::Error; use miden_assembly::Report; use miden_assembly::diagnostics::reporting::PrintDiagnostic; -use miden_core::Felt; use miden_core::mast::MastForestError; +use miden_core::{EventId, Felt}; use miden_crypto::merkle::mmr::MmrError; use miden_crypto::utils::HexParseError; use miden_processor::DeserializationError; @@ -34,7 +34,7 @@ use crate::asset::AssetVaultKey; use crate::batch::BatchId; use crate::block::BlockNumber; use crate::note::{NoteAssets, NoteExecutionHint, NoteTag, NoteType, Nullifier}; -use crate::transaction::TransactionId; +use crate::transaction::{TransactionEventId, TransactionId}; use crate::{ ACCOUNT_UPDATE_MAX_SIZE, MAX_ACCOUNTS_PER_BATCH, @@ -44,6 +44,21 @@ use crate::{ MAX_OUTPUT_NOTES_PER_TX, }; +#[cfg(any(feature = "testing", test))] +mod masm_error; +#[cfg(any(feature = "testing", test))] +pub use masm_error::MasmError; + +/// The errors from the MASM code of the transaction kernel. +#[cfg(any(feature = "testing", test))] +#[rustfmt::skip] +pub mod tx_kernel; + +/// The errors from the MASM code of the Miden protocol library. +#[cfg(any(feature = "testing", test))] +#[rustfmt::skip] +pub mod protocol; + // ACCOUNT COMPONENT TEMPLATE ERROR // ================================================================================================ @@ -706,6 +721,28 @@ pub enum TransactionOutputError { AccountUpdateCommitment(Box), } +// TRANSACTION EVENT PARSING ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum TransactionEventError { + #[error("event id {0} is not a valid transaction event")] + InvalidTransactionEvent(EventId, Option<&'static str>), + #[error("event id {0} is not a transaction kernel event")] + NotTransactionEvent(EventId, Option<&'static str>), + #[error("event id {0} can only be emitted from the root context")] + NotRootContext(TransactionEventId), +} + +// TRANSACTION TRACE PARSING ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum TransactionTraceParsingError { + #[error("trace id {0} is an unknown transaction kernel trace")] + UnknownTransactionTrace(u32), +} + // PROVEN TRANSACTION ERROR // ================================================================================================ diff --git a/crates/miden-lib/src/errors/protocol.rs b/crates/miden-objects/src/errors/protocol.rs similarity index 82% rename from crates/miden-lib/src/errors/protocol.rs rename to crates/miden-objects/src/errors/protocol.rs index 6bff1565c0..ae97567ceb 100644 --- a/crates/miden-lib/src/errors/protocol.rs +++ b/crates/miden-objects/src/errors/protocol.rs @@ -20,13 +20,8 @@ pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE: MasmError = MasmError::from_stati /// Error Message: "unknown version in account ID" pub const ERR_ACCOUNT_ID_UNKNOWN_VERSION: MasmError = MasmError::from_static_str("unknown version in account ID"); -/// Error Message: "burn requires exactly 1 note asset" -pub const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("burn requires exactly 1 note asset"); - /// Error Message: "fungible asset build operation called with amount that exceeds the maximum allowed asset amount" pub const ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT: MasmError = MasmError::from_static_str("fungible asset build operation called with amount that exceeds the maximum allowed asset amount"); -/// Error Message: "distribute would cause the maximum supply to be exceeded" -pub const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED: MasmError = MasmError::from_static_str("distribute would cause the maximum supply to be exceeded"); /// Error Message: "failed to build the fungible asset because the provided faucet id is not from a fungible faucet" pub const ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the fungible asset because the provided faucet id is not from a fungible faucet"); @@ -38,8 +33,5 @@ pub const ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT: MasmError = MasmError::from_s /// Error Message: "the specified number of note inputs does not match the actual number" pub const ERR_NOTE_INVALID_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("the specified number of note inputs does not match the actual number"); -/// Error Message: "note sender is not the owner of the faucet who can mint assets" -pub const ERR_ONLY_OWNER_CAN_MINT: MasmError = MasmError::from_static_str("note sender is not the owner of the faucet who can mint assets"); - /// Error Message: "number of note inputs exceeded the maximum limit of 1024" pub const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 1024"); diff --git a/crates/miden-lib/src/errors/tx_kernel.rs b/crates/miden-objects/src/errors/tx_kernel.rs similarity index 100% rename from crates/miden-lib/src/errors/tx_kernel.rs rename to crates/miden-objects/src/errors/tx_kernel.rs diff --git a/crates/miden-objects/src/lib.rs b/crates/miden-objects/src/lib.rs index 6a803494af..c84363f82a 100644 --- a/crates/miden-objects/src/lib.rs +++ b/crates/miden-objects/src/lib.rs @@ -11,14 +11,15 @@ pub mod address; pub mod asset; pub mod batch; pub mod block; +pub mod errors; pub mod note; +mod protocol; pub mod transaction; #[cfg(any(feature = "testing", test))] pub mod testing; mod constants; -mod errors; // RE-EXPORTS // ================================================================================================ @@ -47,16 +48,20 @@ pub use errors::{ StorageMapError, StorageSlotNameError, TokenSymbolError, + TransactionEventError, TransactionInputError, TransactionOutputError, TransactionScriptError, + TransactionTraceParsingError, }; pub use miden_core::mast::{MastForest, MastNodeId}; pub use miden_core::prettier::PrettyPrint; pub use miden_core::{EMPTY_WORD, Felt, FieldElement, ONE, StarkField, WORD_SIZE, ZERO}; +pub use miden_core_lib::CoreLibrary; pub use miden_crypto::hash::rpo::Rpo256 as Hasher; pub use miden_crypto::word; pub use miden_crypto::word::{LexicographicWord, Word, WordError}; +pub use protocol::ProtocolLib; pub mod assembly { pub use miden_assembly::ast::{Module, ModuleKind, ProcedureName, QualifiedProcedureName}; @@ -103,7 +108,7 @@ pub mod utils { pub mod vm { pub use miden_assembly_syntax::ast::{AttributeSet, QualifiedProcedureName}; pub use miden_core::sys_events::SystemEvent; - pub use miden_core::{AdviceMap, Program, ProgramInfo}; + pub use miden_core::{AdviceMap, EventId, Program, ProgramInfo}; pub use miden_mast_package::{ MastArtifact, Package, diff --git a/crates/miden-objects/src/protocol.rs b/crates/miden-objects/src/protocol.rs new file mode 100644 index 0000000000..37d3e7569e --- /dev/null +++ b/crates/miden-objects/src/protocol.rs @@ -0,0 +1,70 @@ +use alloc::sync::Arc; + +use crate::assembly::Library; +use crate::assembly::mast::MastForest; +use crate::utils::serde::Deserializable; +use crate::utils::sync::LazyLock; + +// CONSTANTS +// ================================================================================================ + +const PROTOCOL_LIB_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/assets/protocol.masl")); + +// PROTOCOL LIBRARY +// ================================================================================================ + +#[derive(Clone)] +pub struct ProtocolLib(Library); + +impl ProtocolLib { + /// Returns a reference to the [`MastForest`] of the inner [`Library`]. + pub fn mast_forest(&self) -> &Arc { + self.0.mast_forest() + } +} + +impl AsRef for ProtocolLib { + fn as_ref(&self) -> &Library { + &self.0 + } +} + +impl From for Library { + fn from(value: ProtocolLib) -> Self { + value.0 + } +} + +impl Default for ProtocolLib { + fn default() -> Self { + static PROTOCOL_LIB: LazyLock = LazyLock::new(|| { + let contents = Library::read_from_bytes(PROTOCOL_LIB_BYTES) + .expect("protocol lib masl should be well-formed"); + ProtocolLib(contents) + }); + PROTOCOL_LIB.clone() + } +} + +// TESTS +// ================================================================================================ + +// NOTE: Most protocol-related tests can be found in miden-testing. +#[cfg(all(test, feature = "std"))] +mod tests { + use super::ProtocolLib; + use crate::assembly::Path; + + #[test] + fn test_compile() { + let path = Path::new("::miden::active_account::get_id"); + let miden = ProtocolLib::default(); + let exists = miden.0.module_infos().any(|module| { + module + .procedures() + .any(|(_, proc)| module.path().join(&proc.name).as_path() == path) + }); + + assert!(exists); + } +} diff --git a/crates/miden-lib/src/testing/mock_util_lib.rs b/crates/miden-objects/src/testing/mock_util_lib.rs similarity index 80% rename from crates/miden-lib/src/testing/mock_util_lib.rs rename to crates/miden-objects/src/testing/mock_util_lib.rs index c9d9e2c951..17535daa70 100644 --- a/crates/miden-lib/src/testing/mock_util_lib.rs +++ b/crates/miden-objects/src/testing/mock_util_lib.rs @@ -1,7 +1,8 @@ -use miden_objects::assembly::Library; -use miden_objects::utils::sync::LazyLock; +use miden_assembly::diagnostics::NamedSource; -use crate::utils::CodeBuilder; +use crate::assembly::Library; +use crate::transaction::TransactionKernel; +use crate::utils::sync::LazyLock; const MOCK_UTIL_LIBRARY_CODE: &str = " use miden::output_note @@ -35,10 +36,9 @@ const MOCK_UTIL_LIBRARY_CODE: &str = " "; static MOCK_UTIL_LIBRARY: LazyLock = LazyLock::new(|| { - CodeBuilder::new() - .compile_component_code("mock::util", MOCK_UTIL_LIBRARY_CODE) + TransactionKernel::assembler() + .assemble_library([NamedSource::new("mock::util", MOCK_UTIL_LIBRARY_CODE)]) .expect("mock util library should be valid") - .into_library() }); /// Returns the mock test [`Library`] under the `mock::util` namespace. diff --git a/crates/miden-objects/src/testing/mod.rs b/crates/miden-objects/src/testing/mod.rs index ca107d73f3..9b04908a64 100644 --- a/crates/miden-objects/src/testing/mod.rs +++ b/crates/miden-objects/src/testing/mod.rs @@ -6,6 +6,7 @@ pub mod asset; pub mod block; pub mod block_note_tree; pub mod constants; +pub mod mock_util_lib; pub mod noop_auth_component; pub mod note; pub mod partial_blockchain; diff --git a/crates/miden-lib/src/transaction/inputs.rs b/crates/miden-objects/src/transaction/kernel/advice_inputs.rs similarity index 96% rename from crates/miden-lib/src/transaction/inputs.rs rename to crates/miden-objects/src/transaction/kernel/advice_inputs.rs index 79d6f4255b..c536f6e188 100644 --- a/crates/miden-lib/src/transaction/inputs.rs +++ b/crates/miden-objects/src/transaction/kernel/advice_inputs.rs @@ -1,17 +1,22 @@ use alloc::vec::Vec; -use miden_objects::account::{AccountHeader, AccountId, PartialAccount}; -use miden_objects::asset::AssetWitness; -use miden_objects::block::account_tree::AccountWitness; -use miden_objects::crypto::SequentialCommit; -use miden_objects::crypto::merkle::InnerNodeInfo; -use miden_objects::crypto::merkle::smt::SmtProof; -use miden_objects::transaction::{AccountInputs, InputNote, PartialBlockchain, TransactionInputs}; -use miden_objects::vm::AdviceInputs; -use miden_objects::{EMPTY_WORD, Felt, FieldElement, Word, ZERO}; use miden_processor::AdviceMutation; -use super::TransactionKernel; +use crate::account::{AccountHeader, AccountId, PartialAccount}; +use crate::asset::AssetWitness; +use crate::block::account_tree::AccountWitness; +use crate::crypto::SequentialCommit; +use crate::crypto::merkle::InnerNodeInfo; +use crate::crypto::merkle::smt::SmtProof; +use crate::transaction::{ + AccountInputs, + InputNote, + PartialBlockchain, + TransactionInputs, + TransactionKernel, +}; +use crate::vm::AdviceInputs; +use crate::{EMPTY_WORD, Felt, FieldElement, Word, ZERO}; // TRANSACTION ADVICE INPUTS // ================================================================================================ @@ -400,7 +405,7 @@ impl TransactionAdviceInputs { self.0.stack.extend(iter); } - /// Extends the [`MerkleStore`](miden_objects::crypto::merkle::MerkleStore) with the given + /// Extends the [`MerkleStore`](crate::crypto::merkle::MerkleStore) with the given /// nodes. fn extend_merkle_store(&mut self, iter: impl Iterator) { self.0.store.extend(iter); diff --git a/crates/miden-lib/src/transaction/memory.rs b/crates/miden-objects/src/transaction/kernel/memory.rs similarity index 100% rename from crates/miden-lib/src/transaction/memory.rs rename to crates/miden-objects/src/transaction/kernel/memory.rs diff --git a/crates/miden-lib/src/transaction/mod.rs b/crates/miden-objects/src/transaction/kernel/mod.rs similarity index 91% rename from crates/miden-lib/src/transaction/mod.rs rename to crates/miden-objects/src/transaction/kernel/mod.rs index 76b28ef97e..d759c822e5 100644 --- a/crates/miden-lib/src/transaction/mod.rs +++ b/crates/miden-objects/src/transaction/kernel/mod.rs @@ -3,43 +3,31 @@ use alloc::sync::Arc; use alloc::vec::Vec; use miden_core_lib::CoreLibrary; -use miden_objects::account::AccountId; + +use crate::account::{AccountHeader, AccountId}; #[cfg(any(feature = "testing", test))] -use miden_objects::assembly::Library; -use miden_objects::assembly::debuginfo::SourceManagerSync; -use miden_objects::assembly::{Assembler, DefaultSourceManager, KernelLibrary}; -use miden_objects::asset::FungibleAsset; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::SequentialCommit; -use miden_objects::transaction::{OutputNote, OutputNotes, TransactionInputs, TransactionOutputs}; -use miden_objects::utils::serde::Deserializable; -use miden_objects::utils::sync::LazyLock; -use miden_objects::vm::{AdviceInputs, Program, ProgramInfo, StackInputs, StackOutputs}; -use miden_objects::{Felt, Hasher, TransactionOutputError, Word}; - -use super::MidenLib; +use crate::assembly::Library; +use crate::assembly::debuginfo::SourceManagerSync; +use crate::assembly::{Assembler, DefaultSourceManager, KernelLibrary}; +use crate::asset::FungibleAsset; +use crate::block::BlockNumber; +use crate::crypto::SequentialCommit; +use crate::protocol::ProtocolLib; +use crate::transaction::{OutputNote, OutputNotes, TransactionInputs, TransactionOutputs}; +use crate::utils::serde::Deserializable; +use crate::utils::sync::LazyLock; +use crate::vm::{AdviceInputs, Program, ProgramInfo, StackInputs, StackOutputs}; +use crate::{Felt, Hasher, TransactionOutputError, Word}; + +mod procedures; pub mod memory; +mod advice_inputs; mod tx_event_id; -pub use tx_event_id::{EventId, TransactionEventId}; - -mod inputs; -pub use inputs::TransactionAdviceInputs; - -mod outputs; -pub use outputs::{ - ACCOUNT_UPDATE_COMMITMENT_WORD_IDX, - EXPIRATION_BLOCK_ELEMENT_IDX, - FEE_ASSET_WORD_IDX, - OUTPUT_NOTES_COMMITMENT_WORD_IDX, - parse_final_account_header, -}; - -pub use crate::errors::{TransactionEventError, TransactionTraceParsingError}; -mod kernel_procedures; -use kernel_procedures::KERNEL_PROCEDURES; +pub use advice_inputs::TransactionAdviceInputs; +pub use tx_event_id::TransactionEventId; // CONSTANTS // ================================================================================================ @@ -78,7 +66,7 @@ impl TransactionKernel { // -------------------------------------------------------------------------------------------- /// Array of kernel procedures. - pub const PROCEDURES: &'static [Word] = &KERNEL_PROCEDURES; + pub const PROCEDURES: &'static [Word] = &procedures::KERNEL_PROCEDURES; // KERNEL SOURCE CODE // -------------------------------------------------------------------------------------------- @@ -155,7 +143,7 @@ impl TransactionKernel { Assembler::with_kernel(source_manager, Self::kernel()) .with_dynamic_library(CoreLibrary::default()) .expect("failed to load std-lib") - .with_dynamic_library(MidenLib::default()) + .with_dynamic_library(ProtocolLib::default()) .expect("failed to load miden-lib") } @@ -272,19 +260,19 @@ impl TransactionKernel { stack: &StackOutputs, // FIXME TODO add an extension trait for this one ) -> Result<(Word, Word, FungibleAsset, BlockNumber), TransactionOutputError> { let output_notes_commitment = stack - .get_stack_word_be(OUTPUT_NOTES_COMMITMENT_WORD_IDX * 4) + .get_stack_word_be(TransactionOutputs::OUTPUT_NOTES_COMMITMENT_WORD_IDX * 4) .expect("output_notes_commitment (first word) missing"); let account_update_commitment = stack - .get_stack_word_be(ACCOUNT_UPDATE_COMMITMENT_WORD_IDX * 4) + .get_stack_word_be(TransactionOutputs::ACCOUNT_UPDATE_COMMITMENT_WORD_IDX * 4) .expect("account_update_commitment (second word) missing"); let fee = stack - .get_stack_word_be(FEE_ASSET_WORD_IDX * 4) + .get_stack_word_be(TransactionOutputs::FEE_ASSET_WORD_IDX * 4) .expect("fee_asset (third word) missing"); let expiration_block_num = stack - .get_stack_item(EXPIRATION_BLOCK_ELEMENT_IDX) + .get_stack_item(TransactionOutputs::EXPIRATION_BLOCK_ELEMENT_IDX) .expect("tx_expiration_block_num (element on index 12) missing"); let expiration_block_num = u32::try_from(expiration_block_num.as_int()) @@ -357,7 +345,7 @@ impl TransactionKernel { .get(&final_account_commitment) .ok_or(TransactionOutputError::FinalAccountCommitmentMissingInAdviceMap)?; - let account = parse_final_account_header(final_account_data) + let account = AccountHeader::try_from_elements(final_account_data) .map_err(TransactionOutputError::FinalAccountHeaderParseFailure)?; // validate output notes @@ -458,8 +446,8 @@ pub(crate) mod source_manager_ext { use std::vec::Vec; use std::{fs, io}; - use miden_objects::assembly::SourceManager; - use miden_objects::assembly::debuginfo::SourceManagerExt; + use crate::assembly::SourceManager; + use crate::assembly::debuginfo::SourceManagerExt; /// Loads all files with a .masm extension in the `asm` directory into the provided source /// manager. diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-objects/src/transaction/kernel/procedures.rs similarity index 99% rename from crates/miden-lib/src/transaction/kernel_procedures.rs rename to crates/miden-objects/src/transaction/kernel/procedures.rs index 03c50d542f..3ecae0bb61 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-objects/src/transaction/kernel/procedures.rs @@ -1,6 +1,6 @@ // This file is generated by build.rs, do not modify -use miden_objects::{Word, word}; +use crate::{Word, word}; // KERNEL PROCEDURES // ================================================================================================ diff --git a/crates/miden-lib/src/transaction/tx_event_id.rs b/crates/miden-objects/src/transaction/kernel/tx_event_id.rs similarity index 99% rename from crates/miden-lib/src/transaction/tx_event_id.rs rename to crates/miden-objects/src/transaction/kernel/tx_event_id.rs index 1c7a3dcb58..da233ae3ef 100644 --- a/crates/miden-lib/src/transaction/tx_event_id.rs +++ b/crates/miden-objects/src/transaction/kernel/tx_event_id.rs @@ -1,8 +1,8 @@ use core::fmt; -pub use miden_core::EventId; +use miden_core::EventId; -use super::TransactionEventError; +use crate::errors::TransactionEventError; // CONSTANTS // ================================================================================================ diff --git a/crates/miden-objects/src/transaction/mod.rs b/crates/miden-objects/src/transaction/mod.rs index 916275abd2..feef2fc878 100644 --- a/crates/miden-objects/src/transaction/mod.rs +++ b/crates/miden-objects/src/transaction/mod.rs @@ -5,6 +5,7 @@ use super::{Felt, Hasher, WORD_SIZE, Word, ZERO}; mod executed_tx; mod inputs; +mod kernel; mod ordered_transactions; mod outputs; mod partial_blockchain; @@ -16,6 +17,7 @@ mod tx_summary; pub use executed_tx::{ExecutedTransaction, TransactionMeasurements}; pub use inputs::{AccountInputs, InputNote, InputNotes, ToInputNoteCommitments, TransactionInputs}; +pub use kernel::{TransactionAdviceInputs, TransactionEventId, TransactionKernel, memory}; pub use ordered_transactions::OrderedTransactionHeaders; pub use outputs::{OutputNote, OutputNotes, TransactionOutputs}; pub use partial_blockchain::PartialBlockchain; diff --git a/crates/miden-objects/src/transaction/outputs.rs b/crates/miden-objects/src/transaction/outputs.rs index f45e313a54..410bbf5449 100644 --- a/crates/miden-objects/src/transaction/outputs.rs +++ b/crates/miden-objects/src/transaction/outputs.rs @@ -43,6 +43,23 @@ pub struct TransactionOutputs { pub expiration_block_num: BlockNumber, } +impl TransactionOutputs { + // CONSTANTS + // -------------------------------------------------------------------------------------------- + + /// The index of the word at which the final account nonce is stored on the output stack. + pub const OUTPUT_NOTES_COMMITMENT_WORD_IDX: usize = 0; + + /// The index of the word at which the account update commitment is stored on the output stack. + pub const ACCOUNT_UPDATE_COMMITMENT_WORD_IDX: usize = 1; + + /// The index of the word at which the fee asset is stored on the output stack. + pub const FEE_ASSET_WORD_IDX: usize = 2; + + /// The index of the item at which the expiration block height is stored on the output stack. + pub const EXPIRATION_BLOCK_ELEMENT_IDX: usize = 12; +} + impl Serializable for TransactionOutputs { fn write_into(&self, target: &mut W) { self.account.write_into(target); diff --git a/crates/miden-testing/src/executor.rs b/crates/miden-testing/src/executor.rs index 9cc0800179..8f3689f671 100644 --- a/crates/miden-testing/src/executor.rs +++ b/crates/miden-testing/src/executor.rs @@ -45,7 +45,7 @@ impl CodeExecutor { use alloc::borrow::ToOwned; use alloc::sync::Arc; - use miden_lib::utils::CodeBuilder; + use miden_lib::code_builder::CodeBuilder; use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; use miden_objects::assembly::{DefaultSourceManager, SourceManagerSync}; @@ -88,7 +88,7 @@ impl CodeExecutor { #[cfg(test)] impl CodeExecutor { pub fn with_default_host() -> Self { - use miden_lib::transaction::TransactionKernel; + use miden_objects::transaction::TransactionKernel; let mut host = DefaultHost::default(); diff --git a/crates/miden-testing/src/kernel_tests/block/header_errors.rs b/crates/miden-testing/src/kernel_tests/block/header_errors.rs index 21c4d9f66c..b79d606ac1 100644 --- a/crates/miden-testing/src/kernel_tests/block/header_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/header_errors.rs @@ -2,7 +2,6 @@ use alloc::vec::Vec; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::block::build_block; use miden_lib::testing::account_component::{IncrNonceAuthComponent, MockAccountComponent}; use miden_lib::testing::mock_account::MockAccountExt; use miden_objects::account::delta::AccountUpdateDetails; @@ -104,7 +103,7 @@ async fn block_building_fails_on_stale_account_witnesses() -> anyhow::Result<()> let proposed_block0 = ProposedBlock::new(invalid_account_tree_block_inputs, batches.clone()) .context("failed to propose block 0")?; - let error = build_block(proposed_block0).unwrap_err(); + let error = proposed_block0.into_header_and_body().unwrap_err(); assert_matches!( error, @@ -141,7 +140,7 @@ async fn block_building_fails_on_stale_nullifier_witnesses() -> anyhow::Result<( let proposed_block2 = ProposedBlock::new(invalid_nullifier_tree_block_inputs, batches.clone()) .context("failed to propose block 2")?; - let error = build_block(proposed_block2).unwrap_err(); + let error = proposed_block2.into_header_and_body().unwrap_err(); assert_matches!( error, @@ -187,7 +186,7 @@ async fn block_building_fails_on_account_tree_root_mismatch() -> anyhow::Result< let proposed_block1 = ProposedBlock::new(stale_account_witness_block_inputs, batches.clone()) .context("failed to propose block 1")?; - let error = build_block(proposed_block1).unwrap_err(); + let error = proposed_block1.into_header_and_body().unwrap_err(); assert_matches!( error, @@ -235,7 +234,7 @@ async fn block_building_fails_on_nullifier_tree_root_mismatch() -> anyhow::Resul let proposed_block3 = ProposedBlock::new(invalid_nullifier_witness_block_inputs, batches) .context("failed to propose block 3")?; - let error = build_block(proposed_block3).unwrap_err(); + let error = proposed_block3.into_header_and_body().unwrap_err(); assert_matches!( error, @@ -329,7 +328,7 @@ async fn block_building_fails_on_creating_account_with_existing_account_id_prefi let block = mock_chain.propose_block(batches).context("failed to propose block")?; - let err = build_block(block).unwrap_err(); + let err = block.into_header_and_body().unwrap_err(); // This should fail when we try to _insert_ the same two prefixes into the partial tree. assert_matches!( @@ -426,7 +425,7 @@ async fn block_building_fails_on_creating_account_with_duplicate_account_id_pref let block = mock_chain.propose_block(batches).context("failed to propose block")?; - let err = build_block(block).unwrap_err(); + let err = block.into_header_and_body().unwrap_err(); // This should fail when we try to _track_ the same two prefixes in the partial tree. assert_matches!( diff --git a/crates/miden-testing/src/kernel_tests/block/utils.rs b/crates/miden-testing/src/kernel_tests/block/utils.rs index da42b7b821..122b4d9884 100644 --- a/crates/miden-testing/src/kernel_tests/block/utils.rs +++ b/crates/miden-testing/src/kernel_tests/block/utils.rs @@ -1,6 +1,6 @@ use std::vec::Vec; -use miden_lib::utils::CodeBuilder; +use miden_lib::code_builder::CodeBuilder; use miden_objects::account::AccountId; use miden_objects::batch::ProvenBatch; use miden_objects::block::BlockNumber; diff --git a/crates/miden-testing/src/kernel_tests/tx/mod.rs b/crates/miden-testing/src/kernel_tests/tx/mod.rs index 0c6eff44a8..5ceaf76464 100644 --- a/crates/miden-testing/src/kernel_tests/tx/mod.rs +++ b/crates/miden-testing/src/kernel_tests/tx/mod.rs @@ -1,7 +1,16 @@ use alloc::string::String; use anyhow::Context; -use miden_lib::transaction::memory::{ +use miden_objects::account::{Account, AccountId}; +use miden_objects::asset::{Asset, FungibleAsset}; +use miden_objects::note::{Note, NoteType}; +use miden_objects::testing::account_id::{ + ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, + ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, + ACCOUNT_ID_SENDER, +}; +use miden_objects::testing::storage::prepare_assets; +use miden_objects::transaction::memory::{ self, MemoryOffset, NOTE_MEM_SIZE, @@ -13,15 +22,6 @@ use miden_lib::transaction::memory::{ OUTPUT_NOTE_RECIPIENT_OFFSET, OUTPUT_NOTE_SECTION_OFFSET, }; -use miden_objects::account::{Account, AccountId}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::{ - ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, - ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, - ACCOUNT_ID_SENDER, -}; -use miden_objects::testing::storage::prepare_assets; use miden_objects::vm::StackInputs; use miden_objects::{Felt, Word, ZERO}; use miden_processor::ContextId; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 56d885ad53..70ac196435 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -4,20 +4,9 @@ use std::collections::BTreeMap; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::errors::tx_kernel::{ - ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO, - ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO, - ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE, - ERR_ACCOUNT_ID_UNKNOWN_VERSION, - ERR_ACCOUNT_NONCE_AT_MAX, - ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE, - ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME, - ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED, -}; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::CodeBuilder; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{ Account, @@ -39,6 +28,16 @@ use miden_objects::account::{ use miden_objects::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette}; use miden_objects::assembly::{DefaultSourceManager, Library}; use miden_objects::asset::{Asset, FungibleAsset}; +use miden_objects::errors::tx_kernel::{ + ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO, + ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO, + ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE, + ERR_ACCOUNT_ID_UNKNOWN_VERSION, + ERR_ACCOUNT_NONCE_AT_MAX, + ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE, + ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME, + ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED, +}; use miden_objects::note::NoteType; use miden_objects::testing::account_id::{ ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET, @@ -49,7 +48,7 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_SENDER, }; use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; -use miden_objects::transaction::OutputNote; +use miden_objects::transaction::{OutputNote, TransactionKernel}; use miden_objects::utils::sync::LazyLock; use miden_objects::{LexicographicWord, StarkField}; use miden_processor::{ExecutionError, Word}; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index df41aaaa36..7bc4712f73 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -3,8 +3,8 @@ use std::collections::BTreeMap; use std::string::String; use anyhow::Context; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::utils::CodeBuilder; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{ Account, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs index 133f437676..b7d1492744 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs @@ -5,7 +5,6 @@ use assert_matches::assert_matches; use miden_lib::note::{NoteConsumptionStatus, WellKnownNote, create_p2id_note, create_p2ide_note}; use miden_lib::testing::mock_account::MockAccountExt; use miden_lib::testing::note::NoteBuilder; -use miden_lib::transaction::TransactionKernel; use miden_objects::account::{Account, AccountId}; use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::crypto::rand::FeltRng; @@ -25,7 +24,7 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::transaction::{InputNote, OutputNote}; +use miden_objects::transaction::{InputNote, OutputNote, TransactionKernel}; use miden_objects::{Felt, StarkField, Word, ZERO}; use miden_processor::ExecutionError; use miden_processor::crypto::RpoRandomCoin; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index dd666fb778..5b48e12b04 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -1,12 +1,12 @@ use alloc::string::String; use anyhow::Context; -use miden_lib::errors::tx_kernel::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::utils::CodeBuilder; use miden_objects::account::Account; use miden_objects::asset::FungibleAsset; use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_objects::errors::tx_kernel::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; use miden_objects::note::{ Note, NoteAssets, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs index 9629f318b5..cb6330e6f2 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs @@ -1,20 +1,20 @@ use assert_matches::assert_matches; -use miden_lib::errors::tx_kernel::{ +use miden_objects::account::AccountId; +use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; +use miden_objects::errors::tx_kernel::{ ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED, ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET, ERR_VAULT_NON_FUNGIBLE_ASSET_ALREADY_EXISTS, ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND, }; -use miden_lib::transaction::memory; -use miden_objects::account::AccountId; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; use miden_objects::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET_1, }; use miden_objects::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; +use miden_objects::transaction::memory; use miden_objects::{AssetVaultError, Felt, ONE, Word, ZERO}; use crate::kernel_tests::tx::ExecutionOutputExt; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs index 64d93e5c0b..6aced0b971 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs @@ -1,11 +1,11 @@ use anyhow::Context; use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::MasmError; -use miden_lib::errors::tx_kernel::ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::account_component::{ConditionalAuthComponent, ERR_WRONG_ARGS_MSG}; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{Account, AccountBuilder}; +use miden_objects::errors::MasmError; +use miden_objects::errors::tx_kernel::ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; use miden_objects::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; use miden_objects::{Felt, ONE}; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 3c009ff167..e94b5c108e 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -1,25 +1,19 @@ use alloc::string::ToString; use alloc::vec::Vec; -use miden_lib::errors::tx_kernel::{ +use miden_lib::code_builder::CodeBuilder; +use miden_lib::testing::mock_account::MockAccountExt; +use miden_lib::testing::note::NoteBuilder; +use miden_objects::Word; +use miden_objects::account::{Account, AccountDelta, AccountStorageDelta, AccountVaultDelta}; +use miden_objects::asset::{Asset, FungibleAsset}; +use miden_objects::errors::tx_kernel::{ ERR_ACCOUNT_DELTA_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED, ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY, ERR_EPILOGUE_NONCE_CANNOT_BE_0, ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME, ERR_TX_INVALID_EXPIRATION_DELTA, }; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::testing::note::NoteBuilder; -use miden_lib::transaction::EXPIRATION_BLOCK_ELEMENT_IDX; -use miden_lib::transaction::memory::{ - NOTE_MEM_SIZE, - OUTPUT_NOTE_ASSET_COMMITMENT_OFFSET, - OUTPUT_NOTE_SECTION_OFFSET, -}; -use miden_lib::utils::CodeBuilder; -use miden_objects::Word; -use miden_objects::account::{Account, AccountDelta, AccountStorageDelta, AccountVaultDelta}; -use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::note::{NoteTag, NoteType}; use miden_objects::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, @@ -28,7 +22,12 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_SENDER, }; use miden_objects::testing::storage::MOCK_VALUE_SLOT0; -use miden_objects::transaction::{OutputNote, OutputNotes}; +use miden_objects::transaction::memory::{ + NOTE_MEM_SIZE, + OUTPUT_NOTE_ASSET_COMMITMENT_OFFSET, + OUTPUT_NOTE_SECTION_OFFSET, +}; +use miden_objects::transaction::{OutputNote, OutputNotes, TransactionOutputs}; use miden_processor::{Felt, ONE}; use super::{ZERO, create_mock_notes_procedure}; @@ -352,7 +351,9 @@ async fn test_block_expiration_height_monotonically_decreases() -> anyhow::Resul let expected_expiry = v1.min(v2) + tx_context.tx_inputs().block_header().block_num().as_u64(); assert_eq!( - exec_output.get_stack_element(EXPIRATION_BLOCK_ELEMENT_IDX).as_int(), + exec_output + .get_stack_element(TransactionOutputs::EXPIRATION_BLOCK_ELEMENT_IDX) + .as_int(), expected_expiry ); } @@ -410,7 +411,9 @@ async fn test_no_expiration_delta_set() -> anyhow::Result<()> { // Default value should be equal to u32::MAX, set in the prologue assert_eq!( - exec_output.get_stack_element(EXPIRATION_BLOCK_ELEMENT_IDX).as_int() as u32, + exec_output + .get_stack_element(TransactionOutputs::EXPIRATION_BLOCK_ELEMENT_IDX) + .as_int() as u32, u32::MAX ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index 605faec694..505454b242 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -1,15 +1,7 @@ use alloc::sync::Arc; -use miden_lib::errors::tx_kernel::{ - ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT, - ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED, - ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND, - ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, - ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, - ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, -}; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -21,6 +13,14 @@ use miden_objects::account::{ }; use miden_objects::assembly::DefaultSourceManager; use miden_objects::asset::{FungibleAsset, NonFungibleAsset}; +use miden_objects::errors::tx_kernel::{ + ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT, + ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED, + ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND, + ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, + ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, + ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, +}; use miden_objects::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 1d4c81020a..8ea605ce37 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -2,25 +2,8 @@ use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; -use miden_lib::errors::tx_kernel::{ - ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT, - ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT, - ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED, -}; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::transaction::memory::{ - ACCOUNT_DATA_LENGTH, - ACCT_CODE_COMMITMENT_OFFSET, - ACCT_ID_AND_NONCE_OFFSET, - ACCT_PROCEDURES_SECTION_OFFSET, - ACCT_STORAGE_COMMITMENT_OFFSET, - ACCT_STORAGE_SLOTS_SECTION_OFFSET, - ACCT_VAULT_ROOT_OFFSET, - NATIVE_ACCOUNT_DATA_PTR, - NUM_ACCT_PROCEDURES_OFFSET, - NUM_ACCT_STORAGE_SLOTS_OFFSET, -}; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -33,11 +16,28 @@ use miden_objects::account::{ }; use miden_objects::assembly::DefaultSourceManager; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; +use miden_objects::errors::tx_kernel::{ + ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT, + ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT, + ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED, +}; use miden_objects::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, }; use miden_objects::testing::storage::STORAGE_LEAVES_2; +use miden_objects::transaction::memory::{ + ACCOUNT_DATA_LENGTH, + ACCT_CODE_COMMITMENT_OFFSET, + ACCT_ID_AND_NONCE_OFFSET, + ACCT_PROCEDURES_SECTION_OFFSET, + ACCT_STORAGE_COMMITMENT_OFFSET, + ACCT_STORAGE_SLOTS_SECTION_OFFSET, + ACCT_VAULT_ROOT_OFFSET, + NATIVE_ACCOUNT_DATA_PTR, + NUM_ACCT_PROCEDURES_OFFSET, + NUM_ACCT_STORAGE_SLOTS_OFFSET, +}; use miden_objects::{FieldElement, Word, ZERO}; use miden_processor::fast::ExecutionOutput; use miden_processor::{AdviceInputs, Felt}; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs index 6165978995..c04eae61b7 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs @@ -1,6 +1,6 @@ use alloc::string::String; -use miden_lib::utils::CodeBuilder; +use miden_lib::code_builder::CodeBuilder; use miden_objects::Word; use miden_objects::note::Note; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index 9ebd66d5be..a440d04fb0 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -2,8 +2,8 @@ //! //! Once lazy loading is enabled generally, it can be removed and/or integrated into other tests. +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; use miden_objects::LexicographicWord; use miden_objects::account::{AccountId, AccountStorage, StorageSlotDelta}; use miden_objects::asset::{Asset, FungibleAsset}; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index aac0489914..00b5643b12 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -3,10 +3,8 @@ use alloc::sync::Arc; use anyhow::Context; use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::MasmError; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::note::NoteBuilder; -use miden_lib::transaction::memory::ACTIVE_INPUT_NOTE_PTR; -use miden_lib::utils::CodeBuilder; use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::account::{AccountBuilder, AccountId}; use miden_objects::assembly::DefaultSourceManager; @@ -14,6 +12,7 @@ use miden_objects::assembly::diagnostics::miette::{self, miette}; use miden_objects::asset::FungibleAsset; use miden_objects::crypto::dsa::falcon512_rpo::SecretKey; use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_objects::errors::MasmError; use miden_objects::note::{ Note, NoteAssets, @@ -30,6 +29,7 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; +use miden_objects::transaction::memory::ACTIVE_INPUT_NOTE_PTR; use miden_objects::transaction::{OutputNote, TransactionArgs}; use miden_objects::{Felt, Word, ZERO}; use miden_processor::fast::ExecutionOutput; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index c0abe54d4f..361bca2311 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -2,24 +2,16 @@ use alloc::string::String; use alloc::vec::Vec; use anyhow::Context; -use miden_lib::errors::tx_kernel::{ - ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS, - ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT, -}; +use miden_lib::code_builder::CodeBuilder; use miden_lib::note::create_p2id_note; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::memory::{ - NOTE_MEM_SIZE, - NUM_OUTPUT_NOTES_PTR, - OUTPUT_NOTE_ASSETS_OFFSET, - OUTPUT_NOTE_METADATA_OFFSET, - OUTPUT_NOTE_RECIPIENT_OFFSET, - OUTPUT_NOTE_SECTION_OFFSET, -}; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{Account, AccountId}; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; use miden_objects::crypto::rand::RpoRandomCoin; +use miden_objects::errors::tx_kernel::{ + ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS, + ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT, +}; use miden_objects::note::{ Note, NoteAssets, @@ -43,6 +35,14 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_SENDER, }; use miden_objects::testing::constants::NON_FUNGIBLE_ASSET_DATA_2; +use miden_objects::transaction::memory::{ + NOTE_MEM_SIZE, + NUM_OUTPUT_NOTES_PTR, + OUTPUT_NOTE_ASSETS_OFFSET, + OUTPUT_NOTE_METADATA_OFFSET, + OUTPUT_NOTE_RECIPIENT_OFFSET, + OUTPUT_NOTE_SECTION_OFFSET, +}; use miden_objects::transaction::{OutputNote, OutputNotes}; use miden_objects::{Felt, Word, ZERO}; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 024bd98324..315565dc8f 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -3,15 +3,34 @@ use alloc::vec::Vec; use anyhow::Context; use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::tx_kernel::{ +use miden_lib::code_builder::CodeBuilder; +use miden_lib::testing::account_component::MockAccountComponent; +use miden_lib::testing::mock_account::MockAccountExt; +use miden_objects::account::{ + Account, + AccountBuilder, + AccountId, + AccountIdVersion, + AccountProcedureRoot, + AccountStorage, + AccountStorageMode, + AccountType, + StorageMap, + StorageSlot, + StorageSlotName, +}; +use miden_objects::asset::{FungibleAsset, NonFungibleAsset}; +use miden_objects::errors::tx_kernel::{ ERR_ACCOUNT_SEED_AND_COMMITMENT_DIGEST_MISMATCH, ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY, ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT, }; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::TransactionKernel; -use miden_lib::transaction::memory::{ +use miden_objects::testing::account_id::{ + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, + ACCOUNT_ID_SENDER, +}; +use miden_objects::testing::noop_auth_component::NoopAuthComponent; +use miden_objects::transaction::memory::{ ACCT_DB_ROOT_PTR, BLOCK_COMMITMENT_PTR, BLOCK_METADATA_PTR, @@ -61,27 +80,7 @@ use miden_lib::transaction::memory::{ VALIDATOR_KEY_COMMITMENT_PTR, VERIFICATION_BASE_FEE_IDX, }; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ - Account, - AccountBuilder, - AccountId, - AccountIdVersion, - AccountProcedureRoot, - AccountStorage, - AccountStorageMode, - AccountType, - StorageMap, - StorageSlot, - StorageSlotName, -}; -use miden_objects::asset::{FungibleAsset, NonFungibleAsset}; -use miden_objects::testing::account_id::{ - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, - ACCOUNT_ID_SENDER, -}; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::transaction::{ExecutedTransaction, TransactionArgs}; +use miden_objects::transaction::{ExecutedTransaction, TransactionArgs, TransactionKernel}; use miden_objects::{EMPTY_WORD, ONE, WORD_SIZE}; use miden_processor::fast::ExecutionOutput; use miden_processor::{AdviceInputs, Word}; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index b881592bfa..dc2753340c 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -5,11 +5,10 @@ use assert_matches::assert_matches; use miden_lib::AuthScheme; use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_lib::account::wallets::BasicWallet; +use miden_lib::code_builder::CodeBuilder; use miden_lib::note::create_p2id_note; use miden_lib::testing::account_component::IncrNonceAuthComponent; use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, @@ -53,6 +52,7 @@ use miden_objects::transaction::{ OutputNote, OutputNotes, TransactionArgs, + TransactionKernel, TransactionSummary, }; use miden_objects::{Felt, FieldElement, Hasher, ONE, Word}; diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index badceba12d..7245c71d34 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -3,7 +3,6 @@ use alloc::vec::Vec; use anyhow::Context; use miden_block_prover::LocalBlockProver; -use miden_lib::block::build_block; use miden_objects::MIN_PROOF_SECURITY_LEVEL; use miden_objects::account::auth::{AuthSecretKey, PublicKey}; use miden_objects::account::delta::AccountUpdateDetails; @@ -986,7 +985,7 @@ impl MockChain { /// Proves proposed block alongside a corresponding list of batches. pub fn prove_block(&self, proposed_block: ProposedBlock) -> anyhow::Result { - let (header, body) = build_block(proposed_block.clone())?; + let (header, body) = proposed_block.clone().into_header_and_body()?; let inputs = self.get_block_inputs(proposed_block.batches().as_slice())?; let block_proof = LocalBlockProver::new(MIN_PROOF_SECURITY_LEVEL).prove_dummy( proposed_block.batches().clone(), diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 196b96dd5a..a668db7dad 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -17,7 +17,6 @@ use miden_lib::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; use miden_lib::account::wallets::BasicWallet; use miden_lib::note::{create_p2id_note, create_p2ide_note, create_swap_note}; use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::transaction::TransactionKernel; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{ Account, @@ -49,7 +48,7 @@ use miden_objects::crypto::merkle::smt::Smt; use miden_objects::note::{Note, NoteDetails, NoteType}; use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; use miden_objects::testing::random_signer::RandomBlockSigner; -use miden_objects::transaction::{OrderedTransactionHeaders, OutputNote}; +use miden_objects::transaction::{OrderedTransactionHeaders, OutputNote, TransactionKernel}; use miden_objects::{Felt, FieldElement, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; use miden_processor::crypto::RpoRandomCoin; use rand::Rng; diff --git a/crates/miden-testing/src/mock_host.rs b/crates/miden-testing/src/mock_host.rs index 6273af9e1f..42216aa538 100644 --- a/crates/miden-testing/src/mock_host.rs +++ b/crates/miden-testing/src/mock_host.rs @@ -2,9 +2,9 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::CoreLibrary; -use miden_lib::transaction::{EventId, TransactionEventId}; -use miden_objects::Word; +use miden_objects::transaction::TransactionEventId; +use miden_objects::vm::EventId; +use miden_objects::{CoreLibrary, Word}; use miden_processor::{ AdviceMutation, AsyncHost, diff --git a/crates/miden-testing/src/tx_context/builder.rs b/crates/miden-testing/src/tx_context/builder.rs index f52012b99a..f33a6b9f9e 100644 --- a/crates/miden-testing/src/tx_context/builder.rs +++ b/crates/miden-testing/src/tx_context/builder.rs @@ -44,7 +44,7 @@ use crate::{MockChain, MockChainNote}; /// # use anyhow::Result; /// # use miden_testing::TransactionContextBuilder; /// # use miden_objects::{account::AccountBuilder,Felt, FieldElement}; -/// # use miden_lib::transaction::TransactionKernel; +/// # use miden_objects::transaction::TransactionKernel; /// # /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<()> { diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index 304b05c55f..7a6de221dc 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -3,8 +3,7 @@ use alloc::collections::{BTreeMap, BTreeSet}; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::CodeBuilder; +use miden_lib::code_builder::CodeBuilder; use miden_objects::account::{ Account, AccountId, @@ -26,6 +25,7 @@ use miden_objects::transaction::{ PartialBlockchain, TransactionArgs, TransactionInputs, + TransactionKernel, }; use miden_processor::fast::ExecutionOutput; use miden_processor::{ExecutionError, FutureMaybeSend, MastForest, MastForestStore, Word}; diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index 4cb36c1a75..f070d199f1 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -1,8 +1,8 @@ use alloc::string::String; use alloc::vec::Vec; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; use miden_objects::account::AccountId; use miden_objects::asset::Asset; use miden_objects::crypto::rand::FeltRng; diff --git a/crates/miden-testing/tests/auth/ecdsa_acl.rs b/crates/miden-testing/tests/auth/ecdsa_acl.rs index 415530ff66..ef09d5e008 100644 --- a/crates/miden-testing/tests/auth/ecdsa_acl.rs +++ b/crates/miden-testing/tests/auth/ecdsa_acl.rs @@ -2,9 +2,9 @@ use core::slice; use assert_matches::assert_matches; use miden_lib::account::auth::AuthEcdsaK256KeccakAcl; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, diff --git a/crates/miden-testing/tests/auth/ecdsa_multisig.rs b/crates/miden-testing/tests/auth/ecdsa_multisig.rs index 2de07ace67..515c5e490b 100644 --- a/crates/miden-testing/tests/auth/ecdsa_multisig.rs +++ b/crates/miden-testing/tests/auth/ecdsa_multisig.rs @@ -2,10 +2,10 @@ use miden_lib::account::auth::AuthEcdsaK256KeccakMultisig; use miden_lib::account::components::ecdsa_k256_keccak_multisig_library; use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_lib::account::wallets::BasicWallet; +use miden_lib::code_builder::CodeBuilder; use miden_lib::errors::standards::ERR_TX_ALREADY_EXECUTED; use miden_lib::note::create_p2id_note; use miden_lib::testing::account_interface::get_public_keys_from_account; -use miden_lib::utils::CodeBuilder; use miden_objects::account::auth::{AuthSecretKey, PublicKey}; use miden_objects::account::{Account, AccountBuilder, AccountId, AccountStorageMode, AccountType}; use miden_objects::asset::FungibleAsset; diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index 67540653ba..7bc1daec97 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -2,10 +2,10 @@ use miden_lib::account::auth::AuthRpoFalcon512Multisig; use miden_lib::account::components::rpo_falcon_512_multisig_library; use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_lib::account::wallets::BasicWallet; +use miden_lib::code_builder::CodeBuilder; use miden_lib::errors::standards::ERR_TX_ALREADY_EXECUTED; use miden_lib::note::create_p2id_note; use miden_lib::testing::account_interface::get_public_keys_from_account; -use miden_lib::utils::CodeBuilder; use miden_objects::account::auth::{AuthSecretKey, PublicKey}; use miden_objects::account::{Account, AccountBuilder, AccountId, AccountStorageMode, AccountType}; use miden_objects::asset::FungibleAsset; diff --git a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs index ca1bb2f8f8..e1550256b2 100644 --- a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs +++ b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs @@ -3,9 +3,9 @@ use core::slice; use anyhow::Context; use assert_matches::assert_matches; use miden_lib::account::auth::AuthRpoFalcon512Acl; +use miden_lib::code_builder::CodeBuilder; use miden_lib::testing::account_component::MockAccountComponent; use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountBuilder, diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index 129298d88e..3d7cd2f6cc 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -4,7 +4,7 @@ mod auth; mod scripts; mod wallet; -use miden_lib::utils::CodeBuilder; +use miden_lib::code_builder::CodeBuilder; use miden_objects::account::AccountId; use miden_objects::asset::FungibleAsset; use miden_objects::crypto::utils::Serializable; diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index b7ffd2c26a..1c2b8d379b 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -4,10 +4,10 @@ use alloc::sync::Arc; use core::slice; use miden_lib::account::faucets::{BasicFungibleFaucet, FungibleFaucetExt, NetworkFungibleFaucet}; -use miden_lib::errors::protocol::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; +use miden_lib::code_builder::CodeBuilder; +use miden_lib::errors::standards::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; use miden_lib::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{ Account, AccountId, diff --git a/crates/miden-testing/tests/scripts/p2id.rs b/crates/miden-testing/tests/scripts/p2id.rs index 2d3449e8c8..6c5fde6410 100644 --- a/crates/miden-testing/tests/scripts/p2id.rs +++ b/crates/miden-testing/tests/scripts/p2id.rs @@ -1,6 +1,6 @@ +use miden_lib::code_builder::CodeBuilder; use miden_lib::errors::standards::ERR_P2ID_TARGET_ACCT_MISMATCH; use miden_lib::note::create_p2id_note; -use miden_lib::utils::CodeBuilder; use miden_objects::account::Account; use miden_objects::asset::{Asset, AssetVault, FungibleAsset}; use miden_objects::crypto::rand::RpoRandomCoin; diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index 58326232f3..daf92deb88 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -2,7 +2,7 @@ use core::slice; use std::collections::BTreeMap; use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; -use miden_lib::utils::CodeBuilder; +use miden_lib::code_builder::CodeBuilder; use miden_objects::Word; use miden_objects::asset::{Asset, FungibleAsset}; use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index abce6229f4..486c3cff17 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -1,6 +1,6 @@ use anyhow::Context; +use miden_lib::code_builder::CodeBuilder; use miden_lib::note::utils; -use miden_lib::utils::CodeBuilder; use miden_objects::account::{Account, AccountId, AccountStorageMode, AccountType}; use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; use miden_objects::note::{ diff --git a/crates/miden-tx/src/auth/tx_authenticator.rs b/crates/miden-tx/src/auth/tx_authenticator.rs index af1617412e..82eaa4d245 100644 --- a/crates/miden-tx/src/auth/tx_authenticator.rs +++ b/crates/miden-tx/src/auth/tx_authenticator.rs @@ -276,8 +276,8 @@ impl TransactionAuthenticator for () { #[cfg(test)] mod test { - use miden_lib::utils::{Deserializable, Serializable}; use miden_objects::account::auth::AuthSecretKey; + use miden_objects::utils::{Deserializable, Serializable}; use miden_objects::{Felt, Word}; use super::SigningInputs; diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index f75562b069..edc677edbf 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -3,7 +3,6 @@ use alloc::collections::{BTreeMap, BTreeSet}; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::TransactionAdviceInputs; use miden_objects::account::auth::PublicKeyCommitment; use miden_objects::account::{AccountCode, AccountDelta, AccountId, PartialAccount}; use miden_objects::assembly::debuginfo::Location; @@ -12,7 +11,13 @@ use miden_objects::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; use miden_objects::block::BlockNumber; use miden_objects::crypto::merkle::smt::SmtProof; use miden_objects::note::{NoteInputs, NoteMetadata, NoteRecipient}; -use miden_objects::transaction::{InputNote, InputNotes, OutputNote, TransactionSummary}; +use miden_objects::transaction::{ + InputNote, + InputNotes, + OutputNote, + TransactionAdviceInputs, + TransactionSummary, +}; use miden_objects::vm::AdviceMap; use miden_objects::{Felt, Hasher, Word}; use miden_processor::{ diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 4cce13bc29..d3a2ad371c 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -1,7 +1,6 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; -use miden_lib::transaction::TransactionKernel; use miden_objects::account::AccountId; use miden_objects::assembly::DefaultSourceManager; use miden_objects::assembly::debuginfo::SourceManagerSync; @@ -13,6 +12,7 @@ use miden_objects::transaction::{ InputNotes, TransactionArgs, TransactionInputs, + TransactionKernel, TransactionScript, }; use miden_objects::vm::StackOutputs; diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index e6a033cb60..6ea8ca0f05 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -2,11 +2,16 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; use miden_lib::note::{NoteConsumptionStatus, WellKnownNote}; -use miden_lib::transaction::TransactionKernel; use miden_objects::account::AccountId; use miden_objects::block::BlockNumber; use miden_objects::note::Note; -use miden_objects::transaction::{InputNote, InputNotes, TransactionArgs, TransactionInputs}; +use miden_objects::transaction::{ + InputNote, + InputNotes, + TransactionArgs, + TransactionInputs, + TransactionKernel, +}; use miden_processor::fast::FastProcessor; use miden_prover::AdviceInputs; diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 9e8f2b3c52..34abee00e6 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -1,4 +1,6 @@ -use miden_lib::transaction::memory::{ +use miden_objects::account::{AccountId, StorageSlotId, StorageSlotType}; +use miden_objects::note::{NoteId, NoteInputs}; +use miden_objects::transaction::memory::{ ACCOUNT_STACK_TOP_PTR, ACCT_CODE_COMMITMENT_OFFSET, ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET, @@ -8,8 +10,6 @@ use miden_lib::transaction::memory::{ ACTIVE_INPUT_NOTE_PTR, NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, }; -use miden_objects::account::{AccountId, StorageSlotId, StorageSlotType}; -use miden_objects::note::{NoteId, NoteInputs}; use miden_objects::{Hasher, Word}; use miden_processor::{ExecutionError, Felt, ProcessState}; diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index a65aaf5bd6..0396f0af57 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -10,8 +10,8 @@ mod account_procedures; pub use account_procedures::AccountProcedureIndexMap; pub(crate) mod note_builder; -use miden_lib::CoreLibrary; -use miden_lib::transaction::EventId; +use miden_objects::CoreLibrary; +use miden_objects::vm::EventId; use note_builder::OutputNoteBuilder; mod kernel_process; diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index e62441740e..c5c046cc60 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -1,10 +1,10 @@ use alloc::vec::Vec; -use miden_lib::transaction::{EventId, TransactionEventId}; use miden_objects::account::{AccountId, StorageMap, StorageSlotName, StorageSlotType}; use miden_objects::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; use miden_objects::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; -use miden_objects::transaction::TransactionSummary; +use miden_objects::transaction::{TransactionEventId, TransactionSummary}; +use miden_objects::vm::EventId; use miden_objects::{Felt, Hasher, Word}; use miden_processor::{AdviceMutation, ProcessState, RowIndex}; diff --git a/crates/miden-tx/src/prover/mast_store.rs b/crates/miden-tx/src/prover/mast_store.rs index 722aea587d..c6d9b8163a 100644 --- a/crates/miden-tx/src/prover/mast_store.rs +++ b/crates/miden-tx/src/prover/mast_store.rs @@ -1,12 +1,12 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; -use miden_lib::transaction::TransactionKernel; -use miden_lib::{CoreLibrary, MidenLib}; -use miden_objects::Word; +use miden_lib::StandardsLib; use miden_objects::account::AccountCode; use miden_objects::assembly::mast::MastForest; +use miden_objects::transaction::TransactionKernel; use miden_objects::utils::sync::RwLock; +use miden_objects::{CoreLibrary, ProtocolLib, Word}; use miden_processor::MastForestStore; // TRANSACTION MAST STORE @@ -43,9 +43,13 @@ impl TransactionMastStore { let miden_core_lib_forest = CoreLibrary::default().mast_forest().clone(); store.insert(miden_core_lib_forest); - // load miden lib MAST forest - let miden_lib_forest = MidenLib::default().mast_forest().clone(); - store.insert(miden_lib_forest); + // load protocol lib MAST forest + let protocol_lib_forest = ProtocolLib::default().mast_forest().clone(); + store.insert(protocol_lib_forest); + + // load standards lib MAST forest + let standards_lib_forest = StandardsLib::default().mast_forest().clone(); + store.insert(standards_lib_forest); store } diff --git a/crates/miden-tx/src/prover/mod.rs b/crates/miden-tx/src/prover/mod.rs index 332b3e84ab..8cd1599eaf 100644 --- a/crates/miden-tx/src/prover/mod.rs +++ b/crates/miden-tx/src/prover/mod.rs @@ -1,7 +1,6 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::TransactionKernel; use miden_objects::account::delta::AccountUpdateDetails; use miden_objects::account::{AccountDelta, PartialAccount}; use miden_objects::asset::Asset; @@ -13,6 +12,7 @@ use miden_objects::transaction::{ ProvenTransaction, ProvenTransactionBuilder, TransactionInputs, + TransactionKernel, TransactionOutputs, }; pub use miden_prover::ProvingOptions; diff --git a/crates/miden-tx/src/verifier/mod.rs b/crates/miden-tx/src/verifier/mod.rs index 4e0778a8c0..9880283704 100644 --- a/crates/miden-tx/src/verifier/mod.rs +++ b/crates/miden-tx/src/verifier/mod.rs @@ -1,6 +1,5 @@ -use miden_lib::CoreLibrary; -use miden_lib::transaction::TransactionKernel; -use miden_objects::transaction::ProvenTransaction; +use miden_objects::CoreLibrary; +use miden_objects::transaction::{ProvenTransaction, TransactionKernel}; use miden_objects::vm::ProgramInfo; use miden_verifier::verify_with_precompiles; From eb0c396d48506c30c3fdc859ddaabbb8bfaf6b00 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Sat, 20 Dec 2025 16:34:22 +0700 Subject: [PATCH 074/114] feat: rename `miden-objects` into `miden-protocol` and `miden-lib` into `miden-standards` (#2197) --- CHANGELOG.md | 2 +- Cargo.lock | 104 +++++++++--------- Cargo.toml | 8 +- Makefile | 2 +- README.md | 4 +- bin/bench-note-checker/Cargo.toml | 8 +- bin/bench-note-checker/src/lib.rs | 12 +- bin/bench-transaction/Cargo.toml | 8 +- bin/bench-transaction/src/context_setups.rs | 16 +-- .../src/cycle_counting_benchmarks/utils.rs | 2 +- bin/bench-transaction/src/main.rs | 2 +- .../src/time_counting_benchmarks/prove.rs | 2 +- crates/miden-block-prover/Cargo.toml | 4 +- .../src/local_block_prover.rs | 4 +- crates/miden-protocol-macros/Cargo.toml | 2 +- .../tests/integration_test.rs | 2 +- .../Cargo.toml | 6 +- .../README.md | 4 +- .../asm/kernels/transaction/api.masm | 0 .../asm/kernels/transaction/lib/account.masm | 2 +- .../transaction/lib/account_delta.masm | 0 .../asm/kernels/transaction/lib/asset.masm | 0 .../kernels/transaction/lib/asset_vault.masm | 0 .../kernels/transaction/lib/constants.masm | 0 .../asm/kernels/transaction/lib/epilogue.masm | 0 .../asm/kernels/transaction/lib/faucet.masm | 0 .../kernels/transaction/lib/input_note.masm | 0 .../asm/kernels/transaction/lib/link_map.masm | 0 .../asm/kernels/transaction/lib/memory.masm | 0 .../asm/kernels/transaction/lib/note.masm | 0 .../kernels/transaction/lib/output_note.masm | 2 +- .../asm/kernels/transaction/lib/prologue.masm | 0 .../asm/kernels/transaction/lib/tx.masm | 0 .../asm/kernels/transaction/main.masm | 0 .../kernels/transaction/tx_script_main.masm | 0 .../asm/protocol}/active_account.masm | 2 +- .../asm/protocol}/active_note.masm | 4 +- .../asm/protocol}/asset.masm | 4 +- .../asm/protocol}/faucet.masm | 6 +- .../asm/protocol}/input_note.masm | 4 +- .../asm/protocol}/kernel_proc_offsets.masm | 0 .../asm/protocol}/native_account.masm | 2 +- .../asm/protocol}/note.masm | 4 +- .../asm/protocol}/output_note.masm | 4 +- .../asm/protocol}/tx.masm | 2 +- .../asm/shared_modules/account_id.masm | 0 .../asm/shared_utils/util/asset.masm | 0 .../asm/shared_utils/util/note.masm | 0 .../benches/account_seed.rs | 6 +- .../build.rs | 48 ++++---- .../masm_doc_comment_fmt.md | 0 .../src/account/account_id/account_type.rs | 0 .../src/account/account_id/id_prefix.rs | 0 .../src/account/account_id/id_version.rs | 0 .../src/account/account_id/mod.rs | 0 .../src/account/account_id/seed.rs | 0 .../src/account/account_id/storage_mode.rs | 0 .../src/account/account_id/v0/mod.rs | 0 .../src/account/account_id/v0/prefix.rs | 0 .../src/account/auth.rs | 6 +- .../src/account/builder/mod.rs | 0 .../src/account/code/header.rs | 0 .../src/account/code/mod.rs | 0 .../src/account/code/procedure.rs | 0 .../src/account/component/code.rs | 0 .../src/account/component/metadata/mod.rs | 4 +- .../src/account/component/mod.rs | 0 .../component/storage/init_storage_data.rs | 0 .../src/account/component/storage/mod.rs | 0 .../src/account/component/storage/schema.rs | 0 .../storage/toml/init_storage_data.rs | 0 .../src/account/component/storage/toml/mod.rs | 0 .../component/storage/toml/serde_impls.rs | 0 .../account/component/storage/toml/tests.rs | 0 .../component/storage/type_registry.rs | 0 .../account/component/storage/value_name.rs | 0 .../src/account/delta/mod.rs | 0 .../src/account/delta/storage.rs | 0 .../src/account/delta/vault.rs | 0 .../src/account/file.rs | 0 .../src/account/header.rs | 0 .../src/account/mod.rs | 0 .../src/account/partial.rs | 0 .../src/account/storage/header.rs | 0 .../src/account/storage/map/mod.rs | 0 .../src/account/storage/map/partial.rs | 0 .../src/account/storage/map/witness.rs | 0 .../src/account/storage/mod.rs | 3 +- .../src/account/storage/partial.rs | 0 .../src/account/storage/slot/mod.rs | 0 .../src/account/storage/slot/slot_content.rs | 0 .../src/account/storage/slot/slot_id.rs | 0 .../src/account/storage/slot/slot_name.rs | 0 .../src/account/storage/slot/storage_slot.rs | 0 .../src/account/storage/slot/type.rs | 0 .../src/address/address_id.rs | 0 .../src/address/interface.rs | 0 .../src/address/mod.rs | 0 .../src/address/network_id.rs | 0 .../src/address/routing_parameters.rs | 0 .../src/address/type.rs | 0 .../src/asset/fungible.rs | 0 .../src/asset/mod.rs | 0 .../src/asset/nonfungible.rs | 0 .../src/asset/token_symbol.rs | 2 +- .../src/asset/vault/asset_witness.rs | 0 .../src/asset/vault/mod.rs | 0 .../src/asset/vault/partial.rs | 0 .../src/asset/vault/vault_key.rs | 0 .../src/batch/account_update.rs | 0 .../src/batch/batch_id.rs | 0 .../src/batch/input_output_note_tracker.rs | 0 .../src/batch/mod.rs | 0 .../src/batch/note_tree.rs | 0 .../src/batch/ordered_batches.rs | 0 .../src/batch/proposed_batch.rs | 0 .../src/batch/proven_batch.rs | 0 .../src/block/account_tree/backend.rs | 0 .../src/block/account_tree/mod.rs | 0 .../src/block/account_tree/partial.rs | 0 .../src/block/account_tree/witness.rs | 0 .../src/block/account_update_witness.rs | 0 .../src/block/block_account_update.rs | 0 .../src/block/block_body.rs | 0 .../src/block/block_inputs.rs | 0 .../src/block/block_number.rs | 0 .../src/block/block_proof.rs | 0 .../src/block/blockchain.rs | 0 .../src/block/header.rs | 0 .../src/block/mod.rs | 0 .../src/block/note_tree.rs | 0 .../src/block/nullifier_tree/backend.rs | 0 .../src/block/nullifier_tree/mod.rs | 0 .../src/block/nullifier_tree/partial.rs | 0 .../src/block/nullifier_tree/witness.rs | 0 .../src/block/proposed_block.rs | 0 .../src/block/proven_block.rs | 0 .../src/block/signer.rs | 0 .../src/constants.rs | 0 .../src/errors/masm_error.rs | 0 .../src/errors/mod.rs | 0 .../src/errors/protocol.rs | 2 +- .../src/errors/tx_kernel.rs | 2 +- .../src/lib.rs | 0 .../src/note/assets.rs | 0 .../src/note/details.rs | 0 .../src/note/execution_hint.rs | 0 .../src/note/file.rs | 0 .../src/note/header.rs | 0 .../src/note/inputs.rs | 0 .../src/note/location.rs | 0 .../src/note/metadata.rs | 0 .../src/note/mod.rs | 0 .../src/note/note_id.rs | 0 .../src/note/note_tag.rs | 0 .../src/note/note_type.rs | 0 .../src/note/nullifier.rs | 0 .../src/note/partial.rs | 0 .../src/note/recipient.rs | 0 .../src/note/script.rs | 0 .../src/protocol.rs | 2 +- .../src/testing/account.rs | 0 .../src/testing/account_code.rs | 0 .../src/testing/account_id.rs | 4 +- .../src/testing/add_component.rs | 0 .../src/testing/asset.rs | 0 .../src/testing/block.rs | 0 .../src/testing/block_note_tree.rs | 0 .../src/testing/constants.rs | 0 .../src/testing/mock_util_lib.rs | 2 +- .../src/testing/mod.rs | 0 .../src/testing/noop_auth_component.rs | 0 .../src/testing/note.rs | 0 .../src/testing/partial_blockchain.rs | 0 .../src/testing/random_signer.rs | 0 .../src/testing/slot_name.rs | 0 .../src/testing/storage.rs | 0 .../src/testing/tx.rs | 0 .../src/transaction/executed_tx.rs | 0 .../src/transaction/inputs/account.rs | 0 .../src/transaction/inputs/mod.rs | 0 .../src/transaction/inputs/notes.rs | 0 .../src/transaction/kernel/advice_inputs.rs | 0 .../src/transaction/kernel/memory.rs | 0 .../src/transaction/kernel/mod.rs | 0 .../src/transaction/kernel/procedures.rs | 10 +- .../src/transaction/kernel/tx_event_id.rs | 0 .../src/transaction/mod.rs | 0 .../src/transaction/ordered_transactions.rs | 0 .../src/transaction/outputs.rs | 0 .../src/transaction/partial_blockchain.rs | 0 .../src/transaction/proven_tx.rs | 0 .../src/transaction/transaction_id.rs | 0 .../src/transaction/tx_args.rs | 0 .../src/transaction/tx_header.rs | 0 .../src/transaction/tx_summary.rs | 0 .../{miden-lib => miden-standards}/Cargo.toml | 16 +-- .../{miden-lib => miden-standards}/README.md | 4 +- .../basic_fungible_faucet.masm | 4 +- .../asm/account_components/basic_wallet.masm | 4 +- .../account_components/ecdsa_k256_keccak.masm | 4 +- .../ecdsa_k256_keccak_acl.masm | 8 +- .../ecdsa_k256_keccak_multisig.masm | 8 +- .../network_fungible_faucet.masm | 4 +- .../asm/account_components/no_auth.masm | 4 +- .../account_components/rpo_falcon_512.masm | 4 +- .../rpo_falcon_512_acl.masm | 8 +- .../rpo_falcon_512_multisig.masm | 8 +- .../asm/note_scripts/BURN.masm | 2 +- .../asm/note_scripts/MINT.masm | 6 +- .../asm/note_scripts/P2ID.masm | 8 +- .../asm/note_scripts/P2IDE.masm | 10 +- .../asm/note_scripts/SWAP.masm | 6 +- .../standards}/auth/ecdsa_k256_keccak.masm | 8 +- .../asm/standards}/auth/mod.masm | 4 +- .../asm/standards}/auth/rpo_falcon512.masm | 8 +- .../standards}/faucets/basic_fungible.masm | 2 +- .../asm/standards}/faucets/mod.masm | 8 +- .../standards}/faucets/network_fungible.masm | 12 +- .../asm/standards}/wallets/basic.masm | 6 +- .../{miden-lib => miden-standards}/build.rs | 29 ++--- .../src/account/auth/ecdsa_k256_keccak.rs | 8 +- .../src/account/auth/ecdsa_k256_keccak_acl.rs | 12 +- .../auth/ecdsa_k256_keccak_multisig.rs | 12 +- .../src/account/auth/mod.rs | 0 .../src/account/auth/no_auth.rs | 4 +- .../src/account/auth/rpo_falcon_512.rs | 8 +- .../src/account/auth/rpo_falcon_512_acl.rs | 12 +- .../account/auth/rpo_falcon_512_multisig.rs | 12 +- .../src/account/components/mod.rs | 10 +- .../src/account/faucets/basic_fungible.rs | 14 +-- .../src/account/faucets/mod.rs | 6 +- .../src/account/faucets/network_fungible.rs | 16 +-- .../src/account/interface/component.rs | 20 ++-- .../src/account/interface/extension.rs | 8 +- .../src/account/interface/mod.rs | 18 +-- .../src/account/interface/test.rs | 46 ++++---- .../src/account/mod.rs | 8 +- .../src/account/wallets/mod.rs | 10 +- .../src/auth.rs | 2 +- .../src/code_builder/mod.rs | 34 +++--- .../src/errors/code_builder_errors.rs | 4 +- .../src/errors/mod.rs | 0 .../src/errors/standards.rs | 4 +- .../{miden-lib => miden-standards}/src/lib.rs | 0 .../src/note/mint_inputs.rs | 4 +- .../src/note/mod.rs | 12 +- .../src/note/utils.rs | 16 +-- .../src/note/well_known_note.rs | 14 +-- .../src/standards_lib.rs | 12 +- .../account_component/conditional_auth.rs | 6 +- .../testing/account_component/incr_nonce.rs | 8 +- .../mock_account_component.rs | 4 +- .../mock_faucet_component.rs | 4 +- .../src/testing/account_component/mod.rs | 0 .../src/testing/account_interface.rs | 4 +- .../src/testing/mock_account.rs | 10 +- .../src/testing/mock_account_code.rs | 18 +-- .../src/testing/mod.rs | 0 .../src/testing/note.rs | 14 +-- crates/miden-testing/Cargo.toml | 8 +- crates/miden-testing/src/executor.rs | 16 +-- .../src/kernel_tests/batch/proposed_batch.rs | 20 ++-- .../kernel_tests/batch/proven_tx_builder.rs | 16 +-- .../src/kernel_tests/block/header_errors.rs | 22 ++-- .../block/proposed_block_errors.rs | 12 +- .../block/proposed_block_success.rs | 20 ++-- .../block/proven_block_success.rs | 18 +-- .../src/kernel_tests/block/utils.rs | 14 +-- .../miden-testing/src/kernel_tests/tx/mod.rs | 20 ++-- .../src/kernel_tests/tx/test_account.rs | 80 +++++++------- .../src/kernel_tests/tx/test_account_delta.rs | 30 ++--- .../kernel_tests/tx/test_account_interface.rs | 29 +++-- .../src/kernel_tests/tx/test_active_note.rs | 38 +++---- .../src/kernel_tests/tx/test_asset.rs | 14 +-- .../src/kernel_tests/tx/test_asset_vault.rs | 20 ++-- .../src/kernel_tests/tx/test_auth.rs | 18 +-- .../src/kernel_tests/tx/test_epilogue.rs | 30 ++--- .../src/kernel_tests/tx/test_faucet.rs | 32 +++--- .../src/kernel_tests/tx/test_fee.rs | 12 +- .../src/kernel_tests/tx/test_fpi.rs | 90 +++++++-------- .../src/kernel_tests/tx/test_input_note.rs | 20 ++-- .../src/kernel_tests/tx/test_lazy_loading.rs | 16 +-- .../src/kernel_tests/tx/test_link_map.rs | 2 +- .../src/kernel_tests/tx/test_note.rs | 44 ++++---- .../src/kernel_tests/tx/test_output_note.rs | 60 +++++----- .../src/kernel_tests/tx/test_prologue.rs | 28 ++--- .../src/kernel_tests/tx/test_tx.rs | 50 ++++----- crates/miden-testing/src/mock_chain/auth.rs | 15 ++- crates/miden-testing/src/mock_chain/chain.rs | 38 +++---- .../src/mock_chain/chain_builder.rs | 38 +++---- crates/miden-testing/src/mock_chain/note.rs | 4 +- crates/miden-testing/src/mock_host.rs | 12 +- .../miden-testing/src/tx_context/builder.rs | 34 +++--- .../miden-testing/src/tx_context/context.rs | 32 +++--- crates/miden-testing/src/utils.rs | 24 ++-- crates/miden-testing/tests/auth/ecdsa_acl.rs | 18 +-- .../tests/auth/ecdsa_multisig.rs | 42 ++++--- crates/miden-testing/tests/auth/multisig.rs | 42 ++++--- .../tests/auth/rpo_falcon_acl.rs | 18 +-- crates/miden-testing/tests/lib.rs | 22 ++-- crates/miden-testing/tests/scripts/faucet.rs | 40 ++++--- crates/miden-testing/tests/scripts/fee.rs | 4 +- crates/miden-testing/tests/scripts/p2id.rs | 26 ++--- crates/miden-testing/tests/scripts/p2ide.rs | 12 +- .../miden-testing/tests/scripts/send_note.rs | 18 +-- crates/miden-testing/tests/scripts/swap.rs | 20 ++-- crates/miden-testing/tests/wallet/mod.rs | 20 ++-- crates/miden-tx-batch-prover/Cargo.toml | 6 +- .../src/local_batch_prover.rs | 4 +- crates/miden-tx/Cargo.toml | 8 +- crates/miden-tx/src/auth/tx_authenticator.rs | 14 +-- crates/miden-tx/src/errors/mod.rs | 20 ++-- crates/miden-tx/src/executor/data_store.rs | 10 +- crates/miden-tx/src/executor/exec_host.rs | 34 +++--- crates/miden-tx/src/executor/mod.rs | 24 ++-- crates/miden-tx/src/executor/notes_checker.rs | 12 +- .../src/host/account_delta_tracker.rs | 4 +- .../miden-tx/src/host/account_procedures.rs | 2 +- crates/miden-tx/src/host/kernel_process.rs | 10 +- crates/miden-tx/src/host/link_map.rs | 4 +- crates/miden-tx/src/host/mod.rs | 34 +++--- crates/miden-tx/src/host/note_builder.rs | 4 +- .../src/host/script_mast_forest_store.rs | 10 +- .../src/host/storage_delta_tracker.rs | 4 +- crates/miden-tx/src/host/tx_event.rs | 12 +- crates/miden-tx/src/lib.rs | 2 +- crates/miden-tx/src/prover/mast_store.rs | 19 ++-- crates/miden-tx/src/prover/mod.rs | 12 +- crates/miden-tx/src/prover/prover_host.rs | 10 +- crates/miden-tx/src/verifier/mod.rs | 6 +- docs/src/account/components.md | 3 +- docs/src/account/storage.md | 2 +- docs/src/note.md | 2 +- docs/src/protocol_library.md | 18 +-- docs/src/transaction.md | 4 +- scripts/check-features.sh | 4 +- 337 files changed, 1244 insertions(+), 1210 deletions(-) rename crates/{miden-objects => miden-protocol}/Cargo.toml (94%) rename crates/{miden-objects => miden-protocol}/README.md (97%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/api.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/account.masm (99%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/account_delta.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/asset.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/asset_vault.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/constants.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/epilogue.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/faucet.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/input_note.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/link_map.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/memory.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/note.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/output_note.masm (99%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/prologue.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/lib/tx.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/main.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/kernels/transaction/tx_script_main.masm (100%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/active_account.masm (99%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/active_note.masm (99%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/asset.masm (96%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/faucet.masm (97%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/input_note.masm (99%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/kernel_proc_offsets.masm (100%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/native_account.masm (99%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/note.masm (98%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/output_note.masm (98%) rename crates/{miden-objects/asm/miden => miden-protocol/asm/protocol}/tx.masm (99%) rename crates/{miden-objects => miden-protocol}/asm/shared_modules/account_id.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/shared_utils/util/asset.masm (100%) rename crates/{miden-objects => miden-protocol}/asm/shared_utils/util/note.masm (100%) rename crates/{miden-objects => miden-protocol}/benches/account_seed.rs (90%) rename crates/{miden-objects => miden-protocol}/build.rs (95%) rename crates/{miden-lib => miden-protocol}/masm_doc_comment_fmt.md (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/account_type.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/id_prefix.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/id_version.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/seed.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/storage_mode.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/v0/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/v0/prefix.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/auth.rs (99%) rename crates/{miden-objects => miden-protocol}/src/account/builder/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/code/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/code/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/code/procedure.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/code.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/metadata/mod.rs (98%) rename crates/{miden-objects => miden-protocol}/src/account/component/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/init_storage_data.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/schema.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/toml/init_storage_data.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/toml/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/toml/serde_impls.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/toml/tests.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/type_registry.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/value_name.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/delta/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/delta/storage.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/delta/vault.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/file.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/map/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/map/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/map/witness.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/mod.rs (99%) rename crates/{miden-objects => miden-protocol}/src/account/storage/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/slot_content.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/slot_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/slot_name.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/storage_slot.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/type.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/address_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/interface.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/network_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/routing_parameters.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/type.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/fungible.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/nonfungible.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/token_symbol.rs (99%) rename crates/{miden-objects => miden-protocol}/src/asset/vault/asset_witness.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/vault/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/vault/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/vault/vault_key.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/account_update.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/batch_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/input_output_note_tracker.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/note_tree.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/ordered_batches.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/proposed_batch.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/proven_batch.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/account_tree/backend.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/account_tree/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/account_tree/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/account_tree/witness.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/account_update_witness.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_account_update.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_body.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_inputs.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_number.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_proof.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/blockchain.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/note_tree.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/nullifier_tree/backend.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/nullifier_tree/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/nullifier_tree/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/nullifier_tree/witness.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/proposed_block.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/proven_block.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/signer.rs (100%) rename crates/{miden-objects => miden-protocol}/src/constants.rs (100%) rename crates/{miden-objects => miden-protocol}/src/errors/masm_error.rs (100%) rename crates/{miden-objects => miden-protocol}/src/errors/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/errors/protocol.rs (99%) rename crates/{miden-objects => miden-protocol}/src/errors/tx_kernel.rs (99%) rename crates/{miden-objects => miden-protocol}/src/lib.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/assets.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/details.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/execution_hint.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/file.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/inputs.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/location.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/metadata.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/note_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/note_tag.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/note_type.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/nullifier.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/recipient.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/script.rs (100%) rename crates/{miden-objects => miden-protocol}/src/protocol.rs (96%) rename crates/{miden-objects => miden-protocol}/src/testing/account.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/account_code.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/account_id.rs (98%) rename crates/{miden-objects => miden-protocol}/src/testing/add_component.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/asset.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/block.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/block_note_tree.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/constants.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/mock_util_lib.rs (97%) rename crates/{miden-objects => miden-protocol}/src/testing/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/noop_auth_component.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/note.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/partial_blockchain.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/random_signer.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/slot_name.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/storage.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/tx.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/executed_tx.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/inputs/account.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/inputs/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/inputs/notes.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/kernel/advice_inputs.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/kernel/memory.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/kernel/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/kernel/procedures.rs (93%) rename crates/{miden-objects => miden-protocol}/src/transaction/kernel/tx_event_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/ordered_transactions.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/outputs.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/partial_blockchain.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/proven_tx.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/transaction_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/tx_args.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/tx_header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/tx_summary.rs (100%) rename crates/{miden-lib => miden-standards}/Cargo.toml (72%) rename crates/{miden-lib => miden-standards}/README.md (61%) rename crates/{miden-lib => miden-standards}/asm/account_components/basic_fungible_faucet.masm (53%) rename crates/{miden-lib => miden-standards}/asm/account_components/basic_wallet.masm (51%) rename crates/{miden-lib => miden-standards}/asm/account_components/ecdsa_k256_keccak.masm (95%) rename crates/{miden-lib => miden-standards}/asm/account_components/ecdsa_k256_keccak_acl.masm (96%) rename crates/{miden-lib => miden-standards}/asm/account_components/ecdsa_k256_keccak_multisig.masm (98%) rename crates/{miden-lib => miden-standards}/asm/account_components/network_fungible_faucet.masm (54%) rename crates/{miden-lib => miden-standards}/asm/account_components/no_auth.masm (95%) rename crates/{miden-lib => miden-standards}/asm/account_components/rpo_falcon_512.masm (95%) rename crates/{miden-lib => miden-standards}/asm/account_components/rpo_falcon_512_acl.masm (96%) rename crates/{miden-lib => miden-standards}/asm/account_components/rpo_falcon_512_multisig.masm (98%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/BURN.masm (96%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/MINT.masm (97%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/P2ID.masm (92%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/P2IDE.masm (97%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/SWAP.masm (96%) rename crates/{miden-lib/asm/miden => miden-standards/asm/standards}/auth/ecdsa_k256_keccak.masm (98%) rename crates/{miden-lib/asm/miden => miden-standards/asm/standards}/auth/mod.masm (98%) rename crates/{miden-lib/asm/miden => miden-standards/asm/standards}/auth/rpo_falcon512.masm (98%) rename crates/{miden-lib/asm/miden/contracts => miden-standards/asm/standards}/faucets/basic_fungible.masm (98%) rename crates/{miden-lib/asm/miden/contracts => miden-standards/asm/standards}/faucets/mod.masm (97%) rename crates/{miden-lib/asm/miden/contracts => miden-standards/asm/standards}/faucets/network_fungible.masm (91%) rename crates/{miden-lib/asm/miden/contracts => miden-standards/asm/standards}/wallets/basic.masm (97%) rename crates/{miden-lib => miden-standards}/build.rs (94%) rename crates/{miden-lib => miden-standards}/src/account/auth/ecdsa_k256_keccak.rs (87%) rename crates/{miden-lib => miden-standards}/src/account/auth/ecdsa_k256_keccak_acl.rs (98%) rename crates/{miden-lib => miden-standards}/src/account/auth/ecdsa_k256_keccak_multisig.rs (97%) rename crates/{miden-lib => miden-standards}/src/account/auth/mod.rs (100%) rename crates/{miden-lib => miden-standards}/src/account/auth/no_auth.rs (94%) rename crates/{miden-lib => miden-standards}/src/account/auth/rpo_falcon_512.rs (90%) rename crates/{miden-lib => miden-standards}/src/account/auth/rpo_falcon_512_acl.rs (98%) rename crates/{miden-lib => miden-standards}/src/account/auth/rpo_falcon_512_multisig.rs (97%) rename crates/{miden-lib => miden-standards}/src/account/components/mod.rs (98%) rename crates/{miden-lib => miden-standards}/src/account/faucets/basic_fungible.rs (97%) rename crates/{miden-lib => miden-standards}/src/account/faucets/mod.rs (93%) rename crates/{miden-lib => miden-standards}/src/account/faucets/network_fungible.rs (96%) rename crates/{miden-lib => miden-standards}/src/account/interface/component.rs (95%) rename crates/{miden-lib => miden-standards}/src/account/interface/extension.rs (97%) rename crates/{miden-lib => miden-standards}/src/account/interface/mod.rs (93%) rename crates/{miden-lib => miden-standards}/src/account/interface/test.rs (96%) rename crates/{miden-lib => miden-standards}/src/account/mod.rs (79%) rename crates/{miden-lib => miden-standards}/src/account/wallets/mod.rs (96%) rename crates/{miden-lib => miden-standards}/src/auth.rs (97%) rename crates/{miden-lib => miden-standards}/src/code_builder/mod.rs (96%) rename crates/{miden-lib => miden-standards}/src/errors/code_builder_errors.rs (93%) rename crates/{miden-lib => miden-standards}/src/errors/mod.rs (100%) rename crates/{miden-lib => miden-standards}/src/errors/standards.rs (98%) rename crates/{miden-lib => miden-standards}/src/lib.rs (100%) rename crates/{miden-lib => miden-standards}/src/note/mint_inputs.rs (95%) rename crates/{miden-lib => miden-standards}/src/note/mod.rs (97%) rename crates/{miden-lib => miden-standards}/src/note/utils.rs (90%) rename crates/{miden-lib => miden-standards}/src/note/well_known_note.rs (98%) rename crates/{miden-lib => miden-standards}/src/standards_lib.rs (86%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/conditional_auth.rs (91%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/incr_nonce.rs (84%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/mock_account_component.rs (91%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/mock_faucet_component.rs (84%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/mod.rs (100%) rename crates/{miden-lib => miden-standards}/src/testing/account_interface.rs (87%) rename crates/{miden-lib => miden-standards}/src/testing/mock_account.rs (93%) rename crates/{miden-lib => miden-standards}/src/testing/mock_account_code.rs (92%) rename crates/{miden-lib => miden-standards}/src/testing/mod.rs (100%) rename crates/{miden-lib => miden-standards}/src/testing/note.rs (93%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09628cb568..f06f41cdbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). -- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191)). +- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197)). - [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). diff --git a/Cargo.lock b/Cargo.lock index a92450807e..e94956126d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,8 +232,8 @@ version = "0.1.0" dependencies = [ "anyhow", "criterion 0.6.0", - "miden-lib", - "miden-objects", + "miden-protocol", + "miden-standards", "miden-testing", "miden-tx", "serde", @@ -247,8 +247,8 @@ version = "0.1.0" dependencies = [ "anyhow", "criterion 0.6.0", - "miden-lib", - "miden-objects", + "miden-protocol", + "miden-standards", "miden-testing", "miden-tx", "serde", @@ -1440,7 +1440,7 @@ dependencies = [ name = "miden-block-prover" version = "0.13.0" dependencies = [ - "miden-objects", + "miden-protocol", "thiserror", ] @@ -1554,25 +1554,6 @@ dependencies = [ "unicode-width 0.1.14", ] -[[package]] -name = "miden-lib" -version = "0.13.0" -dependencies = [ - "anyhow", - "assert_matches", - "fs-err", - "miden-assembly", - "miden-core", - "miden-core-lib", - "miden-lib", - "miden-objects", - "miden-processor", - "rand", - "regex", - "thiserror", - "walkdir", -] - [[package]] name = "miden-mast-package" version = "0.20.1" @@ -1628,7 +1609,27 @@ dependencies = [ ] [[package]] -name = "miden-objects" +name = "miden-processor" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb298dbdda739080497c18eace4d56c58f3e8d257676c9b2f407be441131ecd" +dependencies = [ + "itertools 0.14.0", + "miden-air", + "miden-core", + "miden-debug-types", + "miden-utils-diagnostics", + "miden-utils-indexing", + "paste", + "rayon", + "thiserror", + "tokio", + "tracing", + "winter-prover", +] + +[[package]] +name = "miden-protocol" version = "0.13.0" dependencies = [ "anyhow", @@ -1645,8 +1646,8 @@ dependencies = [ "miden-core-lib", "miden-crypto", "miden-mast-package", - "miden-objects", "miden-processor", + "miden-protocol", "miden-protocol-macros", "miden-utils-sync", "miden-verifier", @@ -1666,31 +1667,11 @@ dependencies = [ "winter-rand-utils", ] -[[package]] -name = "miden-processor" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb298dbdda739080497c18eace4d56c58f3e8d257676c9b2f407be441131ecd" -dependencies = [ - "itertools 0.14.0", - "miden-air", - "miden-core", - "miden-debug-types", - "miden-utils-diagnostics", - "miden-utils-indexing", - "paste", - "rayon", - "thiserror", - "tokio", - "tracing", - "winter-prover", -] - [[package]] name = "miden-protocol-macros" version = "0.13.0" dependencies = [ - "miden-objects", + "miden-protocol", "proc-macro2", "quote", "syn 2.0.111", @@ -1710,6 +1691,25 @@ dependencies = [ "winter-prover", ] +[[package]] +name = "miden-standards" +version = "0.13.0" +dependencies = [ + "anyhow", + "assert_matches", + "fs-err", + "miden-assembly", + "miden-core", + "miden-core-lib", + "miden-processor", + "miden-protocol", + "miden-standards", + "rand", + "regex", + "thiserror", + "walkdir", +] + [[package]] name = "miden-testing" version = "0.13.0" @@ -1718,9 +1718,9 @@ dependencies = [ "assert_matches", "itertools 0.14.0", "miden-block-prover", - "miden-lib", - "miden-objects", "miden-processor", + "miden-protocol", + "miden-standards", "miden-tx", "miden-tx-batch-prover", "rand", @@ -1738,10 +1738,10 @@ dependencies = [ "anyhow", "assert_matches", "miden-assembly", - "miden-lib", - "miden-objects", "miden-processor", + "miden-protocol", "miden-prover", + "miden-standards", "miden-tx", "miden-verifier", "rstest", @@ -1752,7 +1752,7 @@ dependencies = [ name = "miden-tx-batch-prover" version = "0.13.0" dependencies = [ - "miden-objects", + "miden-protocol", "miden-tx", ] diff --git a/Cargo.toml b/Cargo.toml index 05700ab01b..6bd3cf0716 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,9 @@ members = [ "bin/bench-note-checker", "bin/bench-transaction", "crates/miden-block-prover", - "crates/miden-lib", - "crates/miden-objects", + "crates/miden-protocol", "crates/miden-protocol-macros", + "crates/miden-standards", "crates/miden-testing", "crates/miden-tx", "crates/miden-tx-batch-prover", @@ -42,9 +42,9 @@ lto = true [workspace.dependencies] # Workspace crates miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.13" } -miden-lib = { default-features = false, path = "crates/miden-lib", version = "0.13" } -miden-objects = { default-features = false, path = "crates/miden-objects", version = "0.13" } +miden-protocol = { default-features = false, path = "crates/miden-protocol", version = "0.13" } miden-protocol-macros = { default-features = false, path = "crates/miden-protocol-macros", version = "0.13" } +miden-standards = { default-features = false, path = "crates/miden-standards", version = "0.13" } miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.13" } miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.13" } miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.13" } diff --git a/Makefile b/Makefile index 6f67df4e17..10f3f8823a 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ help: WARNINGS=RUSTDOCFLAGS="-D warnings" # Enable file generation in the `src` directory. -# This is used in the build scripts of miden-lib. +# This is used in the build scripts of miden-protocol and miden-standards. BUILD_GENERATED_FILES_IN_SRC=BUILD_GENERATED_FILES_IN_SRC=1 # Enable backtraces for tests where we return an anyhow::Result. If enabled, anyhow::Error will # then contain a `Backtrace` and print it when a test returns an error. diff --git a/README.md b/README.md index 55dd81ee88..df78fec0f4 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,8 @@ Miden is currently on release v0.13. This is an early version of the protocol an | Crate | Description | | ------------------------------- | ------------------------------------------------------------------------------- | -| [objects](crates/miden-objects) | Contains core components defining the Miden rollup protocol. | -| [miden-lib](crates/miden-lib) | Contains the code of the Miden rollup kernels and standardized smart contracts. | +| [miden-protocol](crates/miden-protocol) | Contains core components defining the Miden protocol, including the transaction kernel. | +| [miden-standards](crates/miden-standards) | Contains the code of Miden's standardized smart contracts. | | [miden-tx](crates/miden-tx) | Contains tool for creating, executing, and proving Miden rollup transaction. | | [bench-tx](bin/bench-tx) | Contains transaction execution and proving benchmarks. | diff --git a/bin/bench-note-checker/Cargo.toml b/bin/bench-note-checker/Cargo.toml index 9496b74ecb..630d1b9189 100644 --- a/bin/bench-note-checker/Cargo.toml +++ b/bin/bench-note-checker/Cargo.toml @@ -12,10 +12,10 @@ version = "0.1.0" [dependencies] # Workspace dependencies -miden-lib = { workspace = true } -miden-objects = { features = ["testing"], workspace = true } -miden-testing = { workspace = true } -miden-tx = { workspace = true } +miden-protocol = { features = ["testing"], workspace = true } +miden-standards = { workspace = true } +miden-testing = { workspace = true } +miden-tx = { workspace = true } # External dependencies anyhow = { workspace = true } diff --git a/bin/bench-note-checker/src/lib.rs b/bin/bench-note-checker/src/lib.rs index 36761b03e6..cb08ab832e 100644 --- a/bin/bench-note-checker/src/lib.rs +++ b/bin/bench-note-checker/src/lib.rs @@ -1,12 +1,12 @@ -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::AccountId; -use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::rand::RpoRandomCoin; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::{ +use miden_protocol::account::AccountId; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::crypto::rand::RpoRandomCoin; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_SENDER, }; +use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain, TxContextInput}; use miden_tx::auth::UnreachableAuth; use miden_tx::{NoteConsumptionChecker, TransactionExecutor}; diff --git a/bin/bench-transaction/Cargo.toml b/bin/bench-transaction/Cargo.toml index c5ed2cdb9e..9b07fccd84 100644 --- a/bin/bench-transaction/Cargo.toml +++ b/bin/bench-transaction/Cargo.toml @@ -17,10 +17,10 @@ path = "src/time_counting_benchmarks/prove.rs" [dependencies] # Workspace dependencies -miden-lib = { workspace = true } -miden-objects = { features = ["testing"], workspace = true } -miden-testing = { workspace = true } -miden-tx = { workspace = true } +miden-protocol = { features = ["testing"], workspace = true } +miden-standards = { workspace = true } +miden-testing = { workspace = true } +miden-tx = { workspace = true } # External dependencies anyhow = { workspace = true } diff --git a/bin/bench-transaction/src/context_setups.rs b/bin/bench-transaction/src/context_setups.rs index 90f91a22f0..6a136174ee 100644 --- a/bin/bench-transaction/src/context_setups.rs +++ b/bin/bench-transaction/src/context_setups.rs @@ -1,10 +1,10 @@ use anyhow::Result; -use miden_lib::code_builder::CodeBuilder; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::ACCOUNT_ID_SENDER; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, Word}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::ACCOUNT_ID_SENDER; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, Word}; +use miden_standards::code_builder::CodeBuilder; use miden_testing::{Auth, MockChain, TransactionContext}; /// Returns the transaction context which could be used to run the transaction which creates a @@ -25,7 +25,7 @@ pub fn tx_create_single_p2id_note() -> Result { let tx_note_creation_script = format!( " - use miden::output_note + use miden::protocol::output_note use miden::core::sys begin @@ -40,7 +40,7 @@ pub fn tx_create_single_p2id_note() -> Result { # move the asset to the note push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw # => [note_idx] diff --git a/bin/bench-transaction/src/cycle_counting_benchmarks/utils.rs b/bin/bench-transaction/src/cycle_counting_benchmarks/utils.rs index 1a5865ad1f..49a916c390 100644 --- a/bin/bench-transaction/src/cycle_counting_benchmarks/utils.rs +++ b/bin/bench-transaction/src/cycle_counting_benchmarks/utils.rs @@ -5,7 +5,7 @@ use std::fs::{read_to_string, write}; use std::path::Path; use anyhow::Context; -use miden_objects::transaction::TransactionMeasurements; +use miden_protocol::transaction::TransactionMeasurements; use serde::Serialize; use serde_json::{Value, from_str, to_string_pretty}; diff --git a/bin/bench-transaction/src/main.rs b/bin/bench-transaction/src/main.rs index c036936eea..651be7993b 100644 --- a/bin/bench-transaction/src/main.rs +++ b/bin/bench-transaction/src/main.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::path::Path; use anyhow::{Context, Result}; -use miden_objects::transaction::TransactionMeasurements; +use miden_protocol::transaction::TransactionMeasurements; mod context_setups; use context_setups::{ diff --git a/bin/bench-transaction/src/time_counting_benchmarks/prove.rs b/bin/bench-transaction/src/time_counting_benchmarks/prove.rs index 857fb57121..5dafb4604d 100644 --- a/bin/bench-transaction/src/time_counting_benchmarks/prove.rs +++ b/bin/bench-transaction/src/time_counting_benchmarks/prove.rs @@ -4,7 +4,7 @@ use std::time::Duration; use anyhow::Result; use bench_transaction::context_setups::{tx_consume_single_p2id_note, tx_consume_two_p2id_notes}; use criterion::{BatchSize, Criterion, SamplingMode, criterion_group, criterion_main}; -use miden_objects::transaction::{ExecutedTransaction, ProvenTransaction}; +use miden_protocol::transaction::{ExecutedTransaction, ProvenTransaction}; use miden_tx::LocalTransactionProver; // BENCHMARK NAMES diff --git a/crates/miden-block-prover/Cargo.toml b/crates/miden-block-prover/Cargo.toml index ed9bb534ff..be8732f5d7 100644 --- a/crates/miden-block-prover/Cargo.toml +++ b/crates/miden-block-prover/Cargo.toml @@ -19,5 +19,5 @@ bench = false testing = [] [dependencies] -miden-objects = { workspace = true } -thiserror = { workspace = true } +miden-protocol = { workspace = true } +thiserror = { workspace = true } diff --git a/crates/miden-block-prover/src/local_block_prover.rs b/crates/miden-block-prover/src/local_block_prover.rs index 6de931fa2d..4960d1a593 100644 --- a/crates/miden-block-prover/src/local_block_prover.rs +++ b/crates/miden-block-prover/src/local_block_prover.rs @@ -1,5 +1,5 @@ -use miden_objects::batch::OrderedBatches; -use miden_objects::block::{BlockHeader, BlockInputs, BlockProof}; +use miden_protocol::batch::OrderedBatches; +use miden_protocol::block::{BlockHeader, BlockInputs, BlockProof}; use crate::BlockProverError; diff --git a/crates/miden-protocol-macros/Cargo.toml b/crates/miden-protocol-macros/Cargo.toml index 030b01a60a..0ec8930785 100644 --- a/crates/miden-protocol-macros/Cargo.toml +++ b/crates/miden-protocol-macros/Cargo.toml @@ -21,7 +21,7 @@ quote = "1.0" syn = { features = ["extra-traits", "full"], version = "2.0" } [dev-dependencies] -miden-objects = { path = "../miden-objects" } +miden-protocol = { path = "../miden-protocol" } [package.metadata.cargo-machete] ignored = ["proc-macro2"] diff --git a/crates/miden-protocol-macros/tests/integration_test.rs b/crates/miden-protocol-macros/tests/integration_test.rs index c6c95165ce..05a925e4d5 100644 --- a/crates/miden-protocol-macros/tests/integration_test.rs +++ b/crates/miden-protocol-macros/tests/integration_test.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use miden_objects::{Felt, FieldElement, Word}; + use miden_protocol::{Felt, FieldElement, Word}; use miden_protocol_macros::WordWrapper; #[derive(Debug, Clone, Copy, PartialEq, Eq, WordWrapper)] diff --git a/crates/miden-objects/Cargo.toml b/crates/miden-protocol/Cargo.toml similarity index 94% rename from crates/miden-objects/Cargo.toml rename to crates/miden-protocol/Cargo.toml index 739f6a438d..ea4b9c629d 100644 --- a/crates/miden-objects/Cargo.toml +++ b/crates/miden-protocol/Cargo.toml @@ -4,9 +4,9 @@ categories = ["no-std"] description = "Core components of the Miden protocol" edition.workspace = true homepage.workspace = true -keywords = ["miden", "objects"] +keywords = ["miden", "protocol"] license.workspace = true -name = "miden-objects" +name = "miden-protocol" readme = "README.md" repository.workspace = true rust-version.workspace = true @@ -65,7 +65,7 @@ getrandom = { features = ["wasm_js"], version = "0.3" } anyhow = { features = ["backtrace", "std"], workspace = true } assert_matches = { workspace = true } criterion = { default-features = false, features = ["html_reports"], version = "0.5" } -miden-objects = { features = ["testing"], path = "." } +miden-protocol = { features = ["testing"], path = "." } pprof = { default-features = false, features = ["criterion", "flamegraph"], version = "0.15" } rstest = { workspace = true } tempfile = { version = "3.19" } diff --git a/crates/miden-objects/README.md b/crates/miden-protocol/README.md similarity index 97% rename from crates/miden-objects/README.md rename to crates/miden-protocol/README.md index bfa78937c5..c731a35159 100644 --- a/crates/miden-objects/README.md +++ b/crates/miden-protocol/README.md @@ -1,6 +1,6 @@ -# Miden Objects +# Miden Protocol -This crates contains core components defining the Miden protocol. +This crates contains core components defining the Miden protocol including the Miden protocol kernels. ## Modules diff --git a/crates/miden-objects/asm/kernels/transaction/api.masm b/crates/miden-protocol/asm/kernels/transaction/api.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/api.masm rename to crates/miden-protocol/asm/kernels/transaction/api.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/account.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm similarity index 99% rename from crates/miden-objects/asm/kernels/transaction/lib/account.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/account.masm index 1a13d31343..e5c7dca37e 100644 --- a/crates/miden-objects/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm @@ -64,7 +64,7 @@ const ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account # The name of the account storage slot at which faucet data is stored. # Fungible faucet: The faucet data consists of [0, 0, 0, total_issuance] # Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. -const FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") +const FAUCET_SYSDATA_SLOT=word("miden::protocol::faucet::sysdata") # The maximum storage slot index const MAX_STORAGE_SLOT_INDEX=254 diff --git a/crates/miden-objects/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/account_delta.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/asset.masm b/crates/miden-protocol/asm/kernels/transaction/lib/asset.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/asset.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/asset.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/asset_vault.masm b/crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/asset_vault.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/constants.masm b/crates/miden-protocol/asm/kernels/transaction/lib/constants.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/constants.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/constants.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/epilogue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/epilogue.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/faucet.masm b/crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/faucet.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/input_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/input_note.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/input_note.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/input_note.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/link_map.masm b/crates/miden-protocol/asm/kernels/transaction/lib/link_map.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/link_map.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/link_map.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/memory.masm b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/memory.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/memory.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/note.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/note.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm similarity index 99% rename from crates/miden-objects/asm/kernels/transaction/lib/output_note.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index b9c66a633e..44a0076621 100644 --- a/crates/miden-objects/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -146,7 +146,7 @@ pub proc get_assets_info # => [ASSETS_COMMITMENT, note_data_ptr, num_assets] # next we should store the assets in the advice map using the computed assets commitment to be - # able to get the assets later (in the `miden::output_note::get_assets` procedure) + # able to get the assets later (in the `miden::protocol::output_note::get_assets` procedure) # get the start and the end pointers of the asset data # diff --git a/crates/miden-objects/asm/kernels/transaction/lib/prologue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/prologue.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm diff --git a/crates/miden-objects/asm/kernels/transaction/lib/tx.masm b/crates/miden-protocol/asm/kernels/transaction/lib/tx.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/lib/tx.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/tx.masm diff --git a/crates/miden-objects/asm/kernels/transaction/main.masm b/crates/miden-protocol/asm/kernels/transaction/main.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/main.masm rename to crates/miden-protocol/asm/kernels/transaction/main.masm diff --git a/crates/miden-objects/asm/kernels/transaction/tx_script_main.masm b/crates/miden-protocol/asm/kernels/transaction/tx_script_main.masm similarity index 100% rename from crates/miden-objects/asm/kernels/transaction/tx_script_main.masm rename to crates/miden-protocol/asm/kernels/transaction/tx_script_main.masm diff --git a/crates/miden-objects/asm/miden/active_account.masm b/crates/miden-protocol/asm/protocol/active_account.masm similarity index 99% rename from crates/miden-objects/asm/miden/active_account.masm rename to crates/miden-protocol/asm/protocol/active_account.masm index 58b69c0214..9f69e30cc1 100644 --- a/crates/miden-objects/asm/miden/active_account.masm +++ b/crates/miden-protocol/asm/protocol/active_account.masm @@ -1,4 +1,4 @@ -use miden::kernel_proc_offsets +use miden::protocol::kernel_proc_offsets # ACTIVE ACCOUNT PROCEDURES # ================================================================================================= diff --git a/crates/miden-objects/asm/miden/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm similarity index 99% rename from crates/miden-objects/asm/miden/active_note.masm rename to crates/miden-protocol/asm/protocol/active_note.masm index 533f248077..3a32919912 100644 --- a/crates/miden-objects/asm/miden/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -1,7 +1,7 @@ use miden::core::mem -use miden::kernel_proc_offsets -use miden::note +use miden::protocol::kernel_proc_offsets +use miden::protocol::note # ERRORS # ================================================================================================= diff --git a/crates/miden-objects/asm/miden/asset.masm b/crates/miden-protocol/asm/protocol/asset.masm similarity index 96% rename from crates/miden-objects/asm/miden/asset.masm rename to crates/miden-protocol/asm/protocol/asset.masm index 4a7e612fdd..e8da408a8a 100644 --- a/crates/miden-objects/asm/miden/asset.masm +++ b/crates/miden-protocol/asm/protocol/asset.masm @@ -1,4 +1,4 @@ -use miden::account_id +use miden::protocol::account_id # ERRORS # ================================================================================================= @@ -69,4 +69,4 @@ end #! Outputs: [fungible_asset_max_amount] #! #! fungible_asset_max_amount is the maximum amount of a fungible asset. -pub use ::miden::util::asset::get_fungible_asset_max_amount +pub use ::miden::protocol::util::asset::get_fungible_asset_max_amount diff --git a/crates/miden-objects/asm/miden/faucet.masm b/crates/miden-protocol/asm/protocol/faucet.masm similarity index 97% rename from crates/miden-objects/asm/miden/faucet.masm rename to crates/miden-protocol/asm/protocol/faucet.masm index 13230e7ca7..f3afe38e9b 100644 --- a/crates/miden-objects/asm/miden/faucet.masm +++ b/crates/miden-protocol/asm/protocol/faucet.masm @@ -1,6 +1,6 @@ -use miden::asset -use miden::active_account -use miden::kernel_proc_offsets +use miden::protocol::asset +use miden::protocol::active_account +use miden::protocol::kernel_proc_offsets #! Creates a fungible asset for the faucet the transaction is being executed against. #! diff --git a/crates/miden-objects/asm/miden/input_note.masm b/crates/miden-protocol/asm/protocol/input_note.masm similarity index 99% rename from crates/miden-objects/asm/miden/input_note.masm rename to crates/miden-protocol/asm/protocol/input_note.masm index 722414e29c..9a88dc7636 100644 --- a/crates/miden-objects/asm/miden/input_note.masm +++ b/crates/miden-protocol/asm/protocol/input_note.masm @@ -1,5 +1,5 @@ -use miden::kernel_proc_offsets -use miden::note +use miden::protocol::kernel_proc_offsets +use miden::protocol::note # PROCEDURES # ================================================================================================= diff --git a/crates/miden-objects/asm/miden/kernel_proc_offsets.masm b/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm similarity index 100% rename from crates/miden-objects/asm/miden/kernel_proc_offsets.masm rename to crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm diff --git a/crates/miden-objects/asm/miden/native_account.masm b/crates/miden-protocol/asm/protocol/native_account.masm similarity index 99% rename from crates/miden-objects/asm/miden/native_account.masm rename to crates/miden-protocol/asm/protocol/native_account.masm index 4810682e06..1ca40bdad0 100644 --- a/crates/miden-objects/asm/miden/native_account.masm +++ b/crates/miden-protocol/asm/protocol/native_account.masm @@ -1,4 +1,4 @@ -use miden::kernel_proc_offsets +use miden::protocol::kernel_proc_offsets # NATIVE ACCOUNT PROCEDURES # ================================================================================================= diff --git a/crates/miden-objects/asm/miden/note.masm b/crates/miden-protocol/asm/protocol/note.masm similarity index 98% rename from crates/miden-objects/asm/miden/note.masm rename to crates/miden-protocol/asm/protocol/note.masm index e2b62646a6..ea7ac724f4 100644 --- a/crates/miden-objects/asm/miden/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -1,4 +1,4 @@ -use miden::account_id +use miden::protocol::account_id use miden::core::crypto::hashes::rpo256 use miden::core::math::u64 use miden::core::mem @@ -53,7 +53,7 @@ end #! Output: [max_inputs_per_note] #! #! - max_inputs_per_note is the max inputs per note. -pub use ::miden::util::note::get_max_inputs_per_note +pub use ::miden::protocol::util::note::get_max_inputs_per_note #! Writes the assets data stored in the advice map to the memory specified by the provided #! destination pointer. diff --git a/crates/miden-objects/asm/miden/output_note.masm b/crates/miden-protocol/asm/protocol/output_note.masm similarity index 98% rename from crates/miden-objects/asm/miden/output_note.masm rename to crates/miden-protocol/asm/protocol/output_note.masm index 52f713137d..3257be6172 100644 --- a/crates/miden-objects/asm/miden/output_note.masm +++ b/crates/miden-protocol/asm/protocol/output_note.masm @@ -1,5 +1,5 @@ -use miden::kernel_proc_offsets -use miden::note +use miden::protocol::kernel_proc_offsets +use miden::protocol::note # PROCEDURES # ================================================================================================= diff --git a/crates/miden-objects/asm/miden/tx.masm b/crates/miden-protocol/asm/protocol/tx.masm similarity index 99% rename from crates/miden-objects/asm/miden/tx.masm rename to crates/miden-protocol/asm/protocol/tx.masm index fd4031f0cf..b55b354ebb 100644 --- a/crates/miden-objects/asm/miden/tx.masm +++ b/crates/miden-protocol/asm/protocol/tx.masm @@ -1,4 +1,4 @@ -use miden::kernel_proc_offsets +use miden::protocol::kernel_proc_offsets #! Returns the block number of the transaction reference block. #! diff --git a/crates/miden-objects/asm/shared_modules/account_id.masm b/crates/miden-protocol/asm/shared_modules/account_id.masm similarity index 100% rename from crates/miden-objects/asm/shared_modules/account_id.masm rename to crates/miden-protocol/asm/shared_modules/account_id.masm diff --git a/crates/miden-objects/asm/shared_utils/util/asset.masm b/crates/miden-protocol/asm/shared_utils/util/asset.masm similarity index 100% rename from crates/miden-objects/asm/shared_utils/util/asset.masm rename to crates/miden-protocol/asm/shared_utils/util/asset.masm diff --git a/crates/miden-objects/asm/shared_utils/util/note.masm b/crates/miden-protocol/asm/shared_utils/util/note.masm similarity index 100% rename from crates/miden-objects/asm/shared_utils/util/note.masm rename to crates/miden-protocol/asm/shared_utils/util/note.masm diff --git a/crates/miden-objects/benches/account_seed.rs b/crates/miden-protocol/benches/account_seed.rs similarity index 90% rename from crates/miden-objects/benches/account_seed.rs rename to crates/miden-protocol/benches/account_seed.rs index 8e37e84228..77f6c107a2 100644 --- a/crates/miden-objects/benches/account_seed.rs +++ b/crates/miden-protocol/benches/account_seed.rs @@ -1,8 +1,8 @@ use std::time::Duration; use criterion::{Criterion, criterion_group, criterion_main}; -use miden_objects::Word; -use miden_objects::account::{AccountId, AccountIdVersion, AccountStorageMode, AccountType}; +use miden_protocol::Word; +use miden_protocol::account::{AccountId, AccountIdVersion, AccountStorageMode, AccountType}; use rand::{Rng, SeedableRng}; /// Running this benchmark with --no-default-features will use the single-threaded account seed @@ -13,7 +13,7 @@ use rand::{Rng, SeedableRng}; /// To produce a flamegraph, run with the `--profile-time` argument. /// /// ```sh -/// cargo bench -p miden-objects --no-default-features -- --profile-time 10 +/// cargo bench -p miden-protocol --no-default-features -- --profile-time 10 /// ``` /// /// The flamegraph will be saved as `target/criterion/grind-seed/Grind regular public account diff --git a/crates/miden-objects/build.rs b/crates/miden-protocol/build.rs similarity index 95% rename from crates/miden-objects/build.rs rename to crates/miden-protocol/build.rs index 2a28d2fd38..27d1b96d4b 100644 --- a/crates/miden-objects/build.rs +++ b/crates/miden-protocol/build.rs @@ -19,13 +19,15 @@ const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN const ASSETS_DIR: &str = "assets"; const ASM_DIR: &str = "asm"; -const ASM_MIDEN_DIR: &str = "miden"; +const ASM_PROTOCOL_DIR: &str = "protocol"; const SHARED_UTILS_DIR: &str = "shared_utils"; const SHARED_MODULES_DIR: &str = "shared_modules"; const ASM_TX_KERNEL_DIR: &str = "kernels/transaction"; const KERNEL_PROCEDURES_RS_FILE: &str = "src/transaction/kernel/procedures.rs"; +const PROTOCOL_LIB_NAMESPACE: &str = "miden::protocol"; + const TX_KERNEL_ERRORS_FILE: &str = "src/errors/tx_kernel.rs"; const PROTOCOL_LIB_ERRORS_FILE: &str = "src/errors/protocol.rs"; @@ -53,9 +55,9 @@ const TX_KERNEL_ERROR_CATEGORIES: [&str; 14] = [ // ================================================================================================ /// Read and parse the contents from `./asm`. -/// - Compiles contents of asm/miden directory into a Miden library file (.masl) under miden -/// namespace. -/// - Compiles contents of asm/scripts directory into individual .masb files. +/// - Compiles the contents of asm/protocol directory into a Protocol library file (.masl) under +/// miden::protocol namespace. +/// - Compiles the contents of asm/kernels into the transaction kernel library. fn main() -> Result<()> { // re-build when the MASM code changes println!("cargo::rerun-if-changed={ASM_DIR}/"); @@ -71,7 +73,7 @@ fn main() -> Result<()> { // set source directory to {OUT_DIR}/asm let source_dir = dst.join(ASM_DIR); - // copy the shared modules to the kernel and miden library folders + // copy the shared modules to the kernel and protocol library folders copy_shared_modules(&source_dir)?; // set target directory to {OUT_DIR}/assets @@ -201,7 +203,8 @@ fn generate_kernel_proc_hash_file(kernel: KernelLibrary) -> Result<()> { let (_, module_info, _) = kernel.into_parts(); let to_exclude = BTreeSet::from_iter(["exec_kernel_proc"]); - let offsets_filename = Path::new(ASM_DIR).join(ASM_MIDEN_DIR).join("kernel_proc_offsets.masm"); + let offsets_filename = + Path::new(ASM_DIR).join(ASM_PROTOCOL_DIR).join("kernel_proc_offsets.masm"); let offsets = parse_proc_offsets(&offsets_filename)?; let generated_procs: BTreeMap = module_info @@ -265,22 +268,22 @@ fn parse_proc_offsets(filename: impl AsRef) -> Result Result { - let source_dir = source_dir.join(ASM_MIDEN_DIR); + let source_dir = source_dir.join(ASM_PROTOCOL_DIR); let shared_path = Path::new(ASM_DIR).join(SHARED_UTILS_DIR); - // add the shared modules to the protocol lib under the miden::util namespace + // add the shared modules to the protocol lib under the miden::protocol::util namespace // note that this module is not publicly exported, it is only available for linking the library // itself - assembler.compile_and_statically_link_from_dir(&shared_path, "miden")?; + assembler.compile_and_statically_link_from_dir(&shared_path, PROTOCOL_LIB_NAMESPACE)?; - let protocol_lib = assembler.assemble_library_from_dir(source_dir, "miden")?; + let protocol_lib = assembler.assemble_library_from_dir(source_dir, PROTOCOL_LIB_NAMESPACE)?; let output_file = target_dir.join("protocol").with_extension(Library::LIBRARY_EXTENSION); protocol_lib.write_to_file(output_file).into_diagnostic()?; @@ -299,8 +302,9 @@ fn build_assembler(kernel: Option) -> Result { .with_dynamic_library(miden_core_lib::CoreLibrary::default()) } -/// Copies the content of the build `shared_modules` folder to the `lib` and `miden` build folders. -/// This is required to include the shared modules as APIs of the `kernel` and `miden` libraries. +/// Copies the content of the build `shared_modules` folder to the `lib` and `protocol` build +/// folders. This is required to include the shared modules as APIs of the `kernel` and `protocol` +/// libraries. /// /// This is done to make it possible to import the modules in the `shared_modules` folder directly, /// i.e. "use $kernel::account_id". @@ -315,9 +319,9 @@ fn copy_shared_modules>(source_dir: T) -> Result<()> { let kernel_lib_folder = source_dir.as_ref().join(ASM_TX_KERNEL_DIR).join("lib"); fs::copy(&module_path, kernel_lib_folder.join(module_name)).into_diagnostic()?; - // copy to miden lib - let miden_lib_folder = source_dir.as_ref().join(ASM_MIDEN_DIR); - fs::copy(&module_path, miden_lib_folder.join(module_name)).into_diagnostic()?; + // copy to protocol lib + let protocol_lib_folder = source_dir.as_ref().join(ASM_PROTOCOL_DIR); + fs::copy(&module_path, protocol_lib_folder.join(module_name)).into_diagnostic()?; } Ok(()) @@ -376,9 +380,9 @@ fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { // Miden protocol library errors // ------------------------------------------ - let miden_dir = asm_source_dir.join(ASM_MIDEN_DIR); - let errors = - shared::extract_all_masm_errors(&miden_dir).context("failed to extract all masm errors")?; + let protocol_dir = asm_source_dir.join(ASM_PROTOCOL_DIR); + let errors = shared::extract_all_masm_errors(&protocol_dir) + .context("failed to extract all masm errors")?; shared::generate_error_file( shared::ErrorModule { @@ -746,13 +750,13 @@ mod shared { if module.is_crate_local { writeln!(output, "use crate::errors::MasmError;\n").unwrap(); } else { - writeln!(output, "use miden_objects::errors::MasmError;\n").unwrap(); + writeln!(output, "use miden_protocol::errors::MasmError;\n").unwrap(); } writeln!( output, "// This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `./asm` directory. // // To add a new error, define a constant in MASM of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). diff --git a/crates/miden-lib/masm_doc_comment_fmt.md b/crates/miden-protocol/masm_doc_comment_fmt.md similarity index 100% rename from crates/miden-lib/masm_doc_comment_fmt.md rename to crates/miden-protocol/masm_doc_comment_fmt.md diff --git a/crates/miden-objects/src/account/account_id/account_type.rs b/crates/miden-protocol/src/account/account_id/account_type.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/account_type.rs rename to crates/miden-protocol/src/account/account_id/account_type.rs diff --git a/crates/miden-objects/src/account/account_id/id_prefix.rs b/crates/miden-protocol/src/account/account_id/id_prefix.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/id_prefix.rs rename to crates/miden-protocol/src/account/account_id/id_prefix.rs diff --git a/crates/miden-objects/src/account/account_id/id_version.rs b/crates/miden-protocol/src/account/account_id/id_version.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/id_version.rs rename to crates/miden-protocol/src/account/account_id/id_version.rs diff --git a/crates/miden-objects/src/account/account_id/mod.rs b/crates/miden-protocol/src/account/account_id/mod.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/mod.rs rename to crates/miden-protocol/src/account/account_id/mod.rs diff --git a/crates/miden-objects/src/account/account_id/seed.rs b/crates/miden-protocol/src/account/account_id/seed.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/seed.rs rename to crates/miden-protocol/src/account/account_id/seed.rs diff --git a/crates/miden-objects/src/account/account_id/storage_mode.rs b/crates/miden-protocol/src/account/account_id/storage_mode.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/storage_mode.rs rename to crates/miden-protocol/src/account/account_id/storage_mode.rs diff --git a/crates/miden-objects/src/account/account_id/v0/mod.rs b/crates/miden-protocol/src/account/account_id/v0/mod.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/v0/mod.rs rename to crates/miden-protocol/src/account/account_id/v0/mod.rs diff --git a/crates/miden-objects/src/account/account_id/v0/prefix.rs b/crates/miden-protocol/src/account/account_id/v0/prefix.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/v0/prefix.rs rename to crates/miden-protocol/src/account/account_id/v0/prefix.rs diff --git a/crates/miden-objects/src/account/auth.rs b/crates/miden-protocol/src/account/auth.rs similarity index 99% rename from crates/miden-objects/src/account/auth.rs rename to crates/miden-protocol/src/account/auth.rs index 7c0edc1cbb..a3fc47a577 100644 --- a/crates/miden-objects/src/account/auth.rs +++ b/crates/miden-protocol/src/account/auth.rs @@ -276,9 +276,9 @@ impl Deserializable for PublicKey { /// convert the native signature into a vector of field elements that can be loaded into the advice /// provider. To prepare the signature, use the provided `to_prepared_signature` method: /// ```rust,no_run -/// use miden_objects::account::auth::Signature; -/// use miden_objects::crypto::dsa::falcon512_rpo::SecretKey; -/// use miden_objects::{Felt, Word}; +/// use miden_protocol::account::auth::Signature; +/// use miden_protocol::crypto::dsa::falcon512_rpo::SecretKey; +/// use miden_protocol::{Felt, Word}; /// /// let secret_key = SecretKey::new(); /// let message = Word::default(); diff --git a/crates/miden-objects/src/account/builder/mod.rs b/crates/miden-protocol/src/account/builder/mod.rs similarity index 100% rename from crates/miden-objects/src/account/builder/mod.rs rename to crates/miden-protocol/src/account/builder/mod.rs diff --git a/crates/miden-objects/src/account/code/header.rs b/crates/miden-protocol/src/account/code/header.rs similarity index 100% rename from crates/miden-objects/src/account/code/header.rs rename to crates/miden-protocol/src/account/code/header.rs diff --git a/crates/miden-objects/src/account/code/mod.rs b/crates/miden-protocol/src/account/code/mod.rs similarity index 100% rename from crates/miden-objects/src/account/code/mod.rs rename to crates/miden-protocol/src/account/code/mod.rs diff --git a/crates/miden-objects/src/account/code/procedure.rs b/crates/miden-protocol/src/account/code/procedure.rs similarity index 100% rename from crates/miden-objects/src/account/code/procedure.rs rename to crates/miden-protocol/src/account/code/procedure.rs diff --git a/crates/miden-objects/src/account/component/code.rs b/crates/miden-protocol/src/account/component/code.rs similarity index 100% rename from crates/miden-objects/src/account/component/code.rs rename to crates/miden-protocol/src/account/component/code.rs diff --git a/crates/miden-objects/src/account/component/metadata/mod.rs b/crates/miden-protocol/src/account/component/metadata/mod.rs similarity index 98% rename from crates/miden-objects/src/account/component/metadata/mod.rs rename to crates/miden-protocol/src/account/component/metadata/mod.rs index 961ccacfbb..619e8cc031 100644 --- a/crates/miden-objects/src/account/component/metadata/mod.rs +++ b/crates/miden-protocol/src/account/component/metadata/mod.rs @@ -37,8 +37,8 @@ use crate::AccountError; /// ``` /// use std::collections::BTreeSet; /// -/// use miden_objects::account::StorageSlotName; -/// use miden_objects::account::component::{ +/// use miden_protocol::account::StorageSlotName; +/// use miden_protocol::account::component::{ /// AccountComponentMetadata, /// AccountStorageSchema, /// FeltSchema, diff --git a/crates/miden-objects/src/account/component/mod.rs b/crates/miden-protocol/src/account/component/mod.rs similarity index 100% rename from crates/miden-objects/src/account/component/mod.rs rename to crates/miden-protocol/src/account/component/mod.rs diff --git a/crates/miden-objects/src/account/component/storage/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/init_storage_data.rs rename to crates/miden-protocol/src/account/component/storage/init_storage_data.rs diff --git a/crates/miden-objects/src/account/component/storage/mod.rs b/crates/miden-protocol/src/account/component/storage/mod.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/mod.rs rename to crates/miden-protocol/src/account/component/storage/mod.rs diff --git a/crates/miden-objects/src/account/component/storage/schema.rs b/crates/miden-protocol/src/account/component/storage/schema.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/schema.rs rename to crates/miden-protocol/src/account/component/storage/schema.rs diff --git a/crates/miden-objects/src/account/component/storage/toml/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/toml/init_storage_data.rs rename to crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs diff --git a/crates/miden-objects/src/account/component/storage/toml/mod.rs b/crates/miden-protocol/src/account/component/storage/toml/mod.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/toml/mod.rs rename to crates/miden-protocol/src/account/component/storage/toml/mod.rs diff --git a/crates/miden-objects/src/account/component/storage/toml/serde_impls.rs b/crates/miden-protocol/src/account/component/storage/toml/serde_impls.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/toml/serde_impls.rs rename to crates/miden-protocol/src/account/component/storage/toml/serde_impls.rs diff --git a/crates/miden-objects/src/account/component/storage/toml/tests.rs b/crates/miden-protocol/src/account/component/storage/toml/tests.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/toml/tests.rs rename to crates/miden-protocol/src/account/component/storage/toml/tests.rs diff --git a/crates/miden-objects/src/account/component/storage/type_registry.rs b/crates/miden-protocol/src/account/component/storage/type_registry.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/type_registry.rs rename to crates/miden-protocol/src/account/component/storage/type_registry.rs diff --git a/crates/miden-objects/src/account/component/storage/value_name.rs b/crates/miden-protocol/src/account/component/storage/value_name.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/value_name.rs rename to crates/miden-protocol/src/account/component/storage/value_name.rs diff --git a/crates/miden-objects/src/account/delta/mod.rs b/crates/miden-protocol/src/account/delta/mod.rs similarity index 100% rename from crates/miden-objects/src/account/delta/mod.rs rename to crates/miden-protocol/src/account/delta/mod.rs diff --git a/crates/miden-objects/src/account/delta/storage.rs b/crates/miden-protocol/src/account/delta/storage.rs similarity index 100% rename from crates/miden-objects/src/account/delta/storage.rs rename to crates/miden-protocol/src/account/delta/storage.rs diff --git a/crates/miden-objects/src/account/delta/vault.rs b/crates/miden-protocol/src/account/delta/vault.rs similarity index 100% rename from crates/miden-objects/src/account/delta/vault.rs rename to crates/miden-protocol/src/account/delta/vault.rs diff --git a/crates/miden-objects/src/account/file.rs b/crates/miden-protocol/src/account/file.rs similarity index 100% rename from crates/miden-objects/src/account/file.rs rename to crates/miden-protocol/src/account/file.rs diff --git a/crates/miden-objects/src/account/header.rs b/crates/miden-protocol/src/account/header.rs similarity index 100% rename from crates/miden-objects/src/account/header.rs rename to crates/miden-protocol/src/account/header.rs diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-protocol/src/account/mod.rs similarity index 100% rename from crates/miden-objects/src/account/mod.rs rename to crates/miden-protocol/src/account/mod.rs diff --git a/crates/miden-objects/src/account/partial.rs b/crates/miden-protocol/src/account/partial.rs similarity index 100% rename from crates/miden-objects/src/account/partial.rs rename to crates/miden-protocol/src/account/partial.rs diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-protocol/src/account/storage/header.rs similarity index 100% rename from crates/miden-objects/src/account/storage/header.rs rename to crates/miden-protocol/src/account/storage/header.rs diff --git a/crates/miden-objects/src/account/storage/map/mod.rs b/crates/miden-protocol/src/account/storage/map/mod.rs similarity index 100% rename from crates/miden-objects/src/account/storage/map/mod.rs rename to crates/miden-protocol/src/account/storage/map/mod.rs diff --git a/crates/miden-objects/src/account/storage/map/partial.rs b/crates/miden-protocol/src/account/storage/map/partial.rs similarity index 100% rename from crates/miden-objects/src/account/storage/map/partial.rs rename to crates/miden-protocol/src/account/storage/map/partial.rs diff --git a/crates/miden-objects/src/account/storage/map/witness.rs b/crates/miden-protocol/src/account/storage/map/witness.rs similarity index 100% rename from crates/miden-objects/src/account/storage/map/witness.rs rename to crates/miden-protocol/src/account/storage/map/witness.rs diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-protocol/src/account/storage/mod.rs similarity index 99% rename from crates/miden-objects/src/account/storage/mod.rs rename to crates/miden-protocol/src/account/storage/mod.rs index 6109a2c8a3..6947fd4629 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-protocol/src/account/storage/mod.rs @@ -29,7 +29,8 @@ mod partial; pub use partial::PartialStorage; static FAUCET_SYSDATA_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::faucet::sysdata").expect("storage slot name should be valid") + StorageSlotName::new("miden::protocol::faucet::sysdata") + .expect("storage slot name should be valid") }); // ACCOUNT STORAGE diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-protocol/src/account/storage/partial.rs similarity index 100% rename from crates/miden-objects/src/account/storage/partial.rs rename to crates/miden-protocol/src/account/storage/partial.rs diff --git a/crates/miden-objects/src/account/storage/slot/mod.rs b/crates/miden-protocol/src/account/storage/slot/mod.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/mod.rs rename to crates/miden-protocol/src/account/storage/slot/mod.rs diff --git a/crates/miden-objects/src/account/storage/slot/slot_content.rs b/crates/miden-protocol/src/account/storage/slot/slot_content.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/slot_content.rs rename to crates/miden-protocol/src/account/storage/slot/slot_content.rs diff --git a/crates/miden-objects/src/account/storage/slot/slot_id.rs b/crates/miden-protocol/src/account/storage/slot/slot_id.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/slot_id.rs rename to crates/miden-protocol/src/account/storage/slot/slot_id.rs diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-protocol/src/account/storage/slot/slot_name.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/slot_name.rs rename to crates/miden-protocol/src/account/storage/slot/slot_name.rs diff --git a/crates/miden-objects/src/account/storage/slot/storage_slot.rs b/crates/miden-protocol/src/account/storage/slot/storage_slot.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/storage_slot.rs rename to crates/miden-protocol/src/account/storage/slot/storage_slot.rs diff --git a/crates/miden-objects/src/account/storage/slot/type.rs b/crates/miden-protocol/src/account/storage/slot/type.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/type.rs rename to crates/miden-protocol/src/account/storage/slot/type.rs diff --git a/crates/miden-objects/src/address/address_id.rs b/crates/miden-protocol/src/address/address_id.rs similarity index 100% rename from crates/miden-objects/src/address/address_id.rs rename to crates/miden-protocol/src/address/address_id.rs diff --git a/crates/miden-objects/src/address/interface.rs b/crates/miden-protocol/src/address/interface.rs similarity index 100% rename from crates/miden-objects/src/address/interface.rs rename to crates/miden-protocol/src/address/interface.rs diff --git a/crates/miden-objects/src/address/mod.rs b/crates/miden-protocol/src/address/mod.rs similarity index 100% rename from crates/miden-objects/src/address/mod.rs rename to crates/miden-protocol/src/address/mod.rs diff --git a/crates/miden-objects/src/address/network_id.rs b/crates/miden-protocol/src/address/network_id.rs similarity index 100% rename from crates/miden-objects/src/address/network_id.rs rename to crates/miden-protocol/src/address/network_id.rs diff --git a/crates/miden-objects/src/address/routing_parameters.rs b/crates/miden-protocol/src/address/routing_parameters.rs similarity index 100% rename from crates/miden-objects/src/address/routing_parameters.rs rename to crates/miden-protocol/src/address/routing_parameters.rs diff --git a/crates/miden-objects/src/address/type.rs b/crates/miden-protocol/src/address/type.rs similarity index 100% rename from crates/miden-objects/src/address/type.rs rename to crates/miden-protocol/src/address/type.rs diff --git a/crates/miden-objects/src/asset/fungible.rs b/crates/miden-protocol/src/asset/fungible.rs similarity index 100% rename from crates/miden-objects/src/asset/fungible.rs rename to crates/miden-protocol/src/asset/fungible.rs diff --git a/crates/miden-objects/src/asset/mod.rs b/crates/miden-protocol/src/asset/mod.rs similarity index 100% rename from crates/miden-objects/src/asset/mod.rs rename to crates/miden-protocol/src/asset/mod.rs diff --git a/crates/miden-objects/src/asset/nonfungible.rs b/crates/miden-protocol/src/asset/nonfungible.rs similarity index 100% rename from crates/miden-objects/src/asset/nonfungible.rs rename to crates/miden-protocol/src/asset/nonfungible.rs diff --git a/crates/miden-objects/src/asset/token_symbol.rs b/crates/miden-protocol/src/asset/token_symbol.rs similarity index 99% rename from crates/miden-objects/src/asset/token_symbol.rs rename to crates/miden-protocol/src/asset/token_symbol.rs index 289c66443a..a1132fd396 100644 --- a/crates/miden-objects/src/asset/token_symbol.rs +++ b/crates/miden-protocol/src/asset/token_symbol.rs @@ -25,7 +25,7 @@ impl TokenSymbol { /// This function is `const` and can be used to define token symbols as constants, e.g.: /// /// ```rust - /// # use miden_objects::asset::TokenSymbol; + /// # use miden_protocol::asset::TokenSymbol; /// const TOKEN: TokenSymbol = TokenSymbol::from_static_str("ETH"); /// ``` /// diff --git a/crates/miden-objects/src/asset/vault/asset_witness.rs b/crates/miden-protocol/src/asset/vault/asset_witness.rs similarity index 100% rename from crates/miden-objects/src/asset/vault/asset_witness.rs rename to crates/miden-protocol/src/asset/vault/asset_witness.rs diff --git a/crates/miden-objects/src/asset/vault/mod.rs b/crates/miden-protocol/src/asset/vault/mod.rs similarity index 100% rename from crates/miden-objects/src/asset/vault/mod.rs rename to crates/miden-protocol/src/asset/vault/mod.rs diff --git a/crates/miden-objects/src/asset/vault/partial.rs b/crates/miden-protocol/src/asset/vault/partial.rs similarity index 100% rename from crates/miden-objects/src/asset/vault/partial.rs rename to crates/miden-protocol/src/asset/vault/partial.rs diff --git a/crates/miden-objects/src/asset/vault/vault_key.rs b/crates/miden-protocol/src/asset/vault/vault_key.rs similarity index 100% rename from crates/miden-objects/src/asset/vault/vault_key.rs rename to crates/miden-protocol/src/asset/vault/vault_key.rs diff --git a/crates/miden-objects/src/batch/account_update.rs b/crates/miden-protocol/src/batch/account_update.rs similarity index 100% rename from crates/miden-objects/src/batch/account_update.rs rename to crates/miden-protocol/src/batch/account_update.rs diff --git a/crates/miden-objects/src/batch/batch_id.rs b/crates/miden-protocol/src/batch/batch_id.rs similarity index 100% rename from crates/miden-objects/src/batch/batch_id.rs rename to crates/miden-protocol/src/batch/batch_id.rs diff --git a/crates/miden-objects/src/batch/input_output_note_tracker.rs b/crates/miden-protocol/src/batch/input_output_note_tracker.rs similarity index 100% rename from crates/miden-objects/src/batch/input_output_note_tracker.rs rename to crates/miden-protocol/src/batch/input_output_note_tracker.rs diff --git a/crates/miden-objects/src/batch/mod.rs b/crates/miden-protocol/src/batch/mod.rs similarity index 100% rename from crates/miden-objects/src/batch/mod.rs rename to crates/miden-protocol/src/batch/mod.rs diff --git a/crates/miden-objects/src/batch/note_tree.rs b/crates/miden-protocol/src/batch/note_tree.rs similarity index 100% rename from crates/miden-objects/src/batch/note_tree.rs rename to crates/miden-protocol/src/batch/note_tree.rs diff --git a/crates/miden-objects/src/batch/ordered_batches.rs b/crates/miden-protocol/src/batch/ordered_batches.rs similarity index 100% rename from crates/miden-objects/src/batch/ordered_batches.rs rename to crates/miden-protocol/src/batch/ordered_batches.rs diff --git a/crates/miden-objects/src/batch/proposed_batch.rs b/crates/miden-protocol/src/batch/proposed_batch.rs similarity index 100% rename from crates/miden-objects/src/batch/proposed_batch.rs rename to crates/miden-protocol/src/batch/proposed_batch.rs diff --git a/crates/miden-objects/src/batch/proven_batch.rs b/crates/miden-protocol/src/batch/proven_batch.rs similarity index 100% rename from crates/miden-objects/src/batch/proven_batch.rs rename to crates/miden-protocol/src/batch/proven_batch.rs diff --git a/crates/miden-objects/src/block/account_tree/backend.rs b/crates/miden-protocol/src/block/account_tree/backend.rs similarity index 100% rename from crates/miden-objects/src/block/account_tree/backend.rs rename to crates/miden-protocol/src/block/account_tree/backend.rs diff --git a/crates/miden-objects/src/block/account_tree/mod.rs b/crates/miden-protocol/src/block/account_tree/mod.rs similarity index 100% rename from crates/miden-objects/src/block/account_tree/mod.rs rename to crates/miden-protocol/src/block/account_tree/mod.rs diff --git a/crates/miden-objects/src/block/account_tree/partial.rs b/crates/miden-protocol/src/block/account_tree/partial.rs similarity index 100% rename from crates/miden-objects/src/block/account_tree/partial.rs rename to crates/miden-protocol/src/block/account_tree/partial.rs diff --git a/crates/miden-objects/src/block/account_tree/witness.rs b/crates/miden-protocol/src/block/account_tree/witness.rs similarity index 100% rename from crates/miden-objects/src/block/account_tree/witness.rs rename to crates/miden-protocol/src/block/account_tree/witness.rs diff --git a/crates/miden-objects/src/block/account_update_witness.rs b/crates/miden-protocol/src/block/account_update_witness.rs similarity index 100% rename from crates/miden-objects/src/block/account_update_witness.rs rename to crates/miden-protocol/src/block/account_update_witness.rs diff --git a/crates/miden-objects/src/block/block_account_update.rs b/crates/miden-protocol/src/block/block_account_update.rs similarity index 100% rename from crates/miden-objects/src/block/block_account_update.rs rename to crates/miden-protocol/src/block/block_account_update.rs diff --git a/crates/miden-objects/src/block/block_body.rs b/crates/miden-protocol/src/block/block_body.rs similarity index 100% rename from crates/miden-objects/src/block/block_body.rs rename to crates/miden-protocol/src/block/block_body.rs diff --git a/crates/miden-objects/src/block/block_inputs.rs b/crates/miden-protocol/src/block/block_inputs.rs similarity index 100% rename from crates/miden-objects/src/block/block_inputs.rs rename to crates/miden-protocol/src/block/block_inputs.rs diff --git a/crates/miden-objects/src/block/block_number.rs b/crates/miden-protocol/src/block/block_number.rs similarity index 100% rename from crates/miden-objects/src/block/block_number.rs rename to crates/miden-protocol/src/block/block_number.rs diff --git a/crates/miden-objects/src/block/block_proof.rs b/crates/miden-protocol/src/block/block_proof.rs similarity index 100% rename from crates/miden-objects/src/block/block_proof.rs rename to crates/miden-protocol/src/block/block_proof.rs diff --git a/crates/miden-objects/src/block/blockchain.rs b/crates/miden-protocol/src/block/blockchain.rs similarity index 100% rename from crates/miden-objects/src/block/blockchain.rs rename to crates/miden-protocol/src/block/blockchain.rs diff --git a/crates/miden-objects/src/block/header.rs b/crates/miden-protocol/src/block/header.rs similarity index 100% rename from crates/miden-objects/src/block/header.rs rename to crates/miden-protocol/src/block/header.rs diff --git a/crates/miden-objects/src/block/mod.rs b/crates/miden-protocol/src/block/mod.rs similarity index 100% rename from crates/miden-objects/src/block/mod.rs rename to crates/miden-protocol/src/block/mod.rs diff --git a/crates/miden-objects/src/block/note_tree.rs b/crates/miden-protocol/src/block/note_tree.rs similarity index 100% rename from crates/miden-objects/src/block/note_tree.rs rename to crates/miden-protocol/src/block/note_tree.rs diff --git a/crates/miden-objects/src/block/nullifier_tree/backend.rs b/crates/miden-protocol/src/block/nullifier_tree/backend.rs similarity index 100% rename from crates/miden-objects/src/block/nullifier_tree/backend.rs rename to crates/miden-protocol/src/block/nullifier_tree/backend.rs diff --git a/crates/miden-objects/src/block/nullifier_tree/mod.rs b/crates/miden-protocol/src/block/nullifier_tree/mod.rs similarity index 100% rename from crates/miden-objects/src/block/nullifier_tree/mod.rs rename to crates/miden-protocol/src/block/nullifier_tree/mod.rs diff --git a/crates/miden-objects/src/block/nullifier_tree/partial.rs b/crates/miden-protocol/src/block/nullifier_tree/partial.rs similarity index 100% rename from crates/miden-objects/src/block/nullifier_tree/partial.rs rename to crates/miden-protocol/src/block/nullifier_tree/partial.rs diff --git a/crates/miden-objects/src/block/nullifier_tree/witness.rs b/crates/miden-protocol/src/block/nullifier_tree/witness.rs similarity index 100% rename from crates/miden-objects/src/block/nullifier_tree/witness.rs rename to crates/miden-protocol/src/block/nullifier_tree/witness.rs diff --git a/crates/miden-objects/src/block/proposed_block.rs b/crates/miden-protocol/src/block/proposed_block.rs similarity index 100% rename from crates/miden-objects/src/block/proposed_block.rs rename to crates/miden-protocol/src/block/proposed_block.rs diff --git a/crates/miden-objects/src/block/proven_block.rs b/crates/miden-protocol/src/block/proven_block.rs similarity index 100% rename from crates/miden-objects/src/block/proven_block.rs rename to crates/miden-protocol/src/block/proven_block.rs diff --git a/crates/miden-objects/src/block/signer.rs b/crates/miden-protocol/src/block/signer.rs similarity index 100% rename from crates/miden-objects/src/block/signer.rs rename to crates/miden-protocol/src/block/signer.rs diff --git a/crates/miden-objects/src/constants.rs b/crates/miden-protocol/src/constants.rs similarity index 100% rename from crates/miden-objects/src/constants.rs rename to crates/miden-protocol/src/constants.rs diff --git a/crates/miden-objects/src/errors/masm_error.rs b/crates/miden-protocol/src/errors/masm_error.rs similarity index 100% rename from crates/miden-objects/src/errors/masm_error.rs rename to crates/miden-protocol/src/errors/masm_error.rs diff --git a/crates/miden-objects/src/errors/mod.rs b/crates/miden-protocol/src/errors/mod.rs similarity index 100% rename from crates/miden-objects/src/errors/mod.rs rename to crates/miden-protocol/src/errors/mod.rs diff --git a/crates/miden-objects/src/errors/protocol.rs b/crates/miden-protocol/src/errors/protocol.rs similarity index 99% rename from crates/miden-objects/src/errors/protocol.rs rename to crates/miden-protocol/src/errors/protocol.rs index ae97567ceb..73b7085d33 100644 --- a/crates/miden-objects/src/errors/protocol.rs +++ b/crates/miden-protocol/src/errors/protocol.rs @@ -1,7 +1,7 @@ use crate::errors::MasmError; // This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `./asm` directory. // // To add a new error, define a constant in MASM of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). diff --git a/crates/miden-objects/src/errors/tx_kernel.rs b/crates/miden-protocol/src/errors/tx_kernel.rs similarity index 99% rename from crates/miden-objects/src/errors/tx_kernel.rs rename to crates/miden-protocol/src/errors/tx_kernel.rs index cbfae11c6a..8394873749 100644 --- a/crates/miden-objects/src/errors/tx_kernel.rs +++ b/crates/miden-protocol/src/errors/tx_kernel.rs @@ -1,7 +1,7 @@ use crate::errors::MasmError; // This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `./asm` directory. // // To add a new error, define a constant in MASM of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). diff --git a/crates/miden-objects/src/lib.rs b/crates/miden-protocol/src/lib.rs similarity index 100% rename from crates/miden-objects/src/lib.rs rename to crates/miden-protocol/src/lib.rs diff --git a/crates/miden-objects/src/note/assets.rs b/crates/miden-protocol/src/note/assets.rs similarity index 100% rename from crates/miden-objects/src/note/assets.rs rename to crates/miden-protocol/src/note/assets.rs diff --git a/crates/miden-objects/src/note/details.rs b/crates/miden-protocol/src/note/details.rs similarity index 100% rename from crates/miden-objects/src/note/details.rs rename to crates/miden-protocol/src/note/details.rs diff --git a/crates/miden-objects/src/note/execution_hint.rs b/crates/miden-protocol/src/note/execution_hint.rs similarity index 100% rename from crates/miden-objects/src/note/execution_hint.rs rename to crates/miden-protocol/src/note/execution_hint.rs diff --git a/crates/miden-objects/src/note/file.rs b/crates/miden-protocol/src/note/file.rs similarity index 100% rename from crates/miden-objects/src/note/file.rs rename to crates/miden-protocol/src/note/file.rs diff --git a/crates/miden-objects/src/note/header.rs b/crates/miden-protocol/src/note/header.rs similarity index 100% rename from crates/miden-objects/src/note/header.rs rename to crates/miden-protocol/src/note/header.rs diff --git a/crates/miden-objects/src/note/inputs.rs b/crates/miden-protocol/src/note/inputs.rs similarity index 100% rename from crates/miden-objects/src/note/inputs.rs rename to crates/miden-protocol/src/note/inputs.rs diff --git a/crates/miden-objects/src/note/location.rs b/crates/miden-protocol/src/note/location.rs similarity index 100% rename from crates/miden-objects/src/note/location.rs rename to crates/miden-protocol/src/note/location.rs diff --git a/crates/miden-objects/src/note/metadata.rs b/crates/miden-protocol/src/note/metadata.rs similarity index 100% rename from crates/miden-objects/src/note/metadata.rs rename to crates/miden-protocol/src/note/metadata.rs diff --git a/crates/miden-objects/src/note/mod.rs b/crates/miden-protocol/src/note/mod.rs similarity index 100% rename from crates/miden-objects/src/note/mod.rs rename to crates/miden-protocol/src/note/mod.rs diff --git a/crates/miden-objects/src/note/note_id.rs b/crates/miden-protocol/src/note/note_id.rs similarity index 100% rename from crates/miden-objects/src/note/note_id.rs rename to crates/miden-protocol/src/note/note_id.rs diff --git a/crates/miden-objects/src/note/note_tag.rs b/crates/miden-protocol/src/note/note_tag.rs similarity index 100% rename from crates/miden-objects/src/note/note_tag.rs rename to crates/miden-protocol/src/note/note_tag.rs diff --git a/crates/miden-objects/src/note/note_type.rs b/crates/miden-protocol/src/note/note_type.rs similarity index 100% rename from crates/miden-objects/src/note/note_type.rs rename to crates/miden-protocol/src/note/note_type.rs diff --git a/crates/miden-objects/src/note/nullifier.rs b/crates/miden-protocol/src/note/nullifier.rs similarity index 100% rename from crates/miden-objects/src/note/nullifier.rs rename to crates/miden-protocol/src/note/nullifier.rs diff --git a/crates/miden-objects/src/note/partial.rs b/crates/miden-protocol/src/note/partial.rs similarity index 100% rename from crates/miden-objects/src/note/partial.rs rename to crates/miden-protocol/src/note/partial.rs diff --git a/crates/miden-objects/src/note/recipient.rs b/crates/miden-protocol/src/note/recipient.rs similarity index 100% rename from crates/miden-objects/src/note/recipient.rs rename to crates/miden-protocol/src/note/recipient.rs diff --git a/crates/miden-objects/src/note/script.rs b/crates/miden-protocol/src/note/script.rs similarity index 100% rename from crates/miden-objects/src/note/script.rs rename to crates/miden-protocol/src/note/script.rs diff --git a/crates/miden-objects/src/protocol.rs b/crates/miden-protocol/src/protocol.rs similarity index 96% rename from crates/miden-objects/src/protocol.rs rename to crates/miden-protocol/src/protocol.rs index 37d3e7569e..8ba96c9392 100644 --- a/crates/miden-objects/src/protocol.rs +++ b/crates/miden-protocol/src/protocol.rs @@ -57,7 +57,7 @@ mod tests { #[test] fn test_compile() { - let path = Path::new("::miden::active_account::get_id"); + let path = Path::new("::miden::protocol::active_account::get_id"); let miden = ProtocolLib::default(); let exists = miden.0.module_infos().any(|module| { module diff --git a/crates/miden-objects/src/testing/account.rs b/crates/miden-protocol/src/testing/account.rs similarity index 100% rename from crates/miden-objects/src/testing/account.rs rename to crates/miden-protocol/src/testing/account.rs diff --git a/crates/miden-objects/src/testing/account_code.rs b/crates/miden-protocol/src/testing/account_code.rs similarity index 100% rename from crates/miden-objects/src/testing/account_code.rs rename to crates/miden-protocol/src/testing/account_code.rs diff --git a/crates/miden-objects/src/testing/account_id.rs b/crates/miden-protocol/src/testing/account_id.rs similarity index 98% rename from crates/miden-objects/src/testing/account_id.rs rename to crates/miden-protocol/src/testing/account_id.rs index b90d0cc679..6147ff8546 100644 --- a/crates/miden-objects/src/testing/account_id.rs +++ b/crates/miden-protocol/src/testing/account_id.rs @@ -145,8 +145,8 @@ pub const fn account_id( /// # Example /// /// ``` -/// # use miden_objects::account::{AccountType, AccountStorageMode, AccountId}; -/// # use miden_objects::testing::account_id::{AccountIdBuilder}; +/// # use miden_protocol::account::{AccountType, AccountStorageMode, AccountId}; +/// # use miden_protocol::testing::account_id::{AccountIdBuilder}; /// /// let mut rng = rand::rng(); /// diff --git a/crates/miden-objects/src/testing/add_component.rs b/crates/miden-protocol/src/testing/add_component.rs similarity index 100% rename from crates/miden-objects/src/testing/add_component.rs rename to crates/miden-protocol/src/testing/add_component.rs diff --git a/crates/miden-objects/src/testing/asset.rs b/crates/miden-protocol/src/testing/asset.rs similarity index 100% rename from crates/miden-objects/src/testing/asset.rs rename to crates/miden-protocol/src/testing/asset.rs diff --git a/crates/miden-objects/src/testing/block.rs b/crates/miden-protocol/src/testing/block.rs similarity index 100% rename from crates/miden-objects/src/testing/block.rs rename to crates/miden-protocol/src/testing/block.rs diff --git a/crates/miden-objects/src/testing/block_note_tree.rs b/crates/miden-protocol/src/testing/block_note_tree.rs similarity index 100% rename from crates/miden-objects/src/testing/block_note_tree.rs rename to crates/miden-protocol/src/testing/block_note_tree.rs diff --git a/crates/miden-objects/src/testing/constants.rs b/crates/miden-protocol/src/testing/constants.rs similarity index 100% rename from crates/miden-objects/src/testing/constants.rs rename to crates/miden-protocol/src/testing/constants.rs diff --git a/crates/miden-objects/src/testing/mock_util_lib.rs b/crates/miden-protocol/src/testing/mock_util_lib.rs similarity index 97% rename from crates/miden-objects/src/testing/mock_util_lib.rs rename to crates/miden-protocol/src/testing/mock_util_lib.rs index 17535daa70..9337fde0f5 100644 --- a/crates/miden-objects/src/testing/mock_util_lib.rs +++ b/crates/miden-protocol/src/testing/mock_util_lib.rs @@ -5,7 +5,7 @@ use crate::transaction::TransactionKernel; use crate::utils::sync::LazyLock; const MOCK_UTIL_LIBRARY_CODE: &str = " - use miden::output_note + use miden::protocol::output_note # Inputs: [] # Outputs: [note_idx] diff --git a/crates/miden-objects/src/testing/mod.rs b/crates/miden-protocol/src/testing/mod.rs similarity index 100% rename from crates/miden-objects/src/testing/mod.rs rename to crates/miden-protocol/src/testing/mod.rs diff --git a/crates/miden-objects/src/testing/noop_auth_component.rs b/crates/miden-protocol/src/testing/noop_auth_component.rs similarity index 100% rename from crates/miden-objects/src/testing/noop_auth_component.rs rename to crates/miden-protocol/src/testing/noop_auth_component.rs diff --git a/crates/miden-objects/src/testing/note.rs b/crates/miden-protocol/src/testing/note.rs similarity index 100% rename from crates/miden-objects/src/testing/note.rs rename to crates/miden-protocol/src/testing/note.rs diff --git a/crates/miden-objects/src/testing/partial_blockchain.rs b/crates/miden-protocol/src/testing/partial_blockchain.rs similarity index 100% rename from crates/miden-objects/src/testing/partial_blockchain.rs rename to crates/miden-protocol/src/testing/partial_blockchain.rs diff --git a/crates/miden-objects/src/testing/random_signer.rs b/crates/miden-protocol/src/testing/random_signer.rs similarity index 100% rename from crates/miden-objects/src/testing/random_signer.rs rename to crates/miden-protocol/src/testing/random_signer.rs diff --git a/crates/miden-objects/src/testing/slot_name.rs b/crates/miden-protocol/src/testing/slot_name.rs similarity index 100% rename from crates/miden-objects/src/testing/slot_name.rs rename to crates/miden-protocol/src/testing/slot_name.rs diff --git a/crates/miden-objects/src/testing/storage.rs b/crates/miden-protocol/src/testing/storage.rs similarity index 100% rename from crates/miden-objects/src/testing/storage.rs rename to crates/miden-protocol/src/testing/storage.rs diff --git a/crates/miden-objects/src/testing/tx.rs b/crates/miden-protocol/src/testing/tx.rs similarity index 100% rename from crates/miden-objects/src/testing/tx.rs rename to crates/miden-protocol/src/testing/tx.rs diff --git a/crates/miden-objects/src/transaction/executed_tx.rs b/crates/miden-protocol/src/transaction/executed_tx.rs similarity index 100% rename from crates/miden-objects/src/transaction/executed_tx.rs rename to crates/miden-protocol/src/transaction/executed_tx.rs diff --git a/crates/miden-objects/src/transaction/inputs/account.rs b/crates/miden-protocol/src/transaction/inputs/account.rs similarity index 100% rename from crates/miden-objects/src/transaction/inputs/account.rs rename to crates/miden-protocol/src/transaction/inputs/account.rs diff --git a/crates/miden-objects/src/transaction/inputs/mod.rs b/crates/miden-protocol/src/transaction/inputs/mod.rs similarity index 100% rename from crates/miden-objects/src/transaction/inputs/mod.rs rename to crates/miden-protocol/src/transaction/inputs/mod.rs diff --git a/crates/miden-objects/src/transaction/inputs/notes.rs b/crates/miden-protocol/src/transaction/inputs/notes.rs similarity index 100% rename from crates/miden-objects/src/transaction/inputs/notes.rs rename to crates/miden-protocol/src/transaction/inputs/notes.rs diff --git a/crates/miden-objects/src/transaction/kernel/advice_inputs.rs b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs similarity index 100% rename from crates/miden-objects/src/transaction/kernel/advice_inputs.rs rename to crates/miden-protocol/src/transaction/kernel/advice_inputs.rs diff --git a/crates/miden-objects/src/transaction/kernel/memory.rs b/crates/miden-protocol/src/transaction/kernel/memory.rs similarity index 100% rename from crates/miden-objects/src/transaction/kernel/memory.rs rename to crates/miden-protocol/src/transaction/kernel/memory.rs diff --git a/crates/miden-objects/src/transaction/kernel/mod.rs b/crates/miden-protocol/src/transaction/kernel/mod.rs similarity index 100% rename from crates/miden-objects/src/transaction/kernel/mod.rs rename to crates/miden-protocol/src/transaction/kernel/mod.rs diff --git a/crates/miden-objects/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs similarity index 93% rename from crates/miden-objects/src/transaction/kernel/procedures.rs rename to crates/miden-protocol/src/transaction/kernel/procedures.rs index 3ecae0bb61..b8e805da30 100644 --- a/crates/miden-objects/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -28,7 +28,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_get_initial_item word!("0x182573f7527df3b1e0b7cd28a7a2cb722776654d90219792806923bffe9d74df"), // account_set_item - word!("0x1e1a7357928f22e54c4328d2ad611883c7bcd09c0e5368d52835f3cdb28f3411"), + word!("0x24c88b3ec4b67ff8f58fba5f5a01117347c39efe7ec4e3236d6d38bd238ad99b"), // account_get_map_item word!("0xba1f281fe8c8ef584bfc1615962cbde4573ac01173bb113a5c79bcecf4eb1c65"), // account_get_initial_map_item @@ -60,13 +60,13 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_procedure word!("0xb0b63fdd01af0bcb4aacb2412e934cdc7691308647152d416c7ae4fc909da076"), // faucet_mint_asset - word!("0xc9c3e3a69799564bfb0424a9a473ae4f9f5a2d4d5508a166cdecfaf27e9bdc5b"), + word!("0x76fc1dd0518f83d5aad0fecdda238529e2e0222267eebcb5e8598abc1b202cd5"), // faucet_burn_asset - word!("0xac5a428ad9fdf8b1879bb0e4d6e24ad8bf3aee8bc3b6d1f43c85440d63820308"), + word!("0x024479e5265c131572464f5a41885d2fcdec8e29dcb9146900529665aed8e2e5"), // faucet_get_total_fungible_asset_issuance - word!("0xdd8624380e31863946214a233afffde6529a56b196d0c2214b53b810a4a3251f"), + word!("0xa1d900ff530770c3e56876fedd1b8f9f5ca491e2198524a973148ae3df88c581"), // faucet_is_non_fungible_asset_issued - word!("0xe53348efccf43a1d0ef2b55ea688bdd930130568f3427871daf705737a7e5066"), + word!("0xff77f7a2fabbbba5ef996242963569a84c2ef4614d6e8178e56ab4ae284673c7"), // input_note_get_metadata word!("0x7ad3e94585e7a397ee27443c98b376ed8d4ba762122af6413fde9314c00a6219"), // input_note_get_assets_info diff --git a/crates/miden-objects/src/transaction/kernel/tx_event_id.rs b/crates/miden-protocol/src/transaction/kernel/tx_event_id.rs similarity index 100% rename from crates/miden-objects/src/transaction/kernel/tx_event_id.rs rename to crates/miden-protocol/src/transaction/kernel/tx_event_id.rs diff --git a/crates/miden-objects/src/transaction/mod.rs b/crates/miden-protocol/src/transaction/mod.rs similarity index 100% rename from crates/miden-objects/src/transaction/mod.rs rename to crates/miden-protocol/src/transaction/mod.rs diff --git a/crates/miden-objects/src/transaction/ordered_transactions.rs b/crates/miden-protocol/src/transaction/ordered_transactions.rs similarity index 100% rename from crates/miden-objects/src/transaction/ordered_transactions.rs rename to crates/miden-protocol/src/transaction/ordered_transactions.rs diff --git a/crates/miden-objects/src/transaction/outputs.rs b/crates/miden-protocol/src/transaction/outputs.rs similarity index 100% rename from crates/miden-objects/src/transaction/outputs.rs rename to crates/miden-protocol/src/transaction/outputs.rs diff --git a/crates/miden-objects/src/transaction/partial_blockchain.rs b/crates/miden-protocol/src/transaction/partial_blockchain.rs similarity index 100% rename from crates/miden-objects/src/transaction/partial_blockchain.rs rename to crates/miden-protocol/src/transaction/partial_blockchain.rs diff --git a/crates/miden-objects/src/transaction/proven_tx.rs b/crates/miden-protocol/src/transaction/proven_tx.rs similarity index 100% rename from crates/miden-objects/src/transaction/proven_tx.rs rename to crates/miden-protocol/src/transaction/proven_tx.rs diff --git a/crates/miden-objects/src/transaction/transaction_id.rs b/crates/miden-protocol/src/transaction/transaction_id.rs similarity index 100% rename from crates/miden-objects/src/transaction/transaction_id.rs rename to crates/miden-protocol/src/transaction/transaction_id.rs diff --git a/crates/miden-objects/src/transaction/tx_args.rs b/crates/miden-protocol/src/transaction/tx_args.rs similarity index 100% rename from crates/miden-objects/src/transaction/tx_args.rs rename to crates/miden-protocol/src/transaction/tx_args.rs diff --git a/crates/miden-objects/src/transaction/tx_header.rs b/crates/miden-protocol/src/transaction/tx_header.rs similarity index 100% rename from crates/miden-objects/src/transaction/tx_header.rs rename to crates/miden-protocol/src/transaction/tx_header.rs diff --git a/crates/miden-objects/src/transaction/tx_summary.rs b/crates/miden-protocol/src/transaction/tx_summary.rs similarity index 100% rename from crates/miden-objects/src/transaction/tx_summary.rs rename to crates/miden-protocol/src/transaction/tx_summary.rs diff --git a/crates/miden-lib/Cargo.toml b/crates/miden-standards/Cargo.toml similarity index 72% rename from crates/miden-lib/Cargo.toml rename to crates/miden-standards/Cargo.toml index 623c5745bc..a57fb2814e 100644 --- a/crates/miden-lib/Cargo.toml +++ b/crates/miden-standards/Cargo.toml @@ -1,12 +1,12 @@ [package] authors.workspace = true categories = ["no-std"] -description = "Standard library of the Miden protocol" +description = "Standards of the Miden protocol" edition.workspace = true homepage.workspace = true keywords = ["kernel", "miden", "transaction"] license.workspace = true -name = "miden-lib" +name = "miden-standards" readme = "README.md" repository.workspace = true rust-version.workspace = true @@ -16,13 +16,13 @@ version.workspace = true [features] default = ["std"] -std = ["miden-assembly/std", "miden-core-lib/std", "miden-objects/std", "miden-processor/std"] -testing = ["dep:rand", "miden-assembly/testing", "miden-objects/testing"] +std = ["miden-assembly/std", "miden-core-lib/std", "miden-processor/std", "miden-protocol/std"] +testing = ["dep:rand", "miden-assembly/testing", "miden-protocol/testing"] [dependencies] # Miden dependencies -miden-objects = { workspace = true } miden-processor = { workspace = true } +miden-protocol = { workspace = true } # External dependencies rand = { optional = true, workspace = true } @@ -33,17 +33,17 @@ fs-err = { version = "3" } miden-assembly = { workspace = true } miden-core = { workspace = true } miden-core-lib = { workspace = true } -miden-objects = { workspace = true } +miden-protocol = { workspace = true } regex = { version = "1.11" } walkdir = { version = "2.5" } [dev-dependencies] anyhow = "1.0" assert_matches = { workspace = true } -miden-objects = { features = ["testing"], workspace = true } miden-processor = { features = ["testing"], workspace = true } +miden-protocol = { features = ["testing"], workspace = true } # When building as a dev-dependency (e.g., `cargo test --workspace` or `cargo check --all-targets`), # enable the `testing` feature. This is a workaround for Cargo's lack of test-specific features. # See: https://github.com/rust-lang/cargo/issues/2911#issuecomment-1483256987 -miden-lib = { features = ["testing"], path = "." } +miden-standards = { features = ["testing"], path = "." } diff --git a/crates/miden-lib/README.md b/crates/miden-standards/README.md similarity index 61% rename from crates/miden-lib/README.md rename to crates/miden-standards/README.md index 918fa2c761..7cdd97adcb 100644 --- a/crates/miden-lib/README.md +++ b/crates/miden-standards/README.md @@ -1,6 +1,6 @@ -# Transaction kernel +# Miden Standards -This crate contains the code of the Miden protocol kernels and standardized smart contracts. +This crate contains the code of Miden's standardized smart contracts. ## Status diff --git a/crates/miden-lib/asm/account_components/basic_fungible_faucet.masm b/crates/miden-standards/asm/account_components/basic_fungible_faucet.masm similarity index 53% rename from crates/miden-lib/asm/account_components/basic_fungible_faucet.masm rename to crates/miden-standards/asm/account_components/basic_fungible_faucet.masm index 17e78f9990..5d3b13a920 100644 --- a/crates/miden-lib/asm/account_components/basic_fungible_faucet.masm +++ b/crates/miden-standards/asm/account_components/basic_fungible_faucet.masm @@ -2,5 +2,5 @@ # # See the `BasicFungibleFaucet` Rust type's documentation for more details. -pub use ::miden::contracts::faucets::basic_fungible::distribute -pub use ::miden::contracts::faucets::basic_fungible::burn +pub use ::miden::standards::faucets::basic_fungible::distribute +pub use ::miden::standards::faucets::basic_fungible::burn diff --git a/crates/miden-lib/asm/account_components/basic_wallet.masm b/crates/miden-standards/asm/account_components/basic_wallet.masm similarity index 51% rename from crates/miden-lib/asm/account_components/basic_wallet.masm rename to crates/miden-standards/asm/account_components/basic_wallet.masm index 0670f44c75..e6b613900e 100644 --- a/crates/miden-lib/asm/account_components/basic_wallet.masm +++ b/crates/miden-standards/asm/account_components/basic_wallet.masm @@ -2,5 +2,5 @@ # # See the `BasicWallet` Rust type's documentation for more details. -pub use ::miden::contracts::wallets::basic::receive_asset -pub use ::miden::contracts::wallets::basic::move_asset_to_note +pub use ::miden::standards::wallets::basic::receive_asset +pub use ::miden::standards::wallets::basic::move_asset_to_note diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak.masm similarity index 95% rename from crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm rename to crates/miden-standards/asm/account_components/ecdsa_k256_keccak.masm index aca5d51f93..e226eeee00 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm +++ b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak.masm @@ -2,8 +2,8 @@ # # See the `AuthEcdsaK256Keccak` Rust type's documentation for more details. -use miden::auth::ecdsa_k256_keccak -use miden::active_account +use miden::standards::auth::ecdsa_k256_keccak +use miden::protocol::active_account type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_acl.masm similarity index 96% rename from crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm rename to crates/miden-standards/asm/account_components/ecdsa_k256_keccak_acl.masm index 79215b9db8..173b811386 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm +++ b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_acl.masm @@ -2,9 +2,9 @@ # # See the `AuthEcdsaK256KeccakAcl` Rust type's documentation for more details. -use miden::active_account -use miden::native_account -use miden::tx +use miden::protocol::active_account +use miden::protocol::native_account +use miden::protocol::tx use miden::core::word type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -128,7 +128,7 @@ pub proc auth_tx_ecdsa_k256_keccak_acl(auth_args: BeWord) push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] - exec.::miden::auth::ecdsa_k256_keccak::authenticate_transaction + exec.::miden::standards::auth::ecdsa_k256_keccak::authenticate_transaction else # ------ Check if initial account commitment differs from current commitment ------ diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_multisig.masm b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm similarity index 98% rename from crates/miden-lib/asm/account_components/ecdsa_k256_keccak_multisig.masm rename to crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm index f1d107edce..935e1f3e61 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_multisig.masm +++ b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm @@ -2,9 +2,9 @@ # # See the `AuthEcdsaK256KeccakMultisig` Rust type's documentation for more details. -use miden::active_account -use miden::native_account -use miden::auth +use miden::protocol::active_account +use miden::protocol::native_account +use miden::standards::auth type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -390,7 +390,7 @@ pub proc auth_tx_ecdsa_k256_keccak_multisig(salt: BeWord) push.APPROVER_PUBLIC_KEYS_SLOT[0..2] # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] - exec.::miden::auth::ecdsa_k256_keccak::verify_signatures + exec.::miden::standards::auth::ecdsa_k256_keccak::verify_signatures # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold] # ------ Checking threshold is >= num_verified_signatures ------ diff --git a/crates/miden-lib/asm/account_components/network_fungible_faucet.masm b/crates/miden-standards/asm/account_components/network_fungible_faucet.masm similarity index 54% rename from crates/miden-lib/asm/account_components/network_fungible_faucet.masm rename to crates/miden-standards/asm/account_components/network_fungible_faucet.masm index 1b3cd76d31..604239c7fd 100644 --- a/crates/miden-lib/asm/account_components/network_fungible_faucet.masm +++ b/crates/miden-standards/asm/account_components/network_fungible_faucet.masm @@ -2,5 +2,5 @@ # # See the `NetworkFungibleFaucet` Rust type's documentation for more details. -pub use ::miden::contracts::faucets::network_fungible::distribute -pub use ::miden::contracts::faucets::network_fungible::burn +pub use ::miden::standards::faucets::network_fungible::distribute +pub use ::miden::standards::faucets::network_fungible::burn diff --git a/crates/miden-lib/asm/account_components/no_auth.masm b/crates/miden-standards/asm/account_components/no_auth.masm similarity index 95% rename from crates/miden-lib/asm/account_components/no_auth.masm rename to crates/miden-standards/asm/account_components/no_auth.masm index 1ec18155b2..4ced08325f 100644 --- a/crates/miden-lib/asm/account_components/no_auth.masm +++ b/crates/miden-standards/asm/account_components/no_auth.masm @@ -1,5 +1,5 @@ -use miden::active_account -use miden::native_account +use miden::protocol::active_account +use miden::protocol::native_account use miden::core::word #! Increment the nonce only if the account commitment has changed diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512.masm b/crates/miden-standards/asm/account_components/rpo_falcon_512.masm similarity index 95% rename from crates/miden-lib/asm/account_components/rpo_falcon_512.masm rename to crates/miden-standards/asm/account_components/rpo_falcon_512.masm index 914e80e48b..7fd23fe2c2 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512.masm +++ b/crates/miden-standards/asm/account_components/rpo_falcon_512.masm @@ -2,8 +2,8 @@ # # See the `AuthRpoFalcon512` Rust type's documentation for more details. -use miden::auth::rpo_falcon512 -use miden::active_account +use miden::standards::auth::rpo_falcon512 +use miden::protocol::active_account type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm b/crates/miden-standards/asm/account_components/rpo_falcon_512_acl.masm similarity index 96% rename from crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm rename to crates/miden-standards/asm/account_components/rpo_falcon_512_acl.masm index 635349116e..b183a45c4a 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm +++ b/crates/miden-standards/asm/account_components/rpo_falcon_512_acl.masm @@ -2,9 +2,9 @@ # # See the `AuthRpoFalcon512Acl` Rust type's documentation for more details. -use miden::active_account -use miden::native_account -use miden::tx +use miden::protocol::active_account +use miden::protocol::native_account +use miden::protocol::tx use miden::core::word type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -128,7 +128,7 @@ pub proc auth_tx_rpo_falcon512_acl(auth_args: BeWord) push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] - exec.::miden::auth::rpo_falcon512::authenticate_transaction + exec.::miden::standards::auth::rpo_falcon512::authenticate_transaction else # ------ Check if initial account commitment differs from current commitment ------ diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512_multisig.masm b/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm similarity index 98% rename from crates/miden-lib/asm/account_components/rpo_falcon_512_multisig.masm rename to crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm index 5896eefadb..681d176452 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512_multisig.masm +++ b/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm @@ -2,9 +2,9 @@ # # See the `AuthRpoFalcon512Multisig` Rust type's documentation for more details. -use miden::active_account -use miden::native_account -use miden::auth +use miden::protocol::active_account +use miden::protocol::native_account +use miden::standards::auth type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -390,7 +390,7 @@ pub proc auth_tx_rpo_falcon512_multisig(salt: BeWord) push.APPROVER_PUBLIC_KEYS_SLOT[0..2] # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] - exec.::miden::auth::rpo_falcon512::verify_signatures + exec.::miden::standards::auth::rpo_falcon512::verify_signatures # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold] # ------ Checking threshold is >= num_verified_signatures ------ diff --git a/crates/miden-lib/asm/note_scripts/BURN.masm b/crates/miden-standards/asm/note_scripts/BURN.masm similarity index 96% rename from crates/miden-lib/asm/note_scripts/BURN.masm rename to crates/miden-standards/asm/note_scripts/BURN.masm index acd8b4399b..9282d5249f 100644 --- a/crates/miden-lib/asm/note_scripts/BURN.masm +++ b/crates/miden-standards/asm/note_scripts/BURN.masm @@ -1,4 +1,4 @@ -use miden::contracts::faucets +use miden::standards::faucets #! BURN script: burns the asset from the note by calling the faucet's burn procedure. #! This note can be executed against any faucet account that exposes the faucets::burn procedure diff --git a/crates/miden-lib/asm/note_scripts/MINT.masm b/crates/miden-standards/asm/note_scripts/MINT.masm similarity index 97% rename from crates/miden-lib/asm/note_scripts/MINT.masm rename to crates/miden-standards/asm/note_scripts/MINT.masm index d62903cebc..8d6281b9ee 100644 --- a/crates/miden-lib/asm/note_scripts/MINT.masm +++ b/crates/miden-standards/asm/note_scripts/MINT.masm @@ -1,6 +1,6 @@ -use miden::active_note -use miden::contracts::faucets::network_fungible->network_faucet -use miden::note +use miden::protocol::active_note +use miden::protocol::note +use miden::standards::faucets::network_fungible->network_faucet # CONSTANTS # ================================================================================================= diff --git a/crates/miden-lib/asm/note_scripts/P2ID.masm b/crates/miden-standards/asm/note_scripts/P2ID.masm similarity index 92% rename from crates/miden-lib/asm/note_scripts/P2ID.masm rename to crates/miden-standards/asm/note_scripts/P2ID.masm index a6eec2e964..6e0e455400 100644 --- a/crates/miden-lib/asm/note_scripts/P2ID.masm +++ b/crates/miden-standards/asm/note_scripts/P2ID.masm @@ -1,7 +1,7 @@ -use miden::active_account -use miden::account_id -use miden::active_note -use miden::contracts::wallets::basic->basic_wallet +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::standards::wallets::basic->basic_wallet # ERRORS # ================================================================================================= diff --git a/crates/miden-lib/asm/note_scripts/P2IDE.masm b/crates/miden-standards/asm/note_scripts/P2IDE.masm similarity index 97% rename from crates/miden-lib/asm/note_scripts/P2IDE.masm rename to crates/miden-standards/asm/note_scripts/P2IDE.masm index 5a73ec1074..5a033ba0ef 100644 --- a/crates/miden-lib/asm/note_scripts/P2IDE.masm +++ b/crates/miden-standards/asm/note_scripts/P2IDE.masm @@ -1,8 +1,8 @@ -use miden::active_account -use miden::account_id -use miden::active_note -use miden::tx -use miden::contracts::wallets::basic->basic_wallet +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::protocol::tx +use miden::standards::wallets::basic->basic_wallet # ERRORS # ================================================================================================= diff --git a/crates/miden-lib/asm/note_scripts/SWAP.masm b/crates/miden-standards/asm/note_scripts/SWAP.masm similarity index 96% rename from crates/miden-lib/asm/note_scripts/SWAP.masm rename to crates/miden-standards/asm/note_scripts/SWAP.masm index 75eebc0074..4c1c2ee8f2 100644 --- a/crates/miden-lib/asm/note_scripts/SWAP.masm +++ b/crates/miden-standards/asm/note_scripts/SWAP.masm @@ -1,6 +1,6 @@ -use miden::active_note -use miden::output_note -use miden::contracts::wallets::basic->wallet +use miden::protocol::active_note +use miden::protocol::output_note +use miden::standards::wallets::basic->wallet # CONSTANTS # ================================================================================================= diff --git a/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm b/crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm similarity index 98% rename from crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm rename to crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm index d220bd1c32..17fd8d9e7a 100644 --- a/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm +++ b/crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm @@ -1,8 +1,8 @@ -use miden::active_account -use miden::native_account -use miden::auth -use miden::tx use miden::core::crypto::dsa::ecdsa_k256_keccak +use miden::protocol::active_account +use miden::protocol::native_account +use miden::protocol::tx +use miden::standards::auth # CONSTANTS # ================================================================================================= diff --git a/crates/miden-lib/asm/miden/auth/mod.masm b/crates/miden-standards/asm/standards/auth/mod.masm similarity index 98% rename from crates/miden-lib/asm/miden/auth/mod.masm rename to crates/miden-standards/asm/standards/auth/mod.masm index 0cfd10e644..1c0575afcd 100644 --- a/crates/miden-lib/asm/miden/auth/mod.masm +++ b/crates/miden-standards/asm/standards/auth/mod.masm @@ -1,5 +1,5 @@ -use miden::native_account -use miden::tx +use miden::protocol::native_account +use miden::protocol::tx use miden::core::crypto::hashes::rpo256 #! Inputs: [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT] diff --git a/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm b/crates/miden-standards/asm/standards/auth/rpo_falcon512.masm similarity index 98% rename from crates/miden-lib/asm/miden/auth/rpo_falcon512.masm rename to crates/miden-standards/asm/standards/auth/rpo_falcon512.masm index 7349e34337..4abb032699 100644 --- a/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm +++ b/crates/miden-standards/asm/standards/auth/rpo_falcon512.masm @@ -1,8 +1,8 @@ -use miden::active_account -use miden::native_account -use miden::auth -use miden::tx use miden::core::crypto::dsa::falcon512rpo +use miden::protocol::active_account +use miden::protocol::native_account +use miden::protocol::tx +use miden::standards::auth # CONSTANTS # ================================================================================================= diff --git a/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm b/crates/miden-standards/asm/standards/faucets/basic_fungible.masm similarity index 98% rename from crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm rename to crates/miden-standards/asm/standards/faucets/basic_fungible.masm index bb5bea6559..cc447eeb2e 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm +++ b/crates/miden-standards/asm/standards/faucets/basic_fungible.masm @@ -8,7 +8,7 @@ # - decimals are the decimals of the token. # - token_symbol as three chars encoded in a Felt. -use miden::contracts::faucets +use miden::standards::faucets # CONSTANTS # ================================================================================================= diff --git a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm b/crates/miden-standards/asm/standards/faucets/mod.masm similarity index 97% rename from crates/miden-lib/asm/miden/contracts/faucets/mod.masm rename to crates/miden-standards/asm/standards/faucets/mod.masm index 3c9018d909..f5b2d3461e 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm +++ b/crates/miden-standards/asm/standards/faucets/mod.masm @@ -1,7 +1,7 @@ -use miden::active_account -use miden::active_note -use miden::faucet -use miden::output_note +use miden::protocol::active_account +use miden::protocol::active_note +use miden::protocol::faucet +use miden::protocol::output_note # CONSTANTS # ================================================================================================= diff --git a/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm b/crates/miden-standards/asm/standards/faucets/network_fungible.masm similarity index 91% rename from crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm rename to crates/miden-standards/asm/standards/faucets/network_fungible.masm index 4cf8dba38f..39395c628c 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm +++ b/crates/miden-standards/asm/standards/faucets/network_fungible.masm @@ -1,14 +1,14 @@ -use miden::active_account -use miden::account_id -use miden::active_note -use miden::contracts::faucets -use miden::contracts::faucets::basic_fungible +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::standards::faucets +use miden::standards::faucets::basic_fungible # CONSTANTS # ================================================================================================ # The slot in this component's storage layout where the owner config is stored. -const OWNER_CONFIG_SLOT=word("miden::network_fungible_faucet::owner_config") +const OWNER_CONFIG_SLOT=word("miden::standards::network_fungible_faucet::owner_config") # ERRORS const ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who can mint assets" diff --git a/crates/miden-lib/asm/miden/contracts/wallets/basic.masm b/crates/miden-standards/asm/standards/wallets/basic.masm similarity index 97% rename from crates/miden-lib/asm/miden/contracts/wallets/basic.masm rename to crates/miden-standards/asm/standards/wallets/basic.masm index c1fccdd0b9..837803c09c 100644 --- a/crates/miden-lib/asm/miden/contracts/wallets/basic.masm +++ b/crates/miden-standards/asm/standards/wallets/basic.masm @@ -1,6 +1,6 @@ -use miden::native_account -use miden::output_note -use miden::active_note +use miden::protocol::native_account +use miden::protocol::output_note +use miden::protocol::active_note # CONSTANTS # ================================================================================================= diff --git a/crates/miden-lib/build.rs b/crates/miden-standards/build.rs similarity index 94% rename from crates/miden-lib/build.rs rename to crates/miden-standards/build.rs index a73b4e2eaa..7f05079ddd 100644 --- a/crates/miden-lib/build.rs +++ b/crates/miden-standards/build.rs @@ -5,7 +5,7 @@ use fs_err as fs; use miden_assembly::diagnostics::{IntoDiagnostic, NamedSource, Result, WrapErr}; use miden_assembly::utils::Serializable; use miden_assembly::{Assembler, Library, Report}; -use miden_objects::transaction::TransactionKernel; +use miden_protocol::transaction::TransactionKernel; // CONSTANTS // ================================================================================================ @@ -17,21 +17,23 @@ const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN const ASSETS_DIR: &str = "assets"; const ASM_DIR: &str = "asm"; -const ASM_MIDEN_DIR: &str = "miden"; +const ASM_STANDARDS_DIR: &str = "standards"; const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts"; const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components"; -const STANDARDS_ERRORS_FILE: &str = "src/errors/standards.rs"; +const STANDARDS_LIB_NAMESPACE: &str = "miden::standards"; +const STANDARDS_ERRORS_FILE: &str = "src/errors/standards.rs"; const STANDARDS_ERRORS_ARRAY_NAME: &str = "STANDARDS_ERRORS"; // PRE-PROCESSING // ================================================================================================ /// Read and parse the contents from `./asm`. -/// - Compiles contents of asm/miden directory into a Miden library file (.masl) under miden -/// namespace. -/// - Compiles contents of asm/scripts directory into individual .masb files. +/// - Compiles the contents of asm/standards directory into a Miden library file (.masl) under +/// standards namespace. +/// - Compiles the contents of asm/note_scripts directory into individual .masb files. +/// - Compiles the contents of asm/account_components directory into individual .masl files. fn main() -> Result<()> { // re-build when the MASM code changes println!("cargo::rerun-if-changed={ASM_DIR}/"); @@ -79,16 +81,17 @@ fn main() -> Result<()> { // COMPILE PROTOCOL LIB // ================================================================================================ -/// Reads the MASM files from "{source_dir}/miden" directory, compiles them into a Miden assembly -/// library, saves the library into "{target_dir}/miden.masl", and returns the compiled library. +/// Reads the MASM files from "{source_dir}/standards" directory, compiles them into a Miden +/// assembly library, saves the library into "{target_dir}/standards.masl", and returns the compiled +/// library. fn compile_standards_lib( source_dir: &Path, target_dir: &Path, assembler: Assembler, ) -> Result { - let source_dir = source_dir.join(ASM_MIDEN_DIR); + let source_dir = source_dir.join(ASM_STANDARDS_DIR); - let standards_lib = assembler.assemble_library_from_dir(source_dir, "miden")?; + let standards_lib = assembler.assemble_library_from_dir(source_dir, STANDARDS_LIB_NAMESPACE)?; let output_file = target_dir.join("standards").with_extension(Library::LIBRARY_EXTENSION); standards_lib.write_to_file(output_file).into_diagnostic()?; @@ -218,7 +221,7 @@ fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { Ok(()) } -/// This module should be kept in sync with the copy in miden-objects's build.rs. +/// This module should be kept in sync with the copy in miden-protocol's build.rs. mod shared { use std::collections::BTreeMap; use std::fmt::Write; @@ -417,13 +420,13 @@ mod shared { if module.is_crate_local { writeln!(output, "use crate::errors::MasmError;\n").unwrap(); } else { - writeln!(output, "use miden_objects::errors::MasmError;\n").unwrap(); + writeln!(output, "use miden_protocol::errors::MasmError;\n").unwrap(); } writeln!( output, "// This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `./asm` directory. // // To add a new error, define a constant in MASM of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs similarity index 87% rename from crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs rename to crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs index bdab19c3a4..a187b57729 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs @@ -1,6 +1,6 @@ -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageSlot, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountComponent, StorageSlot, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; use crate::account::components::ecdsa_k256_keccak_library; @@ -14,7 +14,7 @@ static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// It reexports the procedures from `miden::contracts::auth::ecdsa_k256_keccak`. When linking /// against this component, the `miden` library (i.e. -/// [`ProtocolLib`](miden_objects::ProtocolLib)) must be available to the assembler which is the +/// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: /// - `verify_signatures`, which can be used to verify a signature provided via the advice stack to /// authenticate a transaction. diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs similarity index 98% rename from crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs rename to crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs index 834222c7f6..19f60867d9 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs @@ -1,15 +1,15 @@ use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{ +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{ AccountCode, AccountComponent, StorageMap, StorageSlot, StorageSlotName, }; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Word}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_acl_library; @@ -220,8 +220,8 @@ impl From for AccountComponent { #[cfg(test)] mod tests { - use miden_objects::Word; - use miden_objects::account::AccountBuilder; + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::components::WellKnownComponent; diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs similarity index 97% rename from crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs rename to crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs index 233a0b1006..1bb3f9163f 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs @@ -1,10 +1,10 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Word}; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_multisig_library; @@ -205,8 +205,8 @@ impl From for AccountComponent { mod tests { use alloc::string::ToString; - use miden_objects::Word; - use miden_objects::account::AccountBuilder; + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::wallets::BasicWallet; diff --git a/crates/miden-lib/src/account/auth/mod.rs b/crates/miden-standards/src/account/auth/mod.rs similarity index 100% rename from crates/miden-lib/src/account/auth/mod.rs rename to crates/miden-standards/src/account/auth/mod.rs diff --git a/crates/miden-lib/src/account/auth/no_auth.rs b/crates/miden-standards/src/account/auth/no_auth.rs similarity index 94% rename from crates/miden-lib/src/account/auth/no_auth.rs rename to crates/miden-standards/src/account/auth/no_auth.rs index 21d9d2fb7e..1424ecbecb 100644 --- a/crates/miden-lib/src/account/auth/no_auth.rs +++ b/crates/miden-standards/src/account/auth/no_auth.rs @@ -1,4 +1,4 @@ -use miden_objects::account::AccountComponent; +use miden_protocol::account::AccountComponent; use crate::account::components::no_auth_library; @@ -41,7 +41,7 @@ impl From for AccountComponent { #[cfg(test)] mod tests { - use miden_objects::account::AccountBuilder; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::wallets::BasicWallet; diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512.rs similarity index 90% rename from crates/miden-lib/src/account/auth/rpo_falcon_512.rs rename to crates/miden-standards/src/account/auth/rpo_falcon_512.rs index 5bb476bc39..305f5c7c46 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512.rs @@ -1,6 +1,6 @@ -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageSlot, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountComponent, StorageSlot, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; use crate::account::components::rpo_falcon_512_library; @@ -13,7 +13,7 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// transactions. /// /// It reexports the procedures from `miden::contracts::auth::rpo_falcon512`. When linking against -/// this component, the `miden` library (i.e. [`ProtocolLib`](miden_objects::ProtocolLib)) must +/// this component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must /// be available to the assembler which is the case when using [`CodeBuilder`][builder]. The /// procedures of this component are: /// - `verify_signatures`, which can be used to verify a signature provided via the advice stack to diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs similarity index 98% rename from crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs rename to crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs index 10c27a140f..16845d0db8 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs @@ -1,15 +1,15 @@ use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{ +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{ AccountCode, AccountComponent, StorageMap, StorageSlot, StorageSlotName, }; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Word}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Word}; use crate::account::components::rpo_falcon_512_acl_library; @@ -221,8 +221,8 @@ impl From for AccountComponent { #[cfg(test)] mod tests { - use miden_objects::Word; - use miden_objects::account::AccountBuilder; + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::components::WellKnownComponent; diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs similarity index 97% rename from crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs rename to crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs index ac484892f7..d169d35586 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs @@ -1,10 +1,10 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Word}; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Word}; use crate::account::components::rpo_falcon_512_multisig_library; @@ -205,8 +205,8 @@ impl From for AccountComponent { mod tests { use alloc::string::ToString; - use miden_objects::Word; - use miden_objects::account::AccountBuilder; + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::wallets::BasicWallet; diff --git a/crates/miden-lib/src/account/components/mod.rs b/crates/miden-standards/src/account/components/mod.rs similarity index 98% rename from crates/miden-lib/src/account/components/mod.rs rename to crates/miden-standards/src/account/components/mod.rs index 152e91a644..e97dc8b4df 100644 --- a/crates/miden-lib/src/account/components/mod.rs +++ b/crates/miden-standards/src/account/components/mod.rs @@ -1,12 +1,12 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::AccountProcedureRoot; -use miden_objects::assembly::{Library, LibraryExport}; -use miden_objects::utils::Deserializable; -use miden_objects::utils::sync::LazyLock; use miden_processor::MastNodeExt; +use miden_protocol::Word; +use miden_protocol::account::AccountProcedureRoot; +use miden_protocol::assembly::{Library, LibraryExport}; +use miden_protocol::utils::Deserializable; +use miden_protocol::utils::sync::LazyLock; use crate::account::interface::AccountComponentInterface; diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-standards/src/account/faucets/basic_fungible.rs similarity index 97% rename from crates/miden-lib/src/account/faucets/basic_fungible.rs rename to crates/miden-standards/src/account/faucets/basic_fungible.rs index 24b1b568b9..329937ebd4 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-standards/src/account/faucets/basic_fungible.rs @@ -1,4 +1,4 @@ -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -8,8 +8,8 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::asset::{FungibleAsset, TokenSymbol}; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::asset::{FungibleAsset, TokenSymbol}; +use miden_protocol::{Felt, FieldElement, Word}; use super::FungibleFaucetError; use crate::account::AuthScheme; @@ -44,7 +44,7 @@ procedure_digest!( /// /// It reexports the procedures from `miden::contracts::faucets::basic_fungible`. When linking /// against this component, the `miden` library (i.e. -/// [`ProtocolLib`](miden_objects::ProtocolLib)) must be available to the assembler which is the +/// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: /// - `distribute`, which mints an assets and create a note for the provided recipient. /// - `burn`, which burns the provided asset. @@ -311,9 +311,9 @@ pub fn create_basic_fungible_faucet( #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_objects::account::AccountStorage; - use miden_objects::account::auth::PublicKeyCommitment; - use miden_objects::{FieldElement, ONE, Word}; + use miden_protocol::account::AccountStorage; + use miden_protocol::account::auth::PublicKeyCommitment; + use miden_protocol::{FieldElement, ONE, Word}; use super::{ AccountBuilder, diff --git a/crates/miden-lib/src/account/faucets/mod.rs b/crates/miden-standards/src/account/faucets/mod.rs similarity index 93% rename from crates/miden-lib/src/account/faucets/mod.rs rename to crates/miden-standards/src/account/faucets/mod.rs index 4b89f0af31..22a13dcb4f 100644 --- a/crates/miden-lib/src/account/faucets/mod.rs +++ b/crates/miden-standards/src/account/faucets/mod.rs @@ -1,8 +1,8 @@ use alloc::string::String; -use miden_objects::account::{Account, AccountStorage, AccountType, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Felt, TokenSymbolError}; +use miden_protocol::account::{Account, AccountStorage, AccountType, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Felt, TokenSymbolError}; use thiserror::Error; mod basic_fungible; diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-standards/src/account/faucets/network_fungible.rs similarity index 96% rename from crates/miden-lib/src/account/faucets/network_fungible.rs rename to crates/miden-standards/src/account/faucets/network_fungible.rs index f6d7723c3d..912e89e863 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-standards/src/account/faucets/network_fungible.rs @@ -1,4 +1,4 @@ -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -9,9 +9,9 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::asset::TokenSymbol; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::asset::TokenSymbol; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{Felt, FieldElement, Word}; use super::{BasicFungibleFaucet, FungibleFaucetError}; use crate::account::auth::NoAuth; @@ -37,7 +37,7 @@ procedure_digest!( ); static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::network_fungible_faucet::owner_config") + StorageSlotName::new("miden::standards::network_fungible_faucet::owner_config") .expect("storage slot name should be valid") }); @@ -45,7 +45,7 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// It reexports the procedures from `miden::contracts::faucets::network_fungible`. When linking /// against this component, the `miden` library (i.e. -/// [`ProtocolLib`](miden_objects::ProtocolLib)) must be available to the assembler which is the +/// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: /// - `distribute`, which mints an assets and create a note for the provided recipient. /// - `burn`, which burns the provided asset. @@ -84,7 +84,7 @@ impl NetworkFungibleFaucet { /// Returns an error if: /// - the decimals parameter exceeds maximum value of [`Self::MAX_DECIMALS`]. /// - the max supply parameter exceeds maximum possible amount for a fungible asset - /// ([`miden_objects::asset::FungibleAsset::MAX_AMOUNT`]) + /// ([`miden_protocol::asset::FungibleAsset::MAX_AMOUNT`]) pub fn new( symbol: TokenSymbol, decimals: u8, @@ -106,7 +106,7 @@ impl NetworkFungibleFaucet { /// [`AccountComponentInterface::NetworkFungibleFaucet`] component. /// - the decimals parameter exceeds maximum value of [`Self::MAX_DECIMALS`]. /// - the max supply value exceeds maximum possible amount for a fungible asset of - /// [`miden_objects::asset::FungibleAsset::MAX_AMOUNT`]. + /// [`miden_protocol::asset::FungibleAsset::MAX_AMOUNT`]. /// - the token symbol encoded value exceeds the maximum value of /// [`TokenSymbol::MAX_ENCODED_VALUE`]. fn try_from_interface( diff --git a/crates/miden-lib/src/account/interface/component.rs b/crates/miden-standards/src/account/interface/component.rs similarity index 95% rename from crates/miden-lib/src/account/interface/component.rs rename to crates/miden-standards/src/account/interface/component.rs index 0ce3bda505..75bba9bb8f 100644 --- a/crates/miden-lib/src/account/interface/component.rs +++ b/crates/miden-standards/src/account/interface/component.rs @@ -1,10 +1,10 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountId, AccountProcedureRoot, AccountStorage, StorageSlotName}; -use miden_objects::note::PartialNote; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountId, AccountProcedureRoot, AccountStorage, StorageSlotName}; +use miden_protocol::note::PartialNote; +use miden_protocol::{Felt, FieldElement, Word}; use crate::AuthScheme; use crate::account::auth::{ @@ -189,10 +189,10 @@ impl AccountComponentInterface { /// /// ```masm /// push.{note_information} - /// call.::miden::output_note::create + /// call.::miden::protocol::output_note::create /// /// push.{note asset} - /// call.::miden::contracts::wallets::basic::move_asset_to_note dropw + /// call.::miden::standards::wallets::basic::move_asset_to_note dropw /// dropw dropw dropw drop /// ``` /// @@ -202,7 +202,7 @@ impl AccountComponentInterface { /// push.{note information} /// /// push.{asset amount} - /// call.::miden::contracts::faucets::basic_fungible::distribute dropw dropw drop + /// call.::miden::standards::faucets::basic_fungible::distribute dropw dropw drop /// ``` /// /// # Errors: @@ -257,19 +257,19 @@ impl AccountComponentInterface { body.push_str(&format!( "push.{amount} - call.::miden::contracts::faucets::basic_fungible::distribute dropw dropw drop\n", + call.::miden::standards::faucets::basic_fungible::distribute dropw dropw drop\n", amount = asset.unwrap_fungible().amount() )); // stack => [] }, AccountComponentInterface::BasicWallet => { - body.push_str("call.::miden::output_note::create\n"); + body.push_str("call.::miden::protocol::output_note::create\n"); // stack => [note_idx] for asset in partial_note.assets().iter() { body.push_str(&format!( "push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note dropw\n", + call.::miden::standards::wallets::basic::move_asset_to_note dropw\n", asset = Word::from(*asset) )); // stack => [note_idx] diff --git a/crates/miden-lib/src/account/interface/extension.rs b/crates/miden-standards/src/account/interface/extension.rs similarity index 97% rename from crates/miden-lib/src/account/interface/extension.rs rename to crates/miden-standards/src/account/interface/extension.rs index 8e698b0b5c..d83cdd2e7f 100644 --- a/crates/miden-lib/src/account/interface/extension.rs +++ b/crates/miden-standards/src/account/interface/extension.rs @@ -2,11 +2,11 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{Account, AccountCode, AccountId, AccountProcedureRoot}; -use miden_objects::assembly::mast::{MastForest, MastNode, MastNodeId}; -use miden_objects::note::{Note, NoteScript}; use miden_processor::MastNodeExt; +use miden_protocol::Word; +use miden_protocol::account::{Account, AccountCode, AccountId, AccountProcedureRoot}; +use miden_protocol::assembly::mast::{MastForest, MastNode, MastNodeId}; +use miden_protocol::note::{Note, NoteScript}; use crate::AuthScheme; use crate::account::components::{ diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-standards/src/account/interface/mod.rs similarity index 93% rename from crates/miden-lib/src/account/interface/mod.rs rename to crates/miden-standards/src/account/interface/mod.rs index ac32cec99b..cdc967759a 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-standards/src/account/interface/mod.rs @@ -1,9 +1,9 @@ use alloc::string::String; use alloc::vec::Vec; -use miden_objects::account::{AccountId, AccountIdPrefix, AccountType}; -use miden_objects::note::PartialNote; -use miden_objects::transaction::TransactionScript; +use miden_protocol::account::{AccountId, AccountIdPrefix, AccountType}; +use miden_protocol::note::PartialNote; +use miden_protocol::transaction::TransactionScript; use thiserror::Error; use crate::AuthScheme; @@ -72,8 +72,8 @@ impl AccountInterface { } /// Returns `true` if the full state of the account is public on chain, i.e. if the modes are - /// [`AccountStorageMode::Public`](miden_objects::account::AccountStorageMode::Public) or - /// [`AccountStorageMode::Network`](miden_objects::account::AccountStorageMode::Network), + /// [`AccountStorageMode::Public`](miden_protocol::account::AccountStorageMode::Public) or + /// [`AccountStorageMode::Network`](miden_protocol::account::AccountStorageMode::Network), /// `false` otherwise. pub fn has_public_state(&self) -> bool { self.account_id.has_public_state() @@ -129,12 +129,12 @@ impl AccountInterface { /// /// ```masm /// begin - /// push.{expiration_delta} exec.::miden::tx::update_expiration_block_delta + /// push.{expiration_delta} exec.::miden::protocol::tx::update_expiration_block_delta /// /// push.{note information} /// /// push.{asset amount} - /// call.::miden::contracts::faucets::basic_fungible::distribute dropw dropw drop + /// call.::miden::standards::faucets::basic_fungible::distribute dropw dropw drop /// end /// ``` /// @@ -206,7 +206,9 @@ impl AccountInterface { /// Returns a string with the expiration delta update procedure call for the script. fn build_set_tx_expiration_section(&self, expiration_delta: Option) -> String { if let Some(expiration_delta) = expiration_delta { - format!("push.{expiration_delta} exec.::miden::tx::update_expiration_block_delta\n") + format!( + "push.{expiration_delta} exec.::miden::protocol::tx::update_expiration_block_delta\n" + ) } else { String::new() } diff --git a/crates/miden-lib/src/account/interface/test.rs b/crates/miden-standards/src/account/interface/test.rs similarity index 96% rename from crates/miden-lib/src/account/interface/test.rs rename to crates/miden-standards/src/account/interface/test.rs index 2abe1e3b7d..5c86dedab7 100644 --- a/crates/miden-lib/src/account/interface/test.rs +++ b/crates/miden-standards/src/account/interface/test.rs @@ -1,9 +1,9 @@ use assert_matches::assert_matches; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountBuilder, AccountComponent, AccountType}; -use miden_objects::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; -use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; -use miden_objects::note::{ +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountBuilder, AccountComponent, AccountType}; +use miden_protocol::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; +use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -13,11 +13,11 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, }; -use miden_objects::{Felt, NoteError, Word, ZERO}; +use miden_protocol::{Felt, NoteError, Word, ZERO}; use crate::AuthScheme; use crate::account::auth::{ @@ -144,7 +144,7 @@ fn test_basic_wallet_default_notes() { #[test] fn test_custom_account_default_note() { let account_custom_code_source = " - use miden::contracts::wallets::basic + use miden::standards::wallets::basic pub use basic::receive_asset "; @@ -263,9 +263,9 @@ fn test_basic_wallet_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use miden::tx - use miden::contracts::wallets::basic->wallet - use miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::protocol::tx + use miden::standards::wallets::basic->wallet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -293,8 +293,8 @@ fn test_basic_wallet_custom_notes() { ); let incompatible_source_code = " - use miden::contracts::wallets::basic->wallet - use miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::wallets::basic->wallet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -353,8 +353,8 @@ fn test_basic_fungible_faucet_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use miden::contracts::wallets::basic->wallet - use miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::wallets::basic->wallet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -381,8 +381,8 @@ fn test_basic_fungible_faucet_custom_notes() { ); let incompatible_source_code = " - use miden::contracts::wallets::basic->wallet - use miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::wallets::basic->wallet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -463,7 +463,7 @@ fn test_custom_account_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use miden::contracts::wallets::basic->wallet + use miden::standards::wallets::basic->wallet use test::account::component_1->test_account begin @@ -494,7 +494,7 @@ fn test_custom_account_custom_notes() { ); let incompatible_source_code = " - use miden::contracts::wallets::basic->wallet + use miden::standards::wallets::basic->wallet use test::account::component_1->test_account begin @@ -574,9 +574,9 @@ fn test_custom_account_multiple_components_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use miden::contracts::wallets::basic->wallet + use miden::standards::wallets::basic->wallet use test::account::component_1->test_account - use miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -611,9 +611,9 @@ fn test_custom_account_multiple_components_custom_notes() { ); let incompatible_source_code = " - use miden::contracts::wallets::basic->wallet + use miden::standards::wallets::basic->wallet use test::account::component_1->test_account - use miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 diff --git a/crates/miden-lib/src/account/mod.rs b/crates/miden-standards/src/account/mod.rs similarity index 79% rename from crates/miden-lib/src/account/mod.rs rename to crates/miden-standards/src/account/mod.rs index 4f4b0694a2..60683d1392 100644 --- a/crates/miden-lib/src/account/mod.rs +++ b/crates/miden-standards/src/account/mod.rs @@ -11,8 +11,8 @@ pub mod wallets; /// This macro generates a `LazyLock` static variable that lazily initializes /// the digest of a procedure from a library. /// -/// Note: This macro references exported types from `miden_objects`, so your crate must -/// include `miden-objects` as a dependency. +/// Note: This macro references exported types from `miden_protocol`, so your crate must +/// include `miden_protocol` as a dependency. /// /// # Arguments /// * `$name` - The name of the static variable to create @@ -30,8 +30,8 @@ pub mod wallets; #[macro_export] macro_rules! procedure_digest { ($name:ident, $proc_name:expr, $library_fn:expr) => { - static $name: miden_objects::utils::sync::LazyLock = - miden_objects::utils::sync::LazyLock::new(|| { + static $name: miden_protocol::utils::sync::LazyLock = + miden_protocol::utils::sync::LazyLock::new(|| { $library_fn().get_procedure_root_by_path($proc_name).unwrap_or_else(|| { panic!("{} should contain '{}' procedure", stringify!($library_fn), $proc_name) }) diff --git a/crates/miden-lib/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs similarity index 96% rename from crates/miden-lib/src/account/wallets/mod.rs rename to crates/miden-standards/src/account/wallets/mod.rs index 06160377c9..fc7c4f0773 100644 --- a/crates/miden-lib/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -1,13 +1,13 @@ use alloc::string::String; -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, AccountStorageMode, AccountType, }; -use miden_objects::{AccountError, Word}; +use miden_protocol::{AccountError, Word}; use thiserror::Error; use super::AuthScheme; @@ -42,7 +42,7 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic wallet. /// /// It reexports the procedures from `miden::contracts::wallets::basic`. When linking against this -/// component, the `miden` library (i.e. [`ProtocolLib`](miden_objects::ProtocolLib)) must be +/// component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must be /// available to the assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: /// - `receive_asset`, which can be used to add an asset to the account. @@ -170,9 +170,9 @@ pub fn create_basic_wallet( #[cfg(test)] mod tests { - use miden_objects::account::auth::PublicKeyCommitment; - use miden_objects::{ONE, Word}; use miden_processor::utils::{Deserializable, Serializable}; + use miden_protocol::account::auth::PublicKeyCommitment; + use miden_protocol::{ONE, Word}; use super::{Account, AccountStorageMode, AccountType, AuthScheme, create_basic_wallet}; use crate::account::wallets::BasicWallet; diff --git a/crates/miden-lib/src/auth.rs b/crates/miden-standards/src/auth.rs similarity index 97% rename from crates/miden-lib/src/auth.rs rename to crates/miden-standards/src/auth.rs index c7c98947e3..32c924f98c 100644 --- a/crates/miden-lib/src/auth.rs +++ b/crates/miden-standards/src/auth.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; +use miden_protocol::account::auth::PublicKeyCommitment; /// Defines authentication schemes available to standard and faucet accounts. pub enum AuthScheme { diff --git a/crates/miden-lib/src/code_builder/mod.rs b/crates/miden-standards/src/code_builder/mod.rs similarity index 96% rename from crates/miden-lib/src/code_builder/mod.rs rename to crates/miden-standards/src/code_builder/mod.rs index fc99422380..7ea72c9dc6 100644 --- a/crates/miden-lib/src/code_builder/mod.rs +++ b/crates/miden-standards/src/code_builder/mod.rs @@ -1,7 +1,7 @@ use alloc::sync::Arc; -use miden_objects::account::AccountComponentCode; -use miden_objects::assembly::{ +use miden_protocol::account::AccountComponentCode; +use miden_protocol::assembly::{ Assembler, DefaultSourceManager, Library, @@ -10,8 +10,8 @@ use miden_objects::assembly::{ Path, SourceManagerSync, }; -use miden_objects::note::NoteScript; -use miden_objects::transaction::{TransactionKernel, TransactionScript}; +use miden_protocol::note::NoteScript; +use miden_protocol::transaction::{TransactionKernel, TransactionScript}; use crate::errors::CodeBuilderError; use crate::standards_lib::StandardsLib; @@ -55,9 +55,9 @@ use crate::standards_lib::StandardsLib; /// /// ```no_run /// # use anyhow::Context; -/// # use miden_lib::code_builder::CodeBuilder; -/// # use miden_objects::assembly::Library; -/// # use miden_objects::CoreLibrary; +/// # use miden_standards::code_builder::CodeBuilder; +/// # use miden_protocol::assembly::Library; +/// # use miden_protocol::CoreLibrary; /// # fn example() -> anyhow::Result<()> { /// # let module_code = "pub proc test push.1 add end"; /// # let script_code = "begin nop end"; @@ -342,7 +342,7 @@ impl CodeBuilder { /// /// [account_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_account_library /// [faucet_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_faucet_library - /// [util_lib]: miden_objects::testing::mock_util_lib::mock_util_library + /// [util_lib]: miden_protocol::testing::mock_util_lib::mock_util_library #[cfg(any(feature = "testing", test))] pub fn with_mock_libraries() -> Self { Self::with_mock_libraries_with_source_manager(Arc::new(DefaultSourceManager::default())) @@ -351,7 +351,7 @@ impl CodeBuilder { /// Returns the mock account and faucet libraries used in testing. #[cfg(any(feature = "testing", test))] pub fn mock_libraries() -> impl Iterator { - use miden_objects::account::AccountCode; + use miden_protocol::account::AccountCode; use crate::testing::mock_account_code::MockAccountCodeExt; @@ -362,7 +362,7 @@ impl CodeBuilder { pub fn with_mock_libraries_with_source_manager( source_manager: Arc, ) -> Self { - use miden_objects::testing::mock_util_lib::mock_util_library; + use miden_protocol::testing::mock_util_lib::mock_util_library; // Start with the builder linking against the transaction kernel, protocol library and // standards library. @@ -405,7 +405,7 @@ impl From for Assembler { #[cfg(test)] mod tests { use anyhow::Context; - use miden_objects::assembly::diagnostics::NamedSource; + use miden_protocol::assembly::diagnostics::NamedSource; use super::*; @@ -435,8 +435,8 @@ mod tests { "; let account_code = " - use miden::active_account - use miden::native_account + use miden::protocol::active_account + use miden::protocol::native_account use miden::core::sys pub proc increment @@ -473,8 +473,8 @@ mod tests { "; let account_code = " - use miden::active_account - use miden::native_account + use miden::protocol::active_account + use miden::protocol::native_account use miden::core::sys pub proc increment @@ -524,8 +524,8 @@ mod tests { "; let account_code = " - use miden::active_account - use miden::native_account + use miden::protocol::active_account + use miden::protocol::native_account use miden::core::sys pub proc increment diff --git a/crates/miden-lib/src/errors/code_builder_errors.rs b/crates/miden-standards/src/errors/code_builder_errors.rs similarity index 93% rename from crates/miden-lib/src/errors/code_builder_errors.rs rename to crates/miden-standards/src/errors/code_builder_errors.rs index 0c2f1913d5..14f3a9eb71 100644 --- a/crates/miden-lib/src/errors/code_builder_errors.rs +++ b/crates/miden-standards/src/errors/code_builder_errors.rs @@ -2,8 +2,8 @@ use alloc::boxed::Box; use alloc::string::String; use core::error::Error; -use miden_objects::assembly::diagnostics::Report; -use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic; +use miden_protocol::assembly::diagnostics::Report; +use miden_protocol::assembly::diagnostics::reporting::PrintDiagnostic; // CODE BUILDER ERROR // ================================================================================================ diff --git a/crates/miden-lib/src/errors/mod.rs b/crates/miden-standards/src/errors/mod.rs similarity index 100% rename from crates/miden-lib/src/errors/mod.rs rename to crates/miden-standards/src/errors/mod.rs diff --git a/crates/miden-lib/src/errors/standards.rs b/crates/miden-standards/src/errors/standards.rs similarity index 98% rename from crates/miden-lib/src/errors/standards.rs rename to crates/miden-standards/src/errors/standards.rs index 0f53b5fcd7..3bfee4952e 100644 --- a/crates/miden-lib/src/errors/standards.rs +++ b/crates/miden-standards/src/errors/standards.rs @@ -1,7 +1,7 @@ -use miden_objects::errors::MasmError; +use miden_protocol::errors::MasmError; // This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the MASM files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `./asm` directory. // // To add a new error, define a constant in MASM of the pattern `const ERR__...`. // Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). diff --git a/crates/miden-lib/src/lib.rs b/crates/miden-standards/src/lib.rs similarity index 100% rename from crates/miden-lib/src/lib.rs rename to crates/miden-standards/src/lib.rs diff --git a/crates/miden-lib/src/note/mint_inputs.rs b/crates/miden-standards/src/note/mint_inputs.rs similarity index 95% rename from crates/miden-lib/src/note/mint_inputs.rs rename to crates/miden-standards/src/note/mint_inputs.rs index 2588aa3a7a..c5ddb2c63d 100644 --- a/crates/miden-lib/src/note/mint_inputs.rs +++ b/crates/miden-standards/src/note/mint_inputs.rs @@ -1,5 +1,5 @@ -use miden_objects::note::{NoteExecutionHint, NoteInputs, NoteRecipient}; -use miden_objects::{Felt, MAX_INPUTS_PER_NOTE, NoteError, Word}; +use miden_protocol::note::{NoteExecutionHint, NoteInputs, NoteRecipient}; +use miden_protocol::{Felt, MAX_INPUTS_PER_NOTE, NoteError, Word}; /// Represents the different input formats for MINT notes. /// - Private: Creates a private output note using a precomputed recipient digest (8 MINT note diff --git a/crates/miden-lib/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs similarity index 97% rename from crates/miden-lib/src/note/mod.rs rename to crates/miden-standards/src/note/mod.rs index 67f12bf539..2dbf1db2a6 100644 --- a/crates/miden-lib/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -1,10 +1,10 @@ use alloc::vec::Vec; -use miden_objects::account::AccountId; -use miden_objects::asset::Asset; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::rand::FeltRng; -use miden_objects::note::{ +use miden_protocol::account::AccountId; +use miden_protocol::asset::Asset; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::note::{ Note, NoteAssets, NoteDetails, @@ -15,7 +15,7 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::{Felt, NoteError, Word}; +use miden_protocol::{Felt, NoteError, Word}; use utils::build_swap_tag; pub mod mint_inputs; diff --git a/crates/miden-lib/src/note/utils.rs b/crates/miden-standards/src/note/utils.rs similarity index 90% rename from crates/miden-lib/src/note/utils.rs rename to crates/miden-standards/src/note/utils.rs index 0968a042a0..6e3e408983 100644 --- a/crates/miden-lib/src/note/utils.rs +++ b/crates/miden-standards/src/note/utils.rs @@ -1,8 +1,8 @@ -use miden_objects::account::AccountId; -use miden_objects::asset::Asset; -use miden_objects::block::BlockNumber; -use miden_objects::note::{NoteExecutionMode, NoteInputs, NoteRecipient, NoteTag, NoteType}; -use miden_objects::{Felt, NoteError, Word}; +use miden_protocol::account::AccountId; +use miden_protocol::asset::Asset; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{NoteExecutionMode, NoteInputs, NoteRecipient, NoteTag, NoteType}; +use miden_protocol::{Felt, NoteError, Word}; use super::well_known_note::WellKnownNote; @@ -78,9 +78,9 @@ pub fn build_swap_tag( #[cfg(test)] mod tests { - use miden_objects::account::{AccountIdVersion, AccountStorageMode, AccountType}; - use miden_objects::asset::{FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; - use miden_objects::{self}; + use miden_protocol::account::{AccountIdVersion, AccountStorageMode, AccountType}; + use miden_protocol::asset::{FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; + use miden_protocol::{self}; use super::*; diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-standards/src/note/well_known_note.rs similarity index 98% rename from crates/miden-lib/src/note/well_known_note.rs rename to crates/miden-standards/src/note/well_known_note.rs index 949b696708..2c0816fbc5 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-standards/src/note/well_known_note.rs @@ -2,13 +2,13 @@ use alloc::boxed::Box; use alloc::string::String; use core::error::Error; -use miden_objects::account::AccountId; -use miden_objects::block::BlockNumber; -use miden_objects::note::{Note, NoteScript}; -use miden_objects::utils::Deserializable; -use miden_objects::utils::sync::LazyLock; -use miden_objects::vm::Program; -use miden_objects::{Felt, Word}; +use miden_protocol::account::AccountId; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{Note, NoteScript}; +use miden_protocol::utils::Deserializable; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::vm::Program; +use miden_protocol::{Felt, Word}; use crate::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; diff --git a/crates/miden-lib/src/standards_lib.rs b/crates/miden-standards/src/standards_lib.rs similarity index 86% rename from crates/miden-lib/src/standards_lib.rs rename to crates/miden-standards/src/standards_lib.rs index 2c832727a1..b8429b6acb 100644 --- a/crates/miden-lib/src/standards_lib.rs +++ b/crates/miden-standards/src/standards_lib.rs @@ -1,9 +1,9 @@ use alloc::sync::Arc; -use miden_objects::assembly::Library; -use miden_objects::assembly::mast::MastForest; -use miden_objects::utils::serde::Deserializable; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::assembly::Library; +use miden_protocol::assembly::mast::MastForest; +use miden_protocol::utils::serde::Deserializable; +use miden_protocol::utils::sync::LazyLock; // CONSTANTS // ================================================================================================ @@ -53,13 +53,13 @@ impl Default for StandardsLib { // NOTE: Most standards-related tests can be found in miden-testing. #[cfg(all(test, feature = "std"))] mod tests { - use miden_objects::assembly::Path; + use miden_protocol::assembly::Path; use super::StandardsLib; #[test] fn test_compile() { - let path = Path::new("::miden::contracts::faucets::basic_fungible::distribute"); + let path = Path::new("::miden::standards::faucets::basic_fungible::distribute"); let miden = StandardsLib::default(); let exists = miden.0.module_infos().any(|module| { module diff --git a/crates/miden-lib/src/testing/account_component/conditional_auth.rs b/crates/miden-standards/src/testing/account_component/conditional_auth.rs similarity index 91% rename from crates/miden-lib/src/testing/account_component/conditional_auth.rs rename to crates/miden-standards/src/testing/account_component/conditional_auth.rs index df05ff3dab..3ca719e220 100644 --- a/crates/miden-lib/src/testing/account_component/conditional_auth.rs +++ b/crates/miden-standards/src/testing/account_component/conditional_auth.rs @@ -1,7 +1,7 @@ use alloc::string::String; -use miden_objects::account::{AccountComponent, AccountComponentCode}; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::{AccountComponent, AccountComponentCode}; +use miden_protocol::utils::sync::LazyLock; use crate::code_builder::CodeBuilder; @@ -10,7 +10,7 @@ pub const ERR_WRONG_ARGS_MSG: &str = "auth procedure args are incorrect"; static CONDITIONAL_AUTH_CODE: LazyLock = LazyLock::new(|| { format!( r#" - use miden::native_account + use miden::protocol::native_account const WRONG_ARGS="{ERR_WRONG_ARGS_MSG}" diff --git a/crates/miden-lib/src/testing/account_component/incr_nonce.rs b/crates/miden-standards/src/testing/account_component/incr_nonce.rs similarity index 84% rename from crates/miden-lib/src/testing/account_component/incr_nonce.rs rename to crates/miden-standards/src/testing/account_component/incr_nonce.rs index 3956176d9c..34087c04eb 100644 --- a/crates/miden-lib/src/testing/account_component/incr_nonce.rs +++ b/crates/miden-standards/src/testing/account_component/incr_nonce.rs @@ -1,11 +1,11 @@ -use miden_objects::account::AccountComponent; -use miden_objects::assembly::Library; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::AccountComponent; +use miden_protocol::assembly::Library; +use miden_protocol::utils::sync::LazyLock; use crate::code_builder::CodeBuilder; const INCR_NONCE_AUTH_CODE: &str = " - use miden::native_account + use miden::protocol::native_account pub proc auth_incr_nonce exec.native_account::incr_nonce drop diff --git a/crates/miden-lib/src/testing/account_component/mock_account_component.rs b/crates/miden-standards/src/testing/account_component/mock_account_component.rs similarity index 91% rename from crates/miden-lib/src/testing/account_component/mock_account_component.rs rename to crates/miden-standards/src/testing/account_component/mock_account_component.rs index 91e51c6166..3b1f95dc1e 100644 --- a/crates/miden-lib/src/testing/account_component/mock_account_component.rs +++ b/crates/miden-standards/src/testing/account_component/mock_account_component.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use miden_objects::account::{AccountCode, AccountComponent, AccountStorage, StorageSlot}; +use miden_protocol::account::{AccountCode, AccountComponent, AccountStorage, StorageSlot}; use crate::testing::mock_account_code::MockAccountCodeExt; @@ -13,7 +13,7 @@ use crate::testing::mock_account_code::MockAccountCodeExt; /// arbitrary number of storage slots (within the overall limit) so anything can be set for testing /// purposes. /// -/// This component supports all [`AccountType`](miden_objects::account::AccountType)s for testing +/// This component supports all [`AccountType`](miden_protocol::account::AccountType)s for testing /// purposes. /// /// [account_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_account_library diff --git a/crates/miden-lib/src/testing/account_component/mock_faucet_component.rs b/crates/miden-standards/src/testing/account_component/mock_faucet_component.rs similarity index 84% rename from crates/miden-lib/src/testing/account_component/mock_faucet_component.rs rename to crates/miden-standards/src/testing/account_component/mock_faucet_component.rs index 88225d10ee..0520d541cc 100644 --- a/crates/miden-lib/src/testing/account_component/mock_faucet_component.rs +++ b/crates/miden-standards/src/testing/account_component/mock_faucet_component.rs @@ -1,4 +1,4 @@ -use miden_objects::account::{AccountCode, AccountComponent, AccountType}; +use miden_protocol::account::{AccountCode, AccountComponent, AccountType}; use crate::testing::mock_account_code::MockAccountCodeExt; @@ -10,7 +10,7 @@ use crate::testing::mock_account_code::MockAccountCodeExt; /// It uses the [`MockAccountCodeExt::mock_faucet_library`][faucet_lib] and contains no storage /// slots. /// -/// This component supports the faucet [`AccountType`](miden_objects::account::AccountType)s for +/// This component supports the faucet [`AccountType`](miden_protocol::account::AccountType)s for /// testing purposes. /// /// [faucet_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_faucet_library diff --git a/crates/miden-lib/src/testing/account_component/mod.rs b/crates/miden-standards/src/testing/account_component/mod.rs similarity index 100% rename from crates/miden-lib/src/testing/account_component/mod.rs rename to crates/miden-standards/src/testing/account_component/mod.rs diff --git a/crates/miden-lib/src/testing/account_interface.rs b/crates/miden-standards/src/testing/account_interface.rs similarity index 87% rename from crates/miden-lib/src/testing/account_interface.rs rename to crates/miden-standards/src/testing/account_interface.rs index d329b15a09..932c03ee4f 100644 --- a/crates/miden-lib/src/testing/account_interface.rs +++ b/crates/miden-standards/src/testing/account_interface.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::Account; +use miden_protocol::Word; +use miden_protocol::account::Account; use crate::account::interface::{AccountInterface, AccountInterfaceExt}; diff --git a/crates/miden-lib/src/testing/mock_account.rs b/crates/miden-standards/src/testing/mock_account.rs similarity index 93% rename from crates/miden-lib/src/testing/mock_account.rs rename to crates/miden-standards/src/testing/mock_account.rs index 3f3dfdb691..e92e5f595d 100644 --- a/crates/miden-lib/src/testing/mock_account.rs +++ b/crates/miden-standards/src/testing/mock_account.rs @@ -1,4 +1,4 @@ -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -8,10 +8,10 @@ use miden_objects::account::{ StorageMap, StorageSlot, }; -use miden_objects::asset::{AssetVault, NonFungibleAsset}; -use miden_objects::testing::constants::{self}; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::{Felt, Word, ZERO}; +use miden_protocol::asset::{AssetVault, NonFungibleAsset}; +use miden_protocol::testing::constants::{self}; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_protocol::{Felt, Word, ZERO}; use crate::testing::account_component::{MockAccountComponent, MockFaucetComponent}; diff --git a/crates/miden-lib/src/testing/mock_account_code.rs b/crates/miden-standards/src/testing/mock_account_code.rs similarity index 92% rename from crates/miden-lib/src/testing/mock_account_code.rs rename to crates/miden-standards/src/testing/mock_account_code.rs index fe2e7472da..357ae92fbf 100644 --- a/crates/miden-lib/src/testing/mock_account_code.rs +++ b/crates/miden-standards/src/testing/mock_account_code.rs @@ -1,11 +1,11 @@ -use miden_objects::account::AccountCode; -use miden_objects::assembly::Library; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::AccountCode; +use miden_protocol::assembly::Library; +use miden_protocol::utils::sync::LazyLock; use crate::code_builder::CodeBuilder; const MOCK_FAUCET_CODE: &str = " - use miden::faucet + use miden::protocol::faucet #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET, pad(12)] @@ -23,12 +23,12 @@ const MOCK_FAUCET_CODE: &str = " "; const MOCK_ACCOUNT_CODE: &str = " - use miden::active_account - use miden::native_account - use miden::tx + use miden::protocol::active_account + use miden::protocol::native_account + use miden::protocol::tx - pub use ::miden::contracts::wallets::basic::receive_asset - pub use ::miden::contracts::wallets::basic::move_asset_to_note + pub use ::miden::standards::wallets::basic::receive_asset + pub use ::miden::standards::wallets::basic::move_asset_to_note # Note: all account's export procedures below should be only called or dyncall'ed, so it # is assumed that the operand stack at the beginning of their execution is pad'ed and diff --git a/crates/miden-lib/src/testing/mod.rs b/crates/miden-standards/src/testing/mod.rs similarity index 100% rename from crates/miden-lib/src/testing/mod.rs rename to crates/miden-standards/src/testing/mod.rs diff --git a/crates/miden-lib/src/testing/note.rs b/crates/miden-standards/src/testing/note.rs similarity index 93% rename from crates/miden-lib/src/testing/note.rs rename to crates/miden-standards/src/testing/note.rs index a5e6dfc7f9..16451b9687 100644 --- a/crates/miden-lib/src/testing/note.rs +++ b/crates/miden-standards/src/testing/note.rs @@ -2,11 +2,11 @@ use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::account::AccountId; -use miden_objects::assembly::debuginfo::{SourceLanguage, SourceManagerSync, Uri}; -use miden_objects::assembly::{DefaultSourceManager, Library}; -use miden_objects::asset::Asset; -use miden_objects::note::{ +use miden_protocol::account::AccountId; +use miden_protocol::assembly::debuginfo::{SourceLanguage, SourceManagerSync, Uri}; +use miden_protocol::assembly::{DefaultSourceManager, Library}; +use miden_protocol::asset::Asset; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -16,8 +16,8 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::note::DEFAULT_NOTE_CODE; -use miden_objects::{Felt, NoteError, Word, ZERO}; +use miden_protocol::testing::note::DEFAULT_NOTE_CODE; +use miden_protocol::{Felt, NoteError, Word, ZERO}; use rand::Rng; use crate::code_builder::CodeBuilder; diff --git a/crates/miden-testing/Cargo.toml b/crates/miden-testing/Cargo.toml index 4dcdc3cf2c..4420fa6249 100644 --- a/crates/miden-testing/Cargo.toml +++ b/crates/miden-testing/Cargo.toml @@ -13,13 +13,13 @@ rust-version.workspace = true version.workspace = true [features] -std = ["miden-lib/std", "miden-objects/std", "miden-tx/std"] +std = ["miden-protocol/std", "miden-standards/std", "miden-tx/std"] [dependencies] # Workspace dependencies miden-block-prover = { features = ["testing"], workspace = true } -miden-lib = { features = ["testing"], workspace = true } -miden-objects = { features = ["testing"], workspace = true } +miden-protocol = { features = ["testing"], workspace = true } +miden-standards = { features = ["testing"], workspace = true } miden-tx = { features = ["testing"], workspace = true } miden-tx-batch-prover = { features = ["testing"], workspace = true } @@ -36,7 +36,7 @@ winterfell = { version = "0.13" } [dev-dependencies] anyhow = { features = ["backtrace", "std"], workspace = true } assert_matches = { workspace = true } -miden-objects = { features = ["std"], workspace = true } +miden-protocol = { features = ["std"], workspace = true } rstest = { workspace = true } tokio = { features = ["macros", "rt"], workspace = true } winter-rand-utils = { version = "0.13" } diff --git a/crates/miden-testing/src/executor.rs b/crates/miden-testing/src/executor.rs index 8f3689f671..02b79705ed 100644 --- a/crates/miden-testing/src/executor.rs +++ b/crates/miden-testing/src/executor.rs @@ -1,9 +1,9 @@ #[cfg(test)] -use miden_objects::assembly::Assembler; -#[cfg(test)] use miden_processor::DefaultHost; use miden_processor::fast::{ExecutionOutput, FastProcessor}; use miden_processor::{AdviceInputs, AsyncHost, ExecutionError, Program, StackInputs}; +#[cfg(test)] +use miden_protocol::assembly::Assembler; // CODE EXECUTOR // ================================================================================================ @@ -39,15 +39,15 @@ impl CodeExecutor { /// Compiles and runs the desired code in the host and returns the [`Process`] state. /// /// To improve the error message quality, convert the returned [`ExecutionError`] into a - /// [`Report`](miden_objects::assembly::diagnostics::Report). + /// [`Report`](miden_protocol::assembly::diagnostics::Report). #[cfg(test)] pub async fn run(self, code: &str) -> Result { use alloc::borrow::ToOwned; use alloc::sync::Arc; - use miden_lib::code_builder::CodeBuilder; - use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; - use miden_objects::assembly::{DefaultSourceManager, SourceManagerSync}; + use miden_protocol::assembly::debuginfo::{SourceLanguage, Uri}; + use miden_protocol::assembly::{DefaultSourceManager, SourceManagerSync}; + use miden_standards::code_builder::CodeBuilder; let source_manager: Arc = Arc::new(DefaultSourceManager::default()); let assembler: Assembler = CodeBuilder::with_kernel_library(source_manager.clone()).into(); @@ -63,7 +63,7 @@ impl CodeExecutor { /// Executes the provided [`Program`] and returns the [`Process`] state. /// /// To improve the error message quality, convert the returned [`ExecutionError`] into a - /// [`Report`](miden_objects::assembly::diagnostics::Report). + /// [`Report`](miden_protocol::assembly::diagnostics::Report). pub async fn execute_program( mut self, program: Program, @@ -88,7 +88,7 @@ impl CodeExecutor { #[cfg(test)] impl CodeExecutor { pub fn with_default_host() -> Self { - use miden_objects::transaction::TransactionKernel; + use miden_protocol::transaction::TransactionKernel; let mut host = DefaultHost::default(); diff --git a/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs b/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs index 78b1d61eb3..3f6b1f509f 100644 --- a/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs +++ b/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs @@ -3,16 +3,16 @@ use std::collections::BTreeMap; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::{Account, AccountId, AccountStorageMode}; -use miden_objects::batch::ProposedBatch; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::MerkleError; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::AccountIdBuilder; -use miden_objects::transaction::{InputNote, InputNoteCommitment, OutputNote, PartialBlockchain}; -use miden_objects::{BatchAccountUpdateError, ProposedBatchError, Word}; +use miden_protocol::account::{Account, AccountId, AccountStorageMode}; +use miden_protocol::batch::ProposedBatch; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::MerkleError; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::account_id::AccountIdBuilder; +use miden_protocol::transaction::{InputNote, InputNoteCommitment, OutputNote, PartialBlockchain}; +use miden_protocol::{BatchAccountUpdateError, ProposedBatchError, Word}; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::note::NoteBuilder; use rand::rngs::SmallRng; use rand::{Rng, SeedableRng}; diff --git a/crates/miden-testing/src/kernel_tests/batch/proven_tx_builder.rs b/crates/miden-testing/src/kernel_tests/batch/proven_tx_builder.rs index 3c583dd06a..95be1c04c5 100644 --- a/crates/miden-testing/src/kernel_tests/batch/proven_tx_builder.rs +++ b/crates/miden-testing/src/kernel_tests/batch/proven_tx_builder.rs @@ -1,19 +1,19 @@ use alloc::vec::Vec; use anyhow::Context; -use miden_objects::Word; -use miden_objects::account::AccountId; -use miden_objects::asset::FungibleAsset; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::SparseMerklePath; -use miden_objects::note::{Note, NoteInclusionProof, Nullifier}; -use miden_objects::transaction::{ +use miden_protocol::Word; +use miden_protocol::account::AccountId; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::SparseMerklePath; +use miden_protocol::note::{Note, NoteInclusionProof, Nullifier}; +use miden_protocol::transaction::{ InputNote, OutputNote, ProvenTransaction, ProvenTransactionBuilder, }; -use miden_objects::vm::ExecutionProof; +use miden_protocol::vm::ExecutionProof; /// A builder to build mocked [`ProvenTransaction`]s. pub struct MockProvenTxBuilder { diff --git a/crates/miden-testing/src/kernel_tests/block/header_errors.rs b/crates/miden-testing/src/kernel_tests/block/header_errors.rs index b79d606ac1..a4a48262c1 100644 --- a/crates/miden-testing/src/kernel_tests/block/header_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/header_errors.rs @@ -2,10 +2,8 @@ use alloc::vec::Vec; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::testing::account_component::{IncrNonceAuthComponent, MockAccountComponent}; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{ +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -13,13 +11,15 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::asset::FungibleAsset; -use miden_objects::batch::ProvenBatch; -use miden_objects::block::{BlockInputs, BlockNumber, ProposedBlock}; -use miden_objects::note::NoteType; -use miden_objects::transaction::ProvenTransactionBuilder; -use miden_objects::vm::ExecutionProof; -use miden_objects::{AccountTreeError, NullifierTreeError, ProposedBlockError, Word}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::batch::ProvenBatch; +use miden_protocol::block::{BlockInputs, BlockNumber, ProposedBlock}; +use miden_protocol::note::NoteType; +use miden_protocol::transaction::ProvenTransactionBuilder; +use miden_protocol::vm::ExecutionProof; +use miden_protocol::{AccountTreeError, NullifierTreeError, ProposedBlockError, Word}; +use miden_standards::testing::account_component::{IncrNonceAuthComponent, MockAccountComponent}; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::LocalTransactionProver; use crate::kernel_tests::block::utils::MockChainBlockExt; diff --git a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs index b2b0b879fa..18389782c2 100644 --- a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs @@ -3,13 +3,13 @@ use std::collections::BTreeMap; use std::vec::Vec; use assert_matches::assert_matches; -use miden_lib::note::create_p2id_note; -use miden_objects::asset::FungibleAsset; -use miden_objects::block::{BlockInputs, BlockNumber, ProposedBlock}; -use miden_objects::crypto::merkle::SparseMerklePath; -use miden_objects::note::{NoteInclusionProof, NoteType}; -use miden_objects::{MAX_BATCHES_PER_BLOCK, ProposedBlockError, ZERO}; use miden_processor::crypto::MerklePath; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::block::{BlockInputs, BlockNumber, ProposedBlock}; +use miden_protocol::crypto::merkle::SparseMerklePath; +use miden_protocol::note::{NoteInclusionProof, NoteType}; +use miden_protocol::{MAX_BATCHES_PER_BLOCK, ProposedBlockError, ZERO}; +use miden_standards::note::create_p2id_note; use miden_tx::LocalTransactionProver; use crate::kernel_tests::block::utils::MockChainBlockExt; diff --git a/crates/miden-testing/src/kernel_tests/block/proposed_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proposed_block_success.rs index e9ab238fe9..cce09fcaaa 100644 --- a/crates/miden-testing/src/kernel_tests/block/proposed_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proposed_block_success.rs @@ -4,16 +4,16 @@ use std::vec::Vec; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{Account, AccountId, AccountStorageMode}; -use miden_objects::asset::FungibleAsset; -use miden_objects::block::{BlockInputs, ProposedBlock}; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::ACCOUNT_ID_SENDER; -use miden_objects::transaction::{ExecutedTransaction, OutputNote, TransactionHeader}; -use miden_objects::{Felt, FieldElement}; +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{Account, AccountId, AccountStorageMode}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::block::{BlockInputs, ProposedBlock}; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::account_id::ACCOUNT_ID_SENDER; +use miden_protocol::transaction::{ExecutedTransaction, OutputNote, TransactionHeader}; +use miden_protocol::{Felt, FieldElement}; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::note::NoteBuilder; use miden_tx::LocalTransactionProver; use rand::Rng; diff --git a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs index fbe3855c08..6831529a7b 100644 --- a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs @@ -3,15 +3,15 @@ use std::collections::BTreeMap; use std::vec::Vec; use anyhow::Context; -use miden_lib::note::create_p2id_note; -use miden_objects::ZERO; -use miden_objects::asset::FungibleAsset; -use miden_objects::batch::BatchNoteTree; -use miden_objects::block::account_tree::AccountTree; -use miden_objects::block::{BlockInputs, BlockNoteIndex, BlockNoteTree, ProposedBlock}; -use miden_objects::crypto::merkle::smt::Smt; -use miden_objects::note::NoteType; -use miden_objects::transaction::InputNoteCommitment; +use miden_protocol::ZERO; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::batch::BatchNoteTree; +use miden_protocol::block::account_tree::AccountTree; +use miden_protocol::block::{BlockInputs, BlockNoteIndex, BlockNoteTree, ProposedBlock}; +use miden_protocol::crypto::merkle::smt::Smt; +use miden_protocol::note::NoteType; +use miden_protocol::transaction::InputNoteCommitment; +use miden_standards::note::create_p2id_note; use crate::kernel_tests::block::utils::MockChainBlockExt; use crate::utils::create_p2any_note; diff --git a/crates/miden-testing/src/kernel_tests/block/utils.rs b/crates/miden-testing/src/kernel_tests/block/utils.rs index 122b4d9884..f3b5de580e 100644 --- a/crates/miden-testing/src/kernel_tests/block/utils.rs +++ b/crates/miden-testing/src/kernel_tests/block/utils.rs @@ -1,11 +1,11 @@ use std::vec::Vec; -use miden_lib::code_builder::CodeBuilder; -use miden_objects::account::AccountId; -use miden_objects::batch::ProvenBatch; -use miden_objects::block::BlockNumber; -use miden_objects::note::{Note, NoteId}; -use miden_objects::transaction::{ExecutedTransaction, ProvenTransaction, TransactionScript}; +use miden_protocol::account::AccountId; +use miden_protocol::batch::ProvenBatch; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{Note, NoteId}; +use miden_protocol::transaction::{ExecutedTransaction, ProvenTransaction, TransactionScript}; +use miden_standards::code_builder::CodeBuilder; use miden_tx::LocalTransactionProver; use crate::{MockChain, TxContextInput}; @@ -101,7 +101,7 @@ impl MockChainBlockExt for MockChain { fn update_expiration_tx_script(expiration_delta: u16) -> TransactionScript { let code = format!( " - use miden::tx + use miden::protocol::tx begin push.{expiration_delta} diff --git a/crates/miden-testing/src/kernel_tests/tx/mod.rs b/crates/miden-testing/src/kernel_tests/tx/mod.rs index 5ceaf76464..e59b033d0a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/mod.rs +++ b/crates/miden-testing/src/kernel_tests/tx/mod.rs @@ -1,16 +1,18 @@ use alloc::string::String; use anyhow::Context; -use miden_objects::account::{Account, AccountId}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::{ +use miden_processor::ContextId; +use miden_processor::fast::ExecutionOutput; +use miden_protocol::account::{Account, AccountId}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::storage::prepare_assets; -use miden_objects::transaction::memory::{ +use miden_protocol::testing::storage::prepare_assets; +use miden_protocol::transaction::memory::{ self, MemoryOffset, NOTE_MEM_SIZE, @@ -22,10 +24,8 @@ use miden_objects::transaction::memory::{ OUTPUT_NOTE_RECIPIENT_OFFSET, OUTPUT_NOTE_SECTION_OFFSET, }; -use miden_objects::vm::StackInputs; -use miden_objects::{Felt, Word, ZERO}; -use miden_processor::ContextId; -use miden_processor::fast::ExecutionOutput; +use miden_protocol::vm::StackInputs; +use miden_protocol::{Felt, Word, ZERO}; use crate::MockChain; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 70ac196435..64e9c1e905 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -4,11 +4,9 @@ use std::collections::BTreeMap; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{ +use miden_processor::{ExecutionError, Word}; +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{ Account, AccountBuilder, AccountCode, @@ -25,10 +23,10 @@ use miden_objects::account::{ StorageSlotName, StorageSlotType, }; -use miden_objects::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette}; -use miden_objects::assembly::{DefaultSourceManager, Library}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::errors::tx_kernel::{ +use miden_protocol::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette}; +use miden_protocol::assembly::{DefaultSourceManager, Library}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::errors::tx_kernel::{ ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO, ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO, ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE, @@ -38,8 +36,8 @@ use miden_objects::errors::tx_kernel::{ ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME, ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED, }; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::{ +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, @@ -47,11 +45,13 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; -use miden_objects::transaction::{OutputNote, TransactionKernel}; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{LexicographicWord, StarkField}; -use miden_processor::{ExecutionError, Word}; +use miden_protocol::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; +use miden_protocol::transaction::{OutputNote, TransactionKernel}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{LexicographicWord, StarkField}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::LocalTransactionProver; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -88,7 +88,7 @@ pub async fn compute_commitment() -> miette::Result<()> { r#" use miden::core::word - use miden::active_account + use miden::protocol::active_account use mock::account->mock_account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -578,9 +578,9 @@ async fn test_account_get_item_fails_on_unknown_slot() -> anyhow::Result<()> { #[tokio::test] async fn test_account_set_item_fails_on_reserved_faucet_metadata_slot() -> anyhow::Result<()> { let code = r#" - use miden::native_account + use miden::protocol::native_account - const FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") + const FAUCET_SYSDATA_SLOT=word("miden::protocol::faucet::sysdata") begin push.FAUCET_SYSDATA_SLOT[0..2] @@ -807,7 +807,7 @@ async fn test_get_initial_storage_commitment() -> anyhow::Result<()> { let code = format!( r#" - use miden::active_account + use miden::protocol::active_account use $kernel::prologue begin @@ -1005,7 +1005,7 @@ async fn test_get_vault_root() -> anyhow::Result<()> { // get the initial vault root let code = format!( " - use miden::active_account + use miden::protocol::active_account use $kernel::prologue begin @@ -1026,7 +1026,7 @@ async fn test_get_vault_root() -> anyhow::Result<()> { let code = format!( r#" - use miden::active_account + use miden::protocol::active_account use $kernel::prologue use mock::account->mock_account @@ -1052,13 +1052,13 @@ async fn test_get_vault_root() -> anyhow::Result<()> { Ok(()) } -/// This test checks the correctness of the `miden::active_account::get_initial_balance` procedure -/// in two cases: +/// This test checks the correctness of the `miden::protocol::active_account::get_initial_balance` +/// procedure in two cases: /// - when a note adds the asset which already exists in the account vault. /// - when a note adds the asset which doesn't exist in the account vault. /// /// As part of the test pipeline it also checks the correctness of the -/// `miden::active_account::get_balance` procedure. +/// `miden::protocol::active_account::get_balance` procedure. #[tokio::test] async fn test_get_init_balance_addition() -> anyhow::Result<()> { // prepare the testing data @@ -1110,7 +1110,7 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { let add_existing_source = format!( r#" - use miden::active_account + use miden::protocol::active_account begin # push faucet ID prefix and suffix @@ -1164,7 +1164,7 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { let add_new_source = format!( r#" - use miden::active_account + use miden::protocol::active_account begin # push faucet ID prefix and suffix @@ -1206,11 +1206,11 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { Ok(()) } -/// This test checks the correctness of the `miden::active_account::get_initial_balance` procedure -/// in case when we create a note which removes an asset from the account vault. +/// This test checks the correctness of the `miden::protocol::active_account::get_initial_balance` +/// procedure in case when we create a note which removes an asset from the account vault. /// /// As part of the test pipeline it also checks the correctness of the -/// `miden::active_account::get_balance` procedure. +/// `miden::protocol::active_account::get_balance` procedure. #[tokio::test] async fn test_get_init_balance_subtraction() -> anyhow::Result<()> { let mut builder = MockChain::builder(); @@ -1241,8 +1241,8 @@ async fn test_get_init_balance_subtraction() -> anyhow::Result<()> { let remove_existing_source = format!( r#" - use miden::active_account - use miden::contracts::wallets::basic->wallet + use miden::protocol::active_account + use miden::standards::wallets::basic->wallet use mock::util # Inputs: [ASSET, note_idx] @@ -1393,7 +1393,7 @@ async fn test_was_procedure_called() -> miette::Result<()> { let tx_script_code = format!( r#" use mock::account->mock_account - use miden::native_account + use miden::protocol::native_account const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") @@ -1449,7 +1449,7 @@ async fn test_was_procedure_called() -> miette::Result<()> { async fn transaction_executor_account_code_using_custom_library() -> miette::Result<()> { let external_library_code = format!( r#" - use miden::native_account + use miden::protocol::native_account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -1474,7 +1474,7 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res let external_library = TransactionKernel::assembler().assemble_library([external_library_source])?; - let mut assembler: miden_objects::assembly::Assembler = + let mut assembler: miden_protocol::assembly::Assembler = CodeBuilder::with_mock_libraries_with_source_manager(Arc::new( DefaultSourceManager::default(), )) @@ -1534,7 +1534,7 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res #[tokio::test] async fn incrementing_nonce_twice_fails() -> anyhow::Result<()> { let source_code = " - use miden::native_account + use miden::protocol::native_account pub proc auth_incr_nonce_twice exec.native_account::incr_nonce drop @@ -1571,7 +1571,7 @@ async fn test_has_procedure() -> miette::Result<()> { let tx_script_code = r#" use mock::account->mock_account - use miden::active_account + use miden::protocol::active_account begin # check that get_item procedure is available on the mock account @@ -1776,7 +1776,7 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> static COMPONENT_1_LIBRARY: LazyLock = LazyLock::new(|| { let code = format!( r#" - use miden::active_account + use miden::protocol::active_account const TEST_SLOT_NAME = word("{test_slot_name}") @@ -1798,8 +1798,8 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> static COMPONENT_2_LIBRARY: LazyLock = LazyLock::new(|| { let code = format!( r#" - use miden::active_account - use miden::native_account + use miden::protocol::active_account + use miden::protocol::native_account const TEST_SLOT_NAME = word("{test_slot_name}") diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index 7bc4712f73..bf1ffe2849 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -3,10 +3,8 @@ use std::collections::BTreeMap; use std::string::String; use anyhow::Context; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{ +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{ Account, AccountBuilder, AccountDelta, @@ -19,9 +17,9 @@ use miden_objects::account::{ StorageSlotDelta, StorageSlotName, }; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; -use miden_objects::note::{Note, NoteExecutionHint, NoteTag, NoteType}; -use miden_objects::testing::account_id::{ +use miden_protocol::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; +use miden_protocol::note::{Note, NoteExecutionHint, NoteTag, NoteType}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_3, @@ -29,17 +27,19 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_SENDER, AccountIdBuilder, }; -use miden_objects::testing::asset::NonFungibleAssetBuilder; -use miden_objects::testing::constants::{ +use miden_protocol::testing::asset::NonFungibleAssetBuilder; +use miden_protocol::testing::constants::{ CONSUMED_ASSET_1_AMOUNT, CONSUMED_ASSET_3_AMOUNT, FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA, NON_FUNGIBLE_ASSET_DATA_2, }; -use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0}; -use miden_objects::transaction::TransactionScript; -use miden_objects::{EMPTY_WORD, Felt, FieldElement, LexicographicWord, Word, ZERO}; +use miden_protocol::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0}; +use miden_protocol::transaction::TransactionScript; +use miden_protocol::{EMPTY_WORD, Felt, FieldElement, LexicographicWord, Word, ZERO}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; use miden_tx::LocalTransactionProver; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -643,7 +643,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { # move an asset to the created note to partially deplete fungible asset balance swapw dropw push.{REMOVED_ASSET} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note # => [ASSET, note_idx, pad(11)] # clear the stack @@ -660,7 +660,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { let tx_script_src = format!( r#" use mock::account - use miden::output_note + use miden::protocol::output_note const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -1105,7 +1105,7 @@ fn parse_tx_script(code: impl AsRef) -> anyhow::Result { const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " use mock::account - use miden::output_note + use miden::protocol::output_note #! Inputs: [slot_id_prefix, slot_id_suffix, VALUE] #! Outputs: [] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs index b7d1492744..8be831ff05 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs @@ -2,13 +2,12 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use assert_matches::assert_matches; -use miden_lib::note::{NoteConsumptionStatus, WellKnownNote, create_p2id_note, create_p2ide_note}; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::{Account, AccountId}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::crypto::rand::FeltRng; -use miden_objects::note::{ +use miden_processor::ExecutionError; +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::{Account, AccountId}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -18,16 +17,22 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::transaction::{InputNote, OutputNote, TransactionKernel}; -use miden_objects::{Felt, StarkField, Word, ZERO}; -use miden_processor::ExecutionError; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::transaction::{InputNote, OutputNote, TransactionKernel}; +use miden_protocol::{Felt, StarkField, Word, ZERO}; +use miden_standards::note::{ + NoteConsumptionStatus, + WellKnownNote, + create_p2id_note, + create_p2ide_note, +}; +use miden_standards::testing::mock_account::MockAccountExt; +use miden_standards::testing::note::NoteBuilder; use miden_tx::auth::UnreachableAuth; use miden_tx::{ FailedNote, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index 5b48e12b04..b78a247283 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -1,13 +1,11 @@ use alloc::string::String; use anyhow::Context; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::Account; -use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; -use miden_objects::errors::tx_kernel::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; -use miden_objects::note::{ +use miden_protocol::account::Account; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::errors::tx_kernel::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -17,12 +15,14 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::{EMPTY_WORD, Felt, ONE, WORD_SIZE, Word}; +use miden_protocol::{EMPTY_WORD, Felt, ONE, WORD_SIZE, Word}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::mock_account::MockAccountExt; use crate::kernel_tests::tx::ExecutionOutputExt; use crate::utils::create_public_p2any_note; @@ -49,7 +49,7 @@ async fn test_active_note_get_sender_fails_from_tx_script() -> anyhow::Result<() mock_chain.prove_next_block()?; let code = " - use miden::active_note + use miden::protocol::active_note begin # try to get the sender from transaction script @@ -92,7 +92,7 @@ async fn test_active_note_get_metadata() -> anyhow::Result<()> { r#" use $kernel::prologue use $kernel::note->note_internal - use miden::active_note + use miden::protocol::active_note begin exec.prologue::prepare_transaction @@ -137,7 +137,7 @@ async fn test_active_note_get_sender() -> anyhow::Result<()> { let code = " use $kernel::prologue use $kernel::note->note_internal - use miden::active_note + use miden::protocol::active_note begin exec.prologue::prepare_transaction @@ -215,7 +215,7 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { use $kernel::prologue use $kernel::note->note_internal - use miden::active_note + use miden::protocol::active_note proc process_note_0 # drop the note inputs @@ -343,7 +343,7 @@ async fn test_active_note_get_inputs() -> anyhow::Result<()> { " use $kernel::prologue use $kernel::note->note_internal - use miden::active_note + use miden::protocol::active_note begin # => [BH, acct_id, IAH, NC] @@ -385,8 +385,8 @@ async fn test_active_note_get_inputs() -> anyhow::Result<()> { } /// This test checks the scenario when an input note has exactly 8 inputs, and the transaction -/// script attempts to load the inputs to memory using the `miden::active_note::get_inputs` -/// procedure. +/// script attempts to load the inputs to memory using the +/// `miden::protocol::active_note::get_inputs` procedure. /// /// Previously this setup was leading to the incorrect number of note inputs computed during the /// `get_inputs` procedure, see the [issue #1363](https://github.com/0xMiden/miden-base/issues/1363) @@ -442,7 +442,7 @@ async fn test_active_note_get_exactly_8_inputs() -> anyhow::Result<()> { let tx_code = " use $kernel::prologue - use miden::active_note + use miden::protocol::active_note begin exec.prologue::prepare_transaction @@ -485,7 +485,7 @@ async fn test_active_note_get_serial_number() -> anyhow::Result<()> { // calling get_serial_number should return the serial number of the active note let code = " use $kernel::prologue - use miden::active_note + use miden::protocol::active_note begin exec.prologue::prepare_transaction @@ -524,7 +524,7 @@ async fn test_active_note_get_script_root() -> anyhow::Result<()> { // calling get_script_root should return script root of the active note let code = " use $kernel::prologue - use miden::active_note + use miden::protocol::active_note begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset.rs index 4836d6620d..adbacaf792 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset.rs @@ -1,12 +1,12 @@ -use miden_objects::account::AccountId; -use miden_objects::asset::NonFungibleAsset; -use miden_objects::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; -use miden_objects::testing::constants::{ +use miden_protocol::account::AccountId; +use miden_protocol::asset::NonFungibleAsset; +use miden_protocol::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; +use miden_protocol::testing::constants::{ FUNGIBLE_ASSET_AMOUNT, FUNGIBLE_FAUCET_INITIAL_BALANCE, NON_FUNGIBLE_ASSET_DATA, }; -use miden_objects::{Felt, Hasher, Word}; +use miden_protocol::{Felt, Hasher, Word}; use crate::TransactionContextBuilder; use crate::kernel_tests::tx::ExecutionOutputExt; @@ -22,7 +22,7 @@ async fn test_create_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( " use $kernel::prologue - use miden::faucet + use miden::protocol::faucet begin exec.prologue::prepare_transaction @@ -63,7 +63,7 @@ async fn test_create_non_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( " use $kernel::prologue - use miden::faucet + use miden::protocol::faucet begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs index cb6330e6f2..497d083047 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs @@ -1,21 +1,21 @@ use assert_matches::assert_matches; -use miden_objects::account::AccountId; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; -use miden_objects::errors::tx_kernel::{ +use miden_protocol::account::AccountId; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; +use miden_protocol::errors::tx_kernel::{ ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED, ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET, ERR_VAULT_NON_FUNGIBLE_ASSET_ALREADY_EXISTS, ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET_1, }; -use miden_objects::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; -use miden_objects::transaction::memory; -use miden_objects::{AssetVaultError, Felt, ONE, Word, ZERO}; +use miden_protocol::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; +use miden_protocol::transaction::memory; +use miden_protocol::{AssetVaultError, Felt, ONE, Word, ZERO}; use crate::kernel_tests::tx::ExecutionOutputExt; use crate::{TransactionContextBuilder, assert_execution_error}; @@ -29,7 +29,7 @@ async fn get_balance_returns_correct_amount() -> anyhow::Result<()> { let code = format!( r#" use $kernel::prologue - use miden::active_account + use miden::protocol::active_account begin exec.prologue::prepare_transaction @@ -113,7 +113,7 @@ async fn test_get_balance_non_fungible_fails() -> anyhow::Result<()> { let code = format!( " use $kernel::prologue - use miden::active_account + use miden::protocol::active_account begin exec.prologue::prepare_transaction @@ -144,7 +144,7 @@ async fn test_has_non_fungible_asset() -> anyhow::Result<()> { let code = format!( " use $kernel::prologue - use miden::active_account + use miden::protocol::active_account begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs index 6aced0b971..c400161482 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs @@ -1,13 +1,13 @@ use anyhow::Context; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::account_component::{ConditionalAuthComponent, ERR_WRONG_ARGS_MSG}; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::{Account, AccountBuilder}; -use miden_objects::errors::MasmError; -use miden_objects::errors::tx_kernel::ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; -use miden_objects::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; -use miden_objects::{Felt, ONE}; +use miden_protocol::account::{Account, AccountBuilder}; +use miden_protocol::errors::MasmError; +use miden_protocol::errors::tx_kernel::ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; +use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; +use miden_protocol::{Felt, ONE}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::{ConditionalAuthComponent, ERR_WRONG_ARGS_MSG}; +use miden_standards::testing::mock_account::MockAccountExt; use crate::{Auth, TransactionContextBuilder, assert_transaction_executor_error}; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index e94b5c108e..e2563cc412 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -1,34 +1,34 @@ use alloc::string::ToString; use alloc::vec::Vec; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::Word; -use miden_objects::account::{Account, AccountDelta, AccountStorageDelta, AccountVaultDelta}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::errors::tx_kernel::{ +use miden_processor::{Felt, ONE}; +use miden_protocol::Word; +use miden_protocol::account::{Account, AccountDelta, AccountStorageDelta, AccountVaultDelta}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::errors::tx_kernel::{ ERR_ACCOUNT_DELTA_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED, ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY, ERR_EPILOGUE_NONCE_CANNOT_BE_0, ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME, ERR_TX_INVALID_EXPIRATION_DELTA, }; -use miden_objects::note::{NoteTag, NoteType}; -use miden_objects::testing::account_id::{ +use miden_protocol::note::{NoteTag, NoteType}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::storage::MOCK_VALUE_SLOT0; -use miden_objects::transaction::memory::{ +use miden_protocol::testing::storage::MOCK_VALUE_SLOT0; +use miden_protocol::transaction::memory::{ NOTE_MEM_SIZE, OUTPUT_NOTE_ASSET_COMMITMENT_OFFSET, OUTPUT_NOTE_SECTION_OFFSET, }; -use miden_objects::transaction::{OutputNote, OutputNotes, TransactionOutputs}; -use miden_processor::{Felt, ONE}; +use miden_protocol::transaction::{OutputNote, OutputNotes, TransactionOutputs}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::mock_account::MockAccountExt; +use miden_standards::testing::note::NoteBuilder; use super::{ZERO, create_mock_notes_procedure}; use crate::kernel_tests::tx::ExecutionOutputExt; @@ -111,7 +111,7 @@ async fn test_epilogue() -> anyhow::Result<()> { .to_commitment(); let account_update_commitment = - miden_objects::Hasher::merge(&[final_account.commitment(), account_delta_commitment]); + miden_protocol::Hasher::merge(&[final_account.commitment(), account_delta_commitment]); let mut expected_stack = Vec::with_capacity(16); expected_stack.extend(output_notes.commitment().as_elements().iter().rev()); @@ -538,7 +538,7 @@ async fn test_epilogue_empty_transaction_with_empty_output_note() -> anyhow::Res let tx_script_source = format!( r#" use miden::core::word - use miden::output_note + use miden::protocol::output_note use $kernel::prologue use $kernel::epilogue use $kernel::note diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index 505454b242..907a3b58a6 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -1,8 +1,6 @@ use alloc::sync::Arc; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -11,9 +9,9 @@ use miden_objects::account::{ AccountType, StorageMap, }; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::asset::{FungibleAsset, NonFungibleAsset}; -use miden_objects::errors::tx_kernel::{ +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::asset::{FungibleAsset, NonFungibleAsset}; +use miden_protocol::errors::tx_kernel::{ ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT, ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED, ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND, @@ -21,21 +19,23 @@ use miden_objects::errors::tx_kernel::{ ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET_1, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::constants::{ +use miden_protocol::testing::constants::{ CONSUMED_ASSET_1_AMOUNT, FUNGIBLE_ASSET_AMOUNT, FUNGIBLE_FAUCET_INITIAL_BALANCE, NON_FUNGIBLE_ASSET_DATA, NON_FUNGIBLE_ASSET_DATA_2, }; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::{Felt, Word}; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_protocol::{Felt, Word}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::mock_account::MockAccountExt; use crate::utils::create_public_p2any_note; use crate::{TransactionContextBuilder, assert_execution_error, assert_transaction_executor_error}; @@ -51,7 +51,7 @@ async fn test_mint_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" use mock::faucet->mock_faucet - use miden::faucet + use miden::protocol::faucet use $kernel::asset_vault use $kernel::memory use $kernel::prologue @@ -348,7 +348,7 @@ async fn test_burn_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" use mock::faucet->mock_faucet - use miden::faucet + use miden::protocol::faucet use $kernel::asset_vault use $kernel::memory use $kernel::prologue @@ -662,7 +662,7 @@ async fn test_is_non_fungible_asset_issued_succeeds() -> anyhow::Result<()> { let code = format!( r#" use $kernel::prologue - use miden::faucet + use miden::protocol::faucet begin exec.prologue::prepare_transaction @@ -704,7 +704,7 @@ async fn test_get_total_issuance_succeeds() -> anyhow::Result<()> { let code = format!( r#" use $kernel::prologue - use miden::faucet + use miden::protocol::faucet begin exec.prologue::prepare_transaction @@ -737,8 +737,8 @@ fn setup_non_faucet_account() -> anyhow::Result { )) .compile_component_code( "test::non_faucet_component", - "pub use ::miden::faucet::mint - pub use ::miden::faucet::burn", + "pub use ::miden::protocol::faucet::mint + pub use ::miden::protocol::faucet::burn", )?; let faucet_component = AccountComponent::new(faucet_code, vec![])? .with_supported_type(AccountType::RegularAccountUpdatableCode); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs index 53788fdc2c..0a865d1098 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs @@ -1,11 +1,11 @@ use anyhow::Context; use assert_matches::assert_matches; -use miden_objects::account::{AccountId, StorageMap, StorageSlot, StorageSlotName}; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; -use miden_objects::transaction::{ExecutedTransaction, OutputNote}; -use miden_objects::{self, Felt, Word}; +use miden_protocol::account::{AccountId, StorageMap, StorageSlot, StorageSlotName}; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset}; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; +use miden_protocol::transaction::{ExecutedTransaction, OutputNote}; +use miden_protocol::{self, Felt, Word}; use miden_tx::TransactionExecutorError; use winter_rand_utils::rand_value; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 8ea605ce37..42b0cb4098 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -2,9 +2,9 @@ use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_objects::account::{ +use miden_processor::fast::ExecutionOutput; +use miden_processor::{AdviceInputs, Felt}; +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -14,19 +14,19 @@ use miden_objects::account::{ AccountStorageMode, StorageSlot, }; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; -use miden_objects::errors::tx_kernel::{ +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; +use miden_protocol::errors::tx_kernel::{ ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT, ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT, ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, }; -use miden_objects::testing::storage::STORAGE_LEAVES_2; -use miden_objects::transaction::memory::{ +use miden_protocol::testing::storage::STORAGE_LEAVES_2; +use miden_protocol::transaction::memory::{ ACCOUNT_DATA_LENGTH, ACCT_CODE_COMMITMENT_OFFSET, ACCT_ID_AND_NONCE_OFFSET, @@ -38,9 +38,9 @@ use miden_objects::transaction::memory::{ NUM_ACCT_PROCEDURES_OFFSET, NUM_ACCT_STORAGE_SLOTS_OFFSET, }; -use miden_objects::{FieldElement, Word, ZERO}; -use miden_processor::fast::ExecutionOutput; -use miden_processor::{AdviceInputs, Felt}; +use miden_protocol::{FieldElement, Word, ZERO}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; use miden_tx::LocalTransactionProver; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -60,7 +60,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let mock_value_slot0 = AccountStorage::mock_value_slot0(); let mock_map_slot = AccountStorage::mock_map_slot(); let foreign_account_code_source = " - use miden::active_account + use miden::protocol::active_account pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the @@ -125,7 +125,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { use miden::core::sys use $kernel::prologue - use miden::tx + use miden::protocol::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -179,7 +179,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { use miden::core::sys use $kernel::prologue - use miden::tx + use miden::protocol::tx const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -239,7 +239,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { use miden::core::sys use $kernel::prologue - use miden::tx + use miden::protocol::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -316,7 +316,7 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { let mock_value_slot1 = AccountStorage::mock_value_slot1(); let foreign_account_code_source_1 = " - use miden::active_account + use miden::protocol::active_account pub proc get_item_foreign_1 # make this foreign procedure unique to make sure that we invoke the procedure of the @@ -329,7 +329,7 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { end "; let foreign_account_code_source_2 = " - use miden::active_account + use miden::protocol::active_account pub proc get_item_foreign_2 # make this foreign procedure unique to make sure that we invoke the procedure of the @@ -403,7 +403,7 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { use miden::core::sys use $kernel::prologue - use miden::tx + use miden::protocol::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") @@ -535,7 +535,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { let mock_map_slot = AccountStorage::mock_map_slot(); let foreign_account_code_source = " - use miden::active_account + use miden::protocol::active_account pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the @@ -583,7 +583,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { r#" use miden::core::sys - use miden::tx + use miden::protocol::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -686,7 +686,7 @@ async fn foreign_account_can_get_balance_and_presence_of_asset() -> anyhow::Resu let foreign_account_code_source = format!( " - use miden::active_account + use miden::protocol::active_account pub proc get_asset_balance # get balance of first asset @@ -742,7 +742,7 @@ async fn foreign_account_can_get_balance_and_presence_of_asset() -> anyhow::Resu " use miden::core::sys - use miden::tx + use miden::protocol::tx begin # Get the added balance of two assets from foreign account @@ -799,7 +799,7 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { let foreign_account_code_source = format!( " - use miden::active_account + use miden::protocol::active_account pub proc get_initial_balance # push the faucet ID on the stack @@ -847,7 +847,7 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { " use miden::core::sys - use miden::tx + use miden::protocol::tx begin # Get the initial balance of the fungible asset from the foreign account @@ -914,8 +914,8 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { let second_foreign_account_code_source = format!( r#" - use miden::tx - use miden::active_account + use miden::protocol::tx + use miden::protocol::active_account use miden::core::sys @@ -979,8 +979,8 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { // ------ FIRST FOREIGN ACCOUNT --------------------------------------------------------------- let first_foreign_account_code_source = format!( r#" - use miden::tx - use miden::active_account + use miden::protocol::tx + use miden::protocol::active_account use miden::core::sys @@ -1083,7 +1083,7 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { let code = format!( r#" use miden::core::sys - use miden::tx + use miden::protocol::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1173,7 +1173,7 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { // unique procedure which calls the second foreign account via FPI and then returns let first_foreign_account_code_source = format!( r#" - use miden::tx + use miden::protocol::tx use miden::core::sys pub proc first_account_foreign_proc @@ -1241,7 +1241,7 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { let code = format!( r#" use miden::core::sys - use miden::tx + use miden::protocol::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1300,7 +1300,7 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let last_foreign_account_code_source = format!( r#" - use miden::active_account + use miden::protocol::active_account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -1345,7 +1345,7 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let foreign_account_code_source = format!( " - use miden::tx + use miden::protocol::tx use miden::core::sys pub proc read_first_foreign_storage_slot_{foreign_account_index} @@ -1420,7 +1420,7 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { " use miden::core::sys - use miden::tx + use miden::protocol::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1465,7 +1465,7 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { // ------ FIRST FOREIGN ACCOUNT --------------------------------------------------------------- let foreign_account_code_source = " - use miden::tx + use miden::protocol::tx use miden::core::sys @@ -1516,7 +1516,7 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { " use miden::core::sys - use miden::tx + use miden::protocol::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1577,7 +1577,7 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { async fn test_fpi_stale_account() -> anyhow::Result<()> { // Prepare the test data let foreign_account_code_source = " - use miden::native_account + use miden::protocol::native_account # code is not used in this test pub proc set_some_item_foreign @@ -1642,7 +1642,7 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { use miden::core::sys use $kernel::prologue - use miden::tx + use miden::protocol::tx begin exec.prologue::prepare_transaction @@ -1677,8 +1677,8 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { #[tokio::test] async fn test_fpi_get_account_id() -> anyhow::Result<()> { let foreign_account_code_source = " - use miden::active_account - use miden::native_account + use miden::protocol::active_account + use miden::protocol::native_account pub proc get_current_and_native_ids # get the ID of the current (foreign) account @@ -1722,8 +1722,8 @@ async fn test_fpi_get_account_id() -> anyhow::Result<()> { r#" use miden::core::sys - use miden::tx - use miden::account_id + use miden::protocol::tx + use miden::protocol::account_id begin # get the IDs of the foreign and native accounts @@ -1876,7 +1876,7 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - // Create foreign procedures that test get_initial_item and get_initial_map_item let foreign_account_code_source = format!( r#" - use miden::active_account + use miden::protocol::active_account use miden::core::sys const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -1918,7 +1918,7 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - let code = format!( r#" use miden::core::sys - use miden::tx + use miden::protocol::tx const MOCK_MAP_SLOT = word("{mock_map_slot}") diff --git a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs index c04eae61b7..d1c62d88bd 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs @@ -1,8 +1,8 @@ use alloc::string::String; -use miden_lib::code_builder::CodeBuilder; -use miden_objects::Word; -use miden_objects::note::Note; +use miden_protocol::Word; +use miden_protocol::note::Note; +use miden_standards::code_builder::CodeBuilder; use super::{TestSetup, setup_test}; use crate::TxContextInput; @@ -47,7 +47,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { let code = format!( " - use miden::input_note + use miden::protocol::input_note begin {check_note_0} @@ -104,7 +104,7 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { let code = format!( r#" - use miden::input_note + use miden::protocol::input_note begin # get the recipient from the input note @@ -158,7 +158,7 @@ async fn test_get_sender() -> anyhow::Result<()> { let code = format!( r#" - use miden::input_note + use miden::protocol::input_note begin # get the sender from the input note @@ -257,7 +257,7 @@ async fn test_get_assets() -> anyhow::Result<()> { let code = format!( " - use miden::input_note + use miden::protocol::input_note begin {check_note_0} @@ -302,7 +302,7 @@ async fn test_get_inputs_info() -> anyhow::Result<()> { let code = format!( r#" - use miden::input_note + use miden::protocol::input_note begin # get the inputs commitment and length from the input note with index 0 (the only one @@ -352,7 +352,7 @@ async fn test_get_script_root() -> anyhow::Result<()> { let code = format!( r#" - use miden::input_note + use miden::protocol::input_note begin # get the script root from the input note with index 0 (the only one we have) @@ -395,7 +395,7 @@ async fn test_get_serial_number() -> anyhow::Result<()> { let code = format!( r#" - use miden::input_note + use miden::protocol::input_note begin # get the serial number from the input note with index 0 (the only one we have) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index a440d04fb0..4372650953 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -2,18 +2,18 @@ //! //! Once lazy loading is enabled generally, it can be removed and/or integrated into other tests. -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::LexicographicWord; -use miden_objects::account::{AccountId, AccountStorage, StorageSlotDelta}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::testing::account_id::{ +use miden_protocol::LexicographicWord; +use miden_protocol::account::{AccountId, AccountStorage, StorageSlotDelta}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_NATIVE_ASSET_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, }; -use miden_objects::testing::constants::FUNGIBLE_ASSET_AMOUNT; -use miden_objects::testing::storage::MOCK_MAP_SLOT; +use miden_protocol::testing::constants::FUNGIBLE_ASSET_AMOUNT; +use miden_protocol::testing::storage::MOCK_MAP_SLOT; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::note::NoteBuilder; use super::Word; use crate::{Auth, MockChain, TransactionContextBuilder}; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs b/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs index 044cc209d2..fca31cf22a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs @@ -3,8 +3,8 @@ use std::collections::BTreeMap; use std::string::String; use anyhow::Context; -use miden_objects::{EMPTY_WORD, LexicographicWord, Word}; use miden_processor::{ONE, ZERO}; +use miden_protocol::{EMPTY_WORD, LexicographicWord, Word}; use miden_tx::{LinkMap, MemoryViewer}; use rand::seq::IteratorRandom; use winter_rand_utils::rand_value; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 00b5643b12..8bee856271 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -2,18 +2,16 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; use anyhow::Context; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountBuilder, AccountId}; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::diagnostics::miette::{self, miette}; -use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::dsa::falcon512_rpo::SecretKey; -use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; -use miden_objects::errors::MasmError; -use miden_objects::note::{ +use miden_processor::fast::ExecutionOutput; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountBuilder, AccountId}; +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::diagnostics::miette::{self, miette}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::crypto::dsa::falcon512_rpo::SecretKey; +use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::errors::MasmError; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -24,15 +22,17 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::transaction::memory::ACTIVE_INPUT_NOTE_PTR; -use miden_objects::transaction::{OutputNote, TransactionArgs}; -use miden_objects::{Felt, Word, ZERO}; -use miden_processor::fast::ExecutionOutput; +use miden_protocol::transaction::memory::ACTIVE_INPUT_NOTE_PTR; +use miden_protocol::transaction::{OutputNote, TransactionArgs}; +use miden_protocol::{Felt, Word, ZERO}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::note::NoteBuilder; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; @@ -206,7 +206,7 @@ async fn test_build_recipient() -> anyhow::Result<()> { " use miden::core::sys - use miden::note + use miden::protocol::note begin # put the values that will be hashed into the memory @@ -295,7 +295,7 @@ async fn test_compute_inputs_commitment() -> anyhow::Result<()> { " use miden::core::sys - use miden::note + use miden::protocol::note begin # put the values that will be hashed into the memory @@ -434,8 +434,8 @@ pub async fn test_timelock() -> anyhow::Result<()> { let code = format!( r#" - use miden::active_note - use miden::tx + use miden::protocol::active_note + use miden::protocol::tx begin # store the note inputs to memory starting at address 0 @@ -568,7 +568,7 @@ async fn test_build_note_tag_for_network_account() -> anyhow::Result<()> { let code = format!( " use miden::core::sys - use miden::note + use miden::protocol::note begin push.{suffix}.{prefix} diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index 361bca2311..a7c48b76b5 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -2,17 +2,14 @@ use alloc::string::String; use alloc::vec::Vec; use anyhow::Context; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::note::create_p2id_note; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::{Account, AccountId}; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; -use miden_objects::crypto::rand::RpoRandomCoin; -use miden_objects::errors::tx_kernel::{ +use miden_protocol::account::{Account, AccountId}; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset}; +use miden_protocol::crypto::rand::RpoRandomCoin; +use miden_protocol::errors::tx_kernel::{ ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS, ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT, }; -use miden_objects::note::{ +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -23,7 +20,7 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_NETWORK_NON_FUNGIBLE_FAUCET, ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PRIVATE_SENDER, @@ -34,8 +31,8 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::constants::NON_FUNGIBLE_ASSET_DATA_2; -use miden_objects::transaction::memory::{ +use miden_protocol::testing::constants::NON_FUNGIBLE_ASSET_DATA_2; +use miden_protocol::transaction::memory::{ NOTE_MEM_SIZE, NUM_OUTPUT_NOTES_PTR, OUTPUT_NOTE_ASSETS_OFFSET, @@ -43,8 +40,11 @@ use miden_objects::transaction::memory::{ OUTPUT_NOTE_RECIPIENT_OFFSET, OUTPUT_NOTE_SECTION_OFFSET, }; -use miden_objects::transaction::{OutputNote, OutputNotes}; -use miden_objects::{Felt, Word, ZERO}; +use miden_protocol::transaction::{OutputNote, OutputNotes}; +use miden_protocol::{Felt, Word, ZERO}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::note::create_p2id_note; +use miden_standards::testing::mock_account::MockAccountExt; use super::{TestSetup, setup_test}; use crate::kernel_tests::tx::ExecutionOutputExt; @@ -62,7 +62,7 @@ async fn test_create_note() -> anyhow::Result<()> { let code = format!( " - use miden::output_note + use miden::protocol::output_note use $kernel::prologue @@ -143,7 +143,7 @@ async fn test_create_note_with_invalid_tag() -> anyhow::Result<()> { fn note_creation_script(tag: Felt) -> String { format!( " - use miden::output_note + use miden::protocol::output_note use $kernel::prologue begin @@ -174,7 +174,7 @@ async fn test_create_note_too_many_notes() -> anyhow::Result<()> { let code = format!( " - use miden::output_note + use miden::protocol::output_note use $kernel::constants use $kernel::memory use $kernel::prologue @@ -294,8 +294,8 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { " use miden::core::sys - use miden::tx - use miden::output_note + use miden::protocol::tx + use miden::protocol::output_note use $kernel::prologue @@ -397,7 +397,7 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { let code = format!( " - use miden::output_note + use miden::protocol::output_note use $kernel::prologue @@ -466,7 +466,7 @@ async fn test_create_note_and_add_multiple_assets() -> anyhow::Result<()> { let code = format!( " - use miden::output_note + use miden::protocol::output_note use $kernel::prologue begin @@ -548,7 +548,7 @@ async fn test_create_note_and_add_same_nft_twice() -> anyhow::Result<()> { let code = format!( " use $kernel::prologue - use miden::output_note + use miden::protocol::output_note begin exec.prologue::prepare_transaction @@ -639,8 +639,8 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { let recipient = NoteRecipient::new(output_serial_no, input_note_1.script().clone(), inputs); let code = format!( " - use miden::output_note - use miden::note + use miden::protocol::output_note + use miden::protocol::note use $kernel::prologue begin @@ -702,7 +702,7 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { /// This test creates an output note and then adds some assets into it checking the assets info on /// each stage. /// -/// Namely, we invoke the `miden::output_notes::get_assets_info` procedure: +/// Namely, we invoke the `miden::protocol::output_notes::get_assets_info` procedure: /// - After adding the first `asset_0` to the note. /// - Right after the previous check to make sure it returns the same commitment from the cached /// data. @@ -754,7 +754,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { let tx_script_src = &format!( r#" - use miden::output_note + use miden::protocol::output_note use miden::core::sys begin @@ -769,7 +769,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { # move the asset 0 to the note push.{asset_0} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw # => [note_idx] @@ -798,7 +798,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { # add asset_1 to the note push.{asset_1} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw # => [note_idx] @@ -870,7 +870,7 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { let tx_script_src = &format!( r#" - use miden::output_note + use miden::protocol::output_note use miden::core::sys begin @@ -984,7 +984,7 @@ async fn test_get_assets() -> anyhow::Result<()> { let tx_script_src = &format!( " - use miden::output_note + use miden::protocol::output_note use miden::core::sys begin @@ -1055,7 +1055,7 @@ fn create_output_note(note: &Note) -> String { " # move the asset to the note push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw # => [note_idx] ", diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 315565dc8f..d04ba7b65f 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -2,11 +2,9 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; use anyhow::Context; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::{ +use miden_processor::fast::ExecutionOutput; +use miden_processor::{AdviceInputs, Word}; +use miden_protocol::account::{ Account, AccountBuilder, AccountId, @@ -19,18 +17,18 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::asset::{FungibleAsset, NonFungibleAsset}; -use miden_objects::errors::tx_kernel::{ +use miden_protocol::asset::{FungibleAsset, NonFungibleAsset}; +use miden_protocol::errors::tx_kernel::{ ERR_ACCOUNT_SEED_AND_COMMITMENT_DIGEST_MISMATCH, ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY, ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::transaction::memory::{ +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_protocol::transaction::memory::{ ACCT_DB_ROOT_PTR, BLOCK_COMMITMENT_PTR, BLOCK_METADATA_PTR, @@ -80,10 +78,12 @@ use miden_objects::transaction::memory::{ VALIDATOR_KEY_COMMITMENT_PTR, VERIFICATION_BASE_FEE_IDX, }; -use miden_objects::transaction::{ExecutedTransaction, TransactionArgs, TransactionKernel}; -use miden_objects::{EMPTY_WORD, ONE, WORD_SIZE}; -use miden_processor::fast::ExecutionOutput; -use miden_processor::{AdviceInputs, Word}; +use miden_protocol::transaction::{ExecutedTransaction, TransactionArgs, TransactionKernel}; +use miden_protocol::{EMPTY_WORD, ONE, WORD_SIZE}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::TransactionExecutorError; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index dc2753340c..7354f55c90 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -2,14 +2,8 @@ use alloc::sync::Arc; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::AuthScheme; -use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::note::create_p2id_note; -use miden_lib::testing::account_component::IncrNonceAuthComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::{ +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::{ Account, AccountBuilder, AccountCode, @@ -20,11 +14,11 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::diagnostics::NamedSource; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; -use miden_objects::block::BlockNumber; -use miden_objects::note::{ +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::diagnostics::NamedSource; +use miden_protocol::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -37,7 +31,7 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_SENDER, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, @@ -45,9 +39,9 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; -use miden_objects::testing::note::DEFAULT_NOTE_CODE; -use miden_objects::transaction::{ +use miden_protocol::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; +use miden_protocol::testing::note::DEFAULT_NOTE_CODE; +use miden_protocol::transaction::{ InputNotes, OutputNote, OutputNotes, @@ -55,8 +49,14 @@ use miden_objects::transaction::{ TransactionKernel, TransactionSummary, }; -use miden_objects::{Felt, FieldElement, Hasher, ONE, Word}; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::{Felt, FieldElement, Hasher, ONE, Word}; +use miden_standards::AuthScheme; +use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::note::create_p2id_note; +use miden_standards::testing::account_component::IncrNonceAuthComponent; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::auth::UnreachableAuth; use miden_tx::{TransactionExecutor, TransactionExecutorError}; @@ -133,7 +133,7 @@ async fn test_block_procedures() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code = " - use miden::tx + use miden::protocol::tx use $kernel::prologue begin @@ -245,8 +245,8 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let tx_script_src = format!( "\ - use miden::contracts::wallets::basic->wallet - use miden::output_note + use miden::standards::wallets::basic->wallet + use miden::protocol::output_note # Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] # Outputs: [note_idx] @@ -414,8 +414,8 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { #[tokio::test] async fn user_code_can_abort_transaction_with_summary() -> anyhow::Result<()> { let source_code = r#" - use miden::auth - use miden::tx + use miden::standards::auth + use miden::protocol::tx const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized") #! Inputs: [AUTH_ARGS, pad(12)] #! Outputs: [pad(16)] @@ -424,7 +424,7 @@ async fn user_code_can_abort_transaction_with_summary() -> anyhow::Result<()> { # => [pad(16)] push.0.0 exec.tx::get_block_number - exec.::miden::native_account::incr_nonce + exec.::miden::protocol::native_account::incr_nonce # => [[final_nonce, block_num, 0, 0], pad(16)] # => [SALT, pad(16)] diff --git a/crates/miden-testing/src/mock_chain/auth.rs b/crates/miden-testing/src/mock_chain/auth.rs index 2fa5fc0bb1..33b3cd57cc 100644 --- a/crates/miden-testing/src/mock_chain/auth.rs +++ b/crates/miden-testing/src/mock_chain/auth.rs @@ -2,7 +2,11 @@ // ================================================================================================ use alloc::vec::Vec; -use miden_lib::account::auth::{ +use miden_protocol::Word; +use miden_protocol::account::AccountComponent; +use miden_protocol::account::auth::{AuthSecretKey, PublicKeyCommitment}; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_standards::account::auth::{ AuthEcdsaK256Keccak, AuthEcdsaK256KeccakAcl, AuthEcdsaK256KeccakAclConfig, @@ -14,11 +18,10 @@ use miden_lib::account::auth::{ AuthRpoFalcon512Multisig, AuthRpoFalcon512MultisigConfig, }; -use miden_lib::testing::account_component::{ConditionalAuthComponent, IncrNonceAuthComponent}; -use miden_objects::Word; -use miden_objects::account::AccountComponent; -use miden_objects::account::auth::{AuthSecretKey, PublicKeyCommitment}; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; +use miden_standards::testing::account_component::{ + ConditionalAuthComponent, + IncrNonceAuthComponent, +}; use miden_tx::auth::BasicAuthenticator; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index 7245c71d34..5e94bc5df9 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -3,14 +3,15 @@ use alloc::vec::Vec; use anyhow::Context; use miden_block_prover::LocalBlockProver; -use miden_objects::MIN_PROOF_SECURITY_LEVEL; -use miden_objects::account::auth::{AuthSecretKey, PublicKey}; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{Account, AccountId, PartialAccount}; -use miden_objects::batch::{ProposedBatch, ProvenBatch}; -use miden_objects::block::account_tree::{AccountTree, AccountWitness}; -use miden_objects::block::nullifier_tree::{NullifierTree, NullifierWitness}; -use miden_objects::block::{ +use miden_processor::DeserializationError; +use miden_protocol::MIN_PROOF_SECURITY_LEVEL; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey}; +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{Account, AccountId, PartialAccount}; +use miden_protocol::batch::{ProposedBatch, ProvenBatch}; +use miden_protocol::block::account_tree::{AccountTree, AccountWitness}; +use miden_protocol::block::nullifier_tree::{NullifierTree, NullifierWitness}; +use miden_protocol::block::{ BlockHeader, BlockInputs, BlockNumber, @@ -18,9 +19,9 @@ use miden_objects::block::{ ProposedBlock, ProvenBlock, }; -use miden_objects::crypto::dsa::ecdsa_k256_keccak::SecretKey; -use miden_objects::note::{Note, NoteHeader, NoteId, NoteInclusionProof, Nullifier}; -use miden_objects::transaction::{ +use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey; +use miden_protocol::note::{Note, NoteHeader, NoteId, NoteInclusionProof, Nullifier}; +use miden_protocol::transaction::{ ExecutedTransaction, InputNote, InputNotes, @@ -29,7 +30,6 @@ use miden_objects::transaction::{ ProvenTransaction, TransactionInputs, }; -use miden_processor::DeserializationError; use miden_tx::LocalTransactionProver; use miden_tx::auth::BasicAuthenticator; use miden_tx::utils::{ByteReader, Deserializable, Serializable}; @@ -63,7 +63,7 @@ use crate::{MockChainBuilder, TransactionContextBuilder}; /// ## Executing a simple transaction /// ``` /// # use anyhow::Result; -/// # use miden_objects::{ +/// # use miden_protocol::{ /// # asset::{Asset, FungibleAsset}, /// # note::NoteType, /// # }; @@ -127,7 +127,7 @@ use crate::{MockChainBuilder, TransactionContextBuilder}; /// /// ``` /// # use anyhow::Result; -/// # use miden_objects::{Felt, asset::{Asset, FungibleAsset}, note::NoteType}; +/// # use miden_protocol::{Felt, asset::{Asset, FungibleAsset}, note::NoteType}; /// # use miden_testing::{Auth, MockChain, TransactionContextBuilder}; /// # /// # #[tokio::main(flavor = "current_thread")] @@ -1149,15 +1149,15 @@ impl From for TxContextInput { #[cfg(test)] mod tests { - use miden_lib::account::wallets::BasicWallet; - use miden_objects::account::{AccountBuilder, AccountStorageMode}; - use miden_objects::asset::{Asset, FungibleAsset}; - use miden_objects::note::NoteType; - use miden_objects::testing::account_id::{ + use miden_protocol::account::{AccountBuilder, AccountStorageMode}; + use miden_protocol::asset::{Asset, FungibleAsset}; + use miden_protocol::note::NoteType; + use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_SENDER, }; + use miden_standards::account::wallets::BasicWallet; use super::*; use crate::Auth; diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index a668db7dad..34be3a1044 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -13,12 +13,9 @@ const DEFAULT_FAUCET_DECIMALS: u8 = 10; // ================================================================================================ use itertools::Itertools; -use miden_lib::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::note::{create_p2id_note, create_p2ide_note, create_swap_note}; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{ +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{ Account, AccountBuilder, AccountDelta, @@ -28,10 +25,10 @@ use miden_objects::account::{ AccountType, StorageSlot, }; -use miden_objects::asset::{Asset, FungibleAsset, TokenSymbol}; -use miden_objects::block::account_tree::AccountTree; -use miden_objects::block::nullifier_tree::NullifierTree; -use miden_objects::block::{ +use miden_protocol::asset::{Asset, FungibleAsset, TokenSymbol}; +use miden_protocol::block::account_tree::AccountTree; +use miden_protocol::block::nullifier_tree::NullifierTree; +use miden_protocol::block::{ BlockAccountUpdate, BlockBody, BlockHeader, @@ -43,14 +40,17 @@ use miden_objects::block::{ OutputNoteBatch, ProvenBlock, }; -use miden_objects::crypto::dsa::ecdsa_k256_keccak::SecretKey; -use miden_objects::crypto::merkle::smt::Smt; -use miden_objects::note::{Note, NoteDetails, NoteType}; -use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; -use miden_objects::testing::random_signer::RandomBlockSigner; -use miden_objects::transaction::{OrderedTransactionHeaders, OutputNote, TransactionKernel}; -use miden_objects::{Felt, FieldElement, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey; +use miden_protocol::crypto::merkle::smt::Smt; +use miden_protocol::note::{Note, NoteDetails, NoteType}; +use miden_protocol::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; +use miden_protocol::testing::random_signer::RandomBlockSigner; +use miden_protocol::transaction::{OrderedTransactionHeaders, OutputNote, TransactionKernel}; +use miden_protocol::{Felt, FieldElement, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; +use miden_standards::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::note::{create_p2id_note, create_p2ide_note, create_swap_note}; +use miden_standards::testing::account_component::MockAccountComponent; use rand::Rng; use crate::mock_chain::chain::AccountAuthenticator; @@ -63,7 +63,7 @@ use crate::{AccountState, Auth, MockChain}; /// /// ``` /// # use anyhow::Result; -/// # use miden_objects::{ +/// # use miden_protocol::{ /// # asset::{Asset, FungibleAsset}, /// # note::NoteType, /// # }; diff --git a/crates/miden-testing/src/mock_chain/note.rs b/crates/miden-testing/src/mock_chain/note.rs index 799131c6ad..759ef257ea 100644 --- a/crates/miden-testing/src/mock_chain/note.rs +++ b/crates/miden-testing/src/mock_chain/note.rs @@ -1,6 +1,6 @@ -use miden_objects::note::{Note, NoteId, NoteInclusionProof, NoteMetadata}; -use miden_objects::transaction::InputNote; use miden_processor::DeserializationError; +use miden_protocol::note::{Note, NoteId, NoteInclusionProof, NoteMetadata}; +use miden_protocol::transaction::InputNote; use miden_tx::utils::{ByteReader, Deserializable, Serializable}; use winterfell::ByteWriter; diff --git a/crates/miden-testing/src/mock_host.rs b/crates/miden-testing/src/mock_host.rs index 42216aa538..0bcb1d766f 100644 --- a/crates/miden-testing/src/mock_host.rs +++ b/crates/miden-testing/src/mock_host.rs @@ -2,9 +2,6 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::transaction::TransactionEventId; -use miden_objects::vm::EventId; -use miden_objects::{CoreLibrary, Word}; use miden_processor::{ AdviceMutation, AsyncHost, @@ -14,6 +11,9 @@ use miden_processor::{ MastForest, ProcessState, }; +use miden_protocol::transaction::TransactionEventId; +use miden_protocol::vm::EventId; +use miden_protocol::{CoreLibrary, Word}; use miden_tx::TransactionExecutorHost; use miden_tx::auth::UnreachableAuth; @@ -93,10 +93,10 @@ impl<'store> MockHost<'store> { impl<'store> BaseHost for MockHost<'store> { fn get_label_and_source_file( &self, - location: &miden_objects::assembly::debuginfo::Location, + location: &miden_protocol::assembly::debuginfo::Location, ) -> ( - miden_objects::assembly::debuginfo::SourceSpan, - Option>, + miden_protocol::assembly::debuginfo::SourceSpan, + Option>, ) { self.exec_host.get_label_and_source_file(location) } diff --git a/crates/miden-testing/src/tx_context/builder.rs b/crates/miden-testing/src/tx_context/builder.rs index f33a6b9f9e..453bb2c73e 100644 --- a/crates/miden-testing/src/tx_context/builder.rs +++ b/crates/miden-testing/src/tx_context/builder.rs @@ -6,24 +6,24 @@ use alloc::sync::Arc; use alloc::vec::Vec; use anyhow::Context; -use miden_lib::testing::account_component::IncrNonceAuthComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::EMPTY_WORD; -use miden_objects::account::auth::{PublicKeyCommitment, Signature}; -use miden_objects::account::{Account, AccountHeader, AccountId}; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::debuginfo::SourceManagerSync; -use miden_objects::block::account_tree::AccountWitness; -use miden_objects::note::{Note, NoteId, NoteScript}; -use miden_objects::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::transaction::{ +use miden_processor::{AdviceInputs, Felt, Word}; +use miden_protocol::EMPTY_WORD; +use miden_protocol::account::auth::{PublicKeyCommitment, Signature}; +use miden_protocol::account::{Account, AccountHeader, AccountId}; +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::debuginfo::SourceManagerSync; +use miden_protocol::block::account_tree::AccountWitness; +use miden_protocol::note::{Note, NoteId, NoteScript}; +use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_protocol::transaction::{ OutputNote, TransactionArgs, TransactionInputs, TransactionScript, }; -use miden_processor::{AdviceInputs, Felt, Word}; +use miden_standards::testing::account_component::IncrNonceAuthComponent; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::TransactionMastStore; use miden_tx::auth::BasicAuthenticator; @@ -43,8 +43,8 @@ use crate::{MockChain, MockChainNote}; /// ``` /// # use anyhow::Result; /// # use miden_testing::TransactionContextBuilder; -/// # use miden_objects::{account::AccountBuilder,Felt, FieldElement}; -/// # use miden_objects::transaction::TransactionKernel; +/// # use miden_protocol::{account::AccountBuilder,Felt, FieldElement}; +/// # use miden_protocol::transaction::TransactionKernel; /// # /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<()> { @@ -109,11 +109,11 @@ impl TransactionContextBuilder { /// /// The wallet: /// - /// - Includes a series of mocked assets ([miden_objects::asset::AssetVault::mock()]). + /// - Includes a series of mocked assets ([miden_protocol::asset::AssetVault::mock()]). /// - Has a nonce of `1` (so it does not imply seed validation). /// - Has an ID of [`ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE`]. /// - Has an account code based on an - /// [miden_lib::testing::account_component::MockAccountComponent]. + /// [miden_standards::testing::account_component::MockAccountComponent]. pub fn with_existing_mock_account() -> Self { Self::new(Account::mock( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index 7a6de221dc..274b16672b 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -3,21 +3,22 @@ use alloc::collections::{BTreeMap, BTreeSet}; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::code_builder::CodeBuilder; -use miden_objects::account::{ +use miden_processor::fast::ExecutionOutput; +use miden_processor::{ExecutionError, FutureMaybeSend, MastForest, MastForestStore, Word}; +use miden_protocol::account::{ Account, AccountId, PartialAccount, StorageMapWitness, StorageSlotContent, }; -use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; -use miden_objects::assembly::{Assembler, SourceManager, SourceManagerSync}; -use miden_objects::asset::{Asset, AssetVaultKey, AssetWitness}; -use miden_objects::block::account_tree::AccountWitness; -use miden_objects::block::{BlockHeader, BlockNumber}; -use miden_objects::note::{Note, NoteScript}; -use miden_objects::transaction::{ +use miden_protocol::assembly::debuginfo::{SourceLanguage, Uri}; +use miden_protocol::assembly::{Assembler, SourceManager, SourceManagerSync}; +use miden_protocol::asset::{Asset, AssetVaultKey, AssetWitness}; +use miden_protocol::block::account_tree::AccountWitness; +use miden_protocol::block::{BlockHeader, BlockNumber}; +use miden_protocol::note::{Note, NoteScript}; +use miden_protocol::transaction::{ AccountInputs, ExecutedTransaction, InputNote, @@ -27,8 +28,7 @@ use miden_objects::transaction::{ TransactionInputs, TransactionKernel, }; -use miden_processor::fast::ExecutionOutput; -use miden_processor::{ExecutionError, FutureMaybeSend, MastForest, MastForestStore, Word}; +use miden_standards::code_builder::CodeBuilder; use miden_tx::auth::{BasicAuthenticator, UnreachableAuth}; use miden_tx::{ AccountProcedureIndexMap, @@ -73,8 +73,8 @@ impl TransactionContext { /// by the transaction kernel, and also individual kernel functions (not normally exposed). /// /// To improve the error message quality, convert the returned [`ExecutionError`] into a - /// [`Report`](miden_objects::assembly::diagnostics::Report) or use `?` with - /// [`miden_objects::assembly::diagnostics::Result`]. + /// [`Report`](miden_protocol::assembly::diagnostics::Report) or use `?` with + /// [`miden_protocol::assembly::diagnostics::Result`]. /// /// # Errors /// @@ -401,9 +401,9 @@ impl MastForestStore for TransactionContext { #[cfg(test)] mod tests { - use miden_objects::Felt; - use miden_objects::assembly::Assembler; - use miden_objects::note::NoteScript; + use miden_protocol::Felt; + use miden_protocol::assembly::Assembler; + use miden_protocol::note::NoteScript; use super::*; use crate::TransactionContextBuilder; diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index f070d199f1..11007500a9 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -1,15 +1,15 @@ use alloc::string::String; use alloc::vec::Vec; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::AccountId; -use miden_objects::asset::Asset; -use miden_objects::crypto::rand::FeltRng; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::storage::prepare_assets; use miden_processor::Felt; use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::AccountId; +use miden_protocol::asset::Asset; +use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::storage::prepare_assets; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::note::NoteBuilder; use rand::SeedableRng; use rand::rngs::SmallRng; @@ -130,8 +130,8 @@ pub fn create_p2any_note( let code = format!( " use mock::account - use miden::active_note - use miden::contracts::wallets::basic->wallet + use miden::protocol::active_note + use miden::standards::wallets::basic->wallet begin # fetch pointer & number of assets @@ -199,7 +199,7 @@ fn note_script_that_creates_notes<'note>( sender_id: AccountId, output_notes: impl Iterator, ) -> anyhow::Result { - let mut out = String::from("use miden::output_note\n\nbegin\n"); + let mut out = String::from("use miden::protocol::output_note\n\nbegin\n"); for (idx, note) in output_notes.into_iter().enumerate() { anyhow::ensure!( @@ -209,7 +209,7 @@ fn note_script_that_creates_notes<'note>( // Make sure that the transaction's native account matches the note sender. out.push_str(&format!( - r#"exec.::miden::native_account::get_id + r#"exec.::miden::protocol::native_account::get_id # => [native_account_id_prefix, native_account_id_suffix] push.{sender_prefix} assert_eq.err="sender ID prefix does not match native account ID's prefix" # => [native_account_id_suffix] @@ -244,7 +244,7 @@ fn note_script_that_creates_notes<'note>( for asset in assets_str { out.push_str(&format!( " push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note\n", + call.::miden::standards::wallets::basic::move_asset_to_note\n", )); } } diff --git a/crates/miden-testing/tests/auth/ecdsa_acl.rs b/crates/miden-testing/tests/auth/ecdsa_acl.rs index ef09d5e008..9bdaf80ebc 100644 --- a/crates/miden-testing/tests/auth/ecdsa_acl.rs +++ b/crates/miden-testing/tests/auth/ecdsa_acl.rs @@ -1,11 +1,7 @@ use core::slice; use assert_matches::assert_matches; -use miden_lib::account::auth::AuthEcdsaK256KeccakAcl; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -13,10 +9,14 @@ use miden_objects::account::{ AccountStorageMode, AccountType, }; -use miden_objects::note::Note; -use miden_objects::testing::storage::MOCK_VALUE_SLOT0; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::note::Note; +use miden_protocol::testing::storage::MOCK_VALUE_SLOT0; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, FieldElement, Word}; +use miden_standards::account::auth::AuthEcdsaK256KeccakAcl; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain}; use miden_tx::TransactionExecutorError; diff --git a/crates/miden-testing/tests/auth/ecdsa_multisig.rs b/crates/miden-testing/tests/auth/ecdsa_multisig.rs index 515c5e490b..c2ff6fad66 100644 --- a/crates/miden-testing/tests/auth/ecdsa_multisig.rs +++ b/crates/miden-testing/tests/auth/ecdsa_multisig.rs @@ -1,24 +1,30 @@ -use miden_lib::account::auth::AuthEcdsaK256KeccakMultisig; -use miden_lib::account::components::ecdsa_k256_keccak_multisig_library; -use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::errors::standards::ERR_TX_ALREADY_EXECUTED; -use miden_lib::note::create_p2id_note; -use miden_lib::testing::account_interface::get_public_keys_from_account; -use miden_objects::account::auth::{AuthSecretKey, PublicKey}; -use miden_objects::account::{Account, AccountBuilder, AccountId, AccountStorageMode, AccountType}; -use miden_objects::asset::FungibleAsset; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::{ +use miden_processor::AdviceInputs; +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey}; +use miden_protocol::account::{ + Account, + AccountBuilder, + AccountId, + AccountStorageMode, + AccountType, +}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, }; -use miden_objects::transaction::OutputNote; -use miden_objects::vm::AdviceMap; -use miden_objects::{Felt, Hasher, Word}; -use miden_processor::AdviceInputs; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::transaction::OutputNote; +use miden_protocol::vm::AdviceMap; +use miden_protocol::{Felt, Hasher, Word}; +use miden_standards::account::auth::AuthEcdsaK256KeccakMultisig; +use miden_standards::account::components::ecdsa_k256_keccak_multisig_library; +use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::errors::standards::ERR_TX_ALREADY_EXECUTED; +use miden_standards::note::create_p2id_note; +use miden_standards::testing::account_interface::get_public_keys_from_account; use miden_testing::utils::create_spawn_note; use miden_testing::{Auth, MockChainBuilder, assert_transaction_executor_error}; use miden_tx::TransactionExecutorError; diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index 7bc1daec97..2c32f08211 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -1,24 +1,30 @@ -use miden_lib::account::auth::AuthRpoFalcon512Multisig; -use miden_lib::account::components::rpo_falcon_512_multisig_library; -use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::errors::standards::ERR_TX_ALREADY_EXECUTED; -use miden_lib::note::create_p2id_note; -use miden_lib::testing::account_interface::get_public_keys_from_account; -use miden_objects::account::auth::{AuthSecretKey, PublicKey}; -use miden_objects::account::{Account, AccountBuilder, AccountId, AccountStorageMode, AccountType}; -use miden_objects::asset::FungibleAsset; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::{ +use miden_processor::AdviceInputs; +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey}; +use miden_protocol::account::{ + Account, + AccountBuilder, + AccountId, + AccountStorageMode, + AccountType, +}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, }; -use miden_objects::transaction::OutputNote; -use miden_objects::vm::AdviceMap; -use miden_objects::{Felt, Hasher, Word}; -use miden_processor::AdviceInputs; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::transaction::OutputNote; +use miden_protocol::vm::AdviceMap; +use miden_protocol::{Felt, Hasher, Word}; +use miden_standards::account::auth::AuthRpoFalcon512Multisig; +use miden_standards::account::components::rpo_falcon_512_multisig_library; +use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::errors::standards::ERR_TX_ALREADY_EXECUTED; +use miden_standards::note::create_p2id_note; +use miden_standards::testing::account_interface::get_public_keys_from_account; use miden_testing::utils::create_spawn_note; use miden_testing::{Auth, MockChainBuilder, assert_transaction_executor_error}; use miden_tx::TransactionExecutorError; diff --git a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs index e1550256b2..91519a07bb 100644 --- a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs +++ b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs @@ -2,11 +2,7 @@ use core::slice; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::account::auth::AuthRpoFalcon512Acl; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -14,10 +10,14 @@ use miden_objects::account::{ AccountStorageMode, AccountType, }; -use miden_objects::note::Note; -use miden_objects::testing::storage::MOCK_VALUE_SLOT0; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::note::Note; +use miden_protocol::testing::storage::MOCK_VALUE_SLOT0; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, FieldElement, Word}; +use miden_standards::account::auth::AuthRpoFalcon512Acl; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain}; use miden_tx::TransactionExecutorError; diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index 3d7cd2f6cc..37339b3b25 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -4,15 +4,15 @@ mod auth; mod scripts; mod wallet; -use miden_lib::code_builder::CodeBuilder; -use miden_objects::account::AccountId; -use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::utils::Serializable; -use miden_objects::note::{Note, NoteAssets, NoteInputs, NoteMetadata, NoteRecipient, NoteType}; -use miden_objects::testing::account_id::ACCOUNT_ID_SENDER; -use miden_objects::transaction::{ExecutedTransaction, ProvenTransaction}; -use miden_objects::{Word, ZERO}; use miden_processor::utils::Deserializable; +use miden_protocol::account::AccountId; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::crypto::utils::Serializable; +use miden_protocol::note::{Note, NoteAssets, NoteInputs, NoteMetadata, NoteRecipient, NoteType}; +use miden_protocol::testing::account_id::ACCOUNT_ID_SENDER; +use miden_protocol::transaction::{ExecutedTransaction, ProvenTransaction}; +use miden_protocol::{Word, ZERO}; +use miden_standards::code_builder::CodeBuilder; use miden_tx::{ LocalTransactionProver, ProvingOptions, @@ -27,7 +27,7 @@ use miden_tx::{ pub fn prove_and_verify_transaction( executed_transaction: ExecutedTransaction, ) -> Result<(), TransactionVerifierError> { - use miden_objects::transaction::TransactionHeader; + use miden_protocol::transaction::TransactionHeader; let executed_transaction_id = executed_transaction.id(); let executed_tx_header = TransactionHeader::from(&executed_transaction); @@ -46,7 +46,7 @@ pub fn prove_and_verify_transaction( let proven_transaction = ProvenTransaction::read_from_bytes(&serialised_transaction).unwrap(); // Verify that the generated proof is valid - let verifier = TransactionVerifier::new(miden_objects::MIN_PROOF_SECURITY_LEVEL); + let verifier = TransactionVerifier::new(miden_protocol::MIN_PROOF_SECURITY_LEVEL); verifier.verify(&proven_transaction) } @@ -56,7 +56,7 @@ pub fn get_note_with_fungible_asset_and_script( fungible_asset: FungibleAsset, note_script: &str, ) -> Note { - use miden_objects::note::NoteExecutionHint; + use miden_protocol::note::NoteExecutionHint; let note_script = CodeBuilder::default().compile_note_script(note_script).unwrap(); let serial_num = Word::from([1, 2, 3, 4u32]); diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 1c2b8d379b..38b378a903 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -3,21 +3,17 @@ extern crate alloc; use alloc::sync::Arc; use core::slice; -use miden_lib::account::faucets::{BasicFungibleFaucet, FungibleFaucetExt, NetworkFungibleFaucet}; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::errors::standards::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; -use miden_lib::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::{ +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::{ Account, AccountId, AccountIdVersion, AccountStorageMode, AccountType, }; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::{ +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -29,10 +25,18 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::ACCOUNT_ID_PRIVATE_SENDER; -use miden_objects::transaction::{ExecutedTransaction, OutputNote}; -use miden_objects::{Felt, Word}; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::testing::account_id::ACCOUNT_ID_PRIVATE_SENDER; +use miden_protocol::transaction::{ExecutedTransaction, OutputNote}; +use miden_protocol::{Felt, Word}; +use miden_standards::account::faucets::{ + BasicFungibleFaucet, + FungibleFaucetExt, + NetworkFungibleFaucet, +}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::errors::standards::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; +use miden_standards::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; +use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain, assert_transaction_executor_error}; use crate::scripts::swap::create_p2id_note_exact; @@ -67,7 +71,7 @@ pub fn create_mint_script_code(params: &FaucetTestParams) -> String { push.{amount} # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] - call.::miden::contracts::faucets::basic_fungible::distribute + call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] # truncate the stack @@ -186,7 +190,7 @@ async fn faucet_contract_mint_fungible_asset_fails_exceeds_max_supply() -> anyho push.{amount} # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] - call.::miden::contracts::faucets::basic_fungible::distribute + call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] # truncate the stack @@ -263,7 +267,7 @@ async fn prove_burning_fungible_asset_on_existing_faucet_succeeds() -> anyhow::R dropw # => [] - call.::miden::contracts::faucets::basic_fungible::burn + call.::miden::standards::faucets::basic_fungible::burn # => [ASSET] # truncate the stack @@ -358,7 +362,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let trigger_note_script_code = format!( " - use miden::note + use miden::protocol::note begin # Build recipient hash from SERIAL_NUM, SCRIPT_ROOT, and INPUTS_COMMITMENT @@ -391,7 +395,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul push.{amount} # => [amount, tag, aux, note_type, execution_hint, RECIPIENT] - call.::miden::contracts::faucets::basic_fungible::distribute + call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] # Truncate the stack diff --git a/crates/miden-testing/tests/scripts/fee.rs b/crates/miden-testing/tests/scripts/fee.rs index 70622e4819..3fd41d7b78 100644 --- a/crates/miden-testing/tests/scripts/fee.rs +++ b/crates/miden-testing/tests/scripts/fee.rs @@ -1,6 +1,6 @@ use anyhow::Context; -use miden_objects::asset::FungibleAsset; -use miden_objects::{self, Felt, Word}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::{self, Felt, Word}; use miden_testing::{Auth, MockChain}; use crate::prove_and_verify_transaction; diff --git a/crates/miden-testing/tests/scripts/p2id.rs b/crates/miden-testing/tests/scripts/p2id.rs index 6c5fde6410..870bd7a193 100644 --- a/crates/miden-testing/tests/scripts/p2id.rs +++ b/crates/miden-testing/tests/scripts/p2id.rs @@ -1,19 +1,19 @@ -use miden_lib::code_builder::CodeBuilder; -use miden_lib::errors::standards::ERR_P2ID_TARGET_ACCT_MISMATCH; -use miden_lib::note::create_p2id_note; -use miden_objects::account::Account; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset}; -use miden_objects::crypto::rand::RpoRandomCoin; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::{ +use miden_protocol::account::Account; +use miden_protocol::asset::{Asset, AssetVault, FungibleAsset}; +use miden_protocol::crypto::rand::RpoRandomCoin; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, ACCOUNT_ID_SENDER, }; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, Word}; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, Word}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::errors::standards::ERR_P2ID_TARGET_ACCT_MISMATCH; +use miden_standards::note::create_p2id_note; use miden_testing::{Auth, MockChain, assert_transaction_executor_error}; use crate::prove_and_verify_transaction; @@ -222,7 +222,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { let tx_script_src = &format!( " - use miden::output_note + use miden::protocol::output_note begin push.{recipient_1} push.{note_execution_hint_1} @@ -232,7 +232,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { call.output_note::create push.{asset_1} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw dropw dropw dropw push.{recipient_2} @@ -243,7 +243,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { call.output_note::create push.{asset_2} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw dropw dropw dropw end ", diff --git a/crates/miden-testing/tests/scripts/p2ide.rs b/crates/miden-testing/tests/scripts/p2ide.rs index 29c982a02b..a7ac7aa0a1 100644 --- a/crates/miden-testing/tests/scripts/p2ide.rs +++ b/crates/miden-testing/tests/scripts/p2ide.rs @@ -1,17 +1,17 @@ use core::slice; use anyhow::Context; -use miden_lib::errors::standards::{ +use miden_protocol::Felt; +use miden_protocol::account::Account; +use miden_protocol::asset::{Asset, AssetVault, FungibleAsset}; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{Note, NoteType}; +use miden_standards::errors::standards::{ ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER, ERR_P2IDE_RECLAIM_DISABLED, ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED, ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED, }; -use miden_objects::Felt; -use miden_objects::account::Account; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset}; -use miden_objects::block::BlockNumber; -use miden_objects::note::{Note, NoteType}; use miden_testing::{Auth, MockChain, assert_transaction_executor_error}; /// Test that the P2IDE note works like a regular P2ID note diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index daf92deb88..083993039d 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -1,12 +1,10 @@ use core::slice; use std::collections::BTreeMap; -use miden_lib::account::interface::{AccountInterface, AccountInterfaceExt}; -use miden_lib::code_builder::CodeBuilder; -use miden_objects::Word; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; -use miden_objects::note::{ +use miden_protocol::Word; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -17,13 +15,15 @@ use miden_objects::note::{ NoteType, PartialNote, }; -use miden_objects::transaction::OutputNote; +use miden_protocol::transaction::OutputNote; +use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::code_builder::CodeBuilder; use miden_testing::{Auth, MockChain}; /// Tests the execution of the generated send_note transaction script in case the sending account /// has the [`BasicWallet`][wallet] interface. /// -/// [wallet]: miden_lib::account::interface::AccountComponentInterface::BasicWallet +/// [wallet]: miden_standards::account::interface::AccountComponentInterface::BasicWallet #[tokio::test] async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let sent_asset = FungibleAsset::mock(10); @@ -84,7 +84,7 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { /// Tests the execution of the generated send_note transaction script in case the sending account /// has the [`BasicFungibleFaucet`][faucet] interface. /// -/// [faucet]: miden_lib::account::interface::AccountComponentInterface::BasicFungibleFaucet +/// [faucet]: miden_standards::account::interface::AccountComponentInterface::BasicFungibleFaucet #[tokio::test] async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { let mut builder = MockChain::builder(); diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index 486c3cff17..bce9193a6c 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -1,9 +1,7 @@ use anyhow::Context; -use miden_lib::code_builder::CodeBuilder; -use miden_lib::note::utils; -use miden_objects::account::{Account, AccountId, AccountStorageMode, AccountType}; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; -use miden_objects::note::{ +use miden_protocol::account::{Account, AccountId, AccountStorageMode, AccountType}; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset}; +use miden_protocol::note::{ Note, NoteAssets, NoteDetails, @@ -12,13 +10,15 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, AccountIdBuilder, }; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, NoteError, Word}; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, NoteError, Word}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::note::utils; use miden_testing::{Auth, MockChain}; use crate::prove_and_verify_transaction; @@ -40,7 +40,7 @@ pub async fn prove_send_swap_note() -> anyhow::Result<()> { let tx_script_src = &format!( " - use miden::output_note + use miden::protocol::output_note begin push.{recipient} push.{note_execution_hint} @@ -50,7 +50,7 @@ pub async fn prove_send_swap_note() -> anyhow::Result<()> { call.output_note::create push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw dropw dropw dropw end ", diff --git a/crates/miden-testing/tests/wallet/mod.rs b/crates/miden-testing/tests/wallet/mod.rs index fe50b9a24d..28ba42f95c 100644 --- a/crates/miden-testing/tests/wallet/mod.rs +++ b/crates/miden-testing/tests/wallet/mod.rs @@ -1,16 +1,16 @@ -use miden_lib::AuthScheme; -use miden_lib::account::wallets::create_basic_wallet; -use miden_objects::Word; -use miden_objects::account::auth::AuthSecretKey; +use miden_protocol::Word; +use miden_protocol::account::auth::AuthSecretKey; +use miden_standards::AuthScheme; +use miden_standards::account::wallets::create_basic_wallet; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; #[cfg(not(target_arch = "wasm32"))] #[test] fn wallet_creation() { - use miden_lib::account::auth::AuthRpoFalcon512; - use miden_lib::account::wallets::BasicWallet; - use miden_objects::account::{AccountCode, AccountStorageMode, AccountType}; + use miden_protocol::account::{AccountCode, AccountStorageMode, AccountType}; + use miden_standards::account::auth::AuthRpoFalcon512; + use miden_standards::account::wallets::BasicWallet; // we need a Falcon Public Key to create the wallet account let seed = [0_u8; 32]; @@ -49,9 +49,9 @@ fn wallet_creation() { #[cfg(not(target_arch = "wasm32"))] #[test] fn wallet_creation_2() { - use miden_lib::account::auth::AuthEcdsaK256Keccak; - use miden_lib::account::wallets::BasicWallet; - use miden_objects::account::{AccountCode, AccountStorageMode, AccountType}; + use miden_protocol::account::{AccountCode, AccountStorageMode, AccountType}; + use miden_standards::account::auth::AuthEcdsaK256Keccak; + use miden_standards::account::wallets::BasicWallet; // we need a ECDSA Public Key to create the wallet account let seed = [0_u8; 32]; diff --git a/crates/miden-tx-batch-prover/Cargo.toml b/crates/miden-tx-batch-prover/Cargo.toml index 6b214a120b..d664da1d37 100644 --- a/crates/miden-tx-batch-prover/Cargo.toml +++ b/crates/miden-tx-batch-prover/Cargo.toml @@ -17,9 +17,9 @@ bench = false [features] default = ["std"] -std = ["miden-objects/std", "miden-tx/std"] +std = ["miden-protocol/std", "miden-tx/std"] testing = [] [dependencies] -miden-objects = { workspace = true } -miden-tx = { workspace = true } +miden-protocol = { workspace = true } +miden-tx = { workspace = true } diff --git a/crates/miden-tx-batch-prover/src/local_batch_prover.rs b/crates/miden-tx-batch-prover/src/local_batch_prover.rs index 3636f61ac5..1c790a92ee 100644 --- a/crates/miden-tx-batch-prover/src/local_batch_prover.rs +++ b/crates/miden-tx-batch-prover/src/local_batch_prover.rs @@ -1,7 +1,7 @@ use alloc::boxed::Box; -use miden_objects::ProvenBatchError; -use miden_objects::batch::{ProposedBatch, ProvenBatch}; +use miden_protocol::ProvenBatchError; +use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_tx::TransactionVerifier; // LOCAL BATCH PROVER diff --git a/crates/miden-tx/Cargo.toml b/crates/miden-tx/Cargo.toml index 56187d7570..e78138760f 100644 --- a/crates/miden-tx/Cargo.toml +++ b/crates/miden-tx/Cargo.toml @@ -15,13 +15,13 @@ version.workspace = true [features] concurrent = ["miden-prover/concurrent", "std"] default = ["std"] -std = ["miden-lib/std", "miden-objects/std", "miden-processor/std", "miden-prover/std", "miden-verifier/std"] -testing = ["miden-lib/testing", "miden-objects/testing", "miden-processor/testing"] +std = ["miden-processor/std", "miden-protocol/std", "miden-prover/std", "miden-standards/std", "miden-verifier/std"] +testing = ["miden-processor/testing", "miden-protocol/testing", "miden-standards/testing"] [dependencies] # Workspace dependencies -miden-lib = { workspace = true } -miden-objects = { workspace = true } +miden-protocol = { workspace = true } +miden-standards = { workspace = true } # Miden dependencies miden-processor = { workspace = true } diff --git a/crates/miden-tx/src/auth/tx_authenticator.rs b/crates/miden-tx/src/auth/tx_authenticator.rs index 82eaa4d245..4845421445 100644 --- a/crates/miden-tx/src/auth/tx_authenticator.rs +++ b/crates/miden-tx/src/auth/tx_authenticator.rs @@ -3,11 +3,11 @@ use alloc::collections::BTreeMap; use alloc::string::ToString; use alloc::vec::Vec; -use miden_objects::account::auth::{AuthSecretKey, PublicKey, PublicKeyCommitment, Signature}; -use miden_objects::crypto::SequentialCommit; -use miden_objects::transaction::TransactionSummary; -use miden_objects::{Felt, Hasher, Word}; use miden_processor::FutureMaybeSend; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey, PublicKeyCommitment, Signature}; +use miden_protocol::crypto::SequentialCommit; +use miden_protocol::transaction::TransactionSummary; +use miden_protocol::{Felt, Hasher, Word}; use crate::errors::AuthenticationError; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -276,9 +276,9 @@ impl TransactionAuthenticator for () { #[cfg(test)] mod test { - use miden_objects::account::auth::AuthSecretKey; - use miden_objects::utils::{Deserializable, Serializable}; - use miden_objects::{Felt, Word}; + use miden_protocol::account::auth::AuthSecretKey; + use miden_protocol::utils::{Deserializable, Serializable}; + use miden_protocol::{Felt, Word}; use super::SigningInputs; diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 5cf283c035..5be724e9a2 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -3,15 +3,16 @@ use alloc::string::String; use alloc::vec::Vec; use core::error::Error; -use miden_objects::account::AccountId; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic; -use miden_objects::asset::AssetVaultKey; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::smt::SmtProofError; -use miden_objects::note::{NoteId, NoteMetadata}; -use miden_objects::transaction::TransactionSummary; -use miden_objects::{ +use miden_processor::{DeserializationError, ExecutionError}; +use miden_protocol::account::AccountId; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::assembly::diagnostics::reporting::PrintDiagnostic; +use miden_protocol::asset::AssetVaultKey; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::smt::SmtProofError; +use miden_protocol::note::{NoteId, NoteMetadata}; +use miden_protocol::transaction::TransactionSummary; +use miden_protocol::{ AccountDeltaError, AccountError, AssetError, @@ -22,7 +23,6 @@ use miden_objects::{ TransactionOutputError, Word, }; -use miden_processor::{DeserializationError, ExecutionError}; use miden_verifier::VerificationError; use thiserror::Error; diff --git a/crates/miden-tx/src/executor/data_store.rs b/crates/miden-tx/src/executor/data_store.rs index 19fe34ba10..050aeb636d 100644 --- a/crates/miden-tx/src/executor/data_store.rs +++ b/crates/miden-tx/src/executor/data_store.rs @@ -1,12 +1,12 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_objects::account::{AccountId, PartialAccount, StorageMapWitness}; -use miden_objects::asset::{AssetVaultKey, AssetWitness}; -use miden_objects::block::{BlockHeader, BlockNumber}; -use miden_objects::note::NoteScript; -use miden_objects::transaction::{AccountInputs, PartialBlockchain}; use miden_processor::{FutureMaybeSend, MastForestStore, Word}; +use miden_protocol::account::{AccountId, PartialAccount, StorageMapWitness}; +use miden_protocol::asset::{AssetVaultKey, AssetWitness}; +use miden_protocol::block::{BlockHeader, BlockNumber}; +use miden_protocol::note::NoteScript; +use miden_protocol::transaction::{AccountInputs, PartialBlockchain}; use crate::DataStoreError; diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index edc677edbf..d2af2b6974 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -3,23 +3,6 @@ use alloc::collections::{BTreeMap, BTreeSet}; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountCode, AccountDelta, AccountId, PartialAccount}; -use miden_objects::assembly::debuginfo::Location; -use miden_objects::assembly::{SourceFile, SourceManagerSync, SourceSpan}; -use miden_objects::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::smt::SmtProof; -use miden_objects::note::{NoteInputs, NoteMetadata, NoteRecipient}; -use miden_objects::transaction::{ - InputNote, - InputNotes, - OutputNote, - TransactionAdviceInputs, - TransactionSummary, -}; -use miden_objects::vm::AdviceMap; -use miden_objects::{Felt, Hasher, Word}; use miden_processor::{ AdviceMutation, AsyncHost, @@ -29,6 +12,23 @@ use miden_processor::{ MastForest, ProcessState, }; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountCode, AccountDelta, AccountId, PartialAccount}; +use miden_protocol::assembly::debuginfo::Location; +use miden_protocol::assembly::{SourceFile, SourceManagerSync, SourceSpan}; +use miden_protocol::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::smt::SmtProof; +use miden_protocol::note::{NoteInputs, NoteMetadata, NoteRecipient}; +use miden_protocol::transaction::{ + InputNote, + InputNotes, + OutputNote, + TransactionAdviceInputs, + TransactionSummary, +}; +use miden_protocol::vm::AdviceMap; +use miden_protocol::{Felt, Hasher, Word}; use crate::auth::{SigningInputs, TransactionAuthenticator}; use crate::errors::TransactionKernelError; diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index d3a2ad371c..105dbe7ff2 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -1,12 +1,15 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; -use miden_objects::account::AccountId; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::debuginfo::SourceManagerSync; -use miden_objects::asset::{Asset, AssetVaultKey}; -use miden_objects::block::BlockNumber; -use miden_objects::transaction::{ +use miden_processor::fast::FastProcessor; +use miden_processor::{AdviceInputs, ExecutionError, StackInputs}; +pub use miden_processor::{ExecutionOptions, MastForestStore}; +use miden_protocol::account::AccountId; +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::debuginfo::SourceManagerSync; +use miden_protocol::asset::{Asset, AssetVaultKey}; +use miden_protocol::block::BlockNumber; +use miden_protocol::transaction::{ ExecutedTransaction, InputNote, InputNotes, @@ -15,11 +18,8 @@ use miden_objects::transaction::{ TransactionKernel, TransactionScript, }; -use miden_objects::vm::StackOutputs; -use miden_objects::{Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES}; -use miden_processor::fast::FastProcessor; -use miden_processor::{AdviceInputs, ExecutionError, StackInputs}; -pub use miden_processor::{ExecutionOptions, MastForestStore}; +use miden_protocol::vm::StackOutputs; +use miden_protocol::{Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES}; use super::TransactionExecutorError; use crate::auth::TransactionAuthenticator; @@ -101,7 +101,7 @@ where /// /// The `source_manager` is used to map potential errors back to their source code. To get the /// most value out of it, use the same source manager as was used with the - /// [`Assembler`](miden_objects::assembly::Assembler) that assembled the Miden Assembly code + /// [`Assembler`](miden_protocol::assembly::Assembler) that assembled the Miden Assembly code /// that should be debugged, e.g. account components, note scripts or transaction scripts. /// /// This will overwrite any previously set source manager. diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index 6ea8ca0f05..9741e79824 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -1,19 +1,19 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; -use miden_lib::note::{NoteConsumptionStatus, WellKnownNote}; -use miden_objects::account::AccountId; -use miden_objects::block::BlockNumber; -use miden_objects::note::Note; -use miden_objects::transaction::{ +use miden_processor::fast::FastProcessor; +use miden_protocol::account::AccountId; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::Note; +use miden_protocol::transaction::{ InputNote, InputNotes, TransactionArgs, TransactionInputs, TransactionKernel, }; -use miden_processor::fast::FastProcessor; use miden_prover::AdviceInputs; +use miden_standards::note::{NoteConsumptionStatus, WellKnownNote}; use super::TransactionExecutor; use crate::auth::TransactionAuthenticator; diff --git a/crates/miden-tx/src/host/account_delta_tracker.rs b/crates/miden-tx/src/host/account_delta_tracker.rs index 279d806f05..f62e7996e8 100644 --- a/crates/miden-tx/src/host/account_delta_tracker.rs +++ b/crates/miden-tx/src/host/account_delta_tracker.rs @@ -1,11 +1,11 @@ -use miden_objects::account::{ +use miden_protocol::account::{ AccountCode, AccountDelta, AccountId, AccountVaultDelta, PartialAccount, }; -use miden_objects::{Felt, FieldElement, ZERO}; +use miden_protocol::{Felt, FieldElement, ZERO}; use crate::host::storage_delta_tracker::StorageDeltaTracker; diff --git a/crates/miden-tx/src/host/account_procedures.rs b/crates/miden-tx/src/host/account_procedures.rs index bbe747b481..0a74d232e6 100644 --- a/crates/miden-tx/src/host/account_procedures.rs +++ b/crates/miden-tx/src/host/account_procedures.rs @@ -1,4 +1,4 @@ -use miden_objects::account::AccountCode; +use miden_protocol::account::AccountCode; use super::{BTreeMap, Word}; use crate::errors::TransactionKernelError; diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 34abee00e6..50dbb83840 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -1,6 +1,7 @@ -use miden_objects::account::{AccountId, StorageSlotId, StorageSlotType}; -use miden_objects::note::{NoteId, NoteInputs}; -use miden_objects::transaction::memory::{ +use miden_processor::{ExecutionError, Felt, ProcessState}; +use miden_protocol::account::{AccountId, StorageSlotId, StorageSlotType}; +use miden_protocol::note::{NoteId, NoteInputs}; +use miden_protocol::transaction::memory::{ ACCOUNT_STACK_TOP_PTR, ACCT_CODE_COMMITMENT_OFFSET, ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET, @@ -10,8 +11,7 @@ use miden_objects::transaction::memory::{ ACTIVE_INPUT_NOTE_PTR, NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, }; -use miden_objects::{Hasher, Word}; -use miden_processor::{ExecutionError, Felt, ProcessState}; +use miden_protocol::{Hasher, Word}; use crate::errors::TransactionKernelError; diff --git a/crates/miden-tx/src/host/link_map.rs b/crates/miden-tx/src/host/link_map.rs index 5a6f15638c..5024c86dfd 100644 --- a/crates/miden-tx/src/host/link_map.rs +++ b/crates/miden-tx/src/host/link_map.rs @@ -1,9 +1,9 @@ use alloc::vec::Vec; use core::cmp::Ordering; -use miden_objects::{Felt, LexicographicWord, Word, ZERO}; use miden_processor::fast::ExecutionOutput; use miden_processor::{AdviceMutation, ContextId, ProcessState}; +use miden_protocol::{Felt, LexicographicWord, Word, ZERO}; // LINK MAP // ================================================================================================ @@ -316,7 +316,7 @@ impl<'mem> MemoryViewer<'mem> { // https://github.com/0xMiden/miden-vm/issues/2237 // Copy of how Memory::read_element is implemented in Miden VM. - let idx = addr % miden_objects::WORD_SIZE as u32; + let idx = addr % miden_protocol::WORD_SIZE as u32; let word_addr = addr - idx; Some(self.get_kernel_mem_word(word_addr)?[idx as usize]) diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index 0396f0af57..56d3940848 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -10,8 +10,8 @@ mod account_procedures; pub use account_procedures::AccountProcedureIndexMap; pub(crate) mod note_builder; -use miden_objects::CoreLibrary; -use miden_objects::vm::EventId; +use miden_protocol::CoreLibrary; +use miden_protocol::vm::EventId; use note_builder::OutputNoteBuilder; mod kernel_process; @@ -28,8 +28,17 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{ +use miden_processor::{ + AdviceMutation, + EventError, + EventHandlerRegistry, + Felt, + MastForest, + MastForestStore, + ProcessState, +}; +use miden_protocol::Word; +use miden_protocol::account::{ AccountCode, AccountDelta, AccountHeader, @@ -40,9 +49,9 @@ use miden_objects::account::{ StorageSlotId, StorageSlotName, }; -use miden_objects::asset::Asset; -use miden_objects::note::{NoteId, NoteMetadata, NoteRecipient}; -use miden_objects::transaction::{ +use miden_protocol::asset::Asset; +use miden_protocol::note::{NoteId, NoteMetadata, NoteRecipient}; +use miden_protocol::transaction::{ InputNote, InputNotes, OutputNote, @@ -50,16 +59,7 @@ use miden_objects::transaction::{ TransactionMeasurements, TransactionSummary, }; -use miden_objects::vm::RowIndex; -use miden_processor::{ - AdviceMutation, - EventError, - EventHandlerRegistry, - Felt, - MastForest, - MastForestStore, - ProcessState, -}; +use miden_protocol::vm::RowIndex; pub(crate) use tx_event::{RecipientData, TransactionEvent, TransactionProgressEvent}; pub use tx_progress::TransactionProgress; diff --git a/crates/miden-tx/src/host/note_builder.rs b/crates/miden-tx/src/host/note_builder.rs index e3642fd217..9e392c54ef 100644 --- a/crates/miden-tx/src/host/note_builder.rs +++ b/crates/miden-tx/src/host/note_builder.rs @@ -1,5 +1,5 @@ -use miden_objects::asset::Asset; -use miden_objects::note::{Note, NoteAssets, NoteMetadata, NoteRecipient, PartialNote}; +use miden_protocol::asset::Asset; +use miden_protocol::note::{Note, NoteAssets, NoteMetadata, NoteRecipient, PartialNote}; use super::{OutputNote, Word}; use crate::errors::TransactionKernelError; diff --git a/crates/miden-tx/src/host/script_mast_forest_store.rs b/crates/miden-tx/src/host/script_mast_forest_store.rs index 071f84208b..6a8e82054d 100644 --- a/crates/miden-tx/src/host/script_mast_forest_store.rs +++ b/crates/miden-tx/src/host/script_mast_forest_store.rs @@ -1,12 +1,12 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; -use miden_objects::Word; -use miden_objects::assembly::mast::MastForest; -use miden_objects::note::NoteScript; -use miden_objects::transaction::TransactionScript; -use miden_objects::vm::AdviceMap; use miden_processor::MastForestStore; +use miden_protocol::Word; +use miden_protocol::assembly::mast::MastForest; +use miden_protocol::note::NoteScript; +use miden_protocol::transaction::TransactionScript; +use miden_protocol::vm::AdviceMap; /// Stores the MAST forests for a set of scripts (both note scripts and transaction scripts). /// diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index 0baef19a3b..6270612130 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -1,8 +1,8 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{ +use miden_protocol::Word; +use miden_protocol::account::{ AccountStorageDelta, AccountStorageHeader, PartialAccount, diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index c5c046cc60..ab16e8eff6 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -1,12 +1,12 @@ use alloc::vec::Vec; -use miden_objects::account::{AccountId, StorageMap, StorageSlotName, StorageSlotType}; -use miden_objects::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; -use miden_objects::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; -use miden_objects::transaction::{TransactionEventId, TransactionSummary}; -use miden_objects::vm::EventId; -use miden_objects::{Felt, Hasher, Word}; use miden_processor::{AdviceMutation, ProcessState, RowIndex}; +use miden_protocol::account::{AccountId, StorageMap, StorageSlotName, StorageSlotType}; +use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; +use miden_protocol::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; +use miden_protocol::transaction::{TransactionEventId, TransactionSummary}; +use miden_protocol::vm::EventId; +use miden_protocol::{Felt, Hasher, Word}; use crate::host::{TransactionBaseHost, TransactionKernelProcess}; use crate::{LinkMap, TransactionKernelError}; diff --git a/crates/miden-tx/src/lib.rs b/crates/miden-tx/src/lib.rs index 8b3f225574..a756df72b7 100644 --- a/crates/miden-tx/src/lib.rs +++ b/crates/miden-tx/src/lib.rs @@ -48,4 +48,4 @@ pub mod auth; // RE-EXPORTS // ================================================================================================ -pub use miden_objects::utils; +pub use miden_protocol::utils; diff --git a/crates/miden-tx/src/prover/mast_store.rs b/crates/miden-tx/src/prover/mast_store.rs index c6d9b8163a..e92984cc66 100644 --- a/crates/miden-tx/src/prover/mast_store.rs +++ b/crates/miden-tx/src/prover/mast_store.rs @@ -1,13 +1,13 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; -use miden_lib::StandardsLib; -use miden_objects::account::AccountCode; -use miden_objects::assembly::mast::MastForest; -use miden_objects::transaction::TransactionKernel; -use miden_objects::utils::sync::RwLock; -use miden_objects::{CoreLibrary, ProtocolLib, Word}; use miden_processor::MastForestStore; +use miden_protocol::account::AccountCode; +use miden_protocol::assembly::mast::MastForest; +use miden_protocol::transaction::TransactionKernel; +use miden_protocol::utils::sync::RwLock; +use miden_protocol::{CoreLibrary, ProtocolLib, Word}; +use miden_standards::StandardsLib; // TRANSACTION MAST STORE // ================================================================================================ @@ -28,9 +28,10 @@ impl TransactionMastStore { /// Returns a new [TransactionMastStore] instantiated with the default libraries. /// /// The default libraries include: - /// - Miden core library (miden-core-lib). - /// - Miden protocol library (miden-lib). - /// - Transaction kernel. + /// - Miden core library [`CoreLibrary`]. + /// - Miden protocol library [`ProtocolLib`]. + /// - Miden standards library [`StandardsLib`]. + /// - Transaction kernel [`TransactionKernel::kernel`]. pub fn new() -> Self { let mast_forests = RwLock::new(BTreeMap::new()); let store = Self { mast_forests }; diff --git a/crates/miden-tx/src/prover/mod.rs b/crates/miden-tx/src/prover/mod.rs index 8cd1599eaf..60d13b4c52 100644 --- a/crates/miden-tx/src/prover/mod.rs +++ b/crates/miden-tx/src/prover/mod.rs @@ -1,11 +1,11 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{AccountDelta, PartialAccount}; -use miden_objects::asset::Asset; -use miden_objects::block::BlockNumber; -use miden_objects::transaction::{ +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{AccountDelta, PartialAccount}; +use miden_protocol::asset::Asset; +use miden_protocol::block::BlockNumber; +use miden_protocol::transaction::{ InputNote, InputNotes, OutputNote, @@ -168,7 +168,7 @@ impl Default for LocalTransactionProver { impl LocalTransactionProver { pub fn prove_dummy( &self, - executed_transaction: miden_objects::transaction::ExecutedTransaction, + executed_transaction: miden_protocol::transaction::ExecutedTransaction, ) -> Result { let (tx_inputs, tx_outputs, account_delta, _) = executed_transaction.into_parts(); diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index 7bf33dfa03..5cdc3a9edc 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -1,11 +1,6 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{AccountDelta, PartialAccount}; -use miden_objects::assembly::debuginfo::Location; -use miden_objects::assembly::{SourceFile, SourceSpan}; -use miden_objects::transaction::{InputNote, InputNotes, OutputNote}; use miden_processor::{ AdviceMutation, BaseHost, @@ -15,6 +10,11 @@ use miden_processor::{ ProcessState, SyncHost, }; +use miden_protocol::Word; +use miden_protocol::account::{AccountDelta, PartialAccount}; +use miden_protocol::assembly::debuginfo::Location; +use miden_protocol::assembly::{SourceFile, SourceSpan}; +use miden_protocol::transaction::{InputNote, InputNotes, OutputNote}; use crate::host::{RecipientData, ScriptMastForestStore, TransactionBaseHost, TransactionEvent}; use crate::{AccountProcedureIndexMap, TransactionKernelError}; diff --git a/crates/miden-tx/src/verifier/mod.rs b/crates/miden-tx/src/verifier/mod.rs index 9880283704..9855151dec 100644 --- a/crates/miden-tx/src/verifier/mod.rs +++ b/crates/miden-tx/src/verifier/mod.rs @@ -1,6 +1,6 @@ -use miden_objects::CoreLibrary; -use miden_objects::transaction::{ProvenTransaction, TransactionKernel}; -use miden_objects::vm::ProgramInfo; +use miden_protocol::CoreLibrary; +use miden_protocol::transaction::{ProvenTransaction, TransactionKernel}; +use miden_protocol::vm::ProgramInfo; use miden_verifier::verify_with_precompiles; use super::TransactionVerifierError; diff --git a/docs/src/account/components.md b/docs/src/account/components.md index e7963d5f91..aad4412298 100644 --- a/docs/src/account/components.md +++ b/docs/src/account/components.md @@ -187,8 +187,7 @@ Multi-slot values are currently unsupported by component schemas. #### Providing init values -When a storage entry requires init-supplied values, an implementation must provide their concrete values -at instantiation time. This is done through `InitStorageData` (available as `miden_objects::account::component::InitStorageData`), which can be created programmatically or loaded from TOML using `InitStorageData::from_toml()`. +When a storage entry requires init-supplied values, an implementation must provide their concrete values at instantiation time. This is done through `InitStorageData` (available as `miden_protocol::account::component::InitStorageData`), which can be created programmatically or loaded from TOML using `InitStorageData::from_toml()`. For example, the init-populated map entry above can be populated from TOML as follows: diff --git a/docs/src/account/storage.md b/docs/src/account/storage.md index f3e9ad45d0..622a8f54bd 100644 --- a/docs/src/account/storage.md +++ b/docs/src/account/storage.md @@ -9,7 +9,7 @@ title: "Storage" A flexible, arbitrary data store within the `Account`. ::: -The [storage](https://docs.rs/miden-objects/latest/miden_objects/account/struct.AccountStorage.html) consists of up to 256 individual [storage slots](https://docs.rs/miden-objects/latest/miden_objects/account/enum.StorageSlot.html), where each slot consists of: +The [storage](https://docs.rs/miden-protocol/latest/miden_protocol/account/struct.AccountStorage.html) consists of up to 256 individual [storage slots](https://docs.rs/miden-protocol/latest/miden_protocol/account/enum.StorageSlot.html), where each slot consists of: - Key: Slot name. - Value: either a single [`Word`](#storage-units) or a key-value map of [`Word`](#storage-units)s. diff --git a/docs/src/note.md b/docs/src/note.md index 785133e78e..2137a7db1b 100644 --- a/docs/src/note.md +++ b/docs/src/note.md @@ -121,7 +121,7 @@ Only those who know the RECIPIENT’s pre-image can consume the `Note`. For priv The [transaction prologue](transaction) requires all necessary data to compute the `Note` hash. This setup allows scenario-specific restrictions on who may consume a `Note`. -For a practical example, refer to the [SWAP note script](https://github.com/0xMiden/miden-base/blob/next/crates/miden-lib/asm/note_scripts/SWAP.masm), where the RECIPIENT ensures that only a defined target can consume the swapped asset. +For a practical example, refer to the [SWAP note script](https://github.com/0xMiden/miden-base/blob/next/crates/miden-standards/asm/note_scripts/SWAP.masm), where the RECIPIENT ensures that only a defined target can consume the swapped asset. #### Note nullifier ensuring private consumption diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index a3037cd63c..6f2f9ef0cf 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -27,7 +27,7 @@ Most procedures in the Miden protocol library are implemented as wrappers around The procedures maintain the same security and context restrictions as the underlying kernel procedures. When invoking these procedures, ensure that the calling context matches the requirements. -## Active account Procedures (`miden::active_account`) +## Active account Procedures (`miden::protocol::active_account`) Active account procedures can be used to read from storage, fetch or compute commitments or obtain other internal data of the active account. @@ -53,7 +53,7 @@ Active account procedures can be used to read from storage, fetch or compute com | `get_procedure_root` | Returns the procedure root for the procedure at the specified index.

**Inputs:** `[index]`
**Outputs:** `[PROC_ROOT]` | Any | | `has_procedure` | Returns the binary flag indicating whether the procedure with the provided root is available on the active account.

**Inputs:** `[PROC_ROOT]`
**Outputs:** `[is_procedure_available]` | Any | -## Native account Procedures (`miden::native_account`) +## Native account Procedures (`miden::protocol::native_account`) Native account procedures can be used to write to storage, add or remove assets from the vault and compute delta commitment of the native account. @@ -68,7 +68,7 @@ Native account procedures can be used to write to storage, add or remove assets | `remove_asset` | Removes the specified asset from the vault.

**Inputs:** `[ASSET]`
**Outputs:** `[ASSET]` | Native & Account | | `was_procedure_called` | Returns 1 if a native account procedure was called during transaction execution, and 0 otherwise.

**Inputs:** `[PROC_ROOT]`
**Outputs:** `[was_called]` | Any | -## Active Note Procedures (`miden::active_note`) +## Active Note Procedures (`miden::protocol::active_note`) Active note procedures can be used to fetch data from the note that is currently being processed by the transaction kernel. @@ -82,7 +82,7 @@ Active note procedures can be used to fetch data from the note that is currently | `get_serial_number` | Returns the [serial number](note.md#serial-number) of the active note.

**Inputs:** `[]`
**Outputs:** `[SERIAL_NUMBER]` | Note | | `get_script_root` | Returns the [script root](note.md#script) of the active note.

**Inputs:** `[]`
**Outputs:** `[SCRIPT_ROOT]` | Note | -## Input Note Procedures (`miden::input_note`) +## Input Note Procedures (`miden::protocol::input_note`) Input note procedures can be used to fetch data on input notes consumed by the transaction. @@ -97,7 +97,7 @@ Input note procedures can be used to fetch data on input notes consumed by the t | `get_script_root` | Returns the [script root](note.md#script) of the input note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[SCRIPT_ROOT]` | Any | | `get_serial_number` | Returns the [serial number](note.md#serial-number) of the input note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[SERIAL_NUMBER]` | Any | -## Output Note Procedures (`miden::output_note`) +## Output Note Procedures (`miden::protocol::output_note`) Output note procedures can be used to fetch data on output notes created by the transaction. @@ -110,7 +110,7 @@ Output note procedures can be used to fetch data on output notes created by the | `get_recipient` | Returns the [recipient](note#note-recipient-restricting-consumption) of the output note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[RECIPIENT]` | Any | | `get_metadata` | Returns the [metadata](note#metadata) of the output note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[METADATA]` | Any | -## Note Utility Procedures (`miden::note`) +## Note Utility Procedures (`miden::protocol::note`) Note utility procedures can be used to compute the required utility data or write note data to memory. @@ -123,7 +123,7 @@ Note utility procedures can be used to compute the required utility data or writ | `build_recipient` | Builds the recipient hash from note inputs, script root, and serial number.

**Inputs:** `[inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT]`
**Outputs:** `[RECIPIENT]` | Any | | `extract_sender_from_metadata` | Extracts the sender ID from the provided metadata word.

**Inputs:** `[METADATA]`
**Outputs:** `[sender_id_prefix, sender_id_suffix]` | Any | -## Transaction Procedures (`miden::tx`) +## Transaction Procedures (`miden::protocol::tx`) Transaction procedures manage transaction-level operations including note creation, context switching, and reading transaction metadata. @@ -140,7 +140,7 @@ Transaction procedures manage transaction-level operations including note creati | `get_expiration_block_delta` | Returns the transaction expiration delta, or 0 if not set.

**Inputs:** `[]`
**Outputs:** `[block_height_delta]` | Any | | `update_expiration_block_delta` | Updates the transaction expiration delta.

**Inputs:** `[block_height_delta]`
**Outputs:** `[]` | Any | -## Faucet Procedures (`miden::faucet`) +## Faucet Procedures (`miden::protocol::faucet`) Faucet procedures allow reading and writing to faucet accounts to mint and burn assets. @@ -153,7 +153,7 @@ Faucet procedures allow reading and writing to faucet accounts to mint and burn | `get_total_issuance` | Returns the total issuance of the fungible faucet the transaction is being executed against.

**Inputs:** `[]`
**Outputs:** `[total_issuance]` | Faucet | | `is_non_fungible_asset_issued` | Returns a boolean indicating whether the provided non-fungible asset has been already issued by this faucet.

**Inputs:** `[ASSET]`
**Outputs:** `[is_issued]` | Faucet | -## Asset Procedures (`miden::asset`) +## Asset Procedures (`miden::protocol::asset`) Asset procedures provide utilities for creating fungible and non-fungible assets. diff --git a/docs/src/transaction.md b/docs/src/transaction.md index 6ac6aba0bc..feffa9b98c 100644 --- a/docs/src/transaction.md +++ b/docs/src/transaction.md @@ -66,9 +66,9 @@ To illustrate the `Transaction` protocol, we provide two examples for a basic `T Let's assume account A wants to create a P2ID note. P2ID notes are pay-to-ID notes that can only be consumed by a specified target account ID. Note creators can provide the target account ID using the [note inputs](note#inputs). -In this example, account A uses the basic wallet and the authentication component provided by `miden-lib`. The basic wallet component defines the methods `wallets::basic::create_note` and `wallets::basic::move_asset_to_note` to create notes with assets, and `wallets::basic::receive_asset` to receive assets. The authentication component exposes `auth::basic::auth_tx_rpo_falcon512` which allows for signing a transaction. Some account methods like `active_account::get_id` are always exposed. +In this example, account A uses the basic wallet and the authentication component provided by `miden-standards`. The basic wallet component defines the methods `wallets::basic::create_note` and `wallets::basic::move_asset_to_note` to create notes with assets, and `wallets::basic::receive_asset` to receive assets. The authentication component exposes `auth::basic::auth_tx_rpo_falcon512` which allows for signing a transaction. Some account methods like `active_account::get_id` are always exposed. -The executor inputs to the Miden VM a `Transaction` script in which it places on the stack the data (tag, aux, note_type, execution_hint, RECIPIENT) of the note(s) that it wants to create using `wallets::basic::create_note` during the said `Transaction`. The [`NoteRecipient`](https://github.com/0xMiden/miden-base/blob/main/crates/miden-objects/src/note/recipient.rs) is a value that describes under which condition a note can be consumed and is built using a `serial_number`, the `note_script` (in this case P2ID script) and the `note_inputs`. The Miden VM will execute the `Transaction` script and create the note(s). After having been created, the executor can use `wallets::basic::move_asset_to_note` to move assets from the account's vault to the notes vault. +The executor inputs to the Miden VM a `Transaction` script in which it places on the stack the data (tag, aux, note_type, execution_hint, RECIPIENT) of the note(s) that it wants to create using `wallets::basic::create_note` during the said `Transaction`. The [`NoteRecipient`](https://github.com/0xMiden/miden-base/blob/main/crates/miden-protocol/src/note/recipient.rs) is a value that describes under which condition a note can be consumed and is built using a `serial_number`, the `note_script` (in this case P2ID script) and the `note_inputs`. The Miden VM will execute the `Transaction` script and create the note(s). After having been created, the executor can use `wallets::basic::move_asset_to_note` to move assets from the account's vault to the notes vault. After finalizing the `Transaction` the updated state and created note(s) can now be submitted to the Miden operator to be recorded on-chain. diff --git a/scripts/check-features.sh b/scripts/check-features.sh index 1aa931c832..852c7c2863 100755 --- a/scripts/check-features.sh +++ b/scripts/check-features.sh @@ -10,12 +10,12 @@ echo "Checking all feature combinations with cargo-hack..." # Set environment variables to treat warnings as errors export RUSTFLAGS="-D warnings" -# Enable file generation in the `src` directory for miden-lib build scripts +# Enable file generation in the `src` directory for miden-protocol and miden-standards build scripts export BUILD_GENERATED_FILES_IN_SRC=1 # Run cargo-hack with comprehensive feature checking # Focus on library packages that have significant feature matrices -for package in miden-objects miden-lib miden-tx miden-testing miden-block-prover miden-tx-batch-prover; do +for package in miden-protocol miden-standards miden-tx miden-testing miden-block-prover miden-tx-batch-prover; do echo "Checking package: $package" cargo hack check -p "$package" --each-feature --all-targets done From a3f196c31c474e51a86d3f31d80475a05b86a42d Mon Sep 17 00:00:00 2001 From: riemann Date: Tue, 23 Dec 2025 11:11:14 -0500 Subject: [PATCH 075/114] feat: merge next into agglayer branch & create agglayer crate --- CHANGELOG.md | 2 + Cargo.lock | 417 ++++---- Cargo.toml | 32 +- Makefile | 2 +- README.md | 4 +- bin/bench-note-checker/Cargo.toml | 8 +- bin/bench-note-checker/src/lib.rs | 12 +- bin/bench-transaction/Cargo.toml | 8 +- bin/bench-transaction/src/context_setups.rs | 18 +- .../src/cycle_counting_benchmarks/utils.rs | 2 +- bin/bench-transaction/src/main.rs | 2 +- .../src/time_counting_benchmarks/prove.rs | 2 +- crates/miden-agglayer/Cargo.toml | 43 + .../account_components/asset_conversion.masm | 106 ++ .../asm/account_components/bridge_out.masm | 162 +++ .../account_components/local_exit_tree.masm | 120 +++ .../asm/note_scripts/B2AGG.masm | 88 ++ crates/miden-agglayer/build.rs | 477 +++++++++ crates/miden-agglayer/src/errors/agglayer.rs | 18 + crates/miden-agglayer/src/errors/mod.rs | 2 + crates/miden-agglayer/src/lib.rs | 111 ++ crates/miden-agglayer/src/mod.rs | 121 +++ crates/miden-agglayer/src/utils.rs | 175 ++++ crates/miden-block-prover/Cargo.toml | 4 +- .../src/local_block_prover.rs | 4 +- .../asm/miden/contracts/wallets/basic.masm | 61 -- crates/miden-lib/build.rs | 961 ------------------ crates/miden-lib/src/block/mod.rs | 73 -- crates/miden-lib/src/errors/mod.rs | 16 - .../src/errors/transaction_errors.rs | 26 - crates/miden-lib/src/lib.rs | 97 -- crates/miden-lib/src/transaction/outputs.rs | 65 -- crates/miden-lib/src/utils/mod.rs | 6 - crates/miden-protocol-macros/Cargo.toml | 2 +- .../tests/integration_test.rs | 2 +- .../Cargo.toml | 16 +- .../README.md | 4 +- .../asm/kernels/transaction/api.masm | 150 +-- .../asm/kernels/transaction/lib/account.masm | 249 ++--- .../transaction/lib/account_delta.masm | 66 +- .../asm/kernels/transaction/lib/asset.masm | 35 +- .../kernels/transaction/lib/asset_vault.masm | 57 +- .../kernels/transaction/lib/constants.masm | 42 +- .../asm/kernels/transaction/lib/epilogue.masm | 53 +- .../asm/kernels/transaction/lib/faucet.masm | 36 +- .../kernels/transaction/lib/input_note.masm | 10 +- .../asm/kernels/transaction/lib/link_map.masm | 106 +- .../asm/kernels/transaction/lib/memory.masm | 463 +++++---- .../asm/kernels/transaction/lib/note.masm | 28 +- .../kernels/transaction/lib/output_note.masm | 62 +- .../asm/kernels/transaction/lib/prologue.masm | 117 +-- .../asm/kernels/transaction/lib/tx.masm | 28 +- .../asm/kernels/transaction/main.masm | 33 +- .../kernels/transaction/tx_script_main.masm | 10 +- .../asm/protocol}/active_account.masm | 40 +- .../asm/protocol}/active_note.masm | 84 +- .../asm/protocol}/asset.masm | 14 +- .../asm/protocol}/faucet.masm | 18 +- .../asm/protocol}/input_note.masm | 20 +- .../asm/protocol}/kernel_proc_offsets.masm | 208 ++-- .../asm/protocol}/native_account.masm | 18 +- .../asm/protocol}/note.masm | 38 +- .../asm/protocol}/output_note.masm | 16 +- .../asm/protocol}/tx.masm | 23 +- .../asm/shared_modules/account_id.masm | 48 +- .../asm/shared_utils/util/asset.masm | 6 +- .../asm/shared_utils/util/note.masm | 4 +- .../benches/account_seed.rs | 6 +- crates/miden-protocol/build.rs | 817 +++++++++++++++ .../masm_doc_comment_fmt.md | 0 .../src/account/account_id/account_type.rs | 0 .../src/account/account_id/id_prefix.rs | 0 .../src/account/account_id/id_version.rs | 0 .../src/account/account_id/mod.rs | 0 .../src/account/account_id/seed.rs | 0 .../src/account/account_id/storage_mode.rs | 0 .../src/account/account_id/v0/mod.rs | 0 .../src/account/account_id/v0/prefix.rs | 0 .../src/account/auth.rs | 51 +- .../src/account/builder/mod.rs | 8 +- .../src/account/code/header.rs | 0 .../src/account/code/mod.rs | 6 +- .../src/account/code/procedure.rs | 0 .../src/account/component/code.rs | 0 .../src/account/component/metadata/mod.rs | 4 +- .../src/account/component/mod.rs | 24 +- .../component/storage/init_storage_data.rs | 0 .../src/account/component/storage/mod.rs | 0 .../src/account/component/storage/schema.rs | 0 .../storage/toml/init_storage_data.rs | 0 .../src/account/component/storage/toml/mod.rs | 0 .../component/storage/toml/serde_impls.rs | 0 .../account/component/storage/toml/tests.rs | 10 +- .../component/storage/type_registry.rs | 10 +- .../account/component/storage/value_name.rs | 0 .../src/account/delta/mod.rs | 0 .../src/account/delta/storage.rs | 0 .../src/account/delta/vault.rs | 0 .../src/account/file.rs | 4 +- .../src/account/header.rs | 48 +- .../src/account/mod.rs | 2 +- .../src/account/partial.rs | 0 .../src/account/storage/header.rs | 0 .../src/account/storage/map/mod.rs | 3 +- .../src/account/storage/map/partial.rs | 11 +- .../src/account/storage/map/witness.rs | 9 +- .../src/account/storage/mod.rs | 3 +- .../src/account/storage/partial.rs | 3 +- .../src/account/storage/slot/mod.rs | 0 .../src/account/storage/slot/slot_content.rs | 0 .../src/account/storage/slot/slot_id.rs | 0 .../src/account/storage/slot/slot_name.rs | 0 .../src/account/storage/slot/storage_slot.rs | 0 .../src/account/storage/slot/type.rs | 0 .../src/address/address_id.rs | 0 .../src/address/interface.rs | 0 .../src/address/mod.rs | 4 +- .../src/address/network_id.rs | 0 .../src/address/routing_parameters.rs | 10 +- .../src/address/type.rs | 0 .../src/asset/fungible.rs | 0 .../src/asset/mod.rs | 0 .../src/asset/nonfungible.rs | 0 .../src/asset/token_symbol.rs | 2 +- .../src/asset/vault/asset_witness.rs | 5 +- .../src/asset/vault/mod.rs | 2 +- .../src/asset/vault/partial.rs | 5 +- .../src/asset/vault/vault_key.rs | 2 +- .../src/batch/account_update.rs | 0 .../src/batch/batch_id.rs | 0 .../src/batch/input_output_note_tracker.rs | 0 .../src/batch/mod.rs | 0 .../src/batch/note_tree.rs | 3 +- .../src/batch/ordered_batches.rs | 0 .../src/batch/proposed_batch.rs | 2 +- .../src/batch/proven_batch.rs | 0 .../src/block/account_tree/backend.rs | 15 +- .../src/block/account_tree/mod.rs | 11 +- .../src/block/account_tree/partial.rs | 5 +- .../src/block/account_tree/witness.rs | 11 +- .../src/block/account_update_witness.rs | 0 .../src/block/block_account_update.rs | 0 .../src/block/block_body.rs | 0 .../src/block/block_inputs.rs | 0 .../src/block/block_number.rs | 0 .../src/block/block_proof.rs | 0 .../src/block/blockchain.rs | 2 +- .../src/block/header.rs | 0 .../src/block/mod.rs | 0 .../src/block/note_tree.rs | 5 +- .../src/block/nullifier_tree/backend.rs | 10 +- .../src/block/nullifier_tree/mod.rs | 11 +- .../src/block/nullifier_tree/partial.rs | 4 +- .../src/block/nullifier_tree/witness.rs | 2 +- .../src/block/proposed_block.rs | 76 +- .../src/block/proven_block.rs | 0 .../src/block/signer.rs | 0 .../src/constants.rs | 0 .../src/errors/masm_error.rs | 4 +- .../src/errors/mod.rs} | 43 +- crates/miden-protocol/src/errors/protocol.rs | 37 + .../src/errors/tx_kernel.rs} | 25 +- .../src/lib.rs | 13 +- .../src/note/assets.rs | 0 .../src/note/details.rs | 0 .../src/note/execution_hint.rs | 0 .../src/note/file.rs | 0 .../src/note/header.rs | 0 .../src/note/inputs.rs | 0 .../src/note/location.rs | 0 .../src/note/metadata.rs | 0 .../src/note/mod.rs | 0 .../src/note/note_id.rs | 0 .../src/note/note_tag.rs | 0 .../src/note/note_type.rs | 0 .../src/note/nullifier.rs | 0 .../src/note/partial.rs | 0 .../src/note/recipient.rs | 0 .../src/note/script.rs | 0 crates/miden-protocol/src/protocol.rs | 70 ++ .../src/testing/account.rs | 0 .../src/testing/account_code.rs | 4 +- .../src/testing/account_id.rs | 4 +- .../src/testing/add_component.rs | 2 +- .../src/testing/asset.rs | 0 .../src/testing/block.rs | 2 +- .../src/testing/block_note_tree.rs | 0 .../src/testing/constants.rs | 0 .../src/testing/mock_util_lib.rs | 18 +- .../src/testing/mod.rs | 1 + .../src/testing/noop_auth_component.rs | 2 +- .../src/testing/note.rs | 0 .../src/testing/partial_blockchain.rs | 0 .../src/testing/random_signer.rs | 0 .../src/testing/slot_name.rs | 0 .../src/testing/storage.rs | 0 .../src/testing/tx.rs | 0 .../src/transaction/executed_tx.rs | 0 .../src/transaction/inputs/account.rs | 2 +- .../src/transaction/inputs/mod.rs | 0 .../src/transaction/inputs/notes.rs | 0 .../src/transaction/kernel/advice_inputs.rs} | 26 +- .../src/transaction/kernel}/memory.rs | 0 .../src/transaction/kernel}/mod.rs | 76 +- .../src/transaction/kernel/procedures.rs} | 36 +- .../src/transaction/kernel}/tx_event_id.rs | 4 +- .../src/transaction/mod.rs | 2 + .../src/transaction/ordered_transactions.rs | 0 .../src/transaction/outputs.rs | 17 + .../src/transaction/partial_blockchain.rs | 5 +- .../src/transaction/proven_tx.rs | 0 .../src/transaction/transaction_id.rs | 0 .../src/transaction/tx_args.rs | 0 .../src/transaction/tx_header.rs | 0 .../src/transaction/tx_summary.rs | 0 .../{miden-lib => miden-standards}/Cargo.toml | 22 +- .../{miden-lib => miden-standards}/README.md | 4 +- .../basic_fungible_faucet.masm | 4 +- .../asm/account_components/basic_wallet.masm | 4 +- .../account_components/ecdsa_k256_keccak.masm | 4 +- .../ecdsa_k256_keccak_acl.masm | 13 +- .../ecdsa_k256_keccak_multisig.masm} | 17 +- .../network_fungible_faucet.masm | 4 +- .../asm/account_components/no_auth.masm | 6 +- .../account_components/rpo_falcon_512.masm | 4 +- .../rpo_falcon_512_acl.masm | 13 +- .../rpo_falcon_512_multisig.masm} | 17 +- .../asm/note_scripts/BURN.masm | 2 +- .../asm/note_scripts/MINT.masm | 20 +- .../asm/note_scripts/P2ID.masm | 13 +- .../asm/note_scripts/P2IDE.masm | 27 +- .../asm/note_scripts/SWAP.masm | 12 +- .../standards}/auth/ecdsa_k256_keccak.masm | 31 +- .../asm/standards}/auth/mod.masm | 15 +- .../asm/standards}/auth/rpo_falcon512.masm | 31 +- .../standards}/faucets/basic_fungible.masm | 12 +- .../asm/standards}/faucets/mod.masm | 20 +- .../standards}/faucets/network_fungible.masm | 20 +- .../asm/standards/wallets/basic.masm | 120 +++ crates/miden-standards/build.rs | 487 +++++++++ .../src/account/auth/ecdsa_k256_keccak.rs | 22 +- .../src/account/auth/ecdsa_k256_keccak_acl.rs | 12 +- .../auth/ecdsa_k256_keccak_multisig.rs | 12 +- .../src/account/auth/mod.rs | 0 .../src/account/auth/no_auth.rs | 4 +- .../src/account/auth/rpo_falcon_512.rs | 22 +- .../src/account/auth/rpo_falcon_512_acl.rs | 12 +- .../account/auth/rpo_falcon_512_multisig.rs | 12 +- .../src/account/components/mod.rs | 31 +- .../src/account/faucets/basic_fungible.rs | 30 +- .../src/account/faucets/mod.rs | 6 +- .../src/account/faucets/network_fungible.rs | 32 +- .../src/account/interface/component.rs | 49 +- .../src/account/interface/extension.rs | 261 +++++ .../src/account/interface/mod.rs | 229 +---- .../src/account/interface/test.rs | 97 +- .../src/account/mod.rs | 16 +- .../src/account/wallets/mod.rs | 18 +- .../src/auth.rs | 2 +- .../src/code_builder/mod.rs} | 155 ++- .../src/errors/code_builder_errors.rs | 4 +- crates/miden-standards/src/errors/mod.rs | 7 + .../src/errors/standards.rs} | 28 +- crates/miden-standards/src/lib.rs | 21 + .../src/note/mint_inputs.rs | 4 +- .../src/note/mod.rs | 12 +- .../src/note/utils.rs | 16 +- .../src/note/well_known_note.rs | 16 +- crates/miden-standards/src/standards_lib.rs | 72 ++ .../account_component/conditional_auth.rs | 12 +- .../testing/account_component/incr_nonce.rs | 12 +- .../mock_account_component.rs | 4 +- .../mock_faucet_component.rs | 4 +- .../src/testing/account_component/mod.rs | 0 .../src/testing/account_interface.rs | 8 +- .../src/testing/mock_account.rs | 10 +- .../src/testing/mock_account_code.rs | 48 +- .../src/testing/mod.rs | 3 +- .../src/testing/note.rs | 16 +- crates/miden-testing/Cargo.toml | 11 +- crates/miden-testing/src/executor.rs | 16 +- .../src/kernel_tests/batch/proposed_batch.rs | 20 +- .../kernel_tests/batch/proven_tx_builder.rs | 16 +- .../src/kernel_tests/block/header_errors.rs | 35 +- .../block/proposed_block_errors.rs | 12 +- .../block/proposed_block_success.rs | 20 +- .../block/proven_block_success.rs | 18 +- .../src/kernel_tests/block/utils.rs | 14 +- .../miden-testing/src/kernel_tests/tx/mod.rs | 30 +- .../src/kernel_tests/tx/test_account.rs | 205 ++-- .../src/kernel_tests/tx/test_account_delta.rs | 54 +- .../kernel_tests/tx/test_account_interface.rs | 30 +- .../src/kernel_tests/tx/test_active_note.rs | 66 +- .../src/kernel_tests/tx/test_asset.rs | 20 +- .../src/kernel_tests/tx/test_asset_vault.rs | 69 +- .../src/kernel_tests/tx/test_auth.rs | 20 +- .../src/kernel_tests/tx/test_epilogue.rs | 101 +- .../src/kernel_tests/tx/test_faucet.rs | 126 +-- .../src/kernel_tests/tx/test_fee.rs | 12 +- .../src/kernel_tests/tx/test_fpi.rs | 208 ++-- .../src/kernel_tests/tx/test_input_note.rs | 20 +- .../src/kernel_tests/tx/test_lazy_loading.rs | 32 +- .../src/kernel_tests/tx/test_link_map.rs | 8 +- .../src/kernel_tests/tx/test_note.rs | 64 +- .../src/kernel_tests/tx/test_output_note.rs | 102 +- .../src/kernel_tests/tx/test_prologue.rs | 71 +- .../src/kernel_tests/tx/test_tx.rs | 86 +- crates/miden-testing/src/mock_chain/auth.rs | 19 +- crates/miden-testing/src/mock_chain/chain.rs | 41 +- .../src/mock_chain/chain_builder.rs | 39 +- crates/miden-testing/src/mock_chain/note.rs | 4 +- crates/miden-testing/src/mock_host.rs | 18 +- .../miden-testing/src/tx_context/builder.rs | 38 +- .../miden-testing/src/tx_context/context.rs | 35 +- crates/miden-testing/src/utils.rs | 29 +- .../tests/agglayer/asset_conversion.rs | 46 +- .../tests/agglayer/bridge_out.rs | 24 +- crates/miden-testing/tests/auth/ecdsa_acl.rs | 32 +- .../tests/auth/ecdsa_multisig.rs | 59 +- crates/miden-testing/tests/auth/multisig.rs | 61 +- .../tests/auth/rpo_falcon_acl.rs | 32 +- crates/miden-testing/tests/lib.rs | 22 +- crates/miden-testing/tests/scripts/faucet.rs | 40 +- crates/miden-testing/tests/scripts/fee.rs | 4 +- crates/miden-testing/tests/scripts/p2id.rs | 26 +- crates/miden-testing/tests/scripts/p2ide.rs | 12 +- .../miden-testing/tests/scripts/send_note.rs | 37 +- crates/miden-testing/tests/scripts/swap.rs | 20 +- crates/miden-testing/tests/wallet/mod.rs | 22 +- crates/miden-tx-batch-prover/Cargo.toml | 6 +- .../src/local_batch_prover.rs | 4 +- crates/miden-tx/Cargo.toml | 8 +- crates/miden-tx/src/auth/tx_authenticator.rs | 16 +- crates/miden-tx/src/errors/mod.rs | 20 +- crates/miden-tx/src/executor/data_store.rs | 10 +- crates/miden-tx/src/executor/exec_host.rs | 39 +- crates/miden-tx/src/executor/mod.rs | 26 +- crates/miden-tx/src/executor/notes_checker.rs | 17 +- .../src/host/account_delta_tracker.rs | 4 +- .../miden-tx/src/host/account_procedures.rs | 2 +- crates/miden-tx/src/host/kernel_process.rs | 10 +- crates/miden-tx/src/host/link_map.rs | 4 +- crates/miden-tx/src/host/mod.rs | 52 +- crates/miden-tx/src/host/note_builder.rs | 4 +- .../src/host/script_mast_forest_store.rs | 10 +- .../src/host/storage_delta_tracker.rs | 4 +- crates/miden-tx/src/host/tx_event.rs | 12 +- crates/miden-tx/src/lib.rs | 2 +- crates/miden-tx/src/prover/mast_store.rs | 35 +- crates/miden-tx/src/prover/mod.rs | 14 +- crates/miden-tx/src/prover/prover_host.rs | 12 +- crates/miden-tx/src/verifier/mod.rs | 9 +- deny.toml | 3 + deny.toml~ | 69 -- docs/src/account/components.md | 3 +- docs/src/account/storage.md | 2 +- docs/src/note.md | 2 +- docs/src/protocol_library.md | 19 +- docs/src/transaction.md | 4 +- scripts/check-features.sh | 4 +- 360 files changed, 6830 insertions(+), 4933 deletions(-) create mode 100644 crates/miden-agglayer/Cargo.toml create mode 100644 crates/miden-agglayer/asm/account_components/asset_conversion.masm create mode 100644 crates/miden-agglayer/asm/account_components/bridge_out.masm create mode 100644 crates/miden-agglayer/asm/account_components/local_exit_tree.masm create mode 100644 crates/miden-agglayer/asm/note_scripts/B2AGG.masm create mode 100644 crates/miden-agglayer/build.rs create mode 100644 crates/miden-agglayer/src/errors/agglayer.rs create mode 100644 crates/miden-agglayer/src/errors/mod.rs create mode 100644 crates/miden-agglayer/src/lib.rs create mode 100644 crates/miden-agglayer/src/mod.rs create mode 100644 crates/miden-agglayer/src/utils.rs delete mode 100644 crates/miden-lib/asm/miden/contracts/wallets/basic.masm delete mode 100644 crates/miden-lib/build.rs delete mode 100644 crates/miden-lib/src/block/mod.rs delete mode 100644 crates/miden-lib/src/errors/mod.rs delete mode 100644 crates/miden-lib/src/errors/transaction_errors.rs delete mode 100644 crates/miden-lib/src/lib.rs delete mode 100644 crates/miden-lib/src/transaction/outputs.rs delete mode 100644 crates/miden-lib/src/utils/mod.rs rename crates/{miden-objects => miden-protocol}/Cargo.toml (84%) rename crates/{miden-objects => miden-protocol}/README.md (97%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/api.masm (94%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/account.masm (92%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/account_delta.masm (96%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/asset.masm (85%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/asset_vault.masm (94%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/constants.masm (83%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/epilogue.masm (94%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/faucet.masm (94%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/input_note.masm (92%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/link_map.masm (94%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/memory.masm (86%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/note.masm (94%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/output_note.masm (94%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/prologue.masm (94%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/lib/tx.masm (86%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/main.masm (88%) rename crates/{miden-lib => miden-protocol}/asm/kernels/transaction/tx_script_main.masm (93%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/active_account.masm (96%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/active_note.masm (80%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/asset.masm (86%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/faucet.masm (94%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/input_note.masm (97%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/kernel_proc_offsets.masm (80%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/native_account.masm (97%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/note.masm (91%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/output_note.masm (96%) rename crates/{miden-lib/asm/miden => miden-protocol/asm/protocol}/tx.masm (96%) rename crates/{miden-lib => miden-protocol}/asm/shared_modules/account_id.masm (89%) rename crates/{miden-lib => miden-protocol}/asm/shared_utils/util/asset.masm (88%) rename crates/{miden-lib => miden-protocol}/asm/shared_utils/util/note.masm (88%) rename crates/{miden-objects => miden-protocol}/benches/account_seed.rs (90%) create mode 100644 crates/miden-protocol/build.rs rename crates/{miden-lib => miden-protocol}/masm_doc_comment_fmt.md (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/account_type.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/id_prefix.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/id_version.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/seed.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/storage_mode.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/v0/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/account_id/v0/prefix.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/auth.rs (89%) rename crates/{miden-objects => miden-protocol}/src/account/builder/mod.rs (98%) rename crates/{miden-objects => miden-protocol}/src/account/code/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/code/mod.rs (99%) rename crates/{miden-objects => miden-protocol}/src/account/code/procedure.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/code.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/metadata/mod.rs (98%) rename crates/{miden-objects => miden-protocol}/src/account/component/mod.rs (95%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/init_storage_data.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/schema.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/toml/init_storage_data.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/toml/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/toml/serde_impls.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/toml/tests.rs (98%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/type_registry.rs (98%) rename crates/{miden-objects => miden-protocol}/src/account/component/storage/value_name.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/delta/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/delta/storage.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/delta/vault.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/file.rs (97%) rename crates/{miden-objects => miden-protocol}/src/account/header.rs (75%) rename crates/{miden-objects => miden-protocol}/src/account/mod.rs (99%) rename crates/{miden-objects => miden-protocol}/src/account/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/map/mod.rs (98%) rename crates/{miden-objects => miden-protocol}/src/account/storage/map/partial.rs (98%) rename crates/{miden-objects => miden-protocol}/src/account/storage/map/witness.rs (93%) rename crates/{miden-objects => miden-protocol}/src/account/storage/mod.rs (99%) rename crates/{miden-objects => miden-protocol}/src/account/storage/partial.rs (98%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/slot_content.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/slot_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/slot_name.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/storage_slot.rs (100%) rename crates/{miden-objects => miden-protocol}/src/account/storage/slot/type.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/address_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/interface.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/mod.rs (99%) rename crates/{miden-objects => miden-protocol}/src/address/network_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/address/routing_parameters.rs (98%) rename crates/{miden-objects => miden-protocol}/src/address/type.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/fungible.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/nonfungible.rs (100%) rename crates/{miden-objects => miden-protocol}/src/asset/token_symbol.rs (99%) rename crates/{miden-objects => miden-protocol}/src/asset/vault/asset_witness.rs (97%) rename crates/{miden-objects => miden-protocol}/src/asset/vault/mod.rs (99%) rename crates/{miden-objects => miden-protocol}/src/asset/vault/partial.rs (98%) rename crates/{miden-objects => miden-protocol}/src/asset/vault/vault_key.rs (99%) rename crates/{miden-objects => miden-protocol}/src/batch/account_update.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/batch_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/input_output_note_tracker.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/note_tree.rs (96%) rename crates/{miden-objects => miden-protocol}/src/batch/ordered_batches.rs (100%) rename crates/{miden-objects => miden-protocol}/src/batch/proposed_batch.rs (99%) rename crates/{miden-objects => miden-protocol}/src/batch/proven_batch.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/account_tree/backend.rs (96%) rename crates/{miden-objects => miden-protocol}/src/block/account_tree/mod.rs (98%) rename crates/{miden-objects => miden-protocol}/src/block/account_tree/partial.rs (99%) rename crates/{miden-objects => miden-protocol}/src/block/account_tree/witness.rs (97%) rename crates/{miden-objects => miden-protocol}/src/block/account_update_witness.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_account_update.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_body.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_inputs.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_number.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/block_proof.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/blockchain.rs (98%) rename crates/{miden-objects => miden-protocol}/src/block/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/note_tree.rs (97%) rename crates/{miden-objects => miden-protocol}/src/block/nullifier_tree/backend.rs (94%) rename crates/{miden-objects => miden-protocol}/src/block/nullifier_tree/mod.rs (98%) rename crates/{miden-objects => miden-protocol}/src/block/nullifier_tree/partial.rs (98%) rename crates/{miden-objects => miden-protocol}/src/block/nullifier_tree/witness.rs (96%) rename crates/{miden-objects => miden-protocol}/src/block/proposed_block.rs (92%) rename crates/{miden-objects => miden-protocol}/src/block/proven_block.rs (100%) rename crates/{miden-objects => miden-protocol}/src/block/signer.rs (100%) rename crates/{miden-objects => miden-protocol}/src/constants.rs (100%) rename crates/{miden-lib => miden-protocol}/src/errors/masm_error.rs (90%) rename crates/{miden-objects/src/errors.rs => miden-protocol/src/errors/mod.rs} (96%) create mode 100644 crates/miden-protocol/src/errors/protocol.rs rename crates/{miden-lib/src/errors/tx_kernel_errors.rs => miden-protocol/src/errors/tx_kernel.rs} (92%) rename crates/{miden-objects => miden-protocol}/src/lib.rs (90%) rename crates/{miden-objects => miden-protocol}/src/note/assets.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/details.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/execution_hint.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/file.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/inputs.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/location.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/metadata.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/note_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/note_tag.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/note_type.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/nullifier.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/partial.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/recipient.rs (100%) rename crates/{miden-objects => miden-protocol}/src/note/script.rs (100%) create mode 100644 crates/miden-protocol/src/protocol.rs rename crates/{miden-objects => miden-protocol}/src/testing/account.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/account_code.rs (96%) rename crates/{miden-objects => miden-protocol}/src/testing/account_id.rs (98%) rename crates/{miden-objects => miden-protocol}/src/testing/add_component.rs (97%) rename crates/{miden-objects => miden-protocol}/src/testing/asset.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/block.rs (98%) rename crates/{miden-objects => miden-protocol}/src/testing/block_note_tree.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/constants.rs (100%) rename crates/{miden-lib => miden-protocol}/src/testing/mock_util_lib.rs (72%) rename crates/{miden-objects => miden-protocol}/src/testing/mod.rs (92%) rename crates/{miden-objects => miden-protocol}/src/testing/noop_auth_component.rs (97%) rename crates/{miden-objects => miden-protocol}/src/testing/note.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/partial_blockchain.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/random_signer.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/slot_name.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/storage.rs (100%) rename crates/{miden-objects => miden-protocol}/src/testing/tx.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/executed_tx.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/inputs/account.rs (98%) rename crates/{miden-objects => miden-protocol}/src/transaction/inputs/mod.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/inputs/notes.rs (100%) rename crates/{miden-lib/src/transaction/inputs.rs => miden-protocol/src/transaction/kernel/advice_inputs.rs} (96%) rename crates/{miden-lib/src/transaction => miden-protocol/src/transaction/kernel}/memory.rs (100%) rename crates/{miden-lib/src/transaction => miden-protocol/src/transaction/kernel}/mod.rs (90%) rename crates/{miden-lib/src/transaction/kernel_procedures.rs => miden-protocol/src/transaction/kernel/procedures.rs} (77%) rename crates/{miden-lib/src/transaction => miden-protocol/src/transaction/kernel}/tx_event_id.rs (99%) rename crates/{miden-objects => miden-protocol}/src/transaction/mod.rs (90%) rename crates/{miden-objects => miden-protocol}/src/transaction/ordered_transactions.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/outputs.rs (94%) rename crates/{miden-objects => miden-protocol}/src/transaction/partial_blockchain.rs (99%) rename crates/{miden-objects => miden-protocol}/src/transaction/proven_tx.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/transaction_id.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/tx_args.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/tx_header.rs (100%) rename crates/{miden-objects => miden-protocol}/src/transaction/tx_summary.rs (100%) rename crates/{miden-lib => miden-standards}/Cargo.toml (66%) rename crates/{miden-lib => miden-standards}/README.md (61%) rename crates/{miden-lib => miden-standards}/asm/account_components/basic_fungible_faucet.masm (53%) rename crates/{miden-lib => miden-standards}/asm/account_components/basic_wallet.masm (50%) rename crates/{miden-lib => miden-standards}/asm/account_components/ecdsa_k256_keccak.masm (95%) rename crates/{miden-lib => miden-standards}/asm/account_components/ecdsa_k256_keccak_acl.masm (95%) rename crates/{miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm => miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm} (97%) rename crates/{miden-lib => miden-standards}/asm/account_components/network_fungible_faucet.masm (53%) rename crates/{miden-lib => miden-standards}/asm/account_components/no_auth.masm (93%) rename crates/{miden-lib => miden-standards}/asm/account_components/rpo_falcon_512.masm (95%) rename crates/{miden-lib => miden-standards}/asm/account_components/rpo_falcon_512_acl.masm (95%) rename crates/{miden-lib/asm/account_components/multisig_rpo_falcon_512.masm => miden-standards/asm/account_components/rpo_falcon_512_multisig.masm} (97%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/BURN.masm (96%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/MINT.masm (90%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/P2ID.masm (83%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/P2IDE.masm (89%) rename crates/{miden-lib => miden-standards}/asm/note_scripts/SWAP.masm (91%) rename crates/{miden-lib/asm/miden => miden-standards/asm/standards}/auth/ecdsa_k256_keccak.masm (92%) rename crates/{miden-lib/asm/miden => miden-standards/asm/standards}/auth/mod.masm (92%) rename crates/{miden-lib/asm/miden => miden-standards/asm/standards}/auth/rpo_falcon512.masm (92%) rename crates/{miden-lib/asm/miden/contracts => miden-standards/asm/standards}/faucets/basic_fungible.masm (92%) rename crates/{miden-lib/asm/miden/contracts => miden-standards/asm/standards}/faucets/mod.masm (91%) rename crates/{miden-lib/asm/miden/contracts => miden-standards/asm/standards}/faucets/network_fungible.masm (86%) create mode 100644 crates/miden-standards/asm/standards/wallets/basic.masm create mode 100644 crates/miden-standards/build.rs rename crates/{miden-lib => miden-standards}/src/account/auth/ecdsa_k256_keccak.rs (62%) rename crates/{miden-lib => miden-standards}/src/account/auth/ecdsa_k256_keccak_acl.rs (98%) rename crates/{miden-lib => miden-standards}/src/account/auth/ecdsa_k256_keccak_multisig.rs (97%) rename crates/{miden-lib => miden-standards}/src/account/auth/mod.rs (100%) rename crates/{miden-lib => miden-standards}/src/account/auth/no_auth.rs (94%) rename crates/{miden-lib => miden-standards}/src/account/auth/rpo_falcon_512.rs (63%) rename crates/{miden-lib => miden-standards}/src/account/auth/rpo_falcon_512_acl.rs (98%) rename crates/{miden-lib => miden-standards}/src/account/auth/rpo_falcon_512_multisig.rs (97%) rename crates/{miden-lib => miden-standards}/src/account/components/mod.rs (93%) rename crates/{miden-lib => miden-standards}/src/account/faucets/basic_fungible.rs (95%) rename crates/{miden-lib => miden-standards}/src/account/faucets/mod.rs (93%) rename crates/{miden-lib => miden-standards}/src/account/faucets/network_fungible.rs (91%) rename crates/{miden-lib => miden-standards}/src/account/interface/component.rs (87%) create mode 100644 crates/miden-standards/src/account/interface/extension.rs rename crates/{miden-lib => miden-standards}/src/account/interface/mod.rs (51%) rename crates/{miden-lib => miden-standards}/src/account/interface/test.rs (91%) rename crates/{miden-lib => miden-standards}/src/account/mod.rs (56%) rename crates/{miden-lib => miden-standards}/src/account/wallets/mod.rs (93%) rename crates/{miden-lib => miden-standards}/src/auth.rs (97%) rename crates/{miden-lib/src/utils/code_builder.rs => miden-standards/src/code_builder/mod.rs} (82%) rename crates/{miden-lib => miden-standards}/src/errors/code_builder_errors.rs (93%) create mode 100644 crates/miden-standards/src/errors/mod.rs rename crates/{miden-lib/src/errors/note_script_errors.rs => miden-standards/src/errors/standards.rs} (75%) create mode 100644 crates/miden-standards/src/lib.rs rename crates/{miden-lib => miden-standards}/src/note/mint_inputs.rs (95%) rename crates/{miden-lib => miden-standards}/src/note/mod.rs (97%) rename crates/{miden-lib => miden-standards}/src/note/utils.rs (90%) rename crates/{miden-lib => miden-standards}/src/note/well_known_note.rs (98%) create mode 100644 crates/miden-standards/src/standards_lib.rs rename crates/{miden-lib => miden-standards}/src/testing/account_component/conditional_auth.rs (85%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/incr_nonce.rs (78%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/mock_account_component.rs (91%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/mock_faucet_component.rs (84%) rename crates/{miden-lib => miden-standards}/src/testing/account_component/mod.rs (100%) rename crates/{miden-lib => miden-standards}/src/testing/account_interface.rs (60%) rename crates/{miden-lib => miden-standards}/src/testing/mock_account.rs (93%) rename crates/{miden-lib => miden-standards}/src/testing/mock_account_code.rs (83%) rename crates/{miden-lib => miden-standards}/src/testing/mod.rs (83%) rename crates/{miden-lib => miden-standards}/src/testing/note.rs (92%) delete mode 100644 deny.toml~ diff --git a/CHANGELOG.md b/CHANGELOG.md index 39b7695bf5..f06f41cdbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). +- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197)). +- [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). diff --git a/Cargo.lock b/Cargo.lock index 707538b523..0ab6b7af3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,7 +175,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -216,15 +216,15 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bech32" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" [[package]] name = "bench-note-checker" @@ -232,8 +232,8 @@ version = "0.1.0" dependencies = [ "anyhow", "criterion 0.6.0", - "miden-lib", - "miden-objects", + "miden-protocol", + "miden-standards", "miden-testing", "miden-tx", "serde", @@ -247,8 +247,8 @@ version = "0.1.0" dependencies = [ "anyhow", "criterion 0.6.0", - "miden-lib", - "miden-objects", + "miden-protocol", + "miden-standards", "miden-testing", "miden-tx", "serde", @@ -337,9 +337,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.46" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -417,18 +417,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstyle", "clap_lex", @@ -632,7 +632,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -656,22 +656,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ "proc-macro2", "quote", - "syn", + "rustc_version 0.4.1", + "syn 2.0.111", ] [[package]] @@ -765,18 +766,6 @@ dependencies = [ "log", ] -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "env_filter" version = "0.1.4" @@ -817,7 +806,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -972,7 +961,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1096,15 +1085,16 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", "foldhash", "rayon", "serde", + "serde_core", ] [[package]] @@ -1145,9 +1135,9 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", @@ -1257,7 +1247,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1272,9 +1262,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -1341,9 +1331,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libm" @@ -1374,9 +1364,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loom" @@ -1388,7 +1378,7 @@ dependencies = [ "generator", "scoped-tls", "tracing", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", ] [[package]] @@ -1415,11 +1405,27 @@ dependencies = [ "libc", ] +[[package]] +name = "miden-agglayer" +version = "0.13.0" +dependencies = [ + "fs-err", + "miden-agglayer", + "miden-assembly", + "miden-core", + "miden-core-lib", + "miden-protocol", + "miden-standards", + "miden-utils-sync", + "regex", + "walkdir", +] + [[package]] name = "miden-air" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06acfd2ddc25b68f9d23d2add3f15c0ec3f9890ce6418409d71bea9dc6590bd0" +checksum = "e663337017ed028dff8c18a0ce1db64aad0e850996e3214f137f98317533c2e1" dependencies = [ "miden-core", "miden-utils-indexing", @@ -1430,10 +1436,11 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1219b9e48bb286b58a23bb65cf74baa1b24ddbcb462ca625b38186674571047" +checksum = "001249195c227624695529c82ebf51c390ec1c28e99a567549ce3a272a2aedf3" dependencies = [ + "env_logger", "log", "miden-assembly-syntax", "miden-core", @@ -1444,11 +1451,12 @@ dependencies = [ [[package]] name = "miden-assembly-syntax" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eeaef2853061c54527bb2664c0c832ce3d1f80847c79512455fec3b93057f2a" +checksum = "1963cfa667aa6a157c99982df340a7bd42b054652e6f33d5e3513217531eca73" dependencies = [ "aho-corasick", + "env_logger", "lalrpop", "lalrpop-util", "log", @@ -1457,6 +1465,7 @@ dependencies = [ "miden-utils-diagnostics", "midenc-hir-type", "proptest", + "proptest-derive", "regex", "rustc_version 0.4.1", "semver 1.0.27", @@ -1468,37 +1477,59 @@ dependencies = [ name = "miden-block-prover" version = "0.13.0" dependencies = [ - "miden-objects", + "miden-protocol", "thiserror", ] [[package]] name = "miden-core" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452a00429d05c416001ec0578291eb88e115cf94fc22b3308267abfdcd813440" +checksum = "136debf5474190dc584df3252710dac07a0e45315740c9538a7fc0b72c596365" dependencies = [ - "enum_dispatch", + "derive_more", + "itertools 0.14.0", "miden-crypto", "miden-debug-types", "miden-formatting", + "miden-utils-core-derive", "miden-utils-indexing", "num-derive", "num-traits", + "proptest", + "proptest-derive", "thiserror", "winter-math", "winter-utils", ] +[[package]] +name = "miden-core-lib" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcec9fb9a256d2fae347162d9a94653a1790dd33b4af73ad29686475b63deb34" +dependencies = [ + "env_logger", + "fs-err", + "miden-assembly", + "miden-core", + "miden-crypto", + "miden-processor", + "miden-utils-sync", + "sha2", + "thiserror", +] + [[package]] name = "miden-crypto" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395e5cc76b64e24533ee55c8d1ff90305b8cad372bdbea4f4f324239e36a895f" +checksum = "dc7981c1d907bb9864e24f2bd6304c4fca03a41fc4606c09edd6a7f5a8fc80fc" dependencies = [ "blake3", "cc", "chacha20poly1305", + "curve25519-dalek", "ed25519-dalek", "flume", "glob", @@ -1513,6 +1544,7 @@ dependencies = [ "rand_core 0.9.3", "rand_hc", "rayon", + "sha2", "sha3", "subtle", "thiserror", @@ -1524,19 +1556,19 @@ dependencies = [ [[package]] name = "miden-crypto-derive" -version = "0.18.2" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2222f37355ea975f40acd3c098a437574a31a4d8a2c193cf4e9fead2beede577" +checksum = "83479e7af490784c6f2d2e02cec5210fd6e5bc6ce3d4427734e36a773bca72d2" dependencies = [ "quote", - "syn", + "syn 2.0.111", ] [[package]] name = "miden-debug-types" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97eed62ac0ca7420e49148fd306c74786b23a8d31df6da6277c671ba3e5c619a" +checksum = "6dc25083822c3d582c42ad10aeee0138dec15a130f3017b05495bb91e31fde4a" dependencies = [ "memchr", "miden-crypto", @@ -1559,30 +1591,11 @@ dependencies = [ "unicode-width 0.1.14", ] -[[package]] -name = "miden-lib" -version = "0.13.0" -dependencies = [ - "anyhow", - "assert_matches", - "fs-err", - "miden-assembly", - "miden-core", - "miden-lib", - "miden-objects", - "miden-processor", - "miden-stdlib", - "rand", - "regex", - "thiserror", - "walkdir", -] - [[package]] name = "miden-mast-package" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d13e6ba2b357551598f13396ed52f8f21aa99979aa3b338bb5521feeda19c8a" +checksum = "da35f2fc1eacbfd0b6b995e888c2b778bd646acebf34dab27f9f7ed9b3effaa2" dependencies = [ "derive_more", "miden-assembly-syntax", @@ -1613,7 +1626,7 @@ dependencies = [ "supports-color", "supports-hyperlinks", "supports-unicode", - "syn", + "syn 2.0.111", "terminal_size", "textwrap", "thiserror", @@ -1629,11 +1642,31 @@ checksum = "86a905f3ea65634dd4d1041a4f0fd0a3e77aa4118341d265af1a94339182222f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] -name = "miden-objects" +name = "miden-processor" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb298dbdda739080497c18eace4d56c58f3e8d257676c9b2f407be441131ecd" +dependencies = [ + "itertools 0.14.0", + "miden-air", + "miden-core", + "miden-debug-types", + "miden-utils-diagnostics", + "miden-utils-indexing", + "paste", + "rayon", + "thiserror", + "tokio", + "tracing", + "winter-prover", +] + +[[package]] +name = "miden-protocol" version = "0.13.0" dependencies = [ "anyhow", @@ -1641,68 +1674,51 @@ dependencies = [ "bech32", "color-eyre", "criterion 0.5.1", + "fs-err", "getrandom 0.3.4", "miden-air", "miden-assembly", "miden-assembly-syntax", "miden-core", + "miden-core-lib", "miden-crypto", "miden-mast-package", - "miden-objects", "miden-processor", + "miden-protocol", "miden-protocol-macros", - "miden-stdlib", "miden-utils-sync", "miden-verifier", "pprof", "rand", "rand_chacha", "rand_xoshiro", + "regex", "rstest", "semver 1.0.27", "serde", "tempfile", "thiserror", "toml", + "walkdir", "winter-air", "winter-rand-utils", ] -[[package]] -name = "miden-processor" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ef77929651b8755965cde8f589bd38e2345a619d54cab6427f91aa23c47f6a" -dependencies = [ - "itertools 0.14.0", - "miden-air", - "miden-core", - "miden-debug-types", - "miden-utils-diagnostics", - "miden-utils-indexing", - "paste", - "rayon", - "thiserror", - "tokio", - "tracing", - "winter-prover", -] - [[package]] name = "miden-protocol-macros" version = "0.13.0" dependencies = [ - "miden-objects", + "miden-protocol", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] name = "miden-prover" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c30a5d10baeec17b9336de8544cb7f9b96b32de757c4cfb8d95ee0521bb5cd" +checksum = "8506c8eb4d980134c0145887af50bd4631df4010eb23d6e454764cb1ee28836c" dependencies = [ "miden-air", "miden-debug-types", @@ -1713,19 +1729,22 @@ dependencies = [ ] [[package]] -name = "miden-stdlib" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e90a5de45a1e6213ff17b66fff8accde0bbc64264e2c22bbcb9a895f8f3b767" +name = "miden-standards" +version = "0.13.0" dependencies = [ - "env_logger", + "anyhow", + "assert_matches", "fs-err", "miden-assembly", "miden-core", - "miden-crypto", + "miden-core-lib", "miden-processor", - "miden-utils-sync", + "miden-protocol", + "miden-standards", + "rand", + "regex", "thiserror", + "walkdir", ] [[package]] @@ -1735,10 +1754,13 @@ dependencies = [ "anyhow", "assert_matches", "itertools 0.14.0", + "miden-agglayer", + "miden-assembly", "miden-block-prover", - "miden-lib", - "miden-objects", + "miden-core-lib", "miden-processor", + "miden-protocol", + "miden-standards", "miden-tx", "miden-tx-batch-prover", "primitive-types", @@ -1757,10 +1779,10 @@ dependencies = [ "anyhow", "assert_matches", "miden-assembly", - "miden-lib", - "miden-objects", "miden-processor", + "miden-protocol", "miden-prover", + "miden-standards", "miden-tx", "miden-verifier", "rstest", @@ -1771,15 +1793,26 @@ dependencies = [ name = "miden-tx-batch-prover" version = "0.13.0" dependencies = [ - "miden-objects", + "miden-protocol", "miden-tx", ] +[[package]] +name = "miden-utils-core-derive" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0807840c07a4491a292153258cfae27914333e1a7240777a77c22d8ca3b55873" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "miden-utils-diagnostics" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3ff4c019d96539a7066626efb4dce5c9fb7b0e44e961b0c2571e78f34236d5" +checksum = "1b28b1b29e300b471b0f1cbc286997a1326c900814a73b0b28338d5926ce192c" dependencies = [ "miden-crypto", "miden-debug-types", @@ -1790,18 +1823,18 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c798250bee4e856d4f18c161e91cdcbef1906f6614d00cf0063b47031c0f8cc6" +checksum = "f8bd0c1966de07d48a4ed0b2821466919c061f4866296be87afc56970a49716a" dependencies = [ "thiserror", ] [[package]] name = "miden-utils-sync" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feebe7d896c013ea74dbc98de978836606356a044d4ed3b61ded54d3b319d89f" +checksum = "a1fa7e37db2fbf2dee6ba6e411b3570ef48d52ec780b9c8125623f9ddca30da3" dependencies = [ "lock_api", "loom", @@ -1810,9 +1843,9 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.19.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f8e47b78bba1fe1b31faee8f12aafd95385f6d6a8b108b03e92f5d743bb29f" +checksum = "383c934eed92f89be4c1e3dbc97ccf37b48433a0b33727c92a5abbfa2d45f420" dependencies = [ "miden-air", "miden-core", @@ -1917,7 +1950,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2221,6 +2254,17 @@ dependencies = [ "unarray", ] +[[package]] +name = "proptest-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "quick-xml" version = "0.26.0" @@ -2418,7 +2462,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn", + "syn 2.0.111", "unicode-ident", ] @@ -2571,7 +2615,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2763,9 +2807,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "symbolic-common" -version = "12.16.3" +version = "12.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03f433c9befeea460a01d750e698aa86caf86dcfbd77d552885cd6c89d52f50" +checksum = "b3d8046c5674ab857104bc4559d505f4809b8060d57806e45d49737c97afeb60" dependencies = [ "debugid", "memmap2", @@ -2775,9 +2819,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.16.3" +version = "12.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d359ef6192db1760a34321ec4f089245ede4342c27e59be99642f12a859de8" +checksum = "1accb6e5c4b0f682de907623912e616b44be1c9e725775155546669dbff720ec" dependencies = [ "rustc-demangle", "symbolic-common", @@ -2785,9 +2829,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.110" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -2815,9 +2870,9 @@ dependencies = [ [[package]] name = "term" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2111ef44dae28680ae9752bb89409e7310ca33a8c621ebe7b106cf5c928b3ac0" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ "windows-sys 0.61.2", ] @@ -2869,7 +2924,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2909,7 +2964,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2962,9 +3017,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" dependencies = [ "indexmap", "toml_datetime", @@ -2989,9 +3044,9 @@ checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3000,20 +3055,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -3053,9 +3108,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -3157,9 +3212,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "js-sys", "wasm-bindgen", @@ -3213,9 +3268,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -3226,9 +3281,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3236,31 +3291,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -3351,7 +3406,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3362,7 +3417,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3564,9 +3619,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -3623,7 +3678,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d31a19dae58475d019850e25b0170e94b16d382fbf6afee9c0e80fdc935e73e" dependencies = [ "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3702,22 +3757,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6c774e9967..7b3b758c32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,11 @@ members = [ "bin/bench-note-checker", "bin/bench-transaction", + "crates/miden-agglayer", "crates/miden-block-prover", - "crates/miden-lib", - "crates/miden-objects", + "crates/miden-protocol", "crates/miden-protocol-macros", + "crates/miden-standards", "crates/miden-testing", "crates/miden-tx", "crates/miden-tx-batch-prover", @@ -41,26 +42,27 @@ lto = true [workspace.dependencies] # Workspace crates +miden-agglayer = { default-features = false, path = "crates/miden-agglayer", version = "0.13" } miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.13" } -miden-lib = { default-features = false, path = "crates/miden-lib", version = "0.13" } -miden-objects = { default-features = false, path = "crates/miden-objects", version = "0.13" } +miden-protocol = { default-features = false, path = "crates/miden-protocol", version = "0.13" } miden-protocol-macros = { default-features = false, path = "crates/miden-protocol-macros", version = "0.13" } +miden-standards = { default-features = false, path = "crates/miden-standards", version = "0.13" } miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.13" } miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.13" } miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.13" } # Miden dependencies -miden-air = { default-features = false, version = "0.19" } -miden-assembly = { default-features = false, version = "0.19" } -miden-assembly-syntax = { default-features = false, version = "0.19" } -miden-core = { default-features = false, version = "0.19" } -miden-crypto = { default-features = false, version = "0.18.5" } -miden-mast-package = { default-features = false, version = "0.19" } -miden-processor = { default-features = false, version = "0.19" } -miden-prover = { default-features = false, version = "0.19" } -miden-stdlib = { default-features = false, version = "0.19" } -miden-utils-sync = { default-features = false, version = "0.19" } -miden-verifier = { default-features = false, version = "0.19" } +miden-air = { default-features = false, version = "0.20" } +miden-assembly = { default-features = false, version = "0.20" } +miden-assembly-syntax = { default-features = false, version = "0.20" } +miden-core = { default-features = false, version = "0.20" } +miden-core-lib = { default-features = false, version = "0.20" } +miden-crypto = { default-features = false, version = "0.19" } +miden-mast-package = { default-features = false, version = "0.20" } +miden-processor = { default-features = false, version = "0.20" } +miden-prover = { default-features = false, version = "0.20" } +miden-utils-sync = { default-features = false, version = "0.20" } +miden-verifier = { default-features = false, version = "0.20" } # External dependencies anyhow = { default-features = false, features = ["backtrace", "std"], version = "1.0" } diff --git a/Makefile b/Makefile index 6f67df4e17..10f3f8823a 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ help: WARNINGS=RUSTDOCFLAGS="-D warnings" # Enable file generation in the `src` directory. -# This is used in the build scripts of miden-lib. +# This is used in the build scripts of miden-protocol and miden-standards. BUILD_GENERATED_FILES_IN_SRC=BUILD_GENERATED_FILES_IN_SRC=1 # Enable backtraces for tests where we return an anyhow::Result. If enabled, anyhow::Error will # then contain a `Backtrace` and print it when a test returns an error. diff --git a/README.md b/README.md index 55dd81ee88..df78fec0f4 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,8 @@ Miden is currently on release v0.13. This is an early version of the protocol an | Crate | Description | | ------------------------------- | ------------------------------------------------------------------------------- | -| [objects](crates/miden-objects) | Contains core components defining the Miden rollup protocol. | -| [miden-lib](crates/miden-lib) | Contains the code of the Miden rollup kernels and standardized smart contracts. | +| [miden-protocol](crates/miden-protocol) | Contains core components defining the Miden protocol, including the transaction kernel. | +| [miden-standards](crates/miden-standards) | Contains the code of Miden's standardized smart contracts. | | [miden-tx](crates/miden-tx) | Contains tool for creating, executing, and proving Miden rollup transaction. | | [bench-tx](bin/bench-tx) | Contains transaction execution and proving benchmarks. | diff --git a/bin/bench-note-checker/Cargo.toml b/bin/bench-note-checker/Cargo.toml index 9496b74ecb..630d1b9189 100644 --- a/bin/bench-note-checker/Cargo.toml +++ b/bin/bench-note-checker/Cargo.toml @@ -12,10 +12,10 @@ version = "0.1.0" [dependencies] # Workspace dependencies -miden-lib = { workspace = true } -miden-objects = { features = ["testing"], workspace = true } -miden-testing = { workspace = true } -miden-tx = { workspace = true } +miden-protocol = { features = ["testing"], workspace = true } +miden-standards = { workspace = true } +miden-testing = { workspace = true } +miden-tx = { workspace = true } # External dependencies anyhow = { workspace = true } diff --git a/bin/bench-note-checker/src/lib.rs b/bin/bench-note-checker/src/lib.rs index 36761b03e6..cb08ab832e 100644 --- a/bin/bench-note-checker/src/lib.rs +++ b/bin/bench-note-checker/src/lib.rs @@ -1,12 +1,12 @@ -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::AccountId; -use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::rand::RpoRandomCoin; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::{ +use miden_protocol::account::AccountId; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::crypto::rand::RpoRandomCoin; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_SENDER, }; +use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain, TxContextInput}; use miden_tx::auth::UnreachableAuth; use miden_tx::{NoteConsumptionChecker, TransactionExecutor}; diff --git a/bin/bench-transaction/Cargo.toml b/bin/bench-transaction/Cargo.toml index c5ed2cdb9e..9b07fccd84 100644 --- a/bin/bench-transaction/Cargo.toml +++ b/bin/bench-transaction/Cargo.toml @@ -17,10 +17,10 @@ path = "src/time_counting_benchmarks/prove.rs" [dependencies] # Workspace dependencies -miden-lib = { workspace = true } -miden-objects = { features = ["testing"], workspace = true } -miden-testing = { workspace = true } -miden-tx = { workspace = true } +miden-protocol = { features = ["testing"], workspace = true } +miden-standards = { workspace = true } +miden-testing = { workspace = true } +miden-tx = { workspace = true } # External dependencies anyhow = { workspace = true } diff --git a/bin/bench-transaction/src/context_setups.rs b/bin/bench-transaction/src/context_setups.rs index f08b376e90..6a136174ee 100644 --- a/bin/bench-transaction/src/context_setups.rs +++ b/bin/bench-transaction/src/context_setups.rs @@ -1,10 +1,10 @@ use anyhow::Result; -use miden_lib::utils::CodeBuilder; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::ACCOUNT_ID_SENDER; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, Word}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::ACCOUNT_ID_SENDER; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, Word}; +use miden_standards::code_builder::CodeBuilder; use miden_testing::{Auth, MockChain, TransactionContext}; /// Returns the transaction context which could be used to run the transaction which creates a @@ -25,8 +25,8 @@ pub fn tx_create_single_p2id_note() -> Result { let tx_note_creation_script = format!( " - use.miden::output_note - use.std::sys + use miden::protocol::output_note + use miden::core::sys begin # create an output note with fungible asset @@ -40,7 +40,7 @@ pub fn tx_create_single_p2id_note() -> Result { # move the asset to the note push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw # => [note_idx] diff --git a/bin/bench-transaction/src/cycle_counting_benchmarks/utils.rs b/bin/bench-transaction/src/cycle_counting_benchmarks/utils.rs index 1a5865ad1f..49a916c390 100644 --- a/bin/bench-transaction/src/cycle_counting_benchmarks/utils.rs +++ b/bin/bench-transaction/src/cycle_counting_benchmarks/utils.rs @@ -5,7 +5,7 @@ use std::fs::{read_to_string, write}; use std::path::Path; use anyhow::Context; -use miden_objects::transaction::TransactionMeasurements; +use miden_protocol::transaction::TransactionMeasurements; use serde::Serialize; use serde_json::{Value, from_str, to_string_pretty}; diff --git a/bin/bench-transaction/src/main.rs b/bin/bench-transaction/src/main.rs index c036936eea..651be7993b 100644 --- a/bin/bench-transaction/src/main.rs +++ b/bin/bench-transaction/src/main.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::path::Path; use anyhow::{Context, Result}; -use miden_objects::transaction::TransactionMeasurements; +use miden_protocol::transaction::TransactionMeasurements; mod context_setups; use context_setups::{ diff --git a/bin/bench-transaction/src/time_counting_benchmarks/prove.rs b/bin/bench-transaction/src/time_counting_benchmarks/prove.rs index 857fb57121..5dafb4604d 100644 --- a/bin/bench-transaction/src/time_counting_benchmarks/prove.rs +++ b/bin/bench-transaction/src/time_counting_benchmarks/prove.rs @@ -4,7 +4,7 @@ use std::time::Duration; use anyhow::Result; use bench_transaction::context_setups::{tx_consume_single_p2id_note, tx_consume_two_p2id_notes}; use criterion::{BatchSize, Criterion, SamplingMode, criterion_group, criterion_main}; -use miden_objects::transaction::{ExecutedTransaction, ProvenTransaction}; +use miden_protocol::transaction::{ExecutedTransaction, ProvenTransaction}; use miden_tx::LocalTransactionProver; // BENCHMARK NAMES diff --git a/crates/miden-agglayer/Cargo.toml b/crates/miden-agglayer/Cargo.toml new file mode 100644 index 0000000000..ee09c67aa5 --- /dev/null +++ b/crates/miden-agglayer/Cargo.toml @@ -0,0 +1,43 @@ +[package] +authors.workspace = true +categories = ["no-std"] +description = "Agglayer components for the Miden protocol" +edition.workspace = true +homepage.workspace = true +keywords = ["agglayer", "miden"] +license.workspace = true +name = "miden-agglayer" +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lib] +bench = false + +[features] +default = ["std"] +std = ["miden-assembly/std", "miden-core/std"] +testing = ["miden-protocol/testing"] + +[dependencies] +# Miden dependencies +miden-assembly = { workspace = true } +miden-core = { workspace = true } +miden-protocol = { features = ["testing"], workspace = true } +miden-utils-sync = { workspace = true } + +[dev-dependencies] +miden-agglayer = { features = ["testing"], path = "." } + +[build-dependencies] +fs-err = { version = "3" } +miden-assembly = { workspace = true } +miden-core = { workspace = true } +miden-core-lib = { workspace = true } +miden-protocol = { features = ["testing"], workspace = true } +miden-standards = { workspace = true } +regex = { version = "1.11" } +walkdir = { version = "2.5" } + +[package.metadata.cargo-machete] +ignored = ["miden-core-lib", "miden-standards"] diff --git a/crates/miden-agglayer/asm/account_components/asset_conversion.masm b/crates/miden-agglayer/asm/account_components/asset_conversion.masm new file mode 100644 index 0000000000..7596846022 --- /dev/null +++ b/crates/miden-agglayer/asm/account_components/asset_conversion.masm @@ -0,0 +1,106 @@ +use miden::core::math::u64 +use miden::core::word + +# CONSTANTS +# ================================================================================================= + +const MAX_SCALING_FACTOR=18 + +# ERRORS +# ================================================================================================= +const ERR_SCALE_AMOUNT_EXCEEDED_LIMIT="maximum scaling factor is 18" + +#! Calculate 10^scale where scale is a u8 exponent. +#! +#! Inputs: [scale] +#! Outputs: [10^scale] +#! +#! Where: +#! - scale is expected to be a small integer (0-18 typical for crypto decimals) +#! +#! Panics if: +#! - scale > 18 (overflow protection) +proc pow10 + u32assert.err=ERR_SCALE_AMOUNT_EXCEEDED_LIMIT + # => [scale] + + dup u32lte.MAX_SCALING_FACTOR assert.err=ERR_SCALE_AMOUNT_EXCEEDED_LIMIT + # => [scale] + + push.1 swap + # => [scale, result] + + dup neq.0 + # => [is_not_zero, scale, result] + + # Loop to calculate 10^scale + while.true + # => [scale, result] + + # result *= 10 + swap mul.10 swap + # => [scale, result*10] + + # scale -= 1 + sub.1 + # => [scale-1, result*10] + + dup neq.0 + # => [is_not_zero, scale-1, result*10] + end + # => [0, result] + + drop + # => [result] +end + +#! Convert an asset amount to a scaled U256 representation for bridging to Agglayer. +#! +#! This procedure is used to convert Miden asset amounts to EVM asset amounts. +#! It multiplies the input amount by 10^target_scale to adjust for decimal differences +#! between the current representation and the target chain's native decimals. +#! +#! The procedure first calculates 10^target_scale using the pow10 helper, then converts +#! both the amount and scale factor to U64 format, performs U64 multiplication, and +#! returns the result as 8 u32 limbs in little-endian order (U256 format). +#! +#! Inputs: [amount, target_scale] +#! Outputs: [[RESULT_U256[0], RESULT_U256[1]]] +#! +#! Where: +#! - amount: The asset amount to be converted (range: 0 to 2^63 - 2^31) +#! - target_scale: Exponent for scaling factor (10^target_scale) +#! - [RESULT_U256[0], RESULT_U256[1]]: U256 value as 8 u32 limbs in little-endian order +#! (least significant limb at the top of the stack, each limb stored in little-endian format) +#! +#! Examples: +#! - USDC: amount=1000000000, target_scale=0 → 1000000000 (no scaling) +#! - ETH: amount=1e10, target_scale=8 → 1e18 +#! +#! Invocation: exec +pub proc scale_native_amount_to_u256 + swap + # => [target_scale, amount] + + exec.pow10 + # => [scale, amount] + + u32split + # => [scale_hi, scale_lo, amount] + + movup.2 u32split + # => [amount_hi, amount_lo, scale_hi, scale_lo] + + # Perform U64 multiplication: amount * scale + # This is safe because both the scaling factor and amount are guaranteed to be smaller + # than 2^64, so we will never overflow a 256-bit value. + exec.u64::overflowing_mul + # => [res_hi, res_mid_hi, res_mid_lo, res_lo] + + exec.word::reverse + # => [res_lo, res_mid_lo, res_mid_hi, res_hi] + + # convert to U256 & little endian + padw swapw + # => [RESULT_U256[0], RESULT_U256[1]] +end diff --git a/crates/miden-agglayer/asm/account_components/bridge_out.masm b/crates/miden-agglayer/asm/account_components/bridge_out.masm new file mode 100644 index 0000000000..7d4518b2a3 --- /dev/null +++ b/crates/miden-agglayer/asm/account_components/bridge_out.masm @@ -0,0 +1,162 @@ +use miden::protocol::active_note +use miden::protocol::note +use miden::protocol::output_note +use miden::core::crypto::hashes::keccak256 +use miden::core::word +use agglayer::local_exit_tree + +# CONSTANTS +# ================================================================================================= +const MMR_PTR=42 +const LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") + +const BURN_NOTE_ROOT=[6407337173854817345,5626358912819151014,703918618794810515,17401169215223723177] +const EXECUTION_HINT_ALWAYS=1 +const PUBLIC_NOTE=1 +const AUX=0 +const NUM_BURN_NOTE_INPUTS=0 +const BURN_ASSET_MEM_PTR=24 + +#! Computes the SERIAL_NUM of the outputted BURN note. +#! +#! The serial number is computed as hash(B2AGG_SERIAL_NUM, ASSET). +#! +#! Inputs: [ASSET] +#! Outputs: [SERIAL_NUM] +#! +#! Where: +#! - ASSET is the asset for which to compute the burn note serial number. +#! - SERIAL_NUM is the computed serial number for the BURN note. +#! +#! Invocation: exec +proc compute_burn_note_serial_num + exec.active_note::get_serial_number + # => [B2AGG_SERIAL_NUM, ASSET] + + hmerge + # => [SERIAL_NUM] +end + +#! Creates a BURN note for the specified asset. +#! +#! This procedure creates an output note that represents a burn operation for the given asset. +#! The note is configured with the appropriate recipient, tag, and execution hint. +#! +#! Inputs: [ASSET] +#! Outputs: [] +#! +#! Where: +#! - ASSET is the asset to be burned. +#! +#! Invocation: exec +@locals(8) +proc create_burn_note + loc_storew_be.0 dupw + # => [ASSET, ASSET] + + movup.2 drop movup.2 drop + # => [faucet_id_prefix, faucet_id_suffix, ASSET] + + exec.note::build_note_tag_for_network_account + # => [network_faucet_tag, ASSET] + + loc_store.5 + # => [ASSET] + + exec.compute_burn_note_serial_num + # => [SERIAL_NUM] + + push.BURN_NOTE_ROOT swapw + # => [SERIAL_NUM, SCRIPT_ROOT] + + push.NUM_BURN_NOTE_INPUTS push.0 + # => [inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT] + + exec.note::build_recipient + # => [RECIPIENT] + + push.EXECUTION_HINT_ALWAYS + push.PUBLIC_NOTE + push.AUX + loc_load.5 + # => [tag, aux, note_type, execution_hint, RECIPIENT] + + call.output_note::create + # => [note_idx] + + movdn.4 loc_loadw_be.0 + # => [ASSET, note_idx] + + exec.output_note::add_asset + # => [] +end + +#! Bridges an asset out via the AggLayer +#! +#! This procedure handles the complete bridge-out operation, including: +#! - Converting asset data to u32 format +#! - Computing Keccak hash of the data +#! - Adding the hash to the MMR frontier +#! - Storing the updated MMR root in account storage +#! - Creating a BURN note with the bridged out asset +#! +#! Inputs: [ASSET, dest_network, dest_address(5)] +#! Outputs: [] +#! +#! Where: +#! - ASSET is the asset to be bridged out. +#! - dest_network is the u32 destination network/chain ID. +#! - dest_address(5) are 5 u32 values representing a 20-byte Ethereum address. +#! +#! Invocation: call +pub proc bridge_out + mem_storew_be.BURN_ASSET_MEM_PTR + # => [ASSET, dest_network, dest_address(5)] + + # @dev TODO: Look up asset faucet id in asset registry + # -> return scaling factor + + # @dev TODO: Convert ASSET amount to EVM amount using scaling factor + # -> return amount from here: https://github.com/0xMiden/miden-base/pull/2141 + + # Converting SCALED_ASSET, dest_network, dest_address(5) to u32 representation + # in preparation for keccak256 hashing + + # keccak256 inputs: + # => [ASSET, dest_network, dest_address(5)] + # TODO we should convert Miden->Ethereum asset values, incl. amount conversion etc. + + # TODO: make building bridge message a separate procedure + # TODO: match Agglayer addLeafBridge logic + # TODO: convert Miden asset amount to Ethereum amount + # Store ASSET as u32 limbs in memory starting at address 0 + push.0 movdn.4 exec.word::store_word_u32s_le + # => [dest_network, dest_address(5)] + + # Store [dest_network, dest_address[0..3]] as u32 limbs in memory starting at address 8 + push.8 movdn.4 exec.word::store_word_u32s_le + # => [dest_address(2), 0, 0] + + # Store [dest_address[3..5], 0, 0] as u32 limbs in memory starting at address 16 + push.16 movdn.4 exec.word::store_word_u32s_le + # => [] + + # 1 u32 = 4 bytes + # 10 u32 values = 40 bytes + push.40 push.0 + # => [ptr, len_bytes] + + exec.keccak256::hash_bytes + # => [DIGEST_U32[8]] + + # adding DIGEST_U32 double word leaf to mmr frontier + exec.local_exit_tree::add_asset_message + # => [] + + # creating BURN output note for ASSET + mem_loadw_be.BURN_ASSET_MEM_PTR + # => [ASSET] + + exec.create_burn_note + # => [] +end diff --git a/crates/miden-agglayer/asm/account_components/local_exit_tree.masm b/crates/miden-agglayer/asm/account_components/local_exit_tree.masm new file mode 100644 index 0000000000..9e764db80a --- /dev/null +++ b/crates/miden-agglayer/asm/account_components/local_exit_tree.masm @@ -0,0 +1,120 @@ +use miden::standards::wallets::basic->wallet +use miden::protocol::active_account +use miden::protocol::native_account + +# CONSTANTS +# ================================================================================================= + +const MMR_PTR=42 +const LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") + +#! Adds a leaf to the MMR frontier using Keccak hashing (stubbed implementation). +#! +#! This is a stubbed implementation that currently drops all inputs without performing +#! the actual MMR frontier addition operation. +#! +#! Inputs: [LEAF[1], LEAF[0], mmr_ptr] +#! Outputs: [] +#! +#! Where: +#! - LEAF[1], LEAF[0] are the leaf data to add to the MMR frontier. +#! - mmr_ptr is the pointer to the MMR frontier data structure. +#! +#! Invocation: exec +proc mmr_frontier_keccak_add + dropw dropw drop + # => [] +end + +#! Gets the root of the MMR frontier using Keccak hashing (stubbed implementation). +#! +#! This is a stubbed implementation that returns placeholder values instead of +#! computing the actual MMR frontier root. +#! +#! Inputs: [mmr_ptr] +#! Outputs: [ROOT[1], ROOT[0]] +#! +#! Where: +#! - ROOT[1], ROOT[0] are the root hash components of the MMR frontier whose memory location starts at mmr_ptr +#! +#! Invocation: exec +pub proc mmr_frontier_keccak_get_root + # stubbed out for now + drop + # => [] + + push.0.0.0.1 push.LOCAL_EXIT_TREE_SLOT[0..2] + # => [slot_id_prefix, slot_id_suffix, KEY] + + exec.active_account::get_map_item + # => [ROOT[0]] + + push.0.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] + # => [slot_id_prefix, slot_id_suffix, KEY, ROOT[0]] + + exec.active_account::get_map_item + # => [ROOT[1], ROOT[0]] +end + +#! Writes the MMR frontier root to account storage. +#! +#! This procedure retrieves the current MMR frontier root and stores it as a double word +#! in the account's storage map. The root is split across two storage keys: +#! - Key [0,0,0,0] stores ROOT[1] (high part) +#! - Key [0,0,0,1] stores ROOT[0] (low part) +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Invocation: exec +proc write_mmr_frontier_root + push.MMR_PTR + # => [MMR_PTR] + + # getting mmr frontier root + exec.mmr_frontier_keccak_get_root + # => [ROOT[1], ROOT[0]] + + # writing double word root to map keys [0,0,0,0] & [0,0,0,1] + push.0.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] + # => [index, KEY, ROOT[1], ROOT[0]] + + exec.native_account::set_map_item + # => [OLD_MAP_ROOT, OLD_MAP_VALUE, ROOT[0]] + + dropw dropw + # => [ROOT[0]] + + push.1.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] + # => [index, KEY, ROOT[0]] + + exec.native_account::set_map_item + # => [OLD_MAP_ROOT, OLD_MAP_VALUE] + + dropw dropw + # => [] +end + +#! Adds an asset message to the MMR frontier and updates the stored root. +#! +#! This procedure takes a Keccak digest (represented as 8 u32 values) and adds it +#! as a leaf to the MMR frontier. After adding the leaf, it updates the MMR root +#! in the account's storage to reflect the new state. +#! +#! Inputs: [DIGEST_U32[8]] +#! Outputs: [] +#! +#! Where: +#! - DIGEST_U32[8] is a Keccak256 hash represented as 8 u32 values (256 bits total). +#! +#! Invocation: exec +pub proc add_asset_message + push.MMR_PTR movdn.8 + # => [LEAF[1], LEAF[0], mmr_ptr] + + exec.mmr_frontier_keccak_add + # => [] + + exec.write_mmr_frontier_root + # => [] +end \ No newline at end of file diff --git a/crates/miden-agglayer/asm/note_scripts/B2AGG.masm b/crates/miden-agglayer/asm/note_scripts/B2AGG.masm new file mode 100644 index 0000000000..01865da95a --- /dev/null +++ b/crates/miden-agglayer/asm/note_scripts/B2AGG.masm @@ -0,0 +1,88 @@ +use agglayer::bridge_out +use miden::protocol::account_id +use miden::protocol::active_account +use miden::protocol::active_note +use miden::standards::wallets::basic->basic_wallet + +# CONSTANTS +# ================================================================================================= + +const B2AGG_NOTE_INPUTS_COUNT=6 + +# ERRORS +# ================================================================================================= +const ERR_B2AGG_WRONG_NUMBER_OF_ASSETS="B2AGG script requires exactly 1 note asset" + +const ERR_B2AGG_WRONG_NUMBER_OF_INPUTS="B2AGG script expects exactly 6 note inputs" + +#! Bridge-to-AggLayer (B2AGG) note script: bridges assets from Miden to an AggLayer-connected chain. +#! +#! This note can be consumed in two ways: +#! - If the consuming account is the sender (reclaim): the note's assets are added back to the consuming account. +#! - If the consuming account is the Agglayer Bridge: the note's assets are moved to a BURN note, +#! and the note details are hashed into a leaf and appended to the Local Exit Tree. +#! global exit root (GER) merkle tree structure. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Note inputs are assumed to be as follows: +#! - destination_network: u32 value representing the target chain ID +#! - destination_address: split into 5 u32 values representing a 20-byte Ethereum address: +#! - destination_address_0: bytes 0-3 +#! - destination_address_1: bytes 4-7 +#! - destination_address_2: bytes 8-11 +#! - destination_address_3: bytes 12-15 +#! - destination_address_4: bytes 16-19 +#! +#! Panics if: +#! - The note does not contain exactly 6 inputs. +#! - The note does not contain exactly 1 asset. +#! +begin + dropw + # => [pad(16)] + + # Check if reclaim + exec.active_account::get_id + # => [account_id_prefix, account_id_suffix, pad(16)] + + exec.active_note::get_sender + # => [sender_id_prefix, sender_id_suffix, account_id_prefix, account_id_suffix, pad(16)] + + exec.account_id::is_equal + # => [reclaim, pad(16)] + + # B2AGG note is being reclaimed; adding note assets to account + if.true + exec.basic_wallet::add_assets_to_account + # => [pad(16)] + else + # Store note inputs -> mem[8..14] + push.8 exec.active_note::get_inputs + # => [num_inputs, dest_ptr, pad(16)] + + push.B2AGG_NOTE_INPUTS_COUNT assert_eq.err=ERR_B2AGG_WRONG_NUMBER_OF_INPUTS drop + # => [pad(16)] + + # Store note assets -> mem[0..4] + push.0 exec.active_note::get_assets + # => [num_assets, ptr, pad(16)] + + # Must be exactly 1 asset + push.1 assert_eq.err=ERR_B2AGG_WRONG_NUMBER_OF_ASSETS drop + # => [pad(16)] + + # load the 6 B2AGG note input felts as two words + mem_loadw_be.12 swapw.2 mem_loadw_be.8 swapw + # => [EMPTY_WORD, dest_network, dest_address(5), pad(6)] + + # Load ASSET onto the stack + mem_loadw_be.0 + # => [ASSET, dest_network, dest_address(5), pad(6)] + + call.bridge_out::bridge_out + # => [pad(16)] + end + # => [pad(16)] +end diff --git a/crates/miden-agglayer/build.rs b/crates/miden-agglayer/build.rs new file mode 100644 index 0000000000..a64b6fde8f --- /dev/null +++ b/crates/miden-agglayer/build.rs @@ -0,0 +1,477 @@ +use std::env; +use std::path::Path; + +use fs_err as fs; +use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr}; +use miden_assembly::utils::Serializable; +use miden_assembly::{Assembler, Library, Report}; +use miden_protocol::transaction::TransactionKernel; + +// CONSTANTS +// ================================================================================================ + +/// Defines whether the build script should generate files in `/src`. +/// The docs.rs build pipeline has a read-only filesystem, so we have to avoid writing to `src`, +/// otherwise the docs will fail to build there. Note that writing to `OUT_DIR` is fine. +const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN_SRC").is_some(); + +const ASSETS_DIR: &str = "assets"; +const ASM_DIR: &str = "asm"; +const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts"; +const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components"; + +const AGGLAYER_ERRORS_FILE: &str = "src/errors/agglayer.rs"; +const AGGLAYER_ERRORS_ARRAY_NAME: &str = "AGGLAYER_ERRORS"; + +// PRE-PROCESSING +// ================================================================================================ + +/// Read and parse the contents from `./asm`. +/// - Compiles the contents of asm/note_scripts directory into individual .masb files. +/// - Compiles the contents of asm/account_components directory into individual .masl files. +fn main() -> Result<()> { + // re-build when the MASM code changes + println!("cargo::rerun-if-changed={ASM_DIR}/"); + println!("cargo::rerun-if-env-changed=BUILD_GENERATED_FILES_IN_SRC"); + + // Copies the MASM code to the build directory + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let build_dir = env::var("OUT_DIR").unwrap(); + let src = Path::new(&crate_dir).join(ASM_DIR); + let dst = Path::new(&build_dir).to_path_buf(); + shared::copy_directory(src, &dst, ASM_DIR)?; + + // set source directory to {OUT_DIR}/asm + let source_dir = dst.join(ASM_DIR); + + // set target directory to {OUT_DIR}/assets + let target_dir = Path::new(&build_dir).join(ASSETS_DIR); + + let mut assembler = TransactionKernel::assembler(); + + // compile account components first and add the library to the assembler + let agglayer_lib = compile_account_components( + &source_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), + &target_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), + assembler.clone(), + )?; + + // Add the agglayer library to the assembler for note scripts compilation + assembler.link_static_library(agglayer_lib)?; + + // compile note scripts + compile_note_scripts( + &source_dir.join(ASM_NOTE_SCRIPTS_DIR), + &target_dir.join(ASM_NOTE_SCRIPTS_DIR), + assembler.clone(), + )?; + + generate_error_constants(&source_dir)?; + + Ok(()) +} + +// COMPILE EXECUTABLE MODULES +// ================================================================================================ + +/// Reads all MASM files from the "{source_dir}", complies each file individually into a MASB +/// file, and stores the compiled files into the "{target_dir}". +/// +/// The source files are expected to contain executable programs. +fn compile_note_scripts( + source_dir: &Path, + target_dir: &Path, + mut assembler: Assembler, +) -> Result<()> { + fs::create_dir_all(target_dir) + .into_diagnostic() + .wrap_err("failed to create note_scripts directory")?; + + // Add the miden-standards library to the assembler so note scripts can use it + let standards_lib = miden_standards::StandardsLib::default(); + assembler.link_static_library(standards_lib)?; + + for masm_file_path in shared::get_masm_files(source_dir).unwrap() { + // read the MASM file, parse it, and serialize the parsed AST to bytes + let code = assembler.clone().assemble_program(masm_file_path.clone())?; + + let bytes = code.to_bytes(); + + let masm_file_name = masm_file_path + .file_name() + .expect("file name should exist") + .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); + + // write the binary MASB to the output dir + masb_file_path.set_extension("masb"); + fs::write(masb_file_path, bytes).unwrap(); + } + Ok(()) +} + +// COMPILE ACCOUNT COMPONENTS +// ================================================================================================ + +/// Compiles the account components in `source_dir` into MASL libraries and stores the compiled +/// files in `target_dir`. +fn compile_account_components( + source_dir: &Path, + target_dir: &Path, + mut assembler: Assembler, +) -> Result { + if !target_dir.exists() { + fs::create_dir_all(target_dir).unwrap(); + } + + // Add the miden-standards library to the assembler so agglayer components can use it + let standards_lib = miden_standards::StandardsLib::default(); + assembler.link_static_library(standards_lib)?; + + // Compile all components together as a single library under the "agglayer" namespace + // This allows cross-references between components (e.g., bridge_out using + // agglayer::local_exit_tree) + let agglayer_library = assembler + .assemble_library_from_dir(source_dir, "agglayer") + .expect("library assembly should succeed"); + + // Write the combined library + let library_path = target_dir.join("agglayer").with_extension(Library::LIBRARY_EXTENSION); + agglayer_library.write_to_file(library_path).into_diagnostic()?; + + // Also write individual component files for reference + let masm_files = shared::get_masm_files(source_dir).unwrap(); + for masm_file_path in &masm_files { + let component_name = masm_file_path + .file_stem() + .expect("masm file should have a file stem") + .to_str() + .expect("file stem should be valid UTF-8") + .to_owned(); + + let component_source_code = fs::read_to_string(masm_file_path) + .expect("reading the component's MASM source code should succeed"); + + let individual_file_path = target_dir.join(&component_name).with_extension("masm"); + fs::write(individual_file_path, component_source_code).into_diagnostic()?; + } + + Ok(agglayer_library) +} + +// ERROR CONSTANTS FILE GENERATION +// ================================================================================================ + +/// Reads all MASM files from the `asm_source_dir` and extracts its error constants and their +/// associated error message and generates a Rust file for each category of errors. +/// For example: +/// +/// ```text +/// const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" +/// ``` +/// +/// would generate a Rust file for transaction kernel errors (since the error belongs to that +/// category, identified by the category extracted from `ERR_`) with - roughly - the +/// following content: +/// +/// ```rust +/// pub const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY: MasmError = +/// MasmError::from_static_str("new account must have an empty vault"); +/// ``` +/// +/// and add the constant to the error constants array. +/// +/// The function ensures that a constant is not defined twice, except if their error message is the +/// same. This can happen across multiple files. +/// +/// Because the error files will be written to ./src/errors, this should be a no-op if ./src is +/// read-only. To enable writing to ./src, set the `BUILD_GENERATED_FILES_IN_SRC` environment +/// variable. +fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { + if !BUILD_GENERATED_FILES_IN_SRC { + return Ok(()); + } + + // Miden agglayer errors + // ------------------------------------------ + + let errors = shared::extract_all_masm_errors(asm_source_dir) + .context("failed to extract all masm errors")?; + shared::generate_error_file( + shared::ErrorModule { + file_name: AGGLAYER_ERRORS_FILE, + array_name: AGGLAYER_ERRORS_ARRAY_NAME, + is_crate_local: false, + }, + errors, + )?; + + Ok(()) +} + +/// This module should be kept in sync with the copy in miden-protocol's and miden-standards' +/// build.rs. +mod shared { + use std::collections::BTreeMap; + use std::fmt::Write; + use std::io::{self}; + use std::path::{Path, PathBuf}; + + use fs_err as fs; + use miden_assembly::Report; + use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr}; + use regex::Regex; + use walkdir::WalkDir; + + /// Recursively copies `src` into `dst`. + /// + /// This function will overwrite the existing files if re-executed. + pub fn copy_directory, R: AsRef>( + src: T, + dst: R, + asm_dir: &str, + ) -> Result<()> { + let mut prefix = src.as_ref().canonicalize().unwrap(); + // keep all the files inside the `asm` folder + prefix.pop(); + + let target_dir = dst.as_ref().join(asm_dir); + if target_dir.exists() { + // Clear existing asm files that were copied earlier which may no longer exist. + fs::remove_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to remove ASM directory")?; + } + + // Recreate the directory structure. + fs::create_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to create ASM directory")?; + + let dst = dst.as_ref(); + let mut todo = vec![src.as_ref().to_path_buf()]; + + while let Some(goal) = todo.pop() { + for entry in fs::read_dir(goal).unwrap() { + let path = entry.unwrap().path(); + if path.is_dir() { + let src_dir = path.canonicalize().unwrap(); + let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap()); + if !dst_dir.exists() { + fs::create_dir_all(&dst_dir).unwrap(); + } + todo.push(src_dir); + } else { + let dst_file = dst.join(path.strip_prefix(&prefix).unwrap()); + fs::copy(&path, dst_file).unwrap(); + } + } + } + + Ok(()) + } + + /// Returns a vector with paths to all MASM files in the specified directory. + /// + /// All non-MASM files are skipped. + pub fn get_masm_files>(dir_path: P) -> Result> { + let mut files = Vec::new(); + + let path = dir_path.as_ref(); + if path.is_dir() { + let entries = fs::read_dir(path) + .into_diagnostic() + .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; + for entry in entries { + let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; + let file_path = file.path(); + if is_masm_file(&file_path).into_diagnostic()? { + files.push(file_path); + } + } + } else { + println!("cargo:warn=The specified path is not a directory."); + } + + Ok(files) + } + + /// Returns true if the provided path resolves to a file with `.masm` extension. + /// + /// # Errors + /// Returns an error if the path could not be converted to a UTF-8 string. + pub fn is_masm_file(path: &Path) -> io::Result { + if let Some(extension) = path.extension() { + let extension = extension + .to_str() + .ok_or_else(|| io::Error::other("invalid UTF-8 filename"))? + .to_lowercase(); + Ok(extension == "masm") + } else { + Ok(false) + } + } + + /// Extract all masm errors from the given path and returns a map by error category. + pub fn extract_all_masm_errors(asm_source_dir: &Path) -> Result> { + // We use a BTree here to order the errors by their categories which is the first part after + // the ERR_ prefix and to allow for the same error to be defined multiple times in + // different files (as long as the constant name and error messages match). + let mut errors = BTreeMap::new(); + + // Walk all files of the kernel source directory. + for entry in WalkDir::new(asm_source_dir) { + let entry = entry.into_diagnostic()?; + if !is_masm_file(entry.path()).into_diagnostic()? { + continue; + } + let file_contents = std::fs::read_to_string(entry.path()).into_diagnostic()?; + extract_masm_errors(&mut errors, &file_contents)?; + } + + let errors = errors + .into_iter() + .map(|(error_name, error)| NamedError { name: error_name, message: error.message }) + .collect(); + + Ok(errors) + } + + /// Extracts the errors from a single masm file and inserts them into the provided map. + pub fn extract_masm_errors( + errors: &mut BTreeMap, + file_contents: &str, + ) -> Result<()> { + let regex = Regex::new(r#"const\s*ERR_(?.*)\s*=\s*"(?.*)""#).unwrap(); + + for capture in regex.captures_iter(file_contents) { + let error_name = capture + .name("name") + .expect("error name should be captured") + .as_str() + .trim() + .to_owned(); + let error_message = capture + .name("message") + .expect("error code should be captured") + .as_str() + .trim() + .to_owned(); + + if let Some(ExtractedError { message: existing_error_message, .. }) = + errors.get(&error_name) + && existing_error_message != &error_message + { + return Err(Report::msg(format!( + "Transaction kernel error constant ERR_{error_name} is already defined elsewhere but its error message is different" + ))); + } + + // Enforce the "no trailing punctuation" rule from the Rust error guidelines on MASM + // errors. + if error_message.ends_with(".") { + return Err(Report::msg(format!( + "Error messages should not end with a period: `ERR_{error_name}: {error_message}`" + ))); + } + + errors.insert(error_name, ExtractedError { message: error_message }); + } + + Ok(()) + } + + pub fn is_new_error_category<'a>( + last_error: &mut Option<&'a str>, + current_error: &'a str, + ) -> bool { + let is_new = match last_error { + Some(last_err) => { + let last_category = + last_err.split("_").next().expect("there should be at least one entry"); + let new_category = + current_error.split("_").next().expect("there should be at least one entry"); + last_category != new_category + }, + None => false, + }; + + last_error.replace(current_error); + + is_new + } + + /// Generates the content of an error file for the given category and the set of errors and + /// writes it to the category's file. + pub fn generate_error_file(module: ErrorModule, errors: Vec) -> Result<()> { + let mut output = String::new(); + + if module.is_crate_local { + writeln!(output, "use crate::errors::MasmError;\n").unwrap(); + } else { + writeln!(output, "use miden_protocol::errors::MasmError;\n").unwrap(); + } + + writeln!( + output, + "// This file is generated by build.rs, do not modify manually. +// It is generated by extracting errors from the MASM files in the `./asm` directory. +// +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). +" + ) + .unwrap(); + + writeln!( + output, + "// {} +// ================================================================================================ +", + module.array_name.replace("_", " ") + ) + .unwrap(); + + let mut last_error = None; + for named_error in errors.iter() { + let NamedError { name, message } = named_error; + + // Group errors into blocks separate by newlines. + if is_new_error_category(&mut last_error, name) { + writeln!(output).into_diagnostic()?; + } + + writeln!(output, "/// Error Message: \"{message}\"").into_diagnostic()?; + writeln!( + output, + r#"pub const ERR_{name}: MasmError = MasmError::from_static_str("{message}");"# + ) + .into_diagnostic()?; + } + + std::fs::write(module.file_name, output).into_diagnostic()?; + + Ok(()) + } + + pub type ErrorName = String; + + #[derive(Debug, Clone)] + pub struct ExtractedError { + pub message: String, + } + + #[derive(Debug, Clone)] + pub struct NamedError { + pub name: ErrorName, + pub message: String, + } + + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + pub struct ErrorModule { + pub file_name: &'static str, + pub array_name: &'static str, + pub is_crate_local: bool, + } +} diff --git a/crates/miden-agglayer/src/errors/agglayer.rs b/crates/miden-agglayer/src/errors/agglayer.rs new file mode 100644 index 0000000000..d9ec4a3875 --- /dev/null +++ b/crates/miden-agglayer/src/errors/agglayer.rs @@ -0,0 +1,18 @@ +use miden_protocol::errors::MasmError; + +// This file is generated by build.rs, do not modify manually. +// It is generated by extracting errors from the MASM files in the `./asm` directory. +// +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). + +// AGGLAYER ERRORS +// ================================================================================================ + +/// Error Message: "B2AGG script requires exactly 1 note asset" +pub const ERR_B2AGG_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("B2AGG script requires exactly 1 note asset"); +/// Error Message: "B2AGG script expects exactly 6 note inputs" +pub const ERR_B2AGG_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("B2AGG script expects exactly 6 note inputs"); + +/// Error Message: "maximum scaling factor is 18" +pub const ERR_SCALE_AMOUNT_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("maximum scaling factor is 18"); diff --git a/crates/miden-agglayer/src/errors/mod.rs b/crates/miden-agglayer/src/errors/mod.rs new file mode 100644 index 0000000000..18bd2e1e39 --- /dev/null +++ b/crates/miden-agglayer/src/errors/mod.rs @@ -0,0 +1,2 @@ +// Include generated error constants +include!("agglayer.rs"); diff --git a/crates/miden-agglayer/src/lib.rs b/crates/miden-agglayer/src/lib.rs new file mode 100644 index 0000000000..abf35d85d0 --- /dev/null +++ b/crates/miden-agglayer/src/lib.rs @@ -0,0 +1,111 @@ +#![no_std] + +extern crate alloc; + +use alloc::vec; +use alloc::vec::Vec; + +use miden_assembly::Library; +use miden_assembly::utils::Deserializable; +use miden_core::Program; +use miden_protocol::account::{AccountComponent, StorageSlot}; +use miden_utils_sync::LazyLock; + +pub mod errors; +pub mod utils; + +// AGGLAYER NOTE SCRIPTS +// ================================================================================================ + +// Initialize the B2AGG note script only once +static B2AGG_SCRIPT: LazyLock = LazyLock::new(|| { + let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/note_scripts/B2AGG.masb")); + Program::read_from_bytes(bytes).expect("Shipped B2AGG script is well-formed") +}); + +/// Returns the B2AGG (Bridge to AggLayer) note script. +pub fn b2agg_script() -> Program { + B2AGG_SCRIPT.clone() +} + +// AGGLAYER ACCOUNT COMPONENTS +// ================================================================================================ + +// Initialize the unified AggLayer library only once +static AGGLAYER_LIBRARY: LazyLock = LazyLock::new(|| { + let bytes = + include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/agglayer.masl")); + Library::read_from_bytes(bytes).expect("Shipped AggLayer library is well-formed") +}); + +/// Returns the unified AggLayer Library containing all agglayer modules. +pub fn agglayer_library() -> Library { + AGGLAYER_LIBRARY.clone() +} + +/// Returns the Bridge Out Library. +/// +/// Note: This is now the same as agglayer_library() since all agglayer components +/// are compiled into a single library. +pub fn bridge_out_library() -> Library { + agglayer_library() +} + +/// Returns the Local Exit Tree Library. +/// +/// Note: This is now the same as agglayer_library() since all agglayer components +/// are compiled into a single library. +pub fn local_exit_tree_library() -> Library { + agglayer_library() +} + +/// Creates a Local Exit Tree component with the specified storage slots. +/// +/// This component uses the local_exit_tree library and can be added to accounts +/// that need to manage local exit tree functionality. +pub fn local_exit_tree_component(storage_slots: Vec) -> AccountComponent { + let library = local_exit_tree_library(); + + AccountComponent::new(library, storage_slots) + .expect("local_exit_tree component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} + +/// Creates a Bridge Out component with the specified storage slots. +/// +/// This component uses the bridge_out library and can be added to accounts +/// that need to bridge assets out to the AggLayer. +pub fn bridge_out_component(storage_slots: Vec) -> AccountComponent { + let library = bridge_out_library(); + + AccountComponent::new(library, storage_slots) + .expect("bridge_out component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} + +/// Creates a combined Bridge Out component that includes both bridge_out and local_exit_tree +/// modules. +/// +/// This is a convenience function that creates a component with multiple modules. +/// For more fine-grained control, use the individual component functions and combine them +/// using the AccountBuilder pattern. +pub fn bridge_out_with_local_exit_tree_component( + storage_slots: Vec, +) -> Vec { + vec![ + bridge_out_component(storage_slots.clone()), + local_exit_tree_component(vec![]), // local_exit_tree typically doesn't need storage slots + ] +} + +/// Creates an Asset Conversion component with the specified storage slots. +/// +/// This component uses the agglayer library (which includes asset_conversion) and can be added to +/// accounts that need to convert assets between Miden and Ethereum formats. +pub fn asset_conversion_component(storage_slots: Vec) -> AccountComponent { + let library = agglayer_library(); + + AccountComponent::new(library, storage_slots) + .expect("asset_conversion component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} diff --git a/crates/miden-agglayer/src/mod.rs b/crates/miden-agglayer/src/mod.rs new file mode 100644 index 0000000000..be1f75cab5 --- /dev/null +++ b/crates/miden-agglayer/src/mod.rs @@ -0,0 +1,121 @@ +use alloc::vec::Vec; + +use miden_objects::account::{AccountComponent, StorageSlot}; +use miden_objects::assembly::Library; +use miden_objects::note::NoteScript; +use miden_objects::utils::Deserializable; +use miden_objects::utils::sync::LazyLock; +use miden_objects::vm::Program; + +pub mod utils; + +// AGGLAYER NOTE SCRIPTS +// ================================================================================================ + +// Initialize the B2AGG note script only once +static B2AGG_SCRIPT: LazyLock = LazyLock::new(|| { + let bytes = + include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer/note_scripts/B2AGG.masb")); + let program = Program::read_from_bytes(bytes).expect("Shipped B2AGG script is well-formed"); + NoteScript::new(program) +}); + +/// Returns the B2AGG (Bridge to AggLayer) note script. +pub fn b2agg_script() -> NoteScript { + B2AGG_SCRIPT.clone() +} + +// AGGLAYER ACCOUNT COMPONENTS +// ================================================================================================ + +// Initialize the unified AggLayer library only once +static AGGLAYER_LIBRARY: LazyLock = LazyLock::new(|| { + let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer.masl")); + Library::read_from_bytes(bytes).expect("Shipped AggLayer library is well-formed") +}); + +/// Returns the unified AggLayer Library containing all agglayer modules. +pub fn agglayer_library() -> Library { + AGGLAYER_LIBRARY.clone() +} + +/// Returns the Bridge Out Library. +/// +/// Note: This is now the same as agglayer_library() since all agglayer components +/// are compiled into a single library. +pub fn bridge_out_library() -> Library { + agglayer_library() +} + +/// Returns the Local Exit Tree Library. +/// +/// Note: This is now the same as agglayer_library() since all agglayer components +/// are compiled into a single library. +pub fn local_exit_tree_library() -> Library { + agglayer_library() +} + +/// Creates a Local Exit Tree component with the specified storage slots. +/// +/// This component uses the local_exit_tree library and can be added to accounts +/// that need to manage local exit tree functionality. +pub fn local_exit_tree_component(storage_slots: Vec) -> AccountComponent { + let library = local_exit_tree_library(); + + AccountComponent::new(library, storage_slots) + .expect("local_exit_tree component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} + +/// Creates a Bridge Out component with the specified storage slots. +/// +/// This component uses the bridge_out library and can be added to accounts +/// that need to bridge assets out to the AggLayer. +pub fn bridge_out_component(storage_slots: Vec) -> AccountComponent { + let library = bridge_out_library(); + + AccountComponent::new(library, storage_slots) + .expect("bridge_out component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} + +/// Creates a combined Bridge Out component that includes both bridge_out and local_exit_tree +/// modules. +/// +/// This is a convenience function that creates a component with multiple modules. +/// For more fine-grained control, use the individual component functions and combine them +/// using the AccountBuilder pattern. +pub fn bridge_out_with_local_exit_tree_component( + storage_slots: Vec, +) -> Vec { + vec![ + bridge_out_component(storage_slots.clone()), + local_exit_tree_component(vec![]), // local_exit_tree typically doesn't need storage slots + ] +} + +// Initialize the Asset Conversion library only once +static ASSET_CONVERSION_LIBRARY: LazyLock = LazyLock::new(|| { + let bytes = include_bytes!(concat!( + env!("OUT_DIR"), + "/assets/agglayer/account_components/asset_conversion.masl" + )); + Library::read_from_bytes(bytes).expect("Shipped Asset Conversion library is well-formed") +}); + +/// Returns the Asset Conversion Library. +pub fn asset_conversion_library() -> Library { + ASSET_CONVERSION_LIBRARY.clone() +} + +/// Creates an Asset Conversion component with the specified storage slots. +/// +/// This component uses the asset_conversion library and can be added to accounts +/// that need to convert assets between Miden and Ethereum formats. +pub fn asset_conversion_component(storage_slots: Vec) -> AccountComponent { + let library = asset_conversion_library(); + + AccountComponent::new(library, storage_slots) + .expect("asset_conversion component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} diff --git a/crates/miden-agglayer/src/utils.rs b/crates/miden-agglayer/src/utils.rs new file mode 100644 index 0000000000..e9d616d928 --- /dev/null +++ b/crates/miden-agglayer/src/utils.rs @@ -0,0 +1,175 @@ +use alloc::string::String; +use alloc::vec::Vec; + +use miden_core::Felt; + +/// Convert 8 Felt values (u32 limbs in little-endian order) to U256 bytes in little-endian format. +/// +/// The input limbs are expected to be in little-endian order (least significant limb first). +/// This function converts them to a 32-byte array in little-endian format for compatibility +/// with Ethereum/EVM which expects U256 values as 32 bytes in little-endian format. +/// This ensures compatibility when bridging assets between Miden and Ethereum-based chains. +pub fn felts_to_u256_bytes(limbs: [Felt; 8]) -> [u8; 32] { + let mut bytes = [0u8; 32]; + + for (i, limb) in limbs.iter().enumerate() { + let u32_value = limb.as_int() as u32; + let limb_bytes = u32_value.to_le_bytes(); + bytes[i * 4..(i + 1) * 4].copy_from_slice(&limb_bytes); + } + + bytes +} +/// Converts an Ethereum address (20 bytes) into a vector of 5 Felt values. +/// +/// An Ethereum address is 20 bytes, which we split into 5 u32 values (4 bytes each). +/// The address bytes are distributed as follows: +/// - u32\[0\]: bytes 0-3 +/// - u32\[1\]: bytes 4-7 +/// - u32\[2\]: bytes 8-11 +/// - u32\[3\]: bytes 12-15 +/// - u32\[4\]: bytes 16-19 +/// +/// # Arguments +/// * `address` - A 20-byte Ethereum address +/// +/// # Returns +/// A vector of 5 Felt values representing the address +/// +/// # Panics +/// Panics if the address is not exactly 20 bytes +pub fn ethereum_address_to_felts(address: &[u8; 20]) -> Vec { + let mut result = Vec::with_capacity(5); + + // Convert each 4-byte chunk to a u32 (big-endian) + for i in 0..5 { + let start = i * 4; + let chunk = &address[start..start + 4]; + let value = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + result.push(Felt::new(value as u64)); + } + + result +} + +/// Converts a vector of 5 Felt values back into a 20-byte Ethereum address. +/// +/// # Arguments +/// * `felts` - A vector of 5 Felt values representing an Ethereum address +/// +/// # Returns +/// A Result containing a 20-byte Ethereum address array, or an error string +/// +/// # Errors +/// Returns an error if the vector doesn't contain exactly 5 felts +pub fn felts_to_ethereum_address(felts: &[Felt]) -> Result<[u8; 20], String> { + if felts.len() != 5 { + return Err(alloc::format!("Expected 5 felts for Ethereum address, got {}", felts.len())); + } + + let mut address = [0u8; 20]; + + for (i, felt) in felts.iter().enumerate() { + let value = felt.as_int() as u32; + let bytes = value.to_be_bytes(); + let start = i * 4; + address[start..start + 4].copy_from_slice(&bytes); + } + + Ok(address) +} + +/// Converts an Ethereum address string (with or without "0x" prefix) into a vector of 5 Felt +/// values. +/// +/// # Arguments +/// * `address_str` - A hex string representing an Ethereum address (40 hex chars, optionally +/// prefixed with "0x") +/// +/// # Returns +/// A Result containing a vector of 5 Felt values representing the address, or an error string +/// +/// # Errors +/// Returns an error if: +/// - The string is not a valid hex string +/// - The string does not represent exactly 20 bytes (40 hex characters) +pub fn ethereum_address_string_to_felts(address_str: &str) -> Result, String> { + // Remove "0x" prefix if present + let hex_str = address_str.strip_prefix("0x").unwrap_or(address_str); + + // Check length (should be 40 hex characters for 20 bytes) + if hex_str.len() != 40 { + return Err(alloc::format!( + "Invalid Ethereum address length: expected 40 hex characters, got {}", + hex_str.len() + )); + } + + // Parse hex string to bytes + let mut address_bytes = [0u8; 20]; + for (i, chunk) in hex_str.as_bytes().chunks(2).enumerate() { + let hex_byte = core::str::from_utf8(chunk) + .map_err(|_| String::from("Invalid UTF-8 in address string"))?; + address_bytes[i] = u8::from_str_radix(hex_byte, 16) + .map_err(|_| alloc::format!("Invalid hex character in address: {}", hex_byte))?; + } + + Ok(ethereum_address_to_felts(&address_bytes)) +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use super::*; + + #[test] + fn test_ethereum_address_round_trip() { + // Test that converting from string to felts and back gives the same result + let original_address = "0x1234567890abcdef1122334455667788990011aa"; + + // Convert string to felts + let felts = ethereum_address_string_to_felts(original_address).unwrap(); + + // Convert felts back to bytes + let recovered_bytes = felts_to_ethereum_address(&felts).unwrap(); + + // Convert original string to bytes for comparison + let original_hex = original_address.strip_prefix("0x").unwrap(); + let mut expected_bytes = [0u8; 20]; + for (i, chunk) in original_hex.as_bytes().chunks(2).enumerate() { + let hex_byte = core::str::from_utf8(chunk).unwrap(); + expected_bytes[i] = u8::from_str_radix(hex_byte, 16).unwrap(); + } + + // Assert they match + assert_eq!(recovered_bytes, expected_bytes); + } + + #[test] + fn test_ethereum_address_to_felts_basic() { + let address: [u8; 20] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, + ]; + + let result = ethereum_address_to_felts(&address); + assert_eq!(result.len(), 5); + assert_eq!(result[0], Felt::new(0x12345678)); + assert_eq!(result[1], Felt::new(0x9abcdef0)); + } + + #[test] + fn test_felts_to_ethereum_address_invalid_length() { + let felts = vec![Felt::new(1), Felt::new(2)]; // Only 2 felts + let result = felts_to_ethereum_address(&felts); + assert!(result.is_err()); + } + + #[test] + fn test_ethereum_address_string_invalid_length() { + let address_str = "0x123456"; // Too short + let result = ethereum_address_string_to_felts(address_str); + assert!(result.is_err()); + } +} diff --git a/crates/miden-block-prover/Cargo.toml b/crates/miden-block-prover/Cargo.toml index ed9bb534ff..be8732f5d7 100644 --- a/crates/miden-block-prover/Cargo.toml +++ b/crates/miden-block-prover/Cargo.toml @@ -19,5 +19,5 @@ bench = false testing = [] [dependencies] -miden-objects = { workspace = true } -thiserror = { workspace = true } +miden-protocol = { workspace = true } +thiserror = { workspace = true } diff --git a/crates/miden-block-prover/src/local_block_prover.rs b/crates/miden-block-prover/src/local_block_prover.rs index 6de931fa2d..4960d1a593 100644 --- a/crates/miden-block-prover/src/local_block_prover.rs +++ b/crates/miden-block-prover/src/local_block_prover.rs @@ -1,5 +1,5 @@ -use miden_objects::batch::OrderedBatches; -use miden_objects::block::{BlockHeader, BlockInputs, BlockProof}; +use miden_protocol::batch::OrderedBatches; +use miden_protocol::block::{BlockHeader, BlockInputs, BlockProof}; use crate::BlockProverError; diff --git a/crates/miden-lib/asm/miden/contracts/wallets/basic.masm b/crates/miden-lib/asm/miden/contracts/wallets/basic.masm deleted file mode 100644 index 0094eec5af..0000000000 --- a/crates/miden-lib/asm/miden/contracts/wallets/basic.masm +++ /dev/null @@ -1,61 +0,0 @@ -use.miden::native_account -use.miden::output_note - -# CONSTANTS -# ================================================================================================= -const.PUBLIC_NOTE=1 - -#! Adds the provided asset to the active account. -#! -#! Inputs: [ASSET, pad(12)] -#! Outputs: [pad(16)] -#! -#! Where: -#! - ASSET is the asset to be received, can be fungible or non-fungible -#! -#! Panics if: -#! - the same non-fungible asset already exists in the account. -#! - adding a fungible asset would result in amount overflow, i.e., -#! the total amount would be greater than 2^63. -#! -#! Invocation: call -export.receive_asset - exec.native_account::add_asset - # => [ASSET', pad(12)] - - # drop the final asset - dropw - # => [pad(16)] -end - -#! Removes the specified asset from the account and adds it to the output note with the specified -#! index. -#! -#! This procedure is expected to be invoked using a `call` instruction. It makes no guarantees about -#! the contents of the `PAD` elements shown below. It is the caller's responsibility to make sure -#! these elements do not contain any meaningful data. -#! -#! Inputs: [ASSET, note_idx, pad(11)] -#! Outputs: [ASSET, note_idx, pad(11)] -#! -#! Where: -#! - note_idx is the index of the output note. -#! - ASSET is the fungible or non-fungible asset of interest. -#! -#! Panics if: -#! - the fungible asset is not found in the vault. -#! - the amount of the fungible asset in the vault is less than the amount to be removed. -#! - the non-fungible asset is not found in the vault. -#! -#! Invocation: call -export.move_asset_to_note - # remove the asset from the account - exec.native_account::remove_asset - # => [ASSET, note_idx, pad(11)] - - dupw dup.8 movdn.4 - # => [ASSET, note_idx, ASSET, note_idx, pad(11)] - - exec.output_note::add_asset - # => [ASSET, note_idx, pad(11)] -end diff --git a/crates/miden-lib/build.rs b/crates/miden-lib/build.rs deleted file mode 100644 index 8c2b449d56..0000000000 --- a/crates/miden-lib/build.rs +++ /dev/null @@ -1,961 +0,0 @@ -use std::collections::{BTreeMap, BTreeSet}; -use std::env; -use std::fmt::Write; -use std::io::{self}; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -use fs_err as fs; -use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr, miette}; -use miden_assembly::utils::Serializable; -use miden_assembly::{ - Assembler, - DefaultSourceManager, - KernelLibrary, - Library, - LibraryNamespace, - Report, -}; -use regex::Regex; -use walkdir::WalkDir; - -/// A map where the key is the error name and the value is the error code with the message. -type ErrorCategoryMap = BTreeMap>; - -// CONSTANTS -// ================================================================================================ - -/// Defines whether the build script should generate files in `/src`. -/// The docs.rs build pipeline has a read-only filesystem, so we have to avoid writing to `src`, -/// otherwise the docs will fail to build there. Note that writing to `OUT_DIR` is fine. -const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN_SRC").is_some(); - -const ASSETS_DIR: &str = "assets"; -const ASM_DIR: &str = "asm"; -const ASM_MIDEN_DIR: &str = "miden"; -const ASM_AGGLAYER_DIR: &str = "agglayer"; -const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts"; -const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components"; -const SHARED_UTILS_DIR: &str = "shared_utils"; -const SHARED_MODULES_DIR: &str = "shared_modules"; -const ASM_TX_KERNEL_DIR: &str = "kernels/transaction"; -const KERNEL_PROCEDURES_RS_FILE: &str = "src/transaction/kernel_procedures.rs"; - -const TX_KERNEL_ERRORS_FILE: &str = "src/errors/tx_kernel_errors.rs"; -const NOTE_SCRIPT_ERRORS_FILE: &str = "src/errors/note_script_errors.rs"; - -const TX_KERNEL_ERRORS_ARRAY_NAME: &str = "TX_KERNEL_ERRORS"; -const NOTE_SCRIPT_ERRORS_ARRAY_NAME: &str = "NOTE_SCRIPT_ERRORS"; - -const TX_KERNEL_ERROR_CATEGORIES: [TxKernelErrorCategory; 14] = [ - TxKernelErrorCategory::Kernel, - TxKernelErrorCategory::Prologue, - TxKernelErrorCategory::Epilogue, - TxKernelErrorCategory::Tx, - TxKernelErrorCategory::Note, - TxKernelErrorCategory::Account, - TxKernelErrorCategory::ForeignAccount, - TxKernelErrorCategory::Faucet, - TxKernelErrorCategory::FungibleAsset, - TxKernelErrorCategory::NonFungibleAsset, - TxKernelErrorCategory::Vault, - TxKernelErrorCategory::LinkMap, - TxKernelErrorCategory::InputNote, - TxKernelErrorCategory::OutputNote, -]; - -// PRE-PROCESSING -// ================================================================================================ - -/// Read and parse the contents from `./asm`. -/// - Compiles contents of asm/miden directory into a Miden library file (.masl) under miden -/// namespace. -/// - Compiles contents of asm/scripts directory into individual .masb files. -fn main() -> Result<()> { - // re-build when the MASM code changes - println!("cargo::rerun-if-changed={ASM_DIR}/"); - println!("cargo::rerun-if-env-changed=BUILD_GENERATED_FILES_IN_SRC"); - - // Copies the MASM code to the build directory - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let build_dir = env::var("OUT_DIR").unwrap(); - let src = Path::new(&crate_dir).join(ASM_DIR); - let dst = Path::new(&build_dir).to_path_buf(); - copy_directory(src, &dst)?; - - // set source directory to {OUT_DIR}/asm - let source_dir = dst.join(ASM_DIR); - - // copy the shared modules to the kernel and miden library folders - copy_shared_modules(&source_dir)?; - - // set target directory to {OUT_DIR}/assets - let target_dir = Path::new(&build_dir).join(ASSETS_DIR); - - // compile transaction kernel - let mut assembler = - compile_tx_kernel(&source_dir.join(ASM_TX_KERNEL_DIR), &target_dir.join("kernels"))?; - - // compile miden library - let miden_lib = compile_miden_lib(&source_dir, &target_dir, assembler.clone())?; - assembler.link_dynamic_library(miden_lib)?; - - // compile agglayer library and link it - let agglayer_lib = compile_agglayer_lib(&source_dir, &target_dir, assembler.clone())?; - assembler.link_dynamic_library(agglayer_lib)?; - - // compile note scripts - compile_note_scripts( - &source_dir.join(ASM_NOTE_SCRIPTS_DIR), - &target_dir.join(ASM_NOTE_SCRIPTS_DIR), - assembler.clone(), - )?; - - // compile agglayer note scripts - compile_note_scripts( - &source_dir.join(ASM_AGGLAYER_DIR).join(ASM_NOTE_SCRIPTS_DIR), - &target_dir.join(ASM_AGGLAYER_DIR).join(ASM_NOTE_SCRIPTS_DIR), - assembler.clone(), - )?; - - // compile account components - compile_account_components( - &source_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), - &target_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), - assembler.clone(), - )?; - - // compile agglayer account components - compile_account_components( - &source_dir.join(ASM_AGGLAYER_DIR).join(ASM_ACCOUNT_COMPONENTS_DIR), - &target_dir.join(ASM_AGGLAYER_DIR).join(ASM_ACCOUNT_COMPONENTS_DIR), - assembler, - )?; - - generate_error_constants(&source_dir)?; - - generate_event_constants(&source_dir, &target_dir)?; - Ok(()) -} - -// COMPILE TRANSACTION KERNEL -// ================================================================================================ - -/// Reads the transaction kernel MASM source from the `source_dir`, compiles it, saves the results -/// to the `target_dir`, and returns an [Assembler] instantiated with the compiled kernel. -/// -/// Additionally it compiles the transaction script executor program, see the -/// [compile_tx_script_main] procedure for details. -/// -/// `source_dir` is expected to have the following structure: -/// -/// - {source_dir}/api.masm -> defines exported procedures from the transaction kernel. -/// - {source_dir}/main.masm -> defines the executable program of the transaction kernel. -/// - {source_dir}/tx_script_main -> defines the executable program of the arbitrary transaction -/// script. -/// - {source_dir}/lib -> contains common modules used by both api.masm and main.masm. -/// -/// The compiled files are written as follows: -/// -/// - {target_dir}/tx_kernel.masl -> contains kernel library compiled from api.masm. -/// - {target_dir}/tx_kernel.masb -> contains the executable compiled from main.masm. -/// - {target_dir}/tx_script_main.masb -> contains the executable compiled from -/// tx_script_main.masm. -/// - src/transaction/procedures/kernel_v0.rs -> contains the kernel procedures table. -fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result { - let shared_utils_path = Path::new(ASM_DIR).join(SHARED_UTILS_DIR); - let kernel_namespace = LibraryNamespace::Kernel; - - let mut assembler = build_assembler(None)?; - // add the shared util modules to the kernel lib under the kernel::util namespace - assembler.compile_and_statically_link_from_dir(kernel_namespace.clone(), &shared_utils_path)?; - - // assemble the kernel library and write it to the "tx_kernel.masl" file - let kernel_lib = assembler - .assemble_kernel_from_dir(source_dir.join("api.masm"), Some(source_dir.join("lib")))?; - - // generate kernel `procedures.rs` file - generate_kernel_proc_hash_file(kernel_lib.clone())?; - - let output_file = target_dir.join("tx_kernel").with_extension(Library::LIBRARY_EXTENSION); - kernel_lib.write_to_file(output_file).into_diagnostic()?; - - let assembler = build_assembler(Some(kernel_lib))?; - - // assemble the kernel program and write it to the "tx_kernel.masb" file - let mut main_assembler = assembler.clone(); - // add the shared util modules to the kernel lib under the kernel::util namespace - main_assembler - .compile_and_statically_link_from_dir(kernel_namespace.clone(), &shared_utils_path)?; - main_assembler - .compile_and_statically_link_from_dir(kernel_namespace.clone(), source_dir.join("lib"))?; - - let main_file_path = source_dir.join("main.masm"); - let kernel_main = main_assembler.clone().assemble_program(main_file_path)?; - - let masb_file_path = target_dir.join("tx_kernel.masb"); - kernel_main.write_to_file(masb_file_path).into_diagnostic()?; - - // compile the transaction script main program - compile_tx_script_main(source_dir, target_dir, main_assembler)?; - - #[cfg(any(feature = "testing", test))] - { - let mut kernel_lib_assembler = assembler.clone(); - // Build kernel as a library and save it to file. - // This is needed in test assemblers to access individual procedures which would otherwise - // be hidden when using KernelLibrary (api.masm) - - // add the shared util modules to the kernel lib under the kernel::util namespace - kernel_lib_assembler - .compile_and_statically_link_from_dir(kernel_namespace.clone(), &shared_utils_path)?; - - let test_lib = kernel_lib_assembler - .assemble_library_from_dir(source_dir.join("lib"), kernel_namespace) - .unwrap(); - - let masb_file_path = - target_dir.join("kernel_library").with_extension(Library::LIBRARY_EXTENSION); - test_lib.write_to_file(masb_file_path).into_diagnostic()?; - } - - Ok(assembler) -} - -/// Reads the transaction script executor MASM source from the `source_dir/tx_script_main.masm`, -/// compiles it and saves the results to the `target_dir` as a `tx_script_main.masb` binary file. -fn compile_tx_script_main( - source_dir: &Path, - target_dir: &Path, - main_assembler: Assembler, -) -> Result<()> { - // assemble the transaction script executor program and write it to the "tx_script_main.masb" - // file. - let tx_script_main_file_path = source_dir.join("tx_script_main.masm"); - let tx_script_main = main_assembler.assemble_program(tx_script_main_file_path)?; - - let masb_file_path = target_dir.join("tx_script_main.masb"); - tx_script_main.write_to_file(masb_file_path).into_diagnostic() -} - -/// Generates kernel `procedures.rs` file based on the kernel library -fn generate_kernel_proc_hash_file(kernel: KernelLibrary) -> Result<()> { - // Because the kernel Rust file will be stored under ./src, this should be a no-op if we can't - // write there - if !BUILD_GENERATED_FILES_IN_SRC { - return Ok(()); - } - - let (_, module_info, _) = kernel.into_parts(); - - let to_exclude = BTreeSet::from_iter(["exec_kernel_proc"]); - let offsets_filename = Path::new(ASM_DIR).join(ASM_MIDEN_DIR).join("kernel_proc_offsets.masm"); - let offsets = parse_proc_offsets(&offsets_filename)?; - let generated_procs: BTreeMap = module_info - .procedures() - .filter(|(_, proc_info)| !to_exclude.contains::(proc_info.name.as_ref())) - .map(|(_, proc_info)| { - let name = proc_info.name.to_string(); - - let Some(&offset) = offsets.get(&name) else { - panic!("Offset constant for function `{name}` not found in `{offsets_filename:?}`"); - }; - - (offset, format!(" // {name}\n word!(\"{}\"),", proc_info.digest)) - }) - .collect(); - - let proc_count = generated_procs.len(); - let generated_procs: String = generated_procs.into_iter().enumerate().map(|(index, (offset, txt))| { - if index != offset { - panic!("Offset constants in the file `{offsets_filename:?}` are not contiguous (missing offset: {index})"); - } - - txt - }).collect::>().join("\n"); - - fs::write( - KERNEL_PROCEDURES_RS_FILE, - format!( - r#"// This file is generated by build.rs, do not modify - -use miden_objects::{{Word, word}}; - -// KERNEL PROCEDURES -// ================================================================================================ - -/// Hashes of all dynamically executed kernel procedures. -pub const KERNEL_PROCEDURES: [Word; {proc_count}] = [ -{generated_procs} -]; -"#, - ), - ) - .into_diagnostic() -} - -fn parse_proc_offsets(filename: impl AsRef) -> Result> { - let regex: Regex = Regex::new(r"^const\.(?P\w+)_OFFSET\s*=\s*(?P\d+)").unwrap(); - let mut result = BTreeMap::new(); - for line in fs::read_to_string(filename).into_diagnostic()?.lines() { - if let Some(captures) = regex.captures(line) { - result.insert( - captures["name"].to_string().to_lowercase(), - captures["offset"].parse().into_diagnostic()?, - ); - } - } - - Ok(result) -} - -// COMPILE MIDEN LIB -// ================================================================================================ - -/// Reads the MASM files from "{source_dir}/miden" directory, compiles them into a Miden assembly -/// library, saves the library into "{target_dir}/miden.masl", and returns the compiled library. -fn compile_miden_lib( - source_dir: &Path, - target_dir: &Path, - mut assembler: Assembler, -) -> Result { - let source_dir = source_dir.join(ASM_MIDEN_DIR); - let shared_path = Path::new(ASM_DIR).join(SHARED_UTILS_DIR); - - let miden_namespace = "miden".parse::().expect("invalid base namespace"); - // add the shared modules to the kernel lib under the miden::util namespace - assembler.compile_and_statically_link_from_dir(miden_namespace.clone(), &shared_path)?; - - let miden_lib = assembler.assemble_library_from_dir(source_dir, miden_namespace)?; - - let output_file = target_dir.join("miden").with_extension(Library::LIBRARY_EXTENSION); - miden_lib.write_to_file(output_file).into_diagnostic()?; - - Ok(miden_lib) -} - -// COMPILE AGGLAYER LIB -// ================================================================================================ - -/// Reads the MASM files from "{source_dir}/agglayer/account_components" directory, compiles them -/// into an AggLayer assembly library, saves the library into "{target_dir}/agglayer.masl", -/// and returns the compiled library. -fn compile_agglayer_lib( - source_dir: &Path, - target_dir: &Path, - assembler: Assembler, -) -> Result { - let agglayer_namespace = ASM_AGGLAYER_DIR - .parse::() - .expect("invalid agglayer namespace"); - let agglayer_lib = assembler.assemble_library_from_dir( - source_dir.join(ASM_AGGLAYER_DIR).join(ASM_ACCOUNT_COMPONENTS_DIR), - agglayer_namespace, - )?; - - let output_file = target_dir.join(ASM_AGGLAYER_DIR).with_extension(Library::LIBRARY_EXTENSION); - agglayer_lib.write_to_file(output_file).into_diagnostic()?; - - Ok(agglayer_lib) -} - -// COMPILE EXECUTABLE MODULES -// ================================================================================================ - -/// Reads all MASM files from the "{source_dir}", complies each file individually into a MASB -/// file, and stores the compiled files into the "{target_dir}". -/// -/// The source files are expected to contain executable programs. -fn compile_note_scripts(source_dir: &Path, target_dir: &Path, assembler: Assembler) -> Result<()> { - fs::create_dir_all(target_dir) - .into_diagnostic() - .wrap_err("failed to create note_scripts directory")?; - - for masm_file_path in get_masm_files(source_dir).unwrap() { - // read the MASM file, parse it, and serialize the parsed AST to bytes - let code = assembler.clone().assemble_program(masm_file_path.clone())?; - - let bytes = code.to_bytes(); - - let masm_file_name = masm_file_path - .file_name() - .expect("file name should exist") - .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); - - // write the binary MASB to the output dir - masb_file_path.set_extension("masb"); - fs::write(masb_file_path, bytes).unwrap(); - } - Ok(()) -} - -// COMPILE ACCOUNT COMPONENTS -// ================================================================================================ - -/// Compiles the account components in `source_dir` into MASL libraries and stores the compiled -/// files in `target_dir`. -fn compile_account_components( - source_dir: &Path, - target_dir: &Path, - assembler: Assembler, -) -> Result<()> { - if !target_dir.exists() { - fs::create_dir_all(target_dir).unwrap(); - } - - for masm_file_path in get_masm_files(source_dir).unwrap() { - let component_name = masm_file_path - .file_stem() - .expect("masm file should have a file stem") - .to_str() - .expect("file stem should be valid UTF-8") - .to_owned(); - - // Read the source code to string instead of passing it to assemble_library directly since - // that would attempt to interpret the path as a LibraryPath which would fail. - let component_source_code = fs::read_to_string(masm_file_path) - .expect("reading the component's MASM source code should succeed"); - - let component_library = assembler - .clone() - .assemble_library([component_source_code]) - .expect("library assembly should succeed"); - let component_file_path = - target_dir.join(component_name).with_extension(Library::LIBRARY_EXTENSION); - component_library.write_to_file(component_file_path).into_diagnostic()?; - } - - Ok(()) -} - -// HELPER FUNCTIONS -// ================================================================================================ - -/// Returns a new [Assembler] loaded with miden-stdlib and the specified kernel, if provided. -/// -/// The returned assembler will be in the `debug` mode if the `with-debug-info` feature is enabled. -fn build_assembler(kernel: Option) -> Result { - kernel - .map(|kernel| Assembler::with_kernel(Arc::new(DefaultSourceManager::default()), kernel)) - .unwrap_or_default() - .with_debug_mode(cfg!(feature = "with-debug-info")) - .with_dynamic_library(miden_stdlib::StdLibrary::default()) -} - -/// Recursively copies `src` into `dst`. -/// -/// This function will overwrite the existing files if re-executed. -fn copy_directory, R: AsRef>(src: T, dst: R) -> Result<()> { - let mut prefix = src.as_ref().canonicalize().unwrap(); - // keep all the files inside the `asm` folder - prefix.pop(); - - let target_dir = dst.as_ref().join(ASM_DIR); - if target_dir.exists() { - // Clear existing asm files that were copied earlier which may no longer exist. - fs::remove_dir_all(&target_dir) - .into_diagnostic() - .wrap_err("failed to remove ASM directory")?; - } - - // Recreate the directory structure. - fs::create_dir_all(&target_dir) - .into_diagnostic() - .wrap_err("failed to create ASM directory")?; - - let dst = dst.as_ref(); - let mut todo = vec![src.as_ref().to_path_buf()]; - - while let Some(goal) = todo.pop() { - for entry in fs::read_dir(goal).unwrap() { - let path = entry.unwrap().path(); - if path.is_dir() { - let src_dir = path.canonicalize().unwrap(); - let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap()); - if !dst_dir.exists() { - fs::create_dir_all(&dst_dir).unwrap(); - } - todo.push(src_dir); - } else { - let dst_file = dst.join(path.strip_prefix(&prefix).unwrap()); - fs::copy(&path, dst_file).unwrap(); - } - } - } - - Ok(()) -} - -/// Copies the content of the build `shared_modules` folder to the `lib` and `miden` build folders. -/// This is required to include the shared modules as APIs of the `kernel` and `miden` libraries. -/// -/// This is done to make it possible to import the modules in the `shared_modules` folder directly, -/// i.e. "use.$kernel::account_id". -fn copy_shared_modules>(source_dir: T) -> Result<()> { - // source is expected to be an `OUT_DIR/asm` folder - let shared_modules_dir = source_dir.as_ref().join(SHARED_MODULES_DIR); - - for module_path in get_masm_files(shared_modules_dir).unwrap() { - let module_name = module_path.file_name().unwrap(); - - // copy to kernel lib - let kernel_lib_folder = source_dir.as_ref().join(ASM_TX_KERNEL_DIR).join("lib"); - fs::copy(&module_path, kernel_lib_folder.join(module_name)).into_diagnostic()?; - - // copy to miden lib - let miden_lib_folder = source_dir.as_ref().join(ASM_MIDEN_DIR); - fs::copy(&module_path, miden_lib_folder.join(module_name)).into_diagnostic()?; - } - - Ok(()) -} - -/// Returns a vector with paths to all MASM files in the specified directory. -/// -/// All non-MASM files are skipped. -fn get_masm_files>(dir_path: P) -> Result> { - let mut files = Vec::new(); - - let path = dir_path.as_ref(); - if path.is_dir() { - let entries = fs::read_dir(path) - .into_diagnostic() - .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; - for entry in entries { - let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; - let file_path = file.path(); - if is_masm_file(&file_path).into_diagnostic()? { - files.push(file_path); - } - } - } else { - println!("cargo:warn=The specified path is not a directory."); - } - - Ok(files) -} - -/// Returns true if the provided path resolves to a file with `.masm` extension. -/// -/// # Errors -/// Returns an error if the path could not be converted to a UTF-8 string. -fn is_masm_file(path: &Path) -> io::Result { - if let Some(extension) = path.extension() { - let extension = extension - .to_str() - .ok_or_else(|| io::Error::other("invalid UTF-8 filename"))? - .to_lowercase(); - Ok(extension == "masm") - } else { - Ok(false) - } -} - -// ERROR CONSTANTS FILE GENERATION -// ================================================================================================ - -/// Reads all MASM files from the `asm_source_dir` and extracts its error constants and their -/// associated error message and generates a Rust file for each category of errors. -/// For example: -/// -/// ```text -/// const.ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" -/// ``` -/// -/// would generate a Rust file for transaction kernel errors (since the error belongs to that -/// category, identified by the category extracted from `ERR_`) with - roughly - the -/// following content: -/// -/// ```rust -/// pub const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY: MasmError = -/// MasmError::from_static_str("new account must have an empty vault"); -/// ``` -/// -/// and add the constant to the error constants array. -/// -/// The function ensures that a constant is not defined twice, except if their error message is the -/// same. This can happen across multiple files. -/// -/// Because the error files will be written to ./src/errors, this should be a no-op if ./src is -/// read-only. To enable writing to ./src, set the `BUILD_GENERATED_FILES_IN_SRC` environment -/// variable. -fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { - if !BUILD_GENERATED_FILES_IN_SRC { - return Ok(()); - } - - let categories = - extract_all_masm_errors(asm_source_dir).context("failed to extract all masm errors")?; - - for (category, errors) in categories { - // Generate the errors file. - let error_file_content = generate_error_file_content(category, errors)?; - std::fs::write(category.error_file_name(), error_file_content).into_diagnostic()?; - } - - Ok(()) -} - -/// Extract all masm errors from the given path and returns a map by error category. -fn extract_all_masm_errors(asm_source_dir: &Path) -> Result { - // We use a BTree here to order the errors by their categories which is the first part after the - // ERR_ prefix and to allow for the same error to be defined multiple times in different files - // (as long as the constant name and error messages match). - let mut errors = BTreeMap::new(); - - // Walk all files of the kernel source directory. - for entry in WalkDir::new(asm_source_dir) { - let entry = entry.into_diagnostic()?; - if !is_masm_file(entry.path()).into_diagnostic()? { - continue; - } - let file_contents = std::fs::read_to_string(entry.path()).into_diagnostic()?; - extract_masm_errors(&mut errors, &file_contents)?; - } - - let mut category_map: BTreeMap> = BTreeMap::new(); - - for (error_name, error) in errors.into_iter() { - let category = ErrorCategory::match_category(&error_name)?; - - let named_error = NamedError { name: error_name, message: error.message }; - - category_map.entry(category).or_default().push(named_error); - } - - Ok(category_map) -} - -/// Extracts the errors from a single masm file and inserts them into the provided map. -fn extract_masm_errors( - errors: &mut BTreeMap, - file_contents: &str, -) -> Result<()> { - let regex = Regex::new(r#"const(\.|\ )ERR_(?.*)\ ?=\ ?"(?.*)""#).unwrap(); - - for capture in regex.captures_iter(file_contents) { - let error_name = capture - .name("name") - .expect("error name should be captured") - .as_str() - .trim() - .to_owned(); - let error_message = capture - .name("message") - .expect("error code should be captured") - .as_str() - .trim() - .to_owned(); - - if let Some(ExtractedError { message: existing_error_message, .. }) = - errors.get(&error_name) - && existing_error_message != &error_message - { - return Err(Report::msg(format!( - "Transaction kernel error constant ERR_{error_name} is already defined elsewhere but its error message is different" - ))); - } - - // Enforce the "no trailing punctuation" rule from the Rust error guidelines on MASM errors. - if error_message.ends_with(".") { - return Err(Report::msg(format!( - "Error messages should not end with a period: `ERR_{error_name}: {error_message}`" - ))); - } - - errors.insert(error_name, ExtractedError { message: error_message }); - } - - Ok(()) -} - -fn is_new_error_category<'a>(last_error: &mut Option<&'a str>, current_error: &'a str) -> bool { - let is_new = match last_error { - Some(last_err) => { - let last_category = - last_err.split("_").next().expect("there should be at least one entry"); - let new_category = - current_error.split("_").next().expect("there should be at least one entry"); - last_category != new_category - }, - None => false, - }; - - last_error.replace(current_error); - - is_new -} - -/// Generates the content of an error file for the given category and the set of errors. -fn generate_error_file_content(category: ErrorCategory, errors: Vec) -> Result { - let mut output = String::new(); - - writeln!(output, "use crate::errors::MasmError;\n").unwrap(); - - writeln!( - output, - "// This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. -// -// To add a new error, define a constant in masm of the pattern `const.ERR__...`. -// Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, -// Non-Fungible-Asset, ...). -" - ) - .unwrap(); - - writeln!( - output, - "// {} -// ================================================================================================ -", - category.array_name().replace("_", " ") - ) - .unwrap(); - - let mut last_error = None; - for named_error in errors.iter() { - let NamedError { name, message } = named_error; - - // Group errors into blocks separate by newlines. - if is_new_error_category(&mut last_error, name) { - writeln!(output).into_diagnostic()?; - } - - writeln!(output, "/// Error Message: \"{message}\"").into_diagnostic()?; - writeln!( - output, - r#"pub const ERR_{name}: MasmError = MasmError::from_static_str("{message}");"# - ) - .into_diagnostic()?; - } - - Ok(output) -} - -type ErrorName = String; - -#[derive(Debug, Clone)] -struct ExtractedError { - message: String, -} - -#[derive(Debug, Clone)] -struct NamedError { - name: ErrorName, - message: String, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum ErrorCategory { - TxKernel, - NoteScript, -} - -impl ErrorCategory { - pub const fn error_file_name(&self) -> &'static str { - match self { - ErrorCategory::TxKernel => TX_KERNEL_ERRORS_FILE, - ErrorCategory::NoteScript => NOTE_SCRIPT_ERRORS_FILE, - } - } - - pub const fn array_name(&self) -> &'static str { - match self { - ErrorCategory::TxKernel => TX_KERNEL_ERRORS_ARRAY_NAME, - ErrorCategory::NoteScript => NOTE_SCRIPT_ERRORS_ARRAY_NAME, - } - } - - pub fn match_category(error_name: &ErrorName) -> Result { - for kernel_category in TX_KERNEL_ERROR_CATEGORIES { - if error_name.starts_with(kernel_category.category_name()) { - return Ok(ErrorCategory::TxKernel); - } - } - - // If the error is not a tx kernel error, consider it a note script error. - Ok(ErrorCategory::NoteScript) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum TxKernelErrorCategory { - Kernel, - Prologue, - Epilogue, - Tx, - Note, - Account, - ForeignAccount, - Faucet, - FungibleAsset, - NonFungibleAsset, - Vault, - LinkMap, - InputNote, - OutputNote, -} - -impl TxKernelErrorCategory { - pub const fn category_name(&self) -> &'static str { - match self { - TxKernelErrorCategory::Kernel => "KERNEL", - TxKernelErrorCategory::Prologue => "PROLOGUE", - TxKernelErrorCategory::Epilogue => "EPILOGUE", - TxKernelErrorCategory::Tx => "TX", - TxKernelErrorCategory::Note => "NOTE", - TxKernelErrorCategory::Account => "ACCOUNT", - TxKernelErrorCategory::ForeignAccount => "FOREIGN_ACCOUNT", - TxKernelErrorCategory::Faucet => "FAUCET", - TxKernelErrorCategory::FungibleAsset => "FUNGIBLE_ASSET", - TxKernelErrorCategory::NonFungibleAsset => "NON_FUNGIBLE_ASSET", - TxKernelErrorCategory::Vault => "VAULT", - TxKernelErrorCategory::LinkMap => "LINK_MAP", - TxKernelErrorCategory::InputNote => "INPUT_NOTE", - TxKernelErrorCategory::OutputNote => "OUTPUT_NOTE", - } - } -} - -// EVENT CONSTANTS FILE GENERATION -// ================================================================================================ - -/// Reads all MASM files from the `asm_source_dir` and extracts event definitions, -/// then generates the transaction_events.rs file with constants. -fn generate_event_constants(asm_source_dir: &Path, target_dir: &Path) -> Result<()> { - // Extract all event definitions from MASM files - let events = extract_all_event_definitions(asm_source_dir)?; - - // Generate the events file in OUT_DIR - let event_file_content = generate_event_file_content(&events).into_diagnostic()?; - let event_file_path = target_dir.join("transaction_events.rs"); - fs::write(event_file_path, event_file_content).into_diagnostic()?; - - Ok(()) -} - -/// Extract all `const.X=event("x")` definitions from all MASM files -fn extract_all_event_definitions(asm_source_dir: &Path) -> Result> { - // collect mappings event path to const variable name, we want a unique mapping - // which we use to generate the constants and enum variant names - let mut events = BTreeMap::new(); - - // Walk all MASM files - for entry in WalkDir::new(asm_source_dir) { - let entry = entry.into_diagnostic()?; - if !is_masm_file(entry.path()).into_diagnostic()? { - continue; - } - let file_contents = fs::read_to_string(entry.path()).into_diagnostic()?; - extract_event_definitions_from_file(&mut events, &file_contents, entry.path())?; - } - - Ok(events) -} - -/// Extract event definitions from a single MASM file in two possible forms: -/// - `const.${X}=event("${x::path}")` -/// - `const ${X} = event("${x::path}")` -fn extract_event_definitions_from_file( - events: &mut BTreeMap, - file_contents: &str, - file_path: &Path, -) -> Result<()> { - let regex = Regex::new(r#"const(\.|\ )(\w+)\ ?=\ ?event\("([^"]+)"\)"#).unwrap(); - - for capture in regex.captures_iter(file_contents) { - let const_name = capture.get(2).expect("const name should be captured"); - let event_path = capture.get(3).expect("event path should be captured"); - - let event_path = event_path.as_str(); - let const_name = const_name.as_str(); - - let const_name_wo_suffix = - if let Some((const_name_wo_suffix, _)) = const_name.rsplit_once("_EVENT") { - const_name_wo_suffix.to_string() - } else { - const_name.to_owned() - }; - - if !event_path.starts_with("miden::") { - // we ignore any `stdlib::` prefixed ones - if !event_path.starts_with("stdlib::") { - return Err(miette::miette!( - "unhandled `event_path={event_path}`, doesn't with `stdlib::` nor with `miden::`." - )); - } - continue; - } - - // Check for duplicates with different definitions - if let Some(existing_const_name) = events.get(event_path) { - if existing_const_name != &const_name_wo_suffix { - println!( - "cargo:warning=Duplicate event definition found {event_path} with different definitions names: - '{existing_const_name}' vs '{const_name}' in {}", - file_path.display() - ); - } - } else { - events.insert(event_path.to_owned(), const_name_wo_suffix.to_owned()); - } - } - - Ok(()) -} - -/// Generate the content of the transaction_events.rs file -fn generate_event_file_content( - events: &BTreeMap, -) -> std::result::Result { - use std::fmt::Write; - - let mut output = String::new(); - - writeln!(&mut output, "// This file is generated by build.rs, do not modify")?; - writeln!(&mut output)?; - - // Generate constants - // - // Note: If we ever encounter two constants `const.X`, that are both named `X` we will error - // when attempting to generate the rust code. Currently this is a side-effect, but we - // want to error out as early as possible: - // TODO: make the error out at build-time to be able to present better error hints - for (event_path, event_name) in events { - let value = miden_core::EventId::from_name(event_path).as_felt().as_int(); - debug_assert!(!event_name.is_empty()); - writeln!(&mut output, "const {}: u64 = {};", event_name, value)?; - } - - { - writeln!(&mut output)?; - - writeln!(&mut output)?; - - writeln!( - &mut output, - r###" -use alloc::collections::BTreeMap; - -pub(crate) static EVENT_NAME_LUT: ::miden_objects::utils::sync::LazyLock> = - ::miden_objects::utils::sync::LazyLock::new(|| {{ - BTreeMap::from_iter([ -"### - )?; - - for (event_path, const_name) in events { - writeln!(&mut output, " ({}, \"{}\"),", const_name, event_path)?; - } - - writeln!( - &mut output, - r###" ]) -}});"### - )?; - } - - Ok(output) -} diff --git a/crates/miden-lib/src/block/mod.rs b/crates/miden-lib/src/block/mod.rs deleted file mode 100644 index d3db8b80e8..0000000000 --- a/crates/miden-lib/src/block/mod.rs +++ /dev/null @@ -1,73 +0,0 @@ -use miden_objects::ProposedBlockError; -use miden_objects::block::{BlockBody, BlockHeader, ProposedBlock}; - -use crate::transaction::TransactionKernel; - -/// Builds a [`BlockHeader`] and [`BlockBody`] by computing the following from the state -/// updates encapsulated by the provided [`ProposedBlock`]: -/// - the account root; -/// - the nullifier root; -/// - the note root; -/// - the transaction commitment; and -/// - the chain commitment. -/// -/// The returned block header contains the same validator public key as the previous block, as -/// provided by the proposed block. -/// -/// This functionality is handled here because the block header requires [`TransactionKernel`] for -/// its various commitment fields. -pub fn build_block( - proposed_block: ProposedBlock, -) -> Result<(BlockHeader, BlockBody), ProposedBlockError> { - // Get fields from the proposed block before it is consumed. - let block_num = proposed_block.block_num(); - let timestamp = proposed_block.timestamp(); - let prev_block_header = proposed_block.prev_block_header().clone(); - - // Insert the state commitments of updated accounts into the account tree to compute its new - // root. - let new_account_root = proposed_block.compute_account_root()?; - - // Insert the created nullifiers into the nullifier tree to compute its new root. - let new_nullifier_root = proposed_block.compute_nullifier_root()?; - - // Compute the root of the block note tree. - let note_tree = proposed_block.compute_block_note_tree(); - let note_root = note_tree.root(); - - // Insert the previous block header into the block partial blockchain to get the new chain - // commitment. - let new_chain_commitment = proposed_block.compute_chain_commitment(); - - // Construct the block body from the proposed block. - let body = BlockBody::from(proposed_block); - - // Construct the header. - let tx_commitment = body.transaction_commitment(); - let prev_block_commitment = prev_block_header.commitment(); - - // For now we copy the parameters of the previous header, which means the parameters set on - // the genesis block will be passed through. Eventually, the contained base fees will be - // updated based on the demand in the currently proposed block. - let fee_parameters = prev_block_header.fee_parameters().clone(); - - // Currently undefined and reserved for future use. - // See miden-base/1155. - let version = 0; - let tx_kernel_commitment = TransactionKernel.to_commitment(); - let header = BlockHeader::new( - version, - prev_block_commitment, - block_num, - new_chain_commitment, - new_account_root, - new_nullifier_root, - note_root, - tx_commitment, - tx_kernel_commitment, - prev_block_header.validator_key().clone(), - fee_parameters, - timestamp, - ); - Ok((header, body)) -} diff --git a/crates/miden-lib/src/errors/mod.rs b/crates/miden-lib/src/errors/mod.rs deleted file mode 100644 index 32a5d0b629..0000000000 --- a/crates/miden-lib/src/errors/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[cfg(any(feature = "testing", test))] -#[rustfmt::skip] -pub mod tx_kernel_errors; - -#[cfg(any(feature = "testing", test))] -#[rustfmt::skip] -pub mod note_script_errors; - -mod masm_error; -pub use masm_error::MasmError; - -mod code_builder_errors; -pub use code_builder_errors::CodeBuilderError; - -mod transaction_errors; -pub use transaction_errors::{TransactionEventError, TransactionTraceParsingError}; diff --git a/crates/miden-lib/src/errors/transaction_errors.rs b/crates/miden-lib/src/errors/transaction_errors.rs deleted file mode 100644 index d216c1342a..0000000000 --- a/crates/miden-lib/src/errors/transaction_errors.rs +++ /dev/null @@ -1,26 +0,0 @@ -use miden_core::EventId; -use thiserror::Error; - -use crate::transaction::TransactionEventId; - -// TRANSACTION EVENT PARSING ERROR -// ================================================================================================ - -#[derive(Debug, Error)] -pub enum TransactionEventError { - #[error("event id {0} is not a valid transaction event")] - InvalidTransactionEvent(EventId, Option<&'static str>), - #[error("event id {0} is not a transaction kernel event")] - NotTransactionEvent(EventId, Option<&'static str>), - #[error("event id {0} can only be emitted from the root context")] - NotRootContext(TransactionEventId), -} - -// TRANSACTION TRACE PARSING ERROR -// ================================================================================================ - -#[derive(Debug, Error)] -pub enum TransactionTraceParsingError { - #[error("trace id {0} is an unknown transaction kernel trace")] - UnknownTransactionTrace(u32), -} diff --git a/crates/miden-lib/src/lib.rs b/crates/miden-lib/src/lib.rs deleted file mode 100644 index 7ebc5a2797..0000000000 --- a/crates/miden-lib/src/lib.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![no_std] - -use alloc::sync::Arc; - -#[macro_use] -extern crate alloc; - -#[cfg(feature = "std")] -extern crate std; - -use miden_objects::assembly::Library; -use miden_objects::assembly::mast::MastForest; -use miden_objects::utils::serde::Deserializable; -use miden_objects::utils::sync::LazyLock; - -mod auth; -pub use auth::AuthScheme; - -pub mod account; -pub mod agglayer; -pub mod block; -pub mod errors; -pub mod note; -pub mod transaction; -pub mod utils; - -#[cfg(any(feature = "testing", test))] -pub mod testing; - -// RE-EXPORTS -// ================================================================================================ -pub use miden_stdlib::StdLibrary; - -// CONSTANTS -// ================================================================================================ - -const MIDEN_LIB_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/assets/miden.masl")); - -// MIDEN LIBRARY -// ================================================================================================ - -#[derive(Clone)] -pub struct MidenLib(Library); - -impl MidenLib { - /// Returns a reference to the [`MastForest`] of the inner [`Library`]. - pub fn mast_forest(&self) -> &Arc { - self.0.mast_forest() - } -} - -impl AsRef for MidenLib { - fn as_ref(&self) -> &Library { - &self.0 - } -} - -impl From for Library { - fn from(value: MidenLib) -> Self { - value.0 - } -} - -impl Default for MidenLib { - fn default() -> Self { - static MIDEN_LIB: LazyLock = LazyLock::new(|| { - let contents = - Library::read_from_bytes(MIDEN_LIB_BYTES).expect("failed to read miden lib masl!"); - MidenLib(contents) - }); - MIDEN_LIB.clone() - } -} - -// TESTS -// ================================================================================================ - -// NOTE: Most kernel-related tests can be found under /miden-tx/kernel_tests -#[cfg(all(test, feature = "std"))] -mod tests { - use miden_objects::assembly::LibraryPath; - - use super::MidenLib; - - #[test] - fn test_compile() { - let path = "miden::active_account::get_id".parse::().unwrap(); - let miden = MidenLib::default(); - let exists = miden.0.module_infos().any(|module| { - module - .procedures() - .any(|(_, proc)| module.path().clone().append(&proc.name).unwrap() == path) - }); - - assert!(exists); - } -} diff --git a/crates/miden-lib/src/transaction/outputs.rs b/crates/miden-lib/src/transaction/outputs.rs deleted file mode 100644 index bb28b753a8..0000000000 --- a/crates/miden-lib/src/transaction/outputs.rs +++ /dev/null @@ -1,65 +0,0 @@ -use miden_objects::account::{AccountHeader, AccountId}; -use miden_objects::{AccountError, Felt, WORD_SIZE, Word, WordError}; - -use super::memory::{ - ACCT_CODE_COMMITMENT_OFFSET, - ACCT_DATA_MEM_SIZE, - ACCT_ID_AND_NONCE_OFFSET, - ACCT_NONCE_IDX, - ACCT_STORAGE_COMMITMENT_OFFSET, - ACCT_VAULT_ROOT_OFFSET, - MemoryOffset, -}; -use crate::transaction::memory::{ACCT_ID_PREFIX_IDX, ACCT_ID_SUFFIX_IDX}; - -// STACK OUTPUTS -// ================================================================================================ - -/// The index of the word at which the final account nonce is stored on the output stack. -pub const OUTPUT_NOTES_COMMITMENT_WORD_IDX: usize = 0; - -/// The index of the word at which the account update commitment is stored on the output stack. -pub const ACCOUNT_UPDATE_COMMITMENT_WORD_IDX: usize = 1; - -/// The index of the word at which the fee asset is stored on the output stack. -pub const FEE_ASSET_WORD_IDX: usize = 2; - -/// The index of the item at which the expiration block height is stored on the output stack. -pub const EXPIRATION_BLOCK_ELEMENT_IDX: usize = 12; - -// ACCOUNT HEADER EXTRACTOR -// ================================================================================================ - -/// Parses the account header data returned by the VM into individual account component commitments. -/// Returns a tuple of account ID, vault root, storage commitment, code commitment, and nonce. -pub fn parse_final_account_header(elements: &[Felt]) -> Result { - if elements.len() != ACCT_DATA_MEM_SIZE { - return Err(AccountError::HeaderDataIncorrectLength { - actual: elements.len(), - expected: ACCT_DATA_MEM_SIZE, - }); - } - - let id = AccountId::try_from([ - elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_PREFIX_IDX], - elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_SUFFIX_IDX], - ]) - .map_err(AccountError::FinalAccountHeaderIdParsingFailed)?; - let nonce = elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_NONCE_IDX]; - let vault_root = parse_word(elements, ACCT_VAULT_ROOT_OFFSET) - .expect("we should have sliced off exactly 4 bytes"); - let storage_commitment = parse_word(elements, ACCT_STORAGE_COMMITMENT_OFFSET) - .expect("we should have sliced off exactly 4 bytes"); - let code_commitment = parse_word(elements, ACCT_CODE_COMMITMENT_OFFSET) - .expect("we should have sliced off exactly 4 bytes"); - - Ok(AccountHeader::new(id, nonce, vault_root, storage_commitment, code_commitment)) -} - -// HELPER FUNCTIONS -// ================================================================================================ - -/// Creates a new `Word` instance from the slice of `Felt`s using provided offset. -fn parse_word(data: &[Felt], offset: MemoryOffset) -> Result { - Word::try_from(&data[offset as usize..offset as usize + WORD_SIZE]) -} diff --git a/crates/miden-lib/src/utils/mod.rs b/crates/miden-lib/src/utils/mod.rs deleted file mode 100644 index 408cd69922..0000000000 --- a/crates/miden-lib/src/utils/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod code_builder; - -pub use code_builder::CodeBuilder; -pub use miden_objects::utils::*; - -pub use crate::errors::CodeBuilderError; diff --git a/crates/miden-protocol-macros/Cargo.toml b/crates/miden-protocol-macros/Cargo.toml index 030b01a60a..0ec8930785 100644 --- a/crates/miden-protocol-macros/Cargo.toml +++ b/crates/miden-protocol-macros/Cargo.toml @@ -21,7 +21,7 @@ quote = "1.0" syn = { features = ["extra-traits", "full"], version = "2.0" } [dev-dependencies] -miden-objects = { path = "../miden-objects" } +miden-protocol = { path = "../miden-protocol" } [package.metadata.cargo-machete] ignored = ["proc-macro2"] diff --git a/crates/miden-protocol-macros/tests/integration_test.rs b/crates/miden-protocol-macros/tests/integration_test.rs index c6c95165ce..05a925e4d5 100644 --- a/crates/miden-protocol-macros/tests/integration_test.rs +++ b/crates/miden-protocol-macros/tests/integration_test.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use miden_objects::{Felt, FieldElement, Word}; + use miden_protocol::{Felt, FieldElement, Word}; use miden_protocol_macros::WordWrapper; #[derive(Debug, Clone, Copy, PartialEq, Eq, WordWrapper)] diff --git a/crates/miden-objects/Cargo.toml b/crates/miden-protocol/Cargo.toml similarity index 84% rename from crates/miden-objects/Cargo.toml rename to crates/miden-protocol/Cargo.toml index 997c800f15..ea4b9c629d 100644 --- a/crates/miden-objects/Cargo.toml +++ b/crates/miden-protocol/Cargo.toml @@ -4,9 +4,9 @@ categories = ["no-std"] description = "Core components of the Miden protocol" edition.workspace = true homepage.workspace = true -keywords = ["miden", "objects"] +keywords = ["miden", "protocol"] license.workspace = true -name = "miden-objects" +name = "miden-protocol" readme = "README.md" repository.workspace = true rust-version.workspace = true @@ -38,11 +38,11 @@ testing = ["dep:rand_chacha", "dep:rand_xoshiro", "dep:winter-rand-utils", "mide miden-assembly = { workspace = true } miden-assembly-syntax = { workspace = true } miden-core = { workspace = true } +miden-core-lib = { workspace = true } miden-crypto = { workspace = true } miden-mast-package = { workspace = true } miden-processor = { workspace = true } miden-protocol-macros = { workspace = true } -miden-stdlib = { workspace = true } miden-utils-sync = { workspace = true } miden-verifier = { workspace = true } winter-rand-utils = { optional = true, version = "0.13" } @@ -65,7 +65,7 @@ getrandom = { features = ["wasm_js"], version = "0.3" } anyhow = { features = ["backtrace", "std"], workspace = true } assert_matches = { workspace = true } criterion = { default-features = false, features = ["html_reports"], version = "0.5" } -miden-objects = { features = ["testing"], path = "." } +miden-protocol = { features = ["testing"], path = "." } pprof = { default-features = false, features = ["criterion", "flamegraph"], version = "0.15" } rstest = { workspace = true } tempfile = { version = "3.19" } @@ -73,3 +73,11 @@ winter-air = { version = "0.13" } # for HashFunction/ExecutionProof::new_dummy color-eyre = { version = "0.5" } miden-air = { features = ["std", "testing"], workspace = true } + +[build-dependencies] +fs-err = { version = "3" } +miden-assembly = { workspace = true } +miden-core = { workspace = true } +miden-core-lib = { workspace = true } +regex = { version = "1.11" } +walkdir = { version = "2.5" } diff --git a/crates/miden-objects/README.md b/crates/miden-protocol/README.md similarity index 97% rename from crates/miden-objects/README.md rename to crates/miden-protocol/README.md index bfa78937c5..c731a35159 100644 --- a/crates/miden-objects/README.md +++ b/crates/miden-protocol/README.md @@ -1,6 +1,6 @@ -# Miden Objects +# Miden Protocol -This crates contains core components defining the Miden protocol. +This crates contains core components defining the Miden protocol including the Miden protocol kernels. ## Modules diff --git a/crates/miden-lib/asm/kernels/transaction/api.masm b/crates/miden-protocol/asm/kernels/transaction/api.masm similarity index 94% rename from crates/miden-lib/asm/kernels/transaction/api.masm rename to crates/miden-protocol/asm/kernels/transaction/api.masm index 5138dc37e1..e3a5edd3cc 100644 --- a/crates/miden-lib/asm/kernels/transaction/api.masm +++ b/crates/miden-protocol/asm/kernels/transaction/api.masm @@ -1,12 +1,12 @@ -use.$kernel::account -use.$kernel::account_delta -use.$kernel::account_id -use.$kernel::faucet -use.$kernel::input_note -use.$kernel::memory -use.$kernel::note -use.$kernel::output_note -use.$kernel::tx +use $kernel::account +use $kernel::account_delta +use $kernel::account_id +use $kernel::faucet +use $kernel::input_note +use $kernel::memory +use $kernel::note +use $kernel::output_note +use $kernel::tx # NOTE # ================================================================================================= @@ -20,25 +20,25 @@ use.$kernel::tx # ERRORS # ================================================================================================= -const.ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED="for faucets the FAUCET_STORAGE_DATA_SLOT storage slot is reserved and can not be used with set_account_item" +const ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED="for faucets the FAUCET_STORAGE_DATA_SLOT storage slot is reserved and can not be used with set_account_item" -const.ERR_FAUCET_TOTAL_ISSUANCE_PROC_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_FAUCET="the faucet_get_total_fungible_asset_issuance procedure can only be called on a fungible faucet" +const ERR_FAUCET_TOTAL_ISSUANCE_PROC_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_FAUCET="the faucet_get_total_fungible_asset_issuance procedure can only be called on a fungible faucet" -const.ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet" +const ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet" -const.ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS="provided kernel procedure offset is out of bounds" +const ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS="provided kernel procedure offset is out of bounds" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ASSETS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note assets of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ASSETS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note assets of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_RECIPIENT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note recipient of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_RECIPIENT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note recipient of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note metadata of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note metadata of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_INPUTS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note inputs of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_INPUTS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note inputs of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note script root of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note script root of active note because no note is currently being processed" -const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note serial number of active note because no note is currently being processed" +const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note serial number of active note because no note is currently being processed" # AUTHENTICATION # ================================================================================================= @@ -52,7 +52,7 @@ const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSE #! - the invocation of the kernel procedure does not originate from the account context. #! #! Invocation: exec -proc.authenticate_account_origin +proc authenticate_account_origin # get the hash of the caller padw caller # => [CALLER] @@ -73,7 +73,7 @@ end #! of the account. #! #! Invocation: exec -proc.assert_auth_procedure_origin +proc assert_auth_procedure_origin # get the hash of the caller padw caller # => [CALLER] @@ -98,7 +98,7 @@ end #! - INIT_COMMITMENT is the initial account commitment. #! #! Invocation: dynexec -export.account_get_initial_commitment +pub proc account_get_initial_commitment # get the initial account commitment exec.account::get_initial_commitment # => [INIT_COMMITMENT, pad(16)] @@ -117,7 +117,7 @@ end #! - ACCOUNT_COMMITMENT is the commitment of the account data. #! #! Invocation: dynexec -export.account_compute_commitment +pub proc account_compute_commitment # compute the active account commitment exec.account::compute_commitment # => [ACCOUNT_COMMITMENT, pad(16)] @@ -148,7 +148,7 @@ end #! - the vault or storage delta is not empty but the nonce increment is zero. #! #! Invocation: dynexec -export.account_compute_delta_commitment +pub proc account_compute_delta_commitment # compute the account delta commitment exec.account_delta::compute_commitment # => [DELTA_COMMITMENT, pad(16)] @@ -169,7 +169,7 @@ end #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. #! #! Invocation: dynexec -export.account_get_id +pub proc account_get_id # get the native account ID exec.memory::get_native_account_id # => [native_account_id_prefix, native_account_id_suffix, is_native, pad(15)] @@ -215,7 +215,7 @@ end #! - nonce is the active account's nonce. #! #! Invocation: dynexec -export.account_get_nonce +pub proc account_get_nonce # get the account nonce exec.account::get_nonce # => [nonce, pad(16)] @@ -241,7 +241,7 @@ end #! - the nonce has already been incremented. #! #! Invocation: dynexec -export.account_incr_nonce +pub proc account_incr_nonce # check that this procedure was executed against the native account exec.memory::assert_native_account # => [pad(16)] @@ -268,7 +268,7 @@ end #! - CODE_COMMITMENT is the commitment of the account code. #! #! Invocation: dynexec -export.account_get_code_commitment +pub proc account_get_code_commitment # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [pad(16)] @@ -291,7 +291,7 @@ end #! - INIT_STORAGE_COMMITMENT is the initial account storage commitment. #! #! Invocation: dynexec -export.account_get_initial_storage_commitment +pub proc account_get_initial_storage_commitment # get the initial account storage commitment exec.account::get_initial_storage_commitment # => [INIT_STORAGE_COMMITMENT, pad(16)] @@ -310,7 +310,7 @@ end #! - STORAGE_COMMITMENT is the commitment of the account storage. #! #! Invocation: dynexec -export.account_compute_storage_commitment +pub proc account_compute_storage_commitment # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [pad(16)] @@ -338,7 +338,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: dynexec -export.account_get_item +pub proc account_get_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [slot_id_prefix, slot_id_suffix, pad(14)] @@ -369,7 +369,7 @@ end #! - the native account is a faucet and the provided slot ID points to the reserved faucet storage slot. #! #! Invocation: dynexec -export.account_set_item +pub proc account_set_item # check that this procedure was executed against the native account exec.memory::assert_native_account # => [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] @@ -410,7 +410,7 @@ end #! - the requested storage slot type is not map. #! #! Invocation: dynexec -export.account_get_map_item +pub proc account_get_map_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [slot_id_prefix, slot_id_suffix, KEY, pad(10)] @@ -434,7 +434,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: dynexec -export.account_get_initial_item +pub proc account_get_initial_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [slot_id_prefix, slot_id_suffix, pad(14)] @@ -465,7 +465,7 @@ end #! - the requested storage slot type is not map. #! #! Invocation: dynexec -export.account_get_initial_map_item +pub proc account_get_initial_map_item # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [slot_id_prefix, slot_id_suffix, KEY, pad(10)] @@ -497,7 +497,7 @@ end #! - the invocation of this procedure does not originate from the native account. #! #! Invocation: dynexec -export.account_set_map_item +pub proc account_set_map_item # check that this procedure was executed against the native account exec.memory::assert_native_account # => [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] @@ -524,7 +524,7 @@ end #! - INIT_VAULT_ROOT is the initial account vault root. #! #! Invocation: dynexec -export.account_get_initial_vault_root +pub proc account_get_initial_vault_root # get the initial account vault root exec.account::get_initial_vault_root # => [INIT_VAULT_ROOT, pad(16)] @@ -543,7 +543,7 @@ end #! - VAULT_ROOT is the root of the account vault. #! #! Invocation: dynexec -export.account_get_vault_root +pub proc account_get_vault_root # fetch the account vault root exec.memory::get_account_vault_root # => [VAULT_ROOT, pad(16)] @@ -573,7 +573,7 @@ end #! - the invocation of this procedure does not originate from the native account. #! #! Invocation: dynexec -export.account_add_asset +pub proc account_add_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [ASSET, pad(12)] @@ -602,7 +602,7 @@ end #! - the invocation of this procedure does not originate from the native account. #! #! Invocation: dynexec -export.account_remove_asset +pub proc account_remove_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [ASSET, pad(12)] @@ -631,7 +631,7 @@ end #! - the provided faucet ID is not an ID of a fungible faucet. #! #! Invocation: dynexec -export.account_get_balance +pub proc account_get_balance exec.account::get_balance # => [balance, pad(15)] end @@ -651,7 +651,7 @@ end #! - the provided faucet ID is not an ID of a fungible faucet. #! #! Invocation: dynexec -export.account_get_initial_balance +pub proc account_get_initial_balance exec.account::get_initial_balance # => [init_balance, pad(15)] end @@ -670,7 +670,7 @@ end #! - the ASSET is a fungible asset. #! #! Invocation: dynexec -export.account_has_non_fungible_asset +pub proc account_has_non_fungible_asset exec.account::has_non_fungible_asset # => [has_asset, pad(15)] end @@ -688,7 +688,7 @@ end #! - the procedure root is not part of the account code. #! #! Invocation: dynexec -export.account_was_procedure_called +pub proc account_was_procedure_called # check that this procedure was executed against the native account exec.memory::assert_native_account # => [PROC_ROOT, pad(12)] @@ -707,7 +707,7 @@ end #! - num_procedures is the number of procedures in the active account. #! #! Invocation: dynexec -export.account_get_num_procedures +pub proc account_get_num_procedures # get the number of procedures exec.memory::get_num_account_procedures # => [num_procedures, pad(16)] @@ -730,7 +730,7 @@ end #! - the procedure index is out of bounds. #! #! Invocation: dynexec -export.account_get_procedure_root +pub proc account_get_procedure_root # get the procedure root exec.account::get_procedure_root # => [PROC_ROOT, pad(15)] @@ -754,7 +754,7 @@ end #! available on the active account. #! #! Invocation: dynexec -export.account_has_procedure +pub proc account_has_procedure # check if the procedure was called exec.account::has_procedure # => [is_procedure_available, pad(15)] @@ -783,7 +783,7 @@ end #! - if the non-fungible asset being minted already exists. #! #! Invocation: dynexec -export.faucet_mint_asset +pub proc faucet_mint_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [ASSET, pad(12)] @@ -818,7 +818,7 @@ end #! transaction via a note or the accounts vault. #! #! Invocation: dynexec -export.faucet_burn_asset +pub proc faucet_burn_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [ASSET, pad(12)] @@ -845,7 +845,7 @@ end #! - the transaction is not being executed against a fungible faucet. #! #! Invocation: dynexec -export.faucet_get_total_fungible_asset_issuance +pub proc faucet_get_total_fungible_asset_issuance # assert that we are executing a transaction against a fungible faucet (access checks) exec.account::get_id swap drop exec.account_id::is_fungible_faucet assert.err=ERR_FAUCET_TOTAL_ISSUANCE_PROC_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_FAUCET @@ -875,7 +875,7 @@ end #! - the ASSET is not associated with the faucet the transaction is being executed against. #! #! Invocation: dynexec -export.faucet_is_non_fungible_asset_issued +pub proc faucet_is_non_fungible_asset_issued # assert that we are executing a transaction against a non-fungible faucet (access checks) exec.account::get_id swap drop exec.account_id::is_non_fungible_faucet assert.err=ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET @@ -912,7 +912,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_assets_info +pub proc input_note_get_assets_info # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -950,7 +950,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_recipient +pub proc input_note_get_recipient # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -988,7 +988,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_metadata +pub proc input_note_get_metadata # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -1026,7 +1026,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_serial_number +pub proc input_note_get_serial_number # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -1067,7 +1067,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_inputs_info +pub proc input_note_get_inputs_info # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -1111,7 +1111,7 @@ end #! from incorrect context). #! #! Invocation: dynexec -export.input_note_get_script_root +pub proc input_note_get_script_root # get the input note pointer depending on whether the requested note is current or it was # requested by index. exec.get_requested_note_ptr @@ -1150,7 +1150,7 @@ end #! - note_idx is the index of the created note. #! #! Invocation: dynexec -export.output_note_create +pub proc output_note_create # check that this procedure was executed against the native account exec.memory::assert_native_account # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] @@ -1172,7 +1172,7 @@ end #! - the procedure is called when the active account is not the native one. #! #! Invocation: dynexec -export.output_note_add_asset +pub proc output_note_add_asset # check that this procedure was executed against the native account exec.memory::assert_native_account # => [note_idx, ASSET, pad(11)] @@ -1195,7 +1195,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: dynexec -export.output_note_get_assets_info +pub proc output_note_get_assets_info # assert that the provided note index is less than the total number of output notes exec.output_note::assert_note_index_in_bounds # => [note_index, pad(15)] @@ -1224,7 +1224,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: dynexec -export.output_note_get_recipient +pub proc output_note_get_recipient # assert that the provided note index is less than the total number of output notes exec.output_note::assert_note_index_in_bounds # => [note_index, pad(15)] @@ -1255,7 +1255,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: dynexec -export.output_note_get_metadata +pub proc output_note_get_metadata # assert that the provided note index is less than the total number of output notes exec.output_note::assert_note_index_in_bounds # => [note_index, pad(15)] @@ -1291,7 +1291,7 @@ end #! - INPUT_NOTES_COMMITMENT is the input notes commitment hash. #! #! Invocation: dynexec -export.tx_get_input_notes_commitment +pub proc tx_get_input_notes_commitment exec.tx::get_input_notes_commitment # => [INPUT_NOTES_COMMITMENT, pad(16)] @@ -1310,7 +1310,7 @@ end #! - OUTPUT_NOTES_COMMITMENT is the output notes commitment. #! #! Invocation: dynexec -export.tx_get_output_notes_commitment +pub proc tx_get_output_notes_commitment # get the output notes commitment exec.tx::get_output_notes_commitment # => [OUTPUT_NOTES_COMMITMENT, pad(16)] @@ -1329,7 +1329,7 @@ end #! - num_input_notes is the total number of input notes consumed by this transaction. #! #! Invocation: dynexec -export.tx_get_num_input_notes +pub proc tx_get_num_input_notes # get the number of input notes exec.tx::get_num_input_notes # => [num_input_notes, pad(16)] @@ -1348,7 +1348,7 @@ end #! - num_output_notes is the number of output notes created in this transaction so far. #! #! Invocation: dynexec -export.tx_get_num_output_notes +pub proc tx_get_num_output_notes # get the number of input notes exec.tx::get_num_output_notes # => [num_output_notes, pad(16)] @@ -1367,7 +1367,7 @@ end #! - BLOCK_COMMITMENT is the commitment of the transaction reference block. #! #! Invocation: dynexec -export.tx_get_block_commitment +pub proc tx_get_block_commitment exec.tx::get_block_commitment # => [BLOCK_COMMITMENT, pad(16)] @@ -1385,7 +1385,7 @@ end #! - num is the transaction reference block number. #! #! Invocation: dynexec -export.tx_get_block_number +pub proc tx_get_block_number # get the block number exec.tx::get_block_number # => [num, pad(16)] @@ -1402,7 +1402,7 @@ end #! #! Where: #! - timestamp is the timestamp of the reference block for this transaction. -export.tx_get_block_timestamp +pub proc tx_get_block_timestamp # get the transaction reference block timestamp exec.tx::get_block_timestamp # => [timestamp, pad(16)] @@ -1446,7 +1446,7 @@ end #! - foreign context is created against the native account. #! #! Invocation: dynexec -export.tx_start_foreign_context +pub proc tx_start_foreign_context # get the memory address and a flag whether this account was already loaded. exec.account::get_account_data_ptr # OS => [was_loaded, ptr, foreign_account_id_prefix, foreign_account_id_suffix, pad(14)] @@ -1479,7 +1479,7 @@ end #! - the active account is the native account. #! #! Invocation: dynexec -export.tx_end_foreign_context +pub proc tx_end_foreign_context exec.memory::pop_ptr_from_account_stack # => [pad(16)] end @@ -1498,7 +1498,7 @@ end #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). #! #! Invocation: dynexec -export.tx_update_expiration_block_delta +pub proc tx_update_expiration_block_delta exec.tx::update_expiration_block_delta # => [pad(16)] end @@ -1512,7 +1512,7 @@ end #! - block_height_delta is the stored expiration time delta (1 to 0xFFFF). #! #! Invocation: dynexec -export.tx_get_expiration_delta +pub proc tx_get_expiration_delta exec.tx::get_expiration_delta # => [block_height_delta, pad(16)] @@ -1538,7 +1538,7 @@ end #! - the provided procedure offset exceeds the number of kernel procedures. #! #! Invocation: syscall -export.exec_kernel_proc +pub proc exec_kernel_proc # check that the provided procedure offset is within expected bounds dup exec.memory::get_num_kernel_procedures lt assert.err=ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS @@ -1574,7 +1574,7 @@ end #! #! Panics if: #! - the note index is greater or equal to the total number of input notes. -proc.get_requested_note_ptr +proc get_requested_note_ptr # get the memory pointer to the note with the specified index and verify it is valid swap exec.input_note::get_input_note_ptr swap # => [is_active_note, indexed_input_note_ptr] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm similarity index 92% rename from crates/miden-lib/asm/kernels/transaction/lib/account.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/account.masm index 2d2a361ea9..e5c7dca37e 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm @@ -1,62 +1,62 @@ -use.$kernel::account_delta -use.$kernel::account_id -use.$kernel::asset_vault -use.$kernel::constants -use.$kernel::memory -use.std::collections::smt -use.std::collections::sorted_array -use.std::crypto::hashes::rpo -use.std::mem -use.std::word +use $kernel::account_delta +use $kernel::account_id +use $kernel::asset_vault +use $kernel::constants +use $kernel::memory +use miden::core::collections::smt +use miden::core::collections::sorted_array +use miden::core::crypto::hashes::rpo256 +use miden::core::mem +use miden::core::word # ERRORS # ================================================================================================= -const.ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE="account nonce can only be incremented once" +const ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE="account nonce can only be incremented once" -const.ERR_ACCOUNT_NONCE_AT_MAX="account nonce is already at its maximum possible value" +const ERR_ACCOUNT_NONCE_AT_MAX="account nonce is already at its maximum possible value" -const.ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE="account code must be updatable for it to be possible to set new code" +const ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE="account code must be updatable for it to be possible to set new code" -const.ERR_ACCOUNT_SEED_AND_COMMITMENT_DIGEST_MISMATCH="ID of the new account does not match the ID computed from the seed and commitments" +const ERR_ACCOUNT_SEED_AND_COMMITMENT_DIGEST_MISMATCH="ID of the new account does not match the ID computed from the seed and commitments" -const.ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT="failed to write an account value item to a non-value storage slot" +const ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT="failed to write an account value item to a non-value storage slot" -const.ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT="failed to write an account map item to a non-map storage slot" +const ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT="failed to write an account map item to a non-map storage slot" -const.ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE="procedure is not part of the account code" +const ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE="procedure is not part of the account code" -const.ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS="provided procedure index is out of bounds" +const ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS="provided procedure index is out of bounds" -const.ERR_ACCOUNT_PROC_NOT_AUTH_PROC="account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure" +const ERR_ACCOUNT_PROC_NOT_AUTH_PROC="account procedure is not the authentication procedure; some procedures (e.g. `incr_nonce`) can be called only from the authentication procedure" -const.ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE="slot IDs must be unique and sorted in ascending order" +const ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE="slot IDs must be unique and sorted in ascending order" -const.ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" +const ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME="storage slot with the provided name does not exist" -const.ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH="computed account code commitment does not match recorded account code commitment" +const ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH="computed account code commitment does not match recorded account code commitment" -const.ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES="number of account procedures must be at least 2" +const ERR_ACCOUNT_NOT_ENOUGH_PROCEDURES="number of account procedures must be at least 2" -const.ERR_ACCOUNT_TOO_MANY_PROCEDURES="number of account procedures exceeds the maximum limit of 256" +const ERR_ACCOUNT_TOO_MANY_PROCEDURES="number of account procedures exceeds the maximum limit of 256" -const.ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS="number of account storage slots exceeds the maximum limit of 255" +const ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS="number of account storage slots exceeds the maximum limit of 255" -const.ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH="computed account storage commitment does not match recorded account storage commitment" +const ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH="computed account storage commitment does not match recorded account storage commitment" -const.ERR_ACCOUNT_STORAGE_MAP_ENTRIES_DO_NOT_MATCH_MAP_ROOT="storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to" +const ERR_ACCOUNT_STORAGE_MAP_ENTRIES_DO_NOT_MATCH_MAP_ROOT="storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to" -const.ERR_FOREIGN_ACCOUNT_ID_IS_ZERO="ID of the provided foreign account equals zero" +const ERR_FOREIGN_ACCOUNT_ID_IS_ZERO="ID of the provided foreign account equals zero" -const.ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED="maximum allowed number of foreign account to be loaded (64) was exceeded" +const ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED="maximum allowed number of foreign account to be loaded (64) was exceeded" -const.ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT="commitment of the foreign account in the advice provider does not match the commitment in the account tree" +const ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT="commitment of the foreign account in the advice provider does not match the commitment in the account tree" -const.ERR_ACCOUNT_ID_UNKNOWN_VERSION="unknown version in account ID" +const ERR_ACCOUNT_ID_UNKNOWN_VERSION="unknown version in account ID" -const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in account ID" +const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in account ID" -const.ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account map item from a non-map storage slot" +const ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account map item from a non-map storage slot" # CONSTANTS # ================================================================================================= @@ -64,120 +64,120 @@ const.ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT="failed to read an account # The name of the account storage slot at which faucet data is stored. # Fungible faucet: The faucet data consists of [0, 0, 0, total_issuance] # Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. -const.FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") +const FAUCET_SYSDATA_SLOT=word("miden::protocol::faucet::sysdata") # The maximum storage slot index -const.MAX_STORAGE_SLOT_INDEX=254 +const MAX_STORAGE_SLOT_INDEX=254 # The maximum number of account storage slots. -const.MAX_NUM_STORAGE_SLOTS=MAX_STORAGE_SLOT_INDEX+1 +const MAX_NUM_STORAGE_SLOTS=MAX_STORAGE_SLOT_INDEX+1 # The minimum number of account interface procedures. -const.MIN_NUM_PROCEDURES=2 +const MIN_NUM_PROCEDURES=2 # The maximum number of account interface procedures. -const.MAX_NUM_PROCEDURES=256 +const MAX_NUM_PROCEDURES=256 # Given the least significant 32 bits of an account ID's prefix, this mask defines the bits used # to determine the account version. -const.ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 +const ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 # Given the least significant 32 bits of an account ID's prefix, this mask defines the bits used # to determine the account type. -const.ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 +const ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 # Given the least significant 32 bits of an account ID's first felt, this mask defines the bits used # to determine the account storage mode. -const.ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 +const ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 # Given the least significant 32 bits of an account ID's first felt with the storage mode mask # applied, this value defines the public storage mode. -const.ACCOUNT_ID_STORAGE_MODE_PUBLIC_U32=0 # 0b0000_0000 +const ACCOUNT_ID_STORAGE_MODE_PUBLIC_U32=0 # 0b0000_0000 # Given the least significant 32 bits of an account ID's first felt with the storage mode mask # applied, this value defines the private storage mode. -const.ACCOUNT_ID_STORAGE_MODE_PRIVATE_U32=0x80 # 0b1000_0000 +const ACCOUNT_ID_STORAGE_MODE_PRIVATE_U32=0x80 # 0b1000_0000 # Bit pattern for an account w/ immutable code, after the account type mask has been applied. -const.REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 +const REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 # Bit pattern for an account w/ updatable code, after the account type mask has been applied. -const.REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 +const REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 # Bit pattern for a fungible faucet w/ immutable code, after the account type mask has been applied. -const.FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 +const FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 # Bit pattern for a non-fungible faucet w/ immutable code, after the account type mask has been # applied. -const.NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 +const NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 # Bit pattern for a faucet account, after the account type mask has been applied. -const.FAUCET_ACCOUNT=0x20 # 0b10_0000 +const FAUCET_ACCOUNT=0x20 # 0b10_0000 # Depth of the account database tree. -const.ACCOUNT_TREE_DEPTH=64 +const ACCOUNT_TREE_DEPTH=64 # The number of field elements it takes to store one account storage slot. -const.ACCOUNT_STORAGE_SLOT_DATA_LENGTH=8 +const ACCOUNT_STORAGE_SLOT_DATA_LENGTH=8 # The number of field elements it takes to store one account procedure. -const.ACCOUNT_PROCEDURE_DATA_LENGTH=4 +const ACCOUNT_PROCEDURE_DATA_LENGTH=4 # The offset of the slot type in the storage slot. -const.ACCOUNT_SLOT_TYPE_OFFSET=1 +const ACCOUNT_SLOT_TYPE_OFFSET=1 # The offset of the slot's ID suffix in the storage slot. -const.ACCOUNT_SLOT_ID_SUFFIX_OFFSET=2 +const ACCOUNT_SLOT_ID_SUFFIX_OFFSET=2 # The offset of the slot's ID prefix in the storage slot. -const.ACCOUNT_SLOT_ID_PREFIX_OFFSET=3 +const ACCOUNT_SLOT_ID_PREFIX_OFFSET=3 # The offset of the slot value in the storage slot. -const.ACCOUNT_SLOT_VALUE_OFFSET=4 +const ACCOUNT_SLOT_VALUE_OFFSET=4 # EVENTS # ================================================================================================= # Event emitted before a foreign account is loaded from the advice inputs. -const.ACCOUNT_BEFORE_FOREIGN_LOAD_EVENT=event("miden::account::before_foreign_load") +const ACCOUNT_BEFORE_FOREIGN_LOAD_EVENT=event("miden::account::before_foreign_load") # Event emitted before an asset is added to the account vault. -const.ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT=event("miden::account::vault_before_add_asset") +const ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT=event("miden::account::vault_before_add_asset") # Event emitted after an asset is added to the account vault. -const.ACCOUNT_VAULT_AFTER_ADD_ASSET_EVENT=event("miden::account::vault_after_add_asset") +const ACCOUNT_VAULT_AFTER_ADD_ASSET_EVENT=event("miden::account::vault_after_add_asset") # Event emitted before an asset is removed from the account vault. -const.ACCOUNT_VAULT_BEFORE_REMOVE_ASSET_EVENT=event("miden::account::vault_before_remove_asset") +const ACCOUNT_VAULT_BEFORE_REMOVE_ASSET_EVENT=event("miden::account::vault_before_remove_asset") # Event emitted after an asset is removed from the account vault. -const.ACCOUNT_VAULT_AFTER_REMOVE_ASSET_EVENT=event("miden::account::vault_after_remove_asset") +const ACCOUNT_VAULT_AFTER_REMOVE_ASSET_EVENT=event("miden::account::vault_after_remove_asset") # Event emitted before a fungible asset's balance is fetched from the account vault. -const.ACCOUNT_VAULT_BEFORE_GET_BALANCE_EVENT=event("miden::account::vault_before_get_balance") +const ACCOUNT_VAULT_BEFORE_GET_BALANCE_EVENT=event("miden::account::vault_before_get_balance") # Event emitted before it is checked whether a non-fungible asset exists in the account vault. -const.ACCOUNT_VAULT_BEFORE_HAS_NON_FUNGIBLE_ASSET_EVENT=event("miden::account::vault_before_has_non_fungible_asset") +const ACCOUNT_VAULT_BEFORE_HAS_NON_FUNGIBLE_ASSET_EVENT=event("miden::account::vault_before_has_non_fungible_asset") # Event emitted before an account storage item is updated. -const.ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT=event("miden::account::storage_before_set_item") +const ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT=event("miden::account::storage_before_set_item") # Event emitted after an account storage item is updated. -const.ACCOUNT_STORAGE_AFTER_SET_ITEM_EVENT=event("miden::account::storage_after_set_item") +const ACCOUNT_STORAGE_AFTER_SET_ITEM_EVENT=event("miden::account::storage_after_set_item") # Event emitted before an account storage map item is accessed. -const.ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM_EVENT=event("miden::account::storage_before_get_map_item") +const ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM_EVENT=event("miden::account::storage_before_get_map_item") # Event emitted before an account storage map item is updated. -const.ACCOUNT_STORAGE_BEFORE_SET_MAP_ITEM_EVENT=event("miden::account::storage_before_set_map_item") +const ACCOUNT_STORAGE_BEFORE_SET_MAP_ITEM_EVENT=event("miden::account::storage_before_set_map_item") # Event emitted after an account storage map item is updated. -const.ACCOUNT_STORAGE_AFTER_SET_MAP_ITEM_EVENT=event("miden::account::storage_after_set_map_item") +const ACCOUNT_STORAGE_AFTER_SET_MAP_ITEM_EVENT=event("miden::account::storage_after_set_map_item") # Event emitted before an account nonce is incremented. -const.ACCOUNT_BEFORE_INCREMENT_NONCE_EVENT=event("miden::account::before_increment_nonce") +const ACCOUNT_BEFORE_INCREMENT_NONCE_EVENT=event("miden::account::before_increment_nonce") # Event emitted after an account nonce is incremented. -const.ACCOUNT_AFTER_INCREMENT_NONCE_EVENT=event("miden::account::after_increment_nonce") +const ACCOUNT_AFTER_INCREMENT_NONCE_EVENT=event("miden::account::after_increment_nonce") # Event emitted to push the index of the account procedure at the top of the operand stack onto # the advice stack. -const.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_index") +const ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_index") # CONSTANT ACCESSORS # ================================================================================================= @@ -192,7 +192,7 @@ const.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT=event("miden::account::push_procedure_i #! Where: #! - faucet_slot_id{prefix,suffix} are the prefix and suffix felts of the slot identifier, at which #! faucet data is stored. -export.get_faucet_sysdata_slot_id +pub proc get_faucet_sysdata_slot_id push.FAUCET_SYSDATA_SLOT[0..2] end @@ -203,7 +203,7 @@ end #! #! Where: #! - max_num_storage_slots is the maximum number of account storage slots. -export.get_max_num_storage_slots +pub proc get_max_num_storage_slots push.MAX_NUM_STORAGE_SLOTS end @@ -214,7 +214,7 @@ end #! #! Where: #! - max_num_procedures is the maximum number of account interface procedures. -export.get_max_num_procedures +pub proc get_max_num_procedures push.MAX_NUM_PROCEDURES end @@ -231,7 +231,7 @@ end #! #! Where: #! - act_acct_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. -export.memory::get_account_id->get_id +pub use memory::get_account_id->get_id #! Returns the nonce of the active account. #! @@ -240,7 +240,7 @@ export.memory::get_account_id->get_id #! #! Where: #! - nonce is the account nonce. -export.memory::get_account_nonce->get_nonce +pub use memory::get_account_nonce->get_nonce #! Increments the account nonce by one and returns the new nonce. #! @@ -251,7 +251,7 @@ export.memory::get_account_nonce->get_nonce #! #! Panics if: #! - the nonce has already been incremented. -export.incr_nonce +pub proc incr_nonce exec.account_delta::was_nonce_incremented # => [was_nonce_incremented] @@ -292,7 +292,7 @@ end #! #! Where: #! - INIT_COMMITMENT is the initial account commitment. -export.get_initial_commitment +pub proc get_initial_commitment # determine whether the active account is native exec.memory::is_native_account # => [is_native_account] @@ -319,7 +319,7 @@ end #! #! Where: #! - ACCOUNT_COMMITMENT is the commitment of the account data. -export.compute_commitment +pub proc compute_commitment # if outdated, recompute the storage commitment and store it in the memory exec.refresh_storage_commitment # => [] @@ -334,7 +334,7 @@ export.compute_commitment # => [RATE, RATE, PERM, account_data_ptr'] # extract account commitment - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [ACCOUNT_COMMITMENT, account_data_ptr'] # drop account_data_ptr @@ -351,7 +351,7 @@ end #! #! Where: #! - CODE_COMMITMENT is the commitment of the account code. -export.memory::get_account_code_commitment->get_code_commitment +pub use memory::get_account_code_commitment->get_code_commitment ### STORAGE COMMITMENT ################################################### @@ -362,7 +362,7 @@ export.memory::get_account_code_commitment->get_code_commitment #! #! Where: #! - INIT_ACCOUNT_STORAGE_COMMITMENT is the initial account storage commitment. -export.get_initial_storage_commitment +pub proc get_initial_storage_commitment # Get the storage commitment of the active account. For the foreign account this commitment will # be initial. exec.memory::get_account_storage_commitment @@ -389,7 +389,7 @@ end #! #! Where: #! - STORAGE_COMMITMENT is the commitment of the active account storage. -export.compute_storage_commitment +pub proc compute_storage_commitment # if outdated, recompute the storage commitment and store it in the memory exec.refresh_storage_commitment @@ -407,7 +407,7 @@ end #! #! Where: #! - INIT_ACCOUNT_VAULT_ROOT is the initial account vault root. -export.get_initial_vault_root +pub proc get_initial_vault_root # Get the vault root of the active account. For the foreign account this root will be equal to # the initial one. exec.memory::get_account_vault_root @@ -442,7 +442,7 @@ end #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. -export.get_item +pub proc get_item # get account storage slots section offset exec.memory::get_account_storage_slots_section_ptr # => [acct_storage_slots_section_offset, slot_id_prefix, slot_id_suffix] @@ -468,7 +468,7 @@ end #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. -export.get_typed_item +pub proc get_typed_item # get account storage slots section offset exec.memory::get_account_storage_slots_section_ptr # => [acct_storage_slots_section_offset, slot_id_prefix, slot_id_suffix] @@ -496,7 +496,7 @@ end #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. -export.get_initial_item +pub proc get_initial_item # get account initial storage slots section offset exec.memory::get_account_initial_storage_slots_ptr # => [account_initial_storage_slots_ptr, slot_id_prefix, slot_id_suffix] @@ -523,7 +523,7 @@ end #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. #! - the storage slot type is not value. -export.set_item +pub proc set_item emit.ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT # => [slot_id_prefix, slot_id_suffix, VALUE] @@ -574,7 +574,7 @@ end #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. -export.get_map_item +pub proc get_map_item exec.memory::get_account_storage_slots_section_ptr # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY] @@ -595,7 +595,7 @@ end #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. -export.get_initial_map_item +pub proc get_initial_map_item exec.memory::get_account_initial_storage_slots_ptr # => [initial_storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY] @@ -620,7 +620,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! - the storage slot type is not map. #! - no map with the root of the slot is found. -export.set_map_item +pub proc set_map_item exec.memory::get_account_storage_slots_section_ptr # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE] @@ -651,7 +651,7 @@ end #! Where: #! - index is the location in memory of the storage slot. #! - slot_type is the type of the storage slot. -export.get_storage_slot_type +pub proc get_storage_slot_type # convert the index into a memory offset mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH # => [offset] @@ -686,7 +686,7 @@ end #! - the total value of the fungible asset is greater than or equal to 2^63 after the new asset was #! added. #! - the vault already contains the same non-fungible asset. -export.add_asset_to_vault +pub proc add_asset_to_vault # duplicate the ASSET to be able to emit an event after an asset is being added dupw # => [ASSET, ASSET] @@ -726,7 +726,7 @@ end #! - the fungible asset is not found in the vault. #! - the amount of the fungible asset in the vault is less than the amount to be removed. #! - the non-fungible asset is not found in the vault. -export.remove_asset_from_vault +pub proc remove_asset_from_vault # fetch the vault root exec.memory::get_account_vault_root_ptr movdn.4 # => [ASSET, acct_vault_root_ptr] @@ -759,7 +759,7 @@ end #! #! Panics if: #! - the provided faucet ID is not an ID of a fungible faucet. -export.get_balance +pub proc get_balance # get the vault root exec.memory::get_account_vault_root_ptr movdn.2 # => [faucet_id_prefix, faucet_id_suffix, vault_root_ptr] @@ -786,7 +786,7 @@ end #! #! Panics if: #! - the provided faucet ID is not an ID of a fungible faucet. -export.get_initial_balance +pub proc get_initial_balance # get the vault root associated with the initial vault root of the native account exec.memory::get_account_initial_vault_root_ptr movdn.2 # => [faucet_id_prefix, faucet_id_suffix, init_native_vault_root_ptr] @@ -812,7 +812,7 @@ end #! #! Panics if: #! - the ASSET is a fungible asset. -export.has_non_fungible_asset +pub proc has_non_fungible_asset # get the vault root exec.memory::get_account_vault_root_ptr movdn.4 # => [ASSET, vault_root_ptr] @@ -840,7 +840,7 @@ end #! #! Panics if: #! - the procedure root is not part of the account code. -export.authenticate_and_track_procedure +pub proc authenticate_and_track_procedure # load procedure index emit.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT adv_push.1 # => [index, PROC_ROOT] @@ -868,7 +868,7 @@ end #! #! Panics if: #! - the procedure root is not the authentication procedure. -export.assert_auth_procedure +pub proc assert_auth_procedure # authentication procedure is always at index 0 push.0 # => [index, PROC_ROOT] @@ -898,7 +898,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.validate_seed +pub proc validate_seed # Compute the hash of (SEED, CODE_COMMITMENT, STORAGE_COMMITMENT, EMPTY_WORD). # --------------------------------------------------------------------------------------------- @@ -939,7 +939,7 @@ export.validate_seed # => [RATE, RATE, CAP] # extract digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [DIGEST] # Shape suffix to set the lower 8 bits to zero and compare the computed and provided ID. @@ -1094,7 +1094,7 @@ end #! - the computed account code commitment does not match the provided account code commitment. #! - the number of account storage slots exceeded the maximum limit of 255. #! - the computed account storage commitment does not match the provided account storage commitment. -export.load_foreign_account +pub proc load_foreign_account emit.ACCOUNT_BEFORE_FOREIGN_LOAD_EVENT # => [account_id_prefix, account_id_suffix] @@ -1163,7 +1163,7 @@ end #! Panics if: #! - the number of account storage slots exceeded the maximum limit of 255. #! - the computed account storage commitment does not match the provided account storage commitment. -export.save_account_storage_data +pub proc save_account_storage_data # move storage slot data from the advice map to the advice stack adv.push_mapvaln # OS => [STORAGE_COMMITMENT] @@ -1202,7 +1202,7 @@ export.save_account_storage_data # AS => [] # extract the digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # OS => [DIGEST, end_ptr', STORAGE_COMMITMENT] # drop end_ptr @@ -1233,7 +1233,7 @@ end #! Panics if: #! - the number of account procedures exceeded the maximum limit of 256. #! - the computed account code commitment does not match the provided account code commitment. -export.save_account_procedure_data +pub proc save_account_procedure_data # move procedure data from the advice map to the advice stack adv.push_mapvaln # OS => [CODE_COMMITMENT] @@ -1276,7 +1276,7 @@ export.save_account_procedure_data # AS => [] # extract the digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # OS => [DIGEST, end_ptr', CODE_COMMITMENT] # drop end_ptr @@ -1296,7 +1296,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.insert_new_storage +pub proc insert_new_storage exec.memory::get_num_storage_slots # => [num_slots] @@ -1344,7 +1344,7 @@ end #! Operand stack: [slot_idx] #! Advice map: { MAP_ROOT: [MAP_ENTRIES] } #! Outputs: [] -proc.insert_and_validate_storage_map +proc insert_and_validate_storage_map mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH exec.memory::get_account_storage_slots_section_ptr add @@ -1438,7 +1438,7 @@ end #! the first two felts of the hashed slot name. #! - is_faucet_storage_data_slot is a boolean value indicating whether the provided slot is the #! reserved faucet data slot. -export.is_faucet_storage_data_slot +pub proc is_faucet_storage_data_slot exec.get_faucet_sysdata_slot_id # => [faucet_slot_id_prefix, faucet_slot_id_suffix, slot_id_prefix, slot_id_suffix] @@ -1466,7 +1466,7 @@ end #! the first two felts of the hashed slot name. #! - INITIAL_VALUE is the initial value of the item at the beginning of the transaction. #! - CURRENT_VALUE is the current value of the item. -export.get_item_delta +pub proc get_item_delta # convert the index into a memory offset mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH # => [offset] @@ -1511,7 +1511,7 @@ end #! - index is the index of the slot. #! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are #! the first two felts of the hashed slot name. -export.get_slot_id +pub proc get_slot_id # convert the index into a memory offset mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH # => [offset] @@ -1540,7 +1540,7 @@ end #! Where: #! - slot_ptr is the pointer to a slot. #! - VALUE is the new value of the item. -proc.set_item_raw +proc set_item_raw add.ACCOUNT_SLOT_VALUE_OFFSET mem_storew_be dropw # => [] @@ -1573,7 +1573,8 @@ end #! - 0: slot_ptr #! - 4..8: OLD_MAP_VALUE #! - 8..12: OLD_MAP_ROOT -proc set_map_item_raw.12 +@locals(12) +proc set_map_item_raw # store slot_ptr until the end of the procedure dup loc_store.0 # => [slot_ptr, KEY, NEW_VALUE] @@ -1667,7 +1668,7 @@ end #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. -proc.get_map_item_raw +proc get_map_item_raw exec.find_storage_slot # => [slot_ptr, KEY] @@ -1714,7 +1715,7 @@ end #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. -proc.find_storage_slot +proc find_storage_slot # construct the start and end pointers of the storage slot section in which we will search dup exec.memory::get_num_storage_slots mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH add # => [storage_slots_end_ptr, storage_slots_start_ptr, slot_id_prefix, slot_id_suffix] @@ -1766,7 +1767,7 @@ end #! #! Panics if: #! - the procedure index is out of bounds. -proc.get_procedure_root +proc get_procedure_root dup exec.memory::get_num_account_procedures u32assert2.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS u32lt assert.err=ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS @@ -1798,7 +1799,7 @@ end #! Panics if: #! - the prefix or suffix of the provided foreign account ID equal zero. #! - the maximum allowed number of foreign account to be loaded (64) was exceeded. -export.get_account_data_ptr +pub proc get_account_data_ptr # check that foreign account ID is not equal zero dup.1 eq.0 dup.1 eq.0 and not assert.err=ERR_FOREIGN_ACCOUNT_ID_IS_ZERO # => [foreign_account_id_prefix, foreign_account_id_suffix] @@ -1859,7 +1860,7 @@ end #! #! Panics if: #! - the hash of the active account is not represented in the account database. -export.validate_active_foreign_account +pub proc validate_active_foreign_account # get the account database root exec.memory::get_account_db_root # => [ACCOUNT_DB_ROOT] @@ -1890,7 +1891,7 @@ end #! #! Inputs: [KEY] #! Outputs: [HASHED_KEY] -proc.hash_map_key +proc hash_map_key hash # => [HASHED_KEY] end @@ -1904,7 +1905,7 @@ end #! #! Inputs: [] #! Outputs: [] -proc.refresh_storage_commitment +proc refresh_storage_commitment # First we should determine whether the storage commitment should be recomputed. We should do so # if the active account is native and the storage commitment is outdated (the dirty flag equals # 1). Otherwise the commitment value is guaranteed to be up-to-date. @@ -1929,11 +1930,11 @@ proc.refresh_storage_commitment # => [PAD, PAD, PAD, start_ptr, end_ptr] # hash elements from memory - exec.rpo::absorb_double_words_from_memory + exec.rpo256::absorb_double_words_from_memory # => [PERM, PERM, PERM, start_ptr, end_ptr] # extract the digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [DIGEST, end_ptr, end_ptr] # clean stack @@ -1966,7 +1967,7 @@ end #! #! Panics if: #! - the procedure root is not part of the account code. -export.was_procedure_called +pub proc was_procedure_called # load procedure index emit.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT adv_push.1 # => [index, PROC_ROOT] @@ -1999,7 +2000,7 @@ end #! #! Inputs: [proc_idx] #! Outputs: [] -export.set_was_procedure_called +pub proc set_was_procedure_called exec.memory::get_account_procedures_call_tracking_ptr # => [was_called_offset, proc_idx] @@ -2019,7 +2020,7 @@ end #! - PROC_ROOT is the hash of the procedure of interest. #! - is_procedure_available is the binary flag indicating whether the procedure with PROC_ROOT is #! available on the active account. -export.has_procedure +pub proc has_procedure # get the end pointer of the procedure section (where we should stop iterating) exec.memory::get_num_account_procedures exec.memory::get_account_procedure_ptr diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm similarity index 96% rename from crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm index f264825098..220b431df5 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account_delta.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm @@ -1,29 +1,29 @@ -use.$kernel::account -use.$kernel::asset -use.$kernel::asset_vault -use.$kernel::constants -use.$kernel::link_map -use.$kernel::memory -use.std::crypto::hashes::rpo -use.std::word +use $kernel::account +use $kernel::asset +use $kernel::asset_vault +use $kernel::constants +use $kernel::link_map +use $kernel::memory +use miden::core::crypto::hashes::rpo256 +use miden::core::word # ERRORS # ================================================================================================= -const.ERR_ACCOUNT_DELTA_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED="nonce must be incremented if account vault or account storage changed" +const ERR_ACCOUNT_DELTA_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED="nonce must be incremented if account vault or account storage changed" # CONSTANTS # ================================================================================================= # The domain of an asset in the delta commitment. -const.DOMAIN_ASSET=1 +const DOMAIN_ASSET = 1 # The domain of a value storage slot in the delta commitment. -const.DOMAIN_VALUE=2 +const DOMAIN_VALUE = 2 # The domain of a map storage slot in the delta commitment. -const.DOMAIN_MAP=3 +const DOMAIN_MAP = 3 # The maximum value a felt can represent. -const.FELT_MAX=0xffffffff00000000 +const FELT_MAX = 0xffffffff00000000 # PROCEDURES # ================================================================================================= @@ -43,7 +43,7 @@ const.FELT_MAX=0xffffffff00000000 #! #! Panics if: #! - the vault or storage delta is not empty but the nonce increment is zero. -export.compute_commitment +pub proc compute_commitment # pad capacity element of the hasher padw # => [CAPACITY] @@ -76,7 +76,7 @@ export.compute_commitment exec.update_storage_delta # => [RATE, RATE, PERM, ID_AND_NONCE_DIGEST] - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [DELTA_COMMITMENT, ID_AND_NONCE_DIGEST] exec.was_nonce_incremented not @@ -103,7 +103,7 @@ end #! #! Inputs: [RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_storage_delta +proc update_storage_delta exec.memory::get_num_storage_slots movdn.12 # => [RATE, RATE, PERM, num_storage_slots] @@ -144,7 +144,7 @@ end #! #! Inputs: [slot_idx, RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_slot_delta +proc update_slot_delta dup exec.account::get_storage_slot_type # => [storage_slot_type, slot_idx, RATE, RATE, PERM] @@ -164,7 +164,7 @@ end #! #! Inputs: [slot_idx, RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_value_slot_delta +proc update_value_slot_delta exec.account::get_item_delta # => [INIT_VALUE, CURRENT_VALUE, slot_id_prefix, slot_id_suffix, RATE, RATE, PERM] @@ -220,7 +220,8 @@ end #! - 2: has_next #! - 3: iter #! - 4: num_changed_entries -proc.update_map_slot_delta.5 +@locals(5) +proc update_map_slot_delta # initialize num_changed_entries = 0 # this is necessary because this procedure can be called multiple times and the second # invocation shouldn't reuse the first invocation's value @@ -321,7 +322,8 @@ end #! #! Inputs: [RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_fungible_asset_delta.2 +@locals(2) +proc update_fungible_asset_delta exec.memory::get_account_delta_fungible_asset_ptr # => [account_delta_fungible_asset_ptr, RATE, RATE, PERM] @@ -401,7 +403,8 @@ end #! #! Inputs: [RATE, RATE, PERM] #! Outputs: [RATE, RATE, PERM] -proc.update_non_fungible_asset_delta.2 +@locals(2) +proc update_non_fungible_asset_delta exec.memory::get_account_delta_non_fungible_asset_ptr # => [account_delta_non_fungible_asset_ptr, RATE, RATE, PERM] @@ -478,7 +481,7 @@ end #! #! Where: #! - was_nonce_incremented is the boolean flag indicating whether the account nonce was incremented. -export.was_nonce_incremented +pub proc was_nonce_incremented exec.memory::get_init_nonce # => [init_nonce] @@ -498,7 +501,7 @@ end #! #! Where: #! - ASSET is the asset. -export.add_asset +pub proc add_asset # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET] @@ -528,7 +531,7 @@ end #! #! Where: #! - ASSET is the asset. -export.remove_asset +pub proc remove_asset # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET, vault_root_ptr] @@ -556,7 +559,7 @@ end #! Where: #! - ASSET_KEY is the asset key of the fungible asset. #! - amount is the amount by which the fungible asset's amount increases. -export.add_fungible_asset +pub proc add_fungible_asset dupw exec.memory::get_account_delta_fungible_asset_ptr # => [fungible_delta_map_ptr, ASSET_KEY, ASSET_KEY, amount] @@ -593,7 +596,7 @@ end #! Where: #! - ASSET_KEY is the asset key of the fungible asset. #! - amount is the amount by which the fungible asset's amount decreases. -export.remove_fungible_asset +pub proc remove_fungible_asset dupw exec.memory::get_account_delta_fungible_asset_ptr # => [fungible_delta_map_ptr, ASSET_KEY, ASSET_KEY, amount] @@ -643,7 +646,7 @@ end #! #! Where: #! - ASSET is the non-fungible asset to be added. -export.add_non_fungible_asset +pub proc add_non_fungible_asset dupw exec.memory::get_account_delta_non_fungible_asset_ptr # => [non_fungible_delta_map_ptr, ASSET, ASSET] @@ -677,7 +680,7 @@ end #! #! Where: #! - ASSET is the non-fungible asset to be removed. -export.remove_non_fungible_asset +pub proc remove_non_fungible_asset dupw exec.memory::get_account_delta_non_fungible_asset_ptr # => [non_fungible_delta_map_ptr, ASSET, ASSET] @@ -715,7 +718,8 @@ end #! - KEY is the key in the storage map that is being updated. #! - PREV_VALUE is the previous value of the key in the storage map that is being updated. #! - NEW_VALUE is the new value of the key in the storage map that is being updated. -export.set_map_item.8 +@locals(8) +pub proc set_map_item # retrieve the link map ptr to the storage map delta for the provided index exec.memory::get_account_delta_storage_map_ptr # => [account_delta_storage_map_ptr, KEY, PREV_VALUE, NEW_VALUE] @@ -808,7 +812,7 @@ end #! - is_delta_amount_positive indicates whether the delta amount is positive. #! - delta_amount_abs is the absolute value of the delta_amount, meaning negative values are #! remapped to their positive equivalent value. -proc.delta_amount_absolute +proc delta_amount_absolute dup exec.is_delta_amount_negative not # => [is_positive, delta_amount] @@ -829,7 +833,7 @@ end #! Where: #! - delta_amount is the delta amount that can span the entire felt range. #! - is_delta_amount_negative indicates whether the delta amount represents a negative value. -proc.is_delta_amount_negative +proc is_delta_amount_negative # delta_amount represents a negative number if it is greater than the max amount exec.asset::get_fungible_asset_max_amount gt # => [is_delta_amount_negative] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/asset.masm b/crates/miden-protocol/asm/kernels/transaction/lib/asset.masm similarity index 85% rename from crates/miden-lib/asm/kernels/transaction/lib/asset.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/asset.masm index df64d68e36..6429f273d0 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/asset.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/asset.masm @@ -1,22 +1,21 @@ -use.$kernel::account -use.$kernel::account_id +use $kernel::account_id # ERRORS # ================================================================================================= -const.ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ONE_MUST_BE_ZERO="malformed fungible asset: `ASSET[1]` must be 0" +const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ONE_MUST_BE_ZERO="malformed fungible asset: `ASSET[1]` must be 0" -const.ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_TWO_AND_THREE_MUST_BE_FUNGIBLE_FAUCET_ID="malformed fungible asset: `ASSET[2]` and `ASSET[3]` must be a valid fungible faucet id" +const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_TWO_AND_THREE_MUST_BE_FUNGIBLE_FAUCET_ID="malformed fungible asset: `ASSET[2]` and `ASSET[3]` must be a valid fungible faucet id" -const.ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS="malformed fungible asset: `ASSET[0]` exceeds the maximum allowed amount" +const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS="malformed fungible asset: `ASSET[0]` exceeds the maximum allowed amount" -const.ERR_NON_FUNGIBLE_ASSET_FORMAT_ELEMENT_THREE_MUST_BE_FUNGIBLE_FAUCET_ID="malformed non-fungible asset: `ASSET[3]` is not a valid non-fungible faucet id" +const ERR_NON_FUNGIBLE_ASSET_FORMAT_ELEMENT_THREE_MUST_BE_FUNGIBLE_FAUCET_ID="malformed non-fungible asset: `ASSET[3]` is not a valid non-fungible faucet id" -const.ERR_NON_FUNGIBLE_ASSET_FORMAT_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO="malformed non-fungible asset: the most significant bit must be 0" +const ERR_NON_FUNGIBLE_ASSET_FORMAT_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO="malformed non-fungible asset: the most significant bit must be 0" -const.ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the fungible asset is not this faucet" +const ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the fungible asset is not this faucet" -const.ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the non-fungible asset is not this faucet" +const ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the non-fungible asset is not this faucet" # CONSTANT ACCESSORS # ================================================================================================= @@ -28,7 +27,7 @@ const.ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN="the origin of the non-fungibl #! #! Where: #! - fungible_asset_max_amount is the maximum amount of a fungible asset. -export.::$kernel::util::asset::get_fungible_asset_max_amount +pub use ::$kernel::util::asset::get_fungible_asset_max_amount # PROCEDURES # ================================================================================================= @@ -43,7 +42,7 @@ export.::$kernel::util::asset::get_fungible_asset_max_amount #! #! Panics if: #! - the asset is not well formed. -export.validate_fungible_asset +pub proc validate_fungible_asset # assert that ASSET[1] == ZERO dup.2 not assert.err=ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ONE_MUST_BE_ZERO # => [ASSET] @@ -58,7 +57,7 @@ export.validate_fungible_asset # => [ASSET] # assert that the max amount (ASSET[0]) of a fungible asset is not exceeded - dup.3 exec.get_fungible_asset_max_amount lte + dup.3 exec.::$kernel::util::asset::get_fungible_asset_max_amount lte assert.err=ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS # => [ASSET] end @@ -71,7 +70,7 @@ end #! Where: #! - ASSET is the asset to check. #! - is_fungible_asset is a boolean indicating whether the asset is fungible. -export.is_fungible_asset +pub proc is_fungible_asset # check the first element, it will be: # - zero for a fungible asset # - non zero for a non-fungible asset @@ -89,7 +88,7 @@ end #! #! Panics if: #! - the asset is not well formed. -export.validate_non_fungible_asset +pub proc validate_non_fungible_asset # assert that ASSET[3] is a valid account ID prefix # hack: because we only have the prefix we add a 0 as the suffix which is always valid push.0 dup.1 exec.account_id::validate @@ -109,7 +108,7 @@ end #! Where: #! - ASSET is the asset to check. #! - is_non_fungible_asset is a boolean indicating whether the asset is non-fungible. -export.is_non_fungible_asset +pub proc is_non_fungible_asset # check the first element, it will be: # - zero for a fungible asset # - non zero for a non-fungible asset @@ -127,7 +126,7 @@ end #! #! Panics if: #! - the asset is not well formed. -export.validate_asset +pub proc validate_asset # check if the asset is fungible exec.is_fungible_asset # => [is_fungible_asset, ASSET] @@ -150,7 +149,7 @@ end #! Where: #! - faucet_id_prefix is the prefix of the faucet's account ID. #! - ASSET is the asset to validate. -export.validate_fungible_asset_origin +pub proc validate_fungible_asset_origin # assert the origin of the asset is the faucet_id provided via the stack dup.3 dup.3 # => [asset_id_prefix, asset_id_suffix, faucet_id_prefix, faucet_id_suffix, ASSET] @@ -171,7 +170,7 @@ end #! Where: #! - faucet_id_prefix is the prefix of the faucet's account ID. #! - ASSET is the asset to validate. -export.validate_non_fungible_asset_origin +pub proc validate_non_fungible_asset_origin # assert the origin of the asset is the faucet_id prefix provided via the stack dup.1 assert_eq.err=ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN # => [ASSET] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm b/crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm similarity index 94% rename from crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm index 6d439c8e71..111224287f 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/asset_vault.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm @@ -1,30 +1,30 @@ -use.std::collections::smt -use.std::word +use miden::core::collections::smt +use miden::core::word -use.$kernel::account_id -use.$kernel::asset -use.$kernel::memory +use $kernel::account_id +use $kernel::asset +use $kernel::memory # ERRORS # ================================================================================================= -const.ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="get_balance can only be called on a fungible asset" +const ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="get_balance can only be called on a fungible asset" -const.ERR_VAULT_PEEK_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="peek_balance can only be called on a fungible asset" +const ERR_VAULT_PEEK_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="peek_balance can only be called on a fungible asset" -const.ERR_VAULT_HAS_NON_FUNGIBLE_ASSET_PROC_CAN_BE_CALLED_ONLY_WITH_NON_FUNGIBLE_ASSET="the has_non_fungible_asset procedure can only be called on a non-fungible faucet" +const ERR_VAULT_HAS_NON_FUNGIBLE_ASSET_PROC_CAN_BE_CALLED_ONLY_WITH_NON_FUNGIBLE_ASSET="the has_non_fungible_asset procedure can only be called on a non-fungible faucet" -const.ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding the fungible asset to the vault would exceed the max amount of 9223372036854775807" +const ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding the fungible asset to the vault would exceed the max amount of 9223372036854775807" -const.ERR_VAULT_ADD_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to add fungible asset to the asset vault due to the initial value being invalid" +const ERR_VAULT_ADD_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to add fungible asset to the asset vault due to the initial value being invalid" -const.ERR_VAULT_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="the non-fungible asset already exists in the asset vault" +const ERR_VAULT_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="the non-fungible asset already exists in the asset vault" -const.ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW="failed to remove the fungible asset from the vault since the amount of the asset in the vault is less than the amount to remove" +const ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW="failed to remove the fungible asset from the vault since the amount of the asset in the vault is less than the amount to remove" -const.ERR_VAULT_REMOVE_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to remove fungible asset from the asset vault due to the initial value being invalid" +const ERR_VAULT_REMOVE_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to remove fungible asset from the asset vault due to the initial value being invalid" -const.ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND="failed to remove non-existent non-fungible asset from the vault" +const ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND="failed to remove non-existent non-fungible asset from the vault" # EVENTS # ================================================================================================= @@ -32,13 +32,13 @@ const.ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND="failed to remove non-exi # The event is from `stdlib`, visible through the prefix and is _not_ a `TransactionEvent`. # Care must be taken it stays in sync with the `miden-stdlib` definition. Note that generally speaking # we should not emit `"stdlib"` events, consider this "hijacking", tread carefully. -const.SMT_PEEK_EVENT=event("stdlib::collections::smt::smt_peek") +const SMT_PEEK_EVENT=event("miden::core::collections::smt::smt_peek") # CONSTANTS # ================================================================================================= # The bitmask that when applied will set the fungible bit to zero. -const.INVERSE_FUNGIBLE_BITMASK_U32=0xffffffdf # last byte: 0b1101_1111 +const INVERSE_FUNGIBLE_BITMASK_U32=0xffffffdf # last byte: 0b1101_1111 # ACCESSORS # ================================================================================================= @@ -56,7 +56,7 @@ const.INVERSE_FUNGIBLE_BITMASK_U32=0xffffffdf # last byte: 0b1101_1111 #! #! Panics if: #! - the provided faucet ID is not an ID of a fungible faucet. -export.get_balance +pub proc get_balance # assert that the faucet id is a fungible faucet dup exec.account_id::is_fungible_faucet assert.err=ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET @@ -102,7 +102,7 @@ end #! #! Panics if: #! - the asset is not a fungible asset. -export.peek_balance +pub proc peek_balance # assert that the faucet id is a fungible faucet dup exec.account_id::is_fungible_faucet assert.err=ERR_VAULT_PEEK_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET @@ -148,7 +148,7 @@ end #! #! Panics if: #! - the ASSET is a fungible asset. -export.has_non_fungible_asset +pub proc has_non_fungible_asset # check if the asset is a non-fungible asset exec.asset::is_non_fungible_asset assert.err=ERR_VAULT_HAS_NON_FUNGIBLE_ASSET_PROC_CAN_BE_CALLED_ONLY_WITH_NON_FUNGIBLE_ASSET @@ -190,7 +190,7 @@ end #! #! Panics if: #! - the total value of assets is greater than or equal to 2^63. -export.add_fungible_asset +pub proc add_fungible_asset # Create the asset key from the asset. # --------------------------------------------------------------------------------------------- @@ -294,7 +294,7 @@ end #! #! Panics if: #! - the vault already contains the same non-fungible asset. -export.add_non_fungible_asset +pub proc add_non_fungible_asset # Build the asset key from the non-fungible asset. # --------------------------------------------------------------------------------------------- @@ -341,7 +341,7 @@ end #! - the asset is not valid. #! - the total value of two fungible assets is greater than or equal to 2^63. #! - the vault already contains the same non-fungible asset. -export.add_asset +pub proc add_asset # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET] @@ -381,7 +381,8 @@ end #! #! Panics if: #! - the amount of the asset in the vault is less than the amount to be removed. -export.remove_fungible_asset.4 +@locals(4) +pub proc remove_fungible_asset exec.build_fungible_asset_vault_key # => [ASSET_KEY, ASSET, vault_root_ptr] @@ -465,7 +466,7 @@ end #! #! Panics if: #! - the non-fungible asset is not found in the vault. -export.remove_non_fungible_asset +pub proc remove_non_fungible_asset # build non-fungible asset key dupw exec.build_non_fungible_asset_vault_key padw # => [pad(4), ASSET_KEY, ASSET, vault_root_ptr] @@ -504,7 +505,7 @@ end #! - the fungible asset is not found in the vault. #! - the amount of the fungible asset in the vault is less than the amount to be removed. #! - the non-fungible asset is not found in the vault. -export.remove_asset +pub proc remove_asset # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET, vault_root_ptr] @@ -534,7 +535,7 @@ end #! - faucet_id_{prefix, suffix} are the prefix and suffix felts of the faucet id of the fungible #! asset of interest. #! - asset is the peeked asset from the vault. -proc.peek_asset +proc peek_asset # load the asset vault root from memory padw movup.8 mem_loadw_be # => [ASSET_VAULT_ROOT, ASSET_KEY] @@ -566,7 +567,7 @@ end #! Where: #! - ASSET is the non-fungible asset for which the vault key is built. #! - ASSET_KEY is the vault key of the non-fungible asset. -export.build_non_fungible_asset_vault_key +pub proc build_non_fungible_asset_vault_key # create the asset key from the non-fungible asset by swapping hash0 with the faucet id # => [faucet_id_prefix, hash2, hash1, hash0] swap.3 @@ -596,7 +597,7 @@ end #! Where: #! - ASSET is the fungible asset for which the vault key is built. #! - ASSET_KEY is the vault key of the fungible asset. -export.build_fungible_asset_vault_key +pub proc build_fungible_asset_vault_key # => [faucet_id_prefix, faucet_id_suffix, 0, amount] push.0.0 diff --git a/crates/miden-lib/asm/kernels/transaction/lib/constants.masm b/crates/miden-protocol/asm/kernels/transaction/lib/constants.masm similarity index 83% rename from crates/miden-lib/asm/kernels/transaction/lib/constants.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/constants.masm index 7c1da10d20..fb0dfb08f9 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/constants.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/constants.masm @@ -2,33 +2,33 @@ # ================================================================================================= # The number of elements in a Word -const.WORD_SIZE=4 +const WORD_SIZE=4 # The maximum number of input values associated with a single note. -const.MAX_INPUTS_PER_NOTE=1024 +const MAX_INPUTS_PER_NOTE=1024 # The maximum number of assets that can be stored in a single note. -const.MAX_ASSETS_PER_NOTE=256 +const MAX_ASSETS_PER_NOTE=256 # The maximum number of notes that can be consumed in a single transaction. -const.MAX_INPUT_NOTES_PER_TX=1024 +const MAX_INPUT_NOTES_PER_TX=1024 # The size of the memory segment allocated to each note. -const.NOTE_MEM_SIZE=2048 +const NOTE_MEM_SIZE=2048 # The depth of the Merkle tree used to commit to notes produced in a block. -const.NOTE_TREE_DEPTH=16 +const NOTE_TREE_DEPTH=16 # The maximum number of notes that can be created in a single transaction. -const.MAX_OUTPUT_NOTES_PER_TX=1024 +const MAX_OUTPUT_NOTES_PER_TX=1024 # TYPES # ================================================================================================= # Type of storage slot item in the account storage -const.STORAGE_SLOT_TYPE_VALUE=0 -const.STORAGE_SLOT_TYPE_MAP=1 -const.STORAGE_SLOT_TYPE_ARRAY=2 +const STORAGE_SLOT_TYPE_VALUE=0 +const STORAGE_SLOT_TYPE_MAP=1 +const STORAGE_SLOT_TYPE_ARRAY=2 # PROCEDURES # ================================================================================================= @@ -40,7 +40,7 @@ const.STORAGE_SLOT_TYPE_ARRAY=2 #! #! Where: #! - word_size is the number of elements in a Word. -export.get_word_size +pub proc get_word_size push.WORD_SIZE end @@ -51,7 +51,7 @@ end #! #! Where: #! - max_inputs_per_note is the max inputs per note. -export.::$kernel::util::note::get_max_inputs_per_note +pub use ::$kernel::util::note::get_max_inputs_per_note #! Returns the max allowed number of assets per note. #! @@ -60,7 +60,7 @@ export.::$kernel::util::note::get_max_inputs_per_note #! #! Where: #! - max_assets_per_note is the max assets per note. -export.get_max_assets_per_note +pub proc get_max_assets_per_note push.MAX_ASSETS_PER_NOTE end @@ -71,7 +71,7 @@ end #! #! Where: #! - max_num_input_notes is the max number of input notes. -export.get_max_num_input_notes +pub proc get_max_num_input_notes push.MAX_INPUT_NOTES_PER_TX end @@ -82,7 +82,7 @@ end #! #! Where: #! - note_mem_size is the size of the memory segment allocated to each note. -export.get_note_mem_size +pub proc get_note_mem_size push.NOTE_MEM_SIZE end @@ -93,7 +93,7 @@ end #! #! Where: #! - note_tree_depth is the depth of the Merkle tree used to commit to notes produced in a block. -export.get_note_tree_depth +pub proc get_note_tree_depth push.NOTE_TREE_DEPTH end @@ -104,7 +104,7 @@ end #! #! Where: #! - max_num_output_notes is the max number of notes that can be created in a single transaction. -export.get_max_num_output_notes +pub proc get_max_num_output_notes push.MAX_OUTPUT_NOTES_PER_TX end @@ -115,7 +115,7 @@ end #! #! Where: #! - EMPTY_SMT_ROOT is the root of an empty Sparse Merkle Tree. -export.get_empty_smt_root +pub proc get_empty_smt_root push.15321474589252129342.17373224439259377994.15071539326562317628.3312677166725950353 end @@ -126,7 +126,7 @@ end #! #! Where: #! - type_storage_value is the type of storage slot item in the account storage. -export.get_storage_slot_type_value +pub proc get_storage_slot_type_value push.STORAGE_SLOT_TYPE_VALUE end @@ -137,7 +137,7 @@ end #! #! Where: #! - type_storage_map is the type of storage slot item in the account storage. -export.get_storage_slot_type_map +pub proc get_storage_slot_type_map push.STORAGE_SLOT_TYPE_MAP end @@ -148,6 +148,6 @@ end #! #! Where: #! - type_storage_array is the type of storage slot item in the account storage. -export.get_storage_slot_type_array +pub proc get_storage_slot_type_array push.STORAGE_SLOT_TYPE_ARRAY end diff --git a/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm similarity index 94% rename from crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm index f7281e5be9..196f4aeaa6 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/epilogue.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm @@ -1,54 +1,54 @@ -use.$kernel::account -use.$kernel::account_delta -use.$kernel::asset_vault -use.$kernel::constants -use.$kernel::memory -use.$kernel::note +use $kernel::account +use $kernel::account_delta +use $kernel::asset_vault +use $kernel::constants +use $kernel::memory +use $kernel::note -use.std::word +use miden::core::word # ERRORS # ================================================================================================= -const.ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME="total number of assets in the account and all involved notes must stay the same" +const ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME="total number of assets in the account and all involved notes must stay the same" -const.ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY="executed transaction neither changed the account state, nor consumed any notes" +const ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY="executed transaction neither changed the account state, nor consumed any notes" -const.ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT="auth procedure had been called from outside the epilogue" +const ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT="auth procedure has been called from outside the epilogue" -const.ERR_EPILOGUE_NONCE_CANNOT_BE_0="nonce cannot be 0 after an account-creating transaction" +const ERR_EPILOGUE_NONCE_CANNOT_BE_0="nonce cannot be 0 after an account-creating transaction" # CONSTANTS # ================================================================================================= # Event emitted to signal that the compute_fee procedure has obtained the current number of cycles. -const.EPILOGUE_AFTER_TX_CYCLES_OBTAINED_EVENT=event("miden::epilogue::after_tx_cycles_obtained") +const EPILOGUE_AFTER_TX_CYCLES_OBTAINED_EVENT=event("miden::epilogue::after_tx_cycles_obtained") # Event emitted to signal that the fee was computed. -const.EPILOGUE_BEFORE_TX_FEE_REMOVED_FROM_ACCOUNT_EVENT=event("miden::epilogue::before_tx_fee_removed_from_account") +const EPILOGUE_BEFORE_TX_FEE_REMOVED_FROM_ACCOUNT_EVENT=event("miden::epilogue::before_tx_fee_removed_from_account") # Event emitted to signal that an execution of the authentication procedure has started. -const.EPILOGUE_AUTH_PROC_START_EVENT=event("miden::epilogue::auth_proc_start") +const EPILOGUE_AUTH_PROC_START_EVENT=event("miden::epilogue::auth_proc_start") # Event emitted to signal that an execution of the authentication procedure has ended. -const.EPILOGUE_AUTH_PROC_END_EVENT=event("miden::epilogue::auth_proc_end") +const EPILOGUE_AUTH_PROC_END_EVENT=event("miden::epilogue::auth_proc_end") # An additional number of cyclces to account for the number of cycles that smt::set will take when # removing the computed fee from the asset vault. # Theoretically, this can safely be set to worst_case_cycles - best_case_cycles of smt::set. That's # because we can assume that the measured number of cycles already contains at least the best case # number of cycles, and so we only need to add the difference. -const.SMT_SET_ADDITIONAL_CYCLES=250 +const SMT_SET_ADDITIONAL_CYCLES=250 # The number of cycles the epilogue is estimated to take after compute_fee has been executed, # including an unknown cycle number of the above-mentioned call to smt::set. It is safe to assume # that this includes at least smt::set's best case number of cycles. # This can be _estimated_ using the transaction measurements on ExecutedTransaction and can be set # to the lowest observed value. -const.NUM_POST_COMPUTE_FEE_CYCLES=500 +const NUM_POST_COMPUTE_FEE_CYCLES=500 # The number of cycles the epilogue is estimated to take after compute_fee has been executed. -const.ESTIMATED_AFTER_COMPUTE_FEE_CYCLES=NUM_POST_COMPUTE_FEE_CYCLES+SMT_SET_ADDITIONAL_CYCLES +const ESTIMATED_AFTER_COMPUTE_FEE_CYCLES=NUM_POST_COMPUTE_FEE_CYCLES+SMT_SET_ADDITIONAL_CYCLES # OUTPUT NOTES PROCEDURES # ================================================================================================= @@ -72,7 +72,7 @@ const.ESTIMATED_AFTER_COMPUTE_FEE_CYCLES=NUM_POST_COMPUTE_FEE_CYCLES+SMT_SET_ADD #! - output_note_ptr is the start boundary of the output notes section. #! - output_notes_end_ptr is the end boundary of the output notes section. #! - mem[i] is the memory value stored at some address i. -proc.copy_output_notes_to_advice_map +proc copy_output_notes_to_advice_map # get the number of notes created by the transaction exec.memory::get_num_output_notes # => [num_notes, OUTPUT_NOTES_COMMITMENT] @@ -117,7 +117,7 @@ end #! #! Inputs: [] #! Outputs: [] -proc.build_output_vault +proc build_output_vault # copy final account vault root to output account vault root exec.memory::get_account_vault_root exec.memory::set_output_vault_root dropw # => [] @@ -203,7 +203,7 @@ end #! #! Inputs: [] #! Outputs: [] -proc.execute_auth_procedure +proc execute_auth_procedure emit.EPILOGUE_AUTH_PROC_START_EVENT padw padw padw @@ -220,7 +220,7 @@ proc.execute_auth_procedure # if auth procedure was called already, it must have been called by a user, which is disallowed exec.account::was_procedure_called - assertz.err=ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT + assertz.err=ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT # => [auth_procedure_ptr, AUTH_ARGS, pad(12)] # execute the auth procedure @@ -247,7 +247,7 @@ end #! #! Where: #! - fee_amount is the computed fee amount of the transaction in the native asset. -proc.compute_fee +proc compute_fee # get the number of cycles the transaction has taken to execute up this point clk # => [num_current_cycles] @@ -282,7 +282,7 @@ end #! - fee_amount is the computed fee amount of the transaction in the native asset. #! - FEE_ASSET is the fungible asset with amount set to fee_amount and the faucet ID set to the #! native asset. -proc.build_native_fee_asset +proc build_native_fee_asset exec.memory::get_native_asset_id # => [native_asset_id_prefix, native_asset_id_suffix, fee_amount] @@ -308,7 +308,7 @@ end #! #! Panics if: #! - the account vault does not contain the computed fee. -proc.compute_and_remove_fee +proc compute_and_remove_fee # compute the fee the tx needs to pay exec.compute_fee # => [fee_amount] @@ -377,7 +377,8 @@ end #! changing the account's state. The latter is validated as part of computing the account delta #! commitment. #! - the account vault does not contain the computed fee. -export.finalize_transaction.12 +@locals(12) +pub proc finalize_transaction # make sure that the context was switched back to the native account exec.memory::assert_native_account diff --git a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm b/crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm similarity index 94% rename from crates/miden-lib/asm/kernels/transaction/lib/faucet.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm index ef55eba543..d1b1fd2407 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/faucet.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm @@ -1,21 +1,21 @@ -use.$kernel::account -use.$kernel::account_id -use.$kernel::asset -use.$kernel::asset_vault -use.$kernel::memory +use $kernel::account +use $kernel::account_id +use $kernel::asset +use $kernel::asset_vault +use $kernel::memory # ERRORS # ================================================================================================= -const.ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT="asset mint operation would cause the new total supply to exceed the maximum allowed asset amount" +const ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT="asset mint operation would cause the new total supply to exceed the maximum allowed asset amount" -const.ERR_FAUCET_BURN_CANNOT_EXCEED_EXISTING_TOTAL_SUPPLY="asset amount to burn can not exceed the existing total supply" +const ERR_FAUCET_BURN_CANNOT_EXCEED_EXISTING_TOTAL_SUPPLY="asset amount to burn can not exceed the existing total supply" -const.ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED="failed to mint new non-fungible asset because it was already issued" +const ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED="failed to mint new non-fungible asset because it was already issued" -const.ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the burn_non_fungible_asset procedure can only be called on a non-fungible faucet" +const ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the burn_non_fungible_asset procedure can only be called on a non-fungible faucet" -const.ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND="failed to burn non-existent non-fungible asset in the vault" +const ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND="failed to burn non-existent non-fungible asset in the vault" # FUNGIBLE ASSETS # ================================================================================================== @@ -36,7 +36,7 @@ const.ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND="failed to burn non-existe #! executed against. #! - the asset is not well formed. #! - the total issuance after minting is greater than the maximum amount allowed. -export.mint_fungible_asset +pub proc mint_fungible_asset # assert that the asset is associated with the faucet the transaction is being executed against # and that the asset is valid exec.account::get_id exec.asset::validate_fungible_asset_origin @@ -78,7 +78,7 @@ end #! executed against. #! - the asset is not well formed. #! - the amount being burned is greater than the total input to the transaction. -proc.burn_fungible_asset +proc burn_fungible_asset # assert that the asset is associated with the faucet the transaction is being executed against # and that the asset is valid exec.account::get_id exec.asset::validate_fungible_asset_origin @@ -109,7 +109,7 @@ end #! Where: #! - total_issuance is the total issuance of the fungible faucet the transaction is being executed #! against. -export.get_total_issuance +pub proc get_total_issuance # fetch the TOTAL_ISSUANCE from storage exec.account::get_faucet_sysdata_slot_id exec.account::get_item # => [TOTAL_ISSUANCE] @@ -136,7 +136,7 @@ end #! - the non-fungible asset being minted is not associated with the faucet the transaction is being #! executed against. #! - the non-fungible asset being minted already exists. -proc.mint_non_fungible_asset +proc mint_non_fungible_asset # assert that the asset is associated with the faucet the transaction is being executed against # and that the asset is valid exec.account::get_id swap drop exec.asset::validate_non_fungible_asset_origin @@ -180,7 +180,7 @@ end #! executed against. #! - the non-fungible asset being burned does not exist or was not provided as input to the #! transaction via a note or the accounts vault. -proc.burn_non_fungible_asset +proc burn_non_fungible_asset # assert that we are executing a transaction against the non-fungible faucet (access checks) exec.account::get_id swap drop exec.account_id::is_non_fungible_faucet assert.err=ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET @@ -225,7 +225,7 @@ end #! Panics if: #! - the ASSET is a fungible asset. #! - the ASSET is not associated with the faucet the transaction is being executed against. -export.is_non_fungible_asset_issued +pub proc is_non_fungible_asset_issued # assert that the asset is associated with the faucet the transaction is being executed against # and that the asset is valid exec.account::get_id swap drop exec.asset::validate_non_fungible_asset_origin @@ -270,7 +270,7 @@ end #! - For fungible faucets if the total issuance after minting is greater than the maximum amount #! allowed. #! - For non-fungible faucets if the non-fungible asset being minted already exists. -export.mint +pub proc mint # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET] @@ -303,7 +303,7 @@ end #! transaction. #! - For non-fungible faucets if the non-fungible asset being burned does not exist or was not #! provided as input to the transaction via a note or the accounts vault. -export.burn +pub proc burn # check if the asset is a fungible asset exec.asset::is_fungible_asset # => [is_fungible_asset, ASSET] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/input_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/input_note.masm similarity index 92% rename from crates/miden-lib/asm/kernels/transaction/lib/input_note.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/input_note.masm index 717bc67ee0..dd247047ce 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/input_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/input_note.masm @@ -1,9 +1,9 @@ -use.$kernel::memory +use $kernel::memory # ERRORS # ================================================================================================= -const.ERR_INPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested input note index should be less than the total number of input notes" +const ERR_INPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested input note index should be less than the total number of input notes" # INPUT NOTE PROCEDURES # ================================================================================================= @@ -17,7 +17,7 @@ const.ERR_INPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested input note index should be l #! - input_note_ptr is the memory address of the data segment for the requested input note. #! - num_assets is the number of assets in the specified note. #! - ASSETS_COMMITMENT is a sequential hash of the assets in the specified note. -export.get_assets_info +pub proc get_assets_info # get the number of assets in the note dup exec.memory::get_input_note_num_assets # => [num_assets, input_note_ptr] @@ -31,7 +31,7 @@ end #! #! Inputs: [note_index] #! Outputs: [note_index] -export.assert_note_index_in_bounds +pub proc assert_note_index_in_bounds # assert that the provided note index is less than the total number of notes dup exec.memory::get_num_input_notes # => [input_notes_num, note_index, note_index] @@ -53,7 +53,7 @@ end #! #! Panics if: #! - the note index is greater or equal to the total number of input notes. -export.get_input_note_ptr +pub proc get_input_note_ptr # asset that the provided input note index is valid exec.assert_note_index_in_bounds # => [idx] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/link_map.masm b/crates/miden-protocol/asm/kernels/transaction/lib/link_map.masm similarity index 94% rename from crates/miden-lib/asm/kernels/transaction/lib/link_map.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/link_map.masm index bef4faffff..caa4323e13 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/link_map.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/link_map.masm @@ -1,6 +1,6 @@ -use.std::collections::smt -use.std::word -use.$kernel::memory +use miden::core::collections::smt +use miden::core::word +use $kernel::memory # A link map is a map data structure based on a sorted linked list. # @@ -119,58 +119,58 @@ use.$kernel::memory # ERRORS # ================================================================================================= -const.ERR_LINK_MAP_CANNOT_BE_EMPTY_ON_ABSENCE_AFTER_ENTRY="map cannot be empty when proving absence after an entry" +const ERR_LINK_MAP_CANNOT_BE_EMPTY_ON_ABSENCE_AFTER_ENTRY="map cannot be empty when proving absence after an entry" -const.ERR_LINK_MAP_PROVIDED_KEY_NOT_EQUAL_TO_ENTRY_KEY="provided key does not match key in map entry" +const ERR_LINK_MAP_PROVIDED_KEY_NOT_EQUAL_TO_ENTRY_KEY="provided key does not match key in map entry" -const.ERR_LINK_MAP_PROVIDED_KEY_NOT_GREATER_THAN_ENTRY_KEY="provided key is not greater than the entry key" +const ERR_LINK_MAP_PROVIDED_KEY_NOT_GREATER_THAN_ENTRY_KEY="provided key is not greater than the entry key" -const.ERR_LINK_MAP_PROVIDED_KEY_NOT_LESS_THAN_ENTRY_KEY="provided key is not less than the entry key" +const ERR_LINK_MAP_PROVIDED_KEY_NOT_LESS_THAN_ENTRY_KEY="provided key is not less than the entry key" -const.ERR_LINK_MAP_ENTRY_PTR_IS_OUTSIDE_VALID_MEMORY_REGION="host-provided entry ptr is outside the valid memory region" +const ERR_LINK_MAP_ENTRY_PTR_IS_OUTSIDE_VALID_MEMORY_REGION="host-provided entry ptr is outside the valid memory region" -const.ERR_LINK_MAP_ENTRY_PTR_IS_NOT_ENTRY_ALIGNED="host-provided entry ptr is not 'link map entry'-aligned" +const ERR_LINK_MAP_ENTRY_PTR_IS_NOT_ENTRY_ALIGNED="host-provided entry ptr is not 'link map entry'-aligned" -const.ERR_LINK_MAP_MAP_PTR_IN_ENTRY_DOES_NOT_MATCH_EXPECTED_MAP_PTR="map ptr stored in host-provided entry does not match actual pointer of the map" +const ERR_LINK_MAP_MAP_PTR_IN_ENTRY_DOES_NOT_MATCH_EXPECTED_MAP_PTR="map ptr stored in host-provided entry does not match actual pointer of the map" # CONSTANTS # ================================================================================================= # The offset of the prev_entry_ptr in an entry's metadata. -const.PREV_ENTRY_PTR_OFFSET=1 +const PREV_ENTRY_PTR_OFFSET=1 # The offset of the next_entry_ptr in an entry's metadata. -const.NEXT_ENTRY_PTR_OFFSET=2 +const NEXT_ENTRY_PTR_OFFSET=2 # The offset of the KEY in an entry. -const.KEY_OFFSET=4 +const KEY_OFFSET=4 # The offset of the VALUE0 in an entry. -const.VALUE0_OFFSET=8 +const VALUE0_OFFSET=8 # The offset of the VALUE1 in an entry. -const.VALUE1_OFFSET=12 +const VALUE1_OFFSET=12 # The value of the Update operation for a set event. -const.INSERT_OPERATION_UPDATE=0 +const INSERT_OPERATION_UPDATE=0 # The value of the InsertAtHead operation for a set event. -const.INSERT_OPERATION_AT_HEAD=1 +const INSERT_OPERATION_AT_HEAD=1 # The value of the Found operation for a get event. -const.GET_OPERATION_FOUND=0 +const GET_OPERATION_FOUND=0 # The value of the AbsentAtHead operation for a get event. -const.GET_OPERATION_ABSENT_AT_HEAD=1 +const GET_OPERATION_ABSENT_AT_HEAD=1 # EVENTS # ================================================================================================= # Event emitted when an entry is set. -const.LINK_MAP_SET_EVENT=event("miden::link_map::set") +const LINK_MAP_SET_EVENT=event("miden::link_map::set") # Event emitted when an entry is fetched. -const.LINK_MAP_GET_EVENT=event("miden::link_map::get") +const LINK_MAP_GET_EVENT=event("miden::link_map::get") # LINK MAP PROCEDURES # ================================================================================================= @@ -198,7 +198,7 @@ const.LINK_MAP_GET_EVENT=event("miden::link_map::get") #! Panics if: #! - the host provides faulty advice. See panic sections of assert_entry_ptr_is_valid, #! update_entry, insert_at_head, insert_after_entry. -export.set +pub proc set emit.LINK_MAP_SET_EVENT adv_push.2 # => [operation, entry_ptr, map_ptr, KEY, VALUE0, VALUE1] @@ -265,7 +265,7 @@ end #! Panics if: #! - the host provides faulty advice. See panic sections of assert_entry_ptr_is_valid, #! get_existing_value, assert_absent_at_head, assert_absent_after_entry. -export.get +pub proc get emit.LINK_MAP_GET_EVENT adv_push.2 # => [get_operation, entry_ptr, map_ptr, KEY] @@ -318,7 +318,7 @@ end #! #! Inputs: [map_ptr] #! Outputs: [is_empty] -export.is_empty +pub proc is_empty mem_load eq.0 end @@ -328,7 +328,7 @@ end #! #! Inputs: [map_ptr] #! Outputs: [has_next, iter] -export.iter +pub proc iter exec.get_head # => [entry_ptr] @@ -349,7 +349,7 @@ end #! #! Inputs: [iter] #! Outputs: [KEY, VALUE0, VALUE1, has_next, next_iter] -export.next_key_double_value +pub proc next_key_double_value dup exec.next_key # => [KEY, has_next, next_entry_ptr, entry_ptr = iter] @@ -367,7 +367,7 @@ end #! #! Inputs: [iter] #! Outputs: [KEY, VALUE0, has_next, next_iter] -export.next_key_value +pub proc next_key_value dup exec.next_key # => [KEY, has_next, next_entry_ptr, entry_ptr = iter] @@ -385,7 +385,7 @@ end #! #! Inputs: [iter] #! Outputs: [KEY, has_next, next_iter] -export.next_key +pub proc next_key dup exec.get_next_entry_ptr # => [next_entry_ptr, entry_ptr = iter] @@ -406,7 +406,7 @@ end #! #! Panics if: #! - the KEY is not less than the key in the head of the map, unless the map is empty. -proc.insert_at_head +proc insert_at_head exec.memory::link_map_malloc # => [entry_ptr, map_ptr, KEY, VALUE0, VALUE1] @@ -465,7 +465,7 @@ end #! #! Panics if: #! - the key in the entry does not match the provided KEY. -proc.update_entry +proc update_entry dup movdn.5 # => [entry_ptr, KEY, entry_ptr, VALUE0, VALUE1] @@ -485,7 +485,7 @@ end #! - the KEY is not greater than the key in the prev entry. #! - the KEY is not less than the key in prev_entry.next_entry, unless the prev entry is the last #! one in the map. -proc.insert_after_entry +proc insert_after_entry movdn.5 # => [map_ptr, KEY, prev_entry_ptr, VALUE0, VALUE1] @@ -567,7 +567,7 @@ end #! #! Inputs: [entry_ptr, map_ptr, KEY, VALUE0, VALUE1] #! Outputs: [] -proc.insert_pair +proc insert_pair swap dup.1 # => [entry_ptr, map_ptr, entry_ptr, KEY, VALUE0, VALUE1] @@ -597,7 +597,7 @@ end #! #! Panics if: #! - the KEY is not less than the key in the head of the map, unless the map is empty. -proc.assert_absent_at_head +proc assert_absent_at_head dup exec.is_empty # => [is_empty, map_ptr, KEY] @@ -627,7 +627,7 @@ end #! - the map is empty. #! - the KEY is not greater than the key in the entry. #! - the KEY is not less than the key in entry.next_entry, unless the entry is the last one in the map. -proc.assert_absent_after_entry +proc assert_absent_after_entry swap exec.is_empty assertz.err=ERR_LINK_MAP_CANNOT_BE_EMPTY_ON_ABSENCE_AFTER_ENTRY # => [entry_ptr, KEY] @@ -666,7 +666,7 @@ end #! #! Panics if: #! - the key in the entry does not match KEY. -proc.get_existing_value +proc get_existing_value movdn.4 dup.4 # => [entry_ptr, KEY, entry_ptr] @@ -684,7 +684,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [has_next] -proc.has_next +proc has_next exec.get_next_entry_ptr eq.0 not end @@ -692,7 +692,7 @@ end #! #! Inputs: [entry_ptr, next_entry_ptr] #! Outputs: [] -proc.set_next_entry_ptr +proc set_next_entry_ptr add.NEXT_ENTRY_PTR_OFFSET mem_store # => [] end @@ -701,7 +701,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [next_entry_ptr] -proc.get_next_entry_ptr +proc get_next_entry_ptr add.NEXT_ENTRY_PTR_OFFSET mem_load # => [next_entry_ptr] end @@ -710,7 +710,7 @@ end #! #! Inputs: [entry_ptr, prev_entry_ptr] #! Outputs: [] -proc.set_prev_entry_ptr +proc set_prev_entry_ptr add.PREV_ENTRY_PTR_OFFSET mem_store # => [] end @@ -719,7 +719,7 @@ end #! #! Inputs: [entry_ptr, VALUE0, VALUE1] #! Outputs: [] -proc.set_value +proc set_value dup movdn.5 # => [entry_ptr, VALUE0, entry_ptr, VALUE1] @@ -734,7 +734,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [VALUE0, VALUE1] -proc.get_values +proc get_values dup exec.get_value1 # => [VALUE1, entry_ptr] @@ -746,7 +746,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [VALUE0] -proc.get_value0 +proc get_value0 padw movup.4 # => [entry_ptr, pad(4)] @@ -758,7 +758,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [VALUE1] -proc.get_value1 +proc get_value1 padw movup.4 # => [entry_ptr, pad(4)] @@ -770,7 +770,7 @@ end #! #! Inputs: [entry_ptr, KEY] #! Outputs: [] -proc.set_key +proc set_key add.KEY_OFFSET mem_storew_be dropw end @@ -778,7 +778,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [KEY] -proc.get_key +proc get_key padw movup.4 # => [entry_ptr, pad(4)] @@ -790,7 +790,7 @@ end #! #! Inputs: [map_ptr, entry_ptr] #! Outputs: [] -proc.set_head +proc set_head mem_store end @@ -798,7 +798,7 @@ end #! #! Inputs: [map_ptr] #! Outputs: [entry_ptr] -proc.get_head +proc get_head mem_load end @@ -806,7 +806,7 @@ end #! #! Inputs: [entry_ptr, map_ptr] #! Outputs: [] -proc.set_map_ptr +proc set_map_ptr mem_store end @@ -814,7 +814,7 @@ end #! #! Inputs: [entry_ptr] #! Outputs: [map_ptr] -proc.get_map_ptr +proc get_map_ptr mem_load end @@ -825,7 +825,7 @@ end #! #! Inputs: [entry_ptr, KEY] #! Outputs: [] -proc.assert_key_is_equal +proc assert_key_is_equal exec.get_key swapw # => [KEY, ENTRY_KEY] @@ -837,7 +837,7 @@ end #! #! Inputs: [entry_ptr, KEY] #! Outputs: [] -proc.assert_key_is_greater +proc assert_key_is_greater exec.get_key # => [ENTRY_KEY, KEY] @@ -848,7 +848,7 @@ end #! #! Inputs: [entry_ptr, KEY] #! Outputs: [] -proc.assert_key_is_less +proc assert_key_is_less exec.get_key # => [ENTRY_KEY, KEY] @@ -867,7 +867,7 @@ end #! - entry ptr is "link map entry"-aligned, i.e. entry_ptr % LINK_MAP_ENTRY_SIZE == 0. This #! works because every entry ptr is a multiple of LINK_MAP_ENTRY_SIZE. #! - entry's map ptr is equal to the given map_ptr. -export.assert_entry_ptr_is_valid +pub proc assert_entry_ptr_is_valid # Check entry pointer is in valid memory range. # ------------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm similarity index 86% rename from crates/miden-lib/asm/kernels/transaction/lib/memory.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/memory.masm index 6f103d1295..b1d93d415f 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm @@ -1,21 +1,20 @@ -use.$kernel::constants -use.$kernel::account -use.std::mem +use $kernel::constants +use miden::core::mem # ERRORS # ================================================================================================= -const.ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255" +const ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255" -const.ERR_ACCOUNT_IS_NOT_NATIVE="the active account is not native" +const ERR_ACCOUNT_IS_NOT_NATIVE="the active account is not native" -const.ERR_ACCOUNT_STACK_OVERFLOW="depth of the nested FPI calls exceeded 64" +const ERR_ACCOUNT_STACK_OVERFLOW="depth of the nested FPI calls exceeded 64" -const.ERR_ACCOUNT_STACK_UNDERFLOW="failed to end foreign context because the active account is the native account" +const ERR_ACCOUNT_STACK_UNDERFLOW="failed to end foreign context because the active account is the native account" -const.ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT="creation of a foreign context against the native account is forbidden" +const ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT="creation of a foreign context against the native account is forbidden" -const.ERR_LINK_MAP_MAX_ENTRIES_EXCEEDED="number of link map entries exceeds maximum" +const ERR_LINK_MAP_MAX_ENTRIES_EXCEEDED="number of link map entries exceeds maximum" # MEMORY ADDRESS CONSTANTS # ================================================================================================= @@ -24,134 +23,134 @@ const.ERR_LINK_MAP_MAX_ENTRIES_EXCEEDED="number of link map entries exceeds maxi # ------------------------------------------------------------------------------------------------- # The memory address at which a pointer to the currently active input note is stored. -const.ACTIVE_INPUT_NOTE_PTR=0 +const ACTIVE_INPUT_NOTE_PTR=0 # The memory address at which the number of output notes is stored. -const.NUM_OUTPUT_NOTES_PTR=4 +const NUM_OUTPUT_NOTES_PTR=4 # The memory address at which the input vault root is stored. -const.INPUT_VAULT_ROOT_PTR=8 +const INPUT_VAULT_ROOT_PTR=8 # The memory address at which the output vault root is stored. -const.OUTPUT_VAULT_ROOT_PTR=12 +const OUTPUT_VAULT_ROOT_PTR=12 # The memory address at which the dirty flag of the storage commitment of the native account is # stored. # # This binary flag specifies whether the commitment is outdated: it holds 1 if some changes were # made to the account storage since the last re-computation, and 0 otherwise. -const.NATIVE_ACCT_STORAGE_COMMITMENT_DIRTY_FLAG_PTR=16 +const NATIVE_ACCT_STORAGE_COMMITMENT_DIRTY_FLAG_PTR=16 # The memory address at which the absolute expiration block number is stored. -const.TX_EXPIRATION_BLOCK_NUM_PTR=20 +const TX_EXPIRATION_BLOCK_NUM_PTR=20 # The memory address at which the pointer to the account stack element containing the pointer to the # currently accessing account (active account) data is stored. -const.ACCOUNT_STACK_TOP_PTR=24 +const ACCOUNT_STACK_TOP_PTR=24 # Pointer to the first element on the account stack. -const.MIN_ACCOUNT_STACK_PTR=25 +const MIN_ACCOUNT_STACK_PTR=25 # Pointer to the last element on the account stack. -const.MAX_ACCOUNT_STACK_PTR=88 +const MAX_ACCOUNT_STACK_PTR=88 # GLOBAL INPUTS # ------------------------------------------------------------------------------------------------- # The memory address at which the global inputs section begins. -const.GLOBAL_INPUTS_SECTION_OFFSET=400 +const GLOBAL_INPUTS_SECTION_OFFSET=400 # The memory address at which the transaction reference block's commitment is stored. -const.BLOCK_COMMITMENT_PTR=400 +const BLOCK_COMMITMENT_PTR=400 # The memory address at which the native account ID is stored. -const.NATIVE_ACCT_ID_PTR=404 +const NATIVE_ACCT_ID_PTR=404 # The memory address at which the initial account commitment is stored. -const.INIT_ACCOUNT_COMMITMENT_PTR=408 +const INIT_ACCOUNT_COMMITMENT_PTR=408 # The memory address at which the initial nonce is stored. -const.INIT_NONCE_PTR=412 +const INIT_NONCE_PTR=412 # The memory address at which the initial vault root of the native account is stored. -const.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR=416 +const INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR=416 # The memory address at which the initial storage commitment of the native account is stored. -const.INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT_PTR=420 +const INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT_PTR=420 # The memory address at which the input notes commitment is stored. -const.INPUT_NOTES_COMMITMENT_PTR=424 +const INPUT_NOTES_COMMITMENT_PTR=424 # The memory address at which the transaction script mast root is stored. -const.TX_SCRIPT_ROOT_PTR=428 +const TX_SCRIPT_ROOT_PTR=428 # The memory address at which the transaction script arguments are stored. -const.TX_SCRIPT_ARGS_PTR=432 +const TX_SCRIPT_ARGS_PTR=432 # The memory address at which the auth procedure arguments are stored. -const.AUTH_ARGS_PTR=436 +const AUTH_ARGS_PTR=436 # GLOBAL BLOCK DATA # ------------------------------------------------------------------------------------------------- # The memory address at which the block data section begins -const.BLOCK_DATA_SECTION_OFFSET=800 +const BLOCK_DATA_SECTION_OFFSET=800 # The memory address at which the previous block commitment is stored -const.PREV_BLOCK_COMMITMENT_PTR=800 +const PREV_BLOCK_COMMITMENT_PTR=800 # The memory address at which the chain commitment is stored -const.CHAIN_COMMITMENT_PTR=804 +const CHAIN_COMMITMENT_PTR=804 # The memory address at which the account root is stored -const.ACCT_DB_ROOT_PTR=808 +const ACCT_DB_ROOT_PTR=808 # The memory address at which the nullifier root is stored -const.NULLIFIER_ROOT_PTR=812 +const NULLIFIER_ROOT_PTR=812 # The memory address at which the tx commitment is stored -const.TX_COMMITMENT_PTR=816 +const TX_COMMITMENT_PTR=816 # The memory address at which the kernel commitment is stored -const.TX_KERNEL_COMMITMENT_PTR=820 +const TX_KERNEL_COMMITMENT_PTR=820 # The memory address at which the proof commitment is stored -const.VALIDATOR_KEY_COMMITMENT_PTR=824 +const VALIDATOR_KEY_COMMITMENT_PTR=824 # The memory address at which the block metadata is stored [block_number, version, timestamp, 0] -const.BLOCK_METADATA_PTR=828 +const BLOCK_METADATA_PTR=828 # The memory address at which the fee parameters are stored. These occupy a double word. # [native_asset_id_suffix, native_asset_id_prefix, verification_base_fee, 0] # [0, 0, 0, 0] -const.FEE_PARAMETERS_PTR=832 +const FEE_PARAMETERS_PTR=832 # The memory address at which the verification base fee is stored. -const.VERIFICATION_BASE_FEE_PTR=FEE_PARAMETERS_PTR+2 +const VERIFICATION_BASE_FEE_PTR=FEE_PARAMETERS_PTR+2 # The memory address at which the note root is stored -const.NOTE_ROOT_PTR=840 +const NOTE_ROOT_PTR=840 # PARTIAL BLOCKCHAIN # ------------------------------------------------------------------------------------------------- # The memory address at which the chain data section begins -const.PARTIAL_BLOCKCHAIN_PTR=1200 +const PARTIAL_BLOCKCHAIN_PTR=1200 # The memory address at which the total number of leaves in the partial blockchain is stored -const.PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR=1200 +const PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR=1200 # The memory address at which the partial blockchain peaks are stored -const.PARTIAL_BLOCKCHAIN_PEAKS_PTR=1204 +const PARTIAL_BLOCKCHAIN_PEAKS_PTR=1204 # KERNEL DATA # ------------------------------------------------------------------------------------------------- # The memory address at which the number of the kernel procedures is stored. -const.NUM_KERNEL_PROCEDURES_PTR=1600 +const NUM_KERNEL_PROCEDURES_PTR=1600 # The memory address at which the hashes of kernel procedures begin. -const.KERNEL_PROCEDURES_PTR=1604 +const KERNEL_PROCEDURES_PTR=1604 # ACCOUNT DATA # ------------------------------------------------------------------------------------------------- @@ -159,94 +158,94 @@ const.KERNEL_PROCEDURES_PTR=1604 # The largest memory address which can be used to load the foreign account data. # It is computed as `2048 * 64 * 4` -- this is the memory address where the data block of the 64th # account starts. -const.MAX_FOREIGN_ACCOUNT_PTR=524288 +const MAX_FOREIGN_ACCOUNT_PTR=524288 # The memory address at which the native account data is stored. -const.NATIVE_ACCOUNT_DATA_PTR=8192 +const NATIVE_ACCOUNT_DATA_PTR=8192 # The length of the memory interval that the account data occupies. -const.ACCOUNT_DATA_LENGTH=8192 +const ACCOUNT_DATA_LENGTH=8192 # The number of field elements it takes to store one account procedure. # TODO: Duplicated in account.masm, can we remove that? -const.ACCOUNT_PROCEDURE_DATA_LENGTH=4 +const ACCOUNT_PROCEDURE_DATA_LENGTH=4 # The offsets at which the account data is stored relative to the start of the account data segment. -const.ACCT_ID_AND_NONCE_OFFSET=0 -const.ACCT_NONCE_OFFSET=3 -const.ACCT_VAULT_ROOT_OFFSET=4 -const.ACCT_STORAGE_COMMITMENT_OFFSET=8 -const.ACCT_CODE_COMMITMENT_OFFSET=12 -const.ACCT_CORE_DATA_SECTION_END_OFFSET=16 -const.NUM_ACCT_PROCEDURES_OFFSET=28 -const.ACCT_PROCEDURES_SECTION_OFFSET=32 -const.ACCT_PROCEDURES_CALL_TRACKING_OFFSET=2084 -const.NUM_ACCT_STORAGE_SLOTS_OFFSET=2340 -const.ACCT_STORAGE_SLOTS_SECTION_OFFSET=2344 +const ACCT_ID_AND_NONCE_OFFSET=0 +const ACCT_NONCE_OFFSET=3 +const ACCT_VAULT_ROOT_OFFSET=4 +const ACCT_STORAGE_COMMITMENT_OFFSET=8 +const ACCT_CODE_COMMITMENT_OFFSET=12 +const ACCT_CORE_DATA_SECTION_END_OFFSET=16 +const NUM_ACCT_PROCEDURES_OFFSET=28 +const ACCT_PROCEDURES_SECTION_OFFSET=32 +const ACCT_PROCEDURES_CALL_TRACKING_OFFSET=2084 +const NUM_ACCT_STORAGE_SLOTS_OFFSET=2340 +const ACCT_STORAGE_SLOTS_SECTION_OFFSET=2344 # Offset at which an exact copy of the account's storage slot section starting at # ACCT_STORAGE_SLOTS_SECTION_OFFSET is kept. This keeps the initial values of the account storage # slots accessible to enable computing a diff between the initial and current account storage slots # for use in the account delta. This section is only present for the native account. -const.ACCT_INITIAL_STORAGE_SLOTS_SECTION_OFFSET=4384 +const ACCT_INITIAL_STORAGE_SLOTS_SECTION_OFFSET=4384 # NATIVE ACCOUNT DELTA # ------------------------------------------------------------------------------------------------- # The link map pointer at which the delta of the fungible asset vault is stored. -const.ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR=532480 +const ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR=532480 # The link map pointer at which the delta of the non-fungible asset vault is stored. -const.ACCOUNT_DELTA_NON_FUNGIBLE_ASSET_PTR=ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR+4 +const ACCOUNT_DELTA_NON_FUNGIBLE_ASSET_PTR=ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR+4 # The section of link map pointers where storage map deltas are stored. # This section is offset by `slot index` to get the link map ptr for the storage map # delta at that slot index. Slot indices that are not map slots are simply unused. -const.ACCOUNT_DELTA_STORAGE_MAP_SECTION=ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR+8 +const ACCOUNT_DELTA_STORAGE_MAP_SECTION=ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR+8 # INPUT NOTES DATA # ------------------------------------------------------------------------------------------------- # The memory address at which the input note section begins. -const.INPUT_NOTE_SECTION_OFFSET=4194304 +const INPUT_NOTE_SECTION_OFFSET=4194304 # The memory address at which the nullifier section of the input notes begins. -const.INPUT_NOTE_NULLIFIER_SECTION_PTR=4194308 +const INPUT_NOTE_NULLIFIER_SECTION_PTR=4194308 # The memory address at which the input note data section begins. -const.INPUT_NOTE_DATA_SECTION_OFFSET=4259840 +const INPUT_NOTE_DATA_SECTION_OFFSET=4259840 # The memory address at which the number of input notes is stored. -const.NUM_INPUT_NOTES_PTR=INPUT_NOTE_SECTION_OFFSET +const NUM_INPUT_NOTES_PTR=INPUT_NOTE_SECTION_OFFSET # The offsets at which data of an input note is stored relative to the start of its data segment -const.INPUT_NOTE_ID_OFFSET=0 -const.INPUT_NOTE_CORE_DATA_OFFSET=4 -const.INPUT_NOTE_SERIAL_NUM_OFFSET=4 -const.INPUT_NOTE_SCRIPT_ROOT_OFFSET=8 -const.INPUT_NOTE_INPUTS_COMMITMENT_OFFSET=12 -const.INPUT_NOTE_ASSETS_COMMITMENT_OFFSET=16 -const.INPUT_NOTE_RECIPIENT_OFFSET=20 -const.INPUT_NOTE_METADATA_OFFSET=24 -const.INPUT_NOTE_ARGS_OFFSET=28 -const.INPUT_NOTE_NUM_INPUTS_OFFSET=32 -const.INPUT_NOTE_NUM_ASSETS_OFFSET=36 -const.INPUT_NOTE_ASSETS_OFFSET=40 +const INPUT_NOTE_ID_OFFSET=0 +const INPUT_NOTE_CORE_DATA_OFFSET=4 +const INPUT_NOTE_SERIAL_NUM_OFFSET=4 +const INPUT_NOTE_SCRIPT_ROOT_OFFSET=8 +const INPUT_NOTE_INPUTS_COMMITMENT_OFFSET=12 +const INPUT_NOTE_ASSETS_COMMITMENT_OFFSET=16 +const INPUT_NOTE_RECIPIENT_OFFSET=20 +const INPUT_NOTE_METADATA_OFFSET=24 +const INPUT_NOTE_ARGS_OFFSET=28 +const INPUT_NOTE_NUM_INPUTS_OFFSET=32 +const INPUT_NOTE_NUM_ASSETS_OFFSET=36 +const INPUT_NOTE_ASSETS_OFFSET=40 # OUTPUT NOTES # ------------------------------------------------------------------------------------------------- # The memory address at which the output notes section begins. -const.OUTPUT_NOTE_SECTION_OFFSET=16777216 +const OUTPUT_NOTE_SECTION_OFFSET=16777216 # The offsets at which data of an output note is stored relative to the start of its data segment. -const.OUTPUT_NOTE_ID_OFFSET=0 -const.OUTPUT_NOTE_METADATA_OFFSET=4 -const.OUTPUT_NOTE_RECIPIENT_OFFSET=8 -const.OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=12 -const.OUTPUT_NOTE_NUM_ASSETS_OFFSET=16 -const.OUTPUT_NOTE_DIRTY_FLAG_OFFSET=17 -const.OUTPUT_NOTE_ASSETS_OFFSET=20 +const OUTPUT_NOTE_ID_OFFSET=0 +const OUTPUT_NOTE_METADATA_OFFSET=4 +const OUTPUT_NOTE_RECIPIENT_OFFSET=8 +const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=12 +const OUTPUT_NOTE_NUM_ASSETS_OFFSET=16 +const OUTPUT_NOTE_DIRTY_FLAG_OFFSET=17 +const OUTPUT_NOTE_ASSETS_OFFSET=20 # LINK MAP MEMORY # ------------------------------------------------------------------------------------------------- @@ -254,21 +253,21 @@ const.OUTPUT_NOTE_ASSETS_OFFSET=20 # The inclusive start of the link map dynamic memory region. # Chosen as a number greater than 2^25 such that all entry pointers are multiples of # LINK_MAP_ENTRY_SIZE. That enables a simpler check in assert_entry_ptr_is_valid. -const.LINK_MAP_REGION_START_PTR=33554448 +const LINK_MAP_REGION_START_PTR=33554448 # The non-inclusive end of the link map dynamic memory region. # This happens to be 2^26, but if it is changed, it should be chosen as a number such that # LINK_MAP_REGION_END_PTR - LINK_MAP_REGION_START_PTR is a multiple of LINK_MAP_ENTRY_SIZE, # because that enables checking whether a newly allocated entry pointer is at the end of the range # using equality rather than lt/gt in link_map_malloc. -const.LINK_MAP_REGION_END_PTR=67108864 +const LINK_MAP_REGION_END_PTR=67108864 # LINK_MAP_REGION_START_PTR + the currently used size stored at this pointer defines the next # entry pointer that will be allocated. -const.LINK_MAP_USED_MEMORY_SIZE=33554432 +const LINK_MAP_USED_MEMORY_SIZE=33554432 # The size of each map entry, i.e. four words. -const.LINK_MAP_ENTRY_SIZE=16 +const LINK_MAP_ENTRY_SIZE=16 # MEMORY PROCEDURES # ================================================================================================= @@ -283,7 +282,7 @@ const.LINK_MAP_ENTRY_SIZE=16 #! #! Where: #! - num_output_notes is the number of output notes created in this transaction so far. -export.get_num_output_notes +pub proc get_num_output_notes mem_load.NUM_OUTPUT_NOTES_PTR end @@ -294,7 +293,7 @@ end #! #! Where: #! - num_output_notes is the number of output notes. -export.set_num_output_notes +pub proc set_num_output_notes mem_store.NUM_OUTPUT_NOTES_PTR end @@ -305,7 +304,7 @@ end #! #! Where: #! - note_ptr is the memory address of the data segment for the active note. -export.get_active_input_note_ptr +pub proc get_active_input_note_ptr mem_load.ACTIVE_INPUT_NOTE_PTR end @@ -316,7 +315,7 @@ end #! #! Where: #! - note_ptr is the new memory address of the data segment for the input note. -export.set_active_input_note_ptr +pub proc set_active_input_note_ptr mem_store.ACTIVE_INPUT_NOTE_PTR end @@ -328,7 +327,7 @@ end #! Where: #! - input_vault_root_ptr is a pointer to the memory address at which the input vault root is #! stored. -export.get_input_vault_root_ptr +pub proc get_input_vault_root_ptr push.INPUT_VAULT_ROOT_PTR end @@ -339,7 +338,7 @@ end #! #! Where: #! - INPUT_VAULT_ROOT is the input vault root. -export.get_input_vault_root +pub proc get_input_vault_root padw mem_loadw_be.INPUT_VAULT_ROOT_PTR end @@ -350,7 +349,7 @@ end #! #! Where: #! - INPUT_VAULT_ROOT is the input vault root. -export.set_input_vault_root +pub proc set_input_vault_root mem_storew_be.INPUT_VAULT_ROOT_PTR end @@ -362,7 +361,7 @@ end #! Where: #! - output_vault_root_ptr is the pointer to the memory address at which the output vault root is #! stored. -export.get_output_vault_root_ptr +pub proc get_output_vault_root_ptr push.OUTPUT_VAULT_ROOT_PTR end @@ -373,7 +372,7 @@ end #! #! Where: #! - OUTPUT_VAULT_ROOT is the output vault root. -export.get_output_vault_root +pub proc get_output_vault_root padw mem_loadw_be.OUTPUT_VAULT_ROOT_PTR end @@ -384,7 +383,7 @@ end #! #! Where: #! - OUTPUT_VAULT_ROOT is the output vault root. -export.set_output_vault_root +pub proc set_output_vault_root mem_storew_be.OUTPUT_VAULT_ROOT_PTR end @@ -398,7 +397,7 @@ end #! #! Where: #! - BLOCK_COMMITMENT is the commitment of the transaction reference block. -export.set_block_commitment +pub proc set_block_commitment mem_storew_be.BLOCK_COMMITMENT_PTR end @@ -409,7 +408,7 @@ end #! #! Where: #! - BLOCK_COMMITMENT is the commitment of the transaction reference block. -export.get_block_commitment +pub proc get_block_commitment padw mem_loadw_be.BLOCK_COMMITMENT_PTR end @@ -420,7 +419,7 @@ end #! #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the account ID. -export.set_global_account_id +pub proc set_global_account_id push.0.0 # => [0, 0, account_id_prefix, account_id_suffix] mem_storew_be.NATIVE_ACCT_ID_PTR @@ -435,7 +434,7 @@ end #! #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the account ID. -export.get_global_account_id +pub proc get_global_account_id padw mem_loadw_be.NATIVE_ACCT_ID_PTR # => [0, 0, account_id_prefix, account_id_suffix] drop drop @@ -448,7 +447,7 @@ end #! #! Where: #! - INIT_ACCOUNT_COMMITMENT is the initial account commitment. -export.set_init_account_commitment +pub proc set_init_account_commitment mem_storew_be.INIT_ACCOUNT_COMMITMENT_PTR end @@ -459,7 +458,7 @@ end #! #! Where: #! - INIT_ACCOUNT_COMMITMENT is the initial account commitment. -export.get_init_account_commitment +pub proc get_init_account_commitment padw mem_loadw_be.INIT_ACCOUNT_COMMITMENT_PTR end @@ -470,7 +469,7 @@ end #! #! Where: #! - init_nonce is the initial account nonce. -export.set_init_nonce +pub proc set_init_nonce mem_store.INIT_NONCE_PTR end @@ -481,7 +480,7 @@ end #! #! Where: #! - init_nonce is the initial account nonce. -export.get_init_nonce +pub proc get_init_nonce mem_load.INIT_NONCE_PTR end @@ -492,7 +491,7 @@ end #! #! Where: #! - INIT_NATIVE_ACCOUNT_VAULT_ROOT is the initial vault root of the native account. -export.set_init_native_account_vault_root +pub proc set_init_native_account_vault_root mem_storew_be.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR end @@ -503,7 +502,7 @@ end #! #! Where: #! - INIT_NATIVE_ACCOUNT_VAULT_ROOT is the initial vault root of the native account. -export.get_init_native_account_vault_root +pub proc get_init_native_account_vault_root padw mem_loadw_be.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR end @@ -516,7 +515,7 @@ end #! Where: #! - native_account_initial_vault_root_ptr is the memory pointer to the initial vault root of the #! native account. -export.get_init_native_account_vault_root_ptr +pub proc get_init_native_account_vault_root_ptr push.INIT_NATIVE_ACCOUNT_VAULT_ROOT_PTR end @@ -527,7 +526,7 @@ end #! #! Where: #! - INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT is the initial storage commitment of the native account. -export.set_init_account_storage_commitment +pub proc set_init_account_storage_commitment mem_storew_be.INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT_PTR end @@ -538,7 +537,7 @@ end #! #! Where: #! - INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT is the initial storage commitment of the native account. -export.get_init_account_storage_commitment +pub proc get_init_account_storage_commitment padw mem_loadw_be.INIT_NATIVE_ACCOUNT_STORAGE_COMMITMENT_PTR end @@ -551,7 +550,7 @@ end #! #! Where: #! - INPUT_NOTES_COMMITMENT is the input notes commitment. -export.get_input_notes_commitment +pub proc get_input_notes_commitment padw mem_loadw_be.INPUT_NOTES_COMMITMENT_PTR end @@ -562,7 +561,7 @@ end #! #! Where: #! - INPUT_NOTES_COMMITMENT is the notes' commitment. -export.set_nullifier_commitment +pub proc set_nullifier_commitment mem_storew_be.INPUT_NOTES_COMMITMENT_PTR end @@ -573,7 +572,7 @@ end #! #! Where: #! - tx_script_root_ptr is the pointer to the memory where transaction script root is stored. -export.get_tx_script_root_ptr +pub proc get_tx_script_root_ptr push.TX_SCRIPT_ROOT_PTR end @@ -584,7 +583,7 @@ end #! #! Where: #! - TX_SCRIPT_ROOT is the transaction script root. -export.set_tx_script_root +pub proc set_tx_script_root mem_storew_be.TX_SCRIPT_ROOT_PTR end @@ -596,7 +595,7 @@ end #! Where: #! - TX_SCRIPT_ARGS is the word of values which could be used directly or could be used to obtain #! some values associated with it from the advice map. -export.get_tx_script_args +pub proc get_tx_script_args padw mem_loadw_be.TX_SCRIPT_ARGS_PTR end @@ -608,7 +607,7 @@ end #! Where: #! - TX_SCRIPT_ARGS is the word of values which could be used directly or could be used to obtain #! some values associated with it from the advice map. -export.set_tx_script_args +pub proc set_tx_script_args mem_storew_be.TX_SCRIPT_ARGS_PTR end @@ -619,7 +618,7 @@ end #! #! Where: #! - AUTH_ARGS is the argument passed to the auth procedure. -export.get_auth_args +pub proc get_auth_args padw mem_loadw_be.AUTH_ARGS_PTR end @@ -630,7 +629,7 @@ end #! #! Where: #! - AUTH_ARGS is the argument passed to the auth procedure. -export.set_auth_args +pub proc set_auth_args mem_storew_be.AUTH_ARGS_PTR end @@ -644,7 +643,7 @@ end #! #! Where: #! - ptr is a pointer to the block data section. -export.get_block_data_ptr +pub proc get_block_data_ptr push.BLOCK_DATA_SECTION_OFFSET end @@ -655,7 +654,7 @@ end #! #! Where: #! - PREV_BLOCK_COMMITMENT_PTR is the block commitment of the transaction reference block. -export.get_prev_block_commitment +pub proc get_prev_block_commitment padw mem_loadw_be.PREV_BLOCK_COMMITMENT_PTR end @@ -666,7 +665,7 @@ end #! #! Where: #! - blk_num is the block number of the transaction reference block. -export.get_blk_num +pub proc get_blk_num mem_load.BLOCK_METADATA_PTR end @@ -677,8 +676,8 @@ end #! #! Where: #! - version is the protocol version of the transaction reference block. -const.BLOCK_METADATA_VERSION_PTR=BLOCK_METADATA_PTR+1 -export.get_blk_version +const BLOCK_METADATA_VERSION_PTR=BLOCK_METADATA_PTR+1 +pub proc get_blk_version mem_load.BLOCK_METADATA_VERSION_PTR end @@ -689,8 +688,8 @@ end #! #! Where: #! - timestamp is the timestamp of the reference block for this transaction. -const.BLOCK_METADATA_TIMESTAMP_PTR=BLOCK_METADATA_PTR+2 -export.get_blk_timestamp +const BLOCK_METADATA_TIMESTAMP_PTR=BLOCK_METADATA_PTR+2 +pub proc get_blk_timestamp mem_load.BLOCK_METADATA_TIMESTAMP_PTR end @@ -702,7 +701,7 @@ end #! Where: #! - native_asset_id_{prefix,suffix} are the prefix and suffix felts of the faucet ID that defines #! the native asset. -export.get_native_asset_id +pub proc get_native_asset_id padw mem_loadw_be.FEE_PARAMETERS_PTR drop drop # => [native_asset_id_prefix, native_asset_id_suffix] end @@ -715,7 +714,7 @@ end #! Where: #! - verification_base_fee is the base fee capturing the cost for the verification of a #! transaction. -export.get_verification_base_fee +pub proc get_verification_base_fee mem_load.VERIFICATION_BASE_FEE_PTR end @@ -726,7 +725,7 @@ end #! #! Where: #! - CHAIN_COMMITMENT is the chain commitment of the transaction reference block. -export.get_chain_commitment +pub proc get_chain_commitment padw mem_loadw_be.CHAIN_COMMITMENT_PTR end @@ -737,7 +736,7 @@ end #! #! Where: #! - ACCT_DB_ROOT is the account database root of the transaction reference block. -export.get_account_db_root +pub proc get_account_db_root padw mem_loadw_be.ACCT_DB_ROOT_PTR end @@ -748,7 +747,7 @@ end #! #! Where: #! - NULLIFIER_ROOT is the nullifier root of the transaction reference block. -export.get_nullifier_db_root +pub proc get_nullifier_db_root padw mem_loadw_be.NULLIFIER_ROOT_PTR end @@ -759,7 +758,7 @@ end #! #! Where: #! - TX_COMMITMENT is the tx commitment of the transaction reference block. -export.get_tx_commitment +pub proc get_tx_commitment padw mem_loadw_be.TX_COMMITMENT_PTR end @@ -770,7 +769,7 @@ end #! #! Where: #! - TX_KERNEL_COMMITMENT is the sequential hash of the kernel procedures. -export.get_tx_kernel_commitment +pub proc get_tx_kernel_commitment padw mem_loadw_be.TX_KERNEL_COMMITMENT_PTR end @@ -781,7 +780,7 @@ end #! #! Where: #! - VALIDATOR_KEY_COMMITMENT is the public key commitment of the transaction reference block. -export.get_validator_key_commitment +pub proc get_validator_key_commitment padw mem_loadw_be.VALIDATOR_KEY_COMMITMENT_PTR end @@ -792,7 +791,7 @@ end #! #! Where: #! - NOTE_ROOT is the note root of the transaction reference block. -export.get_note_root +pub proc get_note_root padw mem_loadw_be.NOTE_ROOT_PTR end @@ -803,7 +802,7 @@ end #! #! Where: #! - NOTE_ROOT is the note root of the transaction reference block. -export.set_note_root +pub proc set_note_root mem_storew_be.NOTE_ROOT_PTR end @@ -817,7 +816,7 @@ end #! #! Where: #! - ptr is the pointer to the partial blockchain section. -export.get_partial_blockchain_ptr +pub proc get_partial_blockchain_ptr push.PARTIAL_BLOCKCHAIN_PTR end @@ -828,7 +827,7 @@ end #! #! Where: #! - num_leaves is the number of leaves in the partial blockchain. -export.set_partial_blockchain_num_leaves +pub proc set_partial_blockchain_num_leaves mem_store.PARTIAL_BLOCKCHAIN_NUM_LEAVES_PTR end @@ -839,7 +838,7 @@ end #! #! Where: #! - ptr is the pointer to the start of the partial blockchain peaks section. -export.get_partial_blockchain_peaks_ptr +pub proc get_partial_blockchain_peaks_ptr push.PARTIAL_BLOCKCHAIN_PEAKS_PTR end @@ -853,7 +852,7 @@ end #! #! Where: #! - ptr is the memory address at which the native account data is stored. -export.get_native_account_data_ptr +pub proc get_native_account_data_ptr push.NATIVE_ACCOUNT_DATA_PTR end @@ -864,7 +863,7 @@ end #! #! Where: #! - account_data_length is the length of the memory interval that the account data occupies. -export.get_account_data_length +pub proc get_account_data_length push.ACCOUNT_DATA_LENGTH end @@ -876,7 +875,7 @@ end #! Where: #! - max_foreign_account_ptr is the largest memory address which can be used to load the foreign #! account data. -export.get_max_foreign_account_ptr +pub proc get_max_foreign_account_ptr push.MAX_FOREIGN_ACCOUNT_PTR end @@ -884,7 +883,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.set_active_account_data_ptr_to_native_account +pub proc set_active_account_data_ptr_to_native_account # store the native account data pointer into the first account stack element. push.NATIVE_ACCOUNT_DATA_PTR mem_store.MIN_ACCOUNT_STACK_PTR # => [native_acct_stack_ptr, account_stack_top_ptr] @@ -901,7 +900,7 @@ end #! #! Where: #! - active_account_data_ptr is the memory address at which the data of the active account begins. -export.get_active_account_data_ptr +pub proc get_active_account_data_ptr mem_load.ACCOUNT_STACK_TOP_PTR # => [account_stack_top_ptr] @@ -923,7 +922,7 @@ end #! Panics if: #! - the account stack is full, containing 64 accounts total. #! - the provided account data pointer is equal to the native account data pointer. -export.push_ptr_to_account_stack +pub proc push_ptr_to_account_stack # check that the account stack is not full mem_load.ACCOUNT_STACK_TOP_PTR dup # => [account_stack_top_ptr, account_stack_top_ptr, curr_account_data_ptr] @@ -952,7 +951,7 @@ end #! #! Panics if: #! - the account stack contains only native account. -export.pop_ptr_from_account_stack +pub proc pop_ptr_from_account_stack # check that the account stack always is at least of size 1, that is, it contains at least the # native account mem_load.ACCOUNT_STACK_TOP_PTR dup @@ -975,7 +974,7 @@ end #! #! Panics if: #! - the active account data pointer is not equal to native account data pointer (8192). -export.assert_native_account +pub proc assert_native_account exec.is_native_account assert.err=ERR_ACCOUNT_IS_NOT_NATIVE end @@ -984,7 +983,7 @@ end #! #! Inputs: [] #! Outputs: [is_native_account] -export.is_native_account +pub proc is_native_account exec.get_active_account_data_ptr # => [active_account_data_ptr] @@ -996,7 +995,7 @@ end #! #! Inputs: [] #! Outputs: [is_new_account] -export.is_new_account +pub proc is_new_account # the account is new if its nonce is zero # use get_init_nonce so this procedure works correctly even after the account's nonce was # incremented @@ -1010,7 +1009,7 @@ end #! #! Where: #! - ptr is the memory address at which the core account data ends. -export.get_core_account_data_end_ptr +pub proc get_core_account_data_end_ptr exec.get_active_account_data_ptr add.ACCT_CORE_DATA_SECTION_END_OFFSET end @@ -1023,7 +1022,7 @@ end #! #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. -export.get_account_id +pub proc get_account_id padw exec.get_active_account_data_ptr add.ACCT_ID_AND_NONCE_OFFSET mem_loadw_be # => [nonce, 0, account_id_prefix, account_id_suffix] drop drop @@ -1038,7 +1037,7 @@ end #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the native account #! of the transaction. -export.get_native_account_id +pub proc get_native_account_id padw push.NATIVE_ACCOUNT_DATA_PTR add.ACCT_ID_AND_NONCE_OFFSET mem_loadw_be # => [nonce, 0, account_id_prefix, account_id_suffix] drop drop @@ -1053,7 +1052,7 @@ end #! Where: #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. #! - nonce is the nonce of the active account. -export.set_account_id_and_nonce +pub proc set_account_id_and_nonce exec.get_active_account_data_ptr add.ACCT_ID_AND_NONCE_OFFSET mem_storew_be end @@ -1065,7 +1064,7 @@ end #! #! Where: #! - nonce is the nonce of the active account. -export.get_account_nonce +pub proc get_account_nonce exec.get_active_account_data_ptr add.ACCT_NONCE_OFFSET mem_load end @@ -1077,7 +1076,7 @@ end #! #! Where: #! - nonce is the nonce of the native account of the transaction. -export.get_native_account_nonce +pub proc get_native_account_nonce push.NATIVE_ACCOUNT_DATA_PTR add.ACCT_NONCE_OFFSET mem_load end @@ -1089,7 +1088,7 @@ end #! #! Where: #! - nonce is the nonce of the active account. -export.set_account_nonce +pub proc set_account_nonce exec.get_active_account_data_ptr add.ACCT_ID_AND_NONCE_OFFSET padw # => [0, 0, 0, 0, account_id_and_nonce_ptr, new_nonce] dup.4 mem_loadw_be @@ -1107,7 +1106,7 @@ end #! #! Where: #! - account_vault_root_ptr is the memory pointer to the account asset vault root. -export.get_account_vault_root_ptr +pub proc get_account_vault_root_ptr exec.get_active_account_data_ptr add.ACCT_VAULT_ROOT_OFFSET end @@ -1118,7 +1117,7 @@ end #! #! Where: #! - ACCT_VAULT_ROOT is the account asset vault root. -export.get_account_vault_root +pub proc get_account_vault_root padw exec.get_active_account_data_ptr add.ACCT_VAULT_ROOT_OFFSET mem_loadw_be @@ -1131,7 +1130,7 @@ end #! #! Where: #! - ACCT_VAULT_ROOT is the account vault root to be set. -export.set_account_vault_root +pub proc set_account_vault_root exec.get_active_account_data_ptr add.ACCT_VAULT_ROOT_OFFSET mem_storew_be end @@ -1147,7 +1146,7 @@ end #! #! Where: #! - account_initial_vault_root_ptr is the memory pointer to the initial vault root. -export.get_account_initial_vault_root_ptr +pub proc get_account_initial_vault_root_ptr # For foreign account, use the regular vault root pointer since foreign accounts are read-only # and initial == current exec.get_account_vault_root_ptr @@ -1175,7 +1174,7 @@ end #! #! Where: #! - CODE_COMMITMENT is the code commitment of the account. -export.get_account_code_commitment +pub proc get_account_code_commitment padw exec.get_active_account_data_ptr add.ACCT_CODE_COMMITMENT_OFFSET mem_loadw_be @@ -1188,7 +1187,7 @@ end #! #! Where: #! - CODE_COMMITMENT is the code commitment to be set. -export.set_account_code_commitment +pub proc set_account_code_commitment exec.get_active_account_data_ptr add.ACCT_CODE_COMMITMENT_OFFSET mem_storew_be end @@ -1200,7 +1199,7 @@ end #! #! Where: #! - tx_expiration_block_num is the number of the transaction expiration block. -export.set_expiration_block_num +pub proc set_expiration_block_num mem_store.TX_EXPIRATION_BLOCK_NUM_PTR end @@ -1211,7 +1210,7 @@ end #! #! Where: #! - tx_expiration_block_num is the number of the transaction expiration block. -export.get_expiration_block_num +pub proc get_expiration_block_num mem_load.TX_EXPIRATION_BLOCK_NUM_PTR end @@ -1222,7 +1221,7 @@ end #! #! Where: #! - num_procedures is the number of procedures contained in the account code. -export.get_num_account_procedures +pub proc get_num_account_procedures exec.get_active_account_data_ptr add.NUM_ACCT_PROCEDURES_OFFSET mem_load end @@ -1234,7 +1233,7 @@ end #! #! Where: #! - num_procedures is the number of procedures contained in the account code. -export.set_num_account_procedures +pub proc set_num_account_procedures exec.get_active_account_data_ptr add.NUM_ACCT_PROCEDURES_OFFSET mem_store end @@ -1246,7 +1245,7 @@ end #! #! Where: #! - account_procedures_section_ptr is the memory pointer to the account procedures section. -export.get_account_procedures_section_ptr +pub proc get_account_procedures_section_ptr exec.get_active_account_data_ptr add.ACCT_PROCEDURES_SECTION_OFFSET end @@ -1257,7 +1256,7 @@ end #! #! Where: #! - procedures_call_tracking_ptr is the memory pointer to the procedure call tracking section. -export.get_account_procedures_call_tracking_ptr +pub proc get_account_procedures_call_tracking_ptr exec.get_active_account_data_ptr add.ACCT_PROCEDURES_CALL_TRACKING_OFFSET end @@ -1269,7 +1268,7 @@ end #! Where: #! - proc_idx is the index of the account procedure. #! - proc_ptr is the memory pointer to the account procedure at the specified index. -export.get_account_procedure_ptr +pub proc get_account_procedure_ptr mul.ACCOUNT_PROCEDURE_DATA_LENGTH exec.get_account_procedures_section_ptr add end @@ -1282,7 +1281,7 @@ end #! #! Where: #! - STORAGE_COMMITMENT is the account storage commitment. -export.get_account_storage_commitment +pub proc get_account_storage_commitment padw exec.get_active_account_data_ptr add.ACCT_STORAGE_COMMITMENT_OFFSET mem_loadw_be @@ -1295,7 +1294,7 @@ end #! #! Where: #! - STORAGE_COMMITMENT is the account storage commitment. -export.set_account_storage_commitment +pub proc set_account_storage_commitment exec.get_active_account_data_ptr add.ACCT_STORAGE_COMMITMENT_OFFSET mem_storew_be end @@ -1310,7 +1309,7 @@ end #! #! Where: #! - dirty_flag is the flag indicating whether the storage commitment is outdated. -export.set_native_account_storage_commitment_dirty_flag +pub proc set_native_account_storage_commitment_dirty_flag push.NATIVE_ACCT_STORAGE_COMMITMENT_DIRTY_FLAG_PTR mem_store # => [] end @@ -1325,7 +1324,7 @@ end #! Where: #! - should_recompute_storage_commitment is the flag indicating whether the storage commitment #! should be recomputed. -export.get_recompute_storage_commitment_flag +pub proc get_recompute_storage_commitment_flag # get the is_native_account flag exec.is_native_account # => [is_native_account] @@ -1346,7 +1345,7 @@ end #! #! Where: #! - num_storage_slots is the number of storage slots contained in the account storage. -export.get_num_storage_slots +pub proc get_num_storage_slots exec.get_active_account_data_ptr add.NUM_ACCT_STORAGE_SLOTS_OFFSET mem_load end @@ -1358,7 +1357,7 @@ end #! #! Where: #! - num_storage_slots is the number of storage slots contained in the account storage. -export.set_num_storage_slots +pub proc set_num_storage_slots exec.get_active_account_data_ptr add.NUM_ACCT_STORAGE_SLOTS_OFFSET mem_store end @@ -1370,7 +1369,7 @@ end #! #! Where: #! - storage_slots_section_ptr is the memory pointer to the account storage slots section. -export.get_account_storage_slots_section_ptr +pub proc get_account_storage_slots_section_ptr exec.get_active_account_data_ptr add.ACCT_STORAGE_SLOTS_SECTION_OFFSET end @@ -1381,7 +1380,7 @@ end #! #! Where: #! - storage_slots_section_ptr is the memory pointer to the native account's storage slots section. -export.get_native_account_storage_slots_ptr +pub proc get_native_account_storage_slots_ptr exec.get_native_account_data_ptr add.ACCT_STORAGE_SLOTS_SECTION_OFFSET end @@ -1392,7 +1391,7 @@ end #! #! Where: #! - account_initial_storage_slots_ptr is the memory pointer to the initial storage slot values. -export.get_native_account_initial_storage_slots_ptr +pub proc get_native_account_initial_storage_slots_ptr exec.get_native_account_data_ptr add.ACCT_INITIAL_STORAGE_SLOTS_SECTION_OFFSET end @@ -1407,7 +1406,7 @@ end #! #! Where: #! - account_initial_storage_slots_ptr is the memory pointer to the initial storage slot values. -export.get_account_initial_storage_slots_ptr +pub proc get_account_initial_storage_slots_ptr # For foreign account, use the regular storage slots pointer since foreign accounts are # read-only and initial == current exec.get_account_storage_slots_section_ptr @@ -1435,7 +1434,7 @@ end #! #! Where: #! - account_delta_fungible_asset_ptr is the link map pointer to the fungible asset vault delta. -export.get_account_delta_fungible_asset_ptr +pub proc get_account_delta_fungible_asset_ptr push.ACCOUNT_DELTA_FUNGIBLE_ASSET_PTR end @@ -1446,7 +1445,7 @@ end #! #! Where: #! - account_delta_non_fungible_asset_ptr is the link map pointer to the non-fungible asset vault delta. -export.get_account_delta_non_fungible_asset_ptr +pub proc get_account_delta_non_fungible_asset_ptr push.ACCOUNT_DELTA_NON_FUNGIBLE_ASSET_PTR end @@ -1458,7 +1457,7 @@ end #! Where: #! - account_delta_storage_map_ptr is the link map pointer to the storage map delta for the #! requested slot index. -export.get_account_delta_storage_map_ptr +pub proc get_account_delta_storage_map_ptr add.ACCOUNT_DELTA_STORAGE_MAP_SECTION end @@ -1466,7 +1465,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.mem_copy_native_account_initial_storage_slots +pub proc mem_copy_native_account_initial_storage_slots exec.get_native_account_initial_storage_slots_ptr exec.get_native_account_storage_slots_ptr # => [storage_slots_section_ptr, initial_storage_slots_ptr] @@ -1489,7 +1488,7 @@ end #! #! Where: #! - num_input_notes is the total number of input notes consumed by this transaction. -export.get_num_input_notes +pub proc get_num_input_notes mem_load.NUM_INPUT_NOTES_PTR end @@ -1500,7 +1499,7 @@ end #! #! Where: #! - num_input_notes is the total number of input notes consumed by this transaction. -export.set_num_input_notes +pub proc set_num_input_notes mem_store.NUM_INPUT_NOTES_PTR end @@ -1513,7 +1512,7 @@ end #! Where: #! - idx is the index of the input note. #! - note_ptr is the memory address of the data segment for the input note with `idx`. -export.get_input_note_ptr +pub proc get_input_note_ptr exec.constants::get_note_mem_size mul add.INPUT_NOTE_DATA_SECTION_OFFSET end @@ -1525,7 +1524,7 @@ end #! Where: #! - note_ptr is the input note's the memory address. #! - NOTE_ID is the note's id. -export.set_input_note_id +pub proc set_input_note_id mem_storew_be end @@ -1538,7 +1537,7 @@ end #! Where: #! - idx is the index of the input note. #! - nullifier_ptr is the memory address of the nullifier for note idx. -export.get_input_note_nullifier_ptr +pub proc get_input_note_nullifier_ptr mul.4 add.INPUT_NOTE_NULLIFIER_SECTION_PTR end @@ -1550,7 +1549,7 @@ end #! Where: #! - idx is the index of the input note. #! - nullifier is the nullifier of the input note. -export.get_input_note_nullifier +pub proc get_input_note_nullifier mul.4 padw movup.4 add.INPUT_NOTE_NULLIFIER_SECTION_PTR mem_loadw_be end @@ -1563,7 +1562,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - note_data_ptr is the memory address at which the input note core data begins. -export.get_input_note_core_ptr +pub proc get_input_note_core_ptr add.INPUT_NOTE_CORE_DATA_OFFSET end @@ -1575,7 +1574,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - SCRIPT_ROOT is the script root of the input note. -export.get_input_note_script_root +pub proc get_input_note_script_root padw movup.4 add.INPUT_NOTE_SCRIPT_ROOT_OFFSET mem_loadw_be @@ -1589,7 +1588,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - script_root_ptr is the memory address where script root of the input note is stored. -export.get_input_note_script_root_ptr +pub proc get_input_note_script_root_ptr add.INPUT_NOTE_SCRIPT_ROOT_OFFSET end @@ -1601,7 +1600,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - INPUTS_COMMITMENT is the inputs commitment of the input note. -export.get_input_note_inputs_commitment +pub proc get_input_note_inputs_commitment padw movup.4 add.INPUT_NOTE_INPUTS_COMMITMENT_OFFSET mem_loadw_be @@ -1615,7 +1614,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - METADATA is the metadata of the input note. -export.get_input_note_metadata +pub proc get_input_note_metadata padw movup.4 add.INPUT_NOTE_METADATA_OFFSET mem_loadw_be @@ -1629,7 +1628,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - NOTE_METADATA is the metadata of the input note. -export.set_input_note_metadata +pub proc set_input_note_metadata add.INPUT_NOTE_METADATA_OFFSET mem_storew_be end @@ -1642,7 +1641,7 @@ end #! Where: #! - note_ptr is the start memory address of the note. #! - NOTE_ARGS are the note's args. -export.get_input_note_args +pub proc get_input_note_args padw movup.4 add.INPUT_NOTE_ARGS_OFFSET mem_loadw_be @@ -1656,7 +1655,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - NOTE_ARGS are optional note args of the input note. -export.set_input_note_args +pub proc set_input_note_args add.INPUT_NOTE_ARGS_OFFSET mem_storew_be end @@ -1669,7 +1668,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - num_inputs is the number of inputs in in the input note. -export.get_input_note_num_inputs +pub proc get_input_note_num_inputs add.INPUT_NOTE_NUM_INPUTS_OFFSET mem_load end @@ -1682,7 +1681,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - num_inputs is the number of inputs in the input note. -export.set_input_note_num_inputs +pub proc set_input_note_num_inputs add.INPUT_NOTE_NUM_INPUTS_OFFSET mem_store end @@ -1695,7 +1694,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - num_assets is the number of assets in the input note. -export.get_input_note_num_assets +pub proc get_input_note_num_assets add.INPUT_NOTE_NUM_ASSETS_OFFSET mem_load end @@ -1708,7 +1707,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - num_assets is the number of assets in the input note. -export.set_input_note_num_assets +pub proc set_input_note_num_assets add.INPUT_NOTE_NUM_ASSETS_OFFSET mem_store end @@ -1722,7 +1721,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - assets_ptr is the memory address at which the assets segment for the input note begins. -export.get_input_note_assets_ptr +pub proc get_input_note_assets_ptr add.INPUT_NOTE_ASSETS_OFFSET end @@ -1734,7 +1733,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - RECIPIENT is the commitment to the note's script, inputs and the serial number. -export.get_input_note_recipient +pub proc get_input_note_recipient padw movup.4 add.INPUT_NOTE_RECIPIENT_OFFSET mem_loadw_be @@ -1748,7 +1747,7 @@ end #! Where: #! - note_ptr is the memory address at which the output note data begins. #! - RECIPIENT is the commitment to the note's script, inputs and the serial number. -export.set_input_note_recipient +pub proc set_input_note_recipient add.INPUT_NOTE_RECIPIENT_OFFSET mem_storew_be end @@ -1761,7 +1760,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - ASSET_COMMITMENT is the sequential hash of the padded assets of an input note. -export.get_input_note_assets_commitment +pub proc get_input_note_assets_commitment padw movup.4 add.INPUT_NOTE_ASSETS_COMMITMENT_OFFSET mem_loadw_be @@ -1775,7 +1774,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - SERIAL_NUMBER is the input note's serial number. -export.get_input_note_serial_num +pub proc get_input_note_serial_num padw movup.4 add.INPUT_NOTE_SERIAL_NUM_OFFSET mem_loadw_be @@ -1789,7 +1788,7 @@ end #! Where: #! - note_ptr is the memory address at which the input note data begins. #! - sender is the sender for the input note. -export.get_input_note_sender +pub proc get_input_note_sender padw movup.4 add.INPUT_NOTE_METADATA_OFFSET mem_loadw_be @@ -1821,7 +1820,7 @@ end #! #! Where: #! - offset is the offset of the output note data segment. -export.get_output_note_data_offset +pub proc get_output_note_data_offset push.OUTPUT_NOTE_SECTION_OFFSET end @@ -1834,7 +1833,7 @@ end #! Where: #! - i is the index of the output note. #! - ptr is the memory address of the data segment for output note i. -export.get_output_note_ptr +pub proc get_output_note_ptr exec.constants::get_note_mem_size mul add.OUTPUT_NOTE_SECTION_OFFSET end @@ -1846,7 +1845,7 @@ end #! Where: #! - note_ptr is the memory address at which the output note data begins. #! - RECIPIENT is the commitment to the note's script, inputs and the serial number. -export.get_output_note_recipient +pub proc get_output_note_recipient padw movup.4 add.OUTPUT_NOTE_RECIPIENT_OFFSET mem_loadw_be @@ -1860,7 +1859,7 @@ end #! Where: #! - note_ptr is the memory address at which the output note data begins. #! - RECIPIENT is the commitment to the note's script, inputs and the serial number. -export.set_output_note_recipient +pub proc set_output_note_recipient add.OUTPUT_NOTE_RECIPIENT_OFFSET mem_storew_be end @@ -1873,7 +1872,7 @@ end #! Where: #! - METADATA is the note metadata. #! - note_ptr is the memory address at which the output note data begins. -export.get_output_note_metadata +pub proc get_output_note_metadata padw # => [0, 0, 0, 0, note_ptr] movup.4 add.OUTPUT_NOTE_METADATA_OFFSET @@ -1890,7 +1889,7 @@ end #! Where: #! - METADATA is the note metadata. #! - note_ptr is the memory address at which the output note data begins. -export.set_output_note_metadata +pub proc set_output_note_metadata add.OUTPUT_NOTE_METADATA_OFFSET mem_storew_be end @@ -1903,7 +1902,7 @@ end #! Where: #! - note_ptr is a pointer to the memory address at which the output note is stored. #! - num_assets is the number of assets in the output note. -export.get_output_note_num_assets +pub proc get_output_note_num_assets add.OUTPUT_NOTE_NUM_ASSETS_OFFSET mem_load end @@ -1918,7 +1917,7 @@ end #! #! Panics if: #! - the number of assets exceeds the maximum allowed number of assets per note. -export.set_output_note_num_assets +pub proc set_output_note_num_assets add.OUTPUT_NOTE_NUM_ASSETS_OFFSET # => [note_ptr + offset, num_assets] @@ -1939,7 +1938,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - dirty_flag is the flag indicating whether the assets commitment is outdated. -export.get_output_note_dirty_flag +pub proc get_output_note_dirty_flag add.OUTPUT_NOTE_DIRTY_FLAG_OFFSET mem_load end @@ -1954,7 +1953,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - dirty_flag is the flag indicating whether the assets commitment is outdated. -export.set_output_note_dirty_flag +pub proc set_output_note_dirty_flag add.OUTPUT_NOTE_DIRTY_FLAG_OFFSET mem_store end @@ -1966,7 +1965,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - asset_data_ptr is the memory address at which the output note asset data begins. -export.get_output_note_asset_data_ptr +pub proc get_output_note_asset_data_ptr add.OUTPUT_NOTE_ASSETS_OFFSET end @@ -1978,7 +1977,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - ASSETS_COMMITMENT is the sequential hash of the padded assets of an output note. -export.get_output_note_assets_commitment +pub proc get_output_note_assets_commitment padw movup.4 add.OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET mem_loadw_be @@ -1993,7 +1992,7 @@ end #! Where: #! - output_note_data_ptr is the memory address at which the output note data begins. #! - ASSETS_COMMITMENT is the sequential hash of the padded assets of an output note. -export.set_output_note_assets_commitment +pub proc set_output_note_assets_commitment add.OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET mem_storew_be end @@ -2008,7 +2007,7 @@ end #! #! Where: #! - num_kernel_procedures is the number of the procedures of the selected kernel. -export.set_num_kernel_procedures +pub proc set_num_kernel_procedures mem_store.NUM_KERNEL_PROCEDURES_PTR end @@ -2019,7 +2018,7 @@ end #! #! Where: #! - num_kernel_procedures is the number of the procedures of the selected kernel. -export.get_num_kernel_procedures +pub proc get_num_kernel_procedures mem_load.NUM_KERNEL_PROCEDURES_PTR end @@ -2031,7 +2030,7 @@ end #! Where: #! - kernel_procedures_ptr is the memory address where the hashes of the kernel procedures are #! stored. -export.get_kernel_procedures_ptr +pub proc get_kernel_procedures_ptr push.KERNEL_PROCEDURES_PTR end @@ -2042,7 +2041,7 @@ end #! #! Inputs: [] #! Outputs: [start_ptr] -export.get_link_map_region_start_ptr +pub proc get_link_map_region_start_ptr push.LINK_MAP_REGION_START_PTR end @@ -2050,7 +2049,7 @@ end #! #! Inputs: [] #! Outputs: [end_ptr] -export.get_link_map_region_end_ptr +pub proc get_link_map_region_end_ptr push.LINK_MAP_REGION_END_PTR end @@ -2058,7 +2057,7 @@ end #! #! Inputs: [] #! Outputs: [entry_size] -export.get_link_map_entry_size +pub proc get_link_map_entry_size push.LINK_MAP_ENTRY_SIZE end @@ -2069,7 +2068,7 @@ end #! #! Panics if: #! - the allocation exceeds the maximum possible number of link map entries. -export.link_map_malloc +pub proc link_map_malloc # retrieve the current memory size mem_load.LINK_MAP_USED_MEMORY_SIZE dup # => [current_mem_size, current_mem_size] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm similarity index 94% rename from crates/miden-lib/asm/kernels/transaction/lib/note.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/note.masm index b884a3728f..a1917db667 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm @@ -1,19 +1,19 @@ -use.std::crypto::hashes::rpo +use miden::core::crypto::hashes::rpo256 -use.$kernel::constants -use.$kernel::memory +use $kernel::constants +use $kernel::memory # ERRORS # ================================================================================================= -const.ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255" +const ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255" # CONSTANTS # ================================================================================================= # The diff between the memory address after first mem_stream operation and the next target when # generating the output notes commitment. Must be NOTE_MEM_SIZE - 8; -const.OUTPUT_NOTE_HASHING_MEM_DIFF=2040 +const OUTPUT_NOTE_HASHING_MEM_DIFF=2040 # ACTIVE NOTE PROCEDURES # ================================================================================================= @@ -25,7 +25,7 @@ const.OUTPUT_NOTE_HASHING_MEM_DIFF=2040 #! #! Where: #! - active_input_note_ptr is the pointer to the next note to be processed. -export.increment_active_input_note_ptr +pub proc increment_active_input_note_ptr # get the active input note pointer exec.memory::get_active_input_note_ptr # => [orig_input_note_ptr] @@ -44,7 +44,7 @@ end #! #! Inputs: [] #! Outputs: [] -export.note_processing_teardown +pub proc note_processing_teardown # set the active input note pointer to 0 push.0 exec.memory::set_active_input_note_ptr # => [] @@ -60,7 +60,7 @@ end #! Where: #! - note_script_root_ptr is the memory address where note's script root is stored. #! - NOTE_ARGS is the note's arguments. -export.prepare_note +pub proc prepare_note padw padw push.0.0.0 # => [pad(11)] @@ -92,7 +92,7 @@ end #! Where: #! - note_data_ptr is a pointer to the data section of the output note. #! - ASSETS_COMMITMENT is the commitment of the assets of the output note located at note_data_ptr. -export.compute_output_note_assets_commitment +pub proc compute_output_note_assets_commitment # get the assets commitment dirty flag and decide whether we need to recompute the commitment dup exec.memory::get_output_note_dirty_flag # => [dirty_flag, note_data_ptr] @@ -138,7 +138,7 @@ export.compute_output_note_assets_commitment end # extract digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [ASSETS_COMMITMENT, asset_data_ptr, asset_counter, num_asset_pairs, note_data_ptr] # drop accessory variables from stack @@ -176,7 +176,7 @@ end #! Where: #! - note_data_ptr is a pointer to the data section of the output note. #! - NOTE_ID is the ID of the output note located at note_data_ptr. -proc.compute_output_note_id +proc compute_output_note_id # pad capacity elements of hasher padw # => [EMPTY_WORD, note_data_ptr] @@ -190,7 +190,7 @@ proc.compute_output_note_id # => [ASSETS_COMMITMENT, RECIPIENT, EMPTY_WORD, note_data_ptr] # compute output note commitment (which is also note ID) and extract digest - hperm exec.rpo::squeeze_digest + hperm exec.rpo256::squeeze_digest # => [NOTE_ID, note_data_ptr] # save the output note commitment (note ID) to memory @@ -206,7 +206,7 @@ end #! #! Where: #! - OUTPUT_NOTES_COMMITMENT is the commitment to the notes output by the transaction. -export.compute_output_notes_commitment +pub proc compute_output_notes_commitment # get the number of output notes from memory exec.memory::get_num_output_notes # => [num_notes, ...] @@ -251,7 +251,7 @@ export.compute_output_notes_commitment end # extract digest - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [OUTPUT_NOTES_COMMITMENT, end_ptr, end_ptr, ...] # drop accessory variables from stack diff --git a/crates/miden-lib/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm similarity index 94% rename from crates/miden-lib/asm/kernels/transaction/lib/output_note.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index 902ea9768f..44a0076621 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -1,35 +1,35 @@ -use.$kernel::account -use.$kernel::memory -use.$kernel::note -use.$kernel::asset -use.$kernel::constants -use.std::word +use $kernel::account +use $kernel::memory +use $kernel::note +use $kernel::asset +use $kernel::constants +use miden::core::word # CONSTANTS # ================================================================================================= # Constants for different note types -const.PUBLIC_NOTE=1 # 0b01 -const.PRIVATE_NOTE=2 # 0b10 -const.ENCRYPTED_NOTE=3 # 0b11 +const PUBLIC_NOTE=1 # 0b01 +const PRIVATE_NOTE=2 # 0b10 +const ENCRYPTED_NOTE=3 # 0b11 # The note type must be PUBLIC, unless the high bits are `0b11`. (See the table below.) -const.LOCAL_ANY_PREFIX=3 # 0b11 +const LOCAL_ANY_PREFIX=3 # 0b11 # ERRORS # ================================================================================================= -const.ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT="number of output notes in the transaction exceeds the maximum limit of 1024" +const ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT="number of output notes in the transaction exceeds the maximum limit of 1024" -const.ERR_NOTE_INVALID_TYPE="invalid note type" +const ERR_NOTE_INVALID_TYPE="invalid note type" -const.ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested output note index should be less than the total number of created output notes" +const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested output note index should be less than the total number of created output notes" -const.ERR_NOTE_INVALID_INDEX="failed to find note at the given index; index must be within [0, num_of_notes]" +const ERR_NOTE_INVALID_INDEX="failed to find note at the given index; index must be within [0, num_of_notes]" -const.ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807" +const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807" -const.ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="non-fungible asset that already exists in the note cannot be added again" +const ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="non-fungible asset that already exists in the note cannot be added again" # The 2 highest bits in the u32 tag have the following meaning: # @@ -51,22 +51,22 @@ const.ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="non-fungible asset that already exi # users won't see the note 2. generate slightly more load as extra validation is performed for the # invalid tags. None of these scenarios have any significant impact. -const.ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX="invalid note type for the given note tag prefix" +const ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX="invalid note type for the given note tag prefix" -const.ERR_NOTE_TAG_MUST_BE_U32="the note's tag must fit into a u32 so the 32 most significant bits must be zero" +const ERR_NOTE_TAG_MUST_BE_U32="the note's tag must fit into a u32 so the 32 most significant bits must be zero" # EVENTS # ================================================================================================= # Event emitted before a new note is created. -const.NOTE_BEFORE_CREATED_EVENT=event("miden::note::before_created") +const NOTE_BEFORE_CREATED_EVENT=event("miden::note::before_created") # Event emitted after a new note is created. -const.NOTE_AFTER_CREATED_EVENT=event("miden::note::after_created") +const NOTE_AFTER_CREATED_EVENT=event("miden::note::after_created") # Event emitted before an ASSET is added to a note -const.NOTE_BEFORE_ADD_ASSET_EVENT=event("miden::note::before_add_asset") +const NOTE_BEFORE_ADD_ASSET_EVENT=event("miden::note::before_add_asset") # Event emitted after an ASSET is added to a note -const.NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") +const NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") # OUTPUT NOTE PROCEDURES # ================================================================================================= @@ -90,7 +90,7 @@ const.NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") #! - the note_tag is not an u32. #! - the note_tag starts with anything but 0b11 and note_type is not public. #! - the number of output notes exceeds the maximum limit of 1024. -export.create +pub proc create emit.NOTE_BEFORE_CREATED_EVENT exec.build_metadata @@ -132,7 +132,7 @@ end #! - note_index is the index of the output note whose assets info should be returned. #! - num_assets is the number of assets in the specified note. #! - ASSETS_COMMITMENT is a sequential hash of the assets in the specified note. -export.get_assets_info +pub proc get_assets_info # get the note data pointer based on the index of the requested note exec.memory::get_output_note_ptr # => [note_data_ptr] @@ -146,7 +146,7 @@ export.get_assets_info # => [ASSETS_COMMITMENT, note_data_ptr, num_assets] # next we should store the assets in the advice map using the computed assets commitment to be - # able to get the assets later (in the `miden::output_note::get_assets` procedure) + # able to get the assets later (in the `miden::protocol::output_note::get_assets` procedure) # get the start and the end pointers of the asset data # @@ -188,7 +188,7 @@ end #! - the max amount of fungible assets is exceeded. #! - the non-fungible asset already exists in the note. #! - the total number of ASSETs exceeds the maximum of 256. -export.add_asset +pub proc add_asset # check if the note exists, it must be within [0, num_of_notes] dup exec.memory::get_num_output_notes lte assert.err=ERR_NOTE_INVALID_INDEX # => [note_idx, ASSET] @@ -242,7 +242,7 @@ end #! #! Inputs: [note_index] #! Outputs: [note_index] -export.assert_note_index_in_bounds +pub proc assert_note_index_in_bounds # assert that the provided note index is less than the total number of notes dup exec.memory::get_num_output_notes # => [output_notes_num, note_index, note_index] @@ -270,7 +270,7 @@ end #! or off-chain). #! - execution_hint is the hint which specifies when a note is ready to be consumed. #! - NOTE_METADATA is the metadata associated with a note. -export.build_metadata +pub proc build_metadata # Validate the note type. # -------------------------------------------------------------------------------------------- @@ -366,7 +366,7 @@ end #! #! Where: #! - note_idx is the index of the next note to be created. -proc.increment_num_output_notes +proc increment_num_output_notes # get the current number of output notes exec.memory::get_num_output_notes # => [note_idx] @@ -397,7 +397,7 @@ end #! #! Panics if #! - the summed amounts exceed the maximum amount of fungible assets. -proc.add_fungible_asset +proc add_fungible_asset dup.4 exec.memory::get_output_note_asset_data_ptr # => [asset_ptr, ASSET, note_ptr, num_of_assets, note_idx] @@ -487,7 +487,7 @@ end #! #! Panics if: #! - the non-fungible asset already exists in the note. -proc.add_non_fungible_asset +proc add_non_fungible_asset dup.4 exec.memory::get_output_note_asset_data_ptr # => [asset_ptr, ASSET, note_ptr, num_of_assets, note_idx] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm similarity index 94% rename from crates/miden-lib/asm/kernels/transaction/lib/prologue.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm index c3226643a8..9dc1a86926 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm @@ -1,71 +1,71 @@ -use.std::mem -use.std::collections::mmr -use.std::crypto::hashes::rpo -use.std::word - -use.$kernel::account -use.$kernel::account_delta -use.$kernel::account_id -use.$kernel::asset_vault -use.$kernel::constants -use.$kernel::memory +use miden::core::mem +use miden::core::collections::mmr +use miden::core::crypto::hashes::rpo256 +use miden::core::word + +use $kernel::account +use $kernel::account_delta +use $kernel::account_id +use $kernel::asset_vault +use $kernel::constants +use $kernel::memory # CONSTS # ================================================================================================= # Max U32 value, used for initializing the expiration block number -const.MAX_BLOCK_NUM=0xFFFFFFFF +const MAX_BLOCK_NUM=0xFFFFFFFF # EVENTS #================================================================================================= # Emission of an equivalent to `ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT`, use in `add_input_note_assets_to_vault` -const.ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT=event("miden::account::vault_before_add_asset") +const ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT=event("miden::account::vault_before_add_asset") # ERRORS # ================================================================================================= -const.ERR_PROLOGUE_GLOBAL_INPUTS_PROVIDED_DO_NOT_MATCH_BLOCK_COMMITMENT="the provided global inputs do not match the block commitment" +const ERR_PROLOGUE_GLOBAL_INPUTS_PROVIDED_DO_NOT_MATCH_BLOCK_COMMITMENT="the provided global inputs do not match the block commitment" -const.ERR_PROLOGUE_GLOBAL_INPUTS_PROVIDED_DO_NOT_MATCH_BLOCK_NUMBER_COMMITMENT="the provided global inputs do not match the block number commitment" +const ERR_PROLOGUE_GLOBAL_INPUTS_PROVIDED_DO_NOT_MATCH_BLOCK_NUMBER_COMMITMENT="the provided global inputs do not match the block number commitment" -const.ERR_PROLOGUE_NATIVE_ASSET_ID_IS_NOT_FUNGIBLE="native asset account ID in reference block is not of type fungible faucet" +const ERR_PROLOGUE_NATIVE_ASSET_ID_IS_NOT_FUNGIBLE="native asset account ID in reference block is not of type fungible faucet" -const.ERR_PROLOGUE_VERIFICATION_BASE_FEE_MUST_BE_U32="verification base fee must fit into a u32" +const ERR_PROLOGUE_VERIFICATION_BASE_FEE_MUST_BE_U32="verification base fee must fit into a u32" -const.ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" +const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" -const.ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY="reserved slot for new fungible faucet is not empty" +const ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY="reserved slot for new fungible faucet is not empty" -const.ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE="reserved slot for new fungible faucet has an invalid type" +const ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE="reserved slot for new fungible faucet has an invalid type" -const.ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT="reserved slot for non-fungible faucet is not a valid empty SMT" +const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT="reserved slot for non-fungible faucet is not a valid empty SMT" -const.ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE="reserved slot for new non-fungible faucet has an invalid type" +const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE="reserved slot for new non-fungible faucet has an invalid type" -const.ERR_PROLOGUE_PROVIDED_ACCOUNT_DATA_DOES_NOT_MATCH_ON_CHAIN_COMMITMENT="account data provided does not match the commitment recorded on-chain" +const ERR_PROLOGUE_PROVIDED_ACCOUNT_DATA_DOES_NOT_MATCH_ON_CHAIN_COMMITMENT="account data provided does not match the commitment recorded on-chain" -const.ERR_PROLOGUE_EXISTING_ACCOUNT_MUST_HAVE_NON_ZERO_NONCE="existing accounts must have a non-zero nonce" +const ERR_PROLOGUE_EXISTING_ACCOUNT_MUST_HAVE_NON_ZERO_NONCE="existing accounts must have a non-zero nonce" -const.ERR_PROLOGUE_MISMATCH_OF_ACCOUNT_IDS_FROM_GLOBAL_INPUTS_AND_ADVICE_PROVIDER="account IDs provided via global inputs and advice provider do not match" +const ERR_PROLOGUE_MISMATCH_OF_ACCOUNT_IDS_FROM_GLOBAL_INPUTS_AND_ADVICE_PROVIDER="account IDs provided via global inputs and advice provider do not match" -const.ERR_PROLOGUE_MISMATCH_OF_REFERENCE_BLOCK_MMR_AND_NOTE_AUTHENTICATION_MMR="reference block MMR and note's authentication MMR must match" +const ERR_PROLOGUE_MISMATCH_OF_REFERENCE_BLOCK_MMR_AND_NOTE_AUTHENTICATION_MMR="reference block MMR and note's authentication MMR must match" -const.ERR_PROLOGUE_NUMBER_OF_NOTE_ASSETS_EXCEEDS_LIMIT="number of note assets exceeds the maximum limit of 256" +const ERR_PROLOGUE_NUMBER_OF_NOTE_ASSETS_EXCEEDS_LIMIT="number of note assets exceeds the maximum limit of 256" -const.ERR_PROLOGUE_PROVIDED_INPUT_ASSETS_INFO_DOES_NOT_MATCH_ITS_COMMITMENT="provided info about assets of an input does not match its commitment" +const ERR_PROLOGUE_PROVIDED_INPUT_ASSETS_INFO_DOES_NOT_MATCH_ITS_COMMITMENT="provided info about assets of an input does not match its commitment" -const.ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT="number of input notes exceeds the kernel's maximum limit of 1024" +const ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT="number of input notes exceeds the kernel's maximum limit of 1024" -const.ERR_PROLOGUE_INPUT_NOTES_COMMITMENT_MISMATCH="note commitment computed from the input note data does not match given note commitment" +const ERR_PROLOGUE_INPUT_NOTES_COMMITMENT_MISMATCH="note commitment computed from the input note data does not match given note commitment" -const.ERR_PROLOGUE_NEW_ACCOUNT_NONCE_MUST_BE_ZERO="new account must have a zero nonce" +const ERR_PROLOGUE_NEW_ACCOUNT_NONCE_MUST_BE_ZERO="new account must have a zero nonce" -const.ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" +const ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" -const.ERR_PROLOGUE_NOTE_AUTHENTICATION_FAILED="failed to authenticate note inclusion in block" +const ERR_PROLOGUE_NOTE_AUTHENTICATION_FAILED="failed to authenticate note inclusion in block" -const.ERR_PROLOGUE_KERNEL_PROCEDURE_COMMITMENT_MISMATCH="sequential hash over kernel procedures does not match kernel commitment from block" +const ERR_PROLOGUE_KERNEL_PROCEDURE_COMMITMENT_MISMATCH="sequential hash over kernel procedures does not match kernel commitment from block" # PUBLIC INPUTS # ================================================================================================= @@ -89,7 +89,7 @@ const.ERR_PROLOGUE_KERNEL_PROCEDURE_COMMITMENT_MISMATCH="sequential hash over ke #! accounts. #! - INPUT_NOTES_COMMITMENT is the commitment to the input notes. See the #! `tx_get_input_notes_commitment` kernel procedure for details. -proc.process_global_inputs +proc process_global_inputs exec.memory::set_block_commitment dropw exec.memory::set_init_account_commitment dropw exec.memory::set_nullifier_commitment dropw @@ -114,7 +114,7 @@ end #! Where: #! - TX_KERNEL_COMMITMENT is the sequential hash of the kernel procedures. #! - [KERNEL_PROCEDURE_ROOTS] is the array of the kernel procedure roots. -proc.process_kernel_data +proc process_kernel_data # load the transaction kernel commitment from memory exec.memory::get_tx_kernel_commitment # OS => [TX_KERNEL_COMMITMENT] @@ -147,7 +147,7 @@ proc.process_kernel_data # AS => [] # extract the resulting hash - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # OS => [SEQ_KERNEL_PROC_HASH, kernel_procs_ptr', TX_KERNEL_COMMITMENT] # AS => [] @@ -197,7 +197,7 @@ end #! - verification_base_fee is the base fee capturing the cost for the verification of a #! transaction. #! - NOTE_ROOT is the root of the tree with all notes created in the block. -proc.process_block_data +proc process_block_data exec.memory::get_block_data_ptr # => [block_data_ptr, block_num] @@ -209,7 +209,7 @@ proc.process_block_data adv_pipe hperm adv_pipe hperm adv_pipe hperm - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [SUB_COMMITMENT, block_data_ptr', block_num] # store the note root in memory @@ -258,7 +258,7 @@ end #! - PARTIAL_BLOCKCHAIN_COMMITMENT is the sequential hash of the padded MMR peaks. #! - num_blocks is the number of blocks in the MMR. #! - PEAK_1 .. PEAK_N are the MMR peaks. -proc.process_chain_data +proc process_chain_data exec.memory::get_partial_blockchain_ptr dup # => [partial_blockchain_ptr, partial_blockchain_ptr] @@ -291,7 +291,7 @@ end #! #! Inputs: [] #! Outputs: [] -proc.validate_new_account +proc validate_new_account # Assert the account ID of the account is valid exec.memory::get_account_id exec.account_id::validate # => [] @@ -397,7 +397,7 @@ end #! - ACCOUNT_VAULT_ROOT is the account's vault root. #! - ACCOUNT_STORAGE_COMMITMENT is the account's storage commitment. #! - ACCOUNT_CODE_COMMITMENT is the account's code commitment. -proc.process_account_data +proc process_account_data # Initialize the active account data pointer in the bookkeeping section with the native offset # (2048) exec.memory::set_active_account_data_ptr_to_native_account @@ -412,7 +412,7 @@ proc.process_account_data padw padw padw adv_pipe hperm adv_pipe hperm - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [ACCOUNT_COMMITMENT, acct_data_ptr'] movup.4 drop @@ -518,7 +518,8 @@ end #! - BLOCK_SUB_COMMITMENT is the sub_commitment of the block which created the input note. #! - NOTE_ROOT is the merkle root of the notes tree containing the input note. #! - note_index is the input note's position in the notes tree. -proc.authenticate_note.8 +@locals(8) +proc authenticate_note # Load the BLOCK_COMMITMENT from the PARTIAL_BLOCKCHAIN # --------------------------------------------------------------------------------------------- @@ -592,7 +593,7 @@ end #! - ASSETS_COMMITMENT is the sequential hash of the padded note's assets. #! - NULLIFIER is the result of #! `hash(SERIAL_NUMBER || SCRIPT_ROOT || INPUTS_COMMITMENT || ASSETS_COMMITMENT)`. -proc.process_input_note_details +proc process_input_note_details exec.memory::get_input_note_core_ptr # => [note_data_ptr] @@ -600,7 +601,7 @@ proc.process_input_note_details padw padw padw adv_pipe hperm adv_pipe hperm - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [NULLIFIER, note_data_ptr + 16] movup.4 drop @@ -628,7 +629,7 @@ end #! - note_ptr is the memory location for the input note. #! - NOTE_ARGS are the user arguments passed to the note. #! - NOTE_METADATA is the note's metadata. -proc.process_note_args_and_metadata +proc process_note_args_and_metadata padw adv_loadw dup.4 exec.memory::set_input_note_args dropw # => [note_ptr] @@ -648,7 +649,7 @@ end #! Where: #! - note_ptr is the memory location for the input note. #! - inputs_len is the note's input count. -proc.process_note_inputs_length +proc process_note_inputs_length # move the inputs length from the advice stack to the operand stack adv_push.1 # => [inputs_len, note_ptr] @@ -676,7 +677,7 @@ end #! - note_ptr is the memory location for the input note. #! - assets_count is the note's assets count. #! - ASSET_0, ..., ASSET_N are the padded note's assets. -proc.process_note_assets +proc process_note_assets # verify and save the assets count # --------------------------------------------------------------------------------------------- @@ -735,7 +736,7 @@ proc.process_note_assets end # => [PERM, PERM, PERM, assets_ptr+8n, note_ptr, counter+2n, rounded_num_assets] - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [ASSET_COMMITMENT_COMPUTED, assets_ptr+8n, note_ptr, counter+2n, rounded_num_assets] swapw drop movdn.2 drop drop @@ -754,7 +755,7 @@ end #! #! Where: #! - note_ptr is the memory location for the input note. -proc.add_input_note_assets_to_vault +proc add_input_note_assets_to_vault # prepare the stack # --------------------------------------------------------------------------------------------- @@ -806,7 +807,7 @@ end #! Where: #! - note_ptr is the memory location for the input note. #! - NOTE_ID is the note's id, i.e. `hash(RECIPIENT || ASSET_COMMITMENT)`. -proc.compute_input_note_id +proc compute_input_note_id # compute SERIAL_COMMITMENT: hash(SERIAL_NUMBER || EMPTY_WORD) dup exec.memory::get_input_note_serial_num padw hmerge # => [SERIAL_COMMITMENT, note_ptr] @@ -874,7 +875,7 @@ end #! - block_num is the note's creation block number. #! - BLOCK_SUB_COMMITMENT is the block's sub_commitment for which the note was created. #! - NOTE_ROOT is the merkle root of the note's tree. -proc.process_input_note +proc process_input_note # note details # --------------------------------------------------------------------------------------------- @@ -969,7 +970,7 @@ end #! - num_notes is the number of input notes. #! - INPUT_NOTES_COMMITMENT, see `transaction::api::get_input_notes_commitment`. #! - NOTE_DATA is the input notes' details, for format see `prologue::process_input_note`. -proc.process_input_notes_data +proc process_input_notes_data # get the number of input notes from the advice stack adv_push.1 # => [num_notes] @@ -1036,7 +1037,7 @@ proc.process_input_notes_data # => [has_more_notes, PERM, PERM, PERM, idx+1, num_notes] end - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [INPUT_NOTES_COMMITMENT, idx+1, num_notes] # assert the input notes and the commitment matches @@ -1070,7 +1071,7 @@ end #! - TX_SCRIPT_ROOT is the transaction's script root. #! - TX_SCRIPT_ARGS is the word of values which could be used directly or could be used to obtain #! some values associated with it from the advice map. -proc.process_tx_script_data +proc process_tx_script_data # read the transaction script root from the advice stack padw adv_loadw # => [TX_SCRIPT_ROOT] @@ -1101,7 +1102,7 @@ end #! #! Where: #! - AUTH_ARGS is the argument passed to the auth procedure. -proc.process_auth_procedure_data +proc process_auth_procedure_data # read the auth procedure args from the advice stack padw adv_loadw # => [AUTH_ARGS] @@ -1196,7 +1197,7 @@ end #! - data provided by the advice provider does not match global inputs. #! - the account data is invalid. #! - any of the input notes do note exist in the note db. -export.prepare_transaction +pub proc prepare_transaction exec.process_global_inputs # => [block_num] diff --git a/crates/miden-lib/asm/kernels/transaction/lib/tx.masm b/crates/miden-protocol/asm/kernels/transaction/lib/tx.masm similarity index 86% rename from crates/miden-lib/asm/kernels/transaction/lib/tx.masm rename to crates/miden-protocol/asm/kernels/transaction/lib/tx.masm index e7e137c885..df4c0e9d42 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/tx.masm @@ -1,19 +1,19 @@ -use.$kernel::memory -use.$kernel::note +use $kernel::memory +use $kernel::note # CONSTANTS # ================================================================================================= # Max value for U16, used as the upper limit for expiration block delta -const.EXPIRY_UPPER_LIMIT=0xFFFF+1 +const EXPIRY_UPPER_LIMIT=0xFFFF+1 # Max U32 value, used for initializing the expiration block number -const.MAX_BLOCK_NUM=0xFFFFFFFF +const MAX_BLOCK_NUM=0xFFFFFFFF # ERRORS # ================================================================================================= -const.ERR_TX_INVALID_EXPIRATION_DELTA="transaction expiration block delta must be within 0x1 and 0xFFFF" +const ERR_TX_INVALID_EXPIRATION_DELTA="transaction expiration block delta must be within 0x1 and 0xFFFF" # PROCEDURES # ================================================================================================= @@ -25,7 +25,7 @@ const.ERR_TX_INVALID_EXPIRATION_DELTA="transaction expiration block delta must b #! #! Where: #! - BLOCK_COMMITMENT is the commitment of the transaction reference block. -export.memory::get_block_commitment +pub use memory::get_block_commitment #! Returns the block number of the transaction reference block. #! @@ -34,7 +34,7 @@ export.memory::get_block_commitment #! #! Where: #! - num is the transaction reference block number. -export.memory::get_blk_num->get_block_number +pub use memory::get_blk_num->get_block_number #! Returns the block timestamp of the reference block for this transaction. #! @@ -43,7 +43,7 @@ export.memory::get_blk_num->get_block_number #! #! Where: #! - timestamp is the timestamp of the reference block for this transaction. -export.memory::get_blk_timestamp->get_block_timestamp +pub use memory::get_blk_timestamp->get_block_timestamp #! Returns the input notes commitment hash. #! @@ -54,7 +54,7 @@ export.memory::get_blk_timestamp->get_block_timestamp #! #! Where: #! - INPUT_NOTES_COMMITMENT is the input notes commitment hash. -export.memory::get_input_notes_commitment +pub use memory::get_input_notes_commitment #! Returns the output notes commitment hash. This is computed as a sequential hash of #! (note_id, note_metadata) tuples over all output notes. @@ -64,7 +64,7 @@ export.memory::get_input_notes_commitment #! #! Where: #! - OUTPUT_NOTES_COMMITMENT is the output notes commitment. -export.note::compute_output_notes_commitment->get_output_notes_commitment +pub use note::compute_output_notes_commitment->get_output_notes_commitment #! Returns the total number of input notes consumed by this transaction. #! @@ -73,7 +73,7 @@ export.note::compute_output_notes_commitment->get_output_notes_commitment #! #! Where: #! - num_input_notes is the total number of input notes consumed by this transaction. -export.memory::get_num_input_notes +pub use memory::get_num_input_notes #! Returns the current number of output notes created in this transaction. #! @@ -82,7 +82,7 @@ export.memory::get_num_input_notes #! #! Where: #! - num_output_notes is the number of output notes created in this transaction so far. -export.memory::get_num_output_notes +pub use memory::get_num_output_notes #! Updates the transaction expiration block delta. #! @@ -95,7 +95,7 @@ export.memory::get_num_output_notes #! #! Where: #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). -export.update_expiration_block_delta +pub proc update_expiration_block_delta # Ensure block_height_delta is between 1 and 0xFFFF (inclusive) dup neq.0 assert.err=ERR_TX_INVALID_EXPIRATION_DELTA # => [block_height_delta] @@ -130,7 +130,7 @@ end #! #! Where: #! - block_height_delta is the stored expiration time delta (1 to 0xFFFF). -export.get_expiration_delta +pub proc get_expiration_delta exec.memory::get_expiration_block_num # => [stored_expiration_block_num] diff --git a/crates/miden-lib/asm/kernels/transaction/main.masm b/crates/miden-protocol/asm/kernels/transaction/main.masm similarity index 88% rename from crates/miden-lib/asm/kernels/transaction/main.masm rename to crates/miden-protocol/asm/kernels/transaction/main.masm index 959f882d83..61a0763a06 100644 --- a/crates/miden-lib/asm/kernels/transaction/main.masm +++ b/crates/miden-protocol/asm/kernels/transaction/main.masm @@ -1,37 +1,37 @@ -use.std::word +use miden::core::word -use.$kernel::epilogue -use.$kernel::memory -use.$kernel::note -use.$kernel::prologue +use $kernel::epilogue +use $kernel::memory +use $kernel::note +use $kernel::prologue # EVENTS # ================================================================================================= # Event emitted to signal that an execution of the transaction prologue has started. -const.PROLOGUE_START_EVENT=event("miden::tx::prologue_start") +const PROLOGUE_START_EVENT=event("miden::tx::prologue_start") # Event emitted to signal that an execution of the transaction prologue has ended. -const.PROLOGUE_END_EVENT=event("miden::tx::prologue_end") +const PROLOGUE_END_EVENT=event("miden::tx::prologue_end") # Event emitted to signal that the notes processing has started. -const.NOTES_PROCESSING_START_EVENT=event("miden::tx::notes_processing_start") +const NOTES_PROCESSING_START_EVENT=event("miden::tx::notes_processing_start") # Event emitted to signal that the notes processing has ended. -const.NOTES_PROCESSING_END_EVENT=event("miden::tx::notes_processing_end") +const NOTES_PROCESSING_END_EVENT=event("miden::tx::notes_processing_end") # Event emitted to signal that the note consuming has started. -const.NOTE_EXECUTION_START_EVENT=event("miden::tx::note_execution_start") +const NOTE_EXECUTION_START_EVENT=event("miden::tx::note_execution_start") # Event emitted to signal that the note consuming has ended. -const.NOTE_EXECUTION_END_EVENT=event("miden::tx::note_execution_end") +const NOTE_EXECUTION_END_EVENT=event("miden::tx::note_execution_end") # Event emitted to signal that the transaction script processing has started. -const.TX_SCRIPT_PROCESSING_START_EVENT=event("miden::tx::tx_script_processing_start") +const TX_SCRIPT_PROCESSING_START_EVENT=event("miden::tx::tx_script_processing_start") # Event emitted to signal that the transaction script processing has ended. -const.TX_SCRIPT_PROCESSING_END_EVENT=event("miden::tx::tx_script_processing_end") +const TX_SCRIPT_PROCESSING_END_EVENT=event("miden::tx::tx_script_processing_end") # Event emitted to signal that an execution of the transaction epilogue has started. -const.EPILOGUE_START_EVENT=event("miden::tx::epilogue_start") +const EPILOGUE_START_EVENT=event("miden::tx::epilogue_start") # Event emitted to signal that an execution of the transaction epilogue has ended. -const.EPILOGUE_END_EVENT=event("miden::tx::epilogue_end") +const EPILOGUE_END_EVENT=event("miden::tx::epilogue_end") # MAIN # ================================================================================================= @@ -66,7 +66,8 @@ const.EPILOGUE_END_EVENT=event("miden::tx::epilogue_end") #! - ACCOUNT_UPDATE_COMMITMENT is the hash of the the final account commitment and account #! delta commitment. #! - FEE_ASSET is the fungible asset used as the transaction fee. -proc.main.1 +@locals(1) +proc main # Prologue # --------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/asm/kernels/transaction/tx_script_main.masm b/crates/miden-protocol/asm/kernels/transaction/tx_script_main.masm similarity index 93% rename from crates/miden-lib/asm/kernels/transaction/tx_script_main.masm rename to crates/miden-protocol/asm/kernels/transaction/tx_script_main.masm index 79c99f8549..b0b12ea8cf 100644 --- a/crates/miden-lib/asm/kernels/transaction/tx_script_main.masm +++ b/crates/miden-protocol/asm/kernels/transaction/tx_script_main.masm @@ -1,12 +1,12 @@ -use.std::word +use miden::core::word -use.$kernel::memory -use.$kernel::prologue +use $kernel::memory +use $kernel::prologue # ERRORS # ================================================================================================= -const.ERR_TX_TRANSACTION_SCRIPT_IS_MISSING="the transaction script is missing" +const ERR_TX_TRANSACTION_SCRIPT_IS_MISSING="the transaction script is missing" # MAIN # ================================================================================================= @@ -34,7 +34,7 @@ const.ERR_TX_TRANSACTION_SCRIPT_IS_MISSING="the transaction script is missing" #! - account_id is the account that the transaction is being executed against. #! - INITIAL_ACCOUNT_COMMITMENT is the account state prior to the transaction, EMPTY_WORD for new accounts. #! - INPUT_NOTES_COMMITMENT, see `transaction::api::get_input_notes_commitment`. -proc.main +proc main # Prologue # --------------------------------------------------------------------------------------------- diff --git a/crates/miden-lib/asm/miden/active_account.masm b/crates/miden-protocol/asm/protocol/active_account.masm similarity index 96% rename from crates/miden-lib/asm/miden/active_account.masm rename to crates/miden-protocol/asm/protocol/active_account.masm index 6c45d7d80e..9f69e30cc1 100644 --- a/crates/miden-lib/asm/miden/active_account.masm +++ b/crates/miden-protocol/asm/protocol/active_account.masm @@ -1,4 +1,4 @@ -use.miden::kernel_proc_offsets +use miden::protocol::kernel_proc_offsets # ACTIVE ACCOUNT PROCEDURES # ================================================================================================= @@ -15,7 +15,7 @@ use.miden::kernel_proc_offsets #! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account. #! #! Invocation: exec -export.get_id +pub proc get_id # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -48,7 +48,7 @@ end #! - nonce is the active account's nonce. #! #! Invocation: exec -export.get_nonce +pub proc get_nonce # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -76,7 +76,7 @@ end #! - INIT_COMMITMENT is the initial account commitment. #! #! Invocation: exec -export.get_initial_commitment +pub proc get_initial_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -101,7 +101,7 @@ end #! - ACCOUNT_COMMITMENT is the commitment of the account data. #! #! Invocation: exec -export.compute_commitment +pub proc compute_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -139,7 +139,7 @@ end #! - CODE_COMMITMENT is the commitment of the account code. #! #! Invocation: exec -export.get_code_commitment +pub proc get_code_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -169,7 +169,7 @@ end #! - INIT_STORAGE_COMMITMENT is the initial account storage commitment. #! #! Invocation: exec -export.get_initial_storage_commitment +pub proc get_initial_storage_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -203,7 +203,7 @@ end #! - STORAGE_COMMITMENT is the commitment of the account storage. #! #! Invocation: exec -export.compute_storage_commitment +pub proc compute_storage_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -228,7 +228,7 @@ end #! - INIT_VAULT_ROOT is the initial account vault root. #! #! Invocation: exec -export.get_initial_vault_root +pub proc get_initial_vault_root # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -253,7 +253,7 @@ end #! - VAULT_ROOT is the root of the account vault. #! #! Invocation: exec -export.get_vault_root +pub proc get_vault_root # pad the stack for syscall invocation padw padw padw push.0.0.0 # => [pad(15)] @@ -286,7 +286,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: exec -export.get_item +pub proc get_item push.0 movdn.2 # => [slot_id_prefix, slot_id_suffix, 0] @@ -320,7 +320,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! #! Invocation: exec -export.get_initial_item +pub proc get_initial_item push.0 movdn.2 # => [slot_id_prefix, slot_id_suffix, 0] @@ -356,7 +356,7 @@ end #! - the slot item at index is not a map. #! #! Invocation: exec -export.get_map_item +pub proc get_map_item exec.kernel_proc_offsets::account_get_map_item_offset # => [offset, slot_id_prefix, slot_id_suffix, KEY] @@ -389,7 +389,7 @@ end #! - the slot item at index is not a map. #! #! Invocation: exec -export.get_initial_map_item +pub proc get_initial_map_item exec.kernel_proc_offsets::account_get_initial_map_item_offset # => [offset, slot_id_prefix, slot_id_suffix, KEY] @@ -422,7 +422,7 @@ end #! - the provided faucet ID is not an ID of a fungible faucet. #! #! Invocation: exec -export.get_balance +pub proc get_balance exec.kernel_proc_offsets::account_get_balance_offset # => [offset, faucet_id_prefix, faucet_id_suffix] @@ -453,7 +453,7 @@ end #! - the provided faucet ID is not an ID of a fungible faucet. #! #! Invocation: exec -export.get_initial_balance +pub proc get_initial_balance exec.kernel_proc_offsets::account_get_initial_balance_offset # => [offset, faucet_id_prefix, faucet_id_suffix] @@ -483,7 +483,7 @@ end #! - the ASSET is a fungible asset. #! #! Invocation: exec -export.has_non_fungible_asset +pub proc has_non_fungible_asset exec.kernel_proc_offsets::account_has_non_fungible_asset_offset # => [offset, ASSET] @@ -508,7 +508,7 @@ end #! - num_procedures is the number of procedures in the active account. #! #! Invocation: exec -export.get_num_procedures +pub proc get_num_procedures # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -537,7 +537,7 @@ end #! - the procedure index is out of bounds. #! #! Invocation: exec -export.get_procedure_root +pub proc get_procedure_root # => [index] push.0.0 movup.2 @@ -570,7 +570,7 @@ end #! available on the active account. #! #! Invocation: exec -export.has_procedure +pub proc has_procedure exec.kernel_proc_offsets::account_has_procedure_offset # => [offset, PROC_ROOT] diff --git a/crates/miden-lib/asm/miden/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm similarity index 80% rename from crates/miden-lib/asm/miden/active_note.masm rename to crates/miden-protocol/asm/protocol/active_note.masm index 69d2e6753b..3a32919912 100644 --- a/crates/miden-lib/asm/miden/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -1,15 +1,14 @@ -use.std::mem +use miden::core::mem -use.miden::kernel_proc_offsets -use.miden::note -use.miden::contracts::wallets::basic->wallet +use miden::protocol::kernel_proc_offsets +use miden::protocol::note # ERRORS # ================================================================================================= -const.ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT="note data does not match the commitment" +const ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT="note data does not match the commitment" -const.ERR_NOTE_INVALID_NUMBER_OF_INPUTS="the specified number of note inputs does not match the actual number" +const ERR_NOTE_INVALID_NUMBER_OF_INPUTS="the specified number of note inputs does not match the actual number" # ACTIVE NOTE PROCEDURES # ================================================================================================= @@ -30,7 +29,7 @@ const.ERR_NOTE_INVALID_NUMBER_OF_INPUTS="the specified number of note inputs doe #! - no note is currently active. #! #! Invocation: exec -export.get_assets +pub proc get_assets # pad the stack padw padw padw push.0.0 # => [pad(14), dest_ptr] @@ -66,7 +65,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_recipient +pub proc get_recipient # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -103,7 +102,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_inputs +pub proc get_inputs # pad the stack padw padw padw push.0.0 # => [pad(14), dest_ptr] @@ -140,7 +139,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_metadata +pub proc get_metadata # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -172,7 +171,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_sender +pub proc get_sender # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -208,7 +207,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_serial_number +pub proc get_serial_number # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -240,7 +239,7 @@ end #! - no note is currently active. #! #! Invocation: exec -export.get_script_root +pub proc get_script_root # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -260,63 +259,6 @@ export.get_script_root # => [SCRIPT_ROOT] end -#! Adds all assets from the active note to the native account's vault. -#! -#! Inputs: [] -#! Outputs: [] -export.add_assets_to_account.1024 - # write assets to local memory starting at offset 0 - # we have allocated 4 * MAX_ASSETS_PER_NOTE number of locals so all assets should fit - # since the asset memory will be overwritten, we don't have to initialize the locals to zero - locaddr.0 exec.get_assets - # => [num_of_assets, ptr = 0] - - # compute the pointer at which we should stop iterating - mul.4 dup.1 add - # => [end_ptr, ptr] - - # pad the stack and move the pointer to the top - padw movup.5 - # => [ptr, EMPTY_WORD, end_ptr] - - # loop if the amount of assets is non-zero - dup dup.6 neq - # => [should_loop, ptr, EMPTY_WORD, end_ptr] - - while.true - # => [ptr, EMPTY_WORD, end_ptr] - - # save the pointer so that we can use it later - dup movdn.5 - # => [ptr, EMPTY_WORD, ptr, end_ptr] - - # load the asset - mem_loadw_be - # => [ASSET, ptr, end_ptr] - - # pad the stack before call - padw swapw padw padw swapdw - # => [ASSET, pad(12), ptr, end_ptr] - - # add asset to the account - call.wallet::receive_asset - # => [pad(16), ptr, end_ptr] - - # clean the stack after call - dropw dropw dropw - # => [EMPTY_WORD, ptr, end_ptr] - - # increment the pointer and continue looping if ptr != end_ptr - movup.4 add.4 dup dup.6 neq - # => [should_loop, ptr+4, EMPTY_WORD, end_ptr] - end - # => [ptr', EMPTY_WORD, end_ptr] - - # clear the stack - drop dropw drop - # => [] -end - # HELPER PROCEDURES # ================================================================================================= @@ -330,7 +272,7 @@ end #! } #! Outputs: #! Operand stack: [num_inputs, dest_ptr] -proc.write_inputs_to_memory +proc write_inputs_to_memory # load the inputs from the advice map to the advice stack adv.push_mapvaln # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] diff --git a/crates/miden-lib/asm/miden/asset.masm b/crates/miden-protocol/asm/protocol/asset.masm similarity index 86% rename from crates/miden-lib/asm/miden/asset.masm rename to crates/miden-protocol/asm/protocol/asset.masm index 6c6d8f8d6b..e8da408a8a 100644 --- a/crates/miden-lib/asm/miden/asset.masm +++ b/crates/miden-protocol/asm/protocol/asset.masm @@ -1,13 +1,13 @@ -use.miden::account_id +use miden::protocol::account_id # ERRORS # ================================================================================================= -const.ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the fungible asset because the provided faucet id is not from a fungible faucet" +const ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the fungible asset because the provided faucet id is not from a fungible faucet" -const.ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT="fungible asset build operation called with amount that exceeds the maximum allowed asset amount" +const ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT="fungible asset build operation called with amount that exceeds the maximum allowed asset amount" -const.ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet" +const ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet" # PROCEDURES # ================================================================================================= @@ -24,7 +24,7 @@ const.ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID="failed to build the #! - ASSET is the built fungible asset. #! #! Invocation: exec -export.build_fungible_asset +pub proc build_fungible_asset # assert the faucet is a fungible faucet dup exec.account_id::is_fungible_faucet assert.err=ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID # => [faucet_id_prefix, faucet_id_suffix, amount] @@ -51,7 +51,7 @@ end #! - ASSET is the built non-fungible asset. #! #! Invocation: exec -export.build_non_fungible_asset +pub proc build_non_fungible_asset # assert the faucet is a non-fungible faucet dup exec.account_id::is_non_fungible_faucet assert.err=ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID @@ -69,4 +69,4 @@ end #! Outputs: [fungible_asset_max_amount] #! #! fungible_asset_max_amount is the maximum amount of a fungible asset. -export.::miden::util::asset::get_fungible_asset_max_amount +pub use ::miden::protocol::util::asset::get_fungible_asset_max_amount diff --git a/crates/miden-lib/asm/miden/faucet.masm b/crates/miden-protocol/asm/protocol/faucet.masm similarity index 94% rename from crates/miden-lib/asm/miden/faucet.masm rename to crates/miden-protocol/asm/protocol/faucet.masm index 1332f2c3ca..f3afe38e9b 100644 --- a/crates/miden-lib/asm/miden/faucet.masm +++ b/crates/miden-protocol/asm/protocol/faucet.masm @@ -1,6 +1,6 @@ -use.miden::asset -use.miden::active_account -use.miden::kernel_proc_offsets +use miden::protocol::asset +use miden::protocol::active_account +use miden::protocol::kernel_proc_offsets #! Creates a fungible asset for the faucet the transaction is being executed against. #! @@ -15,7 +15,7 @@ use.miden::kernel_proc_offsets #! - the active account is not a fungible faucet. #! #! Invocation: exec -export.create_fungible_asset +pub proc create_fungible_asset # fetch the id of the faucet the transaction is being executed against. exec.active_account::get_id # => [id_prefix, id_suffix, amount] @@ -38,7 +38,7 @@ end #! - the active account is not a non-fungible faucet. #! #! Invocation: exec -export.create_non_fungible_asset +pub proc create_non_fungible_asset # get the id of the faucet the transaction is being executed against exec.active_account::get_id swap drop # => [faucet_id_prefix, DATA_HASH] @@ -66,7 +66,7 @@ end #! - for non-fungible faucets if the non-fungible asset being minted already exists. #! #! Invocation: exec -export.mint +pub proc mint exec.kernel_proc_offsets::faucet_mint_asset_offset # => [offset, ASSET] @@ -101,7 +101,7 @@ end #! provided as input to the transaction via a note or the accounts vault. #! #! Invocation: exec -export.burn +pub proc burn exec.kernel_proc_offsets::faucet_burn_asset_offset # => [offset, ASSET] @@ -130,7 +130,7 @@ end #! - the transaction is not being executed against a fungible faucet. #! #! Invocation: exec -export.get_total_issuance +pub proc get_total_issuance # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -161,7 +161,7 @@ end #! - the ASSET is not associated with the faucet the transaction is being executed against. #! #! Invocation: exec -export.is_non_fungible_asset_issued +pub proc is_non_fungible_asset_issued exec.kernel_proc_offsets::faucet_is_non_fungible_asset_issued_offset # => [offset, ASSET] diff --git a/crates/miden-lib/asm/miden/input_note.masm b/crates/miden-protocol/asm/protocol/input_note.masm similarity index 97% rename from crates/miden-lib/asm/miden/input_note.masm rename to crates/miden-protocol/asm/protocol/input_note.masm index 228899e11e..9a88dc7636 100644 --- a/crates/miden-lib/asm/miden/input_note.masm +++ b/crates/miden-protocol/asm/protocol/input_note.masm @@ -1,5 +1,5 @@ -use.miden::kernel_proc_offsets -use.miden::note +use miden::protocol::kernel_proc_offsets +use miden::protocol::note # PROCEDURES # ================================================================================================= @@ -20,7 +20,7 @@ use.miden::note #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_assets_info +pub proc get_assets_info # start padding the stack push.0 swap # => [note_index, 0] @@ -71,7 +71,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_assets +pub proc get_assets # get the assets commitment and assets number dup.1 exec.get_assets_info # => [ASSETS_COMMITMENT, num_assets, dest_ptr, note_index] @@ -94,7 +94,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_recipient +pub proc get_recipient # start padding the stack push.0 swap # => [note_index, 0] @@ -132,7 +132,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_metadata +pub proc get_metadata # start padding the stack push.0 swap # => [note_index, 0] @@ -170,7 +170,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_sender +pub proc get_sender # start padding the stack push.0 swap # => [note_index, 0] @@ -213,7 +213,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_inputs_info +pub proc get_inputs_info # start padding the stack push.0 swap # => [note_index, 0] @@ -254,7 +254,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_script_root +pub proc get_script_root # start padding the stack push.0 swap # => [note_index, 0] @@ -292,7 +292,7 @@ end #! - the note index is greater or equal to the total number of input notes. #! #! Invocation: exec -export.get_serial_number +pub proc get_serial_number # start padding the stack push.0 swap # => [note_index, 0] diff --git a/crates/miden-lib/asm/miden/kernel_proc_offsets.masm b/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm similarity index 80% rename from crates/miden-lib/asm/miden/kernel_proc_offsets.masm rename to crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm index 3f7215e494..be593b3ec1 100644 --- a/crates/miden-lib/asm/miden/kernel_proc_offsets.masm +++ b/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm @@ -4,92 +4,92 @@ ### Account ##################################### # Entire account commitment -const.ACCOUNT_GET_INITIAL_COMMITMENT_OFFSET=0 -const.ACCOUNT_COMPUTE_COMMITMENT_OFFSET=1 +const ACCOUNT_GET_INITIAL_COMMITMENT_OFFSET=0 +const ACCOUNT_COMPUTE_COMMITMENT_OFFSET=1 # ID -const.ACCOUNT_GET_ID_OFFSET=2 +const ACCOUNT_GET_ID_OFFSET=2 # Nonce -const.ACCOUNT_GET_NONCE_OFFSET=3 # accessor -const.ACCOUNT_INCR_NONCE_OFFSET=4 # mutator +const ACCOUNT_GET_NONCE_OFFSET=3 # accessor +const ACCOUNT_INCR_NONCE_OFFSET=4 # mutator # Code -const.ACCOUNT_GET_CODE_COMMITMENT_OFFSET=5 +const ACCOUNT_GET_CODE_COMMITMENT_OFFSET=5 # Storage -const.ACCOUNT_GET_INITIAL_STORAGE_COMMITMENT_OFFSET=6 -const.ACCOUNT_COMPUTE_STORAGE_COMMITMENT_OFFSET=7 -const.ACCOUNT_GET_ITEM_OFFSET=8 -const.ACCOUNT_GET_INITIAL_ITEM_OFFSET=9 -const.ACCOUNT_SET_ITEM_OFFSET=10 -const.ACCOUNT_GET_MAP_ITEM_OFFSET=11 -const.ACCOUNT_GET_INITIAL_MAP_ITEM_OFFSET=12 -const.ACCOUNT_SET_MAP_ITEM_OFFSET=13 +const ACCOUNT_GET_INITIAL_STORAGE_COMMITMENT_OFFSET=6 +const ACCOUNT_COMPUTE_STORAGE_COMMITMENT_OFFSET=7 +const ACCOUNT_GET_ITEM_OFFSET=8 +const ACCOUNT_GET_INITIAL_ITEM_OFFSET=9 +const ACCOUNT_SET_ITEM_OFFSET=10 +const ACCOUNT_GET_MAP_ITEM_OFFSET=11 +const ACCOUNT_GET_INITIAL_MAP_ITEM_OFFSET=12 +const ACCOUNT_SET_MAP_ITEM_OFFSET=13 # Vault -const.ACCOUNT_GET_INITIAL_VAULT_ROOT_OFFSET=14 -const.ACCOUNT_GET_VAULT_ROOT_OFFSET=15 -const.ACCOUNT_ADD_ASSET_OFFSET=16 -const.ACCOUNT_REMOVE_ASSET_OFFSET=17 -const.ACCOUNT_GET_BALANCE_OFFSET=18 -const.ACCOUNT_GET_INITIAL_BALANCE_OFFSET=19 -const.ACCOUNT_HAS_NON_FUNGIBLE_ASSET_OFFSET=20 +const ACCOUNT_GET_INITIAL_VAULT_ROOT_OFFSET=14 +const ACCOUNT_GET_VAULT_ROOT_OFFSET=15 +const ACCOUNT_ADD_ASSET_OFFSET=16 +const ACCOUNT_REMOVE_ASSET_OFFSET=17 +const ACCOUNT_GET_BALANCE_OFFSET=18 +const ACCOUNT_GET_INITIAL_BALANCE_OFFSET=19 +const ACCOUNT_HAS_NON_FUNGIBLE_ASSET_OFFSET=20 # Delta -const.ACCOUNT_COMPUTE_DELTA_COMMITMENT_OFFSET=21 +const ACCOUNT_COMPUTE_DELTA_COMMITMENT_OFFSET=21 # Procedure introspection -const.ACCOUNT_GET_NUM_PROCEDURES_OFFSET=22 -const.ACCOUNT_GET_PROCEDURE_ROOT_OFFSET=23 -const.ACCOUNT_WAS_PROCEDURE_CALLED_OFFSET=24 -const.ACCOUNT_HAS_PROCEDURE_OFFSET=25 +const ACCOUNT_GET_NUM_PROCEDURES_OFFSET=22 +const ACCOUNT_GET_PROCEDURE_ROOT_OFFSET=23 +const ACCOUNT_WAS_PROCEDURE_CALLED_OFFSET=24 +const ACCOUNT_HAS_PROCEDURE_OFFSET=25 ### Faucet ###################################### -const.FAUCET_MINT_ASSET_OFFSET=26 -const.FAUCET_BURN_ASSET_OFFSET=27 -const.FAUCET_GET_TOTAL_FUNGIBLE_ASSET_ISSUANCE_OFFSET=28 -const.FAUCET_IS_NON_FUNGIBLE_ASSET_ISSUED_OFFSET=29 +const FAUCET_MINT_ASSET_OFFSET=26 +const FAUCET_BURN_ASSET_OFFSET=27 +const FAUCET_GET_TOTAL_FUNGIBLE_ASSET_ISSUANCE_OFFSET=28 +const FAUCET_IS_NON_FUNGIBLE_ASSET_ISSUED_OFFSET=29 ### Note ######################################## # input notes -const.INPUT_NOTE_GET_METADATA_OFFSET=30 -const.INPUT_NOTE_GET_ASSETS_INFO_OFFSET=31 -const.INPUT_NOTE_GET_SCRIPT_ROOT_OFFSET=32 -const.INPUT_NOTE_GET_INPUTS_INFO_OFFSET=33 -const.INPUT_NOTE_GET_SERIAL_NUMBER_OFFSET=34 -const.INPUT_NOTE_GET_RECIPIENT_OFFSET=35 +const INPUT_NOTE_GET_METADATA_OFFSET=30 +const INPUT_NOTE_GET_ASSETS_INFO_OFFSET=31 +const INPUT_NOTE_GET_SCRIPT_ROOT_OFFSET=32 +const INPUT_NOTE_GET_INPUTS_INFO_OFFSET=33 +const INPUT_NOTE_GET_SERIAL_NUMBER_OFFSET=34 +const INPUT_NOTE_GET_RECIPIENT_OFFSET=35 # output notes -const.OUTPUT_NOTE_CREATE_OFFSET=36 -const.OUTPUT_NOTE_GET_METADATA_OFFSET=37 -const.OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET=38 -const.OUTPUT_NOTE_GET_RECIPIENT_OFFSET=39 -const.OUTPUT_NOTE_ADD_ASSET_OFFSET=40 +const OUTPUT_NOTE_CREATE_OFFSET=36 +const OUTPUT_NOTE_GET_METADATA_OFFSET=37 +const OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET=38 +const OUTPUT_NOTE_GET_RECIPIENT_OFFSET=39 +const OUTPUT_NOTE_ADD_ASSET_OFFSET=40 ### Tx ########################################## # input notes -const.TX_GET_NUM_INPUT_NOTES_OFFSET=41 -const.TX_GET_INPUT_NOTES_COMMITMENT_OFFSET=42 +const TX_GET_NUM_INPUT_NOTES_OFFSET=41 +const TX_GET_INPUT_NOTES_COMMITMENT_OFFSET=42 # output notes -const.TX_GET_NUM_OUTPUT_NOTES_OFFSET=43 -const.TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET=44 +const TX_GET_NUM_OUTPUT_NOTES_OFFSET=43 +const TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET=44 # block info -const.TX_GET_BLOCK_COMMITMENT_OFFSET=45 -const.TX_GET_BLOCK_NUMBER_OFFSET=46 -const.TX_GET_BLOCK_TIMESTAMP_OFFSET=47 +const TX_GET_BLOCK_COMMITMENT_OFFSET=45 +const TX_GET_BLOCK_NUMBER_OFFSET=46 +const TX_GET_BLOCK_TIMESTAMP_OFFSET=47 # foreign context -const.TX_START_FOREIGN_CONTEXT_OFFSET=48 -const.TX_END_FOREIGN_CONTEXT_OFFSET=49 +const TX_START_FOREIGN_CONTEXT_OFFSET=48 +const TX_END_FOREIGN_CONTEXT_OFFSET=49 # expiration data -const.TX_GET_EXPIRATION_DELTA_OFFSET=50 # accessor -const.TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=51 # mutator +const TX_GET_EXPIRATION_DELTA_OFFSET=50 # accessor +const TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=51 # mutator # ACCESSORS # ------------------------------------------------------------------------------------------------- @@ -104,7 +104,7 @@ const.TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=51 # mutator #! Where: #! - proc_offset is the offset of the `account_get_initial_commitment` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_initial_commitment_offset +pub proc account_get_initial_commitment_offset push.ACCOUNT_GET_INITIAL_COMMITMENT_OFFSET end @@ -116,7 +116,7 @@ end #! Where: #! - proc_offset is the offset of the `account_compute_commitment` kernel procedure required to get #! the address where this procedure is stored. -export.account_compute_commitment_offset +pub proc account_compute_commitment_offset push.ACCOUNT_COMPUTE_COMMITMENT_OFFSET end @@ -128,7 +128,7 @@ end #! Where: #! - proc_offset is the offset of the `account_compute_delta_commitment` kernel procedure required #! to get the address where this procedure is stored. -export.account_compute_delta_commitment_offset +pub proc account_compute_delta_commitment_offset push.ACCOUNT_COMPUTE_DELTA_COMMITMENT_OFFSET end @@ -140,7 +140,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_id` kernel procedure required to get the address #! where this procedure is stored. -export.account_get_id_offset +pub proc account_get_id_offset push.ACCOUNT_GET_ID_OFFSET end @@ -152,7 +152,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_nonce` kernel procedure required to get the #! address where this procedure is stored. -export.account_get_nonce_offset +pub proc account_get_nonce_offset push.ACCOUNT_GET_NONCE_OFFSET end @@ -164,7 +164,7 @@ end #! Where: #! - proc_offset is the offset of the `account_incr_nonce` kernel procedure required to get the #! address where this procedure is stored. -export.account_incr_nonce_offset +pub proc account_incr_nonce_offset push.ACCOUNT_INCR_NONCE_OFFSET end @@ -176,7 +176,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_code_commitment` kernel procedure required to get #! the address where this procedure is stored. -export.account_get_code_commitment_offset +pub proc account_get_code_commitment_offset push.ACCOUNT_GET_CODE_COMMITMENT_OFFSET end @@ -188,7 +188,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_storage_commitment` kernel procedure #! required to get the address where this procedure is stored. -export.account_get_initial_storage_commitment_offset +pub proc account_get_initial_storage_commitment_offset push.ACCOUNT_GET_INITIAL_STORAGE_COMMITMENT_OFFSET end @@ -200,7 +200,7 @@ end #! Where: #! - proc_offset is the offset of the `account_compute_storage_commitment` kernel procedure required #! to get the address where this procedure is stored. -export.account_compute_storage_commitment_offset +pub proc account_compute_storage_commitment_offset push.ACCOUNT_COMPUTE_STORAGE_COMMITMENT_OFFSET end @@ -212,7 +212,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_item` kernel procedure required to get the #! address where this procedure is stored. -export.account_get_item_offset +pub proc account_get_item_offset push.ACCOUNT_GET_ITEM_OFFSET end @@ -224,7 +224,7 @@ end #! Where: #! - proc_offset is the offset of the `account_set_item` kernel procedure required to get the #! address where this procedure is stored. -export.account_set_item_offset +pub proc account_set_item_offset push.ACCOUNT_SET_ITEM_OFFSET end @@ -236,7 +236,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_map_item` kernel procedure required to get the #! address where this procedure is stored. -export.account_get_map_item_offset +pub proc account_get_map_item_offset push.ACCOUNT_GET_MAP_ITEM_OFFSET end @@ -248,7 +248,7 @@ end #! Where: #! - proc_offset is the offset of the `account_set_map_item` kernel procedure required to get the #! address where this procedure is stored. -export.account_set_map_item_offset +pub proc account_set_map_item_offset push.ACCOUNT_SET_MAP_ITEM_OFFSET end @@ -260,7 +260,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_item` kernel procedure required to get #! the address where this procedure is stored. -export.account_get_initial_item_offset +pub proc account_get_initial_item_offset push.ACCOUNT_GET_INITIAL_ITEM_OFFSET end @@ -272,7 +272,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_map_item` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_initial_map_item_offset +pub proc account_get_initial_map_item_offset push.ACCOUNT_GET_INITIAL_MAP_ITEM_OFFSET end @@ -284,7 +284,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_vault_root` kernel procedure required #! to get the address where this procedure is stored. -export.account_get_initial_vault_root_offset +pub proc account_get_initial_vault_root_offset push.ACCOUNT_GET_INITIAL_VAULT_ROOT_OFFSET end @@ -296,7 +296,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_vault_root` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_vault_root_offset +pub proc account_get_vault_root_offset push.ACCOUNT_GET_VAULT_ROOT_OFFSET end @@ -308,7 +308,7 @@ end #! Where: #! - proc_offset is the offset of the `account_add_asset` kernel procedure required to get the #! address where this procedure is stored. -export.account_add_asset_offset +pub proc account_add_asset_offset push.ACCOUNT_ADD_ASSET_OFFSET end @@ -320,7 +320,7 @@ end #! Where: #! - proc_offset is the offset of the `account_remove_asset` kernel procedure required to get the #! address where this procedure is stored. -export.account_remove_asset_offset +pub proc account_remove_asset_offset push.ACCOUNT_REMOVE_ASSET_OFFSET end @@ -332,7 +332,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_balance` kernel procedure required to get the #! address where this procedure is stored. -export.account_get_balance_offset +pub proc account_get_balance_offset push.ACCOUNT_GET_BALANCE_OFFSET end @@ -344,7 +344,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_initial_balance` kernel procedure required to get #! the address where this procedure is stored. -export.account_get_initial_balance_offset +pub proc account_get_initial_balance_offset push.ACCOUNT_GET_INITIAL_BALANCE_OFFSET end @@ -356,7 +356,7 @@ end #! Where: #! - proc_offset is the offset of the `account_has_non_fungible_asset` kernel procedure required to #! get the address where this procedure is stored. -export.account_has_non_fungible_asset_offset +pub proc account_has_non_fungible_asset_offset push.ACCOUNT_HAS_NON_FUNGIBLE_ASSET_OFFSET end @@ -368,7 +368,7 @@ end #! Where: #! - proc_offset is the offset of the `account_was_procedure_called` kernel procedure required to #! get the address where this procedure is stored. -export.account_was_procedure_called_offset +pub proc account_was_procedure_called_offset push.ACCOUNT_WAS_PROCEDURE_CALLED_OFFSET end @@ -380,7 +380,7 @@ end #! Where: #! - proc_offset is the offset of the `account_has_procedure` kernel procedure required to get the #! address where this procedure is stored. -export.account_has_procedure_offset +pub proc account_has_procedure_offset push.ACCOUNT_HAS_PROCEDURE_OFFSET end @@ -392,7 +392,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_num_procedures` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_num_procedures_offset +pub proc account_get_num_procedures_offset push.ACCOUNT_GET_NUM_PROCEDURES_OFFSET end @@ -404,7 +404,7 @@ end #! Where: #! - proc_offset is the offset of the `account_get_procedure_root` kernel procedure required to #! get the address where this procedure is stored. -export.account_get_procedure_root_offset +pub proc account_get_procedure_root_offset push.ACCOUNT_GET_PROCEDURE_ROOT_OFFSET end @@ -418,7 +418,7 @@ end #! Where: #! - proc_offset is the offset of the `faucet_mint_asset` kernel procedure required to get the #! address where this procedure is stored. -export.faucet_mint_asset_offset +pub proc faucet_mint_asset_offset push.FAUCET_MINT_ASSET_OFFSET end @@ -430,7 +430,7 @@ end #! Where: #! - proc_offset is the offset of the `faucet_burn_asset` kernel procedure required to get the #! address where this procedure is stored. -export.faucet_burn_asset_offset +pub proc faucet_burn_asset_offset push.FAUCET_BURN_ASSET_OFFSET end @@ -442,7 +442,7 @@ end #! Where: #! - proc_offset is the offset of the `faucet_get_total_fungible_asset_issuance` kernel procedure #! required to get the address where this procedure is stored. -export.faucet_get_total_fungible_asset_issuance_offset +pub proc faucet_get_total_fungible_asset_issuance_offset push.FAUCET_GET_TOTAL_FUNGIBLE_ASSET_ISSUANCE_OFFSET end @@ -454,7 +454,7 @@ end #! Where: #! - proc_offset is the offset of the `faucet_is_non_fungible_asset_issued` kernel procedure #! required to get the address where this procedure is stored. -export.faucet_is_non_fungible_asset_issued_offset +pub proc faucet_is_non_fungible_asset_issued_offset push.FAUCET_IS_NON_FUNGIBLE_ASSET_ISSUED_OFFSET end @@ -468,7 +468,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_create` kernel procedure required to get the #! address where this procedure is stored. -export.output_note_create_offset +pub proc output_note_create_offset push.OUTPUT_NOTE_CREATE_OFFSET end @@ -480,7 +480,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_add_asset` kernel procedure required to get the #! address where this procedure is stored. -export.output_note_add_asset_offset +pub proc output_note_add_asset_offset push.OUTPUT_NOTE_ADD_ASSET_OFFSET end @@ -492,7 +492,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_get_assets_info` kernel procedure required to get #! the address where this procedure is stored. -export.output_note_get_assets_info_offset +pub proc output_note_get_assets_info_offset push.OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET end @@ -504,7 +504,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_get_recipient` kernel procedure required to get #! the address where this procedure is stored. -export.output_note_get_recipient_offset +pub proc output_note_get_recipient_offset push.OUTPUT_NOTE_GET_RECIPIENT_OFFSET end @@ -516,7 +516,7 @@ end #! Where: #! - proc_offset is the offset of the `output_note_get_metadata` kernel procedure required to get #! the address where this procedure is stored. -export.output_note_get_metadata_offset +pub proc output_note_get_metadata_offset push.OUTPUT_NOTE_GET_METADATA_OFFSET end @@ -530,7 +530,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_assets_info` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_assets_info_offset +pub proc input_note_get_assets_info_offset push.INPUT_NOTE_GET_ASSETS_INFO_OFFSET end @@ -542,7 +542,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_recipient` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_recipient_offset +pub proc input_note_get_recipient_offset push.INPUT_NOTE_GET_RECIPIENT_OFFSET end @@ -554,7 +554,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_metadata` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_metadata_offset +pub proc input_note_get_metadata_offset push.INPUT_NOTE_GET_METADATA_OFFSET end @@ -566,7 +566,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_serial_number` kernel procedure required to #! get the address where this procedure is stored. -export.input_note_get_serial_number_offset +pub proc input_note_get_serial_number_offset push.INPUT_NOTE_GET_SERIAL_NUMBER_OFFSET end @@ -578,7 +578,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_inputs_info` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_inputs_info_offset +pub proc input_note_get_inputs_info_offset push.INPUT_NOTE_GET_INPUTS_INFO_OFFSET end @@ -590,7 +590,7 @@ end #! Where: #! - proc_offset is the offset of the `input_note_get_script_root` kernel procedure required to get #! the address where this procedure is stored. -export.input_note_get_script_root_offset +pub proc input_note_get_script_root_offset push.INPUT_NOTE_GET_SCRIPT_ROOT_OFFSET end @@ -604,7 +604,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_input_notes_commitment` kernel procedure required to #! get the address where this procedure is stored. -export.tx_get_input_notes_commitment_offset +pub proc tx_get_input_notes_commitment_offset push.TX_GET_INPUT_NOTES_COMMITMENT_OFFSET end @@ -616,7 +616,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_output_notes_commitment` kernel procedure required to #! get the address where this procedure is stored. -export.tx_get_output_notes_commitment_offset +pub proc tx_get_output_notes_commitment_offset push.TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET end @@ -628,7 +628,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_num_input_notes` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_num_input_notes_offset +pub proc tx_get_num_input_notes_offset push.TX_GET_NUM_INPUT_NOTES_OFFSET end @@ -640,7 +640,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_num_output_notes` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_num_output_notes_offset +pub proc tx_get_num_output_notes_offset push.TX_GET_NUM_OUTPUT_NOTES_OFFSET end @@ -652,7 +652,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_block_commitment` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_block_commitment_offset +pub proc tx_get_block_commitment_offset push.TX_GET_BLOCK_COMMITMENT_OFFSET end @@ -664,7 +664,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_block_number` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_block_number_offset +pub proc tx_get_block_number_offset push.TX_GET_BLOCK_NUMBER_OFFSET end @@ -676,7 +676,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_block_timestamp` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_block_timestamp_offset +pub proc tx_get_block_timestamp_offset push.TX_GET_BLOCK_TIMESTAMP_OFFSET end @@ -688,7 +688,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_start_foreign_context` kernel procedure required to get #! the address where this procedure is stored. -export.tx_start_foreign_context_offset +pub proc tx_start_foreign_context_offset push.TX_START_FOREIGN_CONTEXT_OFFSET end @@ -700,7 +700,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_end_foreign_context` kernel procedure required to get the #! address where this procedure is stored. -export.tx_end_foreign_context_offset +pub proc tx_end_foreign_context_offset push.TX_END_FOREIGN_CONTEXT_OFFSET end @@ -712,7 +712,7 @@ end #! Where: #! - proc_offset is the offset of the `tx_update_expiration_block_delta` kernel procedure required #! to get the address where this procedure is stored. -export.tx_update_expiration_block_delta_offset +pub proc tx_update_expiration_block_delta_offset push.TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET end @@ -724,6 +724,6 @@ end #! Where: #! - proc_offset is the offset of the `tx_get_expiration_delta` kernel procedure required to get the #! address where this procedure is stored. -export.tx_get_expiration_delta_offset +pub proc tx_get_expiration_delta_offset push.TX_GET_EXPIRATION_DELTA_OFFSET end diff --git a/crates/miden-lib/asm/miden/native_account.masm b/crates/miden-protocol/asm/protocol/native_account.masm similarity index 97% rename from crates/miden-lib/asm/miden/native_account.masm rename to crates/miden-protocol/asm/protocol/native_account.masm index 6a15423752..1ca40bdad0 100644 --- a/crates/miden-lib/asm/miden/native_account.masm +++ b/crates/miden-protocol/asm/protocol/native_account.masm @@ -1,4 +1,4 @@ -use.miden::kernel_proc_offsets +use miden::protocol::kernel_proc_offsets # NATIVE ACCOUNT PROCEDURES # ================================================================================================= @@ -16,7 +16,7 @@ use.miden::kernel_proc_offsets #! transaction. #! #! Invocation: exec -export.get_id +pub proc get_id # pad the stack padw padw padw push.0.0 # => [pad(14)] @@ -52,7 +52,7 @@ end #! - the nonce has already been incremented. #! #! Invocation: exec -export.incr_nonce +pub proc incr_nonce # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -93,7 +93,7 @@ end #! #! Panics if: #! - the vault or storage delta is not empty but the nonce increment is zero. -export.compute_delta_commitment +pub proc compute_delta_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -129,7 +129,7 @@ end #! - the native account is a faucet and the provided slot ID points to the reserved faucet storage slot. #! #! Invocation: exec -export.set_item +pub proc set_item exec.kernel_proc_offsets::account_set_item_offset # => [offset, slot_id_prefix, slot_id_suffix, VALUE] @@ -166,7 +166,7 @@ end #! - the invocation of this procedure does not originate from the native account. #! #! Invocation: exec -export.set_map_item +pub proc set_map_item exec.kernel_proc_offsets::account_set_map_item_offset # => [offset, slot_id_prefix, slot_id_suffix, KEY, VALUE] @@ -208,7 +208,7 @@ end #! - the vault already contains the same non-fungible asset. #! #! Invocation: exec -export.add_asset +pub proc add_asset exec.kernel_proc_offsets::account_add_asset_offset # => [offset, ASSET] @@ -238,7 +238,7 @@ end #! - the non-fungible asset is not found in the vault. #! #! Invocation: exec -export.remove_asset +pub proc remove_asset exec.kernel_proc_offsets::account_remove_asset_offset # => [offset, ASSET] @@ -271,7 +271,7 @@ end #! - was_called is 1 if the procedure was called, 0 otherwise. #! #! Invocation: exec -export.was_procedure_called +pub proc was_procedure_called exec.kernel_proc_offsets::account_was_procedure_called_offset # => [offset, PROC_ROOT] diff --git a/crates/miden-lib/asm/miden/note.masm b/crates/miden-protocol/asm/protocol/note.masm similarity index 91% rename from crates/miden-lib/asm/miden/note.masm rename to crates/miden-protocol/asm/protocol/note.masm index a8097a70f2..ea7ac724f4 100644 --- a/crates/miden-lib/asm/miden/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -1,12 +1,12 @@ -use.miden::account_id -use.std::crypto::hashes::rpo -use.std::math::u64 -use.std::mem +use miden::protocol::account_id +use miden::core::crypto::hashes::rpo256 +use miden::core::math::u64 +use miden::core::mem # ERRORS # ================================================================================================= -const.ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" +const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceeded the maximum limit of 1024" # NOTE UTILITY PROCEDURES # ================================================================================================= @@ -32,19 +32,18 @@ const.ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceede #! - num_inputs is greater than 1024. #! #! Invocation: exec -export.compute_inputs_commitment +pub proc compute_inputs_commitment # check that number of inputs is less than or equal to 1024 dup.1 push.1024 u32assert2.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT u32lte assert.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT # => [inputs_ptr, num_inputs] - # push 1 as the pad_inputs flag: we should pad the stack while computing the note inputs - # commitment - push.1 movdn.2 - # => [inputs_ptr, num_inputs, pad_inputs_flag] - - exec.rpo::prepare_hasher_state - exec.rpo::hash_memory_with_state + # compute the inputs commitment + # + # TODO: refactor the hash computation process as part of the + # https://github.com/0xMiden/miden-base/issues/2036 and + # https://github.com/0xMiden/miden-base/issues/2129 + exec.rpo256::pad_and_hash_elements # => [INPUTS_COMMITMENT] end @@ -54,7 +53,7 @@ end #! Output: [max_inputs_per_note] #! #! - max_inputs_per_note is the max inputs per note. -export.::miden::util::note::get_max_inputs_per_note +pub use ::miden::protocol::util::note::get_max_inputs_per_note #! Writes the assets data stored in the advice map to the memory specified by the provided #! destination pointer. @@ -66,7 +65,7 @@ export.::miden::util::note::get_max_inputs_per_note #! } #! Outputs: #! Operand stack: [num_assets, dest_ptr] -export.write_assets_to_memory +pub proc write_assets_to_memory # load the asset data from the advice map to the advice stack adv.push_mapval # OS => [ASSETS_COMMITMENT, num_assets, dest_ptr] @@ -123,7 +122,8 @@ end #! - num_inputs is greater than 1024. #! #! Invocation: exec -export.build_recipient.1 +@locals(1) +pub proc build_recipient dup.1 dup.1 # => [inputs_ptr, num_inputs, inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT] @@ -191,7 +191,7 @@ end #! - RECIPIENT is the recipient of the note. #! #! Invocation: exec -export.build_recipient_hash +pub proc build_recipient_hash padw hmerge # => [SERIAL_NUM_HASH, SCRIPT_ROOT, INPUT_COMMITMENT] @@ -211,7 +211,7 @@ end #! - METADATA is the metadata of some note. #! - sender_{prefix,suffix} are the prefix and suffix felts of the sender ID of the note which #! metadata was provided. -export.extract_sender_from_metadata +pub proc extract_sender_from_metadata # => [aux, merged_tag_hint_payload, merged_sender_id_type_hint_tag, sender_id_prefix] # drop aux felt and the felt containing tag, execution hint and payload @@ -241,7 +241,7 @@ end #! - network_account_tag is the computed network note tag. #! #! Invocation: exec -export.build_note_tag_for_network_account +pub proc build_note_tag_for_network_account swap drop # => [account_id_prefix] diff --git a/crates/miden-lib/asm/miden/output_note.masm b/crates/miden-protocol/asm/protocol/output_note.masm similarity index 96% rename from crates/miden-lib/asm/miden/output_note.masm rename to crates/miden-protocol/asm/protocol/output_note.masm index 39c900edfc..3257be6172 100644 --- a/crates/miden-lib/asm/miden/output_note.masm +++ b/crates/miden-protocol/asm/protocol/output_note.masm @@ -1,5 +1,5 @@ -use.miden::kernel_proc_offsets -use.miden::note +use miden::protocol::kernel_proc_offsets +use miden::protocol::note # PROCEDURES # ================================================================================================= @@ -18,7 +18,7 @@ use.miden::note #! - note_idx is the index of the created note. #! #! Invocation: exec -export.create +pub proc create # pad the stack before the syscall to prevent accidental modification of the deeper stack # elements padw padw swapdw movup.8 drop @@ -51,7 +51,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: exec -export.get_assets_info +pub proc get_assets_info # start padding the stack push.0.0 movup.2 # => [note_index, 0, 0] @@ -97,7 +97,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: exec -export.get_assets +pub proc get_assets # get the assets commitment and assets number dup.1 exec.get_assets_info # => [ASSETS_COMMITMENT, num_assets, dest_ptr, note_index] @@ -117,7 +117,7 @@ end #! - ASSET can be a fungible or non-fungible asset. #! #! Invocation: exec -export.add_asset +pub proc add_asset movup.4 exec.kernel_proc_offsets::output_note_add_asset_offset # => [offset, note_idx, ASSET] @@ -147,7 +147,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: exec -export.get_recipient +pub proc get_recipient # start padding the stack push.0.0 movup.2 # => [note_index, 0, 0] @@ -180,7 +180,7 @@ end #! - the note index is greater or equal to the total number of output notes. #! #! Invocation: exec -export.get_metadata +pub proc get_metadata # start padding the stack push.0.0 movup.2 # => [note_index, 0, 0] diff --git a/crates/miden-lib/asm/miden/tx.masm b/crates/miden-protocol/asm/protocol/tx.masm similarity index 96% rename from crates/miden-lib/asm/miden/tx.masm rename to crates/miden-protocol/asm/protocol/tx.masm index a6890e1fe4..b55b354ebb 100644 --- a/crates/miden-lib/asm/miden/tx.masm +++ b/crates/miden-protocol/asm/protocol/tx.masm @@ -1,4 +1,4 @@ -use.miden::kernel_proc_offsets +use miden::protocol::kernel_proc_offsets #! Returns the block number of the transaction reference block. #! @@ -9,7 +9,7 @@ use.miden::kernel_proc_offsets #! - num is the transaction reference block number. #! #! Invocation: exec -export.get_block_number +pub proc get_block_number # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -34,7 +34,7 @@ end #! - BLOCK_COMMITMENT is the commitment to the reference block of the transaction. #! #! Invocation: exec -export.get_block_commitment +pub proc get_block_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -77,7 +77,7 @@ end #! Where: #! - timestamp is the timestamp of the reference block for this transaction. The underlying value is #! of type u32, so u32 operations can be safely used on it. -export.get_block_timestamp +pub proc get_block_timestamp # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -104,7 +104,7 @@ end #! - INPUT_NOTES_COMMITMENT is the input notes commitment hash. #! #! Invocation: exec -export.get_input_notes_commitment +pub proc get_input_notes_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -130,7 +130,7 @@ end #! - OUTPUT_NOTES_COMMITMENT is the output notes commitment. #! #! Invocation: exec -export.get_output_notes_commitment +pub proc get_output_notes_commitment # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -155,7 +155,7 @@ end #! - num_input_notes is the total number of input notes consumed by this transaction. #! #! Invocation: exec -export.get_num_input_notes +pub proc get_num_input_notes # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -183,7 +183,7 @@ end #! - num_output_notes is the number of output notes created in this transaction so far. #! #! Invocation: exec -export.get_num_output_notes +pub proc get_num_output_notes # pad the stack padw padw padw push.0.0.0 # => [pad(15)] @@ -215,7 +215,8 @@ end #! moment of the foreign procedure execution (n = 16 - mem_addr_size - foreign_inputs_len). #! #! Invocation: exec -export.execute_foreign_procedure.4 +@locals(4) +pub proc execute_foreign_procedure # get the start_foreign_context procedure offset push.0 movup.2 movup.2 exec.kernel_proc_offsets::tx_start_foreign_context_offset # => [offset, foreign_account_id_prefix, foreign_account_id_suffix, 0, FOREIGN_PROC_ROOT, , pad(n)] @@ -268,7 +269,7 @@ end #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). #! #! Annotation hint: is not used anywhere -export.update_expiration_block_delta +pub proc update_expiration_block_delta exec.kernel_proc_offsets::tx_update_expiration_block_delta_offset # => [offset, expiration_delta, ...] @@ -291,7 +292,7 @@ end #! - block_height_delta is the stored expiration time delta (1 to 0xFFFF). #! #! Annotation hint: is not used anywhere -export.get_expiration_block_delta +pub proc get_expiration_block_delta # pad the stack padw padw padw push.0.0.0 # => [pad(15)] diff --git a/crates/miden-lib/asm/shared_modules/account_id.masm b/crates/miden-protocol/asm/shared_modules/account_id.masm similarity index 89% rename from crates/miden-lib/asm/shared_modules/account_id.masm rename to crates/miden-protocol/asm/shared_modules/account_id.masm index 4158a6e04e..4dcf7d4e11 100644 --- a/crates/miden-lib/asm/shared_modules/account_id.masm +++ b/crates/miden-protocol/asm/shared_modules/account_id.masm @@ -1,50 +1,50 @@ # ERRORS # ================================================================================================= -const.ERR_ACCOUNT_ID_UNKNOWN_VERSION="unknown version in account ID" +const ERR_ACCOUNT_ID_UNKNOWN_VERSION="unknown version in account ID" -const.ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO="most significant bit of the account ID suffix must be zero" +const ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO="most significant bit of the account ID suffix must be zero" -const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in account ID" +const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in account ID" -const.ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO="least significant byte of the account ID suffix must be zero" +const ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO="least significant byte of the account ID suffix must be zero" -const.ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT="the account ID must have storage mode public if the network flag is set" +const ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT="the account ID must have storage mode public if the network flag is set" # CONSTANTS # ================================================================================================= # Bit pattern for a faucet account, after the account type mask has been applied. -const.FAUCET_ACCOUNT=0x20 # 0b10_0000 +const FAUCET_ACCOUNT=0x20 # 0b10_0000 # Bit pattern for an account w/ updatable code, after the account type mask has been applied. -const.REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 +const REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 # Bit pattern for an account w/ immutable code, after the account type mask has been applied. -const.REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 +const REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 # Bit pattern for a fungible faucet w/ immutable code, after the account type mask has been applied. -const.FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 +const FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 # Bit pattern for a non-fungible faucet w/ immutable code, after the account type mask has been # applied. -const.NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 +const NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 # Given the least significant 32 bits of an account id's prefix, this mask defines the bits used # to determine the account type. -const.ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 +const ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 # Given the least significant 32 bits of an account id's prefix, this mask defines the bits used # to determine the account version. -const.ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 +const ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 # Given the least significant 32 bits of an account ID's prefix, this mask defines the bits used # to determine the account storage mode. -const.ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 +const ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 # Given the least significant 32 bits of an account ID's first felt with the storage mode mask # applied, this value defines the non-existent, invalid storage mode. -const.ACCOUNT_ID_STORAGE_MODE_INVALID_U32=0xc0 # 0b1100_0000 +const ACCOUNT_ID_STORAGE_MODE_INVALID_U32=0xc0 # 0b1100_0000 # PROCEDURES # ================================================================================================= @@ -57,7 +57,7 @@ const.ACCOUNT_ID_STORAGE_MODE_INVALID_U32=0xc0 # 0b1100_0000 #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_fungible_faucet is a boolean indicating whether the account is a fungible faucet. -export.is_fungible_faucet +pub proc is_fungible_faucet exec.id_type eq.FUNGIBLE_FAUCET_ACCOUNT # => [is_fungible_faucet] end @@ -70,7 +70,7 @@ end #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_non_fungible_faucet is a boolean indicating whether the account is a non-fungible faucet. -export.is_non_fungible_faucet +pub proc is_non_fungible_faucet exec.id_type eq.NON_FUNGIBLE_FAUCET_ACCOUNT # => [is_non_fungible_faucet] end @@ -85,7 +85,7 @@ end #! - other_acct_id_{prefix,suffix} are the prefix and suffix felts of the other account ID to #! compare against. #! - is_id_equal is a boolean indicating whether the account IDs are equal. -export.is_equal +pub proc is_equal movup.2 eq # => [is_prefix_equal, acct_id_suffix, other_acct_id_suffix] movdn.2 eq @@ -102,7 +102,7 @@ end #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_faucet is a boolean indicating whether the account is a faucet. -export.is_faucet +pub proc is_faucet u32split drop u32and.FAUCET_ACCOUNT neq.0 # => [is_faucet] end @@ -116,7 +116,7 @@ end #! - acct_id_prefix is the prefix of the account ID. #! - is_updatable_account is a boolean indicating whether the account is a regular updatable #! account. -export.is_updatable_account +pub proc is_updatable_account exec.id_type eq.REGULAR_ACCOUNT_UPDATABLE_CODE # => [is_updatable_account] end @@ -130,7 +130,7 @@ end #! - acct_id_prefix is the prefix of the account ID. #! - is_immutable_account is a boolean indicating whether the account is a regular immutable #! account. -export.is_immutable_account +pub proc is_immutable_account exec.id_type eq.REGULAR_ACCOUNT_IMMUTABLE_CODE # => [is_immutable_account] end @@ -149,7 +149,7 @@ end #! - account_id_prefix does not contain either the public, network or private storage mode. #! - account_id_suffix does not have its most significant bit set to zero. #! - account_id_suffix does not have its lower 8 bits set to zero. -export.validate +pub proc validate # Validate version in prefix. For now only version 0 is supported. # --------------------------------------------------------------------------------------------- @@ -201,7 +201,7 @@ end #! - seed_digest_suffix is the suffix of the digest that should be shaped into the suffix #! of an account ID. #! - account_id_suffix is the suffix of an account ID. -export.shape_suffix +pub proc shape_suffix u32split swap # => [seed_digest_suffix_lo, seed_digest_suffix_hi] @@ -225,7 +225,7 @@ end #! Where: #! - account_id_prefix is the prefix of an account ID. #! - id_version is the version number of the ID. -proc.id_version +proc id_version # extract the lower 32 bits u32split drop # => [account_id_prefix_lo] @@ -249,7 +249,7 @@ end #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - acct_type is the account type. -proc.id_type +proc id_type u32split drop u32and.ACCOUNT_ID_TYPE_MASK_U32 # => [acct_type] end diff --git a/crates/miden-lib/asm/shared_utils/util/asset.masm b/crates/miden-protocol/asm/shared_utils/util/asset.masm similarity index 88% rename from crates/miden-lib/asm/shared_utils/util/asset.masm rename to crates/miden-protocol/asm/shared_utils/util/asset.masm index 9d16b342f2..225ed0366d 100644 --- a/crates/miden-lib/asm/shared_utils/util/asset.masm +++ b/crates/miden-protocol/asm/shared_utils/util/asset.masm @@ -4,7 +4,7 @@ # Specifies the maximum amount a fungible asset can represent. # # This is 2^63 - 2^31. See account_delta.masm for more details. -const.FUNGIBLE_ASSET_MAX_AMOUNT=0x7fffffff80000000 +const FUNGIBLE_ASSET_MAX_AMOUNT=0x7fffffff80000000 # PROCEDURES # ================================================================================================= @@ -16,7 +16,7 @@ const.FUNGIBLE_ASSET_MAX_AMOUNT=0x7fffffff80000000 #! #! Where: #! - fungible_asset_max_amount is the maximum amount of a fungible asset. -export.get_fungible_asset_max_amount +pub proc get_fungible_asset_max_amount push.FUNGIBLE_ASSET_MAX_AMOUNT # => [fungible_asset_max_amount] end @@ -31,7 +31,7 @@ end #! Where: #! - ASSET is the fungible asset from which to extract the balance. #! - balance is the amount of the fungible asset. -export.get_balance_from_fungible_asset +pub proc get_balance_from_fungible_asset drop drop drop # => [balance] end diff --git a/crates/miden-lib/asm/shared_utils/util/note.masm b/crates/miden-protocol/asm/shared_utils/util/note.masm similarity index 88% rename from crates/miden-lib/asm/shared_utils/util/note.masm rename to crates/miden-protocol/asm/shared_utils/util/note.masm index 1a425ce13b..00a4ea88cf 100644 --- a/crates/miden-lib/asm/shared_utils/util/note.masm +++ b/crates/miden-protocol/asm/shared_utils/util/note.masm @@ -2,7 +2,7 @@ # ================================================================================================= # The maximum number of input values associated with a single note. -const.MAX_INPUTS_PER_NOTE=1024 +const MAX_INPUTS_PER_NOTE=1024 # PROCEDURES # ================================================================================================= @@ -14,6 +14,6 @@ const.MAX_INPUTS_PER_NOTE=1024 #! #! Where: #! - max_inputs_per_note is the max inputs per note. -export.get_max_inputs_per_note +pub proc get_max_inputs_per_note push.MAX_INPUTS_PER_NOTE end diff --git a/crates/miden-objects/benches/account_seed.rs b/crates/miden-protocol/benches/account_seed.rs similarity index 90% rename from crates/miden-objects/benches/account_seed.rs rename to crates/miden-protocol/benches/account_seed.rs index 8e37e84228..77f6c107a2 100644 --- a/crates/miden-objects/benches/account_seed.rs +++ b/crates/miden-protocol/benches/account_seed.rs @@ -1,8 +1,8 @@ use std::time::Duration; use criterion::{Criterion, criterion_group, criterion_main}; -use miden_objects::Word; -use miden_objects::account::{AccountId, AccountIdVersion, AccountStorageMode, AccountType}; +use miden_protocol::Word; +use miden_protocol::account::{AccountId, AccountIdVersion, AccountStorageMode, AccountType}; use rand::{Rng, SeedableRng}; /// Running this benchmark with --no-default-features will use the single-threaded account seed @@ -13,7 +13,7 @@ use rand::{Rng, SeedableRng}; /// To produce a flamegraph, run with the `--profile-time` argument. /// /// ```sh -/// cargo bench -p miden-objects --no-default-features -- --profile-time 10 +/// cargo bench -p miden-protocol --no-default-features -- --profile-time 10 /// ``` /// /// The flamegraph will be saved as `target/criterion/grind-seed/Grind regular public account diff --git a/crates/miden-protocol/build.rs b/crates/miden-protocol/build.rs new file mode 100644 index 0000000000..27d1b96d4b --- /dev/null +++ b/crates/miden-protocol/build.rs @@ -0,0 +1,817 @@ +use std::collections::{BTreeMap, BTreeSet}; +use std::env; +use std::path::Path; +use std::sync::Arc; + +use fs_err as fs; +use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr, miette}; +use miden_assembly::{Assembler, DefaultSourceManager, KernelLibrary, Library}; +use regex::Regex; +use walkdir::WalkDir; + +// CONSTANTS +// ================================================================================================ + +/// Defines whether the build script should generate files in `/src`. +/// The docs.rs build pipeline has a read-only filesystem, so we have to avoid writing to `src`, +/// otherwise the docs will fail to build there. Note that writing to `OUT_DIR` is fine. +const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN_SRC").is_some(); + +const ASSETS_DIR: &str = "assets"; +const ASM_DIR: &str = "asm"; +const ASM_PROTOCOL_DIR: &str = "protocol"; + +const SHARED_UTILS_DIR: &str = "shared_utils"; +const SHARED_MODULES_DIR: &str = "shared_modules"; +const ASM_TX_KERNEL_DIR: &str = "kernels/transaction"; +const KERNEL_PROCEDURES_RS_FILE: &str = "src/transaction/kernel/procedures.rs"; + +const PROTOCOL_LIB_NAMESPACE: &str = "miden::protocol"; + +const TX_KERNEL_ERRORS_FILE: &str = "src/errors/tx_kernel.rs"; +const PROTOCOL_LIB_ERRORS_FILE: &str = "src/errors/protocol.rs"; + +const TX_KERNEL_ERRORS_ARRAY_NAME: &str = "TX_KERNEL_ERRORS"; +const PROTOCOL_LIB_ERRORS_ARRAY_NAME: &str = "PROTOCOL_LIB_ERRORS"; + +const TX_KERNEL_ERROR_CATEGORIES: [&str; 14] = [ + "KERNEL", + "PROLOGUE", + "EPILOGUE", + "TX", + "NOTE", + "ACCOUNT", + "FOREIGN_ACCOUNT", + "FAUCET", + "FUNGIBLE_ASSET", + "NON_FUNGIBLE_ASSET", + "VAULT", + "LINK_MAP", + "INPUT_NOTE", + "OUTPUT_NOTE", +]; + +// PRE-PROCESSING +// ================================================================================================ + +/// Read and parse the contents from `./asm`. +/// - Compiles the contents of asm/protocol directory into a Protocol library file (.masl) under +/// miden::protocol namespace. +/// - Compiles the contents of asm/kernels into the transaction kernel library. +fn main() -> Result<()> { + // re-build when the MASM code changes + println!("cargo::rerun-if-changed={ASM_DIR}/"); + println!("cargo::rerun-if-env-changed=BUILD_GENERATED_FILES_IN_SRC"); + + // Copies the MASM code to the build directory + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let build_dir = env::var("OUT_DIR").unwrap(); + let src = Path::new(&crate_dir).join(ASM_DIR); + let dst = Path::new(&build_dir).to_path_buf(); + shared::copy_directory(src, &dst, ASM_DIR)?; + + // set source directory to {OUT_DIR}/asm + let source_dir = dst.join(ASM_DIR); + + // copy the shared modules to the kernel and protocol library folders + copy_shared_modules(&source_dir)?; + + // set target directory to {OUT_DIR}/assets + let target_dir = Path::new(&build_dir).join(ASSETS_DIR); + + // compile transaction kernel + let mut assembler = + compile_tx_kernel(&source_dir.join(ASM_TX_KERNEL_DIR), &target_dir.join("kernels"))?; + + // compile protocol library + let protocol_lib = compile_protocol_lib(&source_dir, &target_dir, assembler.clone())?; + assembler.link_dynamic_library(protocol_lib)?; + + generate_error_constants(&source_dir)?; + + generate_event_constants(&source_dir, &target_dir)?; + + Ok(()) +} + +// COMPILE TRANSACTION KERNEL +// ================================================================================================ + +/// Reads the transaction kernel MASM source from the `source_dir`, compiles it, saves the results +/// to the `target_dir`, and returns an [Assembler] instantiated with the compiled kernel. +/// +/// Additionally it compiles the transaction script executor program, see the +/// [compile_tx_script_main] procedure for details. +/// +/// `source_dir` is expected to have the following structure: +/// +/// - {source_dir}/api.masm -> defines exported procedures from the transaction kernel. +/// - {source_dir}/main.masm -> defines the executable program of the transaction kernel. +/// - {source_dir}/tx_script_main -> defines the executable program of the arbitrary transaction +/// script. +/// - {source_dir}/lib -> contains common modules used by both api.masm and main.masm. +/// +/// The compiled files are written as follows: +/// +/// - {target_dir}/tx_kernel.masl -> contains kernel library compiled from api.masm. +/// - {target_dir}/tx_kernel.masb -> contains the executable compiled from main.masm. +/// - {target_dir}/tx_script_main.masb -> contains the executable compiled from +/// tx_script_main.masm. +/// - src/transaction/procedures/kernel_v0.rs -> contains the kernel procedures table. +fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result { + let shared_utils_path = std::path::Path::new(ASM_DIR).join(SHARED_UTILS_DIR); + let kernel_path = miden_assembly::Path::kernel_path(); + + let mut assembler = build_assembler(None)?; + // add the shared util modules to the kernel lib under the ::$kernel::util namespace + assembler.compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; + + // assemble the kernel library and write it to the "tx_kernel.masl" file + let kernel_lib = assembler + .assemble_kernel_from_dir(source_dir.join("api.masm"), Some(source_dir.join("lib")))?; + + // generate kernel `procedures.rs` file + generate_kernel_proc_hash_file(kernel_lib.clone())?; + + let output_file = target_dir.join("tx_kernel").with_extension(Library::LIBRARY_EXTENSION); + kernel_lib.write_to_file(output_file).into_diagnostic()?; + + let assembler = build_assembler(Some(kernel_lib))?; + + // assemble the kernel program and write it to the "tx_kernel.masb" file + let mut main_assembler = assembler.clone(); + // add the shared util modules to the kernel lib under the ::$kernel::util namespace + main_assembler.compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; + main_assembler.compile_and_statically_link_from_dir(source_dir.join("lib"), kernel_path)?; + + let main_file_path = source_dir.join("main.masm"); + let kernel_main = main_assembler.clone().assemble_program(main_file_path)?; + + let masb_file_path = target_dir.join("tx_kernel.masb"); + kernel_main.write_to_file(masb_file_path).into_diagnostic()?; + + // compile the transaction script main program + compile_tx_script_main(source_dir, target_dir, main_assembler)?; + + #[cfg(any(feature = "testing", test))] + { + let mut kernel_lib_assembler = assembler.clone(); + // Build kernel as a library and save it to file. + // This is needed in test assemblers to access individual procedures which would otherwise + // be hidden when using KernelLibrary (api.masm) + + // add the shared util modules to the kernel lib under the ::$kernel::util namespace + kernel_lib_assembler + .compile_and_statically_link_from_dir(&shared_utils_path, kernel_path)?; + + let test_lib = kernel_lib_assembler + .assemble_library_from_dir(source_dir.join("lib"), kernel_path) + .unwrap(); + + let masb_file_path = + target_dir.join("kernel_library").with_extension(Library::LIBRARY_EXTENSION); + test_lib.write_to_file(masb_file_path).into_diagnostic()?; + } + + Ok(assembler) +} + +/// Reads the transaction script executor MASM source from the `source_dir/tx_script_main.masm`, +/// compiles it and saves the results to the `target_dir` as a `tx_script_main.masb` binary file. +fn compile_tx_script_main( + source_dir: &Path, + target_dir: &Path, + main_assembler: Assembler, +) -> Result<()> { + // assemble the transaction script executor program and write it to the "tx_script_main.masb" + // file. + let tx_script_main_file_path = source_dir.join("tx_script_main.masm"); + let tx_script_main = main_assembler.assemble_program(tx_script_main_file_path)?; + + let masb_file_path = target_dir.join("tx_script_main.masb"); + tx_script_main.write_to_file(masb_file_path).into_diagnostic() +} + +/// Generates kernel `procedures.rs` file based on the kernel library +fn generate_kernel_proc_hash_file(kernel: KernelLibrary) -> Result<()> { + // Because the kernel Rust file will be stored under ./src, this should be a no-op if we can't + // write there + if !BUILD_GENERATED_FILES_IN_SRC { + return Ok(()); + } + + let (_, module_info, _) = kernel.into_parts(); + + let to_exclude = BTreeSet::from_iter(["exec_kernel_proc"]); + let offsets_filename = + Path::new(ASM_DIR).join(ASM_PROTOCOL_DIR).join("kernel_proc_offsets.masm"); + let offsets = parse_proc_offsets(&offsets_filename)?; + + let generated_procs: BTreeMap = module_info + .procedures() + .filter(|(_, proc_info)| !to_exclude.contains::(proc_info.name.as_ref())) + .map(|(_, proc_info)| { + let name = proc_info.name.to_string(); + + let Some(&offset) = offsets.get(&name) else { + panic!("Offset constant for function `{name}` not found in `{offsets_filename:?}`"); + }; + + (offset, format!(" // {name}\n word!(\"{}\"),", proc_info.digest)) + }) + .collect(); + + let proc_count = generated_procs.len(); + let generated_procs: String = generated_procs.into_iter().enumerate().map(|(index, (offset, txt))| { + if index != offset { + panic!("Offset constants in the file `{offsets_filename:?}` are not contiguous (missing offset: {index})"); + } + + txt + }).collect::>().join("\n"); + + fs::write( + KERNEL_PROCEDURES_RS_FILE, + format!( + r#"// This file is generated by build.rs, do not modify + +use crate::{{Word, word}}; + +// KERNEL PROCEDURES +// ================================================================================================ + +/// Hashes of all dynamically executed kernel procedures. +pub const KERNEL_PROCEDURES: [Word; {proc_count}] = [ +{generated_procs} +]; +"#, + ), + ) + .into_diagnostic() +} + +fn parse_proc_offsets(filename: impl AsRef) -> Result> { + let regex: Regex = Regex::new(r"^const\s*(?P\w+)_OFFSET\s*=\s*(?P\d+)").unwrap(); + let mut result = BTreeMap::new(); + for line in fs::read_to_string(filename).into_diagnostic()?.lines() { + if let Some(captures) = regex.captures(line) { + result.insert( + captures["name"].to_string().to_lowercase(), + captures["offset"].parse().into_diagnostic()?, + ); + } + } + + Ok(result) +} + +// COMPILE PROTOCOL LIB +// ================================================================================================ + +/// Reads the MASM files from "{source_dir}/protocol" directory, compiles them into a Miden assembly +/// library, saves the library into "{target_dir}/protocol.masl", and returns the compiled library. +fn compile_protocol_lib( + source_dir: &Path, + target_dir: &Path, + mut assembler: Assembler, +) -> Result { + let source_dir = source_dir.join(ASM_PROTOCOL_DIR); + let shared_path = Path::new(ASM_DIR).join(SHARED_UTILS_DIR); + + // add the shared modules to the protocol lib under the miden::protocol::util namespace + // note that this module is not publicly exported, it is only available for linking the library + // itself + assembler.compile_and_statically_link_from_dir(&shared_path, PROTOCOL_LIB_NAMESPACE)?; + + let protocol_lib = assembler.assemble_library_from_dir(source_dir, PROTOCOL_LIB_NAMESPACE)?; + + let output_file = target_dir.join("protocol").with_extension(Library::LIBRARY_EXTENSION); + protocol_lib.write_to_file(output_file).into_diagnostic()?; + + Ok(protocol_lib) +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Returns a new [Assembler] loaded with miden-core-lib and the specified kernel, if provided. +fn build_assembler(kernel: Option) -> Result { + kernel + .map(|kernel| Assembler::with_kernel(Arc::new(DefaultSourceManager::default()), kernel)) + .unwrap_or_default() + .with_dynamic_library(miden_core_lib::CoreLibrary::default()) +} + +/// Copies the content of the build `shared_modules` folder to the `lib` and `protocol` build +/// folders. This is required to include the shared modules as APIs of the `kernel` and `protocol` +/// libraries. +/// +/// This is done to make it possible to import the modules in the `shared_modules` folder directly, +/// i.e. "use $kernel::account_id". +fn copy_shared_modules>(source_dir: T) -> Result<()> { + // source is expected to be an `OUT_DIR/asm` folder + let shared_modules_dir = source_dir.as_ref().join(SHARED_MODULES_DIR); + + for module_path in shared::get_masm_files(shared_modules_dir).unwrap() { + let module_name = module_path.file_name().unwrap(); + + // copy to kernel lib + let kernel_lib_folder = source_dir.as_ref().join(ASM_TX_KERNEL_DIR).join("lib"); + fs::copy(&module_path, kernel_lib_folder.join(module_name)).into_diagnostic()?; + + // copy to protocol lib + let protocol_lib_folder = source_dir.as_ref().join(ASM_PROTOCOL_DIR); + fs::copy(&module_path, protocol_lib_folder.join(module_name)).into_diagnostic()?; + } + + Ok(()) +} + +// ERROR CONSTANTS FILE GENERATION +// ================================================================================================ + +/// Reads all MASM files from the `asm_source_dir` and extracts its error constants and their +/// associated error message and generates a Rust file for each category of errors. +/// For example: +/// +/// ```text +/// const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" +/// ``` +/// +/// would generate a Rust file for transaction kernel errors (since the error belongs to that +/// category, identified by the category extracted from `ERR_`) with - roughly - the +/// following content: +/// +/// ```rust +/// pub const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY: MasmError = +/// MasmError::from_static_str("new account must have an empty vault"); +/// ``` +/// +/// and add the constant to the error constants array. +/// +/// The function ensures that a constant is not defined twice, except if their error message is +/// the same. This can happen across multiple files. +/// +/// Because the error files will be written to ./src/errors, this should be a no-op if ./src is +/// read-only. To enable writing to ./src, set the `BUILD_GENERATED_FILES_IN_SRC` environment +/// variable. +fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { + if !BUILD_GENERATED_FILES_IN_SRC { + return Ok(()); + } + + // Transaction kernel errors + // ------------------------------------------ + + let tx_kernel_dir = asm_source_dir.join(ASM_TX_KERNEL_DIR); + let errors = shared::extract_all_masm_errors(&tx_kernel_dir) + .context("failed to extract all masm errors")?; + validate_tx_kernel_category(&errors)?; + + shared::generate_error_file( + shared::ErrorModule { + file_name: TX_KERNEL_ERRORS_FILE, + array_name: TX_KERNEL_ERRORS_ARRAY_NAME, + is_crate_local: true, + }, + errors, + )?; + + // Miden protocol library errors + // ------------------------------------------ + + let protocol_dir = asm_source_dir.join(ASM_PROTOCOL_DIR); + let errors = shared::extract_all_masm_errors(&protocol_dir) + .context("failed to extract all masm errors")?; + + shared::generate_error_file( + shared::ErrorModule { + file_name: PROTOCOL_LIB_ERRORS_FILE, + array_name: PROTOCOL_LIB_ERRORS_ARRAY_NAME, + is_crate_local: true, + }, + errors, + )?; + + Ok(()) +} + +/// Validates that all error names in the provided slice start with a known tx kernel error +/// category. +fn validate_tx_kernel_category(errors: &[shared::NamedError]) -> Result<()> { + for error in errors { + if !TX_KERNEL_ERROR_CATEGORIES + .iter() + .any(|known_category| error.name.starts_with(known_category)) + { + return Err(miette::miette!( + "error `{}` does not start with a known tx kernel error category", + error.name + )); + } + } + + Ok(()) +} + +// EVENT CONSTANTS FILE GENERATION +// ================================================================================================ + +/// Reads all MASM files from the `asm_source_dir` and extracts event definitions, +/// then generates the transaction_events.rs file with constants. +fn generate_event_constants(asm_source_dir: &Path, target_dir: &Path) -> Result<()> { + // Extract all event definitions from MASM files + let mut events = extract_all_event_definitions(asm_source_dir)?; + + // Add two additional events we want in `TransactionEventId` that do not appear in kernel or + // protocol lib modules. + events.insert("miden::auth::request".to_owned(), "AUTH_REQUEST".to_owned()); + events.insert("miden::auth::unauthorized".to_owned(), "AUTH_UNAUTHORIZED".to_owned()); + + // Generate the events file in OUT_DIR + let event_file_content = generate_event_file_content(&events).into_diagnostic()?; + let event_file_path = target_dir.join("transaction_events.rs"); + fs::write(event_file_path, event_file_content).into_diagnostic()?; + + Ok(()) +} + +/// Extract all `const X=event("x")` definitions from all MASM files +fn extract_all_event_definitions(asm_source_dir: &Path) -> Result> { + // collect mappings event path to const variable name, we want a unique mapping + // which we use to generate the constants and enum variant names + let mut events = BTreeMap::new(); + + // Walk all MASM files + for entry in WalkDir::new(asm_source_dir) { + let entry = entry.into_diagnostic()?; + if !shared::is_masm_file(entry.path()).into_diagnostic()? { + continue; + } + let file_contents = fs::read_to_string(entry.path()).into_diagnostic()?; + extract_event_definitions_from_file(&mut events, &file_contents, entry.path())?; + } + + Ok(events) +} + +/// Extract event definitions from a single MASM file in form of `const ${X} = event("${x::path}")`. +fn extract_event_definitions_from_file( + events: &mut BTreeMap, + file_contents: &str, + file_path: &Path, +) -> Result<()> { + let regex = Regex::new(r#"const\s*(\w+)\s*=\s*event\("([^"]+)"\)"#).unwrap(); + + for capture in regex.captures_iter(file_contents) { + let const_name = capture.get(1).expect("const name should be captured"); + let event_path = capture.get(2).expect("event path should be captured"); + + let event_path = event_path.as_str(); + let const_name = const_name.as_str(); + + let const_name_wo_suffix = + if let Some((const_name_wo_suffix, _)) = const_name.rsplit_once("_EVENT") { + const_name_wo_suffix.to_string() + } else { + const_name.to_owned() + }; + + if !event_path.starts_with("miden::") { + return Err(miette::miette!("unhandled `event_path={event_path}`")); + } + + // Check for duplicates with different definitions + if let Some(existing_const_name) = events.get(event_path) { + if existing_const_name != &const_name_wo_suffix { + println!( + "cargo:warning=Duplicate event definition found {event_path} with different definitions names: + '{existing_const_name}' vs '{const_name}' in {}", + file_path.display() + ); + } + } else { + events.insert(event_path.to_owned(), const_name_wo_suffix.to_owned()); + } + } + + Ok(()) +} + +/// Generate the content of the transaction_events.rs file +fn generate_event_file_content( + events: &BTreeMap, +) -> std::result::Result { + use std::fmt::Write; + + let mut output = String::new(); + + writeln!(&mut output, "// This file is generated by build.rs, do not modify")?; + writeln!(&mut output)?; + + // Generate constants + // + // Note: If we ever encounter two constants `const X`, that are both named `X` we will error + // when attempting to generate the rust code. Currently this is a side-effect, but we + // want to error out as early as possible: + // TODO: make the error out at build-time to be able to present better error hints + for (event_path, event_name) in events { + let value = miden_core::EventId::from_name(event_path).as_felt().as_int(); + debug_assert!(!event_name.is_empty()); + writeln!(&mut output, "const {}: u64 = {};", event_name, value)?; + } + + { + writeln!(&mut output)?; + + writeln!(&mut output)?; + + writeln!( + &mut output, + r###" +use alloc::collections::BTreeMap; + +pub(crate) static EVENT_NAME_LUT: ::miden_utils_sync::LazyLock> = + ::miden_utils_sync::LazyLock::new(|| {{ + BTreeMap::from_iter([ +"### + )?; + + for (event_path, const_name) in events { + writeln!(&mut output, " ({}, \"{}\"),", const_name, event_path)?; + } + + writeln!( + &mut output, + r###" ]) +}});"### + )?; + } + + Ok(output) +} + +/// This module should be kept in sync with the copy in miden-lib's build.rs. +mod shared { + use std::collections::BTreeMap; + use std::fmt::Write; + use std::io::{self}; + use std::path::{Path, PathBuf}; + + use fs_err as fs; + use miden_assembly::Report; + use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr}; + use regex::Regex; + use walkdir::WalkDir; + + /// Recursively copies `src` into `dst`. + /// + /// This function will overwrite the existing files if re-executed. + pub fn copy_directory, R: AsRef>( + src: T, + dst: R, + asm_dir: &str, + ) -> Result<()> { + let mut prefix = src.as_ref().canonicalize().unwrap(); + // keep all the files inside the `asm` folder + prefix.pop(); + + let target_dir = dst.as_ref().join(asm_dir); + if target_dir.exists() { + // Clear existing asm files that were copied earlier which may no longer exist. + fs::remove_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to remove ASM directory")?; + } + + // Recreate the directory structure. + fs::create_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to create ASM directory")?; + + let dst = dst.as_ref(); + let mut todo = vec![src.as_ref().to_path_buf()]; + + while let Some(goal) = todo.pop() { + for entry in fs::read_dir(goal).unwrap() { + let path = entry.unwrap().path(); + if path.is_dir() { + let src_dir = path.canonicalize().unwrap(); + let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap()); + if !dst_dir.exists() { + fs::create_dir_all(&dst_dir).unwrap(); + } + todo.push(src_dir); + } else { + let dst_file = dst.join(path.strip_prefix(&prefix).unwrap()); + fs::copy(&path, dst_file).unwrap(); + } + } + } + + Ok(()) + } + + /// Returns a vector with paths to all MASM files in the specified directory. + /// + /// All non-MASM files are skipped. + pub fn get_masm_files>(dir_path: P) -> Result> { + let mut files = Vec::new(); + + let path = dir_path.as_ref(); + if path.is_dir() { + let entries = fs::read_dir(path) + .into_diagnostic() + .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; + for entry in entries { + let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; + let file_path = file.path(); + if is_masm_file(&file_path).into_diagnostic()? { + files.push(file_path); + } + } + } else { + println!("cargo:warn=The specified path is not a directory."); + } + + Ok(files) + } + + /// Returns true if the provided path resolves to a file with `.masm` extension. + /// + /// # Errors + /// Returns an error if the path could not be converted to a UTF-8 string. + pub fn is_masm_file(path: &Path) -> io::Result { + if let Some(extension) = path.extension() { + let extension = extension + .to_str() + .ok_or_else(|| io::Error::other("invalid UTF-8 filename"))? + .to_lowercase(); + Ok(extension == "masm") + } else { + Ok(false) + } + } + + /// Extract all masm errors from the given path and returns a map by error category. + pub fn extract_all_masm_errors(asm_source_dir: &Path) -> Result> { + // We use a BTree here to order the errors by their categories which is the first part after + // the ERR_ prefix and to allow for the same error to be defined multiple times in + // different files (as long as the constant name and error messages match). + let mut errors = BTreeMap::new(); + + // Walk all files of the kernel source directory. + for entry in WalkDir::new(asm_source_dir) { + let entry = entry.into_diagnostic()?; + if !is_masm_file(entry.path()).into_diagnostic()? { + continue; + } + let file_contents = std::fs::read_to_string(entry.path()).into_diagnostic()?; + extract_masm_errors(&mut errors, &file_contents)?; + } + + let errors = errors + .into_iter() + .map(|(error_name, error)| NamedError { name: error_name, message: error.message }) + .collect(); + + Ok(errors) + } + + /// Extracts the errors from a single masm file and inserts them into the provided map. + pub fn extract_masm_errors( + errors: &mut BTreeMap, + file_contents: &str, + ) -> Result<()> { + let regex = Regex::new(r#"const\s*ERR_(?.*)\s*=\s*"(?.*)""#).unwrap(); + + for capture in regex.captures_iter(file_contents) { + let error_name = capture + .name("name") + .expect("error name should be captured") + .as_str() + .trim() + .to_owned(); + let error_message = capture + .name("message") + .expect("error code should be captured") + .as_str() + .trim() + .to_owned(); + + if let Some(ExtractedError { message: existing_error_message, .. }) = + errors.get(&error_name) + && existing_error_message != &error_message + { + return Err(Report::msg(format!( + "Transaction kernel error constant ERR_{error_name} is already defined elsewhere but its error message is different" + ))); + } + + // Enforce the "no trailing punctuation" rule from the Rust error guidelines on MASM + // errors. + if error_message.ends_with(".") { + return Err(Report::msg(format!( + "Error messages should not end with a period: `ERR_{error_name}: {error_message}`" + ))); + } + + errors.insert(error_name, ExtractedError { message: error_message }); + } + + Ok(()) + } + + pub fn is_new_error_category<'a>( + last_error: &mut Option<&'a str>, + current_error: &'a str, + ) -> bool { + let is_new = match last_error { + Some(last_err) => { + let last_category = + last_err.split("_").next().expect("there should be at least one entry"); + let new_category = + current_error.split("_").next().expect("there should be at least one entry"); + last_category != new_category + }, + None => false, + }; + + last_error.replace(current_error); + + is_new + } + + /// Generates the content of an error file for the given category and the set of errors and + /// writes it to the category's file. + pub fn generate_error_file(module: ErrorModule, errors: Vec) -> Result<()> { + let mut output = String::new(); + + if module.is_crate_local { + writeln!(output, "use crate::errors::MasmError;\n").unwrap(); + } else { + writeln!(output, "use miden_protocol::errors::MasmError;\n").unwrap(); + } + + writeln!( + output, + "// This file is generated by build.rs, do not modify manually. +// It is generated by extracting errors from the MASM files in the `./asm` directory. +// +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). +" + ) + .unwrap(); + + writeln!( + output, + "// {} +// ================================================================================================ +", + module.array_name.replace("_", " ") + ) + .unwrap(); + + let mut last_error = None; + for named_error in errors.iter() { + let NamedError { name, message } = named_error; + + // Group errors into blocks separate by newlines. + if is_new_error_category(&mut last_error, name) { + writeln!(output).into_diagnostic()?; + } + + writeln!(output, "/// Error Message: \"{message}\"").into_diagnostic()?; + writeln!( + output, + r#"pub const ERR_{name}: MasmError = MasmError::from_static_str("{message}");"# + ) + .into_diagnostic()?; + } + + std::fs::write(module.file_name, output).into_diagnostic()?; + + Ok(()) + } + + pub type ErrorName = String; + + #[derive(Debug, Clone)] + pub struct ExtractedError { + pub message: String, + } + + #[derive(Debug, Clone)] + pub struct NamedError { + pub name: ErrorName, + pub message: String, + } + + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + pub struct ErrorModule { + pub file_name: &'static str, + pub array_name: &'static str, + pub is_crate_local: bool, + } +} diff --git a/crates/miden-lib/masm_doc_comment_fmt.md b/crates/miden-protocol/masm_doc_comment_fmt.md similarity index 100% rename from crates/miden-lib/masm_doc_comment_fmt.md rename to crates/miden-protocol/masm_doc_comment_fmt.md diff --git a/crates/miden-objects/src/account/account_id/account_type.rs b/crates/miden-protocol/src/account/account_id/account_type.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/account_type.rs rename to crates/miden-protocol/src/account/account_id/account_type.rs diff --git a/crates/miden-objects/src/account/account_id/id_prefix.rs b/crates/miden-protocol/src/account/account_id/id_prefix.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/id_prefix.rs rename to crates/miden-protocol/src/account/account_id/id_prefix.rs diff --git a/crates/miden-objects/src/account/account_id/id_version.rs b/crates/miden-protocol/src/account/account_id/id_version.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/id_version.rs rename to crates/miden-protocol/src/account/account_id/id_version.rs diff --git a/crates/miden-objects/src/account/account_id/mod.rs b/crates/miden-protocol/src/account/account_id/mod.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/mod.rs rename to crates/miden-protocol/src/account/account_id/mod.rs diff --git a/crates/miden-objects/src/account/account_id/seed.rs b/crates/miden-protocol/src/account/account_id/seed.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/seed.rs rename to crates/miden-protocol/src/account/account_id/seed.rs diff --git a/crates/miden-objects/src/account/account_id/storage_mode.rs b/crates/miden-protocol/src/account/account_id/storage_mode.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/storage_mode.rs rename to crates/miden-protocol/src/account/account_id/storage_mode.rs diff --git a/crates/miden-objects/src/account/account_id/v0/mod.rs b/crates/miden-protocol/src/account/account_id/v0/mod.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/v0/mod.rs rename to crates/miden-protocol/src/account/account_id/v0/mod.rs diff --git a/crates/miden-objects/src/account/account_id/v0/prefix.rs b/crates/miden-protocol/src/account/account_id/v0/prefix.rs similarity index 100% rename from crates/miden-objects/src/account/account_id/v0/prefix.rs rename to crates/miden-protocol/src/account/account_id/v0/prefix.rs diff --git a/crates/miden-objects/src/account/auth.rs b/crates/miden-protocol/src/account/auth.rs similarity index 89% rename from crates/miden-objects/src/account/auth.rs rename to crates/miden-protocol/src/account/auth.rs index ea1c7f550c..a3fc47a577 100644 --- a/crates/miden-objects/src/account/auth.rs +++ b/crates/miden-protocol/src/account/auth.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use rand::{CryptoRng, Rng}; -use crate::crypto::dsa::{ecdsa_k256_keccak, rpo_falcon512}; +use crate::crypto::dsa::{ecdsa_k256_keccak, falcon512_rpo}; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -95,20 +95,20 @@ impl Deserializable for AuthScheme { #[non_exhaustive] #[repr(u8)] pub enum AuthSecretKey { - RpoFalcon512(rpo_falcon512::SecretKey) = RPO_FALCON_512, + RpoFalcon512(falcon512_rpo::SecretKey) = RPO_FALCON_512, EcdsaK256Keccak(ecdsa_k256_keccak::SecretKey) = ECDSA_K256_KECCAK, } impl AuthSecretKey { /// Generates an RpoFalcon512 secret key from the OS-provided randomness. #[cfg(feature = "std")] - pub fn new_rpo_falcon512() -> Self { - Self::RpoFalcon512(rpo_falcon512::SecretKey::new()) + pub fn new_falcon512_rpo() -> Self { + Self::RpoFalcon512(falcon512_rpo::SecretKey::new()) } /// Generates an RpoFalcon512 secrete key using the provided random number generator. - pub fn new_rpo_falcon512_with_rng(rng: &mut R) -> Self { - Self::RpoFalcon512(rpo_falcon512::SecretKey::with_rng(rng)) + pub fn new_falcon512_rpo_with_rng(rng: &mut R) -> Self { + Self::RpoFalcon512(falcon512_rpo::SecretKey::with_rng(rng)) } /// Generates an EcdsaK256Keccak secret key from the OS-provided randomness. @@ -161,7 +161,7 @@ impl Deserializable for AuthSecretKey { fn read_from(source: &mut R) -> Result { match source.read::()? { AuthScheme::RpoFalcon512 => { - let secret_key = rpo_falcon512::SecretKey::read_from(source)?; + let secret_key = falcon512_rpo::SecretKey::read_from(source)?; Ok(AuthSecretKey::RpoFalcon512(secret_key)) }, AuthScheme::EcdsaK256Keccak => { @@ -185,8 +185,8 @@ impl core::fmt::Display for PublicKeyCommitment { } } -impl From for PublicKeyCommitment { - fn from(value: rpo_falcon512::PublicKey) -> Self { +impl From for PublicKeyCommitment { + fn from(value: falcon512_rpo::PublicKey) -> Self { Self(value.to_commitment()) } } @@ -207,7 +207,7 @@ impl From for PublicKeyCommitment { #[derive(Clone, Debug)] #[non_exhaustive] pub enum PublicKey { - RpoFalcon512(rpo_falcon512::PublicKey), + RpoFalcon512(falcon512_rpo::PublicKey), EcdsaK256Keccak(ecdsa_k256_keccak::PublicKey), } @@ -256,7 +256,7 @@ impl Deserializable for PublicKey { fn read_from(source: &mut R) -> Result { match source.read::()? { AuthScheme::RpoFalcon512 => { - let pub_key = rpo_falcon512::PublicKey::read_from(source)?; + let pub_key = falcon512_rpo::PublicKey::read_from(source)?; Ok(PublicKey::RpoFalcon512(pub_key)) }, AuthScheme::EcdsaK256Keccak => { @@ -276,9 +276,9 @@ impl Deserializable for PublicKey { /// convert the native signature into a vector of field elements that can be loaded into the advice /// provider. To prepare the signature, use the provided `to_prepared_signature` method: /// ```rust,no_run -/// use miden_objects::account::auth::Signature; -/// use miden_objects::crypto::dsa::rpo_falcon512::SecretKey; -/// use miden_objects::{Felt, Word}; +/// use miden_protocol::account::auth::Signature; +/// use miden_protocol::crypto::dsa::falcon512_rpo::SecretKey; +/// use miden_protocol::{Felt, Word}; /// /// let secret_key = SecretKey::new(); /// let message = Word::default(); @@ -288,7 +288,7 @@ impl Deserializable for PublicKey { #[derive(Clone, Debug)] #[repr(u8)] pub enum Signature { - RpoFalcon512(rpo_falcon512::Signature) = RPO_FALCON_512, + RpoFalcon512(falcon512_rpo::Signature) = RPO_FALCON_512, EcdsaK256Keccak(ecdsa_k256_keccak::Signature) = ECDSA_K256_KECCAK, } @@ -310,9 +310,12 @@ impl Signature { // TODO: the `expect()` should be changed to an error; but that will be a part of a bigger // refactoring let mut result = match self { - Signature::RpoFalcon512(sig) => prepare_rpo_falcon512_signature(sig), - Signature::EcdsaK256Keccak(sig) => miden_stdlib::prepare_ecdsa_signature(msg, sig) - .expect("inferring public key from signature and message should succeed"), + Signature::RpoFalcon512(sig) => prepare_falcon512_rpo_signature(sig), + Signature::EcdsaK256Keccak(sig) => { + let pk = ecdsa_k256_keccak::PublicKey::recover_from(msg, sig) + .expect("inferring public key from signature and message should succeed"); + miden_core_lib::dsa::ecdsa_k256_keccak::encode_signature(&pk, sig) + }, }; // reverse the signature data so that when it is pushed onto the advice stack, the first @@ -322,8 +325,8 @@ impl Signature { } } -impl From for Signature { - fn from(signature: rpo_falcon512::Signature) -> Self { +impl From for Signature { + fn from(signature: falcon512_rpo::Signature) -> Self { Signature::RpoFalcon512(signature) } } @@ -342,7 +345,7 @@ impl Deserializable for Signature { fn read_from(source: &mut R) -> Result { match source.read::()? { AuthScheme::RpoFalcon512 => { - let signature = rpo_falcon512::Signature::read_from(source)?; + let signature = falcon512_rpo::Signature::read_from(source)?; Ok(Signature::RpoFalcon512(signature)) }, AuthScheme::EcdsaK256Keccak => { @@ -356,7 +359,7 @@ impl Deserializable for Signature { // SIGNATURE PREPARATION // ================================================================================================ -/// Converts a Falcon [rpo_falcon512::Signature] to a vector of values to be pushed onto the +/// Converts a Falcon [falcon512_rpo::Signature] to a vector of values to be pushed onto the /// advice stack. The values are the ones required for a Falcon signature verification inside the VM /// and they are: /// @@ -367,8 +370,8 @@ impl Deserializable for Signature { /// 4. The product of the above two polynomials `pi` in the ring of polynomials with coefficients in /// the Miden field. /// 5. The nonce represented as 8 field elements. -fn prepare_rpo_falcon512_signature(sig: &rpo_falcon512::Signature) -> Vec { - use rpo_falcon512::Polynomial; +fn prepare_falcon512_rpo_signature(sig: &falcon512_rpo::Signature) -> Vec { + use falcon512_rpo::Polynomial; // The signature is composed of a nonce and a polynomial s2 // The nonce is represented as 8 field elements. diff --git a/crates/miden-objects/src/account/builder/mod.rs b/crates/miden-protocol/src/account/builder/mod.rs similarity index 98% rename from crates/miden-objects/src/account/builder/mod.rs rename to crates/miden-protocol/src/account/builder/mod.rs index 1f43367d7d..e02aac4152 100644 --- a/crates/miden-objects/src/account/builder/mod.rs +++ b/crates/miden-protocol/src/account/builder/mod.rs @@ -295,12 +295,12 @@ mod tests { use crate::testing::noop_auth_component::NoopAuthComponent; const CUSTOM_CODE1: &str = " - export.foo + pub proc foo push.2.2 add eq.4 end "; const CUSTOM_CODE2: &str = " - export.bar + pub proc bar push.4.4 add eq.8 end "; @@ -401,10 +401,10 @@ mod tests { assert_eq!(account.code.procedure_roots().count(), 3); let foo_root = CUSTOM_LIBRARY1.mast_forest() - [CUSTOM_LIBRARY1.get_export_node_id(&CUSTOM_LIBRARY1.exports().next().unwrap().name)] + [CUSTOM_LIBRARY1.get_export_node_id(CUSTOM_LIBRARY1.exports().next().unwrap().path())] .digest(); let bar_root = CUSTOM_LIBRARY2.mast_forest() - [CUSTOM_LIBRARY2.get_export_node_id(&CUSTOM_LIBRARY2.exports().next().unwrap().name)] + [CUSTOM_LIBRARY2.get_export_node_id(CUSTOM_LIBRARY2.exports().next().unwrap().path())] .digest(); assert!(account.code().procedures().contains(&AccountProcedureRoot::from_raw(foo_root))); diff --git a/crates/miden-objects/src/account/code/header.rs b/crates/miden-protocol/src/account/code/header.rs similarity index 100% rename from crates/miden-objects/src/account/code/header.rs rename to crates/miden-protocol/src/account/code/header.rs diff --git a/crates/miden-objects/src/account/code/mod.rs b/crates/miden-protocol/src/account/code/mod.rs similarity index 99% rename from crates/miden-objects/src/account/code/mod.rs rename to crates/miden-protocol/src/account/code/mod.rs index a548921b5a..0d541b70b8 100644 --- a/crates/miden-objects/src/account/code/mod.rs +++ b/crates/miden-protocol/src/account/code/mod.rs @@ -470,13 +470,11 @@ mod tests { use miden_assembly::Assembler; let code_with_multiple_auth = " - use.miden::account - - export.auth_basic + pub proc auth_basic push.1 drop end - export.auth_secondary + pub proc auth_secondary push.0 drop end "; diff --git a/crates/miden-objects/src/account/code/procedure.rs b/crates/miden-protocol/src/account/code/procedure.rs similarity index 100% rename from crates/miden-objects/src/account/code/procedure.rs rename to crates/miden-protocol/src/account/code/procedure.rs diff --git a/crates/miden-objects/src/account/component/code.rs b/crates/miden-protocol/src/account/component/code.rs similarity index 100% rename from crates/miden-objects/src/account/component/code.rs rename to crates/miden-protocol/src/account/component/code.rs diff --git a/crates/miden-objects/src/account/component/metadata/mod.rs b/crates/miden-protocol/src/account/component/metadata/mod.rs similarity index 98% rename from crates/miden-objects/src/account/component/metadata/mod.rs rename to crates/miden-protocol/src/account/component/metadata/mod.rs index 961ccacfbb..619e8cc031 100644 --- a/crates/miden-objects/src/account/component/metadata/mod.rs +++ b/crates/miden-protocol/src/account/component/metadata/mod.rs @@ -37,8 +37,8 @@ use crate::AccountError; /// ``` /// use std::collections::BTreeSet; /// -/// use miden_objects::account::StorageSlotName; -/// use miden_objects::account::component::{ +/// use miden_protocol::account::StorageSlotName; +/// use miden_protocol::account::component::{ /// AccountComponentMetadata, /// AccountStorageSchema, /// FeltSchema, diff --git a/crates/miden-objects/src/account/component/mod.rs b/crates/miden-protocol/src/account/component/mod.rs similarity index 95% rename from crates/miden-objects/src/account/component/mod.rs rename to crates/miden-protocol/src/account/component/mod.rs index 60dbef0a58..9f709b28d4 100644 --- a/crates/miden-objects/src/account/component/mod.rs +++ b/crates/miden-protocol/src/account/component/mod.rs @@ -13,7 +13,7 @@ mod code; pub use code::AccountComponentCode; use crate::account::{AccountType, StorageSlot}; -use crate::assembly::QualifiedProcedureName; +use crate::assembly::Path; use crate::{AccountError, MastForest, Word}; // ACCOUNT COMPONENT @@ -194,13 +194,10 @@ impl AccountComponent { procedures } - /// Returns the digest of the procedure with the specified name, or `None` if it was not found + /// Returns the digest of the procedure with the specified path, or `None` if it was not found /// in this component's library or its library path is malformed. - pub fn get_procedure_root_by_name( - &self, - proc_name: impl TryInto, - ) -> Option { - self.code.as_library().get_procedure_root_by_name(proc_name) + pub fn get_procedure_root_by_path(&self, proc_name: impl AsRef) -> Option { + self.code.as_library().get_procedure_root_by_path(proc_name) } // MUTATORS @@ -250,7 +247,14 @@ mod tests { use miden_assembly::Assembler; use miden_core::utils::Serializable; - use miden_mast_package::{MastArtifact, Package, PackageManifest, Section, SectionId}; + use miden_mast_package::{ + MastArtifact, + Package, + PackageKind, + PackageManifest, + Section, + SectionId, + }; use semver::Version; use super::*; @@ -275,7 +279,7 @@ mod tests { name: "test_package".to_string(), mast: MastArtifact::Library(Arc::new(library.clone())), manifest: PackageManifest::new(None), - + kind: PackageKind::AccountComponent, sections: vec![Section::new( SectionId::ACCOUNT_COMPONENT_METADATA, metadata_bytes.clone(), @@ -298,6 +302,7 @@ mod tests { name: "test_package_no_metadata".to_string(), mast: MastArtifact::Library(Arc::new(library)), manifest: PackageManifest::new(None), + kind: PackageKind::AccountComponent, sections: vec![], // No metadata section version: Default::default(), description: None, @@ -343,6 +348,7 @@ mod tests { let package_without_metadata = Package { name: "test_package_no_metadata".to_string(), mast: MastArtifact::Library(Arc::new(library)), + kind: PackageKind::AccountComponent, manifest: PackageManifest::new(None), sections: vec![], // No metadata section version: Default::default(), diff --git a/crates/miden-objects/src/account/component/storage/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/init_storage_data.rs rename to crates/miden-protocol/src/account/component/storage/init_storage_data.rs diff --git a/crates/miden-objects/src/account/component/storage/mod.rs b/crates/miden-protocol/src/account/component/storage/mod.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/mod.rs rename to crates/miden-protocol/src/account/component/storage/mod.rs diff --git a/crates/miden-objects/src/account/component/storage/schema.rs b/crates/miden-protocol/src/account/component/storage/schema.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/schema.rs rename to crates/miden-protocol/src/account/component/storage/schema.rs diff --git a/crates/miden-objects/src/account/component/storage/toml/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/toml/init_storage_data.rs rename to crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs diff --git a/crates/miden-objects/src/account/component/storage/toml/mod.rs b/crates/miden-protocol/src/account/component/storage/toml/mod.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/toml/mod.rs rename to crates/miden-protocol/src/account/component/storage/toml/mod.rs diff --git a/crates/miden-objects/src/account/component/storage/toml/serde_impls.rs b/crates/miden-protocol/src/account/component/storage/toml/serde_impls.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/toml/serde_impls.rs rename to crates/miden-protocol/src/account/component/storage/toml/serde_impls.rs diff --git a/crates/miden-objects/src/account/component/storage/toml/tests.rs b/crates/miden-protocol/src/account/component/storage/toml/tests.rs similarity index 98% rename from crates/miden-objects/src/account/component/storage/toml/tests.rs rename to crates/miden-protocol/src/account/component/storage/toml/tests.rs index e277441a3f..5d491a53f3 100644 --- a/crates/miden-objects/src/account/component/storage/toml/tests.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/tests.rs @@ -308,7 +308,7 @@ fn metadata_toml_round_trip_typed_slots() { [[storage.slots]] name = "demo::typed_map" - type = { key = "miden::standards::auth::rpo_falcon512::pub_key", value = "miden::standards::auth::rpo_falcon512::pub_key" } + type = { key = "miden::standards::auth::falcon512_rpo::pub_key", value = "miden::standards::auth::falcon512_rpo::pub_key" } "#; let metadata = @@ -336,7 +336,7 @@ fn metadata_toml_round_trip_typed_slots() { _ => panic!("expected map slot"), }; - let pub_key_type = SchemaTypeId::new("miden::standards::auth::rpo_falcon512::pub_key").unwrap(); + let pub_key_type = SchemaTypeId::new("miden::standards::auth::falcon512_rpo::pub_key").unwrap(); assert_eq!(map_slot.key_schema(), &WordSchema::new_simple(pub_key_type.clone())); assert_eq!(map_slot.value_schema(), &WordSchema::new_simple(pub_key_type)); @@ -368,11 +368,11 @@ fn metadata_toml_round_trip_typed_slots() { let map_type = typed_map_entry.get("type").unwrap().as_table().unwrap(); assert_eq!( map_type.get("key").unwrap().as_str().unwrap(), - "miden::standards::auth::rpo_falcon512::pub_key" + "miden::standards::auth::falcon512_rpo::pub_key" ); assert_eq!( map_type.get("value").unwrap().as_str().unwrap(), - "miden::standards::auth::rpo_falcon512::pub_key" + "miden::standards::auth::falcon512_rpo::pub_key" ); } @@ -399,7 +399,7 @@ fn extensive_schema_metadata_and_init_toml_example() { [[storage.slots]] name = "demo::owner_pub_key" description = "Owner public key" - type = "miden::standards::auth::rpo_falcon512::pub_key" + type = "miden::standards::auth::falcon512_rpo::pub_key" # simple felt-typed word slot (parsed as felt, stored as [0,0,0,]) [[storage.slots]] diff --git a/crates/miden-objects/src/account/component/storage/type_registry.rs b/crates/miden-protocol/src/account/component/storage/type_registry.rs similarity index 98% rename from crates/miden-objects/src/account/component/storage/type_registry.rs rename to crates/miden-protocol/src/account/component/storage/type_registry.rs index 4e0c58550b..fe94417574 100644 --- a/crates/miden-objects/src/account/component/storage/type_registry.rs +++ b/crates/miden-protocol/src/account/component/storage/type_registry.rs @@ -6,7 +6,7 @@ use core::fmt::{self, Display}; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; use miden_core::{Felt, Word}; -use miden_crypto::dsa::{ecdsa_k256_keccak, rpo_falcon512}; +use miden_crypto::dsa::{ecdsa_k256_keccak, falcon512_rpo}; use miden_processor::DeserializationError; use thiserror::Error; @@ -26,7 +26,7 @@ pub static SCHEMA_TYPE_REGISTRY: LazyLock = LazyLock::new(|| registry.register_felt_type::(); registry.register_felt_type::(); registry.register_word_type::(); - registry.register_word_type::(); + registry.register_word_type::(); registry.register_word_type::(); registry }); @@ -82,7 +82,7 @@ impl SchemaTypeError { /// Some examples: /// - `u32` /// - `felt` -/// - `miden::standards::auth::rpo_falcon512::pub_key` +/// - `miden::standards::auth::falcon512_rpo::pub_key` #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] #[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))] #[cfg_attr(feature = "std", serde(transparent))] @@ -443,9 +443,9 @@ impl WordType for Word { } } -impl WordType for rpo_falcon512::PublicKey { +impl WordType for falcon512_rpo::PublicKey { fn type_name() -> SchemaTypeId { - SchemaTypeId::new("miden::standards::auth::rpo_falcon512::pub_key") + SchemaTypeId::new("miden::standards::auth::falcon512_rpo::pub_key") .expect("type is well formed") } fn parse_str(input: &str) -> Result { diff --git a/crates/miden-objects/src/account/component/storage/value_name.rs b/crates/miden-protocol/src/account/component/storage/value_name.rs similarity index 100% rename from crates/miden-objects/src/account/component/storage/value_name.rs rename to crates/miden-protocol/src/account/component/storage/value_name.rs diff --git a/crates/miden-objects/src/account/delta/mod.rs b/crates/miden-protocol/src/account/delta/mod.rs similarity index 100% rename from crates/miden-objects/src/account/delta/mod.rs rename to crates/miden-protocol/src/account/delta/mod.rs diff --git a/crates/miden-objects/src/account/delta/storage.rs b/crates/miden-protocol/src/account/delta/storage.rs similarity index 100% rename from crates/miden-objects/src/account/delta/storage.rs rename to crates/miden-protocol/src/account/delta/storage.rs diff --git a/crates/miden-objects/src/account/delta/vault.rs b/crates/miden-protocol/src/account/delta/vault.rs similarity index 100% rename from crates/miden-objects/src/account/delta/vault.rs rename to crates/miden-protocol/src/account/delta/vault.rs diff --git a/crates/miden-objects/src/account/file.rs b/crates/miden-protocol/src/account/file.rs similarity index 97% rename from crates/miden-objects/src/account/file.rs rename to crates/miden-protocol/src/account/file.rs index f882f261e6..4b6f5d4287 100644 --- a/crates/miden-objects/src/account/file.rs +++ b/crates/miden-protocol/src/account/file.rs @@ -119,8 +119,8 @@ mod tests { let storage = AccountStorage::new(vec![]).unwrap(); let nonce = Felt::new(1); let account = Account::new_existing(id, vault, storage, code, nonce); - let auth_secret_key = AuthSecretKey::new_rpo_falcon512(); - let auth_secret_key_2 = AuthSecretKey::new_rpo_falcon512(); + let auth_secret_key = AuthSecretKey::new_falcon512_rpo(); + let auth_secret_key_2 = AuthSecretKey::new_falcon512_rpo(); AccountFile::new(account, vec![auth_secret_key, auth_secret_key_2]) } diff --git a/crates/miden-objects/src/account/header.rs b/crates/miden-protocol/src/account/header.rs similarity index 75% rename from crates/miden-objects/src/account/header.rs rename to crates/miden-protocol/src/account/header.rs index 1d6a14d802..8a2a19f9cd 100644 --- a/crates/miden-objects/src/account/header.rs +++ b/crates/miden-protocol/src/account/header.rs @@ -1,8 +1,19 @@ use alloc::vec::Vec; use super::{Account, AccountId, Felt, PartialAccount, ZERO, hash_account}; -use crate::Word; +use crate::transaction::memory::{ + ACCT_CODE_COMMITMENT_OFFSET, + ACCT_DATA_MEM_SIZE, + ACCT_ID_AND_NONCE_OFFSET, + ACCT_ID_PREFIX_IDX, + ACCT_ID_SUFFIX_IDX, + ACCT_NONCE_IDX, + ACCT_STORAGE_COMMITMENT_OFFSET, + ACCT_VAULT_ROOT_OFFSET, + MemoryOffset, +}; use crate::utils::serde::{Deserializable, Serializable}; +use crate::{AccountError, WORD_SIZE, Word, WordError}; // ACCOUNT HEADER // ================================================================================================ @@ -45,6 +56,33 @@ impl AccountHeader { } } + /// Parses the account header data returned by the VM into individual account component + /// commitments. Returns a tuple of account ID, vault root, storage commitment, code + /// commitment, and nonce. + pub(crate) fn try_from_elements(elements: &[Felt]) -> Result { + if elements.len() != ACCT_DATA_MEM_SIZE { + return Err(AccountError::HeaderDataIncorrectLength { + actual: elements.len(), + expected: ACCT_DATA_MEM_SIZE, + }); + } + + let id = AccountId::try_from([ + elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_PREFIX_IDX], + elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_SUFFIX_IDX], + ]) + .map_err(AccountError::FinalAccountHeaderIdParsingFailed)?; + let nonce = elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_NONCE_IDX]; + let vault_root = parse_word(elements, ACCT_VAULT_ROOT_OFFSET) + .expect("we should have sliced off exactly 4 bytes"); + let storage_commitment = parse_word(elements, ACCT_STORAGE_COMMITMENT_OFFSET) + .expect("we should have sliced off exactly 4 bytes"); + let code_commitment = parse_word(elements, ACCT_CODE_COMMITMENT_OFFSET) + .expect("we should have sliced off exactly 4 bytes"); + + Ok(AccountHeader::new(id, nonce, vault_root, storage_commitment, code_commitment)) + } + // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- @@ -177,6 +215,14 @@ impl Deserializable for AccountHeader { } } +// HELPER FUNCTIONS +// ================================================================================================ + +/// Creates a new `Word` instance from the slice of `Felt`s using provided offset. +fn parse_word(data: &[Felt], offset: MemoryOffset) -> Result { + Word::try_from(&data[offset as usize..offset as usize + WORD_SIZE]) +} + // TESTS // ================================================================================================ diff --git a/crates/miden-objects/src/account/mod.rs b/crates/miden-protocol/src/account/mod.rs similarity index 99% rename from crates/miden-objects/src/account/mod.rs rename to crates/miden-protocol/src/account/mod.rs index 12ed0c282a..95d6eac982 100644 --- a/crates/miden-objects/src/account/mod.rs +++ b/crates/miden-protocol/src/account/mod.rs @@ -828,7 +828,7 @@ mod tests { /// account type returns an error. #[test] fn test_account_unsupported_component_type() { - let code1 = "export.foo add end"; + let code1 = "pub proc foo add end"; let library1 = Assembler::default().assemble_library([code1]).unwrap(); // This component support all account types except the regular account with updatable code. diff --git a/crates/miden-objects/src/account/partial.rs b/crates/miden-protocol/src/account/partial.rs similarity index 100% rename from crates/miden-objects/src/account/partial.rs rename to crates/miden-protocol/src/account/partial.rs diff --git a/crates/miden-objects/src/account/storage/header.rs b/crates/miden-protocol/src/account/storage/header.rs similarity index 100% rename from crates/miden-objects/src/account/storage/header.rs rename to crates/miden-protocol/src/account/storage/header.rs diff --git a/crates/miden-objects/src/account/storage/map/mod.rs b/crates/miden-protocol/src/account/storage/map/mod.rs similarity index 98% rename from crates/miden-objects/src/account/storage/map/mod.rs rename to crates/miden-protocol/src/account/storage/map/mod.rs index 4b262f88ed..2882496190 100644 --- a/crates/miden-objects/src/account/storage/map/mod.rs +++ b/crates/miden-protocol/src/account/storage/map/mod.rs @@ -5,7 +5,8 @@ use miden_crypto::merkle::EmptySubtreeRoots; use super::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, Word}; use crate::account::StorageMapDelta; -use crate::crypto::merkle::{InnerNodeInfo, LeafIndex, SMT_DEPTH, Smt, SmtLeaf}; +use crate::crypto::merkle::InnerNodeInfo; +use crate::crypto::merkle::smt::{LeafIndex, SMT_DEPTH, Smt, SmtLeaf}; use crate::errors::StorageMapError; use crate::{AccountError, Felt, Hasher}; diff --git a/crates/miden-objects/src/account/storage/map/partial.rs b/crates/miden-protocol/src/account/storage/map/partial.rs similarity index 98% rename from crates/miden-objects/src/account/storage/map/partial.rs rename to crates/miden-protocol/src/account/storage/map/partial.rs index 92fd97e868..55f1f408f7 100644 --- a/crates/miden-objects/src/account/storage/map/partial.rs +++ b/crates/miden-protocol/src/account/storage/map/partial.rs @@ -2,15 +2,8 @@ use alloc::collections::BTreeMap; use miden_core::utils::{Deserializable, Serializable}; use miden_crypto::Word; -use miden_crypto::merkle::{ - InnerNodeInfo, - LeafIndex, - MerkleError, - PartialSmt, - SMT_DEPTH, - SmtLeaf, - SmtProof, -}; +use miden_crypto::merkle::smt::{LeafIndex, PartialSmt, SMT_DEPTH, SmtLeaf, SmtProof}; +use miden_crypto::merkle::{InnerNodeInfo, MerkleError}; use crate::account::{StorageMap, StorageMapWitness}; use crate::utils::serde::{ByteReader, DeserializationError}; diff --git a/crates/miden-objects/src/account/storage/map/witness.rs b/crates/miden-protocol/src/account/storage/map/witness.rs similarity index 93% rename from crates/miden-objects/src/account/storage/map/witness.rs rename to crates/miden-protocol/src/account/storage/map/witness.rs index 401d8345a4..f70a8359af 100644 --- a/crates/miden-objects/src/account/storage/map/witness.rs +++ b/crates/miden-protocol/src/account/storage/map/witness.rs @@ -1,6 +1,7 @@ use alloc::collections::BTreeMap; -use miden_crypto::merkle::{InnerNodeInfo, SmtProof}; +use miden_crypto::merkle::InnerNodeInfo; +use miden_crypto::merkle::smt::SmtProof; use crate::Word; use crate::account::StorageMap; @@ -15,9 +16,9 @@ use crate::errors::StorageMapError; /// This type guarantees that the raw key-value pairs it contains are all present in the /// contained SMT proof. Note that the inverse is not necessarily true. The proof may contain more /// entries than the map because to prove inclusion of a given raw key A an -/// [`SmtLeaf::Multiple`](miden_crypto::merkle::SmtLeaf::Multiple) may be present that contains both -/// keys hash(A) and hash(B). However, B may not be present in the key-value pairs and this is a -/// valid state. +/// [`SmtLeaf::Multiple`](miden_crypto::merkle::smt::SmtLeaf::Multiple) may be present that contains +/// both keys hash(A) and hash(B). However, B may not be present in the key-value pairs and this is +/// a valid state. #[derive(Debug, Clone, PartialEq, Eq)] pub struct StorageMapWitness { proof: SmtProof, diff --git a/crates/miden-objects/src/account/storage/mod.rs b/crates/miden-protocol/src/account/storage/mod.rs similarity index 99% rename from crates/miden-objects/src/account/storage/mod.rs rename to crates/miden-protocol/src/account/storage/mod.rs index 6109a2c8a3..6947fd4629 100644 --- a/crates/miden-objects/src/account/storage/mod.rs +++ b/crates/miden-protocol/src/account/storage/mod.rs @@ -29,7 +29,8 @@ mod partial; pub use partial::PartialStorage; static FAUCET_SYSDATA_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::faucet::sysdata").expect("storage slot name should be valid") + StorageSlotName::new("miden::protocol::faucet::sysdata") + .expect("storage slot name should be valid") }); // ACCOUNT STORAGE diff --git a/crates/miden-objects/src/account/storage/partial.rs b/crates/miden-protocol/src/account/storage/partial.rs similarity index 98% rename from crates/miden-objects/src/account/storage/partial.rs rename to crates/miden-protocol/src/account/storage/partial.rs index 755f867d4e..d7e0a5b015 100644 --- a/crates/miden-objects/src/account/storage/partial.rs +++ b/crates/miden-protocol/src/account/storage/partial.rs @@ -2,7 +2,8 @@ use alloc::collections::{BTreeMap, BTreeSet}; use miden_core::utils::{Deserializable, Serializable}; use miden_crypto::Word; -use miden_crypto::merkle::{InnerNodeInfo, SmtLeaf}; +use miden_crypto::merkle::InnerNodeInfo; +use miden_crypto::merkle::smt::SmtLeaf; use super::{AccountStorage, AccountStorageHeader, StorageSlotContent}; use crate::AccountError; diff --git a/crates/miden-objects/src/account/storage/slot/mod.rs b/crates/miden-protocol/src/account/storage/slot/mod.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/mod.rs rename to crates/miden-protocol/src/account/storage/slot/mod.rs diff --git a/crates/miden-objects/src/account/storage/slot/slot_content.rs b/crates/miden-protocol/src/account/storage/slot/slot_content.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/slot_content.rs rename to crates/miden-protocol/src/account/storage/slot/slot_content.rs diff --git a/crates/miden-objects/src/account/storage/slot/slot_id.rs b/crates/miden-protocol/src/account/storage/slot/slot_id.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/slot_id.rs rename to crates/miden-protocol/src/account/storage/slot/slot_id.rs diff --git a/crates/miden-objects/src/account/storage/slot/slot_name.rs b/crates/miden-protocol/src/account/storage/slot/slot_name.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/slot_name.rs rename to crates/miden-protocol/src/account/storage/slot/slot_name.rs diff --git a/crates/miden-objects/src/account/storage/slot/storage_slot.rs b/crates/miden-protocol/src/account/storage/slot/storage_slot.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/storage_slot.rs rename to crates/miden-protocol/src/account/storage/slot/storage_slot.rs diff --git a/crates/miden-objects/src/account/storage/slot/type.rs b/crates/miden-protocol/src/account/storage/slot/type.rs similarity index 100% rename from crates/miden-objects/src/account/storage/slot/type.rs rename to crates/miden-protocol/src/account/storage/slot/type.rs diff --git a/crates/miden-objects/src/address/address_id.rs b/crates/miden-protocol/src/address/address_id.rs similarity index 100% rename from crates/miden-objects/src/address/address_id.rs rename to crates/miden-protocol/src/address/address_id.rs diff --git a/crates/miden-objects/src/address/interface.rs b/crates/miden-protocol/src/address/interface.rs similarity index 100% rename from crates/miden-objects/src/address/interface.rs rename to crates/miden-protocol/src/address/interface.rs diff --git a/crates/miden-objects/src/address/mod.rs b/crates/miden-protocol/src/address/mod.rs similarity index 99% rename from crates/miden-objects/src/address/mod.rs rename to crates/miden-protocol/src/address/mod.rs index 142dd9e8b8..6e9c864a36 100644 --- a/crates/miden-objects/src/address/mod.rs +++ b/crates/miden-protocol/src/address/mod.rs @@ -445,7 +445,7 @@ mod tests { /// Tests that an address with encryption key can be created and used. #[test] fn address_with_encryption_key() -> anyhow::Result<()> { - use crate::crypto::dsa::eddsa_25519::SecretKey; + use crate::crypto::dsa::eddsa_25519_sha512::SecretKey; use crate::crypto::ies::{SealingKey, UnsealingKey}; let rng = &mut rand::rng(); @@ -484,7 +484,7 @@ mod tests { /// Tests that an address with encryption key can be encoded/decoded. #[test] fn address_encryption_key_encode_decode() -> anyhow::Result<()> { - use crate::crypto::dsa::eddsa_25519::SecretKey; + use crate::crypto::dsa::eddsa_25519_sha512::SecretKey; let rng = &mut rand::rng(); // Use a local account type (RegularAccountImmutableCode) instead of network diff --git a/crates/miden-objects/src/address/network_id.rs b/crates/miden-protocol/src/address/network_id.rs similarity index 100% rename from crates/miden-objects/src/address/network_id.rs rename to crates/miden-protocol/src/address/network_id.rs diff --git a/crates/miden-objects/src/address/routing_parameters.rs b/crates/miden-protocol/src/address/routing_parameters.rs similarity index 98% rename from crates/miden-objects/src/address/routing_parameters.rs rename to crates/miden-protocol/src/address/routing_parameters.rs index 5d173f9ee6..5b5899efea 100644 --- a/crates/miden-objects/src/address/routing_parameters.rs +++ b/crates/miden-protocol/src/address/routing_parameters.rs @@ -7,7 +7,7 @@ use bech32::{Bech32m, Hrp}; use crate::AddressError; use crate::address::AddressInterface; -use crate::crypto::dsa::{ecdsa_k256_keccak, eddsa_25519}; +use crate::crypto::dsa::{ecdsa_k256_keccak, eddsa_25519_sha512}; use crate::crypto::ies::SealingKey; use crate::errors::Bech32Error; use crate::note::NoteTag; @@ -363,7 +363,7 @@ fn decode_encryption_key( fn read_x25519_pub_key( byte_iter: &mut impl ExactSizeIterator, -) -> Result { +) -> Result { if byte_iter.len() < X25519_PUBLIC_KEY_LENGTH { return Err(AddressError::decode_error(format!( "expected {} bytes to decode X25519 public key", @@ -371,7 +371,7 @@ fn read_x25519_pub_key( ))); } let key_bytes: [u8; X25519_PUBLIC_KEY_LENGTH] = read_byte_array(byte_iter); - eddsa_25519::PublicKey::read_from_bytes(&key_bytes).map_err(|err| { + eddsa_25519_sha512::PublicKey::read_from_bytes(&key_bytes).map_err(|err| { AddressError::decode_error_with_source("failed to decode X25519 public key", err) }) } @@ -538,7 +538,7 @@ mod tests { // Test X25519XChaCha20Poly1305 { - use crate::crypto::dsa::eddsa_25519::SecretKey; + use crate::crypto::dsa::eddsa_25519_sha512::SecretKey; let secret_key = SecretKey::with_rng(&mut rand::rng()); let public_key = secret_key.public_key(); let encryption_key = SealingKey::X25519XChaCha20Poly1305(public_key); @@ -556,7 +556,7 @@ mod tests { // Test X25519AeadRpo { - use crate::crypto::dsa::eddsa_25519::SecretKey; + use crate::crypto::dsa::eddsa_25519_sha512::SecretKey; let secret_key = SecretKey::with_rng(&mut rand::rng()); let public_key = secret_key.public_key(); let encryption_key = SealingKey::X25519AeadRpo(public_key); diff --git a/crates/miden-objects/src/address/type.rs b/crates/miden-protocol/src/address/type.rs similarity index 100% rename from crates/miden-objects/src/address/type.rs rename to crates/miden-protocol/src/address/type.rs diff --git a/crates/miden-objects/src/asset/fungible.rs b/crates/miden-protocol/src/asset/fungible.rs similarity index 100% rename from crates/miden-objects/src/asset/fungible.rs rename to crates/miden-protocol/src/asset/fungible.rs diff --git a/crates/miden-objects/src/asset/mod.rs b/crates/miden-protocol/src/asset/mod.rs similarity index 100% rename from crates/miden-objects/src/asset/mod.rs rename to crates/miden-protocol/src/asset/mod.rs diff --git a/crates/miden-objects/src/asset/nonfungible.rs b/crates/miden-protocol/src/asset/nonfungible.rs similarity index 100% rename from crates/miden-objects/src/asset/nonfungible.rs rename to crates/miden-protocol/src/asset/nonfungible.rs diff --git a/crates/miden-objects/src/asset/token_symbol.rs b/crates/miden-protocol/src/asset/token_symbol.rs similarity index 99% rename from crates/miden-objects/src/asset/token_symbol.rs rename to crates/miden-protocol/src/asset/token_symbol.rs index 289c66443a..a1132fd396 100644 --- a/crates/miden-objects/src/asset/token_symbol.rs +++ b/crates/miden-protocol/src/asset/token_symbol.rs @@ -25,7 +25,7 @@ impl TokenSymbol { /// This function is `const` and can be used to define token symbols as constants, e.g.: /// /// ```rust - /// # use miden_objects::asset::TokenSymbol; + /// # use miden_protocol::asset::TokenSymbol; /// const TOKEN: TokenSymbol = TokenSymbol::from_static_str("ETH"); /// ``` /// diff --git a/crates/miden-objects/src/asset/vault/asset_witness.rs b/crates/miden-protocol/src/asset/vault/asset_witness.rs similarity index 97% rename from crates/miden-objects/src/asset/vault/asset_witness.rs rename to crates/miden-protocol/src/asset/vault/asset_witness.rs index a1f79c4342..e894a56070 100644 --- a/crates/miden-objects/src/asset/vault/asset_witness.rs +++ b/crates/miden-protocol/src/asset/vault/asset_witness.rs @@ -1,6 +1,7 @@ use alloc::string::ToString; -use miden_crypto::merkle::{InnerNodeInfo, SmtLeaf, SmtProof}; +use miden_crypto::merkle::InnerNodeInfo; +use miden_crypto::merkle::smt::{SmtLeaf, SmtProof}; use super::vault_key::AssetVaultKey; use crate::AssetError; @@ -112,7 +113,7 @@ impl Deserializable for AssetWitness { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_crypto::merkle::Smt; + use miden_crypto::merkle::smt::Smt; use super::*; use crate::Word; diff --git a/crates/miden-objects/src/asset/vault/mod.rs b/crates/miden-protocol/src/asset/vault/mod.rs similarity index 99% rename from crates/miden-objects/src/asset/vault/mod.rs rename to crates/miden-protocol/src/asset/vault/mod.rs index 88b57e0dcf..bb3e1c046a 100644 --- a/crates/miden-objects/src/asset/vault/mod.rs +++ b/crates/miden-protocol/src/asset/vault/mod.rs @@ -15,7 +15,7 @@ use super::{ Serializable, }; use crate::account::{AccountId, AccountVaultDelta, NonFungibleDeltaAction}; -use crate::crypto::merkle::Smt; +use crate::crypto::merkle::smt::Smt; use crate::{AssetVaultError, Word}; mod partial; diff --git a/crates/miden-objects/src/asset/vault/partial.rs b/crates/miden-protocol/src/asset/vault/partial.rs similarity index 98% rename from crates/miden-objects/src/asset/vault/partial.rs rename to crates/miden-protocol/src/asset/vault/partial.rs index b303842cff..1427a8902c 100644 --- a/crates/miden-objects/src/asset/vault/partial.rs +++ b/crates/miden-protocol/src/asset/vault/partial.rs @@ -1,6 +1,7 @@ use alloc::string::ToString; -use miden_crypto::merkle::{InnerNodeInfo, MerkleError, PartialSmt, SmtLeaf, SmtProof}; +use miden_crypto::merkle::smt::{PartialSmt, SmtLeaf, SmtProof}; +use miden_crypto::merkle::{InnerNodeInfo, MerkleError}; use super::{AssetVault, AssetVaultKey}; use crate::Word; @@ -191,7 +192,7 @@ impl Deserializable for PartialVault { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_crypto::merkle::Smt; + use miden_crypto::merkle::smt::Smt; use super::*; use crate::asset::FungibleAsset; diff --git a/crates/miden-objects/src/asset/vault/vault_key.rs b/crates/miden-protocol/src/asset/vault/vault_key.rs similarity index 99% rename from crates/miden-objects/src/asset/vault/vault_key.rs rename to crates/miden-protocol/src/asset/vault/vault_key.rs index c68cf7be10..2cff63d04d 100644 --- a/crates/miden-objects/src/asset/vault/vault_key.rs +++ b/crates/miden-protocol/src/asset/vault/vault_key.rs @@ -1,6 +1,6 @@ use core::fmt; -use miden_crypto::merkle::LeafIndex; +use miden_crypto::merkle::smt::LeafIndex; use miden_processor::SMT_DEPTH; use crate::Word; diff --git a/crates/miden-objects/src/batch/account_update.rs b/crates/miden-protocol/src/batch/account_update.rs similarity index 100% rename from crates/miden-objects/src/batch/account_update.rs rename to crates/miden-protocol/src/batch/account_update.rs diff --git a/crates/miden-objects/src/batch/batch_id.rs b/crates/miden-protocol/src/batch/batch_id.rs similarity index 100% rename from crates/miden-objects/src/batch/batch_id.rs rename to crates/miden-protocol/src/batch/batch_id.rs diff --git a/crates/miden-objects/src/batch/input_output_note_tracker.rs b/crates/miden-protocol/src/batch/input_output_note_tracker.rs similarity index 100% rename from crates/miden-objects/src/batch/input_output_note_tracker.rs rename to crates/miden-protocol/src/batch/input_output_note_tracker.rs diff --git a/crates/miden-objects/src/batch/mod.rs b/crates/miden-protocol/src/batch/mod.rs similarity index 100% rename from crates/miden-objects/src/batch/mod.rs rename to crates/miden-protocol/src/batch/mod.rs diff --git a/crates/miden-objects/src/batch/note_tree.rs b/crates/miden-protocol/src/batch/note_tree.rs similarity index 96% rename from crates/miden-objects/src/batch/note_tree.rs rename to crates/miden-protocol/src/batch/note_tree.rs index 23ba6c7608..de473ee0f6 100644 --- a/crates/miden-objects/src/batch/note_tree.rs +++ b/crates/miden-protocol/src/batch/note_tree.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; -use crate::crypto::merkle::{LeafIndex, MerkleError, SimpleSmt}; +use crate::crypto::merkle::MerkleError; +use crate::crypto::merkle::smt::{LeafIndex, SimpleSmt}; use crate::note::{NoteId, NoteMetadata, compute_note_commitment}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; use crate::{BATCH_NOTE_TREE_DEPTH, EMPTY_WORD, Word}; diff --git a/crates/miden-objects/src/batch/ordered_batches.rs b/crates/miden-protocol/src/batch/ordered_batches.rs similarity index 100% rename from crates/miden-objects/src/batch/ordered_batches.rs rename to crates/miden-protocol/src/batch/ordered_batches.rs diff --git a/crates/miden-objects/src/batch/proposed_batch.rs b/crates/miden-protocol/src/batch/proposed_batch.rs similarity index 99% rename from crates/miden-objects/src/batch/proposed_batch.rs rename to crates/miden-protocol/src/batch/proposed_batch.rs index 5a57cee094..5d5cb0f97b 100644 --- a/crates/miden-objects/src/batch/proposed_batch.rs +++ b/crates/miden-protocol/src/batch/proposed_batch.rs @@ -425,7 +425,7 @@ impl Deserializable for ProposedBatch { #[cfg(test)] mod tests { use anyhow::Context; - use miden_crypto::merkle::{Mmr, PartialMmr}; + use miden_crypto::merkle::mmr::{Mmr, PartialMmr}; use miden_verifier::ExecutionProof; use winter_rand_utils::rand_value; diff --git a/crates/miden-objects/src/batch/proven_batch.rs b/crates/miden-protocol/src/batch/proven_batch.rs similarity index 100% rename from crates/miden-objects/src/batch/proven_batch.rs rename to crates/miden-protocol/src/batch/proven_batch.rs diff --git a/crates/miden-objects/src/block/account_tree/backend.rs b/crates/miden-protocol/src/block/account_tree/backend.rs similarity index 96% rename from crates/miden-objects/src/block/account_tree/backend.rs rename to crates/miden-protocol/src/block/account_tree/backend.rs index 9679d255cf..78dc989786 100644 --- a/crates/miden-objects/src/block/account_tree/backend.rs +++ b/crates/miden-protocol/src/block/account_tree/backend.rs @@ -3,17 +3,10 @@ use alloc::vec::Vec; use super::{AccountId, AccountIdPrefix, AccountTree, AccountTreeError, account_id_to_smt_key}; use crate::Word; +use crate::crypto::merkle::MerkleError; #[cfg(feature = "std")] -use crate::crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; -use crate::crypto::merkle::{ - LeafIndex, - MerkleError, - MutationSet, - SMT_DEPTH, - Smt, - SmtLeaf, - SmtProof, -}; +use crate::crypto::merkle::smt::{LargeSmt, LargeSmtError, SmtStorage}; +use crate::crypto::merkle::smt::{LeafIndex, MutationSet, SMT_DEPTH, Smt, SmtLeaf, SmtProof}; // ACCOUNT TREE BACKEND // ================================================================================================ @@ -183,7 +176,7 @@ where } fn root(&self) -> Word { - LargeSmt::root(self).map_err(large_smt_error_to_merkle_error).unwrap() + LargeSmt::root(self) } } diff --git a/crates/miden-objects/src/block/account_tree/mod.rs b/crates/miden-protocol/src/block/account_tree/mod.rs similarity index 98% rename from crates/miden-objects/src/block/account_tree/mod.rs rename to crates/miden-protocol/src/block/account_tree/mod.rs index 38efc26e82..f48ce04425 100644 --- a/crates/miden-objects/src/block/account_tree/mod.rs +++ b/crates/miden-protocol/src/block/account_tree/mod.rs @@ -3,7 +3,8 @@ use alloc::vec::Vec; use crate::Word; use crate::account::{AccountId, AccountIdPrefix}; -use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt, SmtLeaf}; +use crate::crypto::merkle::MerkleError; +use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt, SmtLeaf}; use crate::errors::AccountTreeError; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -578,7 +579,7 @@ pub(super) mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_basic_operations() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; // Create test data let id0 = AccountIdBuilder::new().build_with_seed([5; 32]); @@ -624,7 +625,7 @@ pub(super) mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_duplicate_prefix_check() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids(); @@ -645,7 +646,7 @@ pub(super) mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_apply_mutations() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let id0 = AccountIdBuilder::new().build_with_seed([5; 32]); let id1 = AccountIdBuilder::new().build_with_seed([6; 32]); @@ -678,7 +679,7 @@ pub(super) mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_same_root_as_regular_smt() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let id0 = AccountIdBuilder::new().build_with_seed([5; 32]); let id1 = AccountIdBuilder::new().build_with_seed([6; 32]); diff --git a/crates/miden-objects/src/block/account_tree/partial.rs b/crates/miden-protocol/src/block/account_tree/partial.rs similarity index 99% rename from crates/miden-objects/src/block/account_tree/partial.rs rename to crates/miden-protocol/src/block/account_tree/partial.rs index f92b8918f0..af84f725ae 100644 --- a/crates/miden-objects/src/block/account_tree/partial.rs +++ b/crates/miden-protocol/src/block/account_tree/partial.rs @@ -1,9 +1,8 @@ -use miden_crypto::merkle::SmtLeaf; +use miden_crypto::merkle::smt::{PartialSmt, SmtLeaf}; use super::{AccountWitness, account_id_to_smt_key}; use crate::Word; use crate::account::AccountId; -use crate::crypto::merkle::PartialSmt; use crate::errors::AccountTreeError; /// The partial sparse merkle tree containing the state commitments of accounts in the chain. @@ -191,7 +190,7 @@ impl PartialAccountTree { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_crypto::merkle::Smt; + use miden_crypto::merkle::smt::Smt; use super::*; use crate::block::account_tree::AccountTree; diff --git a/crates/miden-objects/src/block/account_tree/witness.rs b/crates/miden-protocol/src/block/account_tree/witness.rs similarity index 97% rename from crates/miden-objects/src/block/account_tree/witness.rs rename to crates/miden-protocol/src/block/account_tree/witness.rs index 16ca22f451..b6d6e5083b 100644 --- a/crates/miden-objects/src/block/account_tree/witness.rs +++ b/crates/miden-protocol/src/block/account_tree/witness.rs @@ -1,14 +1,7 @@ use alloc::string::ToString; -use miden_crypto::merkle::{ - InnerNodeInfo, - LeafIndex, - SMT_DEPTH, - SmtLeaf, - SmtProof, - SmtProofError, - SparseMerklePath, -}; +use miden_crypto::merkle::smt::{LeafIndex, SMT_DEPTH, SmtLeaf, SmtProof, SmtProofError}; +use miden_crypto::merkle::{InnerNodeInfo, SparseMerklePath}; use crate::account::AccountId; use crate::block::account_tree::{account_id_to_smt_key, smt_key_to_account_id}; diff --git a/crates/miden-objects/src/block/account_update_witness.rs b/crates/miden-protocol/src/block/account_update_witness.rs similarity index 100% rename from crates/miden-objects/src/block/account_update_witness.rs rename to crates/miden-protocol/src/block/account_update_witness.rs diff --git a/crates/miden-objects/src/block/block_account_update.rs b/crates/miden-protocol/src/block/block_account_update.rs similarity index 100% rename from crates/miden-objects/src/block/block_account_update.rs rename to crates/miden-protocol/src/block/block_account_update.rs diff --git a/crates/miden-objects/src/block/block_body.rs b/crates/miden-protocol/src/block/block_body.rs similarity index 100% rename from crates/miden-objects/src/block/block_body.rs rename to crates/miden-protocol/src/block/block_body.rs diff --git a/crates/miden-objects/src/block/block_inputs.rs b/crates/miden-protocol/src/block/block_inputs.rs similarity index 100% rename from crates/miden-objects/src/block/block_inputs.rs rename to crates/miden-protocol/src/block/block_inputs.rs diff --git a/crates/miden-objects/src/block/block_number.rs b/crates/miden-protocol/src/block/block_number.rs similarity index 100% rename from crates/miden-objects/src/block/block_number.rs rename to crates/miden-protocol/src/block/block_number.rs diff --git a/crates/miden-objects/src/block/block_proof.rs b/crates/miden-protocol/src/block/block_proof.rs similarity index 100% rename from crates/miden-objects/src/block/block_proof.rs rename to crates/miden-protocol/src/block/block_proof.rs diff --git a/crates/miden-objects/src/block/blockchain.rs b/crates/miden-protocol/src/block/blockchain.rs similarity index 98% rename from crates/miden-objects/src/block/blockchain.rs rename to crates/miden-protocol/src/block/blockchain.rs index 58046a630a..a70159fbd0 100644 --- a/crates/miden-objects/src/block/blockchain.rs +++ b/crates/miden-protocol/src/block/blockchain.rs @@ -1,7 +1,7 @@ use alloc::collections::BTreeSet; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_crypto::merkle::{Forest, Mmr, MmrError, MmrPeaks, MmrProof, PartialMmr}; +use miden_crypto::merkle::mmr::{Forest, Mmr, MmrError, MmrPeaks, MmrProof, PartialMmr}; use miden_processor::DeserializationError; use crate::Word; diff --git a/crates/miden-objects/src/block/header.rs b/crates/miden-protocol/src/block/header.rs similarity index 100% rename from crates/miden-objects/src/block/header.rs rename to crates/miden-protocol/src/block/header.rs diff --git a/crates/miden-objects/src/block/mod.rs b/crates/miden-protocol/src/block/mod.rs similarity index 100% rename from crates/miden-objects/src/block/mod.rs rename to crates/miden-protocol/src/block/mod.rs diff --git a/crates/miden-objects/src/block/note_tree.rs b/crates/miden-protocol/src/block/note_tree.rs similarity index 97% rename from crates/miden-objects/src/block/note_tree.rs rename to crates/miden-protocol/src/block/note_tree.rs index 5e6ee6d215..0e919073f1 100644 --- a/crates/miden-objects/src/block/note_tree.rs +++ b/crates/miden-protocol/src/block/note_tree.rs @@ -3,7 +3,8 @@ use alloc::string::ToString; use miden_crypto::merkle::SparseMerklePath; use crate::batch::BatchNoteTree; -use crate::crypto::merkle::{LeafIndex, MerkleError, SimpleSmt}; +use crate::crypto::merkle::MerkleError; +use crate::crypto::merkle::smt::{LeafIndex, SimpleSmt}; use crate::note::{NoteId, NoteMetadata, compute_note_commitment}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; use crate::{ @@ -176,7 +177,7 @@ impl Deserializable for BlockNoteTree { #[cfg(test)] mod tests { - use miden_crypto::merkle::SimpleSmt; + use miden_crypto::merkle::smt::SimpleSmt; use miden_crypto::utils::{Deserializable, Serializable}; use super::BlockNoteTree; diff --git a/crates/miden-objects/src/block/nullifier_tree/backend.rs b/crates/miden-protocol/src/block/nullifier_tree/backend.rs similarity index 94% rename from crates/miden-objects/src/block/nullifier_tree/backend.rs rename to crates/miden-protocol/src/block/nullifier_tree/backend.rs index 8f7f037769..603258ea0a 100644 --- a/crates/miden-objects/src/block/nullifier_tree/backend.rs +++ b/crates/miden-protocol/src/block/nullifier_tree/backend.rs @@ -2,9 +2,10 @@ use alloc::boxed::Box; use super::{BlockNumber, Nullifier, NullifierBlock, NullifierTree, NullifierTreeError}; use crate::Word; +use crate::crypto::merkle::MerkleError; #[cfg(feature = "std")] -use crate::crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage}; -use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt, SmtProof}; +use crate::crypto::merkle::smt::{LargeSmt, LargeSmtError, SmtStorage}; +use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt, SmtProof}; // NULLIFIER TREE BACKEND // ================================================================================================ @@ -158,12 +159,7 @@ where } fn root(&self) -> Word { - // SAFETY: We expect here as storage errors are considered unrecoverable. This maintains - // API compatibility with the non-fallible Smt::root(). - // See issue #2010 for future improvements to error handling. LargeSmt::root(self) - .map_err(large_smt_error_to_merkle_error) - .expect("Storage I/O error accessing root") } } diff --git a/crates/miden-objects/src/block/nullifier_tree/mod.rs b/crates/miden-protocol/src/block/nullifier_tree/mod.rs similarity index 98% rename from crates/miden-objects/src/block/nullifier_tree/mod.rs rename to crates/miden-protocol/src/block/nullifier_tree/mod.rs index ee8226a6b8..812e3906c2 100644 --- a/crates/miden-objects/src/block/nullifier_tree/mod.rs +++ b/crates/miden-protocol/src/block/nullifier_tree/mod.rs @@ -2,7 +2,8 @@ use alloc::string::ToString; use alloc::vec::Vec; use crate::block::BlockNumber; -use crate::crypto::merkle::{MerkleError, MutationSet, SMT_DEPTH, Smt}; +use crate::crypto::merkle::MerkleError; +use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt}; use crate::errors::NullifierTreeError; use crate::note::Nullifier; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -412,7 +413,7 @@ mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_basic_operations() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; // Create test data let nullifier1 = Nullifier::dummy(1); @@ -452,7 +453,7 @@ mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_nullifier_already_spent() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let nullifier1 = Nullifier::dummy(1); @@ -476,7 +477,7 @@ mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_apply_mutations() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let nullifier1 = Nullifier::dummy(1); let nullifier2 = Nullifier::dummy(2); @@ -507,7 +508,7 @@ mod tests { #[cfg(feature = "std")] #[test] fn large_smt_backend_same_root_as_regular_smt() { - use miden_crypto::merkle::{LargeSmt, MemoryStorage}; + use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage}; let nullifier1 = Nullifier::dummy(1); let nullifier2 = Nullifier::dummy(2); diff --git a/crates/miden-objects/src/block/nullifier_tree/partial.rs b/crates/miden-protocol/src/block/nullifier_tree/partial.rs similarity index 98% rename from crates/miden-objects/src/block/nullifier_tree/partial.rs rename to crates/miden-protocol/src/block/nullifier_tree/partial.rs index e745096e90..0bcb90c80d 100644 --- a/crates/miden-objects/src/block/nullifier_tree/partial.rs +++ b/crates/miden-protocol/src/block/nullifier_tree/partial.rs @@ -1,7 +1,7 @@ use super::{NullifierBlock, NullifierWitness}; use crate::Word; use crate::block::BlockNumber; -use crate::crypto::merkle::PartialSmt; +use crate::crypto::merkle::smt::PartialSmt; use crate::errors::NullifierTreeError; use crate::note::Nullifier; @@ -110,7 +110,7 @@ impl PartialNullifierTree { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_crypto::merkle::Smt; + use miden_crypto::merkle::smt::Smt; use winter_rand_utils::rand_value; use super::*; diff --git a/crates/miden-objects/src/block/nullifier_tree/witness.rs b/crates/miden-protocol/src/block/nullifier_tree/witness.rs similarity index 96% rename from crates/miden-objects/src/block/nullifier_tree/witness.rs rename to crates/miden-protocol/src/block/nullifier_tree/witness.rs index 019e5763dc..1b7df6db38 100644 --- a/crates/miden-objects/src/block/nullifier_tree/witness.rs +++ b/crates/miden-protocol/src/block/nullifier_tree/witness.rs @@ -1,4 +1,4 @@ -use crate::crypto::merkle::SmtProof; +use crate::crypto::merkle::smt::SmtProof; use crate::utils::serde::{ ByteReader, ByteWriter, diff --git a/crates/miden-objects/src/block/proposed_block.rs b/crates/miden-protocol/src/block/proposed_block.rs similarity index 92% rename from crates/miden-objects/src/block/proposed_block.rs rename to crates/miden-protocol/src/block/proposed_block.rs index 7e4b6dbc62..800c4239a3 100644 --- a/crates/miden-objects/src/block/proposed_block.rs +++ b/crates/miden-protocol/src/block/proposed_block.rs @@ -16,6 +16,7 @@ use crate::block::block_inputs::BlockInputs; use crate::block::nullifier_tree::{NullifierWitness, PartialNullifierTree}; use crate::block::{ AccountUpdateWitness, + BlockBody, BlockHeader, BlockNoteIndex, BlockNoteTree, @@ -24,7 +25,13 @@ use crate::block::{ }; use crate::errors::ProposedBlockError; use crate::note::{NoteId, Nullifier}; -use crate::transaction::{InputNoteCommitment, OutputNote, PartialBlockchain, TransactionHeader}; +use crate::transaction::{ + InputNoteCommitment, + OutputNote, + PartialBlockchain, + TransactionHeader, + TransactionKernel, +}; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -442,6 +449,73 @@ impl ProposedBlock { // STATE MUTATORS // -------------------------------------------------------------------------------------------- + /// Builds a [`BlockHeader`] and [`BlockBody`] by computing the following from the state + /// updates encapsulated by the provided [`ProposedBlock`]: + /// - the account root; + /// - the nullifier root; + /// - the note root; + /// - the transaction commitment; and + /// - the chain commitment. + /// + /// The returned block header contains the same validator public key as the previous block, as + /// provided by the proposed block. + pub fn into_header_and_body(self) -> Result<(BlockHeader, BlockBody), ProposedBlockError> { + // Get fields from the proposed block before it is consumed. + let block_num = self.block_num(); + let timestamp = self.timestamp(); + let prev_block_header = self.prev_block_header().clone(); + + // Insert the state commitments of updated accounts into the account tree to compute its new + // root. + let new_account_root = self.compute_account_root()?; + + // Insert the created nullifiers into the nullifier tree to compute its new root. + let new_nullifier_root = self.compute_nullifier_root()?; + + // Compute the root of the block note tree. + let note_tree = self.compute_block_note_tree(); + let note_root = note_tree.root(); + + // Insert the previous block header into the block partial blockchain to get the new chain + // commitment. + // TODO: Consider avoiding the partial blockchain clone by constructing `BlockBody` from its + // raw parts, which does not require the partial blockchain. + let new_chain_commitment = self.compute_chain_commitment(); + + // Construct the block body from the proposed block. + let body = BlockBody::from(self); + + // Construct the header. + let tx_commitment = body.transaction_commitment(); + let prev_block_commitment = prev_block_header.commitment(); + + // For now we copy the parameters of the previous header, which means the parameters set on + // the genesis block will be passed through. Eventually, the contained base fees will be + // updated based on the demand in the currently proposed block. + let fee_parameters = prev_block_header.fee_parameters().clone(); + + // Currently undefined and reserved for future use. + // See miden-base/1155. + let version = 0; + let tx_kernel_commitment = TransactionKernel.to_commitment(); + let header = BlockHeader::new( + version, + prev_block_commitment, + block_num, + new_chain_commitment, + new_account_root, + new_nullifier_root, + note_root, + tx_commitment, + tx_kernel_commitment, + prev_block_header.validator_key().clone(), + fee_parameters, + timestamp, + ); + + Ok((header, body)) + } + /// Consumes self and returns the non-[`Copy`] parts of the block. #[allow(clippy::type_complexity)] pub fn into_parts( diff --git a/crates/miden-objects/src/block/proven_block.rs b/crates/miden-protocol/src/block/proven_block.rs similarity index 100% rename from crates/miden-objects/src/block/proven_block.rs rename to crates/miden-protocol/src/block/proven_block.rs diff --git a/crates/miden-objects/src/block/signer.rs b/crates/miden-protocol/src/block/signer.rs similarity index 100% rename from crates/miden-objects/src/block/signer.rs rename to crates/miden-protocol/src/block/signer.rs diff --git a/crates/miden-objects/src/constants.rs b/crates/miden-protocol/src/constants.rs similarity index 100% rename from crates/miden-objects/src/constants.rs rename to crates/miden-protocol/src/constants.rs diff --git a/crates/miden-lib/src/errors/masm_error.rs b/crates/miden-protocol/src/errors/masm_error.rs similarity index 90% rename from crates/miden-lib/src/errors/masm_error.rs rename to crates/miden-protocol/src/errors/masm_error.rs index 3f3606151d..43c55f8797 100644 --- a/crates/miden-lib/src/errors/masm_error.rs +++ b/crates/miden-protocol/src/errors/masm_error.rs @@ -1,6 +1,6 @@ use alloc::borrow::Cow; -use miden_objects::Felt; +use crate::Felt; /// A convenience wrapper around an error extracted from Miden Assembly source files. pub struct MasmError { @@ -27,7 +27,7 @@ impl MasmError { /// Returns the code of this error. pub fn code(&self) -> Felt { - miden_objects::assembly::mast::error_code_from_msg(&self.message) + crate::assembly::mast::error_code_from_msg(&self.message) } } diff --git a/crates/miden-objects/src/errors.rs b/crates/miden-protocol/src/errors/mod.rs similarity index 96% rename from crates/miden-objects/src/errors.rs rename to crates/miden-protocol/src/errors/mod.rs index 985335c7d6..2dfd04eaa2 100644 --- a/crates/miden-objects/src/errors.rs +++ b/crates/miden-protocol/src/errors/mod.rs @@ -5,9 +5,9 @@ use core::error::Error; use miden_assembly::Report; use miden_assembly::diagnostics::reporting::PrintDiagnostic; -use miden_core::Felt; use miden_core::mast::MastForestError; -use miden_crypto::merkle::MmrError; +use miden_core::{EventId, Felt}; +use miden_crypto::merkle::mmr::MmrError; use miden_crypto::utils::HexParseError; use miden_processor::DeserializationError; use thiserror::Error; @@ -34,7 +34,7 @@ use crate::asset::AssetVaultKey; use crate::batch::BatchId; use crate::block::BlockNumber; use crate::note::{NoteAssets, NoteExecutionHint, NoteTag, NoteType, Nullifier}; -use crate::transaction::TransactionId; +use crate::transaction::{TransactionEventId, TransactionId}; use crate::{ ACCOUNT_UPDATE_MAX_SIZE, MAX_ACCOUNTS_PER_BATCH, @@ -44,6 +44,21 @@ use crate::{ MAX_OUTPUT_NOTES_PER_TX, }; +#[cfg(any(feature = "testing", test))] +mod masm_error; +#[cfg(any(feature = "testing", test))] +pub use masm_error::MasmError; + +/// The errors from the MASM code of the transaction kernel. +#[cfg(any(feature = "testing", test))] +#[rustfmt::skip] +pub mod tx_kernel; + +/// The errors from the MASM code of the Miden protocol library. +#[cfg(any(feature = "testing", test))] +#[rustfmt::skip] +pub mod protocol; + // ACCOUNT COMPONENT TEMPLATE ERROR // ================================================================================================ @@ -706,6 +721,28 @@ pub enum TransactionOutputError { AccountUpdateCommitment(Box), } +// TRANSACTION EVENT PARSING ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum TransactionEventError { + #[error("event id {0} is not a valid transaction event")] + InvalidTransactionEvent(EventId, Option<&'static str>), + #[error("event id {0} is not a transaction kernel event")] + NotTransactionEvent(EventId, Option<&'static str>), + #[error("event id {0} can only be emitted from the root context")] + NotRootContext(TransactionEventId), +} + +// TRANSACTION TRACE PARSING ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum TransactionTraceParsingError { + #[error("trace id {0} is an unknown transaction kernel trace")] + UnknownTransactionTrace(u32), +} + // PROVEN TRANSACTION ERROR // ================================================================================================ diff --git a/crates/miden-protocol/src/errors/protocol.rs b/crates/miden-protocol/src/errors/protocol.rs new file mode 100644 index 0000000000..73b7085d33 --- /dev/null +++ b/crates/miden-protocol/src/errors/protocol.rs @@ -0,0 +1,37 @@ +use crate::errors::MasmError; + +// This file is generated by build.rs, do not modify manually. +// It is generated by extracting errors from the MASM files in the `./asm` directory. +// +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). + +// PROTOCOL LIB ERRORS +// ================================================================================================ + +/// Error Message: "the account ID must have storage mode public if the network flag is set" +pub const ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT: MasmError = MasmError::from_static_str("the account ID must have storage mode public if the network flag is set"); +/// Error Message: "least significant byte of the account ID suffix must be zero" +pub const ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO: MasmError = MasmError::from_static_str("least significant byte of the account ID suffix must be zero"); +/// Error Message: "most significant bit of the account ID suffix must be zero" +pub const ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO: MasmError = MasmError::from_static_str("most significant bit of the account ID suffix must be zero"); +/// Error Message: "unknown account storage mode in account ID" +pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE: MasmError = MasmError::from_static_str("unknown account storage mode in account ID"); +/// Error Message: "unknown version in account ID" +pub const ERR_ACCOUNT_ID_UNKNOWN_VERSION: MasmError = MasmError::from_static_str("unknown version in account ID"); + +/// Error Message: "fungible asset build operation called with amount that exceeds the maximum allowed asset amount" +pub const ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT: MasmError = MasmError::from_static_str("fungible asset build operation called with amount that exceeds the maximum allowed asset amount"); +/// Error Message: "failed to build the fungible asset because the provided faucet id is not from a fungible faucet" +pub const ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the fungible asset because the provided faucet id is not from a fungible faucet"); + +/// Error Message: "failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet" +pub const ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet"); + +/// Error Message: "note data does not match the commitment" +pub const ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT: MasmError = MasmError::from_static_str("note data does not match the commitment"); +/// Error Message: "the specified number of note inputs does not match the actual number" +pub const ERR_NOTE_INVALID_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("the specified number of note inputs does not match the actual number"); + +/// Error Message: "number of note inputs exceeded the maximum limit of 1024" +pub const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 1024"); diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-protocol/src/errors/tx_kernel.rs similarity index 92% rename from crates/miden-lib/src/errors/tx_kernel_errors.rs rename to crates/miden-protocol/src/errors/tx_kernel.rs index 530c59315a..8394873749 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-protocol/src/errors/tx_kernel.rs @@ -1,11 +1,10 @@ use crate::errors::MasmError; // This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `./asm` directory. // -// To add a new error, define a constant in masm of the pattern `const.ERR__...`. -// Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, -// Non-Fungible-Asset, ...). +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). // TX KERNEL ERRORS // ================================================================================================ @@ -65,6 +64,8 @@ pub const ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS: MasmError = MasmError::from_static /// Error Message: "storage slot with the provided name does not exist" pub const ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME: MasmError = MasmError::from_static_str("storage slot with the provided name does not exist"); +/// Error Message: "auth procedure has been called from outside the epilogue" +pub const ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT: MasmError = MasmError::from_static_str("auth procedure has been called from outside the epilogue"); /// Error Message: "executed transaction neither changed the account state, nor consumed any notes" pub const ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY: MasmError = MasmError::from_static_str("executed transaction neither changed the account state, nor consumed any notes"); /// Error Message: "nonce cannot be 0 after an account-creating transaction" @@ -98,10 +99,6 @@ pub const ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT: MasmError = MasmError::from_st /// Error Message: "maximum allowed number of foreign account to be loaded (64) was exceeded" pub const ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED: MasmError = MasmError::from_static_str("maximum allowed number of foreign account to be loaded (64) was exceeded"); -/// Error Message: "fungible asset build operation called with amount that exceeds the maximum allowed asset amount" -pub const ERR_FUNGIBLE_ASSET_AMOUNT_EXCEEDS_MAX_ALLOWED_AMOUNT: MasmError = MasmError::from_static_str("fungible asset build operation called with amount that exceeds the maximum allowed asset amount"); -/// Error Message: "distribute would cause the maximum supply to be exceeded" -pub const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED: MasmError = MasmError::from_static_str("distribute would cause the maximum supply to be exceeded"); /// Error Message: "the origin of the fungible asset is not this faucet" pub const ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN: MasmError = MasmError::from_static_str("the origin of the fungible asset is not this faucet"); /// Error Message: "malformed fungible asset: `ASSET[1]` must be 0" @@ -110,8 +107,6 @@ pub const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ONE_MUST_BE_ZERO: MasmError = MasmEr pub const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_TWO_AND_THREE_MUST_BE_FUNGIBLE_FAUCET_ID: MasmError = MasmError::from_static_str("malformed fungible asset: `ASSET[2]` and `ASSET[3]` must be a valid fungible faucet id"); /// Error Message: "malformed fungible asset: `ASSET[0]` exceeds the maximum allowed amount" pub const ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS: MasmError = MasmError::from_static_str("malformed fungible asset: `ASSET[0]` exceeds the maximum allowed amount"); -/// Error Message: "failed to build the fungible asset because the provided faucet id is not from a fungible faucet" -pub const ERR_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the fungible asset because the provided faucet id is not from a fungible faucet"); /// Error Message: "requested input note index should be less than the total number of input notes" pub const ERR_INPUT_NOTE_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("requested input note index should be less than the total number of input notes"); @@ -144,8 +139,6 @@ pub const ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN: MasmError = MasmError::fr pub const ERR_NON_FUNGIBLE_ASSET_FORMAT_ELEMENT_THREE_MUST_BE_FUNGIBLE_FAUCET_ID: MasmError = MasmError::from_static_str("malformed non-fungible asset: `ASSET[3]` is not a valid non-fungible faucet id"); /// Error Message: "malformed non-fungible asset: the most significant bit must be 0" pub const ERR_NON_FUNGIBLE_ASSET_FORMAT_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO: MasmError = MasmError::from_static_str("malformed non-fungible asset: the most significant bit must be 0"); -/// Error Message: "failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet" -pub const ERR_NON_FUNGIBLE_ASSET_PROVIDED_FAUCET_ID_IS_INVALID: MasmError = MasmError::from_static_str("failed to build the non-fungible asset because the provided faucet id is not from a non-fungible faucet"); /// Error Message: "failed to access note assets of active note because no note is currently being processed" pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ASSETS_WHILE_NO_NOTE_BEING_PROCESSED: MasmError = MasmError::from_static_str("failed to access note assets of active note because no note is currently being processed"); @@ -159,16 +152,12 @@ pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_RECIPIENT_WHILE_NO_NOTE_BEING_PROCESSE pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED: MasmError = MasmError::from_static_str("failed to access note script root of active note because no note is currently being processed"); /// Error Message: "failed to access note serial number of active note because no note is currently being processed" pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED: MasmError = MasmError::from_static_str("failed to access note serial number of active note because no note is currently being processed"); -/// Error Message: "note data does not match the commitment" -pub const ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT: MasmError = MasmError::from_static_str("note data does not match the commitment"); /// Error Message: "adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807" pub const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED: MasmError = MasmError::from_static_str("adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807"); /// Error Message: "failed to find note at the given index; index must be within [0, num_of_notes]" pub const ERR_NOTE_INVALID_INDEX: MasmError = MasmError::from_static_str("failed to find note at the given index; index must be within [0, num_of_notes]"); /// Error Message: "invalid note type for the given note tag prefix" pub const ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX: MasmError = MasmError::from_static_str("invalid note type for the given note tag prefix"); -/// Error Message: "the specified number of note inputs does not match the actual number" -pub const ERR_NOTE_INVALID_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("the specified number of note inputs does not match the actual number"); /// Error Message: "invalid note type" pub const ERR_NOTE_INVALID_TYPE: MasmError = MasmError::from_static_str("invalid note type"); /// Error Message: "number of assets in a note exceed 255" @@ -209,8 +198,6 @@ pub const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE: MasmE pub const ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT: MasmError = MasmError::from_static_str("reserved slot for non-fungible faucet is not a valid empty SMT"); /// Error Message: "failed to authenticate note inclusion in block" pub const ERR_PROLOGUE_NOTE_AUTHENTICATION_FAILED: MasmError = MasmError::from_static_str("failed to authenticate note inclusion in block"); -/// Error Message: "number of note inputs exceeded the maximum limit of 1024" -pub const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("number of note inputs exceeded the maximum limit of 1024"); /// Error Message: "number of input notes exceeds the kernel's maximum limit of 1024" pub const ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT: MasmError = MasmError::from_static_str("number of input notes exceeds the kernel's maximum limit of 1024"); /// Error Message: "number of note assets exceeds the maximum limit of 256" @@ -224,8 +211,6 @@ pub const ERR_PROLOGUE_PROVIDED_INPUT_ASSETS_INFO_DOES_NOT_MATCH_ITS_COMMITMENT: /// Error Message: "verification base fee must fit into a u32" pub const ERR_PROLOGUE_VERIFICATION_BASE_FEE_MUST_BE_U32: MasmError = MasmError::from_static_str("verification base fee must fit into a u32"); -/// Error Message: "failed to approve multisig transaction as it was already executed" -pub const ERR_TX_ALREADY_EXECUTED: MasmError = MasmError::from_static_str("failed to approve multisig transaction as it was already executed"); /// Error Message: "transaction expiration block delta must be within 0x1 and 0xFFFF" pub const ERR_TX_INVALID_EXPIRATION_DELTA: MasmError = MasmError::from_static_str("transaction expiration block delta must be within 0x1 and 0xFFFF"); /// Error Message: "number of output notes in the transaction exceeds the maximum limit of 1024" diff --git a/crates/miden-objects/src/lib.rs b/crates/miden-protocol/src/lib.rs similarity index 90% rename from crates/miden-objects/src/lib.rs rename to crates/miden-protocol/src/lib.rs index d1ed2d7faf..c84363f82a 100644 --- a/crates/miden-objects/src/lib.rs +++ b/crates/miden-protocol/src/lib.rs @@ -11,14 +11,15 @@ pub mod address; pub mod asset; pub mod batch; pub mod block; +pub mod errors; pub mod note; +mod protocol; pub mod transaction; #[cfg(any(feature = "testing", test))] pub mod testing; mod constants; -mod errors; // RE-EXPORTS // ================================================================================================ @@ -47,29 +48,33 @@ pub use errors::{ StorageMapError, StorageSlotNameError, TokenSymbolError, + TransactionEventError, TransactionInputError, TransactionOutputError, TransactionScriptError, + TransactionTraceParsingError, }; pub use miden_core::mast::{MastForest, MastNodeId}; pub use miden_core::prettier::PrettyPrint; pub use miden_core::{EMPTY_WORD, Felt, FieldElement, ONE, StarkField, WORD_SIZE, ZERO}; +pub use miden_core_lib::CoreLibrary; pub use miden_crypto::hash::rpo::Rpo256 as Hasher; pub use miden_crypto::word; pub use miden_crypto::word::{LexicographicWord, Word, WordError}; +pub use protocol::ProtocolLib; pub mod assembly { pub use miden_assembly::ast::{Module, ModuleKind, ProcedureName, QualifiedProcedureName}; pub use miden_assembly::debuginfo::SourceManagerSync; + pub use miden_assembly::library::LibraryExport; pub use miden_assembly::{ Assembler, DefaultSourceManager, KernelLibrary, Library, - LibraryNamespace, - LibraryPath, Parse, ParseOptions, + Path, SourceFile, SourceId, SourceManager, @@ -103,7 +108,7 @@ pub mod utils { pub mod vm { pub use miden_assembly_syntax::ast::{AttributeSet, QualifiedProcedureName}; pub use miden_core::sys_events::SystemEvent; - pub use miden_core::{AdviceMap, Program, ProgramInfo}; + pub use miden_core::{AdviceMap, EventId, Program, ProgramInfo}; pub use miden_mast_package::{ MastArtifact, Package, diff --git a/crates/miden-objects/src/note/assets.rs b/crates/miden-protocol/src/note/assets.rs similarity index 100% rename from crates/miden-objects/src/note/assets.rs rename to crates/miden-protocol/src/note/assets.rs diff --git a/crates/miden-objects/src/note/details.rs b/crates/miden-protocol/src/note/details.rs similarity index 100% rename from crates/miden-objects/src/note/details.rs rename to crates/miden-protocol/src/note/details.rs diff --git a/crates/miden-objects/src/note/execution_hint.rs b/crates/miden-protocol/src/note/execution_hint.rs similarity index 100% rename from crates/miden-objects/src/note/execution_hint.rs rename to crates/miden-protocol/src/note/execution_hint.rs diff --git a/crates/miden-objects/src/note/file.rs b/crates/miden-protocol/src/note/file.rs similarity index 100% rename from crates/miden-objects/src/note/file.rs rename to crates/miden-protocol/src/note/file.rs diff --git a/crates/miden-objects/src/note/header.rs b/crates/miden-protocol/src/note/header.rs similarity index 100% rename from crates/miden-objects/src/note/header.rs rename to crates/miden-protocol/src/note/header.rs diff --git a/crates/miden-objects/src/note/inputs.rs b/crates/miden-protocol/src/note/inputs.rs similarity index 100% rename from crates/miden-objects/src/note/inputs.rs rename to crates/miden-protocol/src/note/inputs.rs diff --git a/crates/miden-objects/src/note/location.rs b/crates/miden-protocol/src/note/location.rs similarity index 100% rename from crates/miden-objects/src/note/location.rs rename to crates/miden-protocol/src/note/location.rs diff --git a/crates/miden-objects/src/note/metadata.rs b/crates/miden-protocol/src/note/metadata.rs similarity index 100% rename from crates/miden-objects/src/note/metadata.rs rename to crates/miden-protocol/src/note/metadata.rs diff --git a/crates/miden-objects/src/note/mod.rs b/crates/miden-protocol/src/note/mod.rs similarity index 100% rename from crates/miden-objects/src/note/mod.rs rename to crates/miden-protocol/src/note/mod.rs diff --git a/crates/miden-objects/src/note/note_id.rs b/crates/miden-protocol/src/note/note_id.rs similarity index 100% rename from crates/miden-objects/src/note/note_id.rs rename to crates/miden-protocol/src/note/note_id.rs diff --git a/crates/miden-objects/src/note/note_tag.rs b/crates/miden-protocol/src/note/note_tag.rs similarity index 100% rename from crates/miden-objects/src/note/note_tag.rs rename to crates/miden-protocol/src/note/note_tag.rs diff --git a/crates/miden-objects/src/note/note_type.rs b/crates/miden-protocol/src/note/note_type.rs similarity index 100% rename from crates/miden-objects/src/note/note_type.rs rename to crates/miden-protocol/src/note/note_type.rs diff --git a/crates/miden-objects/src/note/nullifier.rs b/crates/miden-protocol/src/note/nullifier.rs similarity index 100% rename from crates/miden-objects/src/note/nullifier.rs rename to crates/miden-protocol/src/note/nullifier.rs diff --git a/crates/miden-objects/src/note/partial.rs b/crates/miden-protocol/src/note/partial.rs similarity index 100% rename from crates/miden-objects/src/note/partial.rs rename to crates/miden-protocol/src/note/partial.rs diff --git a/crates/miden-objects/src/note/recipient.rs b/crates/miden-protocol/src/note/recipient.rs similarity index 100% rename from crates/miden-objects/src/note/recipient.rs rename to crates/miden-protocol/src/note/recipient.rs diff --git a/crates/miden-objects/src/note/script.rs b/crates/miden-protocol/src/note/script.rs similarity index 100% rename from crates/miden-objects/src/note/script.rs rename to crates/miden-protocol/src/note/script.rs diff --git a/crates/miden-protocol/src/protocol.rs b/crates/miden-protocol/src/protocol.rs new file mode 100644 index 0000000000..8ba96c9392 --- /dev/null +++ b/crates/miden-protocol/src/protocol.rs @@ -0,0 +1,70 @@ +use alloc::sync::Arc; + +use crate::assembly::Library; +use crate::assembly::mast::MastForest; +use crate::utils::serde::Deserializable; +use crate::utils::sync::LazyLock; + +// CONSTANTS +// ================================================================================================ + +const PROTOCOL_LIB_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/assets/protocol.masl")); + +// PROTOCOL LIBRARY +// ================================================================================================ + +#[derive(Clone)] +pub struct ProtocolLib(Library); + +impl ProtocolLib { + /// Returns a reference to the [`MastForest`] of the inner [`Library`]. + pub fn mast_forest(&self) -> &Arc { + self.0.mast_forest() + } +} + +impl AsRef for ProtocolLib { + fn as_ref(&self) -> &Library { + &self.0 + } +} + +impl From for Library { + fn from(value: ProtocolLib) -> Self { + value.0 + } +} + +impl Default for ProtocolLib { + fn default() -> Self { + static PROTOCOL_LIB: LazyLock = LazyLock::new(|| { + let contents = Library::read_from_bytes(PROTOCOL_LIB_BYTES) + .expect("protocol lib masl should be well-formed"); + ProtocolLib(contents) + }); + PROTOCOL_LIB.clone() + } +} + +// TESTS +// ================================================================================================ + +// NOTE: Most protocol-related tests can be found in miden-testing. +#[cfg(all(test, feature = "std"))] +mod tests { + use super::ProtocolLib; + use crate::assembly::Path; + + #[test] + fn test_compile() { + let path = Path::new("::miden::protocol::active_account::get_id"); + let miden = ProtocolLib::default(); + let exists = miden.0.module_infos().any(|module| { + module + .procedures() + .any(|(_, proc)| module.path().join(&proc.name).as_path() == path) + }); + + assert!(exists); + } +} diff --git a/crates/miden-objects/src/testing/account.rs b/crates/miden-protocol/src/testing/account.rs similarity index 100% rename from crates/miden-objects/src/testing/account.rs rename to crates/miden-protocol/src/testing/account.rs diff --git a/crates/miden-objects/src/testing/account_code.rs b/crates/miden-protocol/src/testing/account_code.rs similarity index 96% rename from crates/miden-objects/src/testing/account_code.rs rename to crates/miden-protocol/src/testing/account_code.rs index 90e0afeb56..bd1df53194 100644 --- a/crates/miden-objects/src/testing/account_code.rs +++ b/crates/miden-protocol/src/testing/account_code.rs @@ -7,11 +7,11 @@ use crate::account::{AccountCode, AccountComponent, AccountType}; use crate::testing::noop_auth_component::NoopAuthComponent; pub const CODE: &str = " - export.foo + pub proc foo push.1.2 mul end - export.bar + pub proc bar push.1.2 add end "; diff --git a/crates/miden-objects/src/testing/account_id.rs b/crates/miden-protocol/src/testing/account_id.rs similarity index 98% rename from crates/miden-objects/src/testing/account_id.rs rename to crates/miden-protocol/src/testing/account_id.rs index b90d0cc679..6147ff8546 100644 --- a/crates/miden-objects/src/testing/account_id.rs +++ b/crates/miden-protocol/src/testing/account_id.rs @@ -145,8 +145,8 @@ pub const fn account_id( /// # Example /// /// ``` -/// # use miden_objects::account::{AccountType, AccountStorageMode, AccountId}; -/// # use miden_objects::testing::account_id::{AccountIdBuilder}; +/// # use miden_protocol::account::{AccountType, AccountStorageMode, AccountId}; +/// # use miden_protocol::testing::account_id::{AccountIdBuilder}; /// /// let mut rng = rand::rng(); /// diff --git a/crates/miden-objects/src/testing/add_component.rs b/crates/miden-protocol/src/testing/add_component.rs similarity index 97% rename from crates/miden-objects/src/testing/add_component.rs rename to crates/miden-protocol/src/testing/add_component.rs index eb4b79d79f..3415a70a16 100644 --- a/crates/miden-objects/src/testing/add_component.rs +++ b/crates/miden-protocol/src/testing/add_component.rs @@ -6,7 +6,7 @@ use crate::utils::sync::LazyLock; // ================================================================================================ const ADD_CODE: &str = " - export.add5 + pub proc add5 add.5 end "; diff --git a/crates/miden-objects/src/testing/asset.rs b/crates/miden-protocol/src/testing/asset.rs similarity index 100% rename from crates/miden-objects/src/testing/asset.rs rename to crates/miden-protocol/src/testing/asset.rs diff --git a/crates/miden-objects/src/testing/block.rs b/crates/miden-protocol/src/testing/block.rs similarity index 98% rename from crates/miden-objects/src/testing/block.rs rename to crates/miden-protocol/src/testing/block.rs index e67ef5b21f..3ef1a2c624 100644 --- a/crates/miden-objects/src/testing/block.rs +++ b/crates/miden-protocol/src/testing/block.rs @@ -1,4 +1,4 @@ -use miden_crypto::merkle::Smt; +use miden_crypto::merkle::smt::Smt; #[cfg(not(target_family = "wasm"))] use winter_rand_utils::rand_value; diff --git a/crates/miden-objects/src/testing/block_note_tree.rs b/crates/miden-protocol/src/testing/block_note_tree.rs similarity index 100% rename from crates/miden-objects/src/testing/block_note_tree.rs rename to crates/miden-protocol/src/testing/block_note_tree.rs diff --git a/crates/miden-objects/src/testing/constants.rs b/crates/miden-protocol/src/testing/constants.rs similarity index 100% rename from crates/miden-objects/src/testing/constants.rs rename to crates/miden-protocol/src/testing/constants.rs diff --git a/crates/miden-lib/src/testing/mock_util_lib.rs b/crates/miden-protocol/src/testing/mock_util_lib.rs similarity index 72% rename from crates/miden-lib/src/testing/mock_util_lib.rs rename to crates/miden-protocol/src/testing/mock_util_lib.rs index 7be47cb256..9337fde0f5 100644 --- a/crates/miden-lib/src/testing/mock_util_lib.rs +++ b/crates/miden-protocol/src/testing/mock_util_lib.rs @@ -1,14 +1,15 @@ -use miden_objects::assembly::Library; -use miden_objects::utils::sync::LazyLock; +use miden_assembly::diagnostics::NamedSource; -use crate::utils::CodeBuilder; +use crate::assembly::Library; +use crate::transaction::TransactionKernel; +use crate::utils::sync::LazyLock; const MOCK_UTIL_LIBRARY_CODE: &str = " - use.miden::output_note + use miden::protocol::output_note # Inputs: [] # Outputs: [note_idx] - export.create_random_note + pub proc create_random_note push.1.2.3.4 # = RECIPIENT push.1 # = NoteExecutionHint::Always push.2 # = NoteType::Private @@ -22,7 +23,7 @@ const MOCK_UTIL_LIBRARY_CODE: &str = " # Inputs: [ASSET] # Outputs: [] - export.create_random_note_with_asset + pub proc create_random_note_with_asset exec.create_random_note # => [note_idx, ASSET] @@ -35,10 +36,9 @@ const MOCK_UTIL_LIBRARY_CODE: &str = " "; static MOCK_UTIL_LIBRARY: LazyLock = LazyLock::new(|| { - CodeBuilder::new(false) - .compile_component_code("mock::util", MOCK_UTIL_LIBRARY_CODE) + TransactionKernel::assembler() + .assemble_library([NamedSource::new("mock::util", MOCK_UTIL_LIBRARY_CODE)]) .expect("mock util library should be valid") - .into_library() }); /// Returns the mock test [`Library`] under the `mock::util` namespace. diff --git a/crates/miden-objects/src/testing/mod.rs b/crates/miden-protocol/src/testing/mod.rs similarity index 92% rename from crates/miden-objects/src/testing/mod.rs rename to crates/miden-protocol/src/testing/mod.rs index ca107d73f3..9b04908a64 100644 --- a/crates/miden-objects/src/testing/mod.rs +++ b/crates/miden-protocol/src/testing/mod.rs @@ -6,6 +6,7 @@ pub mod asset; pub mod block; pub mod block_note_tree; pub mod constants; +pub mod mock_util_lib; pub mod noop_auth_component; pub mod note; pub mod partial_blockchain; diff --git a/crates/miden-objects/src/testing/noop_auth_component.rs b/crates/miden-protocol/src/testing/noop_auth_component.rs similarity index 97% rename from crates/miden-objects/src/testing/noop_auth_component.rs rename to crates/miden-protocol/src/testing/noop_auth_component.rs index 8ebc9fb800..35b7a79126 100644 --- a/crates/miden-objects/src/testing/noop_auth_component.rs +++ b/crates/miden-protocol/src/testing/noop_auth_component.rs @@ -6,7 +6,7 @@ use crate::utils::sync::LazyLock; // ================================================================================================ const NOOP_AUTH_CODE: &str = " - export.auth_noop + pub proc auth_noop push.0 drop end "; diff --git a/crates/miden-objects/src/testing/note.rs b/crates/miden-protocol/src/testing/note.rs similarity index 100% rename from crates/miden-objects/src/testing/note.rs rename to crates/miden-protocol/src/testing/note.rs diff --git a/crates/miden-objects/src/testing/partial_blockchain.rs b/crates/miden-protocol/src/testing/partial_blockchain.rs similarity index 100% rename from crates/miden-objects/src/testing/partial_blockchain.rs rename to crates/miden-protocol/src/testing/partial_blockchain.rs diff --git a/crates/miden-objects/src/testing/random_signer.rs b/crates/miden-protocol/src/testing/random_signer.rs similarity index 100% rename from crates/miden-objects/src/testing/random_signer.rs rename to crates/miden-protocol/src/testing/random_signer.rs diff --git a/crates/miden-objects/src/testing/slot_name.rs b/crates/miden-protocol/src/testing/slot_name.rs similarity index 100% rename from crates/miden-objects/src/testing/slot_name.rs rename to crates/miden-protocol/src/testing/slot_name.rs diff --git a/crates/miden-objects/src/testing/storage.rs b/crates/miden-protocol/src/testing/storage.rs similarity index 100% rename from crates/miden-objects/src/testing/storage.rs rename to crates/miden-protocol/src/testing/storage.rs diff --git a/crates/miden-objects/src/testing/tx.rs b/crates/miden-protocol/src/testing/tx.rs similarity index 100% rename from crates/miden-objects/src/testing/tx.rs rename to crates/miden-protocol/src/testing/tx.rs diff --git a/crates/miden-objects/src/transaction/executed_tx.rs b/crates/miden-protocol/src/transaction/executed_tx.rs similarity index 100% rename from crates/miden-objects/src/transaction/executed_tx.rs rename to crates/miden-protocol/src/transaction/executed_tx.rs diff --git a/crates/miden-objects/src/transaction/inputs/account.rs b/crates/miden-protocol/src/transaction/inputs/account.rs similarity index 98% rename from crates/miden-objects/src/transaction/inputs/account.rs rename to crates/miden-protocol/src/transaction/inputs/account.rs index 848f33ef64..f0c2296323 100644 --- a/crates/miden-objects/src/transaction/inputs/account.rs +++ b/crates/miden-protocol/src/transaction/inputs/account.rs @@ -2,7 +2,7 @@ use crate::Word; use crate::account::{AccountCode, AccountId, PartialAccount, PartialStorage}; use crate::asset::PartialVault; use crate::block::account_tree::AccountWitness; -use crate::crypto::merkle::{SmtProof, SmtProofError}; +use crate::crypto::merkle::smt::{SmtProof, SmtProofError}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; // ACCOUNT INPUTS diff --git a/crates/miden-objects/src/transaction/inputs/mod.rs b/crates/miden-protocol/src/transaction/inputs/mod.rs similarity index 100% rename from crates/miden-objects/src/transaction/inputs/mod.rs rename to crates/miden-protocol/src/transaction/inputs/mod.rs diff --git a/crates/miden-objects/src/transaction/inputs/notes.rs b/crates/miden-protocol/src/transaction/inputs/notes.rs similarity index 100% rename from crates/miden-objects/src/transaction/inputs/notes.rs rename to crates/miden-protocol/src/transaction/inputs/notes.rs diff --git a/crates/miden-lib/src/transaction/inputs.rs b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs similarity index 96% rename from crates/miden-lib/src/transaction/inputs.rs rename to crates/miden-protocol/src/transaction/kernel/advice_inputs.rs index d49641c942..c536f6e188 100644 --- a/crates/miden-lib/src/transaction/inputs.rs +++ b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs @@ -1,16 +1,22 @@ use alloc::vec::Vec; -use miden_objects::account::{AccountHeader, AccountId, PartialAccount}; -use miden_objects::asset::AssetWitness; -use miden_objects::block::account_tree::AccountWitness; -use miden_objects::crypto::SequentialCommit; -use miden_objects::crypto::merkle::{InnerNodeInfo, SmtProof}; -use miden_objects::transaction::{AccountInputs, InputNote, PartialBlockchain, TransactionInputs}; -use miden_objects::vm::AdviceInputs; -use miden_objects::{EMPTY_WORD, Felt, FieldElement, Word, ZERO}; use miden_processor::AdviceMutation; -use super::TransactionKernel; +use crate::account::{AccountHeader, AccountId, PartialAccount}; +use crate::asset::AssetWitness; +use crate::block::account_tree::AccountWitness; +use crate::crypto::SequentialCommit; +use crate::crypto::merkle::InnerNodeInfo; +use crate::crypto::merkle::smt::SmtProof; +use crate::transaction::{ + AccountInputs, + InputNote, + PartialBlockchain, + TransactionInputs, + TransactionKernel, +}; +use crate::vm::AdviceInputs; +use crate::{EMPTY_WORD, Felt, FieldElement, Word, ZERO}; // TRANSACTION ADVICE INPUTS // ================================================================================================ @@ -399,7 +405,7 @@ impl TransactionAdviceInputs { self.0.stack.extend(iter); } - /// Extends the [`MerkleStore`](miden_objects::crypto::merkle::MerkleStore) with the given + /// Extends the [`MerkleStore`](crate::crypto::merkle::MerkleStore) with the given /// nodes. fn extend_merkle_store(&mut self, iter: impl Iterator) { self.0.store.extend(iter); diff --git a/crates/miden-lib/src/transaction/memory.rs b/crates/miden-protocol/src/transaction/kernel/memory.rs similarity index 100% rename from crates/miden-lib/src/transaction/memory.rs rename to crates/miden-protocol/src/transaction/kernel/memory.rs diff --git a/crates/miden-lib/src/transaction/mod.rs b/crates/miden-protocol/src/transaction/kernel/mod.rs similarity index 90% rename from crates/miden-lib/src/transaction/mod.rs rename to crates/miden-protocol/src/transaction/kernel/mod.rs index 2c592597fb..d759c822e5 100644 --- a/crates/miden-lib/src/transaction/mod.rs +++ b/crates/miden-protocol/src/transaction/kernel/mod.rs @@ -2,44 +2,32 @@ use alloc::string::ToString; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::account::AccountId; +use miden_core_lib::CoreLibrary; + +use crate::account::{AccountHeader, AccountId}; #[cfg(any(feature = "testing", test))] -use miden_objects::assembly::Library; -use miden_objects::assembly::debuginfo::SourceManagerSync; -use miden_objects::assembly::{Assembler, DefaultSourceManager, KernelLibrary}; -use miden_objects::asset::FungibleAsset; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::SequentialCommit; -use miden_objects::transaction::{OutputNote, OutputNotes, TransactionInputs, TransactionOutputs}; -use miden_objects::utils::serde::Deserializable; -use miden_objects::utils::sync::LazyLock; -use miden_objects::vm::{AdviceInputs, Program, ProgramInfo, StackInputs, StackOutputs}; -use miden_objects::{Felt, Hasher, TransactionOutputError, Word}; -use miden_stdlib::StdLibrary; - -use super::MidenLib; +use crate::assembly::Library; +use crate::assembly::debuginfo::SourceManagerSync; +use crate::assembly::{Assembler, DefaultSourceManager, KernelLibrary}; +use crate::asset::FungibleAsset; +use crate::block::BlockNumber; +use crate::crypto::SequentialCommit; +use crate::protocol::ProtocolLib; +use crate::transaction::{OutputNote, OutputNotes, TransactionInputs, TransactionOutputs}; +use crate::utils::serde::Deserializable; +use crate::utils::sync::LazyLock; +use crate::vm::{AdviceInputs, Program, ProgramInfo, StackInputs, StackOutputs}; +use crate::{Felt, Hasher, TransactionOutputError, Word}; + +mod procedures; pub mod memory; +mod advice_inputs; mod tx_event_id; -pub use tx_event_id::{EventId, TransactionEventId}; - -mod inputs; -pub use inputs::TransactionAdviceInputs; - -mod outputs; -pub use outputs::{ - ACCOUNT_UPDATE_COMMITMENT_WORD_IDX, - EXPIRATION_BLOCK_ELEMENT_IDX, - FEE_ASSET_WORD_IDX, - OUTPUT_NOTES_COMMITMENT_WORD_IDX, - parse_final_account_header, -}; - -pub use crate::errors::{TransactionEventError, TransactionTraceParsingError}; -mod kernel_procedures; -use kernel_procedures::KERNEL_PROCEDURES; +pub use advice_inputs::TransactionAdviceInputs; +pub use tx_event_id::TransactionEventId; // CONSTANTS // ================================================================================================ @@ -78,7 +66,7 @@ impl TransactionKernel { // -------------------------------------------------------------------------------------------- /// Array of kernel procedures. - pub const PROCEDURES: &'static [Word] = &KERNEL_PROCEDURES; + pub const PROCEDURES: &'static [Word] = &procedures::KERNEL_PROCEDURES; // KERNEL SOURCE CODE // -------------------------------------------------------------------------------------------- @@ -141,21 +129,21 @@ impl TransactionKernel { // -------------------------------------------------------------------------------------------- /// Returns a new Miden assembler instantiated with the transaction kernel and loaded with the - /// Miden stdlib as well as with miden-lib. + /// core lib as well as with miden-lib. pub fn assembler() -> Assembler { Self::assembler_with_source_manager(Arc::new(DefaultSourceManager::default())) } /// Returns a new assembler instantiated with the transaction kernel and loaded with the - /// Miden stdlib as well as with miden-lib. + /// core lib as well as with miden-lib. pub fn assembler_with_source_manager(source_manager: Arc) -> Assembler { #[cfg(all(any(feature = "testing", test), feature = "std"))] source_manager_ext::load_masm_source_files(&source_manager); Assembler::with_kernel(source_manager, Self::kernel()) - .with_dynamic_library(StdLibrary::default()) + .with_dynamic_library(CoreLibrary::default()) .expect("failed to load std-lib") - .with_dynamic_library(MidenLib::default()) + .with_dynamic_library(ProtocolLib::default()) .expect("failed to load miden-lib") } @@ -272,19 +260,19 @@ impl TransactionKernel { stack: &StackOutputs, // FIXME TODO add an extension trait for this one ) -> Result<(Word, Word, FungibleAsset, BlockNumber), TransactionOutputError> { let output_notes_commitment = stack - .get_stack_word_be(OUTPUT_NOTES_COMMITMENT_WORD_IDX * 4) + .get_stack_word_be(TransactionOutputs::OUTPUT_NOTES_COMMITMENT_WORD_IDX * 4) .expect("output_notes_commitment (first word) missing"); let account_update_commitment = stack - .get_stack_word_be(ACCOUNT_UPDATE_COMMITMENT_WORD_IDX * 4) + .get_stack_word_be(TransactionOutputs::ACCOUNT_UPDATE_COMMITMENT_WORD_IDX * 4) .expect("account_update_commitment (second word) missing"); let fee = stack - .get_stack_word_be(FEE_ASSET_WORD_IDX * 4) + .get_stack_word_be(TransactionOutputs::FEE_ASSET_WORD_IDX * 4) .expect("fee_asset (third word) missing"); let expiration_block_num = stack - .get_stack_item(EXPIRATION_BLOCK_ELEMENT_IDX) + .get_stack_item(TransactionOutputs::EXPIRATION_BLOCK_ELEMENT_IDX) .expect("tx_expiration_block_num (element on index 12) missing"); let expiration_block_num = u32::try_from(expiration_block_num.as_int()) @@ -357,7 +345,7 @@ impl TransactionKernel { .get(&final_account_commitment) .ok_or(TransactionOutputError::FinalAccountCommitmentMissingInAdviceMap)?; - let account = parse_final_account_header(final_account_data) + let account = AccountHeader::try_from_elements(final_account_data) .map_err(TransactionOutputError::FinalAccountHeaderParseFailure)?; // validate output notes @@ -458,8 +446,8 @@ pub(crate) mod source_manager_ext { use std::vec::Vec; use std::{fs, io}; - use miden_objects::assembly::SourceManager; - use miden_objects::assembly::debuginfo::SourceManagerExt; + use crate::assembly::SourceManager; + use crate::assembly::debuginfo::SourceManagerExt; /// Loads all files with a .masm extension in the `asm` directory into the provided source /// manager. diff --git a/crates/miden-lib/src/transaction/kernel_procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs similarity index 77% rename from crates/miden-lib/src/transaction/kernel_procedures.rs rename to crates/miden-protocol/src/transaction/kernel/procedures.rs index 47cea98a15..b8e805da30 100644 --- a/crates/miden-lib/src/transaction/kernel_procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -1,6 +1,6 @@ // This file is generated by build.rs, do not modify -use miden_objects::{Word, word}; +use crate::{Word, word}; // KERNEL PROCEDURES // ================================================================================================ @@ -24,33 +24,33 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_compute_storage_commitment word!("0xaa54d5c070ad5e2a39c9b8b3a17aaa3c9e9387c35c59f72c965060ba91e4f748"), // account_get_item - word!("0x6e3dc7fd548bfd6c802b6799be6e996eff247ecb2f16834a9363eca1b1751607"), + word!("0x9a4c4b33fde23e9fbe4c78b3430a058cc7ef8b5e487de19fa77d9d52a934e903"), // account_get_initial_item - word!("0x4835f7023bcafd46f28ece1e5c879f0f99191f78e4026c5c0590c01edc8ab598"), + word!("0x182573f7527df3b1e0b7cd28a7a2cb722776654d90219792806923bffe9d74df"), // account_set_item - word!("0x07737e8ce29230526bb76b64f64da0e45739363e11b11cb746657adaa749b37c"), + word!("0x24c88b3ec4b67ff8f58fba5f5a01117347c39efe7ec4e3236d6d38bd238ad99b"), // account_get_map_item - word!("0xd816dc31953635af927407f3b1cd8f1ec6825522363a93c264cc36eaeb23d019"), + word!("0xba1f281fe8c8ef584bfc1615962cbde4573ac01173bb113a5c79bcecf4eb1c65"), // account_get_initial_map_item - word!("0x773b9197efb18287cc9f1009ea53dbd3945b79b7168717b6a56ff0292083d1ea"), + word!("0x7f00c7140a71d12d1162a9cf0143bdd64bbd7e8d63b115fc3a2b07338813f8ab"), // account_set_map_item - word!("0x5e8aafb3ad8a442e29c9850c372d391ffbfd5e55e90109d33e4017d7c2626e62"), + word!("0x77df8fd76b36f7750d968f138ae85ea880a6c0a3f21b6bfab88919a509c81880"), // account_get_initial_vault_root word!("0x46297d9ac95afd60c7ef1a065e024ad49aa4c019f6b3924191905449b244d4ec"), // account_get_vault_root word!("0x42a2bfb8eac4fce9bbf75ea15215b00729faeeaf7fff784692948d3f618a9bb7"), // account_add_asset - word!("0x222ae6f550bb09b675cd73acf2e8ab25e4b8f06afa3ef1a1f66d3372e843ff3e"), + word!("0xb0f56deca8a478de98114c0aaafd96732f9be5db6fa94c1d4e0cf71ed5958e53"), // account_remove_asset - word!("0xc927af9fb41cece5eeafa4cc05ad4d82ec7771e8bc94db677a357bfca1f92ca7"), + word!("0xe70870b0f7baca27f3c6311ed322159af037b2bb0c7e90c4ac5ac0b5feaabc8c"), // account_get_balance - word!("0x1ed792cc7775aa1ce2f32367a3d430561ec9bceb33f5bb222691c49a6bde8112"), + word!("0x6a5eb788fd2beec7555874f978a4dd2f2c4f5d8088cd33e148c61450e4510fe1"), // account_get_initial_balance - word!("0xdc1320d6f044c40d37e5e835a584b643d6e77e3fc1136f498815298e28c912b8"), + word!("0x2e0decbc35a10c15ba72c14ed3e32dc9d4a3866f66114c073b3fc7b127362b74"), // account_has_non_fungible_asset - word!("0xfaad11de0c026551df15231790c2364cc598e891444bf826da01b524b1a8ca8f"), + word!("0xffe57961158c8e5f8a3aaa773943ee208fac7ed4786a7c8b6fed04ba54f39111"), // account_compute_delta_commitment - word!("0xd7ced8e16079d9c775e7dfc6b9d4b7a946ccd973fcd0ccaa84095bf8e10561a2"), + word!("0x5152e37bf0e1b5e0673f129cc84eb97b1c4cfb85127002169e0a2d827145f872"), // account_get_num_procedures word!("0x53b5ec38b7841948762c258010e6e07ad93963bcaac2d83813f8edb6710dc720"), // account_get_procedure_root @@ -60,13 +60,13 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_procedure word!("0xb0b63fdd01af0bcb4aacb2412e934cdc7691308647152d416c7ae4fc909da076"), // faucet_mint_asset - word!("0xf44dbb74a478e3ac63eada9680c32d57890e481f7ade41382cbf2a98592320fe"), + word!("0x76fc1dd0518f83d5aad0fecdda238529e2e0222267eebcb5e8598abc1b202cd5"), // faucet_burn_asset - word!("0x522a0592e159ac3fa15bfc73bba8afd1b753fdb9f86a6526f3c02a25b42bacec"), + word!("0x024479e5265c131572464f5a41885d2fcdec8e29dcb9146900529665aed8e2e5"), // faucet_get_total_fungible_asset_issuance - word!("0x51cfb361598b5dd34ff93ae2c3788e7c1cd702e8831e2ea1d45dfc2ccd97876f"), + word!("0xa1d900ff530770c3e56876fedd1b8f9f5ca491e2198524a973148ae3df88c581"), // faucet_is_non_fungible_asset_issued - word!("0xb4e83658a2c6b89ce2562fed3a6dfa141f7ff00c3d36a89d0d06f70b6690f133"), + word!("0xff77f7a2fabbbba5ef996242963569a84c2ef4614d6e8178e56ab4ae284673c7"), // input_note_get_metadata word!("0x7ad3e94585e7a397ee27443c98b376ed8d4ba762122af6413fde9314c00a6219"), // input_note_get_assets_info @@ -104,7 +104,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // tx_get_block_timestamp word!("0x7903185b847517debb6c2072364e3e757b99ee623e97c2bd0a4661316c5c5418"), // tx_start_foreign_context - word!("0x923b99b2b14173d265dfdeda4375fb4195f1246a6c96b102b256bb1a6400d2e9"), + word!("0xa44d1bcc1c42ec52df448ebf9362aefc61223a374aa14cad6cf51d406c4b1c4c"), // tx_end_foreign_context word!("0xaa0018aa8da890b73511879487f65553753fb7df22de380dd84c11e6f77eec6f"), // tx_get_expiration_delta diff --git a/crates/miden-lib/src/transaction/tx_event_id.rs b/crates/miden-protocol/src/transaction/kernel/tx_event_id.rs similarity index 99% rename from crates/miden-lib/src/transaction/tx_event_id.rs rename to crates/miden-protocol/src/transaction/kernel/tx_event_id.rs index 1c7a3dcb58..da233ae3ef 100644 --- a/crates/miden-lib/src/transaction/tx_event_id.rs +++ b/crates/miden-protocol/src/transaction/kernel/tx_event_id.rs @@ -1,8 +1,8 @@ use core::fmt; -pub use miden_core::EventId; +use miden_core::EventId; -use super::TransactionEventError; +use crate::errors::TransactionEventError; // CONSTANTS // ================================================================================================ diff --git a/crates/miden-objects/src/transaction/mod.rs b/crates/miden-protocol/src/transaction/mod.rs similarity index 90% rename from crates/miden-objects/src/transaction/mod.rs rename to crates/miden-protocol/src/transaction/mod.rs index 916275abd2..feef2fc878 100644 --- a/crates/miden-objects/src/transaction/mod.rs +++ b/crates/miden-protocol/src/transaction/mod.rs @@ -5,6 +5,7 @@ use super::{Felt, Hasher, WORD_SIZE, Word, ZERO}; mod executed_tx; mod inputs; +mod kernel; mod ordered_transactions; mod outputs; mod partial_blockchain; @@ -16,6 +17,7 @@ mod tx_summary; pub use executed_tx::{ExecutedTransaction, TransactionMeasurements}; pub use inputs::{AccountInputs, InputNote, InputNotes, ToInputNoteCommitments, TransactionInputs}; +pub use kernel::{TransactionAdviceInputs, TransactionEventId, TransactionKernel, memory}; pub use ordered_transactions::OrderedTransactionHeaders; pub use outputs::{OutputNote, OutputNotes, TransactionOutputs}; pub use partial_blockchain::PartialBlockchain; diff --git a/crates/miden-objects/src/transaction/ordered_transactions.rs b/crates/miden-protocol/src/transaction/ordered_transactions.rs similarity index 100% rename from crates/miden-objects/src/transaction/ordered_transactions.rs rename to crates/miden-protocol/src/transaction/ordered_transactions.rs diff --git a/crates/miden-objects/src/transaction/outputs.rs b/crates/miden-protocol/src/transaction/outputs.rs similarity index 94% rename from crates/miden-objects/src/transaction/outputs.rs rename to crates/miden-protocol/src/transaction/outputs.rs index f45e313a54..410bbf5449 100644 --- a/crates/miden-objects/src/transaction/outputs.rs +++ b/crates/miden-protocol/src/transaction/outputs.rs @@ -43,6 +43,23 @@ pub struct TransactionOutputs { pub expiration_block_num: BlockNumber, } +impl TransactionOutputs { + // CONSTANTS + // -------------------------------------------------------------------------------------------- + + /// The index of the word at which the final account nonce is stored on the output stack. + pub const OUTPUT_NOTES_COMMITMENT_WORD_IDX: usize = 0; + + /// The index of the word at which the account update commitment is stored on the output stack. + pub const ACCOUNT_UPDATE_COMMITMENT_WORD_IDX: usize = 1; + + /// The index of the word at which the fee asset is stored on the output stack. + pub const FEE_ASSET_WORD_IDX: usize = 2; + + /// The index of the item at which the expiration block height is stored on the output stack. + pub const EXPIRATION_BLOCK_ELEMENT_IDX: usize = 12; +} + impl Serializable for TransactionOutputs { fn write_into(&self, target: &mut W) { self.account.write_into(target); diff --git a/crates/miden-objects/src/transaction/partial_blockchain.rs b/crates/miden-protocol/src/transaction/partial_blockchain.rs similarity index 99% rename from crates/miden-objects/src/transaction/partial_blockchain.rs rename to crates/miden-protocol/src/transaction/partial_blockchain.rs index 4fcf3d0d94..0d63027fe8 100644 --- a/crates/miden-objects/src/transaction/partial_blockchain.rs +++ b/crates/miden-protocol/src/transaction/partial_blockchain.rs @@ -4,7 +4,8 @@ use core::ops::RangeTo; use crate::PartialBlockchainError; use crate::block::{BlockHeader, BlockNumber}; -use crate::crypto::merkle::{InnerNodeInfo, MmrPeaks, PartialMmr}; +use crate::crypto::merkle::InnerNodeInfo; +use crate::crypto::merkle::mmr::{MmrPeaks, PartialMmr}; use crate::utils::serde::{Deserializable, Serializable}; // PARTIAL BLOCKCHAIN @@ -284,7 +285,7 @@ mod tests { use crate::alloc::vec::Vec; use crate::block::{BlockHeader, BlockNumber, FeeParameters}; use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey; - use crate::crypto::merkle::{Mmr, PartialMmr}; + use crate::crypto::merkle::mmr::{Mmr, PartialMmr}; use crate::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; use crate::{PartialBlockchainError, Word}; diff --git a/crates/miden-objects/src/transaction/proven_tx.rs b/crates/miden-protocol/src/transaction/proven_tx.rs similarity index 100% rename from crates/miden-objects/src/transaction/proven_tx.rs rename to crates/miden-protocol/src/transaction/proven_tx.rs diff --git a/crates/miden-objects/src/transaction/transaction_id.rs b/crates/miden-protocol/src/transaction/transaction_id.rs similarity index 100% rename from crates/miden-objects/src/transaction/transaction_id.rs rename to crates/miden-protocol/src/transaction/transaction_id.rs diff --git a/crates/miden-objects/src/transaction/tx_args.rs b/crates/miden-protocol/src/transaction/tx_args.rs similarity index 100% rename from crates/miden-objects/src/transaction/tx_args.rs rename to crates/miden-protocol/src/transaction/tx_args.rs diff --git a/crates/miden-objects/src/transaction/tx_header.rs b/crates/miden-protocol/src/transaction/tx_header.rs similarity index 100% rename from crates/miden-objects/src/transaction/tx_header.rs rename to crates/miden-protocol/src/transaction/tx_header.rs diff --git a/crates/miden-objects/src/transaction/tx_summary.rs b/crates/miden-protocol/src/transaction/tx_summary.rs similarity index 100% rename from crates/miden-objects/src/transaction/tx_summary.rs rename to crates/miden-protocol/src/transaction/tx_summary.rs diff --git a/crates/miden-lib/Cargo.toml b/crates/miden-standards/Cargo.toml similarity index 66% rename from crates/miden-lib/Cargo.toml rename to crates/miden-standards/Cargo.toml index 325f64deb1..a57fb2814e 100644 --- a/crates/miden-lib/Cargo.toml +++ b/crates/miden-standards/Cargo.toml @@ -1,12 +1,12 @@ [package] authors.workspace = true categories = ["no-std"] -description = "Standard library of the Miden protocol" +description = "Standards of the Miden protocol" edition.workspace = true homepage.workspace = true keywords = ["kernel", "miden", "transaction"] license.workspace = true -name = "miden-lib" +name = "miden-standards" readme = "README.md" repository.workspace = true rust-version.workspace = true @@ -15,17 +15,14 @@ version.workspace = true [lib] [features] -default = ["std"] -std = ["miden-assembly/std", "miden-objects/std", "miden-processor/std", "miden-stdlib/std"] -testing = ["dep:rand", "miden-objects/testing"] -with-debug-info = ["miden-stdlib/with-debug-info"] +default = ["std"] +std = ["miden-assembly/std", "miden-core-lib/std", "miden-processor/std", "miden-protocol/std"] +testing = ["dep:rand", "miden-assembly/testing", "miden-protocol/testing"] [dependencies] # Miden dependencies -miden-core = { workspace = true } -miden-objects = { workspace = true } miden-processor = { workspace = true } -miden-stdlib = { workspace = true } +miden-protocol = { workspace = true } # External dependencies rand = { optional = true, workspace = true } @@ -35,17 +32,18 @@ thiserror = { workspace = true } fs-err = { version = "3" } miden-assembly = { workspace = true } miden-core = { workspace = true } -miden-stdlib = { workspace = true } +miden-core-lib = { workspace = true } +miden-protocol = { workspace = true } regex = { version = "1.11" } walkdir = { version = "2.5" } [dev-dependencies] anyhow = "1.0" assert_matches = { workspace = true } -miden-objects = { features = ["testing"], workspace = true } miden-processor = { features = ["testing"], workspace = true } +miden-protocol = { features = ["testing"], workspace = true } # When building as a dev-dependency (e.g., `cargo test --workspace` or `cargo check --all-targets`), # enable the `testing` feature. This is a workaround for Cargo's lack of test-specific features. # See: https://github.com/rust-lang/cargo/issues/2911#issuecomment-1483256987 -miden-lib = { features = ["testing"], path = "." } +miden-standards = { features = ["testing"], path = "." } diff --git a/crates/miden-lib/README.md b/crates/miden-standards/README.md similarity index 61% rename from crates/miden-lib/README.md rename to crates/miden-standards/README.md index 918fa2c761..7cdd97adcb 100644 --- a/crates/miden-lib/README.md +++ b/crates/miden-standards/README.md @@ -1,6 +1,6 @@ -# Transaction kernel +# Miden Standards -This crate contains the code of the Miden protocol kernels and standardized smart contracts. +This crate contains the code of Miden's standardized smart contracts. ## Status diff --git a/crates/miden-lib/asm/account_components/basic_fungible_faucet.masm b/crates/miden-standards/asm/account_components/basic_fungible_faucet.masm similarity index 53% rename from crates/miden-lib/asm/account_components/basic_fungible_faucet.masm rename to crates/miden-standards/asm/account_components/basic_fungible_faucet.masm index f436d1b541..5d3b13a920 100644 --- a/crates/miden-lib/asm/account_components/basic_fungible_faucet.masm +++ b/crates/miden-standards/asm/account_components/basic_fungible_faucet.masm @@ -2,5 +2,5 @@ # # See the `BasicFungibleFaucet` Rust type's documentation for more details. -pub proc ::miden::contracts::faucets::basic_fungible::distribute -pub proc ::miden::contracts::faucets::basic_fungible::burn +pub use ::miden::standards::faucets::basic_fungible::distribute +pub use ::miden::standards::faucets::basic_fungible::burn diff --git a/crates/miden-lib/asm/account_components/basic_wallet.masm b/crates/miden-standards/asm/account_components/basic_wallet.masm similarity index 50% rename from crates/miden-lib/asm/account_components/basic_wallet.masm rename to crates/miden-standards/asm/account_components/basic_wallet.masm index 36fbc7d7b9..e6b613900e 100644 --- a/crates/miden-lib/asm/account_components/basic_wallet.masm +++ b/crates/miden-standards/asm/account_components/basic_wallet.masm @@ -2,5 +2,5 @@ # # See the `BasicWallet` Rust type's documentation for more details. -pub proc ::miden::contracts::wallets::basic::receive_asset -pub proc ::miden::contracts::wallets::basic::move_asset_to_note +pub use ::miden::standards::wallets::basic::receive_asset +pub use ::miden::standards::wallets::basic::move_asset_to_note diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak.masm similarity index 95% rename from crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm rename to crates/miden-standards/asm/account_components/ecdsa_k256_keccak.masm index aca5d51f93..e226eeee00 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak.masm +++ b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak.masm @@ -2,8 +2,8 @@ # # See the `AuthEcdsaK256Keccak` Rust type's documentation for more details. -use miden::auth::ecdsa_k256_keccak -use miden::active_account +use miden::standards::auth::ecdsa_k256_keccak +use miden::protocol::active_account type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } diff --git a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_acl.masm similarity index 95% rename from crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm rename to crates/miden-standards/asm/account_components/ecdsa_k256_keccak_acl.masm index ade3c9c9f8..173b811386 100644 --- a/crates/miden-lib/asm/account_components/ecdsa_k256_keccak_acl.masm +++ b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_acl.masm @@ -2,10 +2,10 @@ # # See the `AuthEcdsaK256KeccakAcl` Rust type's documentation for more details. -use miden::active_account -use miden::native_account -use miden::tx -use std::word +use miden::protocol::active_account +use miden::protocol::native_account +use miden::protocol::tx +use miden::core::word type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -35,7 +35,8 @@ const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::ecdsa_k256_kec #! Outputs: [pad(16)] #! #! Invocation: call -pub proc auth_tx_ecdsa_k256_keccak_acl.2(auth_args: BeWord) +@locals(2) +pub proc auth_tx_ecdsa_k256_keccak_acl(auth_args: BeWord) dropw # => [pad(16)] @@ -127,7 +128,7 @@ pub proc auth_tx_ecdsa_k256_keccak_acl.2(auth_args: BeWord) push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] - exec.::miden::auth::ecdsa_k256_keccak::authenticate_transaction + exec.::miden::standards::auth::ecdsa_k256_keccak::authenticate_transaction else # ------ Check if initial account commitment differs from current commitment ------ diff --git a/crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm similarity index 97% rename from crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm rename to crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm index abd148ab1e..935e1f3e61 100644 --- a/crates/miden-lib/asm/account_components/multisig_ecdsa_k256_keccak.masm +++ b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm @@ -2,9 +2,9 @@ # # See the `AuthEcdsaK256KeccakMultisig` Rust type's documentation for more details. -use miden::active_account -use miden::native_account -use miden::auth +use miden::protocol::active_account +use miden::protocol::native_account +use miden::standards::auth type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -151,7 +151,8 @@ end #! Locals: #! 0: new_num_of_approvers #! 1: init_num_of_approvers -pub proc update_signers_and_threshold.2(multisig_config_hash: BeWord) +@locals(2) +pub proc update_signers_and_threshold(multisig_config_hash: BeWord) adv.push_mapval # => [MULTISIG_CONFIG_HASH, pad(12)] @@ -238,7 +239,8 @@ end # #! Inputs: [default_threshold] #! Outputs: [transaction_threshold] -proc compute_transaction_threshold.1(default_threshold: u32) -> u32 +@locals(1) +proc compute_transaction_threshold(default_threshold: u32) -> u32 # 1. initialize transaction_threshold = 0 # 2. iterate through all account procedures # a. check if the procedure was called during the transaction @@ -353,7 +355,8 @@ end #! - the same transaction has already been executed (replay protection). #! #! Invocation: call -pub proc auth_tx_ecdsa_k256_keccak_multisig.1(salt: BeWord) +@locals(1) +pub proc auth_tx_ecdsa_k256_keccak_multisig(salt: BeWord) exec.native_account::incr_nonce drop # => [SALT] @@ -387,7 +390,7 @@ pub proc auth_tx_ecdsa_k256_keccak_multisig.1(salt: BeWord) push.APPROVER_PUBLIC_KEYS_SLOT[0..2] # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] - exec.::miden::auth::ecdsa_k256_keccak::verify_signatures + exec.::miden::standards::auth::ecdsa_k256_keccak::verify_signatures # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold] # ------ Checking threshold is >= num_verified_signatures ------ diff --git a/crates/miden-lib/asm/account_components/network_fungible_faucet.masm b/crates/miden-standards/asm/account_components/network_fungible_faucet.masm similarity index 53% rename from crates/miden-lib/asm/account_components/network_fungible_faucet.masm rename to crates/miden-standards/asm/account_components/network_fungible_faucet.masm index b88db104ce..604239c7fd 100644 --- a/crates/miden-lib/asm/account_components/network_fungible_faucet.masm +++ b/crates/miden-standards/asm/account_components/network_fungible_faucet.masm @@ -2,5 +2,5 @@ # # See the `NetworkFungibleFaucet` Rust type's documentation for more details. -export.::miden::contracts::faucets::network_fungible::distribute -export.::miden::contracts::faucets::network_fungible::burn +pub use ::miden::standards::faucets::network_fungible::distribute +pub use ::miden::standards::faucets::network_fungible::burn diff --git a/crates/miden-lib/asm/account_components/no_auth.masm b/crates/miden-standards/asm/account_components/no_auth.masm similarity index 93% rename from crates/miden-lib/asm/account_components/no_auth.masm rename to crates/miden-standards/asm/account_components/no_auth.masm index 7726b6c183..4ced08325f 100644 --- a/crates/miden-lib/asm/account_components/no_auth.masm +++ b/crates/miden-standards/asm/account_components/no_auth.masm @@ -1,6 +1,6 @@ -use miden::active_account -use miden::native_account -use std::word +use miden::protocol::active_account +use miden::protocol::native_account +use miden::core::word #! Increment the nonce only if the account commitment has changed #! diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512.masm b/crates/miden-standards/asm/account_components/rpo_falcon_512.masm similarity index 95% rename from crates/miden-lib/asm/account_components/rpo_falcon_512.masm rename to crates/miden-standards/asm/account_components/rpo_falcon_512.masm index 914e80e48b..7fd23fe2c2 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512.masm +++ b/crates/miden-standards/asm/account_components/rpo_falcon_512.masm @@ -2,8 +2,8 @@ # # See the `AuthRpoFalcon512` Rust type's documentation for more details. -use miden::auth::rpo_falcon512 -use miden::active_account +use miden::standards::auth::rpo_falcon512 +use miden::protocol::active_account type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } diff --git a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm b/crates/miden-standards/asm/account_components/rpo_falcon_512_acl.masm similarity index 95% rename from crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm rename to crates/miden-standards/asm/account_components/rpo_falcon_512_acl.masm index d4d7e42e07..b183a45c4a 100644 --- a/crates/miden-lib/asm/account_components/rpo_falcon_512_acl.masm +++ b/crates/miden-standards/asm/account_components/rpo_falcon_512_acl.masm @@ -2,10 +2,10 @@ # # See the `AuthRpoFalcon512Acl` Rust type's documentation for more details. -use miden::active_account -use miden::native_account -use miden::tx -use std::word +use miden::protocol::active_account +use miden::protocol::native_account +use miden::protocol::tx +use miden::core::word type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -35,7 +35,8 @@ const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::rpo_falcon512_ #! Outputs: [pad(16)] #! #! Invocation: call -pub proc auth_tx_rpo_falcon512_acl.2(auth_args: BeWord) +@locals(2) +pub proc auth_tx_rpo_falcon512_acl(auth_args: BeWord) dropw # => [pad(16)] @@ -127,7 +128,7 @@ pub proc auth_tx_rpo_falcon512_acl.2(auth_args: BeWord) push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] - exec.::miden::auth::rpo_falcon512::authenticate_transaction + exec.::miden::standards::auth::rpo_falcon512::authenticate_transaction else # ------ Check if initial account commitment differs from current commitment ------ diff --git a/crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm b/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm similarity index 97% rename from crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm rename to crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm index 0b5f037d52..681d176452 100644 --- a/crates/miden-lib/asm/account_components/multisig_rpo_falcon_512.masm +++ b/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm @@ -2,9 +2,9 @@ # # See the `AuthRpoFalcon512Multisig` Rust type's documentation for more details. -use miden::active_account -use miden::native_account -use miden::auth +use miden::protocol::active_account +use miden::protocol::native_account +use miden::standards::auth type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -151,7 +151,8 @@ end #! Locals: #! 0: new_num_of_approvers #! 1: init_num_of_approvers -pub proc update_signers_and_threshold.2(multisig_config_hash: BeWord) +@locals(2) +pub proc update_signers_and_threshold(multisig_config_hash: BeWord) adv.push_mapval # => [MULTISIG_CONFIG_HASH, pad(12)] @@ -238,7 +239,8 @@ end # #! Inputs: [default_threshold] #! Outputs: [transaction_threshold] -proc compute_transaction_threshold.1(default_threshold: u32) -> u32 +@locals(1) +proc compute_transaction_threshold(default_threshold: u32) -> u32 # 1. initialize transaction_threshold = 0 # 2. iterate through all account procedures # a. check if the procedure was called during the transaction @@ -353,7 +355,8 @@ end #! - the same transaction has already been executed (replay protection). #! #! Invocation: call -pub proc auth_tx_rpo_falcon512_multisig.1(salt: BeWord) +@locals(1) +pub proc auth_tx_rpo_falcon512_multisig(salt: BeWord) exec.native_account::incr_nonce drop # => [SALT] @@ -387,7 +390,7 @@ pub proc auth_tx_rpo_falcon512_multisig.1(salt: BeWord) push.APPROVER_PUBLIC_KEYS_SLOT[0..2] # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] - exec.::miden::auth::rpo_falcon512::verify_signatures + exec.::miden::standards::auth::rpo_falcon512::verify_signatures # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold] # ------ Checking threshold is >= num_verified_signatures ------ diff --git a/crates/miden-lib/asm/note_scripts/BURN.masm b/crates/miden-standards/asm/note_scripts/BURN.masm similarity index 96% rename from crates/miden-lib/asm/note_scripts/BURN.masm rename to crates/miden-standards/asm/note_scripts/BURN.masm index 42b68ed11b..9282d5249f 100644 --- a/crates/miden-lib/asm/note_scripts/BURN.masm +++ b/crates/miden-standards/asm/note_scripts/BURN.masm @@ -1,4 +1,4 @@ -use.miden::contracts::faucets +use miden::standards::faucets #! BURN script: burns the asset from the note by calling the faucet's burn procedure. #! This note can be executed against any faucet account that exposes the faucets::burn procedure diff --git a/crates/miden-lib/asm/note_scripts/MINT.masm b/crates/miden-standards/asm/note_scripts/MINT.masm similarity index 90% rename from crates/miden-lib/asm/note_scripts/MINT.masm rename to crates/miden-standards/asm/note_scripts/MINT.masm index 65f102cfa1..8d6281b9ee 100644 --- a/crates/miden-lib/asm/note_scripts/MINT.masm +++ b/crates/miden-standards/asm/note_scripts/MINT.masm @@ -1,22 +1,22 @@ -use.miden::active_note -use.miden::contracts::faucets::network_fungible->network_faucet -use.miden::note +use miden::protocol::active_note +use miden::protocol::note +use miden::standards::faucets::network_fungible->network_faucet # CONSTANTS # ================================================================================================= -const.MINT_NOTE_NUM_INPUTS_PRIVATE=8 -const.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 +const MINT_NOTE_NUM_INPUTS_PRIVATE=8 +const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 -const.OUTPUT_NOTE_TYPE_PUBLIC=1 -const.OUTPUT_NOTE_TYPE_PRIVATE=2 +const OUTPUT_NOTE_TYPE_PUBLIC=1 +const OUTPUT_NOTE_TYPE_PRIVATE=2 -const.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 -const.OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 +const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 +const OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 # ERRORS # ================================================================================================= -const.ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" +const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" #! Network Faucet MINT script: mints assets by calling the network faucet's distribute function. #! This note is intended to be executed against a network fungible faucet account. diff --git a/crates/miden-lib/asm/note_scripts/P2ID.masm b/crates/miden-standards/asm/note_scripts/P2ID.masm similarity index 83% rename from crates/miden-lib/asm/note_scripts/P2ID.masm rename to crates/miden-standards/asm/note_scripts/P2ID.masm index f5b565d223..6e0e455400 100644 --- a/crates/miden-lib/asm/note_scripts/P2ID.masm +++ b/crates/miden-standards/asm/note_scripts/P2ID.masm @@ -1,13 +1,14 @@ -use.miden::active_account -use.miden::account_id -use.miden::active_note +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::standards::wallets::basic->basic_wallet # ERRORS # ================================================================================================= -const.ERR_P2ID_WRONG_NUMBER_OF_INPUTS="P2ID note expects exactly 2 note inputs" +const ERR_P2ID_WRONG_NUMBER_OF_INPUTS="P2ID note expects exactly 2 note inputs" -const.ERR_P2ID_TARGET_ACCT_MISMATCH="P2ID's target account address and transaction address do not match" +const ERR_P2ID_TARGET_ACCT_MISMATCH="P2ID's target account address and transaction address do not match" #! Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account #! matches target account ID specified by the note inputs. @@ -47,6 +48,6 @@ begin exec.account_id::is_equal assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH # => [] - exec.active_note::add_assets_to_account + exec.basic_wallet::add_assets_to_account # => [] end diff --git a/crates/miden-lib/asm/note_scripts/P2IDE.masm b/crates/miden-standards/asm/note_scripts/P2IDE.masm similarity index 89% rename from crates/miden-lib/asm/note_scripts/P2IDE.masm rename to crates/miden-standards/asm/note_scripts/P2IDE.masm index 8e885cc02d..5a033ba0ef 100644 --- a/crates/miden-lib/asm/note_scripts/P2IDE.masm +++ b/crates/miden-standards/asm/note_scripts/P2IDE.masm @@ -1,20 +1,21 @@ -use.miden::active_account -use.miden::account_id -use.miden::active_note -use.miden::tx +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::protocol::tx +use miden::standards::wallets::basic->basic_wallet # ERRORS # ================================================================================================= -const.ERR_P2IDE_WRONG_NUMBER_OF_INPUTS="P2IDE note expects exactly 4 note inputs" +const ERR_P2IDE_WRONG_NUMBER_OF_INPUTS="P2IDE note expects exactly 4 note inputs" -const.ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER="failed to reclaim P2IDE note because the reclaiming account is not the sender" +const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER="failed to reclaim P2IDE note because the reclaiming account is not the sender" -const.ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED="failed to reclaim P2IDE note because the reclaim block height is not reached yet" +const ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED="failed to reclaim P2IDE note because the reclaim block height is not reached yet" -const.ERR_P2IDE_RECLAIM_DISABLED="P2IDE reclaim is disabled" +const ERR_P2IDE_RECLAIM_DISABLED="P2IDE reclaim is disabled" -const.ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note because the note is still timelocked" +const ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note because the note is still timelocked" # HELPER PROCEDURES # ================================================================================================= @@ -23,7 +24,7 @@ const.ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note becaus #! #! Inputs: [current_block_height, timelock_block_height] #! Outputs: [current_block_height] -proc.verify_unlocked +proc verify_unlocked dup movdn.2 # => [current_block_height, timelock_block_height, current_block_height] @@ -43,7 +44,7 @@ end #! - the reclaim of the active note is disabled. #! - the reclaim block height is not reached yet. #! - the account attempting to reclaim the note is not the sender account. -proc.reclaim_note +proc reclaim_note # check that the reclaim of the active note is enabled movup.3 dup neq.0 assert.err=ERR_P2IDE_RECLAIM_DISABLED # => [reclaim_block_height, account_id_prefix, account_id_suffix, current_block_height] @@ -64,7 +65,7 @@ proc.reclaim_note # => [] # add note assets to account - exec.active_note::add_assets_to_account + exec.basic_wallet::add_assets_to_account # => [] end @@ -130,7 +131,7 @@ begin if.true # we can safely consume the note since the active account is the target of the note - dropw exec.active_note::add_assets_to_account + dropw exec.basic_wallet::add_assets_to_account # => [] else diff --git a/crates/miden-lib/asm/note_scripts/SWAP.masm b/crates/miden-standards/asm/note_scripts/SWAP.masm similarity index 91% rename from crates/miden-lib/asm/note_scripts/SWAP.masm rename to crates/miden-standards/asm/note_scripts/SWAP.masm index 479e328593..4c1c2ee8f2 100644 --- a/crates/miden-lib/asm/note_scripts/SWAP.masm +++ b/crates/miden-standards/asm/note_scripts/SWAP.masm @@ -1,18 +1,18 @@ -use.miden::active_note -use.miden::output_note -use.miden::contracts::wallets::basic->wallet +use miden::protocol::active_note +use miden::protocol::output_note +use miden::standards::wallets::basic->wallet # CONSTANTS # ================================================================================================= -const.SWAP_NOTE_INPUTS_NUMBER=12 +const SWAP_NOTE_INPUTS_NUMBER=12 # ERRORS # ================================================================================================= -const.ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" +const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" -const.ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" +const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" #! Swap script: adds an asset from the note into consumers account and #! creates a note consumable by note issuer containing requested ASSET. diff --git a/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm b/crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm similarity index 92% rename from crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm rename to crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm index 3505028a21..17fd8d9e7a 100644 --- a/crates/miden-lib/asm/miden/auth/ecdsa_k256_keccak.masm +++ b/crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm @@ -1,21 +1,21 @@ -use.miden::active_account -use.miden::native_account -use.miden::auth -use.miden::tx -use.std::crypto::dsa::ecdsa::secp256k1 +use miden::core::crypto::dsa::ecdsa_k256_keccak +use miden::protocol::active_account +use miden::protocol::native_account +use miden::protocol::tx +use miden::standards::auth # CONSTANTS # ================================================================================================= # The event to request an authentication signature. -const.AUTH_REQUEST_EVENT=event("miden::auth::request") +const AUTH_REQUEST_EVENT=event("miden::auth::request") # Local Memory Addresses for multisig operations -const.NUM_OF_APPROVERS_LOC=0 -const.PUB_KEY_SLOT_SUFFIX_LOC=4 -const.PUB_KEY_SLOT_PREFIX_LOC=5 -const.CURRENT_PK_LOC=8 -const.SUCCESSFUL_VERIFICATIONS_LOC=12 +const NUM_OF_APPROVERS_LOC=0 +const PUB_KEY_SLOT_SUFFIX_LOC=4 +const PUB_KEY_SLOT_PREFIX_LOC=5 +const CURRENT_PK_LOC=8 +const SUCCESSFUL_VERIFICATIONS_LOC=12 #! Authenticate a transaction using the ECDSA signature scheme. #! @@ -32,7 +32,7 @@ const.SUCCESSFUL_VERIFICATIONS_LOC=12 #! Outputs: [] #! #! Invocation: exec -export.authenticate_transaction +pub proc authenticate_transaction # Increment the account's nonce. # --------------------------------------------------------------------------------------------- # This has to happen before computing the delta commitment, otherwise that procedure will abort @@ -63,7 +63,7 @@ export.authenticate_transaction # Verify the signature against the public key and the message. The procedure gets as inputs the # hash of the public key and the message via the operand stack. The signature is provided via # the advice stack. The signature is valid if and only if the procedure returns. - exec.secp256k1::verify_ecdsa_k256_keccak + exec.ecdsa_k256_keccak::verify # OS => [] # AS => [] end @@ -81,7 +81,8 @@ end #! #! Inputs: [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, MSG] #! Outputs: [num_verified_signatures, MSG] -export.verify_signatures.16 +@locals(16) +pub proc verify_signatures loc_store.PUB_KEY_SLOT_PREFIX_LOC loc_store.PUB_KEY_SLOT_SUFFIX_LOC # => [num_of_approvers, MSG] @@ -155,7 +156,7 @@ export.verify_signatures.16 # OS => [PUB_KEY, MSG, MSG, i-1] # AS => [SIGNATURE] - exec.secp256k1::verify_ecdsa_k256_keccak + exec.ecdsa_k256_keccak::verify # => [MSG, i-1] loc_load.SUCCESSFUL_VERIFICATIONS_LOC diff --git a/crates/miden-lib/asm/miden/auth/mod.masm b/crates/miden-standards/asm/standards/auth/mod.masm similarity index 92% rename from crates/miden-lib/asm/miden/auth/mod.masm rename to crates/miden-standards/asm/standards/auth/mod.masm index a62b6141cc..1c0575afcd 100644 --- a/crates/miden-lib/asm/miden/auth/mod.masm +++ b/crates/miden-standards/asm/standards/auth/mod.masm @@ -1,10 +1,11 @@ -use.miden::native_account -use.miden::tx -use.std::crypto::hashes::rpo +use miden::protocol::native_account +use miden::protocol::tx +use miden::core::crypto::hashes::rpo256 #! Inputs: [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT] #! Outputs: [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT] -export.adv_insert_hqword.16 +@locals(16) +pub proc adv_insert_hqword loc_storew_be.0 movdnw.3 loc_storew_be.4 @@ -46,7 +47,7 @@ end #! - OUTPUT_NOTES_COMMITMENT is the commitment to the transaction's output notes. #! - INPUT_NOTES_COMMITMENT is the commitment to the transaction's inputs notes. #! - ACCOUNT_DELTA_COMMITMENT is the commitment to the transaction's account delta. -export.create_tx_summary +pub proc create_tx_summary exec.native_account::compute_delta_commitment # => [ACCOUNT_DELTA_COMMITMENT, SALT] @@ -70,7 +71,7 @@ end #! - OUTPUT_NOTES_COMMITMENT is the commitment to the transaction's output notes. #! - INPUT_NOTES_COMMITMENT is the commitment to the transaction's inputs notes. #! - ACCOUNT_DELTA_COMMITMENT is the commitment to the transaction's account delta. -export.hash_tx_summary +pub proc hash_tx_summary swapdw # => [INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT, SALT, OUTPUT_NOTES_COMMITMENT] @@ -91,6 +92,6 @@ export.hash_tx_summary hperm # => [RATE, RATE, PERM] - exec.rpo::squeeze_digest + exec.rpo256::squeeze_digest # => [TX_SUMMARY_COMMITMENT] end diff --git a/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm b/crates/miden-standards/asm/standards/auth/rpo_falcon512.masm similarity index 92% rename from crates/miden-lib/asm/miden/auth/rpo_falcon512.masm rename to crates/miden-standards/asm/standards/auth/rpo_falcon512.masm index 16f4ef165b..4abb032699 100644 --- a/crates/miden-lib/asm/miden/auth/rpo_falcon512.masm +++ b/crates/miden-standards/asm/standards/auth/rpo_falcon512.masm @@ -1,21 +1,21 @@ -use.miden::active_account -use.miden::native_account -use.miden::auth -use.miden::tx -use.std::crypto::dsa::rpo_falcon512 +use miden::core::crypto::dsa::falcon512rpo +use miden::protocol::active_account +use miden::protocol::native_account +use miden::protocol::tx +use miden::standards::auth # CONSTANTS # ================================================================================================= # The event to request an authentication signature. -const.AUTH_REQUEST_EVENT=event("miden::auth::request") +const AUTH_REQUEST_EVENT=event("miden::auth::request") # Local Memory Addresses for multisig operations -const.NUM_OF_APPROVERS_LOC=0 -const.PUB_KEY_SLOT_SUFFIX_LOC=4 -const.PUB_KEY_SLOT_PREFIX_LOC=5 -const.CURRENT_PK_LOC=8 -const.SUCCESSFUL_VERIFICATIONS_LOC=12 +const NUM_OF_APPROVERS_LOC=0 +const PUB_KEY_SLOT_SUFFIX_LOC=4 +const PUB_KEY_SLOT_PREFIX_LOC=5 +const CURRENT_PK_LOC=8 +const SUCCESSFUL_VERIFICATIONS_LOC=12 #! Authenticate a transaction using the Falcon signature scheme. #! @@ -32,7 +32,7 @@ const.SUCCESSFUL_VERIFICATIONS_LOC=12 #! Outputs: [] #! #! Invocation: exec -export.authenticate_transaction +pub proc authenticate_transaction # Increment the account's nonce. # --------------------------------------------------------------------------------------------- # This has to happen before computing the delta commitment, otherwise that procedure will abort @@ -63,7 +63,7 @@ export.authenticate_transaction # Verify the signature against the public key and the message. The procedure gets as inputs the # hash of the public key and the message via the operand stack. The signature is provided via # the advice stack. The signature is valid if and only if the procedure returns. - exec.rpo_falcon512::verify + exec.falcon512rpo::verify # OS => [] # AS => [] end @@ -81,7 +81,8 @@ end #! #! Inputs: [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, MSG] #! Outputs: [num_verified_signatures, MSG] -export.verify_signatures.16 +@locals(16) +pub proc verify_signatures loc_store.PUB_KEY_SLOT_PREFIX_LOC loc_store.PUB_KEY_SLOT_SUFFIX_LOC # => [num_of_approvers, MSG] @@ -155,7 +156,7 @@ export.verify_signatures.16 # OS => [PUB_KEY, MSG, MSG, i-1] # AS => [SIGNATURE] - exec.rpo_falcon512::verify + exec.falcon512rpo::verify # => [MSG, i-1] loc_load.SUCCESSFUL_VERIFICATIONS_LOC diff --git a/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm b/crates/miden-standards/asm/standards/faucets/basic_fungible.masm similarity index 92% rename from crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm rename to crates/miden-standards/asm/standards/faucets/basic_fungible.masm index 9930eda19c..cc447eeb2e 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm +++ b/crates/miden-standards/asm/standards/faucets/basic_fungible.masm @@ -8,18 +8,18 @@ # - decimals are the decimals of the token. # - token_symbol as three chars encoded in a Felt. -use.miden::contracts::faucets +use miden::standards::faucets # CONSTANTS # ================================================================================================= -const.PRIVATE_NOTE=2 +const PRIVATE_NOTE=2 # ERRORS # ================================================================================================= -const.ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED="distribute would cause the maximum supply to be exceeded" +const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED="distribute would cause the maximum supply to be exceeded" -const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 note asset" +const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 note asset" # CONSTANTS # ================================================================================================= @@ -43,7 +43,7 @@ const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no #! - the total issuance after minting is greater than the maximum allowed supply. #! #! Invocation: call -export.distribute +pub proc distribute exec.faucets::distribute # => [pad(16)] end @@ -64,4 +64,4 @@ end #! - the transaction is executed against an account which is not a fungible asset faucet. #! - the transaction is executed against a faucet which is not the origin of the specified asset. #! - the amount about to be burned is greater than the outstanding supply of the asset. -export.faucets::burn +pub use faucets::burn diff --git a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm b/crates/miden-standards/asm/standards/faucets/mod.masm similarity index 91% rename from crates/miden-lib/asm/miden/contracts/faucets/mod.masm rename to crates/miden-standards/asm/standards/faucets/mod.masm index 45eb22f9fa..f5b2d3461e 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/mod.masm +++ b/crates/miden-standards/asm/standards/faucets/mod.masm @@ -1,24 +1,24 @@ -use.miden::active_account -use.miden::active_note -use.miden::faucet -use.miden::output_note +use miden::protocol::active_account +use miden::protocol::active_note +use miden::protocol::faucet +use miden::protocol::output_note # CONSTANTS # ================================================================================================= -const.PRIVATE_NOTE=2 +const PRIVATE_NOTE=2 # ERRORS # ================================================================================================= -const.ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED="distribute would cause the maximum supply to be exceeded" +const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED="distribute would cause the maximum supply to be exceeded" -const.ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 note asset" +const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 note asset" # CONSTANTS # ================================================================================================= # The standard slot where fungible faucet metadata like token symbol or decimals are stored. -const.METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") +const METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") #! Distributes freshly minted fungible assets to the provided recipient by creating a note. #! @@ -39,7 +39,7 @@ const.METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") #! - the total issuance after minting is greater than the maximum allowed supply. #! #! Invocation: exec -export.distribute +pub proc distribute # get max supply of this faucet. We assume it is stored at pos 3 of slot 0 push.METADATA_SLOT[0..2] exec.active_account::get_item drop drop drop # => [max_supply, amount, tag, aux, note_type, execution_hint, RECIPIENT] @@ -96,7 +96,7 @@ end #! - the amount about to be burned is greater than the outstanding supply of the asset. #! #! Invocation: call -export.burn +pub proc burn # Get the assets from the note. This will fail if not called from a note context. push.0 exec.active_note::get_assets # => [num_assets, dest_ptr, pad(16)] diff --git a/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm b/crates/miden-standards/asm/standards/faucets/network_fungible.masm similarity index 86% rename from crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm rename to crates/miden-standards/asm/standards/faucets/network_fungible.masm index 41e0e90db1..39395c628c 100644 --- a/crates/miden-lib/asm/miden/contracts/faucets/network_fungible.masm +++ b/crates/miden-standards/asm/standards/faucets/network_fungible.masm @@ -1,17 +1,17 @@ -use.miden::active_account -use.miden::account_id -use.miden::active_note -use.miden::contracts::faucets -use.miden::contracts::faucets::basic_fungible +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::standards::faucets +use miden::standards::faucets::basic_fungible # CONSTANTS # ================================================================================================ # The slot in this component's storage layout where the owner config is stored. -const.OWNER_CONFIG_SLOT=word("miden::network_fungible_faucet::owner_config") +const OWNER_CONFIG_SLOT=word("miden::standards::network_fungible_faucet::owner_config") # ERRORS -const.ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who can mint assets" +const ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who can mint assets" #! Checks if the note sender is the owner of this faucet. #! @@ -20,7 +20,7 @@ const.ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who ca #! #! Where: #! - is_owner is 1 if the sender is the owner, 0 otherwise. -proc.is_owner +proc is_owner push.OWNER_CONFIG_SLOT[0..2] exec.active_account::get_item # => [owner_prefix, owner_suffix, 0, 0] @@ -55,7 +55,7 @@ end #! - any of the validations in faucets::distribute fail. #! #! Invocation: call -export.distribute +pub proc distribute exec.is_owner # => [is_owner, amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] @@ -82,4 +82,4 @@ end #! - the amount about to be burned is greater than the outstanding supply of the asset. #! #! Invocation: call -export.faucets::burn +pub use faucets::burn diff --git a/crates/miden-standards/asm/standards/wallets/basic.masm b/crates/miden-standards/asm/standards/wallets/basic.masm new file mode 100644 index 0000000000..837803c09c --- /dev/null +++ b/crates/miden-standards/asm/standards/wallets/basic.masm @@ -0,0 +1,120 @@ +use miden::protocol::native_account +use miden::protocol::output_note +use miden::protocol::active_note + +# CONSTANTS +# ================================================================================================= +const PUBLIC_NOTE=1 + +#! Adds the provided asset to the active account. +#! +#! Inputs: [ASSET, pad(12)] +#! Outputs: [pad(16)] +#! +#! Where: +#! - ASSET is the asset to be received, can be fungible or non-fungible +#! +#! Panics if: +#! - the same non-fungible asset already exists in the account. +#! - adding a fungible asset would result in amount overflow, i.e., +#! the total amount would be greater than 2^63. +#! +#! Invocation: call +pub proc receive_asset + exec.native_account::add_asset + # => [ASSET', pad(12)] + + # drop the final asset + dropw + # => [pad(16)] +end + +#! Removes the specified asset from the account and adds it to the output note with the specified +#! index. +#! +#! This procedure is expected to be invoked using a `call` instruction. It makes no guarantees about +#! the contents of the `PAD` elements shown below. It is the caller's responsibility to make sure +#! these elements do not contain any meaningful data. +#! +#! Inputs: [ASSET, note_idx, pad(11)] +#! Outputs: [ASSET, note_idx, pad(11)] +#! +#! Where: +#! - note_idx is the index of the output note. +#! - ASSET is the fungible or non-fungible asset of interest. +#! +#! Panics if: +#! - the fungible asset is not found in the vault. +#! - the amount of the fungible asset in the vault is less than the amount to be removed. +#! - the non-fungible asset is not found in the vault. +#! +#! Invocation: call +pub proc move_asset_to_note + # remove the asset from the account + exec.native_account::remove_asset + # => [ASSET, note_idx, pad(11)] + + dupw dup.8 movdn.4 + # => [ASSET, note_idx, ASSET, note_idx, pad(11)] + + exec.output_note::add_asset + # => [ASSET, note_idx, pad(11)] +end + +#! Adds all assets from the active note to the native account's vault. +#! +#! Inputs: [] +#! Outputs: [] +@locals(1024) +pub proc add_assets_to_account + # write assets to local memory starting at offset 0 + # we have allocated 4 * MAX_ASSETS_PER_NOTE number of locals so all assets should fit + # since the asset memory will be overwritten, we don't have to initialize the locals to zero + locaddr.0 exec.active_note::get_assets + # => [num_of_assets, ptr = 0] + + # compute the pointer at which we should stop iterating + mul.4 dup.1 add + # => [end_ptr, ptr] + + # pad the stack and move the pointer to the top + padw movup.5 + # => [ptr, EMPTY_WORD, end_ptr] + + # loop if the amount of assets is non-zero + dup dup.6 neq + # => [should_loop, ptr, EMPTY_WORD, end_ptr] + + while.true + # => [ptr, EMPTY_WORD, end_ptr] + + # save the pointer so that we can use it later + dup movdn.5 + # => [ptr, EMPTY_WORD, ptr, end_ptr] + + # load the asset + mem_loadw_be + # => [ASSET, ptr, end_ptr] + + # pad the stack before call + padw swapw padw padw swapdw + # => [ASSET, pad(12), ptr, end_ptr] + + # add asset to the account + call.receive_asset + # => [pad(16), ptr, end_ptr] + + # clean the stack after call + dropw dropw dropw + # => [EMPTY_WORD, ptr, end_ptr] + + # increment the pointer and continue looping if ptr != end_ptr + movup.4 add.4 dup dup.6 neq + # => [should_loop, ptr+4, EMPTY_WORD, end_ptr] + end + # => [ptr', EMPTY_WORD, end_ptr] + + # clear the stack + drop dropw drop + # => [] +end diff --git a/crates/miden-standards/build.rs b/crates/miden-standards/build.rs new file mode 100644 index 0000000000..7f05079ddd --- /dev/null +++ b/crates/miden-standards/build.rs @@ -0,0 +1,487 @@ +use std::env; +use std::path::Path; + +use fs_err as fs; +use miden_assembly::diagnostics::{IntoDiagnostic, NamedSource, Result, WrapErr}; +use miden_assembly::utils::Serializable; +use miden_assembly::{Assembler, Library, Report}; +use miden_protocol::transaction::TransactionKernel; + +// CONSTANTS +// ================================================================================================ + +/// Defines whether the build script should generate files in `/src`. +/// The docs.rs build pipeline has a read-only filesystem, so we have to avoid writing to `src`, +/// otherwise the docs will fail to build there. Note that writing to `OUT_DIR` is fine. +const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN_SRC").is_some(); + +const ASSETS_DIR: &str = "assets"; +const ASM_DIR: &str = "asm"; +const ASM_STANDARDS_DIR: &str = "standards"; +const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts"; +const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components"; + +const STANDARDS_LIB_NAMESPACE: &str = "miden::standards"; + +const STANDARDS_ERRORS_FILE: &str = "src/errors/standards.rs"; +const STANDARDS_ERRORS_ARRAY_NAME: &str = "STANDARDS_ERRORS"; + +// PRE-PROCESSING +// ================================================================================================ + +/// Read and parse the contents from `./asm`. +/// - Compiles the contents of asm/standards directory into a Miden library file (.masl) under +/// standards namespace. +/// - Compiles the contents of asm/note_scripts directory into individual .masb files. +/// - Compiles the contents of asm/account_components directory into individual .masl files. +fn main() -> Result<()> { + // re-build when the MASM code changes + println!("cargo::rerun-if-changed={ASM_DIR}/"); + println!("cargo::rerun-if-env-changed=BUILD_GENERATED_FILES_IN_SRC"); + + // Copies the MASM code to the build directory + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let build_dir = env::var("OUT_DIR").unwrap(); + let src = Path::new(&crate_dir).join(ASM_DIR); + let dst = Path::new(&build_dir).to_path_buf(); + shared::copy_directory(src, &dst, ASM_DIR)?; + + // set source directory to {OUT_DIR}/asm + let source_dir = dst.join(ASM_DIR); + + // set target directory to {OUT_DIR}/assets + let target_dir = Path::new(&build_dir).join(ASSETS_DIR); + + // compile standards library + let standards_lib = + compile_standards_lib(&source_dir, &target_dir, TransactionKernel::assembler())?; + + let mut assembler = TransactionKernel::assembler(); + assembler.link_static_library(standards_lib)?; + + // compile note scripts + compile_note_scripts( + &source_dir.join(ASM_NOTE_SCRIPTS_DIR), + &target_dir.join(ASM_NOTE_SCRIPTS_DIR), + assembler.clone(), + )?; + + // compile account components + compile_account_components( + &source_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), + &target_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), + assembler, + )?; + + generate_error_constants(&source_dir)?; + + Ok(()) +} + +// COMPILE PROTOCOL LIB +// ================================================================================================ + +/// Reads the MASM files from "{source_dir}/standards" directory, compiles them into a Miden +/// assembly library, saves the library into "{target_dir}/standards.masl", and returns the compiled +/// library. +fn compile_standards_lib( + source_dir: &Path, + target_dir: &Path, + assembler: Assembler, +) -> Result { + let source_dir = source_dir.join(ASM_STANDARDS_DIR); + + let standards_lib = assembler.assemble_library_from_dir(source_dir, STANDARDS_LIB_NAMESPACE)?; + + let output_file = target_dir.join("standards").with_extension(Library::LIBRARY_EXTENSION); + standards_lib.write_to_file(output_file).into_diagnostic()?; + + Ok(standards_lib) +} + +// COMPILE EXECUTABLE MODULES +// ================================================================================================ + +/// Reads all MASM files from the "{source_dir}", complies each file individually into a MASB +/// file, and stores the compiled files into the "{target_dir}". +/// +/// The source files are expected to contain executable programs. +fn compile_note_scripts(source_dir: &Path, target_dir: &Path, assembler: Assembler) -> Result<()> { + fs::create_dir_all(target_dir) + .into_diagnostic() + .wrap_err("failed to create note_scripts directory")?; + + for masm_file_path in shared::get_masm_files(source_dir).unwrap() { + // read the MASM file, parse it, and serialize the parsed AST to bytes + let code = assembler.clone().assemble_program(masm_file_path.clone())?; + + let bytes = code.to_bytes(); + + let masm_file_name = masm_file_path + .file_name() + .expect("file name should exist") + .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); + + // write the binary MASB to the output dir + masb_file_path.set_extension("masb"); + fs::write(masb_file_path, bytes).unwrap(); + } + Ok(()) +} + +// COMPILE ACCOUNT COMPONENTS +// ================================================================================================ + +/// Compiles the account components in `source_dir` into MASL libraries and stores the compiled +/// files in `target_dir`. +fn compile_account_components( + source_dir: &Path, + target_dir: &Path, + assembler: Assembler, +) -> Result<()> { + if !target_dir.exists() { + fs::create_dir_all(target_dir).unwrap(); + } + + for masm_file_path in shared::get_masm_files(source_dir).unwrap() { + let component_name = masm_file_path + .file_stem() + .expect("masm file should have a file stem") + .to_str() + .expect("file stem should be valid UTF-8") + .to_owned(); + + let component_source_code = fs::read_to_string(masm_file_path) + .expect("reading the component's MASM source code should succeed"); + + let named_source = NamedSource::new(component_name.clone(), component_source_code); + + let component_library = assembler + .clone() + .assemble_library([named_source]) + .expect("library assembly should succeed"); + + let component_file_path = + target_dir.join(component_name).with_extension(Library::LIBRARY_EXTENSION); + component_library.write_to_file(component_file_path).into_diagnostic()?; + } + + Ok(()) +} + +// ERROR CONSTANTS FILE GENERATION +// ================================================================================================ + +/// Reads all MASM files from the `asm_source_dir` and extracts its error constants and their +/// associated error message and generates a Rust file for each category of errors. +/// For example: +/// +/// ```text +/// const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY="new account must have an empty vault" +/// ``` +/// +/// would generate a Rust file for transaction kernel errors (since the error belongs to that +/// category, identified by the category extracted from `ERR_`) with - roughly - the +/// following content: +/// +/// ```rust +/// pub const ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY: MasmError = +/// MasmError::from_static_str("new account must have an empty vault"); +/// ``` +/// +/// and add the constant to the error constants array. +/// +/// The function ensures that a constant is not defined twice, except if their error message is the +/// same. This can happen across multiple files. +/// +/// Because the error files will be written to ./src/errors, this should be a no-op if ./src is +/// read-only. To enable writing to ./src, set the `BUILD_GENERATED_FILES_IN_SRC` environment +/// variable. +fn generate_error_constants(asm_source_dir: &Path) -> Result<()> { + if !BUILD_GENERATED_FILES_IN_SRC { + return Ok(()); + } + + // Miden standards errors + // ------------------------------------------ + + let errors = shared::extract_all_masm_errors(asm_source_dir) + .context("failed to extract all masm errors")?; + shared::generate_error_file( + shared::ErrorModule { + file_name: STANDARDS_ERRORS_FILE, + array_name: STANDARDS_ERRORS_ARRAY_NAME, + is_crate_local: false, + }, + errors, + )?; + + Ok(()) +} + +/// This module should be kept in sync with the copy in miden-protocol's build.rs. +mod shared { + use std::collections::BTreeMap; + use std::fmt::Write; + use std::io::{self}; + use std::path::{Path, PathBuf}; + + use fs_err as fs; + use miden_assembly::Report; + use miden_assembly::diagnostics::{IntoDiagnostic, Result, WrapErr}; + use regex::Regex; + use walkdir::WalkDir; + + /// Recursively copies `src` into `dst`. + /// + /// This function will overwrite the existing files if re-executed. + pub fn copy_directory, R: AsRef>( + src: T, + dst: R, + asm_dir: &str, + ) -> Result<()> { + let mut prefix = src.as_ref().canonicalize().unwrap(); + // keep all the files inside the `asm` folder + prefix.pop(); + + let target_dir = dst.as_ref().join(asm_dir); + if target_dir.exists() { + // Clear existing asm files that were copied earlier which may no longer exist. + fs::remove_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to remove ASM directory")?; + } + + // Recreate the directory structure. + fs::create_dir_all(&target_dir) + .into_diagnostic() + .wrap_err("failed to create ASM directory")?; + + let dst = dst.as_ref(); + let mut todo = vec![src.as_ref().to_path_buf()]; + + while let Some(goal) = todo.pop() { + for entry in fs::read_dir(goal).unwrap() { + let path = entry.unwrap().path(); + if path.is_dir() { + let src_dir = path.canonicalize().unwrap(); + let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap()); + if !dst_dir.exists() { + fs::create_dir_all(&dst_dir).unwrap(); + } + todo.push(src_dir); + } else { + let dst_file = dst.join(path.strip_prefix(&prefix).unwrap()); + fs::copy(&path, dst_file).unwrap(); + } + } + } + + Ok(()) + } + + /// Returns a vector with paths to all MASM files in the specified directory. + /// + /// All non-MASM files are skipped. + pub fn get_masm_files>(dir_path: P) -> Result> { + let mut files = Vec::new(); + + let path = dir_path.as_ref(); + if path.is_dir() { + let entries = fs::read_dir(path) + .into_diagnostic() + .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; + for entry in entries { + let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; + let file_path = file.path(); + if is_masm_file(&file_path).into_diagnostic()? { + files.push(file_path); + } + } + } else { + println!("cargo:warn=The specified path is not a directory."); + } + + Ok(files) + } + + /// Returns true if the provided path resolves to a file with `.masm` extension. + /// + /// # Errors + /// Returns an error if the path could not be converted to a UTF-8 string. + pub fn is_masm_file(path: &Path) -> io::Result { + if let Some(extension) = path.extension() { + let extension = extension + .to_str() + .ok_or_else(|| io::Error::other("invalid UTF-8 filename"))? + .to_lowercase(); + Ok(extension == "masm") + } else { + Ok(false) + } + } + + /// Extract all masm errors from the given path and returns a map by error category. + pub fn extract_all_masm_errors(asm_source_dir: &Path) -> Result> { + // We use a BTree here to order the errors by their categories which is the first part after + // the ERR_ prefix and to allow for the same error to be defined multiple times in + // different files (as long as the constant name and error messages match). + let mut errors = BTreeMap::new(); + + // Walk all files of the kernel source directory. + for entry in WalkDir::new(asm_source_dir) { + let entry = entry.into_diagnostic()?; + if !is_masm_file(entry.path()).into_diagnostic()? { + continue; + } + let file_contents = std::fs::read_to_string(entry.path()).into_diagnostic()?; + extract_masm_errors(&mut errors, &file_contents)?; + } + + let errors = errors + .into_iter() + .map(|(error_name, error)| NamedError { name: error_name, message: error.message }) + .collect(); + + Ok(errors) + } + + /// Extracts the errors from a single masm file and inserts them into the provided map. + pub fn extract_masm_errors( + errors: &mut BTreeMap, + file_contents: &str, + ) -> Result<()> { + let regex = Regex::new(r#"const\s*ERR_(?.*)\s*=\s*"(?.*)""#).unwrap(); + + for capture in regex.captures_iter(file_contents) { + let error_name = capture + .name("name") + .expect("error name should be captured") + .as_str() + .trim() + .to_owned(); + let error_message = capture + .name("message") + .expect("error code should be captured") + .as_str() + .trim() + .to_owned(); + + if let Some(ExtractedError { message: existing_error_message, .. }) = + errors.get(&error_name) + && existing_error_message != &error_message + { + return Err(Report::msg(format!( + "Transaction kernel error constant ERR_{error_name} is already defined elsewhere but its error message is different" + ))); + } + + // Enforce the "no trailing punctuation" rule from the Rust error guidelines on MASM + // errors. + if error_message.ends_with(".") { + return Err(Report::msg(format!( + "Error messages should not end with a period: `ERR_{error_name}: {error_message}`" + ))); + } + + errors.insert(error_name, ExtractedError { message: error_message }); + } + + Ok(()) + } + + pub fn is_new_error_category<'a>( + last_error: &mut Option<&'a str>, + current_error: &'a str, + ) -> bool { + let is_new = match last_error { + Some(last_err) => { + let last_category = + last_err.split("_").next().expect("there should be at least one entry"); + let new_category = + current_error.split("_").next().expect("there should be at least one entry"); + last_category != new_category + }, + None => false, + }; + + last_error.replace(current_error); + + is_new + } + + /// Generates the content of an error file for the given category and the set of errors and + /// writes it to the category's file. + pub fn generate_error_file(module: ErrorModule, errors: Vec) -> Result<()> { + let mut output = String::new(); + + if module.is_crate_local { + writeln!(output, "use crate::errors::MasmError;\n").unwrap(); + } else { + writeln!(output, "use miden_protocol::errors::MasmError;\n").unwrap(); + } + + writeln!( + output, + "// This file is generated by build.rs, do not modify manually. +// It is generated by extracting errors from the MASM files in the `./asm` directory. +// +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). +" + ) + .unwrap(); + + writeln!( + output, + "// {} +// ================================================================================================ +", + module.array_name.replace("_", " ") + ) + .unwrap(); + + let mut last_error = None; + for named_error in errors.iter() { + let NamedError { name, message } = named_error; + + // Group errors into blocks separate by newlines. + if is_new_error_category(&mut last_error, name) { + writeln!(output).into_diagnostic()?; + } + + writeln!(output, "/// Error Message: \"{message}\"").into_diagnostic()?; + writeln!( + output, + r#"pub const ERR_{name}: MasmError = MasmError::from_static_str("{message}");"# + ) + .into_diagnostic()?; + } + + std::fs::write(module.file_name, output).into_diagnostic()?; + + Ok(()) + } + + pub type ErrorName = String; + + #[derive(Debug, Clone)] + pub struct ExtractedError { + pub message: String, + } + + #[derive(Debug, Clone)] + pub struct NamedError { + pub name: ErrorName, + pub message: String, + } + + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + pub struct ErrorModule { + pub file_name: &'static str, + pub array_name: &'static str, + pub is_crate_local: bool, + } +} diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs similarity index 62% rename from crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs rename to crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs index 00c8078cc4..a187b57729 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs @@ -1,6 +1,6 @@ -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageSlot, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountComponent, StorageSlot, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; use crate::account::components::ecdsa_k256_keccak_library; @@ -12,16 +12,18 @@ static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing the ECDSA K256 Keccak signature scheme for authentication /// of transactions. /// -/// It reexports the procedures from `miden::contracts::auth::basic`. When linking against this -/// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures -/// of this component are: -/// - `auth_tx_ecdsa_k256_keccak`, which can be used to verify a signature provided via the advice -/// stack to authenticate a transaction. +/// It reexports the procedures from `miden::contracts::auth::ecdsa_k256_keccak`. When linking +/// against this component, the `miden` library (i.e. +/// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the +/// case when using [`CodeBuilder`][builder]. The procedures of this component are: +/// - `verify_signatures`, which can be used to verify a signature provided via the advice stack to +/// authenticate a transaction. +/// - `authenticate_transaction`, which can be used to authenticate a transaction using the ECDSA +/// signature scheme. /// /// This component supports all account types. /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct AuthEcdsaK256Keccak { pub_key: PublicKeyCommitment, } diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs similarity index 98% rename from crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs rename to crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs index 834222c7f6..19f60867d9 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_acl.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs @@ -1,15 +1,15 @@ use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{ +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{ AccountCode, AccountComponent, StorageMap, StorageSlot, StorageSlotName, }; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Word}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_acl_library; @@ -220,8 +220,8 @@ impl From for AccountComponent { #[cfg(test)] mod tests { - use miden_objects::Word; - use miden_objects::account::AccountBuilder; + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::components::WellKnownComponent; diff --git a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs similarity index 97% rename from crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs rename to crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs index 233a0b1006..1bb3f9163f 100644 --- a/crates/miden-lib/src/account/auth/ecdsa_k256_keccak_multisig.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs @@ -1,10 +1,10 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Word}; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_multisig_library; @@ -205,8 +205,8 @@ impl From for AccountComponent { mod tests { use alloc::string::ToString; - use miden_objects::Word; - use miden_objects::account::AccountBuilder; + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::wallets::BasicWallet; diff --git a/crates/miden-lib/src/account/auth/mod.rs b/crates/miden-standards/src/account/auth/mod.rs similarity index 100% rename from crates/miden-lib/src/account/auth/mod.rs rename to crates/miden-standards/src/account/auth/mod.rs diff --git a/crates/miden-lib/src/account/auth/no_auth.rs b/crates/miden-standards/src/account/auth/no_auth.rs similarity index 94% rename from crates/miden-lib/src/account/auth/no_auth.rs rename to crates/miden-standards/src/account/auth/no_auth.rs index 21d9d2fb7e..1424ecbecb 100644 --- a/crates/miden-lib/src/account/auth/no_auth.rs +++ b/crates/miden-standards/src/account/auth/no_auth.rs @@ -1,4 +1,4 @@ -use miden_objects::account::AccountComponent; +use miden_protocol::account::AccountComponent; use crate::account::components::no_auth_library; @@ -41,7 +41,7 @@ impl From for AccountComponent { #[cfg(test)] mod tests { - use miden_objects::account::AccountBuilder; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::wallets::BasicWallet; diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512.rs similarity index 63% rename from crates/miden-lib/src/account/auth/rpo_falcon_512.rs rename to crates/miden-standards/src/account/auth/rpo_falcon_512.rs index 9d0795915d..305f5c7c46 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512.rs @@ -1,6 +1,6 @@ -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageSlot, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountComponent, StorageSlot, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; use crate::account::components::rpo_falcon_512_library; @@ -12,12 +12,14 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing the RpoFalcon512 signature scheme for authentication of /// transactions. /// -/// It reexports the procedures from `miden::contracts::auth::basic`. When linking against this -/// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures -/// of this component are: -/// - `auth_tx_rpo_falcon512`, which can be used to verify a signature provided via the advice stack -/// to authenticate a transaction. +/// It reexports the procedures from `miden::contracts::auth::rpo_falcon512`. When linking against +/// this component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must +/// be available to the assembler which is the case when using [`CodeBuilder`][builder]. The +/// procedures of this component are: +/// - `verify_signatures`, which can be used to verify a signature provided via the advice stack to +/// authenticate a transaction. +/// - `authenticate_transaction`, which can be used to authenticate a transaction using the Falcon +/// signature scheme. /// /// This component supports all account types. /// @@ -25,7 +27,7 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// /// - [`Self::public_key_slot`]: Public key /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct AuthRpoFalcon512 { pub_key: PublicKeyCommitment, } diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs similarity index 98% rename from crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs rename to crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs index 10c27a140f..16845d0db8 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_acl.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs @@ -1,15 +1,15 @@ use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{ +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{ AccountCode, AccountComponent, StorageMap, StorageSlot, StorageSlotName, }; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Word}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Word}; use crate::account::components::rpo_falcon_512_acl_library; @@ -221,8 +221,8 @@ impl From for AccountComponent { #[cfg(test)] mod tests { - use miden_objects::Word; - use miden_objects::account::AccountBuilder; + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::components::WellKnownComponent; diff --git a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs similarity index 97% rename from crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs rename to crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs index ac484892f7..d169d35586 100644 --- a/crates/miden-lib/src/account/auth/rpo_falcon_512_multisig.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs @@ -1,10 +1,10 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Word}; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Word}; use crate::account::components::rpo_falcon_512_multisig_library; @@ -205,8 +205,8 @@ impl From for AccountComponent { mod tests { use alloc::string::ToString; - use miden_objects::Word; - use miden_objects::account::AccountBuilder; + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; use super::*; use crate::account::wallets::BasicWallet; diff --git a/crates/miden-lib/src/account/components/mod.rs b/crates/miden-standards/src/account/components/mod.rs similarity index 93% rename from crates/miden-lib/src/account/components/mod.rs rename to crates/miden-standards/src/account/components/mod.rs index b16d1984b9..e97dc8b4df 100644 --- a/crates/miden-lib/src/account/components/mod.rs +++ b/crates/miden-standards/src/account/components/mod.rs @@ -1,12 +1,12 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::AccountProcedureRoot; -use miden_objects::assembly::Library; -use miden_objects::utils::Deserializable; -use miden_objects::utils::sync::LazyLock; use miden_processor::MastNodeExt; +use miden_protocol::Word; +use miden_protocol::account::AccountProcedureRoot; +use miden_protocol::assembly::{Library, LibraryExport}; +use miden_protocol::utils::Deserializable; +use miden_protocol::utils::sync::LazyLock; use crate::account::interface::AccountComponentInterface; @@ -39,7 +39,7 @@ static ECDSA_K256_KECCAK_ACL_LIBRARY: LazyLock = LazyLock::new(|| { static ECDSA_K256_KECCAK_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/multisig_ecdsa_k256_keccak.masl" + "/assets/account_components/ecdsa_k256_keccak_multisig.masl" )); Library::read_from_bytes(bytes) .expect("Shipped Multisig Ecdsa K256 Keccak library is well-formed") @@ -89,7 +89,7 @@ static NO_AUTH_LIBRARY: LazyLock = LazyLock::new(|| { static RPO_FALCON_512_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/multisig_rpo_falcon_512.masl" + "/assets/account_components/rpo_falcon_512_multisig.masl" )); Library::read_from_bytes(bytes).expect("Shipped Multisig Rpo Falcon 512 library is well-formed") }); @@ -177,13 +177,16 @@ impl WellKnownComponent { Self::AuthNoAuth => NO_AUTH_LIBRARY.as_ref(), }; - library.exports().map(|export| { - library - .mast_forest() - .get_node_by_id(export.node) - .expect("export node not in the forest") - .digest() - }) + library + .exports() + .filter(|export| matches!(export, LibraryExport::Procedure(_))) + .map(|proc_export| { + library + .mast_forest() + .get_node_by_id(proc_export.unwrap_procedure().node) + .expect("export node not in the forest") + .digest() + }) } /// Checks whether procedures from the current component are present in the procedures map diff --git a/crates/miden-lib/src/account/faucets/basic_fungible.rs b/crates/miden-standards/src/account/faucets/basic_fungible.rs similarity index 95% rename from crates/miden-lib/src/account/faucets/basic_fungible.rs rename to crates/miden-standards/src/account/faucets/basic_fungible.rs index 481f131155..329937ebd4 100644 --- a/crates/miden-lib/src/account/faucets/basic_fungible.rs +++ b/crates/miden-standards/src/account/faucets/basic_fungible.rs @@ -1,4 +1,4 @@ -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -8,8 +8,8 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::asset::{FungibleAsset, TokenSymbol}; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::asset::{FungibleAsset, TokenSymbol}; +use miden_protocol::{Felt, FieldElement, Word}; use super::FungibleFaucetError; use crate::account::AuthScheme; @@ -20,7 +20,7 @@ use crate::account::auth::{ AuthRpoFalcon512AclConfig, }; use crate::account::components::basic_fungible_faucet_library; -use crate::account::interface::{AccountComponentInterface, AccountInterface}; +use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::procedure_digest; // BASIC FUNGIBLE FAUCET ACCOUNT COMPONENT @@ -43,9 +43,9 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic fungible faucet. /// /// It reexports the procedures from `miden::contracts::faucets::basic_fungible`. When linking -/// against this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be -/// available to the assembler which is the case when using -/// [`CodeBuilder`][builder]. The procedures of this component are: +/// against this component, the `miden` library (i.e. +/// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the +/// case when using [`CodeBuilder`][builder]. The procedures of this component are: /// - `distribute`, which mints an assets and create a note for the provided recipient. /// - `burn`, which burns the provided asset. /// @@ -60,7 +60,7 @@ procedure_digest!( /// /// - [`Self::metadata_slot`]: Fungible faucet metadata /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct BasicFungibleFaucet { symbol: TokenSymbol, decimals: u8, @@ -74,8 +74,8 @@ impl BasicFungibleFaucet { /// The maximum number of decimals supported by the component. pub const MAX_DECIMALS: u8 = 12; - const DISTRIBUTE_PROC_NAME: &str = "distribute"; - const BURN_PROC_NAME: &str = "burn"; + const DISTRIBUTE_PROC_NAME: &str = "basic_fungible_faucet::distribute"; + const BURN_PROC_NAME: &str = "basic_fungible_faucet::burn"; // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -208,7 +208,7 @@ impl TryFrom for BasicFungibleFaucet { type Error = FungibleFaucetError; fn try_from(account: Account) -> Result { - let account_interface = AccountInterface::from(&account); + let account_interface = AccountInterface::from_account(&account); BasicFungibleFaucet::try_from_interface(account_interface, account.storage()) } @@ -218,7 +218,7 @@ impl TryFrom<&Account> for BasicFungibleFaucet { type Error = FungibleFaucetError; fn try_from(account: &Account) -> Result { - let account_interface = AccountInterface::from(account); + let account_interface = AccountInterface::from_account(account); BasicFungibleFaucet::try_from_interface(account_interface, account.storage()) } @@ -311,9 +311,9 @@ pub fn create_basic_fungible_faucet( #[cfg(test)] mod tests { use assert_matches::assert_matches; - use miden_objects::account::AccountStorage; - use miden_objects::account::auth::PublicKeyCommitment; - use miden_objects::{FieldElement, ONE, Word}; + use miden_protocol::account::AccountStorage; + use miden_protocol::account::auth::PublicKeyCommitment; + use miden_protocol::{FieldElement, ONE, Word}; use super::{ AccountBuilder, diff --git a/crates/miden-lib/src/account/faucets/mod.rs b/crates/miden-standards/src/account/faucets/mod.rs similarity index 93% rename from crates/miden-lib/src/account/faucets/mod.rs rename to crates/miden-standards/src/account/faucets/mod.rs index 4b89f0af31..22a13dcb4f 100644 --- a/crates/miden-lib/src/account/faucets/mod.rs +++ b/crates/miden-standards/src/account/faucets/mod.rs @@ -1,8 +1,8 @@ use alloc::string::String; -use miden_objects::account::{Account, AccountStorage, AccountType, StorageSlotName}; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{AccountError, Felt, TokenSymbolError}; +use miden_protocol::account::{Account, AccountStorage, AccountType, StorageSlotName}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{AccountError, Felt, TokenSymbolError}; use thiserror::Error; mod basic_fungible; diff --git a/crates/miden-lib/src/account/faucets/network_fungible.rs b/crates/miden-standards/src/account/faucets/network_fungible.rs similarity index 91% rename from crates/miden-lib/src/account/faucets/network_fungible.rs rename to crates/miden-standards/src/account/faucets/network_fungible.rs index 25d88717f7..912e89e863 100644 --- a/crates/miden-lib/src/account/faucets/network_fungible.rs +++ b/crates/miden-standards/src/account/faucets/network_fungible.rs @@ -1,4 +1,4 @@ -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -9,14 +9,14 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::asset::TokenSymbol; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::asset::TokenSymbol; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{Felt, FieldElement, Word}; use super::{BasicFungibleFaucet, FungibleFaucetError}; use crate::account::auth::NoAuth; use crate::account::components::network_fungible_faucet_library; -use crate::account::interface::{AccountComponentInterface, AccountInterface}; +use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::procedure_digest; // NETWORK FUNGIBLE FAUCET ACCOUNT COMPONENT @@ -37,16 +37,16 @@ procedure_digest!( ); static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::network_fungible_faucet::owner_config") + StorageSlotName::new("miden::standards::network_fungible_faucet::owner_config") .expect("storage slot name should be valid") }); /// An [`AccountComponent`] implementing a network fungible faucet. /// /// It reexports the procedures from `miden::contracts::faucets::network_fungible`. When linking -/// against this component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be -/// available to the assembler which is the case when using -/// [`CodeBuilder`][builder]. The procedures of this component are: +/// against this component, the `miden` library (i.e. +/// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the +/// case when using [`CodeBuilder`][builder]. The procedures of this component are: /// - `distribute`, which mints an assets and create a note for the provided recipient. /// - `burn`, which burns the provided asset. /// @@ -59,7 +59,7 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// - [`Self::metadata_slot`]: Fungible faucet metadata. /// - [`Self::owner_config_slot`]: The owner account of this network faucet. /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct NetworkFungibleFaucet { faucet: BasicFungibleFaucet, owner_account_id: AccountId, @@ -72,8 +72,8 @@ impl NetworkFungibleFaucet { /// The maximum number of decimals supported by the component. pub const MAX_DECIMALS: u8 = 12; - const DISTRIBUTE_PROC_NAME: &str = "distribute"; - const BURN_PROC_NAME: &str = "burn"; + const DISTRIBUTE_PROC_NAME: &str = "network_fungible_faucet::distribute"; + const BURN_PROC_NAME: &str = "network_fungible_faucet::burn"; // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -84,7 +84,7 @@ impl NetworkFungibleFaucet { /// Returns an error if: /// - the decimals parameter exceeds maximum value of [`Self::MAX_DECIMALS`]. /// - the max supply parameter exceeds maximum possible amount for a fungible asset - /// ([`miden_objects::asset::FungibleAsset::MAX_AMOUNT`]) + /// ([`miden_protocol::asset::FungibleAsset::MAX_AMOUNT`]) pub fn new( symbol: TokenSymbol, decimals: u8, @@ -106,7 +106,7 @@ impl NetworkFungibleFaucet { /// [`AccountComponentInterface::NetworkFungibleFaucet`] component. /// - the decimals parameter exceeds maximum value of [`Self::MAX_DECIMALS`]. /// - the max supply value exceeds maximum possible amount for a fungible asset of - /// [`miden_objects::asset::FungibleAsset::MAX_AMOUNT`]. + /// [`miden_protocol::asset::FungibleAsset::MAX_AMOUNT`]. /// - the token symbol encoded value exceeds the maximum value of /// [`TokenSymbol::MAX_ENCODED_VALUE`]. fn try_from_interface( @@ -243,7 +243,7 @@ impl TryFrom for NetworkFungibleFaucet { type Error = FungibleFaucetError; fn try_from(account: Account) -> Result { - let account_interface = AccountInterface::from(&account); + let account_interface = AccountInterface::from_account(&account); NetworkFungibleFaucet::try_from_interface(account_interface, account.storage()) } @@ -253,7 +253,7 @@ impl TryFrom<&Account> for NetworkFungibleFaucet { type Error = FungibleFaucetError; fn try_from(account: &Account) -> Result { - let account_interface = AccountInterface::from(account); + let account_interface = AccountInterface::from_account(account); NetworkFungibleFaucet::try_from_interface(account_interface, account.storage()) } diff --git a/crates/miden-lib/src/account/interface/component.rs b/crates/miden-standards/src/account/interface/component.rs similarity index 87% rename from crates/miden-lib/src/account/interface/component.rs rename to crates/miden-standards/src/account/interface/component.rs index fc26de424d..75bba9bb8f 100644 --- a/crates/miden-lib/src/account/interface/component.rs +++ b/crates/miden-standards/src/account/interface/component.rs @@ -1,11 +1,10 @@ -use alloc::collections::BTreeSet; use alloc::string::{String, ToString}; use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountId, AccountProcedureRoot, AccountStorage, StorageSlotName}; -use miden_objects::note::PartialNote; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountId, AccountProcedureRoot, AccountStorage, StorageSlotName}; +use miden_protocol::note::PartialNote; +use miden_protocol::{Felt, FieldElement, Word}; use crate::AuthScheme; use crate::account::auth::{ @@ -16,7 +15,6 @@ use crate::account::auth::{ AuthRpoFalcon512Acl, AuthRpoFalcon512Multisig, }; -use crate::account::components::WellKnownComponent; use crate::account::interface::AccountInterfaceError; // ACCOUNT COMPONENT INTERFACE @@ -175,33 +173,6 @@ impl AccountComponentInterface { } } - /// Creates a vector of [AccountComponentInterface] instances. This vector specifies the - /// components which were used to create an account with the provided procedures info array. - pub fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec { - let mut component_interface_vec = Vec::new(); - - let mut procedures = BTreeSet::from_iter(procedures.iter().copied()); - - // Well known component interfaces - // ---------------------------------------------------------------------------------------- - - // Get all available well known components which could be constructed from the `procedures` - // map and push them to the `component_interface_vec` - WellKnownComponent::extract_well_known_components( - &mut procedures, - &mut component_interface_vec, - ); - - // Custom component interfaces - // ---------------------------------------------------------------------------------------- - - // All remaining procedures are put into the custom bucket. - component_interface_vec - .push(AccountComponentInterface::Custom(procedures.into_iter().collect())); - - component_interface_vec - } - /// Generates a body for the note creation of the `send_note` transaction script. The resulting /// code could use different procedures for note creation, which depends on the used interface. /// @@ -218,10 +189,10 @@ impl AccountComponentInterface { /// /// ```masm /// push.{note_information} - /// call.::miden::output_note::create + /// call.::miden::protocol::output_note::create /// /// push.{note asset} - /// call.::miden::contracts::wallets::basic::move_asset_to_note dropw + /// call.::miden::standards::wallets::basic::move_asset_to_note dropw /// dropw dropw dropw drop /// ``` /// @@ -231,7 +202,7 @@ impl AccountComponentInterface { /// push.{note information} /// /// push.{asset amount} - /// call.::miden::contracts::faucets::basic_fungible::distribute dropw dropw drop + /// call.::miden::standards::faucets::basic_fungible::distribute dropw dropw drop /// ``` /// /// # Errors: @@ -286,19 +257,19 @@ impl AccountComponentInterface { body.push_str(&format!( "push.{amount} - call.::miden::contracts::faucets::basic_fungible::distribute dropw dropw drop\n", + call.::miden::standards::faucets::basic_fungible::distribute dropw dropw drop\n", amount = asset.unwrap_fungible().amount() )); // stack => [] }, AccountComponentInterface::BasicWallet => { - body.push_str("call.::miden::output_note::create\n"); + body.push_str("call.::miden::protocol::output_note::create\n"); // stack => [note_idx] for asset in partial_note.assets().iter() { body.push_str(&format!( "push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note dropw\n", + call.::miden::standards::wallets::basic::move_asset_to_note dropw\n", asset = Word::from(*asset) )); // stack => [note_idx] diff --git a/crates/miden-standards/src/account/interface/extension.rs b/crates/miden-standards/src/account/interface/extension.rs new file mode 100644 index 0000000000..d83cdd2e7f --- /dev/null +++ b/crates/miden-standards/src/account/interface/extension.rs @@ -0,0 +1,261 @@ +use alloc::collections::BTreeSet; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use miden_processor::MastNodeExt; +use miden_protocol::Word; +use miden_protocol::account::{Account, AccountCode, AccountId, AccountProcedureRoot}; +use miden_protocol::assembly::mast::{MastForest, MastNode, MastNodeId}; +use miden_protocol::note::{Note, NoteScript}; + +use crate::AuthScheme; +use crate::account::components::{ + WellKnownComponent, + basic_fungible_faucet_library, + basic_wallet_library, + ecdsa_k256_keccak_acl_library, + ecdsa_k256_keccak_library, + ecdsa_k256_keccak_multisig_library, + network_fungible_faucet_library, + no_auth_library, + rpo_falcon_512_acl_library, + rpo_falcon_512_library, + rpo_falcon_512_multisig_library, +}; +use crate::account::interface::{ + AccountComponentInterface, + AccountInterface, + NoteAccountCompatibility, +}; +use crate::note::WellKnownNote; + +// ACCOUNT INTERFACE EXTENSION TRAIT +// ================================================================================================ + +/// An extension for [`AccountInterface`] that allows instantiation from higher-level types. +pub trait AccountInterfaceExt { + /// Creates a new [`AccountInterface`] instance from the provided account ID, authentication + /// schemes and account code. + fn from_code(account_id: AccountId, auth: Vec, code: &AccountCode) -> Self; + + /// Creates a new [`AccountInterface`] instance from the provided [`Account`]. + fn from_account(account: &Account) -> Self; + + /// Returns [NoteAccountCompatibility::Maybe] if the provided note is compatible with the + /// current [AccountInterface], and [NoteAccountCompatibility::No] otherwise. + fn is_compatible_with(&self, note: &Note) -> NoteAccountCompatibility; + + /// Returns the set of digests of all procedures from all account component interfaces. + fn get_procedure_digests(&self) -> BTreeSet; +} + +impl AccountInterfaceExt for AccountInterface { + fn from_code(account_id: AccountId, auth: Vec, code: &AccountCode) -> Self { + let components = AccountComponentInterface::from_procedures(code.procedures()); + + Self::new(account_id, auth, components) + } + + fn from_account(account: &Account) -> Self { + let components = AccountComponentInterface::from_procedures(account.code().procedures()); + let mut auth = Vec::new(); + + // Find the auth component and extract all auth schemes from it + // An account should have only one auth component + for component in components.iter() { + if component.is_auth_component() { + auth = component.get_auth_schemes(account.storage()); + break; + } + } + + Self::new(account.id(), auth, components) + } + + /// Returns [NoteAccountCompatibility::Maybe] if the provided note is compatible with the + /// current [AccountInterface], and [NoteAccountCompatibility::No] otherwise. + fn is_compatible_with(&self, note: &Note) -> NoteAccountCompatibility { + if let Some(well_known_note) = WellKnownNote::from_note(note) { + if well_known_note.is_compatible_with(self) { + NoteAccountCompatibility::Maybe + } else { + NoteAccountCompatibility::No + } + } else { + verify_note_script_compatibility(note.script(), self.get_procedure_digests()) + } + } + + fn get_procedure_digests(&self) -> BTreeSet { + let mut component_proc_digests = BTreeSet::new(); + for component in self.components.iter() { + match component { + AccountComponentInterface::BasicWallet => { + component_proc_digests + .extend(basic_wallet_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::BasicFungibleFaucet => { + component_proc_digests + .extend(basic_fungible_faucet_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::NetworkFungibleFaucet => { + component_proc_digests.extend( + network_fungible_faucet_library().mast_forest().procedure_digests(), + ); + }, + AccountComponentInterface::AuthEcdsaK256Keccak => { + component_proc_digests + .extend(ecdsa_k256_keccak_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::AuthEcdsaK256KeccakAcl => { + component_proc_digests + .extend(ecdsa_k256_keccak_acl_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::AuthEcdsaK256KeccakMultisig => { + component_proc_digests.extend( + ecdsa_k256_keccak_multisig_library().mast_forest().procedure_digests(), + ); + }, + AccountComponentInterface::AuthRpoFalcon512 => { + component_proc_digests + .extend(rpo_falcon_512_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::AuthRpoFalcon512Acl => { + component_proc_digests + .extend(rpo_falcon_512_acl_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::AuthRpoFalcon512Multisig => { + component_proc_digests.extend( + rpo_falcon_512_multisig_library().mast_forest().procedure_digests(), + ); + }, + AccountComponentInterface::AuthNoAuth => { + component_proc_digests + .extend(no_auth_library().mast_forest().procedure_digests()); + }, + AccountComponentInterface::Custom(custom_procs) => { + component_proc_digests + .extend(custom_procs.iter().map(|info| *info.mast_root())); + }, + } + } + + component_proc_digests + } +} + +/// An extension for [`AccountComponentInterface`] that allows instantiation from a set of procedure +/// roots. +pub trait AccountComponentInterfaceExt { + /// Creates a vector of [`AccountComponentInterface`] instances from the provided set of + /// procedures. + fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec; +} + +impl AccountComponentInterfaceExt for AccountComponentInterface { + fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec { + let mut component_interface_vec = Vec::new(); + + let mut procedures = BTreeSet::from_iter(procedures.iter().copied()); + + // Well known component interfaces + // ---------------------------------------------------------------------------------------- + + // Get all available well known components which could be constructed from the + // `procedures` map and push them to the `component_interface_vec` + WellKnownComponent::extract_well_known_components( + &mut procedures, + &mut component_interface_vec, + ); + + // Custom component interfaces + // ---------------------------------------------------------------------------------------- + + // All remaining procedures are put into the custom bucket. + component_interface_vec + .push(AccountComponentInterface::Custom(procedures.into_iter().collect())); + + component_interface_vec + } +} + +// HELPER FUNCTIONS +// ------------------------------------------------------------------------------------------------ + +/// Verifies that the provided note script is compatible with the target account interfaces. +/// +/// This is achieved by checking that at least one execution branch in the note script is compatible +/// with the account procedures vector. +/// +/// This check relies on the fact that account procedures are the only procedures that are `call`ed +/// from note scripts, while kernel procedures are `sycall`ed. +fn verify_note_script_compatibility( + note_script: &NoteScript, + account_procedures: BTreeSet, +) -> NoteAccountCompatibility { + // collect call branches of the note script + let branches = collect_call_branches(note_script); + + // if none of the branches are compatible with the target account, return a `CheckResult::No` + if !branches.iter().any(|call_targets| call_targets.is_subset(&account_procedures)) { + return NoteAccountCompatibility::No; + } + + NoteAccountCompatibility::Maybe +} + +/// Collect call branches by recursively traversing through program execution branches and +/// accumulating call targets. +fn collect_call_branches(note_script: &NoteScript) -> Vec> { + let mut branches = vec![BTreeSet::new()]; + + let entry_node = note_script.entrypoint(); + recursively_collect_call_branches(entry_node, &mut branches, ¬e_script.mast()); + branches +} + +/// Generates a list of calls invoked in each execution branch of the provided code block. +fn recursively_collect_call_branches( + mast_node_id: MastNodeId, + branches: &mut Vec>, + note_script_forest: &Arc, +) { + let mast_node = ¬e_script_forest[mast_node_id]; + + match mast_node { + MastNode::Block(_) => {}, + MastNode::Join(join_node) => { + recursively_collect_call_branches(join_node.first(), branches, note_script_forest); + recursively_collect_call_branches(join_node.second(), branches, note_script_forest); + }, + MastNode::Split(split_node) => { + let current_branch = branches.last().expect("at least one execution branch").clone(); + recursively_collect_call_branches(split_node.on_false(), branches, note_script_forest); + + // If the previous branch had additional calls we need to create a new branch + if branches.last().expect("at least one execution branch").len() > current_branch.len() + { + branches.push(current_branch); + } + + recursively_collect_call_branches(split_node.on_true(), branches, note_script_forest); + }, + MastNode::Loop(loop_node) => { + recursively_collect_call_branches(loop_node.body(), branches, note_script_forest); + }, + MastNode::Call(call_node) => { + if call_node.is_syscall() { + return; + } + + let callee_digest = note_script_forest[call_node.callee()].digest(); + + branches + .last_mut() + .expect("at least one execution branch") + .insert(callee_digest); + }, + MastNode::Dyn(_) => {}, + MastNode::External(_) => {}, + } +} diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-standards/src/account/interface/mod.rs similarity index 51% rename from crates/miden-lib/src/account/interface/mod.rs rename to crates/miden-standards/src/account/interface/mod.rs index 2b51c9d17a..cdc967759a 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-standards/src/account/interface/mod.rs @@ -1,32 +1,14 @@ -use alloc::collections::BTreeSet; use alloc::string::String; -use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{Account, AccountCode, AccountId, AccountIdPrefix, AccountType}; -use miden_objects::assembly::mast::{MastForest, MastNode, MastNodeId}; -use miden_objects::note::{Note, NoteScript, PartialNote}; -use miden_objects::transaction::TransactionScript; -use miden_processor::MastNodeExt; +use miden_protocol::account::{AccountId, AccountIdPrefix, AccountType}; +use miden_protocol::note::PartialNote; +use miden_protocol::transaction::TransactionScript; use thiserror::Error; use crate::AuthScheme; -use crate::account::components::{ - basic_fungible_faucet_library, - basic_wallet_library, - ecdsa_k256_keccak_acl_library, - ecdsa_k256_keccak_library, - ecdsa_k256_keccak_multisig_library, - network_fungible_faucet_library, - no_auth_library, - rpo_falcon_512_acl_library, - rpo_falcon_512_library, - rpo_falcon_512_multisig_library, -}; +use crate::code_builder::CodeBuilder; use crate::errors::CodeBuilderError; -use crate::note::WellKnownNote; -use crate::utils::CodeBuilder; #[cfg(test)] mod test; @@ -34,6 +16,9 @@ mod test; mod component; pub use component::AccountComponentInterface; +mod extension; +pub use extension::{AccountComponentInterfaceExt, AccountInterfaceExt}; + // ACCOUNT INTERFACE // ================================================================================================ @@ -54,10 +39,12 @@ impl AccountInterface { // -------------------------------------------------------------------------------------------- /// Creates a new [`AccountInterface`] instance from the provided account ID, authentication - /// schemes and account code. - pub fn new(account_id: AccountId, auth: Vec, code: &AccountCode) -> Self { - let components = AccountComponentInterface::from_procedures(code.procedures()); - + /// schemes and account component interfaces. + pub fn new( + account_id: AccountId, + auth: Vec, + components: Vec, + ) -> Self { Self { account_id, auth, components } } @@ -85,8 +72,8 @@ impl AccountInterface { } /// Returns `true` if the full state of the account is public on chain, i.e. if the modes are - /// [`AccountStorageMode::Public`](miden_objects::account::AccountStorageMode::Public) or - /// [`AccountStorageMode::Network`](miden_objects::account::AccountStorageMode::Network), + /// [`AccountStorageMode::Public`](miden_protocol::account::AccountStorageMode::Public) or + /// [`AccountStorageMode::Network`](miden_protocol::account::AccountStorageMode::Network), /// `false` otherwise. pub fn has_public_state(&self) -> bool { self.account_id.has_public_state() @@ -116,78 +103,6 @@ impl AccountInterface { pub fn components(&self) -> &Vec { &self.components } - - /// Returns [NoteAccountCompatibility::Maybe] if the provided note is compatible with the - /// current [AccountInterface], and [NoteAccountCompatibility::No] otherwise. - pub fn is_compatible_with(&self, note: &Note) -> NoteAccountCompatibility { - if let Some(well_known_note) = WellKnownNote::from_note(note) { - if well_known_note.is_compatible_with(self) { - NoteAccountCompatibility::Maybe - } else { - NoteAccountCompatibility::No - } - } else { - verify_note_script_compatibility(note.script(), self.get_procedure_digests()) - } - } - - /// Returns a digests set of all procedures from all account component interfaces. - pub(crate) fn get_procedure_digests(&self) -> BTreeSet { - let mut component_proc_digests = BTreeSet::new(); - for component in self.components.iter() { - match component { - AccountComponentInterface::BasicWallet => { - component_proc_digests - .extend(basic_wallet_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::BasicFungibleFaucet => { - component_proc_digests - .extend(basic_fungible_faucet_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::NetworkFungibleFaucet => { - component_proc_digests.extend( - network_fungible_faucet_library().mast_forest().procedure_digests(), - ); - }, - AccountComponentInterface::AuthEcdsaK256Keccak => { - component_proc_digests - .extend(ecdsa_k256_keccak_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::AuthEcdsaK256KeccakAcl => { - component_proc_digests - .extend(ecdsa_k256_keccak_acl_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::AuthEcdsaK256KeccakMultisig => { - component_proc_digests.extend( - ecdsa_k256_keccak_multisig_library().mast_forest().procedure_digests(), - ); - }, - AccountComponentInterface::AuthRpoFalcon512 => { - component_proc_digests - .extend(rpo_falcon_512_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::AuthRpoFalcon512Acl => { - component_proc_digests - .extend(rpo_falcon_512_acl_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::AuthRpoFalcon512Multisig => { - component_proc_digests.extend( - rpo_falcon_512_multisig_library().mast_forest().procedure_digests(), - ); - }, - AccountComponentInterface::AuthNoAuth => { - component_proc_digests - .extend(no_auth_library().mast_forest().procedure_digests()); - }, - AccountComponentInterface::Custom(custom_procs) => { - component_proc_digests - .extend(custom_procs.iter().map(|info| *info.mast_root())); - }, - } - } - - component_proc_digests - } } // ------------------------------------------------------------------------------------------------ @@ -214,12 +129,12 @@ impl AccountInterface { /// /// ```masm /// begin - /// push.{expiration_delta} exec.::miden::tx::update_expiration_block_delta + /// push.{expiration_delta} exec.::miden::protocol::tx::update_expiration_block_delta /// /// push.{note information} /// /// push.{asset amount} - /// call.::miden::contracts::faucets::basic_fungible::distribute dropw dropw drop + /// call.::miden::standards::faucets::basic_fungible::distribute dropw dropw drop /// end /// ``` /// @@ -237,7 +152,6 @@ impl AccountInterface { &self, output_notes: &[PartialNote], expiration_delta: Option, - in_debug_mode: bool, ) -> Result { let note_creation_source = self.build_create_notes_section(output_notes)?; @@ -247,7 +161,7 @@ impl AccountInterface { note_creation_source, ); - let tx_script = CodeBuilder::new(in_debug_mode) + let tx_script = CodeBuilder::new() .compile_tx_script(script) .map_err(AccountInterfaceError::InvalidTransactionScript)?; @@ -292,35 +206,15 @@ impl AccountInterface { /// Returns a string with the expiration delta update procedure call for the script. fn build_set_tx_expiration_section(&self, expiration_delta: Option) -> String { if let Some(expiration_delta) = expiration_delta { - format!("push.{expiration_delta} exec.::miden::tx::update_expiration_block_delta\n") + format!( + "push.{expiration_delta} exec.::miden::protocol::tx::update_expiration_block_delta\n" + ) } else { String::new() } } } -impl From<&Account> for AccountInterface { - fn from(account: &Account) -> Self { - let components = AccountComponentInterface::from_procedures(account.code().procedures()); - let mut auth = Vec::new(); - - // Find the auth component and extract all auth schemes from it - // An account should have only one auth component - for component in components.iter() { - if component.is_auth_component() { - auth = component.get_auth_schemes(account.storage()); - break; - } - } - - Self { - account_id: account.id(), - auth, - components, - } - } -} - // NOTE ACCOUNT COMPATIBILITY // ================================================================================================ @@ -339,87 +233,6 @@ pub enum NoteAccountCompatibility { Yes, } -// HELPER FUNCTIONS -// ------------------------------------------------------------------------------------------------ - -/// Verifies that the provided note script is compatible with the target account interfaces. -/// -/// This is achieved by checking that at least one execution branch in the note script is compatible -/// with the account procedures vector. -/// -/// This check relies on the fact that account procedures are the only procedures that are `call`ed -/// from note scripts, while kernel procedures are `sycall`ed. -fn verify_note_script_compatibility( - note_script: &NoteScript, - account_procedures: BTreeSet, -) -> NoteAccountCompatibility { - // collect call branches of the note script - let branches = collect_call_branches(note_script); - - // if none of the branches are compatible with the target account, return a `CheckResult::No` - if !branches.iter().any(|call_targets| call_targets.is_subset(&account_procedures)) { - return NoteAccountCompatibility::No; - } - - NoteAccountCompatibility::Maybe -} - -/// Collect call branches by recursively traversing through program execution branches and -/// accumulating call targets. -fn collect_call_branches(note_script: &NoteScript) -> Vec> { - let mut branches = vec![BTreeSet::new()]; - - let entry_node = note_script.entrypoint(); - recursively_collect_call_branches(entry_node, &mut branches, ¬e_script.mast()); - branches -} - -/// Generates a list of calls invoked in each execution branch of the provided code block. -fn recursively_collect_call_branches( - mast_node_id: MastNodeId, - branches: &mut Vec>, - note_script_forest: &Arc, -) { - let mast_node = ¬e_script_forest[mast_node_id]; - - match mast_node { - MastNode::Block(_) => {}, - MastNode::Join(join_node) => { - recursively_collect_call_branches(join_node.first(), branches, note_script_forest); - recursively_collect_call_branches(join_node.second(), branches, note_script_forest); - }, - MastNode::Split(split_node) => { - let current_branch = branches.last().expect("at least one execution branch").clone(); - recursively_collect_call_branches(split_node.on_false(), branches, note_script_forest); - - // If the previous branch had additional calls we need to create a new branch - if branches.last().expect("at least one execution branch").len() > current_branch.len() - { - branches.push(current_branch); - } - - recursively_collect_call_branches(split_node.on_true(), branches, note_script_forest); - }, - MastNode::Loop(loop_node) => { - recursively_collect_call_branches(loop_node.body(), branches, note_script_forest); - }, - MastNode::Call(call_node) => { - if call_node.is_syscall() { - return; - } - - let callee_digest = note_script_forest[call_node.callee()].digest(); - - branches - .last_mut() - .expect("at least one execution branch") - .insert(callee_digest); - }, - MastNode::Dyn(_) => {}, - MastNode::External(_) => {}, - } -} - // ACCOUNT INTERFACE ERROR // ============================================================================================ diff --git a/crates/miden-lib/src/account/interface/test.rs b/crates/miden-standards/src/account/interface/test.rs similarity index 91% rename from crates/miden-lib/src/account/interface/test.rs rename to crates/miden-standards/src/account/interface/test.rs index a574e03284..5c86dedab7 100644 --- a/crates/miden-lib/src/account/interface/test.rs +++ b/crates/miden-standards/src/account/interface/test.rs @@ -1,9 +1,9 @@ use assert_matches::assert_matches; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountBuilder, AccountComponent, AccountType}; -use miden_objects::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; -use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; -use miden_objects::note::{ +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountBuilder, AccountComponent, AccountType}; +use miden_protocol::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; +use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -13,11 +13,11 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, }; -use miden_objects::{Felt, NoteError, Word, ZERO}; +use miden_protocol::{Felt, NoteError, Word, ZERO}; use crate::AuthScheme; use crate::account::auth::{ @@ -31,12 +31,13 @@ use crate::account::faucets::BasicFungibleFaucet; use crate::account::interface::{ AccountComponentInterface, AccountInterface, + AccountInterfaceExt, NoteAccountCompatibility, }; use crate::account::wallets::BasicWallet; +use crate::code_builder::CodeBuilder; use crate::note::{create_p2id_note, create_p2ide_note, create_swap_note}; use crate::testing::account_interface::get_public_keys_from_account; -use crate::utils::CodeBuilder; // DEFAULT NOTES // ================================================================================================ @@ -51,7 +52,7 @@ fn test_basic_wallet_default_notes() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); let mock_seed = Word::from([Felt::new(4), Felt::new(5), Felt::new(6), Felt::new(7)]).as_bytes(); let faucet_account = AccountBuilder::new(mock_seed) @@ -67,7 +68,7 @@ fn test_basic_wallet_default_notes() { ) .build_existing() .expect("failed to create wallet account"); - let faucet_account_interface = AccountInterface::from(&faucet_account); + let faucet_account_interface = AccountInterface::from_account(&faucet_account); let p2id_note = create_p2id_note( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap(), @@ -143,9 +144,9 @@ fn test_basic_wallet_default_notes() { #[test] fn test_custom_account_default_note() { let account_custom_code_source = " - use.miden::contracts::wallets::basic + use miden::standards::wallets::basic - export.basic::receive_asset + pub use basic::receive_asset "; let account_code = CodeBuilder::default() @@ -160,7 +161,7 @@ fn test_custom_account_default_note() { .with_component(account_component.clone()) .build_existing() .unwrap(); - let target_account_interface = AccountInterface::from(&target_account); + let target_account_interface = AccountInterface::from_account(&target_account); let p2id_note = create_p2id_note( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap(), @@ -246,7 +247,7 @@ fn test_basic_wallet_custom_notes() { .with_assets(vec![FungibleAsset::mock(20)]) .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); let sender_account_id = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into().unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); @@ -262,9 +263,9 @@ fn test_basic_wallet_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use.miden::tx - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::protocol::tx + use miden::standards::wallets::basic->wallet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -292,8 +293,8 @@ fn test_basic_wallet_custom_notes() { ); let incompatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::wallets::basic->wallet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -336,7 +337,7 @@ fn test_basic_fungible_faucet_custom_notes() { ) .build_existing() .expect("failed to create wallet account"); - let faucet_account_interface = AccountInterface::from(&faucet_account); + let faucet_account_interface = AccountInterface::from_account(&faucet_account); let sender_account_id = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into().unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); @@ -352,8 +353,8 @@ fn test_basic_fungible_faucet_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::wallets::basic->wallet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -380,8 +381,8 @@ fn test_basic_fungible_faucet_custom_notes() { ); let incompatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::wallets::basic->wallet + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -418,11 +419,11 @@ fn test_basic_fungible_faucet_custom_notes() { #[test] fn test_custom_account_custom_notes() { let account_custom_code_source = " - export.procedure_1 + pub proc procedure_1 push.1.2.3.4 dropw end - export.procedure_2 + pub proc procedure_2 push.5.6.7.8 dropw end "; @@ -439,7 +440,7 @@ fn test_custom_account_custom_notes() { .with_component(account_component.clone()) .build_existing() .unwrap(); - let target_account_interface = AccountInterface::from(&target_account); + let target_account_interface = AccountInterface::from_account(&target_account); let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let sender_account = AccountBuilder::new(mock_seed) @@ -462,8 +463,8 @@ fn test_custom_account_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.test::account::component_1->test_account + use miden::standards::wallets::basic->wallet + use test::account::component_1->test_account begin push.1 @@ -493,8 +494,8 @@ fn test_custom_account_custom_notes() { ); let incompatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.test::account::component_1->test_account + use miden::standards::wallets::basic->wallet + use test::account::component_1->test_account begin push.1 @@ -528,11 +529,11 @@ fn test_custom_account_custom_notes() { #[test] fn test_custom_account_multiple_components_custom_notes() { let account_custom_code_source = " - export.procedure_1 + pub proc procedure_1 push.1.2.3.4 dropw end - export.procedure_2 + pub proc procedure_2 push.5.6.7.8 dropw end "; @@ -550,7 +551,7 @@ fn test_custom_account_multiple_components_custom_notes() { .with_component(BasicWallet) .build_existing() .unwrap(); - let target_account_interface = AccountInterface::from(&target_account); + let target_account_interface = AccountInterface::from_account(&target_account); let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let sender_account = AccountBuilder::new(mock_seed) @@ -573,10 +574,9 @@ fn test_custom_account_multiple_components_custom_notes() { let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::auth::basic->basic_auth - use.test::account::component_1->test_account - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::wallets::basic->wallet + use test::account::component_1->test_account + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -611,10 +611,9 @@ fn test_custom_account_multiple_components_custom_notes() { ); let incompatible_source_code = " - use.miden::contracts::wallets::basic->wallet - use.miden::contracts::auth::basic->basic_auth - use.test::account::component_1->test_account - use.miden::contracts::faucets::basic_fungible->fungible_faucet + use miden::standards::wallets::basic->wallet + use test::account::component_1->test_account + use miden::standards::faucets::basic_fungible->fungible_faucet begin push.1 @@ -679,7 +678,7 @@ fn test_get_auth_scheme_ecdsa_k256_keccak() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); // Find the EcdsaK256Keccak component interface let ecdsa_k256_keccak_component = wallet_account_interface @@ -709,7 +708,7 @@ fn test_get_auth_scheme_rpo_falcon512() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); // Find the RpoFalcon512 component interface let rpo_falcon_component = wallet_account_interface @@ -739,7 +738,7 @@ fn test_get_auth_scheme_no_auth() { .build_existing() .expect("failed to create no-auth account"); - let no_auth_account_interface = AccountInterface::from(&no_auth_account); + let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); // Find the NoAuth component interface let no_auth_component = no_auth_account_interface @@ -783,7 +782,7 @@ fn test_account_interface_from_account_uses_get_auth_scheme() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); // Should have exactly one auth scheme assert_eq!(wallet_account_interface.auth().len(), 1); @@ -803,7 +802,7 @@ fn test_account_interface_from_account_uses_get_auth_scheme() { .build_existing() .expect("failed to create no-auth account"); - let no_auth_account_interface = AccountInterface::from(&no_auth_account); + let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); // Should have exactly one auth scheme assert_eq!(no_auth_account_interface.auth().len(), 1); @@ -824,7 +823,7 @@ fn test_account_interface_get_auth_scheme() { .build_existing() .expect("failed to create wallet account"); - let wallet_account_interface = AccountInterface::from(&wallet_account); + let wallet_account_interface = AccountInterface::from_account(&wallet_account); // Test that auth() method provides the authentication schemes assert_eq!(wallet_account_interface.auth().len(), 1); @@ -842,7 +841,7 @@ fn test_account_interface_get_auth_scheme() { .build_existing() .expect("failed to create no-auth account"); - let no_auth_account_interface = AccountInterface::from(&no_auth_account); + let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); // Test that auth() method provides the authentication schemes assert_eq!(no_auth_account_interface.auth().len(), 1); diff --git a/crates/miden-lib/src/account/mod.rs b/crates/miden-standards/src/account/mod.rs similarity index 56% rename from crates/miden-lib/src/account/mod.rs rename to crates/miden-standards/src/account/mod.rs index 78afc9f8dd..60683d1392 100644 --- a/crates/miden-lib/src/account/mod.rs +++ b/crates/miden-standards/src/account/mod.rs @@ -11,8 +11,8 @@ pub mod wallets; /// This macro generates a `LazyLock` static variable that lazily initializes /// the digest of a procedure from a library. /// -/// Note: This macro references exported types from `miden_objects`, so your crate must -/// include `miden-objects` as a dependency. +/// Note: This macro references exported types from `miden_protocol`, so your crate must +/// include `miden_protocol` as a dependency. /// /// # Arguments /// * `$name` - The name of the static variable to create @@ -30,15 +30,9 @@ pub mod wallets; #[macro_export] macro_rules! procedure_digest { ($name:ident, $proc_name:expr, $library_fn:expr) => { - static $name: miden_objects::utils::sync::LazyLock = - miden_objects::utils::sync::LazyLock::new(|| { - let qualified_name = miden_objects::assembly::QualifiedProcedureName::new( - ::core::default::Default::default(), - miden_objects::assembly::ProcedureName::new($proc_name).unwrap_or_else(|_| { - panic!("failed to create name for '{}' procedure", $proc_name) - }), - ); - $library_fn().get_procedure_root_by_name(qualified_name).unwrap_or_else(|| { + static $name: miden_protocol::utils::sync::LazyLock = + miden_protocol::utils::sync::LazyLock::new(|| { + $library_fn().get_procedure_root_by_path($proc_name).unwrap_or_else(|| { panic!("{} should contain '{}' procedure", stringify!($library_fn), $proc_name) }) }); diff --git a/crates/miden-lib/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs similarity index 93% rename from crates/miden-lib/src/account/wallets/mod.rs rename to crates/miden-standards/src/account/wallets/mod.rs index 3fc7851efd..fc7c4f0773 100644 --- a/crates/miden-lib/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -1,13 +1,13 @@ use alloc::string::String; -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, AccountStorageMode, AccountType, }; -use miden_objects::{AccountError, Word}; +use miden_protocol::{AccountError, Word}; use thiserror::Error; use super::AuthScheme; @@ -42,8 +42,8 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic wallet. /// /// It reexports the procedures from `miden::contracts::wallets::basic`. When linking against this -/// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the -/// assembler which is the case when using [`CodeBuilder`][builder]. The procedures +/// component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must be +/// available to the assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: /// - `receive_asset`, which can be used to add an asset to the account. /// - `move_asset_to_note`, which can be used to remove the specified asset from the account and add @@ -54,14 +54,14 @@ procedure_digest!( /// /// This component supports all account types. /// -/// [builder]: crate::utils::CodeBuilder +/// [builder]: crate::code_builder::CodeBuilder pub struct BasicWallet; impl BasicWallet { // CONSTANTS // -------------------------------------------------------------------------------------------- - const RECEIVE_ASSET_PROC_NAME: &str = "receive_asset"; - const MOVE_ASSET_TO_NOTE_PROC_NAME: &str = "move_asset_to_note"; + const RECEIVE_ASSET_PROC_NAME: &str = "basic_wallet::receive_asset"; + const MOVE_ASSET_TO_NOTE_PROC_NAME: &str = "basic_wallet::move_asset_to_note"; // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- @@ -170,9 +170,9 @@ pub fn create_basic_wallet( #[cfg(test)] mod tests { - use miden_objects::account::auth::PublicKeyCommitment; - use miden_objects::{ONE, Word}; use miden_processor::utils::{Deserializable, Serializable}; + use miden_protocol::account::auth::PublicKeyCommitment; + use miden_protocol::{ONE, Word}; use super::{Account, AccountStorageMode, AccountType, AuthScheme, create_basic_wallet}; use crate::account::wallets::BasicWallet; diff --git a/crates/miden-lib/src/auth.rs b/crates/miden-standards/src/auth.rs similarity index 97% rename from crates/miden-lib/src/auth.rs rename to crates/miden-standards/src/auth.rs index c7c98947e3..32c924f98c 100644 --- a/crates/miden-lib/src/auth.rs +++ b/crates/miden-standards/src/auth.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use miden_objects::account::auth::PublicKeyCommitment; +use miden_protocol::account::auth::PublicKeyCommitment; /// Defines authentication schemes available to standard and faucet accounts. pub enum AuthScheme { diff --git a/crates/miden-lib/src/utils/code_builder.rs b/crates/miden-standards/src/code_builder/mod.rs similarity index 82% rename from crates/miden-lib/src/utils/code_builder.rs rename to crates/miden-standards/src/code_builder/mod.rs index 2e58cded31..7ea72c9dc6 100644 --- a/crates/miden-lib/src/utils/code_builder.rs +++ b/crates/miden-standards/src/code_builder/mod.rs @@ -1,20 +1,20 @@ use alloc::sync::Arc; -use miden_objects::account::AccountComponentCode; -use miden_objects::assembly::{ +use miden_protocol::account::AccountComponentCode; +use miden_protocol::assembly::{ Assembler, DefaultSourceManager, Library, - LibraryPath, Parse, ParseOptions, + Path, SourceManagerSync, }; -use miden_objects::note::NoteScript; -use miden_objects::transaction::TransactionScript; +use miden_protocol::note::NoteScript; +use miden_protocol::transaction::{TransactionKernel, TransactionScript}; use crate::errors::CodeBuilderError; -use crate::transaction::TransactionKernel; +use crate::standards_lib::StandardsLib; // CODE BUILDER // ================================================================================================ @@ -55,15 +55,15 @@ use crate::transaction::TransactionKernel; /// /// ```no_run /// # use anyhow::Context; -/// # use miden_lib::utils::CodeBuilder; -/// # use miden_objects::assembly::Library; -/// # use miden_stdlib::StdLibrary; +/// # use miden_standards::code_builder::CodeBuilder; +/// # use miden_protocol::assembly::Library; +/// # use miden_protocol::CoreLibrary; /// # fn example() -> anyhow::Result<()> { -/// # let module_code = "export.test push.1 add end"; +/// # let module_code = "pub proc test push.1 add end"; /// # let script_code = "begin nop end"; /// # // Create sample libraries for the example -/// # let my_lib: Library = StdLibrary::default().into(); // Convert StdLibrary to Library -/// # let fpi_lib: Library = StdLibrary::default().into(); +/// # let my_lib: Library = CoreLibrary::default().into(); // Convert CoreLibrary to Library +/// # let fpi_lib: Library = CoreLibrary::default().into(); /// let script = CodeBuilder::default() /// .with_linked_module("my::module", module_code).context("failed to link module")? /// .with_statically_linked_library(&my_lib).context("failed to link static library")? @@ -87,26 +87,19 @@ impl CodeBuilder { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new CodeBuilder with the specified debug mode. - /// - /// # Arguments - /// * `in_debug_mode` - Whether to enable debug mode in the assembler - pub fn new(in_debug_mode: bool) -> Self { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()) - .with_debug_mode(in_debug_mode); - Self { assembler, source_manager } + /// Creates a new CodeBuilder. + pub fn new() -> Self { + Self::with_source_manager(Arc::new(DefaultSourceManager::default())) } /// Creates a new CodeBuilder with the specified source manager. /// - /// The returned builder is instantiated with debug mode enabled. - /// /// # Arguments /// * `source_manager` - The source manager to use with the internal `Assembler` pub fn with_source_manager(source_manager: Arc) -> Self { let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()) - .with_debug_mode(true); + .with_dynamic_library(StandardsLib::default()) + .expect("linking std lib should work"); Self { assembler, source_manager } } @@ -132,22 +125,12 @@ impl CodeBuilder { module_path: impl AsRef, module_code: impl Parse, ) -> Result<(), CodeBuilderError> { - // Parse the library path - let lib_path = LibraryPath::new(module_path.as_ref()).map_err(|err| { - CodeBuilderError::build_error_with_source( - format!("invalid module path: {}", module_path.as_ref()), - err, - ) - })?; - let mut parse_options = ParseOptions::for_library(); - parse_options.path = Some(lib_path); + parse_options.path = Some(Path::new(module_path.as_ref()).into()); - let module = module_code - .parse_with_options(self.source_manager.as_ref(), parse_options) - .map_err(|err| { - CodeBuilderError::build_error_with_report("failed to parse module code", err) - })?; + let module = module_code.parse_with_options(self.source_manager(), parse_options).map_err( + |err| CodeBuilderError::build_error_with_report("failed to parse module code", err), + )?; self.assembler.compile_and_statically_link(module).map_err(|err| { CodeBuilderError::build_error_with_report("failed to assemble module", err) @@ -258,29 +241,22 @@ impl CodeBuilder { /// # Errors /// Returns an error if: /// - Compiling the account component code fails - /// - If `component_path` is not a valid [`LibraryPath`] pub fn compile_component_code( self, component_path: impl AsRef, component_code: impl Parse, ) -> Result { let CodeBuilder { assembler, source_manager } = self; - let component_path = component_path.as_ref(); - let lib_path = LibraryPath::new(component_path).map_err(|err| { - CodeBuilderError::build_error_with_source( - format!("invalid component path: {component_path}"), - err, - ) - })?; let mut parse_options = ParseOptions::for_library(); - parse_options.path = Some(lib_path); + parse_options.path = Some(Path::new(component_path.as_ref()).into()); - let module = component_code - .parse_with_options(source_manager.as_ref(), parse_options) - .map_err(|err| { - CodeBuilderError::build_error_with_report("failed to parse component code", err) - })?; + let module = + component_code + .parse_with_options(source_manager, parse_options) + .map_err(|err| { + CodeBuilderError::build_error_with_report("failed to parse component code", err) + })?; let library = assembler.assemble_library([module]).map_err(|err| { CodeBuilderError::build_error_with_report("failed to parse component code", err) @@ -366,7 +342,7 @@ impl CodeBuilder { /// /// [account_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_account_library /// [faucet_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_faucet_library - /// [util_lib]: crate::testing::mock_util_lib::mock_util_library + /// [util_lib]: miden_protocol::testing::mock_util_lib::mock_util_library #[cfg(any(feature = "testing", test))] pub fn with_mock_libraries() -> Self { Self::with_mock_libraries_with_source_manager(Arc::new(DefaultSourceManager::default())) @@ -375,7 +351,7 @@ impl CodeBuilder { /// Returns the mock account and faucet libraries used in testing. #[cfg(any(feature = "testing", test))] pub fn mock_libraries() -> impl Iterator { - use miden_objects::account::AccountCode; + use miden_protocol::account::AccountCode; use crate::testing::mock_account_code::MockAccountCodeExt; @@ -386,35 +362,34 @@ impl CodeBuilder { pub fn with_mock_libraries_with_source_manager( source_manager: Arc, ) -> Self { - use crate::testing::mock_util_lib::mock_util_library; + use miden_protocol::testing::mock_util_lib::mock_util_library; - // Start from the full kernel-aware assembler (includes stdlib and miden-lib). - let mut assembler = - TransactionKernel::assembler_with_source_manager(source_manager.clone()) - .with_debug_mode(true); + // Start with the builder linking against the transaction kernel, protocol library and + // standards library. + let mut builder = Self::with_source_manager(source_manager); // Expose kernel procedures under `$kernel` for testing. - assembler - .link_dynamic_library(TransactionKernel::library()) + builder + .link_dynamic_library(&TransactionKernel::library()) .expect("failed to link kernel library"); // Add mock account/faucet libs (built in debug mode) and mock util. for library in Self::mock_libraries() { - assembler - .link_dynamic_library(library) + builder + .link_dynamic_library(&library) .expect("failed to link mock account libraries"); } - assembler - .link_static_library(mock_util_library()) + builder + .link_static_library(&mock_util_library()) .expect("failed to link mock util library"); - Self { assembler, source_manager } + builder } } impl Default for CodeBuilder { fn default() -> Self { - Self::new(true) + Self::new() } } @@ -430,7 +405,7 @@ impl From for Assembler { #[cfg(test)] mod tests { use anyhow::Context; - use miden_objects::assembly::diagnostics::NamedSource; + use miden_protocol::assembly::diagnostics::NamedSource; use super::*; @@ -452,7 +427,7 @@ mod tests { #[test] fn test_create_library_and_create_tx_script() -> anyhow::Result<()> { let script_code = " - use.external_contract::counter_contract + use external_contract::counter_contract begin call.counter_contract::increment @@ -460,11 +435,11 @@ mod tests { "; let account_code = " - use.miden::active_account - use.miden::native_account - use.std::sys + use miden::protocol::active_account + use miden::protocol::native_account + use miden::core::sys - export.increment + pub proc increment push.0 exec.active_account::get_item push.1 add @@ -490,7 +465,7 @@ mod tests { #[test] fn test_parse_library_and_add_to_builder() -> anyhow::Result<()> { let script_code = " - use.external_contract::counter_contract + use external_contract::counter_contract begin call.counter_contract::increment @@ -498,11 +473,11 @@ mod tests { "; let account_code = " - use.miden::active_account - use.miden::native_account - use.std::sys + use miden::protocol::active_account + use miden::protocol::native_account + use miden::core::sys - export.increment + pub proc increment push.0 exec.active_account::get_item push.1 add @@ -529,7 +504,7 @@ mod tests { .link_module(library_path, account_code) .context("failed to link first module")?; builder_with_libs - .link_module("test::lib", "export.test nop end") + .link_module("test::lib", "pub proc test nop end") .context("failed to link second module")?; builder_with_libs .compile_tx_script(script_code) @@ -541,7 +516,7 @@ mod tests { #[test] fn test_builder_style_chaining() -> anyhow::Result<()> { let script_code = " - use.external_contract::counter_contract + use external_contract::counter_contract begin call.counter_contract::increment @@ -549,11 +524,11 @@ mod tests { "; let account_code = " - use.miden::active_account - use.miden::native_account - use.std::sys + use miden::protocol::active_account + use miden::protocol::native_account + use miden::core::sys - export.increment + pub proc increment push.0 exec.active_account::get_item push.1 add @@ -576,13 +551,13 @@ mod tests { #[test] fn test_multiple_chained_modules() -> anyhow::Result<()> { let script_code = - "use.test::lib1 use.test::lib2 begin exec.lib1::test1 exec.lib2::test2 end"; + "use test::lib1 use test::lib2 begin exec.lib1::test1 exec.lib2::test2 end"; // Test chaining multiple modules let builder = CodeBuilder::default() - .with_linked_module("test::lib1", "export.test1 push.1 add end") + .with_linked_module("test::lib1", "pub proc test1 push.1 add end") .context("failed to link first module")? - .with_linked_module("test::lib2", "export.test2 push.2 add end") + .with_linked_module("test::lib2", "pub proc test2 push.2 add end") .context("failed to link second module")?; builder.compile_tx_script(script_code).context("failed to parse tx script")?; @@ -593,7 +568,7 @@ mod tests { #[test] fn test_static_and_dynamic_linking() -> anyhow::Result<()> { let script_code = " - use.contracts::static_contract + use contracts::static_contract begin call.static_contract::increment_1 @@ -601,13 +576,13 @@ mod tests { "; let account_code_1 = " - export.increment_1 + pub proc increment_1 push.0 drop end "; let account_code_2 = " - export.increment_2 + pub proc increment_2 push.0 drop end "; diff --git a/crates/miden-lib/src/errors/code_builder_errors.rs b/crates/miden-standards/src/errors/code_builder_errors.rs similarity index 93% rename from crates/miden-lib/src/errors/code_builder_errors.rs rename to crates/miden-standards/src/errors/code_builder_errors.rs index 0c2f1913d5..14f3a9eb71 100644 --- a/crates/miden-lib/src/errors/code_builder_errors.rs +++ b/crates/miden-standards/src/errors/code_builder_errors.rs @@ -2,8 +2,8 @@ use alloc::boxed::Box; use alloc::string::String; use core::error::Error; -use miden_objects::assembly::diagnostics::Report; -use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic; +use miden_protocol::assembly::diagnostics::Report; +use miden_protocol::assembly::diagnostics::reporting::PrintDiagnostic; // CODE BUILDER ERROR // ================================================================================================ diff --git a/crates/miden-standards/src/errors/mod.rs b/crates/miden-standards/src/errors/mod.rs new file mode 100644 index 0000000000..2bf69e28e0 --- /dev/null +++ b/crates/miden-standards/src/errors/mod.rs @@ -0,0 +1,7 @@ +/// The errors from the MASM code of the Miden standards. +#[cfg(any(feature = "testing", test))] +#[rustfmt::skip] +pub mod standards; + +mod code_builder_errors; +pub use code_builder_errors::CodeBuilderError; diff --git a/crates/miden-lib/src/errors/note_script_errors.rs b/crates/miden-standards/src/errors/standards.rs similarity index 75% rename from crates/miden-lib/src/errors/note_script_errors.rs rename to crates/miden-standards/src/errors/standards.rs index c4fc42471a..3bfee4952e 100644 --- a/crates/miden-lib/src/errors/note_script_errors.rs +++ b/crates/miden-standards/src/errors/standards.rs @@ -1,26 +1,20 @@ -use crate::errors::MasmError; +use miden_protocol::errors::MasmError; // This file is generated by build.rs, do not modify manually. -// It is generated by extracting errors from the masm files in the `miden-lib/asm` directory. +// It is generated by extracting errors from the MASM files in the `./asm` directory. // -// To add a new error, define a constant in masm of the pattern `const.ERR__...`. -// Try to fit the error into a pre-existing category if possible (e.g. Account, Prologue, -// Non-Fungible-Asset, ...). +// To add a new error, define a constant in MASM of the pattern `const ERR__...`. +// Try to fit the error into a pre-existing category if possible (e.g. Account, Note, ...). -// NOTE SCRIPT ERRORS +// STANDARDS ERRORS // ================================================================================================ -/// Error Message: "auth procedure had been called from outside the epilogue" -pub const ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT: MasmError = MasmError::from_static_str("auth procedure had been called from outside the epilogue"); - -/// Error Message: "B2AGG script requires exactly 1 note asset" -pub const ERR_B2AGG_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("B2AGG script requires exactly 1 note asset"); -/// Error Message: "B2AGG script expects exactly 6 note inputs" -pub const ERR_B2AGG_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("B2AGG script expects exactly 6 note inputs"); - /// Error Message: "burn requires exactly 1 note asset" pub const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("burn requires exactly 1 note asset"); +/// Error Message: "distribute would cause the maximum supply to be exceeded" +pub const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED: MasmError = MasmError::from_static_str("distribute would cause the maximum supply to be exceeded"); + /// Error Message: "number of approvers must be equal to or greater than threshold" pub const ERR_MALFORMED_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers must be equal to or greater than threshold"); @@ -46,13 +40,13 @@ pub const ERR_P2ID_TARGET_ACCT_MISMATCH: MasmError = MasmError::from_static_str( /// Error Message: "P2ID note expects exactly 2 note inputs" pub const ERR_P2ID_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("P2ID note expects exactly 2 note inputs"); -/// Error Message: "maximum scaling factor is 18" -pub const ERR_SCALE_AMOUNT_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("maximum scaling factor is 18"); - /// Error Message: "SWAP script requires exactly 1 note asset" pub const ERR_SWAP_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("SWAP script requires exactly 1 note asset"); /// Error Message: "SWAP script expects exactly 12 note inputs" pub const ERR_SWAP_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("SWAP script expects exactly 12 note inputs"); +/// Error Message: "failed to approve multisig transaction as it was already executed" +pub const ERR_TX_ALREADY_EXECUTED: MasmError = MasmError::from_static_str("failed to approve multisig transaction as it was already executed"); + /// Error Message: "number of approvers or threshold must not be zero" pub const ERR_ZERO_IN_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers or threshold must not be zero"); diff --git a/crates/miden-standards/src/lib.rs b/crates/miden-standards/src/lib.rs new file mode 100644 index 0000000000..8818e57389 --- /dev/null +++ b/crates/miden-standards/src/lib.rs @@ -0,0 +1,21 @@ +#![no_std] + +#[macro_use] +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +mod auth; +pub use auth::AuthScheme; + +pub mod account; +pub mod code_builder; +pub mod errors; +pub mod note; +mod standards_lib; + +pub use standards_lib::StandardsLib; + +#[cfg(any(feature = "testing", test))] +pub mod testing; diff --git a/crates/miden-lib/src/note/mint_inputs.rs b/crates/miden-standards/src/note/mint_inputs.rs similarity index 95% rename from crates/miden-lib/src/note/mint_inputs.rs rename to crates/miden-standards/src/note/mint_inputs.rs index 2588aa3a7a..c5ddb2c63d 100644 --- a/crates/miden-lib/src/note/mint_inputs.rs +++ b/crates/miden-standards/src/note/mint_inputs.rs @@ -1,5 +1,5 @@ -use miden_objects::note::{NoteExecutionHint, NoteInputs, NoteRecipient}; -use miden_objects::{Felt, MAX_INPUTS_PER_NOTE, NoteError, Word}; +use miden_protocol::note::{NoteExecutionHint, NoteInputs, NoteRecipient}; +use miden_protocol::{Felt, MAX_INPUTS_PER_NOTE, NoteError, Word}; /// Represents the different input formats for MINT notes. /// - Private: Creates a private output note using a precomputed recipient digest (8 MINT note diff --git a/crates/miden-lib/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs similarity index 97% rename from crates/miden-lib/src/note/mod.rs rename to crates/miden-standards/src/note/mod.rs index 67f12bf539..2dbf1db2a6 100644 --- a/crates/miden-lib/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -1,10 +1,10 @@ use alloc::vec::Vec; -use miden_objects::account::AccountId; -use miden_objects::asset::Asset; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::rand::FeltRng; -use miden_objects::note::{ +use miden_protocol::account::AccountId; +use miden_protocol::asset::Asset; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::note::{ Note, NoteAssets, NoteDetails, @@ -15,7 +15,7 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::{Felt, NoteError, Word}; +use miden_protocol::{Felt, NoteError, Word}; use utils::build_swap_tag; pub mod mint_inputs; diff --git a/crates/miden-lib/src/note/utils.rs b/crates/miden-standards/src/note/utils.rs similarity index 90% rename from crates/miden-lib/src/note/utils.rs rename to crates/miden-standards/src/note/utils.rs index 0968a042a0..6e3e408983 100644 --- a/crates/miden-lib/src/note/utils.rs +++ b/crates/miden-standards/src/note/utils.rs @@ -1,8 +1,8 @@ -use miden_objects::account::AccountId; -use miden_objects::asset::Asset; -use miden_objects::block::BlockNumber; -use miden_objects::note::{NoteExecutionMode, NoteInputs, NoteRecipient, NoteTag, NoteType}; -use miden_objects::{Felt, NoteError, Word}; +use miden_protocol::account::AccountId; +use miden_protocol::asset::Asset; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{NoteExecutionMode, NoteInputs, NoteRecipient, NoteTag, NoteType}; +use miden_protocol::{Felt, NoteError, Word}; use super::well_known_note::WellKnownNote; @@ -78,9 +78,9 @@ pub fn build_swap_tag( #[cfg(test)] mod tests { - use miden_objects::account::{AccountIdVersion, AccountStorageMode, AccountType}; - use miden_objects::asset::{FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; - use miden_objects::{self}; + use miden_protocol::account::{AccountIdVersion, AccountStorageMode, AccountType}; + use miden_protocol::asset::{FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; + use miden_protocol::{self}; use super::*; diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-standards/src/note/well_known_note.rs similarity index 98% rename from crates/miden-lib/src/note/well_known_note.rs rename to crates/miden-standards/src/note/well_known_note.rs index 92586e58a4..2c0816fbc5 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-standards/src/note/well_known_note.rs @@ -2,16 +2,16 @@ use alloc::boxed::Box; use alloc::string::String; use core::error::Error; -use miden_objects::account::AccountId; -use miden_objects::block::BlockNumber; -use miden_objects::note::{Note, NoteScript}; -use miden_objects::utils::Deserializable; -use miden_objects::utils::sync::LazyLock; -use miden_objects::vm::Program; -use miden_objects::{Felt, Word}; +use miden_protocol::account::AccountId; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{Note, NoteScript}; +use miden_protocol::utils::Deserializable; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::vm::Program; +use miden_protocol::{Felt, Word}; use crate::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; -use crate::account::interface::{AccountComponentInterface, AccountInterface}; +use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::account::wallets::BasicWallet; // WELL KNOWN NOTE SCRIPTS diff --git a/crates/miden-standards/src/standards_lib.rs b/crates/miden-standards/src/standards_lib.rs new file mode 100644 index 0000000000..b8429b6acb --- /dev/null +++ b/crates/miden-standards/src/standards_lib.rs @@ -0,0 +1,72 @@ +use alloc::sync::Arc; + +use miden_protocol::assembly::Library; +use miden_protocol::assembly::mast::MastForest; +use miden_protocol::utils::serde::Deserializable; +use miden_protocol::utils::sync::LazyLock; + +// CONSTANTS +// ================================================================================================ + +const STANDARDS_LIB_BYTES: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/assets/standards.masl")); + +// MIDEN STANDARDS LIBRARY +// ================================================================================================ + +#[derive(Clone)] +pub struct StandardsLib(Library); + +impl StandardsLib { + /// Returns a reference to the [`MastForest`] of the inner [`Library`]. + pub fn mast_forest(&self) -> &Arc { + self.0.mast_forest() + } +} + +impl AsRef for StandardsLib { + fn as_ref(&self) -> &Library { + &self.0 + } +} + +impl From for Library { + fn from(value: StandardsLib) -> Self { + value.0 + } +} + +impl Default for StandardsLib { + fn default() -> Self { + static STANDARDS_LIB: LazyLock = LazyLock::new(|| { + let contents = Library::read_from_bytes(STANDARDS_LIB_BYTES) + .expect("standards lib masl should be well-formed"); + StandardsLib(contents) + }); + STANDARDS_LIB.clone() + } +} + +// TESTS +// ================================================================================================ + +// NOTE: Most standards-related tests can be found in miden-testing. +#[cfg(all(test, feature = "std"))] +mod tests { + use miden_protocol::assembly::Path; + + use super::StandardsLib; + + #[test] + fn test_compile() { + let path = Path::new("::miden::standards::faucets::basic_fungible::distribute"); + let miden = StandardsLib::default(); + let exists = miden.0.module_infos().any(|module| { + module + .procedures() + .any(|(_, proc)| module.path().join(&proc.name).as_path() == path) + }); + + assert!(exists); + } +} diff --git a/crates/miden-lib/src/testing/account_component/conditional_auth.rs b/crates/miden-standards/src/testing/account_component/conditional_auth.rs similarity index 85% rename from crates/miden-lib/src/testing/account_component/conditional_auth.rs rename to crates/miden-standards/src/testing/account_component/conditional_auth.rs index fbc3a7d989..3ca719e220 100644 --- a/crates/miden-lib/src/testing/account_component/conditional_auth.rs +++ b/crates/miden-standards/src/testing/account_component/conditional_auth.rs @@ -1,20 +1,20 @@ use alloc::string::String; -use miden_objects::account::{AccountComponent, AccountComponentCode}; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::{AccountComponent, AccountComponentCode}; +use miden_protocol::utils::sync::LazyLock; -use crate::utils::CodeBuilder; +use crate::code_builder::CodeBuilder; pub const ERR_WRONG_ARGS_MSG: &str = "auth procedure args are incorrect"; static CONDITIONAL_AUTH_CODE: LazyLock = LazyLock::new(|| { format!( r#" - use.miden::native_account + use miden::protocol::native_account - const.WRONG_ARGS="{ERR_WRONG_ARGS_MSG}" + const WRONG_ARGS="{ERR_WRONG_ARGS_MSG}" - export.auth_conditional + pub proc auth_conditional # => [AUTH_ARGS] # If [97, 98, 99] is passed as an argument, all good. diff --git a/crates/miden-lib/src/testing/account_component/incr_nonce.rs b/crates/miden-standards/src/testing/account_component/incr_nonce.rs similarity index 78% rename from crates/miden-lib/src/testing/account_component/incr_nonce.rs rename to crates/miden-standards/src/testing/account_component/incr_nonce.rs index a505205dad..34087c04eb 100644 --- a/crates/miden-lib/src/testing/account_component/incr_nonce.rs +++ b/crates/miden-standards/src/testing/account_component/incr_nonce.rs @@ -1,13 +1,13 @@ -use miden_objects::account::AccountComponent; -use miden_objects::assembly::Library; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::AccountComponent; +use miden_protocol::assembly::Library; +use miden_protocol::utils::sync::LazyLock; -use crate::utils::CodeBuilder; +use crate::code_builder::CodeBuilder; const INCR_NONCE_AUTH_CODE: &str = " - use.miden::native_account + use miden::protocol::native_account - export.auth_incr_nonce + pub proc auth_incr_nonce exec.native_account::incr_nonce drop end "; diff --git a/crates/miden-lib/src/testing/account_component/mock_account_component.rs b/crates/miden-standards/src/testing/account_component/mock_account_component.rs similarity index 91% rename from crates/miden-lib/src/testing/account_component/mock_account_component.rs rename to crates/miden-standards/src/testing/account_component/mock_account_component.rs index 91e51c6166..3b1f95dc1e 100644 --- a/crates/miden-lib/src/testing/account_component/mock_account_component.rs +++ b/crates/miden-standards/src/testing/account_component/mock_account_component.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use miden_objects::account::{AccountCode, AccountComponent, AccountStorage, StorageSlot}; +use miden_protocol::account::{AccountCode, AccountComponent, AccountStorage, StorageSlot}; use crate::testing::mock_account_code::MockAccountCodeExt; @@ -13,7 +13,7 @@ use crate::testing::mock_account_code::MockAccountCodeExt; /// arbitrary number of storage slots (within the overall limit) so anything can be set for testing /// purposes. /// -/// This component supports all [`AccountType`](miden_objects::account::AccountType)s for testing +/// This component supports all [`AccountType`](miden_protocol::account::AccountType)s for testing /// purposes. /// /// [account_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_account_library diff --git a/crates/miden-lib/src/testing/account_component/mock_faucet_component.rs b/crates/miden-standards/src/testing/account_component/mock_faucet_component.rs similarity index 84% rename from crates/miden-lib/src/testing/account_component/mock_faucet_component.rs rename to crates/miden-standards/src/testing/account_component/mock_faucet_component.rs index 88225d10ee..0520d541cc 100644 --- a/crates/miden-lib/src/testing/account_component/mock_faucet_component.rs +++ b/crates/miden-standards/src/testing/account_component/mock_faucet_component.rs @@ -1,4 +1,4 @@ -use miden_objects::account::{AccountCode, AccountComponent, AccountType}; +use miden_protocol::account::{AccountCode, AccountComponent, AccountType}; use crate::testing::mock_account_code::MockAccountCodeExt; @@ -10,7 +10,7 @@ use crate::testing::mock_account_code::MockAccountCodeExt; /// It uses the [`MockAccountCodeExt::mock_faucet_library`][faucet_lib] and contains no storage /// slots. /// -/// This component supports the faucet [`AccountType`](miden_objects::account::AccountType)s for +/// This component supports the faucet [`AccountType`](miden_protocol::account::AccountType)s for /// testing purposes. /// /// [faucet_lib]: crate::testing::mock_account_code::MockAccountCodeExt::mock_faucet_library diff --git a/crates/miden-lib/src/testing/account_component/mod.rs b/crates/miden-standards/src/testing/account_component/mod.rs similarity index 100% rename from crates/miden-lib/src/testing/account_component/mod.rs rename to crates/miden-standards/src/testing/account_component/mod.rs diff --git a/crates/miden-lib/src/testing/account_interface.rs b/crates/miden-standards/src/testing/account_interface.rs similarity index 60% rename from crates/miden-lib/src/testing/account_interface.rs rename to crates/miden-standards/src/testing/account_interface.rs index dbac4986eb..932c03ee4f 100644 --- a/crates/miden-lib/src/testing/account_interface.rs +++ b/crates/miden-standards/src/testing/account_interface.rs @@ -1,13 +1,13 @@ use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::Account; +use miden_protocol::Word; +use miden_protocol::account::Account; -use crate::account::interface::AccountInterface; +use crate::account::interface::{AccountInterface, AccountInterfaceExt}; /// Helper function to extract public keys from an account pub fn get_public_keys_from_account(account: &Account) -> Vec { - let interface: AccountInterface = account.into(); + let interface = AccountInterface::from_account(account); interface .auth() diff --git a/crates/miden-lib/src/testing/mock_account.rs b/crates/miden-standards/src/testing/mock_account.rs similarity index 93% rename from crates/miden-lib/src/testing/mock_account.rs rename to crates/miden-standards/src/testing/mock_account.rs index 3f3dfdb691..e92e5f595d 100644 --- a/crates/miden-lib/src/testing/mock_account.rs +++ b/crates/miden-standards/src/testing/mock_account.rs @@ -1,4 +1,4 @@ -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -8,10 +8,10 @@ use miden_objects::account::{ StorageMap, StorageSlot, }; -use miden_objects::asset::{AssetVault, NonFungibleAsset}; -use miden_objects::testing::constants::{self}; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::{Felt, Word, ZERO}; +use miden_protocol::asset::{AssetVault, NonFungibleAsset}; +use miden_protocol::testing::constants::{self}; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_protocol::{Felt, Word, ZERO}; use crate::testing::account_component::{MockAccountComponent, MockFaucetComponent}; diff --git a/crates/miden-lib/src/testing/mock_account_code.rs b/crates/miden-standards/src/testing/mock_account_code.rs similarity index 83% rename from crates/miden-lib/src/testing/mock_account_code.rs rename to crates/miden-standards/src/testing/mock_account_code.rs index 86a3d4f888..357ae92fbf 100644 --- a/crates/miden-lib/src/testing/mock_account_code.rs +++ b/crates/miden-standards/src/testing/mock_account_code.rs @@ -1,34 +1,34 @@ -use miden_objects::account::AccountCode; -use miden_objects::assembly::Library; -use miden_objects::utils::sync::LazyLock; +use miden_protocol::account::AccountCode; +use miden_protocol::assembly::Library; +use miden_protocol::utils::sync::LazyLock; -use crate::utils::CodeBuilder; +use crate::code_builder::CodeBuilder; const MOCK_FAUCET_CODE: &str = " - use.miden::faucet + use miden::protocol::faucet #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET, pad(12)] - export.mint + pub proc mint exec.faucet::mint # => [ASSET, pad(12)] end #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET, pad(12)] - export.burn + pub proc burn exec.faucet::burn # => [ASSET, pad(12)] end "; const MOCK_ACCOUNT_CODE: &str = " - use.miden::active_account - use.miden::native_account - use.miden::tx + use miden::protocol::active_account + use miden::protocol::native_account + use miden::protocol::tx - export.::miden::contracts::wallets::basic::receive_asset - export.::miden::contracts::wallets::basic::move_asset_to_note + pub use ::miden::standards::wallets::basic::receive_asset + pub use ::miden::standards::wallets::basic::move_asset_to_note # Note: all account's export procedures below should be only called or dyncall'ed, so it # is assumed that the operand stack at the beginning of their execution is pad'ed and @@ -36,14 +36,14 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] #! Outputs: [OLD_VALUE, pad(12)] - export.set_item + pub proc set_item exec.native_account::set_item # => [OLD_VALUE, pad(12)] end #! Inputs: [slot_id_prefix, slot_id_suffix, pad(14)] #! Outputs: [VALUE, pad(12)] - export.get_item + pub proc get_item exec.active_account::get_item # => [VALUE, pad(14)] @@ -54,7 +54,7 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [slot_id_prefix, slot_id_suffix, pad(14)] #! Outputs: [VALUE, pad(12)] - export.get_initial_item + pub proc get_initial_item exec.active_account::get_initial_item # => [VALUE, pad(14)] @@ -65,28 +65,28 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] - export.set_map_item + pub proc set_map_item exec.native_account::set_map_item # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] end #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] #! Outputs: [VALUE, pad(12)] - export.get_map_item + pub proc get_map_item exec.active_account::get_map_item # => [VALUE, pad(12)] end #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] #! Outputs: [INIT_VALUE, pad(12)] - export.get_initial_map_item + pub proc get_initial_map_item exec.active_account::get_initial_map_item # => [INIT_VALUE, pad(12)] end #! Inputs: [pad(16)] #! Outputs: [CODE_COMMITMENT, pad(12)] - export.get_code_commitment + pub proc get_code_commitment exec.active_account::get_code_commitment # => [CODE_COMMITMENT, pad(16)] @@ -97,7 +97,7 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [pad(16)] #! Outputs: [CODE_COMMITMENT, pad(12)] - export.compute_storage_commitment + pub proc compute_storage_commitment exec.active_account::compute_storage_commitment # => [STORAGE_COMMITMENT, pad(16)] @@ -107,21 +107,21 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET', pad(12)] - export.add_asset + pub proc add_asset exec.native_account::add_asset # => [ASSET', pad(12)] end #! Inputs: [ASSET, pad(12)] #! Outputs: [ASSET, pad(12)] - export.remove_asset + pub proc remove_asset exec.native_account::remove_asset # => [ASSET, pad(12)] end #! Inputs: [pad(16)] #! Outputs: [3, pad(12)] - export.account_procedure_1 + pub proc account_procedure_1 push.1.2 add # truncate the stack @@ -130,7 +130,7 @@ const MOCK_ACCOUNT_CODE: &str = " #! Inputs: [pad(16)] #! Outputs: [1, pad(12)] - export.account_procedure_2 + pub proc account_procedure_2 push.2.1 sub # truncate the stack diff --git a/crates/miden-lib/src/testing/mod.rs b/crates/miden-standards/src/testing/mod.rs similarity index 83% rename from crates/miden-lib/src/testing/mod.rs rename to crates/miden-standards/src/testing/mod.rs index fa43a14205..f08811b562 100644 --- a/crates/miden-lib/src/testing/mod.rs +++ b/crates/miden-standards/src/testing/mod.rs @@ -1,6 +1,7 @@ pub mod account_component; pub mod account_interface; + pub mod mock_account; pub mod mock_account_code; -pub mod mock_util_lib; + pub mod note; diff --git a/crates/miden-lib/src/testing/note.rs b/crates/miden-standards/src/testing/note.rs similarity index 92% rename from crates/miden-lib/src/testing/note.rs rename to crates/miden-standards/src/testing/note.rs index f6d590a008..16451b9687 100644 --- a/crates/miden-lib/src/testing/note.rs +++ b/crates/miden-standards/src/testing/note.rs @@ -2,11 +2,11 @@ use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::account::AccountId; -use miden_objects::assembly::debuginfo::{SourceLanguage, SourceManagerSync, Uri}; -use miden_objects::assembly::{DefaultSourceManager, Library}; -use miden_objects::asset::Asset; -use miden_objects::note::{ +use miden_protocol::account::AccountId; +use miden_protocol::assembly::debuginfo::{SourceLanguage, SourceManagerSync, Uri}; +use miden_protocol::assembly::{DefaultSourceManager, Library}; +use miden_protocol::asset::Asset; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -16,11 +16,11 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::note::DEFAULT_NOTE_CODE; -use miden_objects::{Felt, NoteError, Word, ZERO}; +use miden_protocol::testing::note::DEFAULT_NOTE_CODE; +use miden_protocol::{Felt, NoteError, Word, ZERO}; use rand::Rng; -use crate::utils::CodeBuilder; +use crate::code_builder::CodeBuilder; // NOTE BUILDER // ================================================================================================ diff --git a/crates/miden-testing/Cargo.toml b/crates/miden-testing/Cargo.toml index 3735bacedf..bbcbb06991 100644 --- a/crates/miden-testing/Cargo.toml +++ b/crates/miden-testing/Cargo.toml @@ -13,17 +13,20 @@ rust-version.workspace = true version.workspace = true [features] -std = ["miden-lib/std", "miden-objects/std", "miden-tx/std"] +std = ["miden-protocol/std", "miden-standards/std", "miden-tx/std"] [dependencies] # Workspace dependencies +miden-agglayer = { features = ["testing"], workspace = true } miden-block-prover = { features = ["testing"], workspace = true } -miden-lib = { features = ["testing"], workspace = true } -miden-objects = { features = ["testing"], workspace = true } +miden-protocol = { features = ["testing"], workspace = true } +miden-standards = { features = ["testing"], workspace = true } miden-tx = { features = ["testing"], workspace = true } miden-tx-batch-prover = { features = ["testing"], workspace = true } # Miden dependencies +miden-assembly = { workspace = true } +miden-core-lib = { workspace = true } miden-processor = { workspace = true } # External dependencies @@ -36,7 +39,7 @@ winterfell = { version = "0.13" } [dev-dependencies] anyhow = { features = ["backtrace", "std"], workspace = true } assert_matches = { workspace = true } -miden-objects = { features = ["std"], workspace = true } +miden-protocol = { features = ["std"], workspace = true } primitive-types = { workspace = true } rstest = { workspace = true } tokio = { features = ["macros", "rt"], workspace = true } diff --git a/crates/miden-testing/src/executor.rs b/crates/miden-testing/src/executor.rs index 9cc0800179..02b79705ed 100644 --- a/crates/miden-testing/src/executor.rs +++ b/crates/miden-testing/src/executor.rs @@ -1,9 +1,9 @@ #[cfg(test)] -use miden_objects::assembly::Assembler; -#[cfg(test)] use miden_processor::DefaultHost; use miden_processor::fast::{ExecutionOutput, FastProcessor}; use miden_processor::{AdviceInputs, AsyncHost, ExecutionError, Program, StackInputs}; +#[cfg(test)] +use miden_protocol::assembly::Assembler; // CODE EXECUTOR // ================================================================================================ @@ -39,15 +39,15 @@ impl CodeExecutor { /// Compiles and runs the desired code in the host and returns the [`Process`] state. /// /// To improve the error message quality, convert the returned [`ExecutionError`] into a - /// [`Report`](miden_objects::assembly::diagnostics::Report). + /// [`Report`](miden_protocol::assembly::diagnostics::Report). #[cfg(test)] pub async fn run(self, code: &str) -> Result { use alloc::borrow::ToOwned; use alloc::sync::Arc; - use miden_lib::utils::CodeBuilder; - use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; - use miden_objects::assembly::{DefaultSourceManager, SourceManagerSync}; + use miden_protocol::assembly::debuginfo::{SourceLanguage, Uri}; + use miden_protocol::assembly::{DefaultSourceManager, SourceManagerSync}; + use miden_standards::code_builder::CodeBuilder; let source_manager: Arc = Arc::new(DefaultSourceManager::default()); let assembler: Assembler = CodeBuilder::with_kernel_library(source_manager.clone()).into(); @@ -63,7 +63,7 @@ impl CodeExecutor { /// Executes the provided [`Program`] and returns the [`Process`] state. /// /// To improve the error message quality, convert the returned [`ExecutionError`] into a - /// [`Report`](miden_objects::assembly::diagnostics::Report). + /// [`Report`](miden_protocol::assembly::diagnostics::Report). pub async fn execute_program( mut self, program: Program, @@ -88,7 +88,7 @@ impl CodeExecutor { #[cfg(test)] impl CodeExecutor { pub fn with_default_host() -> Self { - use miden_lib::transaction::TransactionKernel; + use miden_protocol::transaction::TransactionKernel; let mut host = DefaultHost::default(); diff --git a/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs b/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs index 78b1d61eb3..3f6b1f509f 100644 --- a/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs +++ b/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs @@ -3,16 +3,16 @@ use std::collections::BTreeMap; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::{Account, AccountId, AccountStorageMode}; -use miden_objects::batch::ProposedBatch; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::MerkleError; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::AccountIdBuilder; -use miden_objects::transaction::{InputNote, InputNoteCommitment, OutputNote, PartialBlockchain}; -use miden_objects::{BatchAccountUpdateError, ProposedBatchError, Word}; +use miden_protocol::account::{Account, AccountId, AccountStorageMode}; +use miden_protocol::batch::ProposedBatch; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::MerkleError; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::account_id::AccountIdBuilder; +use miden_protocol::transaction::{InputNote, InputNoteCommitment, OutputNote, PartialBlockchain}; +use miden_protocol::{BatchAccountUpdateError, ProposedBatchError, Word}; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::note::NoteBuilder; use rand::rngs::SmallRng; use rand::{Rng, SeedableRng}; diff --git a/crates/miden-testing/src/kernel_tests/batch/proven_tx_builder.rs b/crates/miden-testing/src/kernel_tests/batch/proven_tx_builder.rs index 3c583dd06a..95be1c04c5 100644 --- a/crates/miden-testing/src/kernel_tests/batch/proven_tx_builder.rs +++ b/crates/miden-testing/src/kernel_tests/batch/proven_tx_builder.rs @@ -1,19 +1,19 @@ use alloc::vec::Vec; use anyhow::Context; -use miden_objects::Word; -use miden_objects::account::AccountId; -use miden_objects::asset::FungibleAsset; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::SparseMerklePath; -use miden_objects::note::{Note, NoteInclusionProof, Nullifier}; -use miden_objects::transaction::{ +use miden_protocol::Word; +use miden_protocol::account::AccountId; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::SparseMerklePath; +use miden_protocol::note::{Note, NoteInclusionProof, Nullifier}; +use miden_protocol::transaction::{ InputNote, OutputNote, ProvenTransaction, ProvenTransactionBuilder, }; -use miden_objects::vm::ExecutionProof; +use miden_protocol::vm::ExecutionProof; /// A builder to build mocked [`ProvenTransaction`]s. pub struct MockProvenTxBuilder { diff --git a/crates/miden-testing/src/kernel_tests/block/header_errors.rs b/crates/miden-testing/src/kernel_tests/block/header_errors.rs index 21c4d9f66c..a4a48262c1 100644 --- a/crates/miden-testing/src/kernel_tests/block/header_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/header_errors.rs @@ -2,11 +2,8 @@ use alloc::vec::Vec; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::block::build_block; -use miden_lib::testing::account_component::{IncrNonceAuthComponent, MockAccountComponent}; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{ +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -14,13 +11,15 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::asset::FungibleAsset; -use miden_objects::batch::ProvenBatch; -use miden_objects::block::{BlockInputs, BlockNumber, ProposedBlock}; -use miden_objects::note::NoteType; -use miden_objects::transaction::ProvenTransactionBuilder; -use miden_objects::vm::ExecutionProof; -use miden_objects::{AccountTreeError, NullifierTreeError, ProposedBlockError, Word}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::batch::ProvenBatch; +use miden_protocol::block::{BlockInputs, BlockNumber, ProposedBlock}; +use miden_protocol::note::NoteType; +use miden_protocol::transaction::ProvenTransactionBuilder; +use miden_protocol::vm::ExecutionProof; +use miden_protocol::{AccountTreeError, NullifierTreeError, ProposedBlockError, Word}; +use miden_standards::testing::account_component::{IncrNonceAuthComponent, MockAccountComponent}; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::LocalTransactionProver; use crate::kernel_tests::block::utils::MockChainBlockExt; @@ -104,7 +103,7 @@ async fn block_building_fails_on_stale_account_witnesses() -> anyhow::Result<()> let proposed_block0 = ProposedBlock::new(invalid_account_tree_block_inputs, batches.clone()) .context("failed to propose block 0")?; - let error = build_block(proposed_block0).unwrap_err(); + let error = proposed_block0.into_header_and_body().unwrap_err(); assert_matches!( error, @@ -141,7 +140,7 @@ async fn block_building_fails_on_stale_nullifier_witnesses() -> anyhow::Result<( let proposed_block2 = ProposedBlock::new(invalid_nullifier_tree_block_inputs, batches.clone()) .context("failed to propose block 2")?; - let error = build_block(proposed_block2).unwrap_err(); + let error = proposed_block2.into_header_and_body().unwrap_err(); assert_matches!( error, @@ -187,7 +186,7 @@ async fn block_building_fails_on_account_tree_root_mismatch() -> anyhow::Result< let proposed_block1 = ProposedBlock::new(stale_account_witness_block_inputs, batches.clone()) .context("failed to propose block 1")?; - let error = build_block(proposed_block1).unwrap_err(); + let error = proposed_block1.into_header_and_body().unwrap_err(); assert_matches!( error, @@ -235,7 +234,7 @@ async fn block_building_fails_on_nullifier_tree_root_mismatch() -> anyhow::Resul let proposed_block3 = ProposedBlock::new(invalid_nullifier_witness_block_inputs, batches) .context("failed to propose block 3")?; - let error = build_block(proposed_block3).unwrap_err(); + let error = proposed_block3.into_header_and_body().unwrap_err(); assert_matches!( error, @@ -329,7 +328,7 @@ async fn block_building_fails_on_creating_account_with_existing_account_id_prefi let block = mock_chain.propose_block(batches).context("failed to propose block")?; - let err = build_block(block).unwrap_err(); + let err = block.into_header_and_body().unwrap_err(); // This should fail when we try to _insert_ the same two prefixes into the partial tree. assert_matches!( @@ -426,7 +425,7 @@ async fn block_building_fails_on_creating_account_with_duplicate_account_id_pref let block = mock_chain.propose_block(batches).context("failed to propose block")?; - let err = build_block(block).unwrap_err(); + let err = block.into_header_and_body().unwrap_err(); // This should fail when we try to _track_ the same two prefixes in the partial tree. assert_matches!( diff --git a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs index b2b0b879fa..18389782c2 100644 --- a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs @@ -3,13 +3,13 @@ use std::collections::BTreeMap; use std::vec::Vec; use assert_matches::assert_matches; -use miden_lib::note::create_p2id_note; -use miden_objects::asset::FungibleAsset; -use miden_objects::block::{BlockInputs, BlockNumber, ProposedBlock}; -use miden_objects::crypto::merkle::SparseMerklePath; -use miden_objects::note::{NoteInclusionProof, NoteType}; -use miden_objects::{MAX_BATCHES_PER_BLOCK, ProposedBlockError, ZERO}; use miden_processor::crypto::MerklePath; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::block::{BlockInputs, BlockNumber, ProposedBlock}; +use miden_protocol::crypto::merkle::SparseMerklePath; +use miden_protocol::note::{NoteInclusionProof, NoteType}; +use miden_protocol::{MAX_BATCHES_PER_BLOCK, ProposedBlockError, ZERO}; +use miden_standards::note::create_p2id_note; use miden_tx::LocalTransactionProver; use crate::kernel_tests::block::utils::MockChainBlockExt; diff --git a/crates/miden-testing/src/kernel_tests/block/proposed_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proposed_block_success.rs index e9ab238fe9..cce09fcaaa 100644 --- a/crates/miden-testing/src/kernel_tests/block/proposed_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proposed_block_success.rs @@ -4,16 +4,16 @@ use std::vec::Vec; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::note::NoteBuilder; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{Account, AccountId, AccountStorageMode}; -use miden_objects::asset::FungibleAsset; -use miden_objects::block::{BlockInputs, ProposedBlock}; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::ACCOUNT_ID_SENDER; -use miden_objects::transaction::{ExecutedTransaction, OutputNote, TransactionHeader}; -use miden_objects::{Felt, FieldElement}; +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{Account, AccountId, AccountStorageMode}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::block::{BlockInputs, ProposedBlock}; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::account_id::ACCOUNT_ID_SENDER; +use miden_protocol::transaction::{ExecutedTransaction, OutputNote, TransactionHeader}; +use miden_protocol::{Felt, FieldElement}; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::note::NoteBuilder; use miden_tx::LocalTransactionProver; use rand::Rng; diff --git a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs index cf5d5718ee..6831529a7b 100644 --- a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs @@ -3,15 +3,15 @@ use std::collections::BTreeMap; use std::vec::Vec; use anyhow::Context; -use miden_lib::note::create_p2id_note; -use miden_objects::ZERO; -use miden_objects::asset::FungibleAsset; -use miden_objects::batch::BatchNoteTree; -use miden_objects::block::account_tree::AccountTree; -use miden_objects::block::{BlockInputs, BlockNoteIndex, BlockNoteTree, ProposedBlock}; -use miden_objects::crypto::merkle::Smt; -use miden_objects::note::NoteType; -use miden_objects::transaction::InputNoteCommitment; +use miden_protocol::ZERO; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::batch::BatchNoteTree; +use miden_protocol::block::account_tree::AccountTree; +use miden_protocol::block::{BlockInputs, BlockNoteIndex, BlockNoteTree, ProposedBlock}; +use miden_protocol::crypto::merkle::smt::Smt; +use miden_protocol::note::NoteType; +use miden_protocol::transaction::InputNoteCommitment; +use miden_standards::note::create_p2id_note; use crate::kernel_tests::block::utils::MockChainBlockExt; use crate::utils::create_p2any_note; diff --git a/crates/miden-testing/src/kernel_tests/block/utils.rs b/crates/miden-testing/src/kernel_tests/block/utils.rs index e2e35dc488..f3b5de580e 100644 --- a/crates/miden-testing/src/kernel_tests/block/utils.rs +++ b/crates/miden-testing/src/kernel_tests/block/utils.rs @@ -1,11 +1,11 @@ use std::vec::Vec; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::AccountId; -use miden_objects::batch::ProvenBatch; -use miden_objects::block::BlockNumber; -use miden_objects::note::{Note, NoteId}; -use miden_objects::transaction::{ExecutedTransaction, ProvenTransaction, TransactionScript}; +use miden_protocol::account::AccountId; +use miden_protocol::batch::ProvenBatch; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{Note, NoteId}; +use miden_protocol::transaction::{ExecutedTransaction, ProvenTransaction, TransactionScript}; +use miden_standards::code_builder::CodeBuilder; use miden_tx::LocalTransactionProver; use crate::{MockChain, TxContextInput}; @@ -101,7 +101,7 @@ impl MockChainBlockExt for MockChain { fn update_expiration_tx_script(expiration_delta: u16) -> TransactionScript { let code = format!( " - use.miden::tx + use miden::protocol::tx begin push.{expiration_delta} diff --git a/crates/miden-testing/src/kernel_tests/tx/mod.rs b/crates/miden-testing/src/kernel_tests/tx/mod.rs index 7c488b71b6..e59b033d0a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/mod.rs +++ b/crates/miden-testing/src/kernel_tests/tx/mod.rs @@ -1,7 +1,18 @@ use alloc::string::String; use anyhow::Context; -use miden_lib::transaction::memory::{ +use miden_processor::ContextId; +use miden_processor::fast::ExecutionOutput; +use miden_protocol::account::{Account, AccountId}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::account_id::{ + ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, + ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, + ACCOUNT_ID_SENDER, +}; +use miden_protocol::testing::storage::prepare_assets; +use miden_protocol::transaction::memory::{ self, MemoryOffset, NOTE_MEM_SIZE, @@ -13,19 +24,8 @@ use miden_lib::transaction::memory::{ OUTPUT_NOTE_RECIPIENT_OFFSET, OUTPUT_NOTE_SECTION_OFFSET, }; -use miden_objects::account::{Account, AccountId}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::account_id::{ - ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, - ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, - ACCOUNT_ID_SENDER, -}; -use miden_objects::testing::storage::prepare_assets; -use miden_objects::vm::StackInputs; -use miden_objects::{Felt, Word, ZERO}; -use miden_processor::ContextId; -use miden_processor::fast::ExecutionOutput; +use miden_protocol::vm::StackInputs; +use miden_protocol::{Felt, Word, ZERO}; use crate::MockChain; @@ -125,7 +125,7 @@ pub fn create_mock_notes_procedure(notes: &[Note]) -> String { } let mut script = String::from( - "proc.create_mock_notes + "proc create_mock_notes # remove padding from prologue dropw dropw dropw dropw ", diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index bcf0b213e8..64e9c1e905 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -4,22 +4,9 @@ use std::collections::BTreeMap; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::errors::tx_kernel_errors::{ - ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO, - ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO, - ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE, - ERR_ACCOUNT_ID_UNKNOWN_VERSION, - ERR_ACCOUNT_NONCE_AT_MAX, - ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE, - ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME, - ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED, -}; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{ +use miden_processor::{ExecutionError, Word}; +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{ Account, AccountBuilder, AccountCode, @@ -36,11 +23,21 @@ use miden_objects::account::{ StorageSlotName, StorageSlotType, }; -use miden_objects::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette}; -use miden_objects::assembly::{DefaultSourceManager, Library}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::{ +use miden_protocol::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette}; +use miden_protocol::assembly::{DefaultSourceManager, Library}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::errors::tx_kernel::{ + ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO, + ERR_ACCOUNT_ID_SUFFIX_MOST_SIGNIFICANT_BIT_MUST_BE_ZERO, + ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE, + ERR_ACCOUNT_ID_UNKNOWN_VERSION, + ERR_ACCOUNT_NONCE_AT_MAX, + ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE, + ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME, + ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED, +}; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, @@ -48,11 +45,13 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; -use miden_objects::transaction::OutputNote; -use miden_objects::utils::sync::LazyLock; -use miden_objects::{LexicographicWord, StarkField}; -use miden_processor::{ExecutionError, Word}; +use miden_protocol::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; +use miden_protocol::transaction::{OutputNote, TransactionKernel}; +use miden_protocol::utils::sync::LazyLock; +use miden_protocol::{LexicographicWord, StarkField}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::LocalTransactionProver; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -87,11 +86,10 @@ pub async fn compute_commitment() -> miette::Result<()> { let tx_script = format!( r#" - use.std::word + use miden::core::word - use.miden::prologue - use.miden::active_account - use.mock::account->mock_account + use miden::protocol::active_account + use mock::account->mock_account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -190,7 +188,7 @@ async fn test_account_type() -> miette::Result<()> { let code = format!( " - use.$kernel::account_id + use $kernel::account_id begin exec.account_id::{procedure} @@ -262,7 +260,7 @@ async fn test_account_validate_id() -> miette::Result<()> { let suffix = Felt::try_from((account_id % (1u128 << 64)) as u64).unwrap(); let code = " - use.$kernel::account_id + use $kernel::account_id begin exec.account_id::validate @@ -315,7 +313,7 @@ async fn test_is_faucet_procedure() -> miette::Result<()> { let code = format!( " - use.$kernel::account_id + use $kernel::account_id begin push.{prefix} @@ -356,8 +354,8 @@ pub async fn test_compute_code_commitment() -> miette::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::prologue + use mock::account->mock_account begin exec.prologue::prepare_transaction @@ -385,10 +383,10 @@ async fn test_get_item() -> miette::Result<()> { let code = format!( r#" - use.$kernel::account - use.$kernel::prologue + use $kernel::account + use $kernel::prologue - const.SLOT_NAME = word("{slot_name}") + const SLOT_NAME = word("{slot_name}") begin exec.prologue::prepare_transaction @@ -431,8 +429,8 @@ async fn test_get_map_item() -> miette::Result<()> { for (key, expected_value) in map.entries() { let code = format!( r#" - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account const SLOT_NAME = word("{slot_name}") @@ -448,7 +446,7 @@ async fn test_get_map_item() -> miette::Result<()> { push.{expected_value} assert_eqw.err="value did not match {expected_value}" - exec.::std::sys::truncate_stack + exec.::miden::core::sys::truncate_stack end "#, slot_name = slot.name(), @@ -479,8 +477,8 @@ async fn test_get_storage_slot_type() -> miette::Result<()> { let code = format!( " - use.$kernel::account - use.$kernel::prologue + use $kernel::account + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -545,9 +543,9 @@ async fn test_account_get_item_fails_on_unknown_slot() -> anyhow::Result<()> { let chain = builder.build()?; let code = r#" - use.mock::account + use mock::account - const.UNKNOWN_SLOT_NAME = word("unknown::slot::name") + const UNKNOWN_SLOT_NAME = word("unknown::slot::name") begin push.UNKNOWN_SLOT_NAME[0..2] @@ -580,9 +578,9 @@ async fn test_account_get_item_fails_on_unknown_slot() -> anyhow::Result<()> { #[tokio::test] async fn test_account_set_item_fails_on_reserved_faucet_metadata_slot() -> anyhow::Result<()> { let code = r#" - use.miden::native_account + use miden::protocol::native_account - const.FAUCET_SYSDATA_SLOT=word("miden::faucet::sysdata") + const FAUCET_SYSDATA_SLOT=word("miden::protocol::faucet::sysdata") begin push.FAUCET_SYSDATA_SLOT[0..2] @@ -640,7 +638,7 @@ async fn test_is_slot_id_lt() -> miette::Result<()> { for (prev_slot, curr_slot) in test_cases { let code = format!( r#" - use.$kernel::account + use $kernel::account begin push.{curr_suffix}.{curr_prefix}.{prev_suffix}.{prev_prefix} @@ -677,10 +675,10 @@ async fn test_set_item() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::account - use.$kernel::prologue + use $kernel::account + use $kernel::prologue - const.MOCK_VALUE_SLOT0 = word("{slot_name}") + const MOCK_VALUE_SLOT0 = word("{slot_name}") begin exec.prologue::prepare_transaction @@ -728,12 +726,12 @@ async fn test_set_map_item() -> miette::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::prologue + use mock::account->mock_account - const.SLOT_NAME=word("{slot_name}") + const SLOT_NAME=word("{slot_name}") begin exec.prologue::prepare_transaction @@ -809,8 +807,8 @@ async fn test_get_initial_storage_commitment() -> anyhow::Result<()> { let code = format!( r#" - use.miden::active_account - use.$kernel::prologue + use miden::protocol::active_account + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -860,12 +858,11 @@ async fn test_compute_storage_commitment() -> anyhow::Result<()> { let code = format!( r#" - use.miden::account - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::prologue + use mock::account->mock_account - const.MOCK_VALUE_SLOT0=word("{mock_value_slot0}") - const.MOCK_MAP_SLOT=word("{mock_map_slot}") + const MOCK_VALUE_SLOT0=word("{mock_value_slot0}") + const MOCK_MAP_SLOT=word("{mock_map_slot}") begin exec.prologue::prepare_transaction @@ -1008,8 +1005,8 @@ async fn test_get_vault_root() -> anyhow::Result<()> { // get the initial vault root let code = format!( " - use.miden::active_account - use.$kernel::prologue + use miden::protocol::active_account + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -1029,9 +1026,9 @@ async fn test_get_vault_root() -> anyhow::Result<()> { let code = format!( r#" - use.miden::active_account - use.$kernel::prologue - use.mock::account->mock_account + use miden::protocol::active_account + use $kernel::prologue + use mock::account->mock_account begin exec.prologue::prepare_transaction @@ -1055,13 +1052,13 @@ async fn test_get_vault_root() -> anyhow::Result<()> { Ok(()) } -/// This test checks the correctness of the `miden::active_account::get_initial_balance` procedure -/// in two cases: +/// This test checks the correctness of the `miden::protocol::active_account::get_initial_balance` +/// procedure in two cases: /// - when a note adds the asset which already exists in the account vault. /// - when a note adds the asset which doesn't exist in the account vault. /// /// As part of the test pipeline it also checks the correctness of the -/// `miden::active_account::get_balance` procedure. +/// `miden::protocol::active_account::get_balance` procedure. #[tokio::test] async fn test_get_init_balance_addition() -> anyhow::Result<()> { // prepare the testing data @@ -1113,7 +1110,7 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { let add_existing_source = format!( r#" - use.miden::active_account + use miden::protocol::active_account begin # push faucet ID prefix and suffix @@ -1167,7 +1164,7 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { let add_new_source = format!( r#" - use.miden::active_account + use miden::protocol::active_account begin # push faucet ID prefix and suffix @@ -1209,11 +1206,11 @@ async fn test_get_init_balance_addition() -> anyhow::Result<()> { Ok(()) } -/// This test checks the correctness of the `miden::active_account::get_initial_balance` procedure -/// in case when we create a note which removes an asset from the account vault. +/// This test checks the correctness of the `miden::protocol::active_account::get_initial_balance` +/// procedure in case when we create a note which removes an asset from the account vault. /// /// As part of the test pipeline it also checks the correctness of the -/// `miden::active_account::get_balance` procedure. +/// `miden::protocol::active_account::get_balance` procedure. #[tokio::test] async fn test_get_init_balance_subtraction() -> anyhow::Result<()> { let mut builder = MockChain::builder(); @@ -1244,13 +1241,13 @@ async fn test_get_init_balance_subtraction() -> anyhow::Result<()> { let remove_existing_source = format!( r#" - use.miden::active_account - use.miden::contracts::wallets::basic->wallet - use.mock::util + use miden::protocol::active_account + use miden::standards::wallets::basic->wallet + use mock::util # Inputs: [ASSET, note_idx] # Outputs: [ASSET, note_idx] - proc.move_asset_to_note + proc move_asset_to_note # pad the stack before call push.0.0.0 movdn.7 movdn.7 movdn.7 padw padw swapdw # => [ASSET, note_idx, pad(11)] @@ -1339,8 +1336,8 @@ async fn test_authenticate_and_track_procedure() -> miette::Result<()> { let code = format!( " - use.$kernel::account - use.$kernel::prologue + use $kernel::account + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -1395,8 +1392,8 @@ async fn test_was_procedure_called() -> miette::Result<()> { // 5. Checks that `was_procedure_called` returns `true` let tx_script_code = format!( r#" - use.mock::account->mock_account - use.miden::native_account + use mock::account->mock_account + use miden::protocol::native_account const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") @@ -1452,11 +1449,11 @@ async fn test_was_procedure_called() -> miette::Result<()> { async fn transaction_executor_account_code_using_custom_library() -> miette::Result<()> { let external_library_code = format!( r#" - use.miden::native_account + use miden::protocol::native_account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") - export.external_setter + pub proc external_setter push.2.3.4.5 push.MOCK_VALUE_SLOT0[0..2] exec.native_account::set_item @@ -1466,9 +1463,9 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res ); const ACCOUNT_COMPONENT_CODE: &str = " - use.external_library::external_module + use external_library::external_module - export.custom_setter + pub proc custom_setter exec.external_module::external_setter end"; @@ -1477,7 +1474,7 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res let external_library = TransactionKernel::assembler().assemble_library([external_library_source])?; - let mut assembler: miden_objects::assembly::Assembler = + let mut assembler: miden_protocol::assembly::Assembler = CodeBuilder::with_mock_libraries_with_source_manager(Arc::new( DefaultSourceManager::default(), )) @@ -1490,7 +1487,7 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res assembler.clone().assemble_library([account_component_source]).unwrap(); let tx_script_src = "\ - use.account_component::account_module + use account_component::account_module begin call.account_module::custom_setter @@ -1537,9 +1534,9 @@ async fn transaction_executor_account_code_using_custom_library() -> miette::Res #[tokio::test] async fn incrementing_nonce_twice_fails() -> anyhow::Result<()> { let source_code = " - use.miden::native_account + use miden::protocol::native_account - export.auth_incr_nonce_twice + pub proc auth_incr_nonce_twice exec.native_account::incr_nonce drop exec.native_account::incr_nonce drop end @@ -1573,8 +1570,8 @@ async fn test_has_procedure() -> miette::Result<()> { .unwrap(); let tx_script_code = r#" - use.mock::account->mock_account - use.miden::active_account + use mock::account->mock_account + use miden::protocol::active_account begin # check that get_item procedure is available on the mock account @@ -1625,9 +1622,9 @@ async fn test_get_initial_item() -> miette::Result<()> { // Test that get_initial_item returns the initial value before any changes let code = format!( r#" - use.$kernel::account - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::account + use $kernel::prologue + use mock::account->mock_account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -1691,8 +1688,8 @@ async fn test_get_initial_map_item() -> miette::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.mock::account->mock_account + use $kernel::prologue + use mock::account->mock_account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -1779,7 +1776,7 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> static COMPONENT_1_LIBRARY: LazyLock = LazyLock::new(|| { let code = format!( r#" - use.miden::active_account + use miden::protocol::active_account const TEST_SLOT_NAME = word("{test_slot_name}") @@ -1794,7 +1791,6 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> let source = NamedSource::new("component1::interface", code); TransactionKernel::assembler() - .with_debug_mode(true) .assemble_library([source]) .expect("mock account code should be valid") }); @@ -1802,8 +1798,8 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> static COMPONENT_2_LIBRARY: LazyLock = LazyLock::new(|| { let code = format!( r#" - use.miden::active_account - use.miden::native_account + use miden::protocol::active_account + use miden::protocol::native_account const TEST_SLOT_NAME = word("{test_slot_name}") @@ -1825,7 +1821,6 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> let source = NamedSource::new("component2::interface", code); TransactionKernel::assembler() - .with_debug_mode(true) .assemble_library([source]) .expect("mock account code should be valid") }); @@ -1862,8 +1857,8 @@ async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> .context("failed to build account")?; let tx_script = r#" - use.component1::interface->comp1_interface - use.component2::interface->comp2_interface + use component1::interface->comp1_interface + use component2::interface->comp2_interface begin call.comp1_interface::get_slot_content diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index fa7ae57a3d..bf1ffe2849 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -3,10 +3,8 @@ use std::collections::BTreeMap; use std::string::String; use anyhow::Context; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{ +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{ Account, AccountBuilder, AccountDelta, @@ -19,9 +17,9 @@ use miden_objects::account::{ StorageSlotDelta, StorageSlotName, }; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; -use miden_objects::note::{Note, NoteExecutionHint, NoteTag, NoteType}; -use miden_objects::testing::account_id::{ +use miden_protocol::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; +use miden_protocol::note::{Note, NoteExecutionHint, NoteTag, NoteType}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_3, @@ -29,17 +27,19 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_SENDER, AccountIdBuilder, }; -use miden_objects::testing::asset::NonFungibleAssetBuilder; -use miden_objects::testing::constants::{ +use miden_protocol::testing::asset::NonFungibleAssetBuilder; +use miden_protocol::testing::constants::{ CONSUMED_ASSET_1_AMOUNT, CONSUMED_ASSET_3_AMOUNT, FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA, NON_FUNGIBLE_ASSET_DATA_2, }; -use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0}; -use miden_objects::transaction::TransactionScript; -use miden_objects::{EMPTY_WORD, Felt, FieldElement, LexicographicWord, Word, ZERO}; +use miden_protocol::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0}; +use miden_protocol::transaction::TransactionScript; +use miden_protocol::{EMPTY_WORD, Felt, FieldElement, LexicographicWord, Word, ZERO}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; use miden_tx::LocalTransactionProver; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -643,7 +643,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { # move an asset to the created note to partially deplete fungible asset balance swapw dropw push.{REMOVED_ASSET} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note # => [ASSET, note_idx, pad(11)] # clear the stack @@ -659,8 +659,8 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { let tx_script_src = format!( r#" - use.mock::account - use.miden::output_note + use mock::account + use miden::protocol::output_note const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -848,9 +848,9 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: let code = format!( r#" - use.mock::account + use mock::account - const.MAP_SLOT=word("{map2_slot_name}") + const MAP_SLOT=word("{map2_slot_name}") begin # Update an existing key. @@ -860,7 +860,7 @@ async fn proven_tx_storage_maps_matches_executed_tx_for_new_account() -> anyhow: # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] call.account::set_map_item - exec.::std::sys::truncate_stack + exec.::miden::core::sys::truncate_stack end "# ); @@ -1104,12 +1104,12 @@ fn parse_tx_script(code: impl AsRef) -> anyhow::Result { } const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " - use.mock::account - use.miden::output_note + use mock::account + use miden::protocol::output_note #! Inputs: [slot_id_prefix, slot_id_suffix, VALUE] #! Outputs: [] - proc.set_item + proc set_item repeat.10 push.0 movdn.6 end # => [slot_id_prefix, slot_id_suffix, VALUE, pad(10)] @@ -1121,7 +1121,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, VALUE] #! Outputs: [] - proc.set_map_item + proc set_map_item repeat.6 push.0 movdn.10 end # => [index, KEY, VALUE, pad(6)] @@ -1134,7 +1134,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [ASSET] #! Outputs: [] - proc.create_note_with_asset + proc create_note_with_asset push.0.1.2.3 # recipient push.1 # note_execution_hint push.2 # note_type private @@ -1154,7 +1154,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] #! Outputs: [note_idx] - proc.create_note + proc create_note repeat.8 push.0 movdn.8 end # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] @@ -1167,7 +1167,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [ASSET, note_idx] #! Outputs: [] - proc.move_asset_to_note + proc move_asset_to_note repeat.11 push.0 movdn.5 end # => [ASSET, note_idx, pad(11)] @@ -1179,7 +1179,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [ASSET] #! Outputs: [ASSET'] - proc.add_asset + proc add_asset repeat.12 push.0 movdn.4 end # => [ASSET, pad(12)] @@ -1192,7 +1192,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Inputs: [ASSET] #! Outputs: [ASSET] - proc.remove_asset + proc remove_asset repeat.12 push.0 movdn.4 end # => [ASSET, pad(12)] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs index 133f437676..8be831ff05 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs @@ -2,14 +2,12 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use assert_matches::assert_matches; -use miden_lib::note::{NoteConsumptionStatus, WellKnownNote, create_p2id_note, create_p2ide_note}; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::testing::note::NoteBuilder; -use miden_lib::transaction::TransactionKernel; -use miden_objects::account::{Account, AccountId}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::crypto::rand::FeltRng; -use miden_objects::note::{ +use miden_processor::ExecutionError; +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::{Account, AccountId}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -19,16 +17,22 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::transaction::{InputNote, OutputNote}; -use miden_objects::{Felt, StarkField, Word, ZERO}; -use miden_processor::ExecutionError; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::transaction::{InputNote, OutputNote, TransactionKernel}; +use miden_protocol::{Felt, StarkField, Word, ZERO}; +use miden_standards::note::{ + NoteConsumptionStatus, + WellKnownNote, + create_p2id_note, + create_p2ide_note, +}; +use miden_standards::testing::mock_account::MockAccountExt; +use miden_standards::testing::note::NoteBuilder; use miden_tx::auth::UnreachableAuth; use miden_tx::{ FailedNote, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index cbd725e035..b78a247283 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -1,13 +1,11 @@ use alloc::string::String; use anyhow::Context; -use miden_lib::errors::tx_kernel_errors::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::Account; -use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; -use miden_objects::note::{ +use miden_protocol::account::Account; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::errors::tx_kernel::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -17,12 +15,14 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::{EMPTY_WORD, Felt, ONE, WORD_SIZE, Word}; +use miden_protocol::{EMPTY_WORD, Felt, ONE, WORD_SIZE, Word}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::mock_account::MockAccountExt; use crate::kernel_tests::tx::ExecutionOutputExt; use crate::utils::create_public_p2any_note; @@ -49,7 +49,7 @@ async fn test_active_note_get_sender_fails_from_tx_script() -> anyhow::Result<() mock_chain.prove_next_block()?; let code = " - use.miden::active_note + use miden::protocol::active_note begin # try to get the sender from transaction script @@ -90,9 +90,9 @@ async fn test_active_note_get_metadata() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.$kernel::note->note_internal - use.miden::active_note + use $kernel::prologue + use $kernel::note->note_internal + use miden::protocol::active_note begin exec.prologue::prepare_transaction @@ -135,9 +135,9 @@ async fn test_active_note_get_sender() -> anyhow::Result<()> { // calling get_sender should return sender of the active note let code = " - use.$kernel::prologue - use.$kernel::note->note_internal - use.miden::active_note + use $kernel::prologue + use $kernel::note->note_internal + use miden::protocol::active_note begin exec.prologue::prepare_transaction @@ -211,13 +211,13 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { // calling get_assets should return assets at the specified address let code = format!( " - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.$kernel::note->note_internal - use.miden::active_note + use $kernel::prologue + use $kernel::note->note_internal + use miden::protocol::active_note - proc.process_note_0 + proc process_note_0 # drop the note inputs dropw dropw dropw dropw @@ -240,7 +240,7 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { drop end - proc.process_note_1 + proc process_note_1 # drop the note inputs dropw dropw dropw dropw @@ -341,9 +341,9 @@ async fn test_active_note_get_inputs() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.$kernel::note->note_internal - use.miden::active_note + use $kernel::prologue + use $kernel::note->note_internal + use miden::protocol::active_note begin # => [BH, acct_id, IAH, NC] @@ -385,8 +385,8 @@ async fn test_active_note_get_inputs() -> anyhow::Result<()> { } /// This test checks the scenario when an input note has exactly 8 inputs, and the transaction -/// script attempts to load the inputs to memory using the `miden::active_note::get_inputs` -/// procedure. +/// script attempts to load the inputs to memory using the +/// `miden::protocol::active_note::get_inputs` procedure. /// /// Previously this setup was leading to the incorrect number of note inputs computed during the /// `get_inputs` procedure, see the [issue #1363](https://github.com/0xMiden/miden-base/issues/1363) @@ -441,8 +441,8 @@ async fn test_active_note_get_exactly_8_inputs() -> anyhow::Result<()> { .build()?; let tx_code = " - use.$kernel::prologue - use.miden::active_note + use $kernel::prologue + use miden::protocol::active_note begin exec.prologue::prepare_transaction @@ -484,8 +484,8 @@ async fn test_active_note_get_serial_number() -> anyhow::Result<()> { // calling get_serial_number should return the serial number of the active note let code = " - use.$kernel::prologue - use.miden::active_note + use $kernel::prologue + use miden::protocol::active_note begin exec.prologue::prepare_transaction @@ -523,8 +523,8 @@ async fn test_active_note_get_script_root() -> anyhow::Result<()> { // calling get_script_root should return script root of the active note let code = " - use.$kernel::prologue - use.miden::active_note + use $kernel::prologue + use miden::protocol::active_note begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset.rs index 092e2608ac..adbacaf792 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset.rs @@ -1,12 +1,12 @@ -use miden_objects::account::AccountId; -use miden_objects::asset::NonFungibleAsset; -use miden_objects::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; -use miden_objects::testing::constants::{ +use miden_protocol::account::AccountId; +use miden_protocol::asset::NonFungibleAsset; +use miden_protocol::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; +use miden_protocol::testing::constants::{ FUNGIBLE_ASSET_AMOUNT, FUNGIBLE_FAUCET_INITIAL_BALANCE, NON_FUNGIBLE_ASSET_DATA, }; -use miden_objects::{Felt, Hasher, Word}; +use miden_protocol::{Felt, Hasher, Word}; use crate::TransactionContextBuilder; use crate::kernel_tests::tx::ExecutionOutputExt; @@ -21,8 +21,8 @@ async fn test_create_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.miden::faucet + use $kernel::prologue + use miden::protocol::faucet begin exec.prologue::prepare_transaction @@ -62,8 +62,8 @@ async fn test_create_non_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.miden::faucet + use $kernel::prologue + use miden::protocol::faucet begin exec.prologue::prepare_transaction @@ -95,7 +95,7 @@ async fn test_validate_non_fungible_asset() -> anyhow::Result<()> { let code = format!( " - use.$kernel::asset + use $kernel::asset begin push.{non_fungible_asset} diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs index 37b492d8cc..497d083047 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs @@ -1,21 +1,21 @@ use assert_matches::assert_matches; -use miden_lib::errors::tx_kernel_errors::{ +use miden_protocol::account::AccountId; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; +use miden_protocol::errors::tx_kernel::{ ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED, ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET, ERR_VAULT_NON_FUNGIBLE_ASSET_ALREADY_EXISTS, ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND, }; -use miden_lib::transaction::memory; -use miden_objects::account::AccountId; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET_1, }; -use miden_objects::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; -use miden_objects::{AssetVaultError, Felt, ONE, Word, ZERO}; +use miden_protocol::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; +use miden_protocol::transaction::memory; +use miden_protocol::{AssetVaultError, Felt, ONE, Word, ZERO}; use crate::kernel_tests::tx::ExecutionOutputExt; use crate::{TransactionContextBuilder, assert_execution_error}; @@ -28,8 +28,8 @@ async fn get_balance_returns_correct_amount() -> anyhow::Result<()> { let faucet_id: AccountId = ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET.try_into().unwrap(); let code = format!( r#" - use.$kernel::prologue - use.miden::active_account + use $kernel::prologue + use miden::protocol::active_account begin exec.prologue::prepare_transaction @@ -64,10 +64,9 @@ async fn peek_balance_returns_correct_amount() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.$kernel::memory - use.$kernel::asset_vault - use.miden::account + use $kernel::prologue + use $kernel::memory + use $kernel::asset_vault begin exec.prologue::prepare_transaction @@ -113,8 +112,8 @@ async fn test_get_balance_non_fungible_fails() -> anyhow::Result<()> { let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET).unwrap(); let code = format!( " - use.$kernel::prologue - use.miden::active_account + use $kernel::prologue + use miden::protocol::active_account begin exec.prologue::prepare_transaction @@ -144,8 +143,8 @@ async fn test_has_non_fungible_asset() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.miden::active_account + use $kernel::prologue + use miden::protocol::active_account begin exec.prologue::prepare_transaction @@ -182,8 +181,8 @@ async fn test_add_fungible_asset_success() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -229,8 +228,8 @@ async fn test_add_non_fungible_asset_fail_overflow() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -260,8 +259,8 @@ async fn test_add_non_fungible_asset_success() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -302,8 +301,8 @@ async fn test_add_non_fungible_asset_fail_duplicate() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -339,8 +338,8 @@ async fn test_remove_fungible_asset_success_no_balance_remaining() -> anyhow::Re let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -384,8 +383,8 @@ async fn test_remove_fungible_asset_fail_remove_too_much() -> anyhow::Result<()> let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -423,8 +422,8 @@ async fn test_remove_fungible_asset_success_balance_remaining() -> anyhow::Resul let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -472,8 +471,8 @@ async fn test_remove_inexisting_non_fungible_asset_fails() -> anyhow::Result<()> let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction @@ -508,8 +507,8 @@ async fn test_remove_non_fungible_asset_success() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.mock::account + use $kernel::prologue + use mock::account begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs index b6d0cf449b..c400161482 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_auth.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_auth.rs @@ -1,13 +1,13 @@ use anyhow::Context; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::MasmError; -use miden_lib::errors::note_script_errors::ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; -use miden_lib::testing::account_component::{ConditionalAuthComponent, ERR_WRONG_ARGS_MSG}; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{Account, AccountBuilder}; -use miden_objects::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; -use miden_objects::{Felt, ONE}; +use miden_protocol::account::{Account, AccountBuilder}; +use miden_protocol::errors::MasmError; +use miden_protocol::errors::tx_kernel::ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT; +use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; +use miden_protocol::{Felt, ONE}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::{ConditionalAuthComponent, ERR_WRONG_ARGS_MSG}; +use miden_standards::testing::mock_account::MockAccountExt; use crate::{Auth, TransactionContextBuilder, assert_transaction_executor_error}; @@ -91,7 +91,7 @@ async fn test_auth_procedure_called_from_wrong_context() -> anyhow::Result<()> { assert_transaction_executor_error!( execution_result, - ERR_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT + ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT ); Ok(()) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 42a8c7aa9b..e2563cc412 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -1,35 +1,34 @@ use alloc::string::ToString; use alloc::vec::Vec; -use miden_lib::errors::tx_kernel_errors::{ +use miden_processor::{Felt, ONE}; +use miden_protocol::Word; +use miden_protocol::account::{Account, AccountDelta, AccountStorageDelta, AccountVaultDelta}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::errors::tx_kernel::{ ERR_ACCOUNT_DELTA_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED, ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY, ERR_EPILOGUE_NONCE_CANNOT_BE_0, ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME, ERR_TX_INVALID_EXPIRATION_DELTA, }; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::testing::note::NoteBuilder; -use miden_lib::transaction::EXPIRATION_BLOCK_ELEMENT_IDX; -use miden_lib::transaction::memory::{ - NOTE_MEM_SIZE, - OUTPUT_NOTE_ASSET_COMMITMENT_OFFSET, - OUTPUT_NOTE_SECTION_OFFSET, -}; -use miden_lib::utils::CodeBuilder; -use miden_objects::Word; -use miden_objects::account::{Account, AccountDelta, AccountStorageDelta, AccountVaultDelta}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::{NoteTag, NoteType}; -use miden_objects::testing::account_id::{ +use miden_protocol::note::{NoteTag, NoteType}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::storage::MOCK_VALUE_SLOT0; -use miden_objects::transaction::{OutputNote, OutputNotes}; -use miden_processor::{Felt, ONE}; +use miden_protocol::testing::storage::MOCK_VALUE_SLOT0; +use miden_protocol::transaction::memory::{ + NOTE_MEM_SIZE, + OUTPUT_NOTE_ASSET_COMMITMENT_OFFSET, + OUTPUT_NOTE_SECTION_OFFSET, +}; +use miden_protocol::transaction::{OutputNote, OutputNotes, TransactionOutputs}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::mock_account::MockAccountExt; +use miden_standards::testing::note::NoteBuilder; use super::{ZERO, create_mock_notes_procedure}; use crate::kernel_tests::tx::ExecutionOutputExt; @@ -69,9 +68,9 @@ async fn test_epilogue() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.$kernel::account - use.$kernel::epilogue + use $kernel::prologue + use $kernel::account + use $kernel::epilogue {output_notes_data_procedure} @@ -112,7 +111,7 @@ async fn test_epilogue() -> anyhow::Result<()> { .to_commitment(); let account_update_commitment = - miden_objects::Hasher::merge(&[final_account.commitment(), account_delta_commitment]); + miden_protocol::Hasher::merge(&[final_account.commitment(), account_delta_commitment]); let mut expected_stack = Vec::with_capacity(16); expected_stack.extend(output_notes.commitment().as_elements().iter().rev()); @@ -169,8 +168,8 @@ async fn test_compute_output_note_id() -> anyhow::Result<()> { for (note, i) in tx_context.expected_output_notes().iter().zip(0u32..) { let code = format!( " - use.$kernel::prologue - use.$kernel::epilogue + use $kernel::prologue + use $kernel::epilogue {output_notes_data_procedure} @@ -227,8 +226,8 @@ async fn epilogue_fails_when_num_output_assets_exceed_num_input_assets() -> anyh let code = format!( " - use.mock::account - use.mock::util + use mock::account + use mock::util begin # create a note with the output asset @@ -280,8 +279,8 @@ async fn epilogue_fails_when_num_input_assets_exceed_num_output_assets() -> anyh let code = format!( " - use.mock::account - use.mock::util + use mock::account + use mock::util begin # create a note with the output asset @@ -318,10 +317,10 @@ async fn test_block_expiration_height_monotonically_decreases() -> anyhow::Resul let test_pairs: [(u64, u64); 3] = [(9, 12), (18, 3), (20, 20)]; let code_template = " - use.$kernel::prologue - use.$kernel::tx - use.$kernel::epilogue - use.$kernel::account + use $kernel::prologue + use $kernel::tx + use $kernel::epilogue + use $kernel::account begin exec.prologue::prepare_transaction @@ -352,7 +351,9 @@ async fn test_block_expiration_height_monotonically_decreases() -> anyhow::Resul let expected_expiry = v1.min(v2) + tx_context.tx_inputs().block_header().block_num().as_u64(); assert_eq!( - exec_output.get_stack_element(EXPIRATION_BLOCK_ELEMENT_IDX).as_int(), + exec_output + .get_stack_element(TransactionOutputs::EXPIRATION_BLOCK_ELEMENT_IDX) + .as_int(), expected_expiry ); } @@ -366,7 +367,7 @@ async fn test_invalid_expiration_deltas() -> anyhow::Result<()> { let test_values = [0u64, u16::MAX as u64 + 1, u32::MAX as u64]; let code_template = " - use.$kernel::tx + use $kernel::tx begin push.{value_1} @@ -389,10 +390,10 @@ async fn test_no_expiration_delta_set() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code_template = " - use.$kernel::prologue - use.$kernel::epilogue - use.$kernel::tx - use.$kernel::account + use $kernel::prologue + use $kernel::epilogue + use $kernel::tx + use $kernel::account begin exec.prologue::prepare_transaction @@ -410,7 +411,9 @@ async fn test_no_expiration_delta_set() -> anyhow::Result<()> { // Default value should be equal to u32::MAX, set in the prologue assert_eq!( - exec_output.get_stack_element(EXPIRATION_BLOCK_ELEMENT_IDX).as_int() as u32, + exec_output + .get_stack_element(TransactionOutputs::EXPIRATION_BLOCK_ELEMENT_IDX) + .as_int() as u32, u32::MAX ); @@ -425,10 +428,10 @@ async fn test_epilogue_increment_nonce_success() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.mock::account - use.$kernel::epilogue - use.$kernel::memory + use $kernel::prologue + use mock::account + use $kernel::epilogue + use $kernel::memory const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -461,7 +464,7 @@ async fn test_epilogue_increment_nonce_success() -> anyhow::Result<()> { async fn epilogue_fails_on_account_state_change_without_nonce_increment() -> anyhow::Result<()> { let code = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -534,11 +537,11 @@ async fn test_epilogue_empty_transaction_with_empty_output_note() -> anyhow::Res // create an empty output note in the transaction script let tx_script_source = format!( r#" - use.std::word - use.miden::output_note - use.$kernel::prologue - use.$kernel::epilogue - use.$kernel::note + use miden::core::word + use miden::protocol::output_note + use $kernel::prologue + use $kernel::epilogue + use $kernel::note begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs index 7a091aab88..907a3b58a6 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_faucet.rs @@ -1,16 +1,6 @@ use alloc::sync::Arc; -use miden_lib::errors::tx_kernel_errors::{ - ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT, - ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED, - ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND, - ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, - ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, - ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, -}; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -19,23 +9,33 @@ use miden_objects::account::{ AccountType, StorageMap, }; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::asset::{FungibleAsset, NonFungibleAsset}; -use miden_objects::testing::account_id::{ +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::asset::{FungibleAsset, NonFungibleAsset}; +use miden_protocol::errors::tx_kernel::{ + ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT, + ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED, + ERR_FAUCET_NON_FUNGIBLE_ASSET_TO_BURN_NOT_FOUND, + ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, + ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN, + ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, +}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET_1, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::constants::{ +use miden_protocol::testing::constants::{ CONSUMED_ASSET_1_AMOUNT, FUNGIBLE_ASSET_AMOUNT, FUNGIBLE_FAUCET_INITIAL_BALANCE, NON_FUNGIBLE_ASSET_DATA, NON_FUNGIBLE_ASSET_DATA_2, }; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::{Felt, Word}; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_protocol::{Felt, Word}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::mock_account::MockAccountExt; use crate::utils::create_public_p2any_note; use crate::{TransactionContextBuilder, assert_execution_error, assert_transaction_executor_error}; @@ -50,11 +50,11 @@ async fn test_mint_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.mock::faucet->mock_faucet - use.miden::faucet - use.$kernel::asset_vault - use.$kernel::memory - use.$kernel::prologue + use mock::faucet->mock_faucet + use miden::protocol::faucet + use $kernel::asset_vault + use $kernel::memory + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -100,7 +100,7 @@ async fn mint_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result<()> let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -131,8 +131,8 @@ async fn test_mint_fungible_asset_inconsistent_faucet_id() -> anyhow::Result<()> let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -153,7 +153,7 @@ async fn test_mint_fungible_asset_inconsistent_faucet_id() -> anyhow::Result<()> async fn test_mint_fungible_asset_fails_saturate_max_amount() -> anyhow::Result<()> { let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -194,13 +194,13 @@ async fn test_mint_non_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.std::collections::smt + use miden::core::collections::smt - use.$kernel::account - use.$kernel::asset_vault - use.$kernel::memory - use.$kernel::prologue - use.mock::faucet->mock_faucet + use $kernel::account + use $kernel::asset_vault + use $kernel::memory + use $kernel::prologue + use mock::faucet->mock_faucet const FAUCET_SYSDATA_SLOT_NAME = word("{faucet_sysdata_slot_name}") @@ -250,8 +250,8 @@ async fn test_mint_non_fungible_asset_fails_inconsistent_faucet_id() -> anyhow:: let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -275,7 +275,7 @@ async fn mint_non_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -306,8 +306,8 @@ async fn test_mint_non_fungible_asset_fails_asset_already_exists() -> anyhow::Re let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -347,11 +347,11 @@ async fn test_burn_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.mock::faucet->mock_faucet - use.miden::faucet - use.$kernel::asset_vault - use.$kernel::memory - use.$kernel::prologue + use mock::faucet->mock_faucet + use miden::protocol::faucet + use $kernel::asset_vault + use $kernel::memory + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -395,7 +395,7 @@ async fn burn_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result<()> let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -428,8 +428,8 @@ async fn test_burn_fungible_asset_inconsistent_faucet_id() -> anyhow::Result<()> let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -459,8 +459,8 @@ async fn test_burn_fungible_asset_insufficient_input_amount() -> anyhow::Result< let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin exec.prologue::prepare_transaction @@ -496,11 +496,11 @@ async fn test_burn_non_fungible_asset_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::account - use.$kernel::asset_vault - use.$kernel::memory - use.$kernel::prologue - use.mock::faucet->mock_faucet + use $kernel::account + use $kernel::asset_vault + use $kernel::memory + use $kernel::prologue + use mock::faucet->mock_faucet const FAUCET_SYSDATA_SLOT_NAME = word("{faucet_sysdata_slot_name}") @@ -567,8 +567,8 @@ async fn test_burn_non_fungible_asset_fails_does_not_exist() -> anyhow::Result<( let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin # burn asset @@ -593,7 +593,7 @@ async fn burn_non_fungible_asset_fails_on_non_faucet_account() -> anyhow::Result let code = format!( " - use.mock::faucet + use mock::faucet begin push.{asset} @@ -626,8 +626,8 @@ async fn test_burn_non_fungible_asset_fails_inconsistent_faucet_id() -> anyhow:: let code = format!( " - use.$kernel::prologue - use.mock::faucet + use $kernel::prologue + use mock::faucet begin # burn asset @@ -661,8 +661,8 @@ async fn test_is_non_fungible_asset_issued_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.miden::faucet + use $kernel::prologue + use miden::protocol::faucet begin exec.prologue::prepare_transaction @@ -703,8 +703,8 @@ async fn test_get_total_issuance_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::prologue - use.miden::faucet + use $kernel::prologue + use miden::protocol::faucet begin exec.prologue::prepare_transaction @@ -737,8 +737,8 @@ fn setup_non_faucet_account() -> anyhow::Result { )) .compile_component_code( "test::non_faucet_component", - "export.::miden::faucet::mint - export.::miden::faucet::burn", + "pub use ::miden::protocol::faucet::mint + pub use ::miden::protocol::faucet::burn", )?; let faucet_component = AccountComponent::new(faucet_code, vec![])? .with_supported_type(AccountType::RegularAccountUpdatableCode); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs index 53788fdc2c..0a865d1098 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fee.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fee.rs @@ -1,11 +1,11 @@ use anyhow::Context; use assert_matches::assert_matches; -use miden_objects::account::{AccountId, StorageMap, StorageSlot, StorageSlotName}; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; -use miden_objects::transaction::{ExecutedTransaction, OutputNote}; -use miden_objects::{self, Felt, Word}; +use miden_protocol::account::{AccountId, StorageMap, StorageSlot, StorageSlotName}; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset}; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; +use miden_protocol::transaction::{ExecutedTransaction, OutputNote}; +use miden_protocol::{self, Felt, Word}; use miden_tx::TransactionExecutorError; use winter_rand_utils::rand_value; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 81416446ec..42b0cb4098 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -2,13 +2,31 @@ use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; -use miden_lib::errors::tx_kernel_errors::{ +use miden_processor::fast::ExecutionOutput; +use miden_processor::{AdviceInputs, Felt}; +use miden_protocol::account::{ + Account, + AccountBuilder, + AccountComponent, + AccountId, + AccountProcedureRoot, + AccountStorage, + AccountStorageMode, + StorageSlot, +}; +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; +use miden_protocol::errors::tx_kernel::{ ERR_FOREIGN_ACCOUNT_CONTEXT_AGAINST_NATIVE_ACCOUNT, ERR_FOREIGN_ACCOUNT_INVALID_COMMITMENT, ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED, }; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::transaction::memory::{ +use miden_protocol::testing::account_id::{ + ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, + ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, +}; +use miden_protocol::testing::storage::STORAGE_LEAVES_2; +use miden_protocol::transaction::memory::{ ACCOUNT_DATA_LENGTH, ACCT_CODE_COMMITMENT_OFFSET, ACCT_ID_AND_NONCE_OFFSET, @@ -20,27 +38,9 @@ use miden_lib::transaction::memory::{ NUM_ACCT_PROCEDURES_OFFSET, NUM_ACCT_STORAGE_SLOTS_OFFSET, }; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ - Account, - AccountBuilder, - AccountComponent, - AccountId, - AccountProcedureRoot, - AccountStorage, - AccountStorageMode, - StorageSlot, -}; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; -use miden_objects::testing::account_id::{ - ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, - ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, -}; -use miden_objects::testing::storage::STORAGE_LEAVES_2; -use miden_objects::{FieldElement, Word, ZERO}; -use miden_processor::fast::ExecutionOutput; -use miden_processor::{AdviceInputs, Felt}; +use miden_protocol::{FieldElement, Word, ZERO}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; use miden_tx::LocalTransactionProver; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -60,9 +60,9 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let mock_value_slot0 = AccountStorage::mock_value_slot0(); let mock_map_slot = AccountStorage::mock_map_slot(); let foreign_account_code_source = " - use.miden::active_account + use miden::protocol::active_account - export.get_item_foreign + pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.1 drop @@ -72,7 +72,7 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { movup.6 movup.6 drop drop end - export.get_map_item_foreign + pub proc get_map_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.2 drop @@ -122,10 +122,10 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::protocol::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -176,10 +176,10 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::protocol::tx const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -236,10 +236,10 @@ async fn test_fpi_memory_single_account() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::protocol::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -316,9 +316,9 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { let mock_value_slot1 = AccountStorage::mock_value_slot1(); let foreign_account_code_source_1 = " - use.miden::active_account + use miden::protocol::active_account - export.get_item_foreign_1 + pub proc get_item_foreign_1 # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.1 drop @@ -329,9 +329,9 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { end "; let foreign_account_code_source_2 = " - use.miden::active_account + use miden::protocol::active_account - export.get_item_foreign_2 + pub proc get_item_foreign_2 # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.2 drop @@ -400,10 +400,10 @@ async fn test_fpi_memory_two_accounts() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::protocol::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") @@ -535,9 +535,9 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { let mock_map_slot = AccountStorage::mock_map_slot(); let foreign_account_code_source = " - use.miden::active_account + use miden::protocol::active_account - export.get_item_foreign + pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.1 drop @@ -547,7 +547,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { movup.6 movup.6 drop drop end - export.get_map_item_foreign + pub proc get_map_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.2 drop @@ -581,9 +581,9 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.miden::tx + use miden::protocol::tx const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -686,9 +686,9 @@ async fn foreign_account_can_get_balance_and_presence_of_asset() -> anyhow::Resu let foreign_account_code_source = format!( " - use.miden::active_account + use miden::protocol::active_account - export.get_asset_balance + pub proc get_asset_balance # get balance of first asset push.{fungible_faucet_id_suffix} push.{fungible_faucet_id_prefix} exec.active_account::get_balance @@ -740,9 +740,9 @@ async fn foreign_account_can_get_balance_and_presence_of_asset() -> anyhow::Resu let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx + use miden::protocol::tx begin # Get the added balance of two assets from foreign account @@ -799,9 +799,9 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { let foreign_account_code_source = format!( " - use.miden::active_account + use miden::protocol::active_account - export.get_initial_balance + pub proc get_initial_balance # push the faucet ID on the stack push.{fungible_faucet_id_suffix} push.{fungible_faucet_id_prefix} @@ -845,9 +845,9 @@ async fn foreign_account_get_initial_balance() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx + use miden::protocol::tx begin # Get the initial balance of the fungible asset from the foreign account @@ -914,15 +914,15 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { let second_foreign_account_code_source = format!( r#" - use.miden::tx - use.miden::active_account + use miden::protocol::tx + use miden::protocol::active_account - use.std::sys + use miden::core::sys const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") const MOCK_VALUE_SLOT1 = word("{mock_value_slot1}") - export.second_account_foreign_proc + pub proc second_account_foreign_proc # get the storage item at value1 # pad the stack for the `execute_foreign_procedure` execution padw padw @@ -979,14 +979,14 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { // ------ FIRST FOREIGN ACCOUNT --------------------------------------------------------------- let first_foreign_account_code_source = format!( r#" - use.miden::tx - use.miden::active_account + use miden::protocol::tx + use miden::protocol::active_account - use.std::sys + use miden::core::sys const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") - export.first_account_foreign_proc + pub proc first_account_foreign_proc # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 # => [pad(15)] @@ -1013,7 +1013,7 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { exec.sys::truncate_stack end - export.get_item_foreign + pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure of the # foreign account, not the native one push.1 drop @@ -1082,10 +1082,8 @@ async fn test_nested_fpi_cyclic_invocation() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys - - use.miden::tx - use.miden::account + use miden::core::sys + use miden::protocol::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1147,9 +1145,9 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { // ------ SECOND FOREIGN ACCOUNT --------------------------------------------------------------- // unique procedure which just leaves a constant on the stack let second_foreign_account_code_source = r#" - use.std::sys + use miden::core::sys - export.second_account_foreign_proc + pub proc second_account_foreign_proc # leave a constant result on the stack push.3 @@ -1175,10 +1173,10 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { // unique procedure which calls the second foreign account via FPI and then returns let first_foreign_account_code_source = format!( r#" - use.miden::tx - use.std::sys + use miden::protocol::tx + use miden::core::sys - export.first_account_foreign_proc + pub proc first_account_foreign_proc # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 # => [pad(15)] @@ -1242,8 +1240,8 @@ async fn test_prove_fpi_two_foreign_accounts_chain() -> anyhow::Result<()> { // Call the first foreign account's procedure. It will call into the second FA via FPI. let code = format!( r#" - use.std::sys - use.miden::tx + use miden::core::sys + use miden::protocol::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1302,11 +1300,11 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let last_foreign_account_code_source = format!( r#" - use.miden::active_account + use miden::protocol::active_account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") - export.get_item_foreign + pub proc get_item_foreign # make this foreign procedure unique to make sure that we invoke the procedure # of the foreign account, not the native one push.1 drop @@ -1347,10 +1345,10 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let foreign_account_code_source = format!( " - use.miden::tx - use.std::sys + use miden::protocol::tx + use miden::core::sys - export.read_first_foreign_storage_slot_{foreign_account_index} + pub proc read_first_foreign_storage_slot_{foreign_account_index} # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 # => [pad(15)] @@ -1420,9 +1418,9 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx + use miden::protocol::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1467,11 +1465,11 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { // ------ FIRST FOREIGN ACCOUNT --------------------------------------------------------------- let foreign_account_code_source = " - use.miden::tx + use miden::protocol::tx - use.std::sys + use miden::core::sys - export.first_account_foreign_proc + pub proc first_account_foreign_proc # pad the stack for the `execute_foreign_procedure` execution padw padw padw push.0.0.0 # => [pad(15)] @@ -1516,9 +1514,9 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx + use miden::protocol::tx begin # pad the stack for the `execute_foreign_procedure` execution @@ -1579,10 +1577,10 @@ async fn test_nested_fpi_native_account_invocation() -> anyhow::Result<()> { async fn test_fpi_stale_account() -> anyhow::Result<()> { // Prepare the test data let foreign_account_code_source = " - use.miden::native_account + use miden::protocol::native_account # code is not used in this test - export.set_some_item_foreign + pub proc set_some_item_foreign push.34.1 exec.native_account::set_item end @@ -1641,10 +1639,10 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.$kernel::prologue - use.miden::tx + use $kernel::prologue + use miden::protocol::tx begin exec.prologue::prepare_transaction @@ -1679,10 +1677,10 @@ async fn test_fpi_stale_account() -> anyhow::Result<()> { #[tokio::test] async fn test_fpi_get_account_id() -> anyhow::Result<()> { let foreign_account_code_source = " - use.miden::active_account - use.miden::native_account + use miden::protocol::active_account + use miden::protocol::native_account - export.get_current_and_native_ids + pub proc get_current_and_native_ids # get the ID of the current (foreign) account exec.active_account::get_id # => [acct_id_prefix, acct_id_suffix, pad(16)] @@ -1722,10 +1720,10 @@ async fn test_fpi_get_account_id() -> anyhow::Result<()> { let code = format!( r#" - use.std::sys + use miden::core::sys - use.miden::tx - use.miden::account_id + use miden::protocol::tx + use miden::protocol::account_id begin # get the IDs of the foreign and native accounts @@ -1878,18 +1876,18 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - // Create foreign procedures that test get_initial_item and get_initial_map_item let foreign_account_code_source = format!( r#" - use.miden::active_account - use.std::sys + use miden::protocol::active_account + use miden::core::sys const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") - export.test_get_initial_item + pub proc test_get_initial_item push.MOCK_VALUE_SLOT0[0..2] exec.active_account::get_initial_item exec.sys::truncate_stack end - export.test_get_initial_map_item + pub proc test_get_initial_map_item exec.active_account::get_initial_map_item exec.sys::truncate_stack end @@ -1919,8 +1917,8 @@ async fn test_get_initial_item_and_get_initial_map_item_with_foreign_account() - let code = format!( r#" - use.std::sys - use.miden::tx + use miden::core::sys + use miden::protocol::tx const MOCK_MAP_SLOT = word("{mock_map_slot}") diff --git a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs index e0b9fb9f12..d1c62d88bd 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs @@ -1,8 +1,8 @@ use alloc::string::String; -use miden_lib::utils::CodeBuilder; -use miden_objects::Word; -use miden_objects::note::Note; +use miden_protocol::Word; +use miden_protocol::note::Note; +use miden_standards::code_builder::CodeBuilder; use super::{TestSetup, setup_test}; use crate::TxContextInput; @@ -47,7 +47,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { let code = format!( " - use.miden::input_note + use miden::protocol::input_note begin {check_note_0} @@ -104,7 +104,7 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::protocol::input_note begin # get the recipient from the input note @@ -158,7 +158,7 @@ async fn test_get_sender() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::protocol::input_note begin # get the sender from the input note @@ -257,7 +257,7 @@ async fn test_get_assets() -> anyhow::Result<()> { let code = format!( " - use.miden::input_note + use miden::protocol::input_note begin {check_note_0} @@ -302,7 +302,7 @@ async fn test_get_inputs_info() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::protocol::input_note begin # get the inputs commitment and length from the input note with index 0 (the only one @@ -352,7 +352,7 @@ async fn test_get_script_root() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::protocol::input_note begin # get the script root from the input note with index 0 (the only one we have) @@ -395,7 +395,7 @@ async fn test_get_serial_number() -> anyhow::Result<()> { let code = format!( r#" - use.miden::input_note + use miden::protocol::input_note begin # get the serial number from the input note with index 0 (the only one we have) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index a48cfcbd0f..4372650953 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -2,18 +2,18 @@ //! //! Once lazy loading is enabled generally, it can be removed and/or integrated into other tests. -use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; -use miden_objects::LexicographicWord; -use miden_objects::account::{AccountId, AccountStorage, StorageSlotDelta}; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::testing::account_id::{ +use miden_protocol::LexicographicWord; +use miden_protocol::account::{AccountId, AccountStorage, StorageSlotDelta}; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_NATIVE_ASSET_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, }; -use miden_objects::testing::constants::FUNGIBLE_ASSET_AMOUNT; -use miden_objects::testing::storage::MOCK_MAP_SLOT; +use miden_protocol::testing::constants::FUNGIBLE_ASSET_AMOUNT; +use miden_protocol::testing::storage::MOCK_MAP_SLOT; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::note::NoteBuilder; use super::Word; use crate::{Auth, MockChain, TransactionContextBuilder}; @@ -40,7 +40,7 @@ async fn adding_fungible_assets_with_lazy_loading_succeeds() -> anyhow::Result<( let code = format!( " - use.mock::account + use mock::account begin push.{FUNGIBLE_ASSET1} @@ -87,8 +87,8 @@ async fn removing_fungible_assets_with_lazy_loading_succeeds() -> anyhow::Result let code = format!( " - use.mock::account - use.mock::util + use mock::account + use mock::util begin push.{FUNGIBLE_ASSET1} @@ -185,7 +185,7 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.mock::account + use mock::account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -204,7 +204,7 @@ async fn setting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { # => [slot_id_prefix, slot_id_suffix, KEY, VALUE] call.account::set_map_item - exec.::std::sys::truncate_stack + exec.::miden::core::sys::truncate_stack end "# ); @@ -253,8 +253,8 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { let code = format!( r#" - use.std::word - use.mock::account + use miden::core::word + use mock::account const MOCK_MAP_SLOT = word("{mock_map_slot}") @@ -276,7 +276,7 @@ async fn getting_map_item_with_lazy_loading_succeeds() -> anyhow::Result<()> { padw assert_eqw.err="non-existent value should be the empty word" - exec.::std::sys::truncate_stack + exec.::miden::core::sys::truncate_stack end "# ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs b/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs index 69979d73d3..fca31cf22a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_link_map.rs @@ -3,8 +3,8 @@ use std::collections::BTreeMap; use std::string::String; use anyhow::Context; -use miden_objects::{EMPTY_WORD, LexicographicWord, Word}; use miden_processor::{ONE, ZERO}; +use miden_protocol::{EMPTY_WORD, LexicographicWord, Word}; use miden_tx::{LinkMap, MemoryViewer}; use rand::seq::IteratorRandom; use winter_rand_utils::rand_value; @@ -32,9 +32,9 @@ async fn insertion() -> anyhow::Result<()> { let code = format!( r#" - use.$kernel::link_map + use $kernel::link_map - const.MAP_PTR={map_ptr} + const MAP_PTR={map_ptr} begin # Insert key {entry1_key} into an empty map. @@ -534,7 +534,7 @@ async fn execute_link_map_test(operations: Vec) -> anyhow::Result let code = format!( r#" - use.$kernel::link_map + use $kernel::link_map begin {test_code} end diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index a439f64e8a..8bee856271 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -2,19 +2,16 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; use anyhow::Context; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::MasmError; -use miden_lib::testing::note::NoteBuilder; -use miden_lib::transaction::memory::ACTIVE_INPUT_NOTE_PTR; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountBuilder, AccountId}; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::diagnostics::miette::{self, miette}; -use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::dsa::rpo_falcon512::SecretKey; -use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; -use miden_objects::note::{ +use miden_processor::fast::ExecutionOutput; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountBuilder, AccountId}; +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::diagnostics::miette::{self, miette}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::crypto::dsa::falcon512_rpo::SecretKey; +use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::errors::MasmError; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -25,14 +22,17 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::transaction::{OutputNote, TransactionArgs}; -use miden_objects::{Felt, Word, ZERO}; -use miden_processor::fast::ExecutionOutput; +use miden_protocol::transaction::memory::ACTIVE_INPUT_NOTE_PTR; +use miden_protocol::transaction::{OutputNote, TransactionArgs}; +use miden_protocol::{Felt, Word, ZERO}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::note::NoteBuilder; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; @@ -66,8 +66,8 @@ async fn test_note_setup() -> anyhow::Result<()> { }; let code = " - use.$kernel::prologue - use.$kernel::note + use $kernel::prologue + use $kernel::note begin exec.prologue::prepare_transaction @@ -124,9 +124,9 @@ async fn test_note_script_and_note_args() -> miette::Result<()> { }; let code = " - use.$kernel::prologue - use.$kernel::memory - use.$kernel::note + use $kernel::prologue + use $kernel::memory + use $kernel::note begin exec.prologue::prepare_transaction @@ -204,9 +204,9 @@ async fn test_build_recipient() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::note + use miden::protocol::note begin # put the values that will be hashed into the memory @@ -293,9 +293,9 @@ async fn test_compute_inputs_commitment() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::note + use miden::protocol::note begin # put the values that will be hashed into the memory @@ -399,8 +399,8 @@ async fn test_build_metadata() -> miette::Result<()> { for (iteration, test_metadata) in [test_metadata1, test_metadata2].into_iter().enumerate() { let code = format!( " - use.$kernel::prologue - use.$kernel::output_note + use $kernel::prologue + use $kernel::output_note begin exec.prologue::prepare_transaction @@ -434,8 +434,8 @@ pub async fn test_timelock() -> anyhow::Result<()> { let code = format!( r#" - use.miden::active_note - use.miden::tx + use miden::protocol::active_note + use miden::protocol::tx begin # store the note inputs to memory starting at address 0 @@ -567,8 +567,8 @@ async fn test_build_note_tag_for_network_account() -> anyhow::Result<()> { let code = format!( " - use.std::sys - use.miden::note + use miden::core::sys + use miden::protocol::note begin push.{suffix}.{prefix} diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index d4256a7182..a7c48b76b5 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -2,25 +2,14 @@ use alloc::string::String; use alloc::vec::Vec; use anyhow::Context; -use miden_lib::errors::tx_kernel_errors::{ +use miden_protocol::account::{Account, AccountId}; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset}; +use miden_protocol::crypto::rand::RpoRandomCoin; +use miden_protocol::errors::tx_kernel::{ ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS, ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT, }; -use miden_lib::note::create_p2id_note; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::memory::{ - NOTE_MEM_SIZE, - NUM_OUTPUT_NOTES_PTR, - OUTPUT_NOTE_ASSETS_OFFSET, - OUTPUT_NOTE_METADATA_OFFSET, - OUTPUT_NOTE_RECIPIENT_OFFSET, - OUTPUT_NOTE_SECTION_OFFSET, -}; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{Account, AccountId}; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; -use miden_objects::crypto::rand::RpoRandomCoin; -use miden_objects::note::{ +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -31,7 +20,7 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_NETWORK_NON_FUNGIBLE_FAUCET, ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PRIVATE_SENDER, @@ -42,9 +31,20 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::constants::NON_FUNGIBLE_ASSET_DATA_2; -use miden_objects::transaction::{OutputNote, OutputNotes}; -use miden_objects::{Felt, Word, ZERO}; +use miden_protocol::testing::constants::NON_FUNGIBLE_ASSET_DATA_2; +use miden_protocol::transaction::memory::{ + NOTE_MEM_SIZE, + NUM_OUTPUT_NOTES_PTR, + OUTPUT_NOTE_ASSETS_OFFSET, + OUTPUT_NOTE_METADATA_OFFSET, + OUTPUT_NOTE_RECIPIENT_OFFSET, + OUTPUT_NOTE_SECTION_OFFSET, +}; +use miden_protocol::transaction::{OutputNote, OutputNotes}; +use miden_protocol::{Felt, Word, ZERO}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::note::create_p2id_note; +use miden_standards::testing::mock_account::MockAccountExt; use super::{TestSetup, setup_test}; use crate::kernel_tests::tx::ExecutionOutputExt; @@ -62,9 +62,9 @@ async fn test_create_note() -> anyhow::Result<()> { let code = format!( " - use.miden::output_note + use miden::protocol::output_note - use.$kernel::prologue + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -143,8 +143,8 @@ async fn test_create_note_with_invalid_tag() -> anyhow::Result<()> { fn note_creation_script(tag: Felt) -> String { format!( " - use.miden::output_note - use.$kernel::prologue + use miden::protocol::output_note + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -174,10 +174,10 @@ async fn test_create_note_too_many_notes() -> anyhow::Result<()> { let code = format!( " - use.miden::output_note - use.$kernel::constants - use.$kernel::memory - use.$kernel::prologue + use miden::protocol::output_note + use $kernel::constants + use $kernel::memory + use $kernel::prologue begin exec.constants::get_max_num_output_notes @@ -292,12 +292,12 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { let code = format!( " - use.std::sys + use miden::core::sys - use.miden::tx - use.miden::output_note + use miden::protocol::tx + use miden::protocol::output_note - use.$kernel::prologue + use $kernel::prologue begin # => [BH, acct_id, IAH, NC] @@ -397,9 +397,9 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { let code = format!( " - use.miden::output_note + use miden::protocol::output_note - use.$kernel::prologue + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -466,8 +466,8 @@ async fn test_create_note_and_add_multiple_assets() -> anyhow::Result<()> { let code = format!( " - use.miden::output_note - use.$kernel::prologue + use miden::protocol::output_note + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -547,8 +547,8 @@ async fn test_create_note_and_add_same_nft_twice() -> anyhow::Result<()> { let code = format!( " - use.$kernel::prologue - use.miden::output_note + use $kernel::prologue + use miden::protocol::output_note begin exec.prologue::prepare_transaction @@ -639,9 +639,9 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { let recipient = NoteRecipient::new(output_serial_no, input_note_1.script().clone(), inputs); let code = format!( " - use.miden::output_note - use.miden::note - use.$kernel::prologue + use miden::protocol::output_note + use miden::protocol::note + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -702,7 +702,7 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { /// This test creates an output note and then adds some assets into it checking the assets info on /// each stage. /// -/// Namely, we invoke the `miden::output_notes::get_assets_info` procedure: +/// Namely, we invoke the `miden::protocol::output_notes::get_assets_info` procedure: /// - After adding the first `asset_0` to the note. /// - Right after the previous check to make sure it returns the same commitment from the cached /// data. @@ -754,8 +754,8 @@ async fn test_get_asset_info() -> anyhow::Result<()> { let tx_script_src = &format!( r#" - use.miden::output_note - use.std::sys + use miden::protocol::output_note + use miden::core::sys begin # create an output note with fungible asset 0 @@ -769,7 +769,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { # move the asset 0 to the note push.{asset_0} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw # => [note_idx] @@ -798,7 +798,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { # add asset_1 to the note push.{asset_1} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw # => [note_idx] @@ -870,8 +870,8 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { let tx_script_src = &format!( r#" - use.miden::output_note - use.std::sys + use miden::protocol::output_note + use miden::core::sys begin # create an output note with one asset @@ -984,8 +984,8 @@ async fn test_get_assets() -> anyhow::Result<()> { let tx_script_src = &format!( " - use.miden::output_note - use.std::sys + use miden::protocol::output_note + use miden::core::sys begin {create_note_0} @@ -1055,7 +1055,7 @@ fn create_output_note(note: &Note) -> String { " # move the asset to the note push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw # => [note_idx] ", diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index 6eb4ca1334..d04ba7b65f 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -2,16 +2,33 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; use anyhow::Context; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::tx_kernel_errors::{ +use miden_processor::fast::ExecutionOutput; +use miden_processor::{AdviceInputs, Word}; +use miden_protocol::account::{ + Account, + AccountBuilder, + AccountId, + AccountIdVersion, + AccountProcedureRoot, + AccountStorage, + AccountStorageMode, + AccountType, + StorageMap, + StorageSlot, + StorageSlotName, +}; +use miden_protocol::asset::{FungibleAsset, NonFungibleAsset}; +use miden_protocol::errors::tx_kernel::{ ERR_ACCOUNT_SEED_AND_COMMITMENT_DIGEST_MISMATCH, ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_EMPTY, ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT, }; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::TransactionKernel; -use miden_lib::transaction::memory::{ +use miden_protocol::testing::account_id::{ + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, + ACCOUNT_ID_SENDER, +}; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_protocol::transaction::memory::{ ACCT_DB_ROOT_PTR, BLOCK_COMMITMENT_PTR, BLOCK_METADATA_PTR, @@ -61,30 +78,12 @@ use miden_lib::transaction::memory::{ VALIDATOR_KEY_COMMITMENT_PTR, VERIFICATION_BASE_FEE_IDX, }; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ - Account, - AccountBuilder, - AccountId, - AccountIdVersion, - AccountProcedureRoot, - AccountStorage, - AccountStorageMode, - AccountType, - StorageMap, - StorageSlot, - StorageSlotName, -}; -use miden_objects::asset::{FungibleAsset, NonFungibleAsset}; -use miden_objects::testing::account_id::{ - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, - ACCOUNT_ID_SENDER, -}; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::transaction::{ExecutedTransaction, TransactionArgs}; -use miden_objects::{EMPTY_WORD, ONE, WORD_SIZE}; -use miden_processor::fast::ExecutionOutput; -use miden_processor::{AdviceInputs, Word}; +use miden_protocol::transaction::{ExecutedTransaction, TransactionArgs, TransactionKernel}; +use miden_protocol::{EMPTY_WORD, ONE, WORD_SIZE}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::TransactionExecutorError; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -124,7 +123,7 @@ async fn test_transaction_prologue() -> anyhow::Result<()> { }; let code = " - use.$kernel::prologue + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -743,7 +742,7 @@ pub async fn create_account_invalid_seed() -> anyhow::Result<()> { .build()?; let code = " - use.$kernel::prologue + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -761,8 +760,8 @@ pub async fn create_account_invalid_seed() -> anyhow::Result<()> { async fn test_get_blk_version() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code = " - use.$kernel::memory - use.$kernel::prologue + use $kernel::memory + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -787,8 +786,8 @@ async fn test_get_blk_version() -> anyhow::Result<()> { async fn test_get_blk_timestamp() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code = " - use.$kernel::memory - use.$kernel::prologue + use $kernel::memory + use $kernel::prologue begin exec.prologue::prepare_transaction diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index b8cb5480ae..7354f55c90 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -2,15 +2,8 @@ use alloc::sync::Arc; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::AuthScheme; -use miden_lib::account::interface::AccountInterface; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::note::create_p2id_note; -use miden_lib::testing::account_component::IncrNonceAuthComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::{ Account, AccountBuilder, AccountCode, @@ -21,11 +14,11 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::diagnostics::NamedSource; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; -use miden_objects::block::BlockNumber; -use miden_objects::note::{ +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::diagnostics::NamedSource; +use miden_protocol::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -38,7 +31,7 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_SENDER, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, @@ -46,17 +39,24 @@ use miden_objects::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }; -use miden_objects::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; -use miden_objects::testing::note::DEFAULT_NOTE_CODE; -use miden_objects::transaction::{ +use miden_protocol::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; +use miden_protocol::testing::note::DEFAULT_NOTE_CODE; +use miden_protocol::transaction::{ InputNotes, OutputNote, OutputNotes, TransactionArgs, + TransactionKernel, TransactionSummary, }; -use miden_objects::{Felt, FieldElement, Hasher, ONE, Word}; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::{Felt, FieldElement, Hasher, ONE, Word}; +use miden_standards::AuthScheme; +use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::note::create_p2id_note; +use miden_standards::testing::account_component::IncrNonceAuthComponent; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::auth::UnreachableAuth; use miden_tx::{TransactionExecutor, TransactionExecutorError}; @@ -133,8 +133,8 @@ async fn test_block_procedures() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let code = " - use.miden::tx - use.$kernel::prologue + use miden::protocol::tx + use $kernel::prologue begin exec.prologue::prepare_transaction @@ -245,12 +245,12 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let tx_script_src = format!( "\ - use.miden::contracts::wallets::basic->wallet - use.miden::output_note + use miden::standards::wallets::basic->wallet + use miden::protocol::output_note # Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] # Outputs: [note_idx] - proc.create_note + proc create_note # pad the stack before the call to prevent accidental modification of the deeper stack # elements padw padw swapdw @@ -266,7 +266,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { # Inputs: [ASSET, note_idx] # Outputs: [ASSET, note_idx] - proc.move_asset_to_note + proc move_asset_to_note # pad the stack before call push.0.0.0 movdn.7 movdn.7 movdn.7 padw padw swapdw # => [ASSET, note_idx, pad(11)] @@ -414,17 +414,17 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { #[tokio::test] async fn user_code_can_abort_transaction_with_summary() -> anyhow::Result<()> { let source_code = r#" - use.miden::auth - use.miden::tx - const.AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized") + use miden::standards::auth + use miden::protocol::tx + const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized") #! Inputs: [AUTH_ARGS, pad(12)] #! Outputs: [pad(16)] - export.auth_abort_tx + pub proc auth_abort_tx dropw # => [pad(16)] push.0.0 exec.tx::get_block_number - exec.::miden::native_account::incr_nonce + exec.::miden::protocol::native_account::incr_nonce # => [[final_nonce, block_num, 0, 0], pad(16)] # => [SALT, pad(16)] @@ -529,7 +529,7 @@ async fn tx_summary_commitment_is_signed_by_falcon_auth() -> anyhow::Result<()> ); let summary_commitment = summary.to_commitment(); - let account_interface = AccountInterface::from(&account); + let account_interface = AccountInterface::from_account(&account); let pub_key = match account_interface.auth().first().unwrap() { AuthScheme::RpoFalcon512 { pub_key } => pub_key, AuthScheme::NoAuth => panic!("Expected RpoFalcon512 auth scheme, got NoAuth"), @@ -593,7 +593,7 @@ async fn tx_summary_commitment_is_signed_by_ecdsa_auth() -> anyhow::Result<()> { ); let summary_commitment = summary.to_commitment(); - let account_interface = AccountInterface::from(&account); + let account_interface = AccountInterface::from_account(&account); let pub_key = match account_interface.auth().first().unwrap() { AuthScheme::EcdsaK256Keccak { pub_key } => pub_key, AuthScheme::EcdsaK256KeccakMultisig { .. } => { @@ -624,7 +624,7 @@ async fn tx_summary_commitment_is_signed_by_ecdsa_auth() -> anyhow::Result<()> { #[tokio::test] async fn execute_tx_view_script() -> anyhow::Result<()> { let test_module_source = " - export.foo + pub proc foo push.3.4 add swapw dropw @@ -638,8 +638,8 @@ async fn execute_tx_view_script() -> anyhow::Result<()> { let library = assembler.assemble_library([source]).unwrap(); let source = " - use.test::module_1 - use.std::sys + use test::module_1 + use miden::core::sys begin push.1.2 @@ -648,7 +648,7 @@ async fn execute_tx_view_script() -> anyhow::Result<()> { end "; - let tx_script = CodeBuilder::new(false) + let tx_script = CodeBuilder::new() .with_statically_linked_library(&library)? .compile_tx_script(source)?; let tx_context = TransactionContextBuilder::with_existing_mock_account() @@ -681,8 +681,6 @@ async fn test_tx_script_inputs() -> anyhow::Result<()> { let tx_script_input_value = Word::from([9, 8, 7, 6u32]); let tx_script_src = format!( " - use.miden::account - begin # push the tx script input key onto the stack push.{tx_script_input_key} @@ -714,8 +712,6 @@ async fn test_tx_script_args() -> anyhow::Result<()> { let tx_script_args = Word::from([1, 2, 3, 4u32]); let tx_script_src = r#" - use.miden::account - begin # => [TX_SCRIPT_ARGS] # `TX_SCRIPT_ARGS` value is a user provided word, which could be used during the @@ -759,9 +755,9 @@ async fn test_tx_script_args() -> anyhow::Result<()> { #[tokio::test] async fn inputs_created_correctly() -> anyhow::Result<()> { let account_component_masm = r#" - adv_map.A([6,7,8,9])=[10,11,12,13] + adv_map A([6,7,8,9]) = [10,11,12,13] - export.assert_adv_map + pub proc assert_adv_map # test tx script advice map push.[1,2,3,4] adv.push_mapval adv_loadw @@ -784,9 +780,7 @@ async fn inputs_created_correctly() -> anyhow::Result<()> { )?; let script = r#" - use.miden::account - - adv_map.A([1,2,3,4])=[5,6,7,8] + adv_map A([1,2,3,4]) = [5,6,7,8] begin call.::test::adv_map_component::assert_adv_map diff --git a/crates/miden-testing/src/mock_chain/auth.rs b/crates/miden-testing/src/mock_chain/auth.rs index 7de10857f8..33b3cd57cc 100644 --- a/crates/miden-testing/src/mock_chain/auth.rs +++ b/crates/miden-testing/src/mock_chain/auth.rs @@ -2,7 +2,11 @@ // ================================================================================================ use alloc::vec::Vec; -use miden_lib::account::auth::{ +use miden_protocol::Word; +use miden_protocol::account::AccountComponent; +use miden_protocol::account::auth::{AuthSecretKey, PublicKeyCommitment}; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_standards::account::auth::{ AuthEcdsaK256Keccak, AuthEcdsaK256KeccakAcl, AuthEcdsaK256KeccakAclConfig, @@ -14,11 +18,10 @@ use miden_lib::account::auth::{ AuthRpoFalcon512Multisig, AuthRpoFalcon512MultisigConfig, }; -use miden_lib::testing::account_component::{ConditionalAuthComponent, IncrNonceAuthComponent}; -use miden_objects::Word; -use miden_objects::account::AccountComponent; -use miden_objects::account::auth::{AuthSecretKey, PublicKeyCommitment}; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; +use miden_standards::testing::account_component::{ + ConditionalAuthComponent, + IncrNonceAuthComponent, +}; use miden_tx::auth::BasicAuthenticator; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; @@ -88,7 +91,7 @@ impl Auth { match self { Auth::BasicAuth => { let mut rng = ChaCha20Rng::from_seed(Default::default()); - let sec_key = AuthSecretKey::new_rpo_falcon512_with_rng(&mut rng); + let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); let component = AuthRpoFalcon512::new(pub_key).into(); @@ -138,7 +141,7 @@ impl Auth { allow_unauthorized_input_notes, } => { let mut rng = ChaCha20Rng::from_seed(Default::default()); - let sec_key = AuthSecretKey::new_rpo_falcon512_with_rng(&mut rng); + let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); let component = AuthRpoFalcon512Acl::new( diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index badceba12d..5e94bc5df9 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -3,15 +3,15 @@ use alloc::vec::Vec; use anyhow::Context; use miden_block_prover::LocalBlockProver; -use miden_lib::block::build_block; -use miden_objects::MIN_PROOF_SECURITY_LEVEL; -use miden_objects::account::auth::{AuthSecretKey, PublicKey}; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{Account, AccountId, PartialAccount}; -use miden_objects::batch::{ProposedBatch, ProvenBatch}; -use miden_objects::block::account_tree::{AccountTree, AccountWitness}; -use miden_objects::block::nullifier_tree::{NullifierTree, NullifierWitness}; -use miden_objects::block::{ +use miden_processor::DeserializationError; +use miden_protocol::MIN_PROOF_SECURITY_LEVEL; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey}; +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{Account, AccountId, PartialAccount}; +use miden_protocol::batch::{ProposedBatch, ProvenBatch}; +use miden_protocol::block::account_tree::{AccountTree, AccountWitness}; +use miden_protocol::block::nullifier_tree::{NullifierTree, NullifierWitness}; +use miden_protocol::block::{ BlockHeader, BlockInputs, BlockNumber, @@ -19,9 +19,9 @@ use miden_objects::block::{ ProposedBlock, ProvenBlock, }; -use miden_objects::crypto::dsa::ecdsa_k256_keccak::SecretKey; -use miden_objects::note::{Note, NoteHeader, NoteId, NoteInclusionProof, Nullifier}; -use miden_objects::transaction::{ +use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey; +use miden_protocol::note::{Note, NoteHeader, NoteId, NoteInclusionProof, Nullifier}; +use miden_protocol::transaction::{ ExecutedTransaction, InputNote, InputNotes, @@ -30,7 +30,6 @@ use miden_objects::transaction::{ ProvenTransaction, TransactionInputs, }; -use miden_processor::DeserializationError; use miden_tx::LocalTransactionProver; use miden_tx::auth::BasicAuthenticator; use miden_tx::utils::{ByteReader, Deserializable, Serializable}; @@ -64,7 +63,7 @@ use crate::{MockChainBuilder, TransactionContextBuilder}; /// ## Executing a simple transaction /// ``` /// # use anyhow::Result; -/// # use miden_objects::{ +/// # use miden_protocol::{ /// # asset::{Asset, FungibleAsset}, /// # note::NoteType, /// # }; @@ -128,7 +127,7 @@ use crate::{MockChainBuilder, TransactionContextBuilder}; /// /// ``` /// # use anyhow::Result; -/// # use miden_objects::{Felt, asset::{Asset, FungibleAsset}, note::NoteType}; +/// # use miden_protocol::{Felt, asset::{Asset, FungibleAsset}, note::NoteType}; /// # use miden_testing::{Auth, MockChain, TransactionContextBuilder}; /// # /// # #[tokio::main(flavor = "current_thread")] @@ -986,7 +985,7 @@ impl MockChain { /// Proves proposed block alongside a corresponding list of batches. pub fn prove_block(&self, proposed_block: ProposedBlock) -> anyhow::Result { - let (header, body) = build_block(proposed_block.clone())?; + let (header, body) = proposed_block.clone().into_header_and_body()?; let inputs = self.get_block_inputs(proposed_block.batches().as_slice())?; let block_proof = LocalBlockProver::new(MIN_PROOF_SECURITY_LEVEL).prove_dummy( proposed_block.batches().clone(), @@ -1150,15 +1149,15 @@ impl From for TxContextInput { #[cfg(test)] mod tests { - use miden_lib::account::wallets::BasicWallet; - use miden_objects::account::{AccountBuilder, AccountStorageMode}; - use miden_objects::asset::{Asset, FungibleAsset}; - use miden_objects::note::NoteType; - use miden_objects::testing::account_id::{ + use miden_protocol::account::{AccountBuilder, AccountStorageMode}; + use miden_protocol::asset::{Asset, FungibleAsset}; + use miden_protocol::note::NoteType; + use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_SENDER, }; + use miden_standards::account::wallets::BasicWallet; use super::*; use crate::Auth; diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index f3adace37b..34be3a1044 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -13,13 +13,9 @@ const DEFAULT_FAUCET_DECIMALS: u8 = 10; // ================================================================================================ use itertools::Itertools; -use miden_lib::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::note::{create_p2id_note, create_p2ide_note, create_swap_note}; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::transaction::TransactionKernel; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{ +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{ Account, AccountBuilder, AccountDelta, @@ -29,10 +25,10 @@ use miden_objects::account::{ AccountType, StorageSlot, }; -use miden_objects::asset::{Asset, FungibleAsset, TokenSymbol}; -use miden_objects::block::account_tree::AccountTree; -use miden_objects::block::nullifier_tree::NullifierTree; -use miden_objects::block::{ +use miden_protocol::asset::{Asset, FungibleAsset, TokenSymbol}; +use miden_protocol::block::account_tree::AccountTree; +use miden_protocol::block::nullifier_tree::NullifierTree; +use miden_protocol::block::{ BlockAccountUpdate, BlockBody, BlockHeader, @@ -44,14 +40,17 @@ use miden_objects::block::{ OutputNoteBatch, ProvenBlock, }; -use miden_objects::crypto::dsa::ecdsa_k256_keccak::SecretKey; -use miden_objects::crypto::merkle::Smt; -use miden_objects::note::{Note, NoteDetails, NoteType}; -use miden_objects::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; -use miden_objects::testing::random_signer::RandomBlockSigner; -use miden_objects::transaction::{OrderedTransactionHeaders, OutputNote}; -use miden_objects::{Felt, FieldElement, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey; +use miden_protocol::crypto::merkle::smt::Smt; +use miden_protocol::note::{Note, NoteDetails, NoteType}; +use miden_protocol::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; +use miden_protocol::testing::random_signer::RandomBlockSigner; +use miden_protocol::transaction::{OrderedTransactionHeaders, OutputNote, TransactionKernel}; +use miden_protocol::{Felt, FieldElement, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; +use miden_standards::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::note::{create_p2id_note, create_p2ide_note, create_swap_note}; +use miden_standards::testing::account_component::MockAccountComponent; use rand::Rng; use crate::mock_chain::chain::AccountAuthenticator; @@ -64,7 +63,7 @@ use crate::{AccountState, Auth, MockChain}; /// /// ``` /// # use anyhow::Result; -/// # use miden_objects::{ +/// # use miden_protocol::{ /// # asset::{Asset, FungibleAsset}, /// # note::NoteType, /// # }; diff --git a/crates/miden-testing/src/mock_chain/note.rs b/crates/miden-testing/src/mock_chain/note.rs index 799131c6ad..759ef257ea 100644 --- a/crates/miden-testing/src/mock_chain/note.rs +++ b/crates/miden-testing/src/mock_chain/note.rs @@ -1,6 +1,6 @@ -use miden_objects::note::{Note, NoteId, NoteInclusionProof, NoteMetadata}; -use miden_objects::transaction::InputNote; use miden_processor::DeserializationError; +use miden_protocol::note::{Note, NoteId, NoteInclusionProof, NoteMetadata}; +use miden_protocol::transaction::InputNote; use miden_tx::utils::{ByteReader, Deserializable, Serializable}; use winterfell::ByteWriter; diff --git a/crates/miden-testing/src/mock_host.rs b/crates/miden-testing/src/mock_host.rs index 92b08ecc9b..0bcb1d766f 100644 --- a/crates/miden-testing/src/mock_host.rs +++ b/crates/miden-testing/src/mock_host.rs @@ -2,9 +2,6 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::StdLibrary; -use miden_lib::transaction::{EventId, TransactionEventId}; -use miden_objects::Word; use miden_processor::{ AdviceMutation, AsyncHost, @@ -14,6 +11,9 @@ use miden_processor::{ MastForest, ProcessState, }; +use miden_protocol::transaction::TransactionEventId; +use miden_protocol::vm::EventId; +use miden_protocol::{CoreLibrary, Word}; use miden_tx::TransactionExecutorHost; use miden_tx::auth::UnreachableAuth; @@ -50,12 +50,12 @@ impl<'store> MockHost<'store> { pub fn new( exec_host: TransactionExecutorHost<'store, 'static, TransactionContext, UnreachableAuth>, ) -> Self { - // StdLibrary events are always handled. - let stdlib_handlers = StdLibrary::default() + // CoreLibrary events are always handled. + let core_lib_handlers = CoreLibrary::default() .handlers() .into_iter() .map(|(handler_event_name, _)| handler_event_name.to_event_id()); - let mut handled_events = BTreeSet::from_iter(stdlib_handlers); + let mut handled_events = BTreeSet::from_iter(core_lib_handlers); // The default set of transaction events that are always handled. handled_events.extend( @@ -93,10 +93,10 @@ impl<'store> MockHost<'store> { impl<'store> BaseHost for MockHost<'store> { fn get_label_and_source_file( &self, - location: &miden_objects::assembly::debuginfo::Location, + location: &miden_protocol::assembly::debuginfo::Location, ) -> ( - miden_objects::assembly::debuginfo::SourceSpan, - Option>, + miden_protocol::assembly::debuginfo::SourceSpan, + Option>, ) { self.exec_host.get_label_and_source_file(location) } diff --git a/crates/miden-testing/src/tx_context/builder.rs b/crates/miden-testing/src/tx_context/builder.rs index ba6e20f708..453bb2c73e 100644 --- a/crates/miden-testing/src/tx_context/builder.rs +++ b/crates/miden-testing/src/tx_context/builder.rs @@ -6,24 +6,24 @@ use alloc::sync::Arc; use alloc::vec::Vec; use anyhow::Context; -use miden_lib::testing::account_component::IncrNonceAuthComponent; -use miden_lib::testing::mock_account::MockAccountExt; -use miden_objects::EMPTY_WORD; -use miden_objects::account::auth::{PublicKeyCommitment, Signature}; -use miden_objects::account::{Account, AccountHeader, AccountId}; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::debuginfo::SourceManagerSync; -use miden_objects::block::account_tree::AccountWitness; -use miden_objects::note::{Note, NoteId, NoteScript}; -use miden_objects::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; -use miden_objects::testing::noop_auth_component::NoopAuthComponent; -use miden_objects::transaction::{ +use miden_processor::{AdviceInputs, Felt, Word}; +use miden_protocol::EMPTY_WORD; +use miden_protocol::account::auth::{PublicKeyCommitment, Signature}; +use miden_protocol::account::{Account, AccountHeader, AccountId}; +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::debuginfo::SourceManagerSync; +use miden_protocol::block::account_tree::AccountWitness; +use miden_protocol::note::{Note, NoteId, NoteScript}; +use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; +use miden_protocol::testing::noop_auth_component::NoopAuthComponent; +use miden_protocol::transaction::{ OutputNote, TransactionArgs, TransactionInputs, TransactionScript, }; -use miden_processor::{AdviceInputs, Felt, Word}; +use miden_standards::testing::account_component::IncrNonceAuthComponent; +use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::TransactionMastStore; use miden_tx::auth::BasicAuthenticator; @@ -43,16 +43,16 @@ use crate::{MockChain, MockChainNote}; /// ``` /// # use anyhow::Result; /// # use miden_testing::TransactionContextBuilder; -/// # use miden_objects::{account::AccountBuilder,Felt, FieldElement}; -/// # use miden_lib::transaction::TransactionKernel; +/// # use miden_protocol::{account::AccountBuilder,Felt, FieldElement}; +/// # use miden_protocol::transaction::TransactionKernel; /// # /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<()> { /// let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; /// /// let code = " -/// use.$kernel::prologue -/// use.mock::account +/// use $kernel::prologue +/// use mock::account /// /// begin /// exec.prologue::prepare_transaction @@ -109,11 +109,11 @@ impl TransactionContextBuilder { /// /// The wallet: /// - /// - Includes a series of mocked assets ([miden_objects::asset::AssetVault::mock()]). + /// - Includes a series of mocked assets ([miden_protocol::asset::AssetVault::mock()]). /// - Has a nonce of `1` (so it does not imply seed validation). /// - Has an ID of [`ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE`]. /// - Has an account code based on an - /// [miden_lib::testing::account_component::MockAccountComponent]. + /// [miden_standards::testing::account_component::MockAccountComponent]. pub fn with_existing_mock_account() -> Self { Self::new(Account::mock( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, diff --git a/crates/miden-testing/src/tx_context/context.rs b/crates/miden-testing/src/tx_context/context.rs index 4f2db63216..274b16672b 100644 --- a/crates/miden-testing/src/tx_context/context.rs +++ b/crates/miden-testing/src/tx_context/context.rs @@ -3,22 +3,22 @@ use alloc::collections::{BTreeMap, BTreeSet}; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::TransactionKernel; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ +use miden_processor::fast::ExecutionOutput; +use miden_processor::{ExecutionError, FutureMaybeSend, MastForest, MastForestStore, Word}; +use miden_protocol::account::{ Account, AccountId, PartialAccount, StorageMapWitness, StorageSlotContent, }; -use miden_objects::assembly::debuginfo::{SourceLanguage, Uri}; -use miden_objects::assembly::{Assembler, SourceManager, SourceManagerSync}; -use miden_objects::asset::{Asset, AssetVaultKey, AssetWitness}; -use miden_objects::block::account_tree::AccountWitness; -use miden_objects::block::{BlockHeader, BlockNumber}; -use miden_objects::note::{Note, NoteScript}; -use miden_objects::transaction::{ +use miden_protocol::assembly::debuginfo::{SourceLanguage, Uri}; +use miden_protocol::assembly::{Assembler, SourceManager, SourceManagerSync}; +use miden_protocol::asset::{Asset, AssetVaultKey, AssetWitness}; +use miden_protocol::block::account_tree::AccountWitness; +use miden_protocol::block::{BlockHeader, BlockNumber}; +use miden_protocol::note::{Note, NoteScript}; +use miden_protocol::transaction::{ AccountInputs, ExecutedTransaction, InputNote, @@ -26,9 +26,9 @@ use miden_objects::transaction::{ PartialBlockchain, TransactionArgs, TransactionInputs, + TransactionKernel, }; -use miden_processor::fast::ExecutionOutput; -use miden_processor::{ExecutionError, FutureMaybeSend, MastForest, MastForestStore, Word}; +use miden_standards::code_builder::CodeBuilder; use miden_tx::auth::{BasicAuthenticator, UnreachableAuth}; use miden_tx::{ AccountProcedureIndexMap, @@ -73,8 +73,8 @@ impl TransactionContext { /// by the transaction kernel, and also individual kernel functions (not normally exposed). /// /// To improve the error message quality, convert the returned [`ExecutionError`] into a - /// [`Report`](miden_objects::assembly::diagnostics::Report) or use `?` with - /// [`miden_objects::assembly::diagnostics::Result`]. + /// [`Report`](miden_protocol::assembly::diagnostics::Report) or use `?` with + /// [`miden_protocol::assembly::diagnostics::Result`]. /// /// # Errors /// @@ -133,7 +133,6 @@ impl TransactionContext { .into(); let program = assembler - .with_debug_mode(true) .assemble_program(virtual_source_file) .expect("code was not well formed"); @@ -402,9 +401,9 @@ impl MastForestStore for TransactionContext { #[cfg(test)] mod tests { - use miden_objects::Felt; - use miden_objects::assembly::Assembler; - use miden_objects::note::NoteScript; + use miden_protocol::Felt; + use miden_protocol::assembly::Assembler; + use miden_protocol::note::NoteScript; use super::*; use crate::TransactionContextBuilder; diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index 4b430f47cd..11007500a9 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -1,15 +1,15 @@ use alloc::string::String; use alloc::vec::Vec; -use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::AccountId; -use miden_objects::asset::Asset; -use miden_objects::crypto::rand::FeltRng; -use miden_objects::note::{Note, NoteType}; -use miden_objects::testing::storage::prepare_assets; use miden_processor::Felt; use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::AccountId; +use miden_protocol::asset::Asset; +use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::note::{Note, NoteType}; +use miden_protocol::testing::storage::prepare_assets; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::note::NoteBuilder; use rand::SeedableRng; use rand::rngs::SmallRng; @@ -20,7 +20,7 @@ use rand::rngs::SmallRng; macro_rules! assert_execution_error { ($execution_result:expr, $expected_err:expr) => { match $execution_result { - Err(miden_processor::ExecutionError::FailedAssertion { label: _, source_file: _, clk: _, err_code, err_msg }) => { + Err(miden_processor::ExecutionError::FailedAssertion { label: _, source_file: _, clk: _, err_code, err_msg, err: _ }) => { if let Some(ref msg) = err_msg { assert_eq!(msg.as_ref(), $expected_err.message(), "error messages did not match"); } @@ -48,6 +48,7 @@ macro_rules! assert_transaction_executor_error { clk: _, err_code, err_msg, + err: _, }, )) => { if let Some(ref msg) = err_msg { @@ -128,9 +129,9 @@ pub fn create_p2any_note( let code = format!( " - use.mock::account - use.miden::active_note - use.miden::contracts::wallets::basic->wallet + use mock::account + use miden::protocol::active_note + use miden::standards::wallets::basic->wallet begin # fetch pointer & number of assets @@ -198,7 +199,7 @@ fn note_script_that_creates_notes<'note>( sender_id: AccountId, output_notes: impl Iterator, ) -> anyhow::Result { - let mut out = String::from("use.miden::output_note\n\nbegin\n"); + let mut out = String::from("use miden::protocol::output_note\n\nbegin\n"); for (idx, note) in output_notes.into_iter().enumerate() { anyhow::ensure!( @@ -208,7 +209,7 @@ fn note_script_that_creates_notes<'note>( // Make sure that the transaction's native account matches the note sender. out.push_str(&format!( - r#"exec.::miden::native_account::get_id + r#"exec.::miden::protocol::native_account::get_id # => [native_account_id_prefix, native_account_id_suffix] push.{sender_prefix} assert_eq.err="sender ID prefix does not match native account ID's prefix" # => [native_account_id_suffix] @@ -243,7 +244,7 @@ fn note_script_that_creates_notes<'note>( for asset in assets_str { out.push_str(&format!( " push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note\n", + call.::miden::standards::wallets::basic::move_asset_to_note\n", )); } } diff --git a/crates/miden-testing/tests/agglayer/asset_conversion.rs b/crates/miden-testing/tests/agglayer/asset_conversion.rs index 9a72ca1430..2e1ba5c567 100644 --- a/crates/miden-testing/tests/agglayer/asset_conversion.rs +++ b/crates/miden-testing/tests/agglayer/asset_conversion.rs @@ -2,13 +2,13 @@ extern crate alloc; use alloc::sync::Arc; -use miden_lib::StdLibrary; -use miden_lib::agglayer::{asset_conversion_library, utils}; -use miden_lib::transaction::TransactionKernel; -use miden_objects::Felt; -use miden_objects::assembly::{Assembler, DefaultSourceManager}; +use miden_agglayer::{agglayer_library, utils}; +use miden_assembly::{Assembler, DefaultSourceManager}; +use miden_core_lib::CoreLibrary; use miden_processor::fast::{ExecutionOutput, FastProcessor}; use miden_processor::{AdviceInputs, DefaultHost, ExecutionError, Program, StackInputs}; +use miden_protocol::Felt; +use miden_protocol::transaction::TransactionKernel; use primitive_types::U256; /// Convert a Vec to a U256 @@ -35,10 +35,10 @@ async fn execute_program_with_default_host( let test_lib = TransactionKernel::library(); host.load_library(test_lib.mast_forest()).unwrap(); - let std_lib = StdLibrary::default(); + let std_lib = CoreLibrary::default(); host.load_library(std_lib.mast_forest()).unwrap(); - let asset_conversion_lib = miden_lib::agglayer::asset_conversion_library(); + let asset_conversion_lib = agglayer_library(); host.load_library(asset_conversion_lib.mast_forest()).unwrap(); let stack_inputs = StackInputs::new(vec![]).unwrap(); @@ -55,15 +55,16 @@ async fn test_convert_to_u256_helper( expected_result_array: [u32; 8], expected_result_u256: U256, ) -> anyhow::Result<()> { - let asset_conversion_lib = asset_conversion_library(); + let asset_conversion_lib = agglayer_library(); let script_code = format!( " - use.std::sys + use miden::core::sys + use agglayer::asset_conversion begin push.{}.{} - exec.::scale_native_amount_to_u256 + exec.asset_conversion::scale_native_amount_to_u256 exec.sys::truncate_stack end ", @@ -71,8 +72,7 @@ async fn test_convert_to_u256_helper( ); let program = Assembler::new(Arc::new(DefaultSourceManager::default())) - .with_debug_mode(true) - .with_dynamic_library(StdLibrary::default()) + .with_dynamic_library(CoreLibrary::default()) .unwrap() .with_dynamic_library(asset_conversion_lib.clone()) .unwrap() @@ -132,15 +132,16 @@ async fn test_convert_to_u256_scaled_eth() -> anyhow::Result<()> { // scale to 1e18 let target_scale = Felt::new(12); - let asset_conversion_lib = asset_conversion_library(); + let asset_conversion_lib = agglayer_library(); let script_code = format!( " - use.std::sys + use miden::core::sys + use agglayer::asset_conversion begin push.{}.{} - exec.::scale_native_amount_to_u256 + exec.asset_conversion::scale_native_amount_to_u256 exec.sys::truncate_stack end ", @@ -148,8 +149,7 @@ async fn test_convert_to_u256_scaled_eth() -> anyhow::Result<()> { ); let program = Assembler::new(Arc::new(DefaultSourceManager::default())) - .with_debug_mode(true) - .with_dynamic_library(StdLibrary::default()) + .with_dynamic_library(CoreLibrary::default()) .unwrap() .with_dynamic_library(asset_conversion_lib.clone()) .unwrap() @@ -174,16 +174,17 @@ async fn test_convert_to_u256_scaled_large_amount() -> anyhow::Result<()> { // scale to base 1e18 let scale_exponent = Felt::new(8); - let asset_conversion_lib = asset_conversion_library(); + let asset_conversion_lib = agglayer_library(); let script_code = format!( " - use.std::sys - + use miden::core::sys + use agglayer::asset_conversion + begin push.{}.{} - exec.::scale_native_amount_to_u256 + exec.asset_conversion::scale_native_amount_to_u256 exec.sys::truncate_stack end ", @@ -191,8 +192,7 @@ async fn test_convert_to_u256_scaled_large_amount() -> anyhow::Result<()> { ); let program = Assembler::new(Arc::new(DefaultSourceManager::default())) - .with_debug_mode(true) - .with_dynamic_library(StdLibrary::default()) + .with_dynamic_library(CoreLibrary::default()) .unwrap() .with_dynamic_library(asset_conversion_lib.clone()) .unwrap() diff --git a/crates/miden-testing/tests/agglayer/bridge_out.rs b/crates/miden-testing/tests/agglayer/bridge_out.rs index 21d8351509..e5be8f3814 100644 --- a/crates/miden-testing/tests/agglayer/bridge_out.rs +++ b/crates/miden-testing/tests/agglayer/bridge_out.rs @@ -1,10 +1,8 @@ extern crate alloc; -use miden_lib::account::faucets::FungibleFaucetExt; -use miden_lib::agglayer::utils::ethereum_address_string_to_felts; -use miden_lib::agglayer::{b2agg_script, bridge_out_component}; -use miden_lib::note::WellKnownNote; -use miden_objects::account::{ +use miden_agglayer::utils::ethereum_address_string_to_felts; +use miden_agglayer::{b2agg_script, bridge_out_component}; +use miden_protocol::account::{ Account, AccountId, AccountIdVersion, @@ -13,8 +11,8 @@ use miden_objects::account::{ StorageSlot, StorageSlotName, }; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::{ +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -25,8 +23,10 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, Word}; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, Word}; +use miden_standards::account::faucets::FungibleFaucetExt; +use miden_standards::note::WellKnownNote; use miden_testing::{AccountState, Auth, MockChain}; use rand::Rng; @@ -97,7 +97,8 @@ async fn test_bridge_out_consumes_b2agg_note() -> anyhow::Result<()> { NoteMetadata::new(faucet.id(), note_type, tag, note_execution_hint, aux)?; let b2agg_note_assets = NoteAssets::new(vec![bridge_asset])?; let serial_num = Word::from([1, 2, 3, 4u32]); - let b2agg_note_recipient = NoteRecipient::new(serial_num, b2agg_script, inputs); + let b2agg_note_script = NoteScript::new(b2agg_script); + let b2agg_note_recipient = NoteRecipient::new(serial_num, b2agg_note_script, inputs); let b2agg_note = Note::new(b2agg_note_assets, b2agg_note_metadata, b2agg_note_recipient); // Add the B2AGG note to the mock chain @@ -255,7 +256,8 @@ async fn test_b2agg_note_reclaim_scenario() -> anyhow::Result<()> { NoteMetadata::new(user_account.id(), note_type, tag, note_execution_hint, aux)?; let b2agg_note_assets = NoteAssets::new(vec![bridge_asset])?; let serial_num = Word::from([1, 2, 3, 4u32]); - let b2agg_note_recipient = NoteRecipient::new(serial_num, b2agg_script, inputs); + let b2agg_note_script = NoteScript::new(b2agg_script); + let b2agg_note_recipient = NoteRecipient::new(serial_num, b2agg_note_script, inputs); let b2agg_note = Note::new(b2agg_note_assets, b2agg_note_metadata, b2agg_note_recipient); // Add the B2AGG note to the mock chain diff --git a/crates/miden-testing/tests/auth/ecdsa_acl.rs b/crates/miden-testing/tests/auth/ecdsa_acl.rs index 0a5d1980ec..9bdaf80ebc 100644 --- a/crates/miden-testing/tests/auth/ecdsa_acl.rs +++ b/crates/miden-testing/tests/auth/ecdsa_acl.rs @@ -1,11 +1,7 @@ use core::slice; use assert_matches::assert_matches; -use miden_lib::account::auth::AuthEcdsaK256KeccakAcl; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -13,10 +9,14 @@ use miden_objects::account::{ AccountStorageMode, AccountType, }; -use miden_objects::note::Note; -use miden_objects::testing::storage::MOCK_VALUE_SLOT0; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::note::Note; +use miden_protocol::testing::storage::MOCK_VALUE_SLOT0; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, FieldElement, Word}; +use miden_standards::account::auth::AuthEcdsaK256KeccakAcl; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain}; use miden_tx::TransactionExecutorError; @@ -26,7 +26,7 @@ use crate::prove_and_verify_transaction; // ================================================================================================ const TX_SCRIPT_NO_TRIGGER: &str = r#" - use.mock::account + use mock::account begin call.account::account_procedure_1 drop @@ -46,10 +46,10 @@ fn setup_ecdsa_acl_test( MockAccountComponent::with_slots(AccountStorage::mock_storage_slots()).into(); let get_item_proc_root = component - .get_procedure_root_by_name("mock::account::get_item") + .get_procedure_root_by_path("mock::account::get_item") .expect("get_item procedure should exist"); let set_item_proc_root = component - .get_procedure_root_by_name("mock::account::set_item") + .get_procedure_root_by_path("mock::account::set_item") .expect("set_item procedure should exist"); let auth_trigger_procedures = vec![get_item_proc_root, set_item_proc_root]; @@ -88,10 +88,10 @@ async fn test_ecdsa_acl() -> anyhow::Result<()> { MockAccountComponent::with_slots(AccountStorage::mock_storage_slots()).into(); let get_item_proc_root = component - .get_procedure_root_by_name("mock::account::get_item") + .get_procedure_root_by_path("mock::account::get_item") .expect("get_item procedure should exist"); let set_item_proc_root = component - .get_procedure_root_by_name("mock::account::set_item") + .get_procedure_root_by_path("mock::account::set_item") .expect("set_item procedure should exist"); let auth_trigger_procedures = vec![get_item_proc_root, set_item_proc_root]; @@ -104,7 +104,7 @@ async fn test_ecdsa_acl() -> anyhow::Result<()> { let tx_script_with_trigger_1 = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -119,7 +119,7 @@ async fn test_ecdsa_acl() -> anyhow::Result<()> { let tx_script_with_trigger_2 = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") diff --git a/crates/miden-testing/tests/auth/ecdsa_multisig.rs b/crates/miden-testing/tests/auth/ecdsa_multisig.rs index 35e716e087..c2ff6fad66 100644 --- a/crates/miden-testing/tests/auth/ecdsa_multisig.rs +++ b/crates/miden-testing/tests/auth/ecdsa_multisig.rs @@ -1,24 +1,30 @@ -use miden_lib::account::auth::AuthEcdsaK256KeccakMultisig; -use miden_lib::account::components::ecdsa_k256_keccak_multisig_library; -use miden_lib::account::interface::AccountInterface; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::tx_kernel_errors::ERR_TX_ALREADY_EXECUTED; -use miden_lib::note::create_p2id_note; -use miden_lib::testing::account_interface::get_public_keys_from_account; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::auth::{AuthSecretKey, PublicKey}; -use miden_objects::account::{Account, AccountBuilder, AccountId, AccountStorageMode, AccountType}; -use miden_objects::asset::FungibleAsset; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::{ +use miden_processor::AdviceInputs; +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey}; +use miden_protocol::account::{ + Account, + AccountBuilder, + AccountId, + AccountStorageMode, + AccountType, +}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, }; -use miden_objects::transaction::OutputNote; -use miden_objects::vm::AdviceMap; -use miden_objects::{Felt, Hasher, Word}; -use miden_processor::AdviceInputs; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::transaction::OutputNote; +use miden_protocol::vm::AdviceMap; +use miden_protocol::{Felt, Hasher, Word}; +use miden_standards::account::auth::AuthEcdsaK256KeccakMultisig; +use miden_standards::account::components::ecdsa_k256_keccak_multisig_library; +use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::errors::standards::ERR_TX_ALREADY_EXECUTED; +use miden_standards::note::create_p2id_note; +use miden_standards::testing::account_interface::get_public_keys_from_account; use miden_testing::utils::create_spawn_note; use miden_testing::{Auth, MockChainBuilder, assert_transaction_executor_error}; use miden_tx::TransactionExecutorError; @@ -399,7 +405,7 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::update_signers_and_threshold + call.::ecdsa_k256_keccak_multisig::update_signers_and_threshold end "; @@ -644,7 +650,9 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { // Create transaction script let tx_script = CodeBuilder::default() .with_dynamically_linked_library(ecdsa_k256_keccak_multisig_library())? - .compile_tx_script("begin\n call.::update_signers_and_threshold\nend")?; + .compile_tx_script( + "begin\n call.::ecdsa_k256_keccak_multisig::update_signers_and_threshold\nend", + )?; let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; @@ -846,7 +854,7 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::update_signers_and_threshold + call.::ecdsa_k256_keccak_multisig::update_signers_and_threshold end "; @@ -999,12 +1007,9 @@ async fn test_multisig_proc_threshold_overrides() -> anyhow::Result<()> { Default::default(), &mut RpoRandomCoin::new(Word::from([Felt::new(42); 4])), )?; - let multisig_account_interface = AccountInterface::from(&multisig_account); - let send_note_transaction_script = multisig_account_interface.build_send_notes_script( - &[output_note.clone().into()], - None, - false, - )?; + let multisig_account_interface = AccountInterface::from_account(&multisig_account); + let send_note_transaction_script = + multisig_account_interface.build_send_notes_script(&[output_note.clone().into()], None)?; // Execute transaction without signatures to get tx summary let tx_context_init = mock_chain diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index c35ffcd880..2c32f08211 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -1,24 +1,30 @@ -use miden_lib::account::auth::AuthRpoFalcon512Multisig; -use miden_lib::account::components::rpo_falcon_512_multisig_library; -use miden_lib::account::interface::AccountInterface; -use miden_lib::account::wallets::BasicWallet; -use miden_lib::errors::tx_kernel_errors::ERR_TX_ALREADY_EXECUTED; -use miden_lib::note::create_p2id_note; -use miden_lib::testing::account_interface::get_public_keys_from_account; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::auth::{AuthSecretKey, PublicKey}; -use miden_objects::account::{Account, AccountBuilder, AccountId, AccountStorageMode, AccountType}; -use miden_objects::asset::FungibleAsset; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::{ +use miden_processor::AdviceInputs; +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey}; +use miden_protocol::account::{ + Account, + AccountBuilder, + AccountId, + AccountStorageMode, + AccountType, +}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, }; -use miden_objects::transaction::OutputNote; -use miden_objects::vm::AdviceMap; -use miden_objects::{Felt, Hasher, Word}; -use miden_processor::AdviceInputs; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::transaction::OutputNote; +use miden_protocol::vm::AdviceMap; +use miden_protocol::{Felt, Hasher, Word}; +use miden_standards::account::auth::AuthRpoFalcon512Multisig; +use miden_standards::account::components::rpo_falcon_512_multisig_library; +use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::errors::standards::ERR_TX_ALREADY_EXECUTED; +use miden_standards::note::create_p2id_note; +use miden_standards::testing::account_interface::get_public_keys_from_account; use miden_testing::utils::create_spawn_note; use miden_testing::{Auth, MockChainBuilder, assert_transaction_executor_error}; use miden_tx::TransactionExecutorError; @@ -45,7 +51,7 @@ fn setup_keys_and_authenticators( let mut authenticators = Vec::new(); for _ in 0..num_approvers { - let sec_key = AuthSecretKey::new_rpo_falcon512_with_rng(&mut rng); + let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key(); secret_keys.push(sec_key); @@ -395,7 +401,7 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::update_signers_and_threshold + call.::rpo_falcon_512_multisig::update_signers_and_threshold end "; @@ -639,7 +645,9 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { // Create transaction script let tx_script = CodeBuilder::default() .with_dynamically_linked_library(rpo_falcon_512_multisig_library())? - .compile_tx_script("begin\n call.::update_signers_and_threshold\nend")?; + .compile_tx_script( + "begin\n call.::rpo_falcon_512_multisig::update_signers_and_threshold\nend", + )?; let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; @@ -836,7 +844,7 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::update_signers_and_threshold + call.::rpo_falcon_512_multisig::update_signers_and_threshold end "; @@ -989,12 +997,9 @@ async fn test_multisig_proc_threshold_overrides() -> anyhow::Result<()> { Default::default(), &mut RpoRandomCoin::new(Word::from([Felt::new(42); 4])), )?; - let multisig_account_interface = AccountInterface::from(&multisig_account); - let send_note_transaction_script = multisig_account_interface.build_send_notes_script( - &[output_note.clone().into()], - None, - false, - )?; + let multisig_account_interface = AccountInterface::from_account(&multisig_account); + let send_note_transaction_script = + multisig_account_interface.build_send_notes_script(&[output_note.clone().into()], None)?; // Execute transaction without signatures to get tx summary let tx_context_init = mock_chain diff --git a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs index 791b33ffc4..91519a07bb 100644 --- a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs +++ b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs @@ -2,11 +2,7 @@ use core::slice; use anyhow::Context; use assert_matches::assert_matches; -use miden_lib::account::auth::AuthRpoFalcon512Acl; -use miden_lib::testing::account_component::MockAccountComponent; -use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ +use miden_protocol::account::{ Account, AccountBuilder, AccountComponent, @@ -14,10 +10,14 @@ use miden_objects::account::{ AccountStorageMode, AccountType, }; -use miden_objects::note::Note; -use miden_objects::testing::storage::MOCK_VALUE_SLOT0; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, FieldElement, Word}; +use miden_protocol::note::Note; +use miden_protocol::testing::storage::MOCK_VALUE_SLOT0; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, FieldElement, Word}; +use miden_standards::account::auth::AuthRpoFalcon512Acl; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::testing::account_component::MockAccountComponent; +use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain}; use miden_tx::TransactionExecutorError; @@ -25,7 +25,7 @@ use miden_tx::TransactionExecutorError; // ================================================================================================ const TX_SCRIPT_NO_TRIGGER: &str = r#" - use.mock::account + use mock::account begin call.account::account_procedure_1 drop @@ -45,10 +45,10 @@ fn setup_rpo_falcon_acl_test( MockAccountComponent::with_slots(AccountStorage::mock_storage_slots()).into(); let get_item_proc_root = component - .get_procedure_root_by_name("mock::account::get_item") + .get_procedure_root_by_path("mock::account::get_item") .expect("get_item procedure should exist"); let set_item_proc_root = component - .get_procedure_root_by_name("mock::account::set_item") + .get_procedure_root_by_path("mock::account::set_item") .expect("set_item procedure should exist"); let auth_trigger_procedures = vec![get_item_proc_root, set_item_proc_root]; @@ -87,10 +87,10 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { MockAccountComponent::with_slots(AccountStorage::mock_storage_slots()).into(); let get_item_proc_root = component - .get_procedure_root_by_name("mock::account::get_item") + .get_procedure_root_by_path("mock::account::get_item") .expect("get_item procedure should exist"); let set_item_proc_root = component - .get_procedure_root_by_name("mock::account::set_item") + .get_procedure_root_by_path("mock::account::set_item") .expect("set_item procedure should exist"); let auth_trigger_procedures = vec![get_item_proc_root, set_item_proc_root]; @@ -103,7 +103,7 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { let tx_script_with_trigger_1 = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") @@ -118,7 +118,7 @@ async fn test_rpo_falcon_acl() -> anyhow::Result<()> { let tx_script_with_trigger_2 = format!( r#" - use.mock::account + use mock::account const MOCK_VALUE_SLOT0 = word("{mock_value_slot0}") diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index 2171d2dc5b..7df53cd58e 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -5,15 +5,15 @@ mod auth; mod scripts; mod wallet; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::AccountId; -use miden_objects::asset::FungibleAsset; -use miden_objects::crypto::utils::Serializable; -use miden_objects::note::{Note, NoteAssets, NoteInputs, NoteMetadata, NoteRecipient, NoteType}; -use miden_objects::testing::account_id::ACCOUNT_ID_SENDER; -use miden_objects::transaction::{ExecutedTransaction, ProvenTransaction}; -use miden_objects::{Word, ZERO}; use miden_processor::utils::Deserializable; +use miden_protocol::account::AccountId; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::crypto::utils::Serializable; +use miden_protocol::note::{Note, NoteAssets, NoteInputs, NoteMetadata, NoteRecipient, NoteType}; +use miden_protocol::testing::account_id::ACCOUNT_ID_SENDER; +use miden_protocol::transaction::{ExecutedTransaction, ProvenTransaction}; +use miden_protocol::{Word, ZERO}; +use miden_standards::code_builder::CodeBuilder; use miden_tx::{ LocalTransactionProver, ProvingOptions, @@ -28,7 +28,7 @@ use miden_tx::{ pub fn prove_and_verify_transaction( executed_transaction: ExecutedTransaction, ) -> Result<(), TransactionVerifierError> { - use miden_objects::transaction::TransactionHeader; + use miden_protocol::transaction::TransactionHeader; let executed_transaction_id = executed_transaction.id(); let executed_tx_header = TransactionHeader::from(&executed_transaction); @@ -47,7 +47,7 @@ pub fn prove_and_verify_transaction( let proven_transaction = ProvenTransaction::read_from_bytes(&serialised_transaction).unwrap(); // Verify that the generated proof is valid - let verifier = TransactionVerifier::new(miden_objects::MIN_PROOF_SECURITY_LEVEL); + let verifier = TransactionVerifier::new(miden_protocol::MIN_PROOF_SECURITY_LEVEL); verifier.verify(&proven_transaction) } @@ -57,7 +57,7 @@ pub fn get_note_with_fungible_asset_and_script( fungible_asset: FungibleAsset, note_script: &str, ) -> Note { - use miden_objects::note::NoteExecutionHint; + use miden_protocol::note::NoteExecutionHint; let note_script = CodeBuilder::default().compile_note_script(note_script).unwrap(); let serial_num = Word::from([1, 2, 3, 4u32]); diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 11b4b1c96a..38b378a903 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -3,21 +3,17 @@ extern crate alloc; use alloc::sync::Arc; use core::slice; -use miden_lib::account::faucets::{BasicFungibleFaucet, FungibleFaucetExt, NetworkFungibleFaucet}; -use miden_lib::errors::tx_kernel_errors::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; -use miden_lib::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; -use miden_lib::testing::note::NoteBuilder; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{ +use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::account::{ Account, AccountId, AccountIdVersion, AccountStorageMode, AccountType, }; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::note::{ +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -29,10 +25,18 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::ACCOUNT_ID_PRIVATE_SENDER; -use miden_objects::transaction::{ExecutedTransaction, OutputNote}; -use miden_objects::{Felt, Word}; -use miden_processor::crypto::RpoRandomCoin; +use miden_protocol::testing::account_id::ACCOUNT_ID_PRIVATE_SENDER; +use miden_protocol::transaction::{ExecutedTransaction, OutputNote}; +use miden_protocol::{Felt, Word}; +use miden_standards::account::faucets::{ + BasicFungibleFaucet, + FungibleFaucetExt, + NetworkFungibleFaucet, +}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::errors::standards::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; +use miden_standards::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; +use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain, assert_transaction_executor_error}; use crate::scripts::swap::create_p2id_note_exact; @@ -67,7 +71,7 @@ pub fn create_mint_script_code(params: &FaucetTestParams) -> String { push.{amount} # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] - call.::miden::contracts::faucets::basic_fungible::distribute + call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] # truncate the stack @@ -186,7 +190,7 @@ async fn faucet_contract_mint_fungible_asset_fails_exceeds_max_supply() -> anyho push.{amount} # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] - call.::miden::contracts::faucets::basic_fungible::distribute + call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] # truncate the stack @@ -263,7 +267,7 @@ async fn prove_burning_fungible_asset_on_existing_faucet_succeeds() -> anyhow::R dropw # => [] - call.::miden::contracts::faucets::basic_fungible::burn + call.::miden::standards::faucets::basic_fungible::burn # => [ASSET] # truncate the stack @@ -358,7 +362,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let trigger_note_script_code = format!( " - use.miden::note + use miden::protocol::note begin # Build recipient hash from SERIAL_NUM, SCRIPT_ROOT, and INPUTS_COMMITMENT @@ -391,7 +395,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul push.{amount} # => [amount, tag, aux, note_type, execution_hint, RECIPIENT] - call.::miden::contracts::faucets::basic_fungible::distribute + call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] # Truncate the stack diff --git a/crates/miden-testing/tests/scripts/fee.rs b/crates/miden-testing/tests/scripts/fee.rs index 70622e4819..3fd41d7b78 100644 --- a/crates/miden-testing/tests/scripts/fee.rs +++ b/crates/miden-testing/tests/scripts/fee.rs @@ -1,6 +1,6 @@ use anyhow::Context; -use miden_objects::asset::FungibleAsset; -use miden_objects::{self, Felt, Word}; +use miden_protocol::asset::FungibleAsset; +use miden_protocol::{self, Felt, Word}; use miden_testing::{Auth, MockChain}; use crate::prove_and_verify_transaction; diff --git a/crates/miden-testing/tests/scripts/p2id.rs b/crates/miden-testing/tests/scripts/p2id.rs index 7e4b7a5874..870bd7a193 100644 --- a/crates/miden-testing/tests/scripts/p2id.rs +++ b/crates/miden-testing/tests/scripts/p2id.rs @@ -1,19 +1,19 @@ -use miden_lib::errors::note_script_errors::ERR_P2ID_TARGET_ACCT_MISMATCH; -use miden_lib::note::create_p2id_note; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::Account; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset}; -use miden_objects::crypto::rand::RpoRandomCoin; -use miden_objects::note::NoteType; -use miden_objects::testing::account_id::{ +use miden_protocol::account::Account; +use miden_protocol::asset::{Asset, AssetVault, FungibleAsset}; +use miden_protocol::crypto::rand::RpoRandomCoin; +use miden_protocol::note::NoteType; +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, ACCOUNT_ID_SENDER, }; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, Word}; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, Word}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::errors::standards::ERR_P2ID_TARGET_ACCT_MISMATCH; +use miden_standards::note::create_p2id_note; use miden_testing::{Auth, MockChain, assert_transaction_executor_error}; use crate::prove_and_verify_transaction; @@ -222,7 +222,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { let tx_script_src = &format!( " - use.miden::output_note + use miden::protocol::output_note begin push.{recipient_1} push.{note_execution_hint_1} @@ -232,7 +232,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { call.output_note::create push.{asset_1} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw dropw dropw dropw push.{recipient_2} @@ -243,7 +243,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { call.output_note::create push.{asset_2} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw dropw dropw dropw end ", diff --git a/crates/miden-testing/tests/scripts/p2ide.rs b/crates/miden-testing/tests/scripts/p2ide.rs index d149ced65f..a7ac7aa0a1 100644 --- a/crates/miden-testing/tests/scripts/p2ide.rs +++ b/crates/miden-testing/tests/scripts/p2ide.rs @@ -1,17 +1,17 @@ use core::slice; use anyhow::Context; -use miden_lib::errors::note_script_errors::{ +use miden_protocol::Felt; +use miden_protocol::account::Account; +use miden_protocol::asset::{Asset, AssetVault, FungibleAsset}; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::{Note, NoteType}; +use miden_standards::errors::standards::{ ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER, ERR_P2IDE_RECLAIM_DISABLED, ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED, ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED, }; -use miden_objects::Felt; -use miden_objects::account::Account; -use miden_objects::asset::{Asset, AssetVault, FungibleAsset}; -use miden_objects::block::BlockNumber; -use miden_objects::note::{Note, NoteType}; use miden_testing::{Auth, MockChain, assert_transaction_executor_error}; /// Test that the P2IDE note works like a regular P2ID note diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index bc50fc1e93..083993039d 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -1,12 +1,10 @@ use core::slice; use std::collections::BTreeMap; -use miden_lib::account::interface::AccountInterface; -use miden_lib::utils::CodeBuilder; -use miden_objects::Word; -use miden_objects::asset::{Asset, FungibleAsset}; -use miden_objects::crypto::rand::{FeltRng, RpoRandomCoin}; -use miden_objects::note::{ +use miden_protocol::Word; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, @@ -17,13 +15,15 @@ use miden_objects::note::{ NoteType, PartialNote, }; -use miden_objects::transaction::OutputNote; +use miden_protocol::transaction::OutputNote; +use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::code_builder::CodeBuilder; use miden_testing::{Auth, MockChain}; /// Tests the execution of the generated send_note transaction script in case the sending account /// has the [`BasicWallet`][wallet] interface. /// -/// [wallet]: miden_lib::account::interface::AccountComponentInterface::BasicWallet +/// [wallet]: miden_standards::account::interface::AccountComponentInterface::BasicWallet #[tokio::test] async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let sent_asset = FungibleAsset::mock(10); @@ -33,7 +33,7 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { builder.add_existing_wallet_with_assets(Auth::BasicAuth, [FungibleAsset::mock(100)])?; let mock_chain = builder.build()?; - let sender_account_interface = AccountInterface::from(&sender_basic_wallet_account); + let sender_account_interface = AccountInterface::from_account(&sender_basic_wallet_account); let tag = NoteTag::from_account_id(sender_basic_wallet_account.id()); let metadata = NoteMetadata::new( @@ -52,11 +52,8 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let partial_note: PartialNote = note.clone().into(); let expiration_delta = 10u16; - let send_note_transaction_script = sender_account_interface.build_send_notes_script( - slice::from_ref(&partial_note), - Some(expiration_delta), - false, - )?; + let send_note_transaction_script = sender_account_interface + .build_send_notes_script(slice::from_ref(&partial_note), Some(expiration_delta))?; let executed_transaction = mock_chain .build_tx_context(sender_basic_wallet_account.id(), &[], &[]) @@ -87,7 +84,7 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { /// Tests the execution of the generated send_note transaction script in case the sending account /// has the [`BasicFungibleFaucet`][faucet] interface. /// -/// [faucet]: miden_lib::account::interface::AccountComponentInterface::BasicFungibleFaucet +/// [faucet]: miden_standards::account::interface::AccountComponentInterface::BasicFungibleFaucet #[tokio::test] async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { let mut builder = MockChain::builder(); @@ -95,7 +92,8 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { builder.add_existing_basic_faucet(Auth::BasicAuth, "POL", 200, None)?; let mock_chain = builder.build()?; - let sender_account_interface = AccountInterface::from(&sender_basic_fungible_faucet_account); + let sender_account_interface = + AccountInterface::from_account(&sender_basic_fungible_faucet_account); let tag = NoteTag::from_account_id(sender_basic_fungible_faucet_account.id()); let metadata = NoteMetadata::new( @@ -116,11 +114,8 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { let partial_note: PartialNote = note.clone().into(); let expiration_delta = 10u16; - let send_note_transaction_script = sender_account_interface.build_send_notes_script( - slice::from_ref(&partial_note), - Some(expiration_delta), - false, - )?; + let send_note_transaction_script = sender_account_interface + .build_send_notes_script(slice::from_ref(&partial_note), Some(expiration_delta))?; let _executed_transaction = mock_chain .build_tx_context(sender_basic_fungible_faucet_account.id(), &[], &[]) diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index 748b641421..bce9193a6c 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -1,9 +1,7 @@ use anyhow::Context; -use miden_lib::note::utils; -use miden_lib::utils::CodeBuilder; -use miden_objects::account::{Account, AccountId, AccountStorageMode, AccountType}; -use miden_objects::asset::{Asset, FungibleAsset, NonFungibleAsset}; -use miden_objects::note::{ +use miden_protocol::account::{Account, AccountId, AccountStorageMode, AccountType}; +use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset}; +use miden_protocol::note::{ Note, NoteAssets, NoteDetails, @@ -12,13 +10,15 @@ use miden_objects::note::{ NoteTag, NoteType, }; -use miden_objects::testing::account_id::{ +use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, AccountIdBuilder, }; -use miden_objects::transaction::OutputNote; -use miden_objects::{Felt, NoteError, Word}; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, NoteError, Word}; +use miden_standards::code_builder::CodeBuilder; +use miden_standards::note::utils; use miden_testing::{Auth, MockChain}; use crate::prove_and_verify_transaction; @@ -40,7 +40,7 @@ pub async fn prove_send_swap_note() -> anyhow::Result<()> { let tx_script_src = &format!( " - use.miden::output_note + use miden::protocol::output_note begin push.{recipient} push.{note_execution_hint} @@ -50,7 +50,7 @@ pub async fn prove_send_swap_note() -> anyhow::Result<()> { call.output_note::create push.{asset} - call.::miden::contracts::wallets::basic::move_asset_to_note + call.::miden::standards::wallets::basic::move_asset_to_note dropw dropw dropw dropw end ", diff --git a/crates/miden-testing/tests/wallet/mod.rs b/crates/miden-testing/tests/wallet/mod.rs index 75f6da77f8..28ba42f95c 100644 --- a/crates/miden-testing/tests/wallet/mod.rs +++ b/crates/miden-testing/tests/wallet/mod.rs @@ -1,22 +1,22 @@ -use miden_lib::AuthScheme; -use miden_lib::account::wallets::create_basic_wallet; -use miden_objects::Word; -use miden_objects::account::auth::AuthSecretKey; +use miden_protocol::Word; +use miden_protocol::account::auth::AuthSecretKey; +use miden_standards::AuthScheme; +use miden_standards::account::wallets::create_basic_wallet; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; #[cfg(not(target_arch = "wasm32"))] #[test] fn wallet_creation() { - use miden_lib::account::auth::AuthRpoFalcon512; - use miden_lib::account::wallets::BasicWallet; - use miden_objects::account::{AccountCode, AccountStorageMode, AccountType}; + use miden_protocol::account::{AccountCode, AccountStorageMode, AccountType}; + use miden_standards::account::auth::AuthRpoFalcon512; + use miden_standards::account::wallets::BasicWallet; // we need a Falcon Public Key to create the wallet account let seed = [0_u8; 32]; let mut rng = ChaCha20Rng::from_seed(seed); - let sec_key = AuthSecretKey::new_rpo_falcon512_with_rng(&mut rng); + let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); let auth_scheme: AuthScheme = AuthScheme::RpoFalcon512 { pub_key }; @@ -49,9 +49,9 @@ fn wallet_creation() { #[cfg(not(target_arch = "wasm32"))] #[test] fn wallet_creation_2() { - use miden_lib::account::auth::AuthEcdsaK256Keccak; - use miden_lib::account::wallets::BasicWallet; - use miden_objects::account::{AccountCode, AccountStorageMode, AccountType}; + use miden_protocol::account::{AccountCode, AccountStorageMode, AccountType}; + use miden_standards::account::auth::AuthEcdsaK256Keccak; + use miden_standards::account::wallets::BasicWallet; // we need a ECDSA Public Key to create the wallet account let seed = [0_u8; 32]; diff --git a/crates/miden-tx-batch-prover/Cargo.toml b/crates/miden-tx-batch-prover/Cargo.toml index 6b214a120b..d664da1d37 100644 --- a/crates/miden-tx-batch-prover/Cargo.toml +++ b/crates/miden-tx-batch-prover/Cargo.toml @@ -17,9 +17,9 @@ bench = false [features] default = ["std"] -std = ["miden-objects/std", "miden-tx/std"] +std = ["miden-protocol/std", "miden-tx/std"] testing = [] [dependencies] -miden-objects = { workspace = true } -miden-tx = { workspace = true } +miden-protocol = { workspace = true } +miden-tx = { workspace = true } diff --git a/crates/miden-tx-batch-prover/src/local_batch_prover.rs b/crates/miden-tx-batch-prover/src/local_batch_prover.rs index 3636f61ac5..1c790a92ee 100644 --- a/crates/miden-tx-batch-prover/src/local_batch_prover.rs +++ b/crates/miden-tx-batch-prover/src/local_batch_prover.rs @@ -1,7 +1,7 @@ use alloc::boxed::Box; -use miden_objects::ProvenBatchError; -use miden_objects::batch::{ProposedBatch, ProvenBatch}; +use miden_protocol::ProvenBatchError; +use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_tx::TransactionVerifier; // LOCAL BATCH PROVER diff --git a/crates/miden-tx/Cargo.toml b/crates/miden-tx/Cargo.toml index 56187d7570..e78138760f 100644 --- a/crates/miden-tx/Cargo.toml +++ b/crates/miden-tx/Cargo.toml @@ -15,13 +15,13 @@ version.workspace = true [features] concurrent = ["miden-prover/concurrent", "std"] default = ["std"] -std = ["miden-lib/std", "miden-objects/std", "miden-processor/std", "miden-prover/std", "miden-verifier/std"] -testing = ["miden-lib/testing", "miden-objects/testing", "miden-processor/testing"] +std = ["miden-processor/std", "miden-protocol/std", "miden-prover/std", "miden-standards/std", "miden-verifier/std"] +testing = ["miden-processor/testing", "miden-protocol/testing", "miden-standards/testing"] [dependencies] # Workspace dependencies -miden-lib = { workspace = true } -miden-objects = { workspace = true } +miden-protocol = { workspace = true } +miden-standards = { workspace = true } # Miden dependencies miden-processor = { workspace = true } diff --git a/crates/miden-tx/src/auth/tx_authenticator.rs b/crates/miden-tx/src/auth/tx_authenticator.rs index 86a9febb70..4845421445 100644 --- a/crates/miden-tx/src/auth/tx_authenticator.rs +++ b/crates/miden-tx/src/auth/tx_authenticator.rs @@ -3,11 +3,11 @@ use alloc::collections::BTreeMap; use alloc::string::ToString; use alloc::vec::Vec; -use miden_objects::account::auth::{AuthSecretKey, PublicKey, PublicKeyCommitment, Signature}; -use miden_objects::crypto::SequentialCommit; -use miden_objects::transaction::TransactionSummary; -use miden_objects::{Felt, Hasher, Word}; use miden_processor::FutureMaybeSend; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey, PublicKeyCommitment, Signature}; +use miden_protocol::crypto::SequentialCommit; +use miden_protocol::transaction::TransactionSummary; +use miden_protocol::{Felt, Hasher, Word}; use crate::errors::AuthenticationError; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -276,15 +276,15 @@ impl TransactionAuthenticator for () { #[cfg(test)] mod test { - use miden_lib::utils::{Deserializable, Serializable}; - use miden_objects::account::auth::AuthSecretKey; - use miden_objects::{Felt, Word}; + use miden_protocol::account::auth::AuthSecretKey; + use miden_protocol::utils::{Deserializable, Serializable}; + use miden_protocol::{Felt, Word}; use super::SigningInputs; #[test] fn serialize_auth_key() { - let auth_key = AuthSecretKey::new_rpo_falcon512(); + let auth_key = AuthSecretKey::new_falcon512_rpo(); let serialized = auth_key.to_bytes(); let deserialized = AuthSecretKey::read_from_bytes(&serialized).unwrap(); diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 3b51d5ee1d..5be724e9a2 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -3,15 +3,16 @@ use alloc::string::String; use alloc::vec::Vec; use core::error::Error; -use miden_objects::account::AccountId; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic; -use miden_objects::asset::AssetVaultKey; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::SmtProofError; -use miden_objects::note::{NoteId, NoteMetadata}; -use miden_objects::transaction::TransactionSummary; -use miden_objects::{ +use miden_processor::{DeserializationError, ExecutionError}; +use miden_protocol::account::AccountId; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::assembly::diagnostics::reporting::PrintDiagnostic; +use miden_protocol::asset::AssetVaultKey; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::smt::SmtProofError; +use miden_protocol::note::{NoteId, NoteMetadata}; +use miden_protocol::transaction::TransactionSummary; +use miden_protocol::{ AccountDeltaError, AccountError, AssetError, @@ -22,7 +23,6 @@ use miden_objects::{ TransactionOutputError, Word, }; -use miden_processor::{DeserializationError, ExecutionError}; use miden_verifier::VerificationError; use thiserror::Error; diff --git a/crates/miden-tx/src/executor/data_store.rs b/crates/miden-tx/src/executor/data_store.rs index 19fe34ba10..050aeb636d 100644 --- a/crates/miden-tx/src/executor/data_store.rs +++ b/crates/miden-tx/src/executor/data_store.rs @@ -1,12 +1,12 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; -use miden_objects::account::{AccountId, PartialAccount, StorageMapWitness}; -use miden_objects::asset::{AssetVaultKey, AssetWitness}; -use miden_objects::block::{BlockHeader, BlockNumber}; -use miden_objects::note::NoteScript; -use miden_objects::transaction::{AccountInputs, PartialBlockchain}; use miden_processor::{FutureMaybeSend, MastForestStore, Word}; +use miden_protocol::account::{AccountId, PartialAccount, StorageMapWitness}; +use miden_protocol::asset::{AssetVaultKey, AssetWitness}; +use miden_protocol::block::{BlockHeader, BlockNumber}; +use miden_protocol::note::NoteScript; +use miden_protocol::transaction::{AccountInputs, PartialBlockchain}; use crate::DataStoreError; diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 77e0e9507a..d2af2b6974 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -3,18 +3,6 @@ use alloc::collections::{BTreeMap, BTreeSet}; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::TransactionAdviceInputs; -use miden_objects::account::auth::PublicKeyCommitment; -use miden_objects::account::{AccountCode, AccountDelta, AccountId, PartialAccount}; -use miden_objects::assembly::debuginfo::Location; -use miden_objects::assembly::{SourceFile, SourceManagerSync, SourceSpan}; -use miden_objects::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; -use miden_objects::block::BlockNumber; -use miden_objects::crypto::merkle::SmtProof; -use miden_objects::note::{NoteInputs, NoteMetadata, NoteRecipient}; -use miden_objects::transaction::{InputNote, InputNotes, OutputNote, TransactionSummary}; -use miden_objects::vm::AdviceMap; -use miden_objects::{Felt, Hasher, Word}; use miden_processor::{ AdviceMutation, AsyncHost, @@ -24,6 +12,23 @@ use miden_processor::{ MastForest, ProcessState, }; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountCode, AccountDelta, AccountId, PartialAccount}; +use miden_protocol::assembly::debuginfo::Location; +use miden_protocol::assembly::{SourceFile, SourceManagerSync, SourceSpan}; +use miden_protocol::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::smt::SmtProof; +use miden_protocol::note::{NoteInputs, NoteMetadata, NoteRecipient}; +use miden_protocol::transaction::{ + InputNote, + InputNotes, + OutputNote, + TransactionAdviceInputs, + TransactionSummary, +}; +use miden_protocol::vm::AdviceMap; +use miden_protocol::{Felt, Hasher, Word}; use crate::auth::{SigningInputs, TransactionAuthenticator}; use crate::errors::TransactionKernelError; @@ -451,22 +456,22 @@ where &mut self, process: &ProcessState, ) -> impl FutureMaybeSend, EventError>> { - let stdlib_event_result = self.base_host.handle_stdlib_events(process); + let core_lib_event_result = self.base_host.handle_core_lib_events(process); - // If the event was handled by a stdlib handler (Ok(Some)), we will return the result from + // If the event was handled by a core lib handler (Ok(Some)), we will return the result from // within the async block below. So, we only need to extract th tx event if the event was // not yet handled (Ok(None)). - let tx_event_result = match stdlib_event_result { + let tx_event_result = match core_lib_event_result { Ok(None) => Some(TransactionEvent::extract(&self.base_host, process)), _ => None, }; async move { - if let Some(mutations) = stdlib_event_result? { + if let Some(mutations) = core_lib_event_result? { return Ok(mutations); } - // The outer None means the event was handled by stdlib handlers. + // The outer None means the event was handled by core lib handlers. let Some(tx_event_result) = tx_event_result else { return Ok(Vec::new()); }; diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 4cce13bc29..105dbe7ff2 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -1,25 +1,25 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; -use miden_lib::transaction::TransactionKernel; -use miden_objects::account::AccountId; -use miden_objects::assembly::DefaultSourceManager; -use miden_objects::assembly::debuginfo::SourceManagerSync; -use miden_objects::asset::{Asset, AssetVaultKey}; -use miden_objects::block::BlockNumber; -use miden_objects::transaction::{ +use miden_processor::fast::FastProcessor; +use miden_processor::{AdviceInputs, ExecutionError, StackInputs}; +pub use miden_processor::{ExecutionOptions, MastForestStore}; +use miden_protocol::account::AccountId; +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::debuginfo::SourceManagerSync; +use miden_protocol::asset::{Asset, AssetVaultKey}; +use miden_protocol::block::BlockNumber; +use miden_protocol::transaction::{ ExecutedTransaction, InputNote, InputNotes, TransactionArgs, TransactionInputs, + TransactionKernel, TransactionScript, }; -use miden_objects::vm::StackOutputs; -use miden_objects::{Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES}; -use miden_processor::fast::FastProcessor; -use miden_processor::{AdviceInputs, ExecutionError, StackInputs}; -pub use miden_processor::{ExecutionOptions, MastForestStore}; +use miden_protocol::vm::StackOutputs; +use miden_protocol::{Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES}; use super::TransactionExecutorError; use crate::auth::TransactionAuthenticator; @@ -101,7 +101,7 @@ where /// /// The `source_manager` is used to map potential errors back to their source code. To get the /// most value out of it, use the same source manager as was used with the - /// [`Assembler`](miden_objects::assembly::Assembler) that assembled the Miden Assembly code + /// [`Assembler`](miden_protocol::assembly::Assembler) that assembled the Miden Assembly code /// that should be debugged, e.g. account components, note scripts or transaction scripts. /// /// This will overwrite any previously set source manager. diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index e6a033cb60..9741e79824 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -1,14 +1,19 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; -use miden_lib::note::{NoteConsumptionStatus, WellKnownNote}; -use miden_lib::transaction::TransactionKernel; -use miden_objects::account::AccountId; -use miden_objects::block::BlockNumber; -use miden_objects::note::Note; -use miden_objects::transaction::{InputNote, InputNotes, TransactionArgs, TransactionInputs}; use miden_processor::fast::FastProcessor; +use miden_protocol::account::AccountId; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::Note; +use miden_protocol::transaction::{ + InputNote, + InputNotes, + TransactionArgs, + TransactionInputs, + TransactionKernel, +}; use miden_prover::AdviceInputs; +use miden_standards::note::{NoteConsumptionStatus, WellKnownNote}; use super::TransactionExecutor; use crate::auth::TransactionAuthenticator; diff --git a/crates/miden-tx/src/host/account_delta_tracker.rs b/crates/miden-tx/src/host/account_delta_tracker.rs index 279d806f05..f62e7996e8 100644 --- a/crates/miden-tx/src/host/account_delta_tracker.rs +++ b/crates/miden-tx/src/host/account_delta_tracker.rs @@ -1,11 +1,11 @@ -use miden_objects::account::{ +use miden_protocol::account::{ AccountCode, AccountDelta, AccountId, AccountVaultDelta, PartialAccount, }; -use miden_objects::{Felt, FieldElement, ZERO}; +use miden_protocol::{Felt, FieldElement, ZERO}; use crate::host::storage_delta_tracker::StorageDeltaTracker; diff --git a/crates/miden-tx/src/host/account_procedures.rs b/crates/miden-tx/src/host/account_procedures.rs index bbe747b481..0a74d232e6 100644 --- a/crates/miden-tx/src/host/account_procedures.rs +++ b/crates/miden-tx/src/host/account_procedures.rs @@ -1,4 +1,4 @@ -use miden_objects::account::AccountCode; +use miden_protocol::account::AccountCode; use super::{BTreeMap, Word}; use crate::errors::TransactionKernelError; diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 9e8f2b3c52..50dbb83840 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -1,4 +1,7 @@ -use miden_lib::transaction::memory::{ +use miden_processor::{ExecutionError, Felt, ProcessState}; +use miden_protocol::account::{AccountId, StorageSlotId, StorageSlotType}; +use miden_protocol::note::{NoteId, NoteInputs}; +use miden_protocol::transaction::memory::{ ACCOUNT_STACK_TOP_PTR, ACCT_CODE_COMMITMENT_OFFSET, ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET, @@ -8,10 +11,7 @@ use miden_lib::transaction::memory::{ ACTIVE_INPUT_NOTE_PTR, NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, }; -use miden_objects::account::{AccountId, StorageSlotId, StorageSlotType}; -use miden_objects::note::{NoteId, NoteInputs}; -use miden_objects::{Hasher, Word}; -use miden_processor::{ExecutionError, Felt, ProcessState}; +use miden_protocol::{Hasher, Word}; use crate::errors::TransactionKernelError; diff --git a/crates/miden-tx/src/host/link_map.rs b/crates/miden-tx/src/host/link_map.rs index 5a6f15638c..5024c86dfd 100644 --- a/crates/miden-tx/src/host/link_map.rs +++ b/crates/miden-tx/src/host/link_map.rs @@ -1,9 +1,9 @@ use alloc::vec::Vec; use core::cmp::Ordering; -use miden_objects::{Felt, LexicographicWord, Word, ZERO}; use miden_processor::fast::ExecutionOutput; use miden_processor::{AdviceMutation, ContextId, ProcessState}; +use miden_protocol::{Felt, LexicographicWord, Word, ZERO}; // LINK MAP // ================================================================================================ @@ -316,7 +316,7 @@ impl<'mem> MemoryViewer<'mem> { // https://github.com/0xMiden/miden-vm/issues/2237 // Copy of how Memory::read_element is implemented in Miden VM. - let idx = addr % miden_objects::WORD_SIZE as u32; + let idx = addr % miden_protocol::WORD_SIZE as u32; let word_addr = addr - idx; Some(self.get_kernel_mem_word(word_addr)?[idx as usize]) diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index fc39d3b517..56d3940848 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -10,8 +10,8 @@ mod account_procedures; pub use account_procedures::AccountProcedureIndexMap; pub(crate) mod note_builder; -use miden_lib::StdLibrary; -use miden_lib::transaction::EventId; +use miden_protocol::CoreLibrary; +use miden_protocol::vm::EventId; use note_builder::OutputNoteBuilder; mod kernel_process; @@ -28,8 +28,17 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{ +use miden_processor::{ + AdviceMutation, + EventError, + EventHandlerRegistry, + Felt, + MastForest, + MastForestStore, + ProcessState, +}; +use miden_protocol::Word; +use miden_protocol::account::{ AccountCode, AccountDelta, AccountHeader, @@ -40,9 +49,9 @@ use miden_objects::account::{ StorageSlotId, StorageSlotName, }; -use miden_objects::asset::Asset; -use miden_objects::note::{NoteId, NoteMetadata, NoteRecipient}; -use miden_objects::transaction::{ +use miden_protocol::asset::Asset; +use miden_protocol::note::{NoteId, NoteMetadata, NoteRecipient}; +use miden_protocol::transaction::{ InputNote, InputNotes, OutputNote, @@ -50,16 +59,7 @@ use miden_objects::transaction::{ TransactionMeasurements, TransactionSummary, }; -use miden_objects::vm::RowIndex; -use miden_processor::{ - AdviceMutation, - EventError, - EventHandlerRegistry, - Felt, - MastForest, - MastForestStore, - ProcessState, -}; +use miden_protocol::vm::RowIndex; pub(crate) use tx_event::{RecipientData, TransactionEvent, TransactionProgressEvent}; pub use tx_progress::TransactionProgress; @@ -101,7 +101,7 @@ pub struct TransactionBaseHost<'store, STORE> { output_notes: BTreeMap, /// Handle the VM default events _before_ passing it to user defined ones. - stdlib_handlers: EventHandlerRegistry, + core_lib_handlers: EventHandlerRegistry, } impl<'store, STORE> TransactionBaseHost<'store, STORE> { @@ -116,14 +116,14 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { scripts_mast_store: ScriptMastForestStore, acct_procedure_index_map: AccountProcedureIndexMap, ) -> Self { - let stdlib_handlers = { + let core_lib_handlers = { let mut registry = EventHandlerRegistry::new(); - let stdlib = StdLibrary::default(); - for (event_id, handler) in stdlib.handlers() { + let core_lib = CoreLibrary::default(); + for (event_id, handler) in core_lib.handlers() { registry .register(event_id, handler) - .expect("There are no duplicates in the stdlibrary handlers"); + .expect("There are no duplicates in the core library handlers"); } registry }; @@ -136,7 +136,7 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { acct_procedure_index_map, output_notes: BTreeMap::default(), input_notes, - stdlib_handlers, + core_lib_handlers, } } @@ -263,16 +263,16 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { // EVENT HANDLERS // -------------------------------------------------------------------------------------------- - /// Handles the event if the stdlib event handler registry contains a handler with the emitted + /// Handles the event if the core lib event handler registry contains a handler with the emitted /// event ID. /// /// Returns `Some` if the event was handled, `None` otherwise. - pub fn handle_stdlib_events( + pub fn handle_core_lib_events( &self, process: &ProcessState, ) -> Result>, EventError> { let event_id = EventId::from_felt(process.get_stack_item(0)); - if let Some(mutations) = self.stdlib_handlers.handle_event(event_id, process)? { + if let Some(mutations) = self.core_lib_handlers.handle_event(event_id, process)? { Ok(Some(mutations)) } else { Ok(None) diff --git a/crates/miden-tx/src/host/note_builder.rs b/crates/miden-tx/src/host/note_builder.rs index e3642fd217..9e392c54ef 100644 --- a/crates/miden-tx/src/host/note_builder.rs +++ b/crates/miden-tx/src/host/note_builder.rs @@ -1,5 +1,5 @@ -use miden_objects::asset::Asset; -use miden_objects::note::{Note, NoteAssets, NoteMetadata, NoteRecipient, PartialNote}; +use miden_protocol::asset::Asset; +use miden_protocol::note::{Note, NoteAssets, NoteMetadata, NoteRecipient, PartialNote}; use super::{OutputNote, Word}; use crate::errors::TransactionKernelError; diff --git a/crates/miden-tx/src/host/script_mast_forest_store.rs b/crates/miden-tx/src/host/script_mast_forest_store.rs index 071f84208b..6a8e82054d 100644 --- a/crates/miden-tx/src/host/script_mast_forest_store.rs +++ b/crates/miden-tx/src/host/script_mast_forest_store.rs @@ -1,12 +1,12 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; -use miden_objects::Word; -use miden_objects::assembly::mast::MastForest; -use miden_objects::note::NoteScript; -use miden_objects::transaction::TransactionScript; -use miden_objects::vm::AdviceMap; use miden_processor::MastForestStore; +use miden_protocol::Word; +use miden_protocol::assembly::mast::MastForest; +use miden_protocol::note::NoteScript; +use miden_protocol::transaction::TransactionScript; +use miden_protocol::vm::AdviceMap; /// Stores the MAST forests for a set of scripts (both note scripts and transaction scripts). /// diff --git a/crates/miden-tx/src/host/storage_delta_tracker.rs b/crates/miden-tx/src/host/storage_delta_tracker.rs index 0baef19a3b..6270612130 100644 --- a/crates/miden-tx/src/host/storage_delta_tracker.rs +++ b/crates/miden-tx/src/host/storage_delta_tracker.rs @@ -1,8 +1,8 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{ +use miden_protocol::Word; +use miden_protocol::account::{ AccountStorageDelta, AccountStorageHeader, PartialAccount, diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index e62441740e..ab16e8eff6 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -1,12 +1,12 @@ use alloc::vec::Vec; -use miden_lib::transaction::{EventId, TransactionEventId}; -use miden_objects::account::{AccountId, StorageMap, StorageSlotName, StorageSlotType}; -use miden_objects::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; -use miden_objects::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; -use miden_objects::transaction::TransactionSummary; -use miden_objects::{Felt, Hasher, Word}; use miden_processor::{AdviceMutation, ProcessState, RowIndex}; +use miden_protocol::account::{AccountId, StorageMap, StorageSlotName, StorageSlotType}; +use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; +use miden_protocol::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; +use miden_protocol::transaction::{TransactionEventId, TransactionSummary}; +use miden_protocol::vm::EventId; +use miden_protocol::{Felt, Hasher, Word}; use crate::host::{TransactionBaseHost, TransactionKernelProcess}; use crate::{LinkMap, TransactionKernelError}; diff --git a/crates/miden-tx/src/lib.rs b/crates/miden-tx/src/lib.rs index 8b3f225574..a756df72b7 100644 --- a/crates/miden-tx/src/lib.rs +++ b/crates/miden-tx/src/lib.rs @@ -48,4 +48,4 @@ pub mod auth; // RE-EXPORTS // ================================================================================================ -pub use miden_objects::utils; +pub use miden_protocol::utils; diff --git a/crates/miden-tx/src/prover/mast_store.rs b/crates/miden-tx/src/prover/mast_store.rs index 3ee9d7d5dc..e92984cc66 100644 --- a/crates/miden-tx/src/prover/mast_store.rs +++ b/crates/miden-tx/src/prover/mast_store.rs @@ -1,13 +1,13 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; -use miden_lib::transaction::TransactionKernel; -use miden_lib::{MidenLib, StdLibrary}; -use miden_objects::Word; -use miden_objects::account::AccountCode; -use miden_objects::assembly::mast::MastForest; -use miden_objects::utils::sync::RwLock; use miden_processor::MastForestStore; +use miden_protocol::account::AccountCode; +use miden_protocol::assembly::mast::MastForest; +use miden_protocol::transaction::TransactionKernel; +use miden_protocol::utils::sync::RwLock; +use miden_protocol::{CoreLibrary, ProtocolLib, Word}; +use miden_standards::StandardsLib; // TRANSACTION MAST STORE // ================================================================================================ @@ -28,9 +28,10 @@ impl TransactionMastStore { /// Returns a new [TransactionMastStore] instantiated with the default libraries. /// /// The default libraries include: - /// - Miden standard library (miden-stdlib). - /// - Miden protocol library (miden-lib). - /// - Transaction kernel. + /// - Miden core library [`CoreLibrary`]. + /// - Miden protocol library [`ProtocolLib`]. + /// - Miden standards library [`StandardsLib`]. + /// - Transaction kernel [`TransactionKernel::kernel`]. pub fn new() -> Self { let mast_forests = RwLock::new(BTreeMap::new()); let store = Self { mast_forests }; @@ -39,13 +40,17 @@ impl TransactionMastStore { let kernels_forest = TransactionKernel::kernel().mast_forest().clone(); store.insert(kernels_forest); - // load miden-stdlib MAST forest - let miden_stdlib_forest = StdLibrary::default().mast_forest().clone(); - store.insert(miden_stdlib_forest); + // load miden-core-lib MAST forest + let miden_core_lib_forest = CoreLibrary::default().mast_forest().clone(); + store.insert(miden_core_lib_forest); - // load miden lib MAST forest - let miden_lib_forest = MidenLib::default().mast_forest().clone(); - store.insert(miden_lib_forest); + // load protocol lib MAST forest + let protocol_lib_forest = ProtocolLib::default().mast_forest().clone(); + store.insert(protocol_lib_forest); + + // load standards lib MAST forest + let standards_lib_forest = StandardsLib::default().mast_forest().clone(); + store.insert(standards_lib_forest); store } diff --git a/crates/miden-tx/src/prover/mod.rs b/crates/miden-tx/src/prover/mod.rs index 332b3e84ab..60d13b4c52 100644 --- a/crates/miden-tx/src/prover/mod.rs +++ b/crates/miden-tx/src/prover/mod.rs @@ -1,18 +1,18 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use miden_lib::transaction::TransactionKernel; -use miden_objects::account::delta::AccountUpdateDetails; -use miden_objects::account::{AccountDelta, PartialAccount}; -use miden_objects::asset::Asset; -use miden_objects::block::BlockNumber; -use miden_objects::transaction::{ +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{AccountDelta, PartialAccount}; +use miden_protocol::asset::Asset; +use miden_protocol::block::BlockNumber; +use miden_protocol::transaction::{ InputNote, InputNotes, OutputNote, ProvenTransaction, ProvenTransactionBuilder, TransactionInputs, + TransactionKernel, TransactionOutputs, }; pub use miden_prover::ProvingOptions; @@ -168,7 +168,7 @@ impl Default for LocalTransactionProver { impl LocalTransactionProver { pub fn prove_dummy( &self, - executed_transaction: miden_objects::transaction::ExecutedTransaction, + executed_transaction: miden_protocol::transaction::ExecutedTransaction, ) -> Result { let (tx_inputs, tx_outputs, account_delta, _) = executed_transaction.into_parts(); diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index 3572a8eb4e..5cdc3a9edc 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -1,11 +1,6 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use miden_objects::Word; -use miden_objects::account::{AccountDelta, PartialAccount}; -use miden_objects::assembly::debuginfo::Location; -use miden_objects::assembly::{SourceFile, SourceSpan}; -use miden_objects::transaction::{InputNote, InputNotes, OutputNote}; use miden_processor::{ AdviceMutation, BaseHost, @@ -15,6 +10,11 @@ use miden_processor::{ ProcessState, SyncHost, }; +use miden_protocol::Word; +use miden_protocol::account::{AccountDelta, PartialAccount}; +use miden_protocol::assembly::debuginfo::Location; +use miden_protocol::assembly::{SourceFile, SourceSpan}; +use miden_protocol::transaction::{InputNote, InputNotes, OutputNote}; use crate::host::{RecipientData, ScriptMastForestStore, TransactionBaseHost, TransactionEvent}; use crate::{AccountProcedureIndexMap, TransactionKernelError}; @@ -91,7 +91,7 @@ where } fn on_event(&mut self, process: &ProcessState) -> Result, EventError> { - if let Some(advice_mutations) = self.base_host.handle_stdlib_events(process)? { + if let Some(advice_mutations) = self.base_host.handle_core_lib_events(process)? { return Ok(advice_mutations); } diff --git a/crates/miden-tx/src/verifier/mod.rs b/crates/miden-tx/src/verifier/mod.rs index 062b3f8ccc..9855151dec 100644 --- a/crates/miden-tx/src/verifier/mod.rs +++ b/crates/miden-tx/src/verifier/mod.rs @@ -1,7 +1,6 @@ -use miden_lib::StdLibrary; -use miden_lib::transaction::TransactionKernel; -use miden_objects::transaction::ProvenTransaction; -use miden_objects::vm::ProgramInfo; +use miden_protocol::CoreLibrary; +use miden_protocol::transaction::{ProvenTransaction, TransactionKernel}; +use miden_protocol::vm::ProgramInfo; use miden_verifier::verify_with_precompiles; use super::TransactionVerifierError; @@ -50,7 +49,7 @@ impl TransactionVerifier { ); // verify transaction proof - let precompile_verifiers = StdLibrary::default().verifier_registry(); + let precompile_verifiers = CoreLibrary::default().verifier_registry(); let proof_security_level = verify_with_precompiles( self.tx_program_info.clone(), stack_inputs, diff --git a/deny.toml b/deny.toml index 799128b16d..3679142018 100644 --- a/deny.toml +++ b/deny.toml @@ -59,6 +59,9 @@ skip-tree = [ # Allow windows-sys v0.48.x/v0.59.x - multiple Windows system libraries { name = "windows-sys", version = "=0.48.*" }, { name = "windows-sys", version = "=0.59.*" }, + # Allow syn v1.x and v2.x - our derive macros need v1.x while ecosystem uses v2.x + { name = "syn", version = "=1.0.109" }, + { name = "syn", version = "=2.0.111" }, ] wildcards = "allow" diff --git a/deny.toml~ b/deny.toml~ deleted file mode 100644 index 1d91acb183..0000000000 --- a/deny.toml~ +++ /dev/null @@ -1,69 +0,0 @@ -# cargo-deny configuration for DeepProve workspace - -# Graph configuration -[graph] -targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"] - -# Advisory database configuration -[advisories] -db-path = "~/.cargo/advisory-db" -db-urls = ["https://github.com/rustsec/advisory-db"] -ignore = [ - "RUSTSEC-2024-0436", # paste is unmaintained but no alternative available - "RUSTSEC-2025-0055", # tracing-subscriber vulnerability - will be fixed by upgrade - "RUSTSEC-2025-0056", # adler is unmaintained but used by miniz_oxide -] -yanked = "warn" - -# License configuration -[licenses] -allow = [ - "Apache-2.0 WITH LLVM-exception", - "Apache-2.0", - "BSD-2-Clause", - "BSD-3-Clause", - "ISC", - "MIT", - "Unicode-3.0", - "Zlib", -] -exceptions = [ - # Each entry is the crate and version constraint, and its specific allowed licenses - #{ allow = ["Zlib"], name = "adler32", version = "*" }, -] - -# Bans configuration -[bans] -allow = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -deny = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -highlight = "all" -multiple-versions = "deny" -skip = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -skip-tree = [ - # Allow getrandom v0.2.x - legacy version used by nanorand - { name = "getrandom", version = "=0.2.*" }, - # Allow rand_core v0.6.x - legacy version used by winterfell crates - { name = "rand_core", version = "=0.6.*" }, - # Allow rustc_version v0.2.x - build dependency version - { name = "rustc_version", version = "=0.2.*" }, - # Allow unicode-width v0.1.x - used by miden-formatting vs textwrap conflict - { name = "unicode-width", version = "=0.1.*" }, - # Allow windows-targets v0.48.x - older Windows target version - { name = "windows-targets", version = "=0.48.*" }, - # Allow windows-sys v0.48.x/v0.59.x - multiple Windows system libraries - { name = "windows-sys", version = "=0.48.*" }, - { name = "windows-sys", version = "=0.59.*" }, -] -wildcards = "allow" - -# Sources configuration -[sources] -allow-registry = ["https://github.com/rust-lang/crates.io-index"] -unknown-git = "deny" -unknown-registry = "deny" diff --git a/docs/src/account/components.md b/docs/src/account/components.md index e7963d5f91..aad4412298 100644 --- a/docs/src/account/components.md +++ b/docs/src/account/components.md @@ -187,8 +187,7 @@ Multi-slot values are currently unsupported by component schemas. #### Providing init values -When a storage entry requires init-supplied values, an implementation must provide their concrete values -at instantiation time. This is done through `InitStorageData` (available as `miden_objects::account::component::InitStorageData`), which can be created programmatically or loaded from TOML using `InitStorageData::from_toml()`. +When a storage entry requires init-supplied values, an implementation must provide their concrete values at instantiation time. This is done through `InitStorageData` (available as `miden_protocol::account::component::InitStorageData`), which can be created programmatically or loaded from TOML using `InitStorageData::from_toml()`. For example, the init-populated map entry above can be populated from TOML as follows: diff --git a/docs/src/account/storage.md b/docs/src/account/storage.md index f3e9ad45d0..622a8f54bd 100644 --- a/docs/src/account/storage.md +++ b/docs/src/account/storage.md @@ -9,7 +9,7 @@ title: "Storage" A flexible, arbitrary data store within the `Account`. ::: -The [storage](https://docs.rs/miden-objects/latest/miden_objects/account/struct.AccountStorage.html) consists of up to 256 individual [storage slots](https://docs.rs/miden-objects/latest/miden_objects/account/enum.StorageSlot.html), where each slot consists of: +The [storage](https://docs.rs/miden-protocol/latest/miden_protocol/account/struct.AccountStorage.html) consists of up to 256 individual [storage slots](https://docs.rs/miden-protocol/latest/miden_protocol/account/enum.StorageSlot.html), where each slot consists of: - Key: Slot name. - Value: either a single [`Word`](#storage-units) or a key-value map of [`Word`](#storage-units)s. diff --git a/docs/src/note.md b/docs/src/note.md index 785133e78e..2137a7db1b 100644 --- a/docs/src/note.md +++ b/docs/src/note.md @@ -121,7 +121,7 @@ Only those who know the RECIPIENT’s pre-image can consume the `Note`. For priv The [transaction prologue](transaction) requires all necessary data to compute the `Note` hash. This setup allows scenario-specific restrictions on who may consume a `Note`. -For a practical example, refer to the [SWAP note script](https://github.com/0xMiden/miden-base/blob/next/crates/miden-lib/asm/note_scripts/SWAP.masm), where the RECIPIENT ensures that only a defined target can consume the swapped asset. +For a practical example, refer to the [SWAP note script](https://github.com/0xMiden/miden-base/blob/next/crates/miden-standards/asm/note_scripts/SWAP.masm), where the RECIPIENT ensures that only a defined target can consume the swapped asset. #### Note nullifier ensuring private consumption diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index d17f09ed68..6f2f9ef0cf 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -27,7 +27,7 @@ Most procedures in the Miden protocol library are implemented as wrappers around The procedures maintain the same security and context restrictions as the underlying kernel procedures. When invoking these procedures, ensure that the calling context matches the requirements. -## Active account Procedures (`miden::active_account`) +## Active account Procedures (`miden::protocol::active_account`) Active account procedures can be used to read from storage, fetch or compute commitments or obtain other internal data of the active account. @@ -53,7 +53,7 @@ Active account procedures can be used to read from storage, fetch or compute com | `get_procedure_root` | Returns the procedure root for the procedure at the specified index.

**Inputs:** `[index]`
**Outputs:** `[PROC_ROOT]` | Any | | `has_procedure` | Returns the binary flag indicating whether the procedure with the provided root is available on the active account.

**Inputs:** `[PROC_ROOT]`
**Outputs:** `[is_procedure_available]` | Any | -## Native account Procedures (`miden::native_account`) +## Native account Procedures (`miden::protocol::native_account`) Native account procedures can be used to write to storage, add or remove assets from the vault and compute delta commitment of the native account. @@ -68,7 +68,7 @@ Native account procedures can be used to write to storage, add or remove assets | `remove_asset` | Removes the specified asset from the vault.

**Inputs:** `[ASSET]`
**Outputs:** `[ASSET]` | Native & Account | | `was_procedure_called` | Returns 1 if a native account procedure was called during transaction execution, and 0 otherwise.

**Inputs:** `[PROC_ROOT]`
**Outputs:** `[was_called]` | Any | -## Active Note Procedures (`miden::active_note`) +## Active Note Procedures (`miden::protocol::active_note`) Active note procedures can be used to fetch data from the note that is currently being processed by the transaction kernel. @@ -81,9 +81,8 @@ Active note procedures can be used to fetch data from the note that is currently | `get_sender` | Returns the sender of the active note.

**Inputs:** `[]`
**Outputs:** `[sender_id_prefix, sender_id_suffix]` | Note | | `get_serial_number` | Returns the [serial number](note.md#serial-number) of the active note.

**Inputs:** `[]`
**Outputs:** `[SERIAL_NUMBER]` | Note | | `get_script_root` | Returns the [script root](note.md#script) of the active note.

**Inputs:** `[]`
**Outputs:** `[SCRIPT_ROOT]` | Note | -| `add_assets_to_account` | Adds all assets from the active note to the account vault.

**Inputs:** `[]`
**Outputs:** `[]` | Note | -## Input Note Procedures (`miden::input_note`) +## Input Note Procedures (`miden::protocol::input_note`) Input note procedures can be used to fetch data on input notes consumed by the transaction. @@ -98,7 +97,7 @@ Input note procedures can be used to fetch data on input notes consumed by the t | `get_script_root` | Returns the [script root](note.md#script) of the input note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[SCRIPT_ROOT]` | Any | | `get_serial_number` | Returns the [serial number](note.md#serial-number) of the input note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[SERIAL_NUMBER]` | Any | -## Output Note Procedures (`miden::output_note`) +## Output Note Procedures (`miden::protocol::output_note`) Output note procedures can be used to fetch data on output notes created by the transaction. @@ -111,7 +110,7 @@ Output note procedures can be used to fetch data on output notes created by the | `get_recipient` | Returns the [recipient](note#note-recipient-restricting-consumption) of the output note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[RECIPIENT]` | Any | | `get_metadata` | Returns the [metadata](note#metadata) of the output note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[METADATA]` | Any | -## Note Utility Procedures (`miden::note`) +## Note Utility Procedures (`miden::protocol::note`) Note utility procedures can be used to compute the required utility data or write note data to memory. @@ -124,7 +123,7 @@ Note utility procedures can be used to compute the required utility data or writ | `build_recipient` | Builds the recipient hash from note inputs, script root, and serial number.

**Inputs:** `[inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT]`
**Outputs:** `[RECIPIENT]` | Any | | `extract_sender_from_metadata` | Extracts the sender ID from the provided metadata word.

**Inputs:** `[METADATA]`
**Outputs:** `[sender_id_prefix, sender_id_suffix]` | Any | -## Transaction Procedures (`miden::tx`) +## Transaction Procedures (`miden::protocol::tx`) Transaction procedures manage transaction-level operations including note creation, context switching, and reading transaction metadata. @@ -141,7 +140,7 @@ Transaction procedures manage transaction-level operations including note creati | `get_expiration_block_delta` | Returns the transaction expiration delta, or 0 if not set.

**Inputs:** `[]`
**Outputs:** `[block_height_delta]` | Any | | `update_expiration_block_delta` | Updates the transaction expiration delta.

**Inputs:** `[block_height_delta]`
**Outputs:** `[]` | Any | -## Faucet Procedures (`miden::faucet`) +## Faucet Procedures (`miden::protocol::faucet`) Faucet procedures allow reading and writing to faucet accounts to mint and burn assets. @@ -154,7 +153,7 @@ Faucet procedures allow reading and writing to faucet accounts to mint and burn | `get_total_issuance` | Returns the total issuance of the fungible faucet the transaction is being executed against.

**Inputs:** `[]`
**Outputs:** `[total_issuance]` | Faucet | | `is_non_fungible_asset_issued` | Returns a boolean indicating whether the provided non-fungible asset has been already issued by this faucet.

**Inputs:** `[ASSET]`
**Outputs:** `[is_issued]` | Faucet | -## Asset Procedures (`miden::asset`) +## Asset Procedures (`miden::protocol::asset`) Asset procedures provide utilities for creating fungible and non-fungible assets. diff --git a/docs/src/transaction.md b/docs/src/transaction.md index 6ac6aba0bc..feffa9b98c 100644 --- a/docs/src/transaction.md +++ b/docs/src/transaction.md @@ -66,9 +66,9 @@ To illustrate the `Transaction` protocol, we provide two examples for a basic `T Let's assume account A wants to create a P2ID note. P2ID notes are pay-to-ID notes that can only be consumed by a specified target account ID. Note creators can provide the target account ID using the [note inputs](note#inputs). -In this example, account A uses the basic wallet and the authentication component provided by `miden-lib`. The basic wallet component defines the methods `wallets::basic::create_note` and `wallets::basic::move_asset_to_note` to create notes with assets, and `wallets::basic::receive_asset` to receive assets. The authentication component exposes `auth::basic::auth_tx_rpo_falcon512` which allows for signing a transaction. Some account methods like `active_account::get_id` are always exposed. +In this example, account A uses the basic wallet and the authentication component provided by `miden-standards`. The basic wallet component defines the methods `wallets::basic::create_note` and `wallets::basic::move_asset_to_note` to create notes with assets, and `wallets::basic::receive_asset` to receive assets. The authentication component exposes `auth::basic::auth_tx_rpo_falcon512` which allows for signing a transaction. Some account methods like `active_account::get_id` are always exposed. -The executor inputs to the Miden VM a `Transaction` script in which it places on the stack the data (tag, aux, note_type, execution_hint, RECIPIENT) of the note(s) that it wants to create using `wallets::basic::create_note` during the said `Transaction`. The [`NoteRecipient`](https://github.com/0xMiden/miden-base/blob/main/crates/miden-objects/src/note/recipient.rs) is a value that describes under which condition a note can be consumed and is built using a `serial_number`, the `note_script` (in this case P2ID script) and the `note_inputs`. The Miden VM will execute the `Transaction` script and create the note(s). After having been created, the executor can use `wallets::basic::move_asset_to_note` to move assets from the account's vault to the notes vault. +The executor inputs to the Miden VM a `Transaction` script in which it places on the stack the data (tag, aux, note_type, execution_hint, RECIPIENT) of the note(s) that it wants to create using `wallets::basic::create_note` during the said `Transaction`. The [`NoteRecipient`](https://github.com/0xMiden/miden-base/blob/main/crates/miden-protocol/src/note/recipient.rs) is a value that describes under which condition a note can be consumed and is built using a `serial_number`, the `note_script` (in this case P2ID script) and the `note_inputs`. The Miden VM will execute the `Transaction` script and create the note(s). After having been created, the executor can use `wallets::basic::move_asset_to_note` to move assets from the account's vault to the notes vault. After finalizing the `Transaction` the updated state and created note(s) can now be submitted to the Miden operator to be recorded on-chain. diff --git a/scripts/check-features.sh b/scripts/check-features.sh index 1aa931c832..7eae778e4b 100755 --- a/scripts/check-features.sh +++ b/scripts/check-features.sh @@ -10,12 +10,12 @@ echo "Checking all feature combinations with cargo-hack..." # Set environment variables to treat warnings as errors export RUSTFLAGS="-D warnings" -# Enable file generation in the `src` directory for miden-lib build scripts +# Enable file generation in the `src` directory for miden-protocol and miden-standards build scripts export BUILD_GENERATED_FILES_IN_SRC=1 # Run cargo-hack with comprehensive feature checking # Focus on library packages that have significant feature matrices -for package in miden-objects miden-lib miden-tx miden-testing miden-block-prover miden-tx-batch-prover; do +for package in miden-protocol miden-standards miden-agglayer miden-tx miden-testing miden-block-prover miden-tx-batch-prover; do echo "Checking package: $package" cargo hack check -p "$package" --each-feature --all-targets done From cdfb4c48557c083a2ba410e7ec863a275a062984 Mon Sep 17 00:00:00 2001 From: riemann Date: Tue, 23 Dec 2025 11:21:11 -0500 Subject: [PATCH 076/114] fix: lint check --- crates/miden-agglayer/Cargo.toml | 2 +- crates/miden-agglayer/src/errors/mod.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/miden-agglayer/Cargo.toml b/crates/miden-agglayer/Cargo.toml index ee09c67aa5..e453e032a9 100644 --- a/crates/miden-agglayer/Cargo.toml +++ b/crates/miden-agglayer/Cargo.toml @@ -23,7 +23,7 @@ testing = ["miden-protocol/testing"] # Miden dependencies miden-assembly = { workspace = true } miden-core = { workspace = true } -miden-protocol = { features = ["testing"], workspace = true } +miden-protocol = { workspace = true } miden-utils-sync = { workspace = true } [dev-dependencies] diff --git a/crates/miden-agglayer/src/errors/mod.rs b/crates/miden-agglayer/src/errors/mod.rs index 18bd2e1e39..2a86602e6f 100644 --- a/crates/miden-agglayer/src/errors/mod.rs +++ b/crates/miden-agglayer/src/errors/mod.rs @@ -1,2 +1,3 @@ // Include generated error constants +#[cfg(any(feature = "testing", test))] include!("agglayer.rs"); From 1598a9473972d23c66dc542a5ee52b50c407bb9c Mon Sep 17 00:00:00 2001 From: riemann Date: Wed, 24 Dec 2025 11:45:47 -0500 Subject: [PATCH 077/114] fix: rm miden-lib crate --- .../account_components/asset_conversion.masm | 106 ----------- .../account_components/bridge_out.masm | 162 ---------------- .../account_components/local_exit_tree.masm | 120 ------------ .../asm/agglayer/note_scripts/B2AGG.masm | 87 --------- crates/miden-lib/src/agglayer/mod.rs | 121 ------------ crates/miden-lib/src/agglayer/utils.rs | 173 ------------------ 6 files changed, 769 deletions(-) delete mode 100644 crates/miden-lib/asm/agglayer/account_components/asset_conversion.masm delete mode 100644 crates/miden-lib/asm/agglayer/account_components/bridge_out.masm delete mode 100644 crates/miden-lib/asm/agglayer/account_components/local_exit_tree.masm delete mode 100644 crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm delete mode 100644 crates/miden-lib/src/agglayer/mod.rs delete mode 100644 crates/miden-lib/src/agglayer/utils.rs diff --git a/crates/miden-lib/asm/agglayer/account_components/asset_conversion.masm b/crates/miden-lib/asm/agglayer/account_components/asset_conversion.masm deleted file mode 100644 index 58f3462eab..0000000000 --- a/crates/miden-lib/asm/agglayer/account_components/asset_conversion.masm +++ /dev/null @@ -1,106 +0,0 @@ -use.std::math::u64 -use.std::word - -# CONSTANTS -# ================================================================================================= - -const.MAX_SCALING_FACTOR=18 - -# ERRORS -# ================================================================================================= -const.ERR_SCALE_AMOUNT_EXCEEDED_LIMIT="maximum scaling factor is 18" - -#! Calculate 10^scale where scale is a u8 exponent. -#! -#! Inputs: [scale] -#! Outputs: [10^scale] -#! -#! Where: -#! - scale is expected to be a small integer (0-18 typical for crypto decimals) -#! -#! Panics if: -#! - scale > 18 (overflow protection) -proc pow10 - u32assert.err=ERR_SCALE_AMOUNT_EXCEEDED_LIMIT - # => [scale] - - dup u32lte.MAX_SCALING_FACTOR assert.err=ERR_SCALE_AMOUNT_EXCEEDED_LIMIT - # => [scale] - - push.1 swap - # => [scale, result] - - dup neq.0 - # => [is_not_zero, scale, result] - - # Loop to calculate 10^scale - while.true - # => [scale, result] - - # result *= 10 - swap mul.10 swap - # => [scale, result*10] - - # scale -= 1 - sub.1 - # => [scale-1, result*10] - - dup neq.0 - # => [is_not_zero, scale-1, result*10] - end - # => [0, result] - - drop - # => [result] -end - -#! Convert an asset amount to a scaled U256 representation for bridging to Agglayer. -#! -#! This procedure is used to convert Miden asset amounts to EVM asset amounts. -#! It multiplies the input amount by 10^target_scale to adjust for decimal differences -#! between the current representation and the target chain's native decimals. -#! -#! The procedure first calculates 10^target_scale using the pow10 helper, then converts -#! both the amount and scale factor to U64 format, performs U64 multiplication, and -#! returns the result as 8 u32 limbs in little-endian order (U256 format). -#! -#! Inputs: [amount, target_scale] -#! Outputs: [[RESULT_U256[0], RESULT_U256[1]]] -#! -#! Where: -#! - amount: The asset amount to be converted (range: 0 to 2^63 - 2^31) -#! - target_scale: Exponent for scaling factor (10^target_scale) -#! - [RESULT_U256[0], RESULT_U256[1]]: U256 value as 8 u32 limbs in little-endian order -#! (least significant limb at the top of the stack, each limb stored in little-endian format) -#! -#! Examples: -#! - USDC: amount=1000000000, target_scale=0 → 1000000000 (no scaling) -#! - ETH: amount=1e10, target_scale=8 → 1e18 -#! -#! Invocation: exec -pub proc scale_native_amount_to_u256 - swap - # => [target_scale, amount] - - exec.pow10 - # => [scale, amount] - - u32split - # => [scale_hi, scale_lo, amount] - - movup.2 u32split - # => [amount_hi, amount_lo, scale_hi, scale_lo] - - # Perform U64 multiplication: amount * scale - # This is safe because both the scaling factor and amount are guaranteed to be smaller - # than 2^64, so we will never overflow a 256-bit value. - exec.u64::overflowing_mul - # => [res_hi, res_mid_hi, res_mid_lo, res_lo] - - exec.word::reverse - # => [res_lo, res_mid_lo, res_mid_hi, res_hi] - - # convert to U256 & little endian - padw swapw - # => [RESULT_U256[0], RESULT_U256[1]] -end diff --git a/crates/miden-lib/asm/agglayer/account_components/bridge_out.masm b/crates/miden-lib/asm/agglayer/account_components/bridge_out.masm deleted file mode 100644 index c0e5cffbcd..0000000000 --- a/crates/miden-lib/asm/agglayer/account_components/bridge_out.masm +++ /dev/null @@ -1,162 +0,0 @@ -use.miden::active_note -use.miden::native_account -use.miden::note -use.miden::output_note -use std::crypto::hashes::keccak256 -use std::word -use.agglayer::local_exit_tree - -# CONSTANTS -# ================================================================================================= -const.MMR_PTR=42 -const.LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") - -const.BURN_NOTE_ROOT=[6407337173854817345,5626358912819151014,703918618794810515,17401169215223723177] -const.EXECUTION_HINT_ALWAYS=1 -const.PUBLIC_NOTE=1 -const.AUX=0 -const.NUM_BURN_NOTE_INPUTS=0 -const.BURN_ASSET_MEM_PTR=24 - -#! Computes the SERIAL_NUM of the outputted BURN note. -#! -#! The serial number is computed as hash(B2AGG_SERIAL_NUM, ASSET). -#! -#! Inputs: [ASSET] -#! Outputs: [SERIAL_NUM] -#! -#! Where: -#! - ASSET is the asset for which to compute the burn note serial number. -#! - SERIAL_NUM is the computed serial number for the BURN note. -#! -#! Invocation: exec -proc compute_burn_note_serial_num - exec.active_note::get_serial_number - # => [B2AGG_SERIAL_NUM, ASSET] - - hmerge - # => [SERIAL_NUM] -end - -#! Creates a BURN note for the specified asset. -#! -#! This procedure creates an output note that represents a burn operation for the given asset. -#! The note is configured with the appropriate recipient, tag, and execution hint. -#! -#! Inputs: [ASSET] -#! Outputs: [] -#! -#! Where: -#! - ASSET is the asset to be burned. -#! -#! Invocation: exec -proc create_burn_note.8 - loc_storew_be.0 dupw - # => [ASSET, ASSET] - - movup.2 drop movup.2 drop - # => [faucet_id_prefix, faucet_id_suffix, ASSET] - - exec.note::build_note_tag_for_network_account - # => [network_faucet_tag, ASSET] - - loc_store.5 - # => [ASSET] - - exec.compute_burn_note_serial_num - # => [SERIAL_NUM] - - push.BURN_NOTE_ROOT swapw - # => [SERIAL_NUM, SCRIPT_ROOT] - - push.NUM_BURN_NOTE_INPUTS push.0 - # => [inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT] - - exec.note::build_recipient - # => [RECIPIENT] - - push.EXECUTION_HINT_ALWAYS - push.PUBLIC_NOTE - push.AUX - loc_load.5 - # => [tag, aux, note_type, execution_hint, RECIPIENT] - - call.output_note::create - # => [note_idx] - - movdn.4 loc_loadw_be.0 - # => [ASSET, note_idx] - - exec.output_note::add_asset - # => [] -end - -#! Bridges an asset out via the AggLayer -#! -#! This procedure handles the complete bridge-out operation, including: -#! - Converting asset data to u32 format -#! - Computing Keccak hash of the data -#! - Adding the hash to the MMR frontier -#! - Storing the updated MMR root in account storage -#! - Creating a BURN note with the bridged out asset -#! -#! Inputs: [ASSET, dest_network, dest_address(5)] -#! Outputs: [] -#! -#! Where: -#! - ASSET is the asset to be bridged out. -#! - dest_network is the u32 destination network/chain ID. -#! - dest_address(5) are 5 u32 values representing a 20-byte Ethereum address. -#! -#! Invocation: call -pub proc bridge_out - mem_storew_be.BURN_ASSET_MEM_PTR - # => [ASSET, dest_network, dest_address(5)] - - # @dev TODO: Look up asset faucet id in asset registry - # -> return scaling factor - - # @dev TODO: Convert ASSET amount to EVM amount using scaling factor - # -> return amount from here: https://github.com/0xMiden/miden-base/pull/2141 - - # Converting SCALED_ASSET, dest_network, dest_address(5) to u32 representation - # in preparation for keccak256 hashing - - # keccak256 inputs: - # => [ASSET, dest_network, dest_address(5)] - # TODO we should convert Miden->Ethereum asset values, incl. amount conversion etc. - - # TODO: make building bridge message a separate procedure - # TODO: match Agglayer addLeafBridge logic - # TODO: convert Miden asset amount to Ethereum amount - # Store ASSET as u32 limbs in memory starting at address 0 - push.0 movdn.4 exec.word::store_word_u32s_le - # => [dest_network, dest_address(5)] - - # Store [dest_network, dest_address[0..3]] as u32 limbs in memory starting at address 8 - push.8 movdn.4 exec.word::store_word_u32s_le - # => [dest_address(2), 0, 0] - - # Store [dest_address[3..5], 0, 0] as u32 limbs in memory starting at address 16 - push.16 movdn.4 exec.word::store_word_u32s_le - # => [] - - # 1 u32 = 4 bytes - # 10 u32 values = 40 bytes - push.40 push.0 - # => [ptr, len_bytes] - - exec.keccak256::hash_memory - # => [DIGEST_U32[8]] - - # adding DIGEST_U32 double word leaf to mmr frontier - exec.local_exit_tree::add_asset_message - # => [] - - # creating BURN output note for ASSET - mem_loadw_be.BURN_ASSET_MEM_PTR - # => [ASSET] - - exec.create_burn_note - # => [] -end diff --git a/crates/miden-lib/asm/agglayer/account_components/local_exit_tree.masm b/crates/miden-lib/asm/agglayer/account_components/local_exit_tree.masm deleted file mode 100644 index 407dfd4a24..0000000000 --- a/crates/miden-lib/asm/agglayer/account_components/local_exit_tree.masm +++ /dev/null @@ -1,120 +0,0 @@ -use.miden::native_account -use.miden::active_account - - -# CONSTANTS -# ================================================================================================= - -const.MMR_PTR=42 -const.LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") - -#! Adds a leaf to the MMR frontier using Keccak hashing (stubbed implementation). -#! -#! This is a stubbed implementation that currently drops all inputs without performing -#! the actual MMR frontier addition operation. -#! -#! Inputs: [LEAF[1], LEAF[0], mmr_ptr] -#! Outputs: [] -#! -#! Where: -#! - LEAF[1], LEAF[0] are the leaf data to add to the MMR frontier. -#! - mmr_ptr is the pointer to the MMR frontier data structure. -#! -#! Invocation: exec -proc mmr_frontier_keccak_add - dropw dropw drop - # => [] -end - -#! Gets the root of the MMR frontier using Keccak hashing (stubbed implementation). -#! -#! This is a stubbed implementation that returns placeholder values instead of -#! computing the actual MMR frontier root. -#! -#! Inputs: [mmr_ptr] -#! Outputs: [ROOT[1], ROOT[0]] -#! -#! Where: -#! - ROOT[1], ROOT[0] are the root hash components of the MMR frontier whose memory location starts at mmr_ptr -#! -#! Invocation: exec -pub proc mmr_frontier_keccak_get_root - # stubbed out for now - drop - # => [] - - push.0.0.0.1 push.LOCAL_EXIT_TREE_SLOT[0..2] - # => [slot_id_prefix, slot_id_suffix, KEY] - - exec.active_account::get_map_item - # => [ROOT[0]] - - push.0.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] - # => [slot_id_prefix, slot_id_suffix, KEY, ROOT[0]] - - exec.active_account::get_map_item - # => [ROOT[1], ROOT[0]] -end - -#! Writes the MMR frontier root to account storage. -#! -#! This procedure retrieves the current MMR frontier root and stores it as a double word -#! in the account's storage map. The root is split across two storage keys: -#! - Key [0,0,0,0] stores ROOT[1] (high part) -#! - Key [0,0,0,1] stores ROOT[0] (low part) -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Invocation: exec -proc write_mmr_frontier_root - push.MMR_PTR - # => [MMR_PTR] - - # getting mmr frontier root - exec.mmr_frontier_keccak_get_root - # => [ROOT[1], ROOT[0]] - - # writing double word root to map keys [0,0,0,0] & [0,0,0,1] - push.0.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] - # => [index, KEY, ROOT[1], ROOT[0]] - - exec.native_account::set_map_item - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, ROOT[0]] - - dropw dropw - # => [ROOT[0]] - - push.1.0.0.0 push.LOCAL_EXIT_TREE_SLOT[0..2] - # => [index, KEY, ROOT[0]] - - exec.native_account::set_map_item - # => [OLD_MAP_ROOT, OLD_MAP_VALUE] - - dropw dropw - # => [] -end - -#! Adds an asset message to the MMR frontier and updates the stored root. -#! -#! This procedure takes a Keccak digest (represented as 8 u32 values) and adds it -#! as a leaf to the MMR frontier. After adding the leaf, it updates the MMR root -#! in the account's storage to reflect the new state. -#! -#! Inputs: [DIGEST_U32[8]] -#! Outputs: [] -#! -#! Where: -#! - DIGEST_U32[8] is a Keccak256 hash represented as 8 u32 values (256 bits total). -#! -#! Invocation: exec -pub proc add_asset_message - push.MMR_PTR movdn.8 - # => [LEAF[1], LEAF[0], mmr_ptr] - - exec.mmr_frontier_keccak_add - # => [] - - exec.write_mmr_frontier_root - # => [] -end \ No newline at end of file diff --git a/crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm b/crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm deleted file mode 100644 index 73ea4a1124..0000000000 --- a/crates/miden-lib/asm/agglayer/note_scripts/B2AGG.masm +++ /dev/null @@ -1,87 +0,0 @@ -use.agglayer::bridge_out -> agglayer -use.miden::account_id -use.miden::active_account -use.miden::active_note - -# CONSTANTS -# ================================================================================================= - -const.B2AGG_NOTE_INPUTS_COUNT=6 - -# ERRORS -# ================================================================================================= -const.ERR_B2AGG_WRONG_NUMBER_OF_ASSETS="B2AGG script requires exactly 1 note asset" - -const.ERR_B2AGG_WRONG_NUMBER_OF_INPUTS="B2AGG script expects exactly 6 note inputs" - -#! Bridge-to-AggLayer (B2AGG) note script: bridges assets from Miden to an AggLayer-connected chain. -#! -#! This note can be consumed in two ways: -#! - If the consuming account is the sender (reclaim): the note's assets are added back to the consuming account. -#! - If the consuming account is the Agglayer Bridge: the note's assets are moved to a BURN note, -#! and the note details are hashed into a leaf and appended to the Local Exit Tree. -#! global exit root (GER) merkle tree structure. -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Note inputs are assumed to be as follows: -#! - destination_network: u32 value representing the target chain ID -#! - destination_address: split into 5 u32 values representing a 20-byte Ethereum address: -#! - destination_address_0: bytes 0-3 -#! - destination_address_1: bytes 4-7 -#! - destination_address_2: bytes 8-11 -#! - destination_address_3: bytes 12-15 -#! - destination_address_4: bytes 16-19 -#! -#! Panics if: -#! - The note does not contain exactly 6 inputs. -#! - The note does not contain exactly 1 asset. -#! -begin - dropw - # => [pad(16)] - - # Check if reclaim - exec.active_account::get_id - # => [account_id_prefix, account_id_suffix, pad(16)] - - exec.active_note::get_sender - # => [sender_id_prefix, sender_id_suffix, account_id_prefix, account_id_suffix, pad(16)] - - exec.account_id::is_equal - # => [reclaim, pad(16)] - - # B2AGG note is being reclaimed; adding note assets to account - if.true - exec.active_note::add_assets_to_account - # => [pad(16)] - else - # Store note inputs -> mem[8..14] - push.8 exec.active_note::get_inputs - # => [num_inputs, dest_ptr, pad(16)] - - push.B2AGG_NOTE_INPUTS_COUNT assert_eq.err=ERR_B2AGG_WRONG_NUMBER_OF_INPUTS drop - # => [pad(16)] - - # Store note assets -> mem[0..4] - push.0 exec.active_note::get_assets - # => [num_assets, ptr, pad(16)] - - # Must be exactly 1 asset - push.1 assert_eq.err=ERR_B2AGG_WRONG_NUMBER_OF_ASSETS drop - # => [pad(16)] - - # load the 6 B2AGG note input felts as two words - mem_loadw_be.12 swapw.2 mem_loadw_be.8 swapw - # => [EMPTY_WORD, dest_network, dest_address(5), pad(6)] - - # Load ASSET onto the stack - mem_loadw_be.0 - # => [ASSET, dest_network, dest_address(5), pad(6)] - - call.agglayer::bridge_out - # => [pad(16)] - end - # => [pad(16)] -end diff --git a/crates/miden-lib/src/agglayer/mod.rs b/crates/miden-lib/src/agglayer/mod.rs deleted file mode 100644 index be1f75cab5..0000000000 --- a/crates/miden-lib/src/agglayer/mod.rs +++ /dev/null @@ -1,121 +0,0 @@ -use alloc::vec::Vec; - -use miden_objects::account::{AccountComponent, StorageSlot}; -use miden_objects::assembly::Library; -use miden_objects::note::NoteScript; -use miden_objects::utils::Deserializable; -use miden_objects::utils::sync::LazyLock; -use miden_objects::vm::Program; - -pub mod utils; - -// AGGLAYER NOTE SCRIPTS -// ================================================================================================ - -// Initialize the B2AGG note script only once -static B2AGG_SCRIPT: LazyLock = LazyLock::new(|| { - let bytes = - include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer/note_scripts/B2AGG.masb")); - let program = Program::read_from_bytes(bytes).expect("Shipped B2AGG script is well-formed"); - NoteScript::new(program) -}); - -/// Returns the B2AGG (Bridge to AggLayer) note script. -pub fn b2agg_script() -> NoteScript { - B2AGG_SCRIPT.clone() -} - -// AGGLAYER ACCOUNT COMPONENTS -// ================================================================================================ - -// Initialize the unified AggLayer library only once -static AGGLAYER_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer.masl")); - Library::read_from_bytes(bytes).expect("Shipped AggLayer library is well-formed") -}); - -/// Returns the unified AggLayer Library containing all agglayer modules. -pub fn agglayer_library() -> Library { - AGGLAYER_LIBRARY.clone() -} - -/// Returns the Bridge Out Library. -/// -/// Note: This is now the same as agglayer_library() since all agglayer components -/// are compiled into a single library. -pub fn bridge_out_library() -> Library { - agglayer_library() -} - -/// Returns the Local Exit Tree Library. -/// -/// Note: This is now the same as agglayer_library() since all agglayer components -/// are compiled into a single library. -pub fn local_exit_tree_library() -> Library { - agglayer_library() -} - -/// Creates a Local Exit Tree component with the specified storage slots. -/// -/// This component uses the local_exit_tree library and can be added to accounts -/// that need to manage local exit tree functionality. -pub fn local_exit_tree_component(storage_slots: Vec) -> AccountComponent { - let library = local_exit_tree_library(); - - AccountComponent::new(library, storage_slots) - .expect("local_exit_tree component should satisfy the requirements of a valid account component") - .with_supports_all_types() -} - -/// Creates a Bridge Out component with the specified storage slots. -/// -/// This component uses the bridge_out library and can be added to accounts -/// that need to bridge assets out to the AggLayer. -pub fn bridge_out_component(storage_slots: Vec) -> AccountComponent { - let library = bridge_out_library(); - - AccountComponent::new(library, storage_slots) - .expect("bridge_out component should satisfy the requirements of a valid account component") - .with_supports_all_types() -} - -/// Creates a combined Bridge Out component that includes both bridge_out and local_exit_tree -/// modules. -/// -/// This is a convenience function that creates a component with multiple modules. -/// For more fine-grained control, use the individual component functions and combine them -/// using the AccountBuilder pattern. -pub fn bridge_out_with_local_exit_tree_component( - storage_slots: Vec, -) -> Vec { - vec![ - bridge_out_component(storage_slots.clone()), - local_exit_tree_component(vec![]), // local_exit_tree typically doesn't need storage slots - ] -} - -// Initialize the Asset Conversion library only once -static ASSET_CONVERSION_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = include_bytes!(concat!( - env!("OUT_DIR"), - "/assets/agglayer/account_components/asset_conversion.masl" - )); - Library::read_from_bytes(bytes).expect("Shipped Asset Conversion library is well-formed") -}); - -/// Returns the Asset Conversion Library. -pub fn asset_conversion_library() -> Library { - ASSET_CONVERSION_LIBRARY.clone() -} - -/// Creates an Asset Conversion component with the specified storage slots. -/// -/// This component uses the asset_conversion library and can be added to accounts -/// that need to convert assets between Miden and Ethereum formats. -pub fn asset_conversion_component(storage_slots: Vec) -> AccountComponent { - let library = asset_conversion_library(); - - AccountComponent::new(library, storage_slots) - .expect("asset_conversion component should satisfy the requirements of a valid account component") - .with_supports_all_types() -} diff --git a/crates/miden-lib/src/agglayer/utils.rs b/crates/miden-lib/src/agglayer/utils.rs deleted file mode 100644 index 3995ff9ca4..0000000000 --- a/crates/miden-lib/src/agglayer/utils.rs +++ /dev/null @@ -1,173 +0,0 @@ -use alloc::string::String; -use alloc::vec::Vec; - -use miden_objects::Felt; - -/// Convert 8 Felt values (u32 limbs in little-endian order) to U256 bytes in little-endian format. -/// -/// The input limbs are expected to be in little-endian order (least significant limb first). -/// This function converts them to a 32-byte array in little-endian format for compatibility -/// with Ethereum/EVM which expects U256 values as 32 bytes in little-endian format. -/// This ensures compatibility when bridging assets between Miden and Ethereum-based chains. -pub fn felts_to_u256_bytes(limbs: [Felt; 8]) -> [u8; 32] { - let mut bytes = [0u8; 32]; - - for (i, limb) in limbs.iter().enumerate() { - let u32_value = limb.as_int() as u32; - let limb_bytes = u32_value.to_le_bytes(); - bytes[i * 4..(i + 1) * 4].copy_from_slice(&limb_bytes); - } - - bytes -} -/// Converts an Ethereum address (20 bytes) into a vector of 5 Felt values. -/// -/// An Ethereum address is 20 bytes, which we split into 5 u32 values (4 bytes each). -/// The address bytes are distributed as follows: -/// - u32\[0\]: bytes 0-3 -/// - u32\[1\]: bytes 4-7 -/// - u32\[2\]: bytes 8-11 -/// - u32\[3\]: bytes 12-15 -/// - u32\[4\]: bytes 16-19 -/// -/// # Arguments -/// * `address` - A 20-byte Ethereum address -/// -/// # Returns -/// A vector of 5 Felt values representing the address -/// -/// # Panics -/// Panics if the address is not exactly 20 bytes -pub fn ethereum_address_to_felts(address: &[u8; 20]) -> Vec { - let mut result = Vec::with_capacity(5); - - // Convert each 4-byte chunk to a u32 (big-endian) - for i in 0..5 { - let start = i * 4; - let chunk = &address[start..start + 4]; - let value = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); - result.push(Felt::new(value as u64)); - } - - result -} - -/// Converts a vector of 5 Felt values back into a 20-byte Ethereum address. -/// -/// # Arguments -/// * `felts` - A vector of 5 Felt values representing an Ethereum address -/// -/// # Returns -/// A Result containing a 20-byte Ethereum address array, or an error string -/// -/// # Errors -/// Returns an error if the vector doesn't contain exactly 5 felts -pub fn felts_to_ethereum_address(felts: &[Felt]) -> Result<[u8; 20], String> { - if felts.len() != 5 { - return Err(alloc::format!("Expected 5 felts for Ethereum address, got {}", felts.len())); - } - - let mut address = [0u8; 20]; - - for (i, felt) in felts.iter().enumerate() { - let value = felt.as_int() as u32; - let bytes = value.to_be_bytes(); - let start = i * 4; - address[start..start + 4].copy_from_slice(&bytes); - } - - Ok(address) -} - -/// Converts an Ethereum address string (with or without "0x" prefix) into a vector of 5 Felt -/// values. -/// -/// # Arguments -/// * `address_str` - A hex string representing an Ethereum address (40 hex chars, optionally -/// prefixed with "0x") -/// -/// # Returns -/// A Result containing a vector of 5 Felt values representing the address, or an error string -/// -/// # Errors -/// Returns an error if: -/// - The string is not a valid hex string -/// - The string does not represent exactly 20 bytes (40 hex characters) -pub fn ethereum_address_string_to_felts(address_str: &str) -> Result, String> { - // Remove "0x" prefix if present - let hex_str = address_str.strip_prefix("0x").unwrap_or(address_str); - - // Check length (should be 40 hex characters for 20 bytes) - if hex_str.len() != 40 { - return Err(alloc::format!( - "Invalid Ethereum address length: expected 40 hex characters, got {}", - hex_str.len() - )); - } - - // Parse hex string to bytes - let mut address_bytes = [0u8; 20]; - for (i, chunk) in hex_str.as_bytes().chunks(2).enumerate() { - let hex_byte = core::str::from_utf8(chunk) - .map_err(|_| String::from("Invalid UTF-8 in address string"))?; - address_bytes[i] = u8::from_str_radix(hex_byte, 16) - .map_err(|_| alloc::format!("Invalid hex character in address: {}", hex_byte))?; - } - - Ok(ethereum_address_to_felts(&address_bytes)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ethereum_address_round_trip() { - // Test that converting from string to felts and back gives the same result - let original_address = "0x1234567890abcdef1122334455667788990011aa"; - - // Convert string to felts - let felts = ethereum_address_string_to_felts(original_address).unwrap(); - - // Convert felts back to bytes - let recovered_bytes = felts_to_ethereum_address(&felts).unwrap(); - - // Convert original string to bytes for comparison - let original_hex = original_address.strip_prefix("0x").unwrap(); - let mut expected_bytes = [0u8; 20]; - for (i, chunk) in original_hex.as_bytes().chunks(2).enumerate() { - let hex_byte = core::str::from_utf8(chunk).unwrap(); - expected_bytes[i] = u8::from_str_radix(hex_byte, 16).unwrap(); - } - - // Assert they match - assert_eq!(recovered_bytes, expected_bytes); - } - - #[test] - fn test_ethereum_address_to_felts_basic() { - let address: [u8; 20] = [ - 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, - ]; - - let result = ethereum_address_to_felts(&address); - assert_eq!(result.len(), 5); - assert_eq!(result[0], Felt::new(0x12345678)); - assert_eq!(result[1], Felt::new(0x9abcdef0)); - } - - #[test] - fn test_felts_to_ethereum_address_invalid_length() { - let felts = vec![Felt::new(1), Felt::new(2)]; // Only 2 felts - let result = felts_to_ethereum_address(&felts); - assert!(result.is_err()); - } - - #[test] - fn test_ethereum_address_string_invalid_length() { - let address_str = "0x123456"; // Too short - let result = ethereum_address_string_to_felts(address_str); - assert!(result.is_err()); - } -} From 9d05e1a77da2e226275a8bc68c65fa366e28119c Mon Sep 17 00:00:00 2001 From: juan518munoz <62400508+juan518munoz@users.noreply.github.com> Date: Wed, 24 Dec 2025 19:29:52 -0300 Subject: [PATCH 078/114] feat: add `Clone` to `NoteConsumptionStatus` (#2209) --- .../src/note/well_known_note.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/miden-standards/src/note/well_known_note.rs b/crates/miden-standards/src/note/well_known_note.rs index 2c0816fbc5..df0f219782 100644 --- a/crates/miden-standards/src/note/well_known_note.rs +++ b/crates/miden-standards/src/note/well_known_note.rs @@ -1,5 +1,5 @@ use alloc::boxed::Box; -use alloc::string::String; +use alloc::string::{String, ToString}; use core::error::Error; use miden_protocol::account::AccountId; @@ -429,6 +429,27 @@ pub enum NoteConsumptionStatus { NeverConsumable(Box), } +impl Clone for NoteConsumptionStatus { + fn clone(&self) -> Self { + match self { + NoteConsumptionStatus::Consumable => NoteConsumptionStatus::Consumable, + NoteConsumptionStatus::ConsumableAfter(block_height) => { + NoteConsumptionStatus::ConsumableAfter(*block_height) + }, + NoteConsumptionStatus::ConsumableWithAuthorization => { + NoteConsumptionStatus::ConsumableWithAuthorization + }, + NoteConsumptionStatus::UnconsumableConditions => { + NoteConsumptionStatus::UnconsumableConditions + }, + NoteConsumptionStatus::NeverConsumable(error) => { + let err = error.to_string(); + NoteConsumptionStatus::NeverConsumable(err.into()) + }, + } + } +} + #[derive(thiserror::Error, Debug)] #[error("{message}")] struct StaticAnalysisError { From 4c4b2ed373c3765e47504dab872ba32bfcaebd70 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Fri, 26 Dec 2025 14:31:54 -0500 Subject: [PATCH 079/114] Update crates/miden-agglayer/asm/account_components/bridge_out.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/account_components/bridge_out.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/account_components/bridge_out.masm b/crates/miden-agglayer/asm/account_components/bridge_out.masm index 7d4518b2a3..c2da0873a8 100644 --- a/crates/miden-agglayer/asm/account_components/bridge_out.masm +++ b/crates/miden-agglayer/asm/account_components/bridge_out.masm @@ -10,7 +10,7 @@ use agglayer::local_exit_tree const MMR_PTR=42 const LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") -const BURN_NOTE_ROOT=[6407337173854817345,5626358912819151014,703918618794810515,17401169215223723177] +const BURN_NOTE_ROOT = [6407337173854817345, 5626358912819151014, 703918618794810515, 17401169215223723177] const EXECUTION_HINT_ALWAYS=1 const PUBLIC_NOTE=1 const AUX=0 From d94d6fbe15742c9f2f033ec71c849f08c0a5261e Mon Sep 17 00:00:00 2001 From: riemann Date: Fri, 26 Dec 2025 17:05:50 -0500 Subject: [PATCH 080/114] refactor: rm unused import --- .../miden-agglayer/asm/account_components/local_exit_tree.masm | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/miden-agglayer/asm/account_components/local_exit_tree.masm b/crates/miden-agglayer/asm/account_components/local_exit_tree.masm index 9e764db80a..083b7ed330 100644 --- a/crates/miden-agglayer/asm/account_components/local_exit_tree.masm +++ b/crates/miden-agglayer/asm/account_components/local_exit_tree.masm @@ -1,4 +1,3 @@ -use miden::standards::wallets::basic->wallet use miden::protocol::active_account use miden::protocol::native_account From 8e0ee1aaa0974e73306bfedd76d4fec8d1842c7f Mon Sep 17 00:00:00 2001 From: riemann Date: Fri, 26 Dec 2025 17:16:07 -0500 Subject: [PATCH 081/114] refactor: update miden-agglayer asm directory & agglayer masm path --- .../asset_conversion.masm | 0 .../bridge_out.masm | 2 +- .../local_exit_tree.masm | 0 .../asm/note_scripts/B2AGG.masm | 2 +- crates/miden-agglayer/build.rs | 22 +++++++++---------- 5 files changed, 13 insertions(+), 13 deletions(-) rename crates/miden-agglayer/asm/{account_components => bridge}/asset_conversion.masm (100%) rename crates/miden-agglayer/asm/{account_components => bridge}/bridge_out.masm (99%) rename crates/miden-agglayer/asm/{account_components => bridge}/local_exit_tree.masm (100%) diff --git a/crates/miden-agglayer/asm/account_components/asset_conversion.masm b/crates/miden-agglayer/asm/bridge/asset_conversion.masm similarity index 100% rename from crates/miden-agglayer/asm/account_components/asset_conversion.masm rename to crates/miden-agglayer/asm/bridge/asset_conversion.masm diff --git a/crates/miden-agglayer/asm/account_components/bridge_out.masm b/crates/miden-agglayer/asm/bridge/bridge_out.masm similarity index 99% rename from crates/miden-agglayer/asm/account_components/bridge_out.masm rename to crates/miden-agglayer/asm/bridge/bridge_out.masm index c2da0873a8..9f8f401014 100644 --- a/crates/miden-agglayer/asm/account_components/bridge_out.masm +++ b/crates/miden-agglayer/asm/bridge/bridge_out.masm @@ -3,7 +3,7 @@ use miden::protocol::note use miden::protocol::output_note use miden::core::crypto::hashes::keccak256 use miden::core::word -use agglayer::local_exit_tree +use miden::agglayer::local_exit_tree # CONSTANTS # ================================================================================================= diff --git a/crates/miden-agglayer/asm/account_components/local_exit_tree.masm b/crates/miden-agglayer/asm/bridge/local_exit_tree.masm similarity index 100% rename from crates/miden-agglayer/asm/account_components/local_exit_tree.masm rename to crates/miden-agglayer/asm/bridge/local_exit_tree.masm diff --git a/crates/miden-agglayer/asm/note_scripts/B2AGG.masm b/crates/miden-agglayer/asm/note_scripts/B2AGG.masm index 01865da95a..80bdcfbb7f 100644 --- a/crates/miden-agglayer/asm/note_scripts/B2AGG.masm +++ b/crates/miden-agglayer/asm/note_scripts/B2AGG.masm @@ -1,4 +1,4 @@ -use agglayer::bridge_out +use miden::agglayer::bridge_out use miden::protocol::account_id use miden::protocol::active_account use miden::protocol::active_note diff --git a/crates/miden-agglayer/build.rs b/crates/miden-agglayer/build.rs index a64b6fde8f..fda54fd73c 100644 --- a/crates/miden-agglayer/build.rs +++ b/crates/miden-agglayer/build.rs @@ -18,7 +18,7 @@ const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN const ASSETS_DIR: &str = "assets"; const ASM_DIR: &str = "asm"; const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts"; -const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components"; +const ASM_BRIDGE_DIR: &str = "bridge"; const AGGLAYER_ERRORS_FILE: &str = "src/errors/agglayer.rs"; const AGGLAYER_ERRORS_ARRAY_NAME: &str = "AGGLAYER_ERRORS"; @@ -49,10 +49,10 @@ fn main() -> Result<()> { let mut assembler = TransactionKernel::assembler(); - // compile account components first and add the library to the assembler - let agglayer_lib = compile_account_components( - &source_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), - &target_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), + // compile bridge components first and add the library to the assembler + let agglayer_lib = compile_bridge_components( + &source_dir.join(ASM_BRIDGE_DIR), + &target_dir.join(ASM_BRIDGE_DIR), assembler.clone(), )?; @@ -111,12 +111,12 @@ fn compile_note_scripts( Ok(()) } -// COMPILE ACCOUNT COMPONENTS +// COMPILE BRIDGE COMPONENTS // ================================================================================================ -/// Compiles the account components in `source_dir` into MASL libraries and stores the compiled +/// Compiles the bridge components in `source_dir` into MASL libraries and stores the compiled /// files in `target_dir`. -fn compile_account_components( +fn compile_bridge_components( source_dir: &Path, target_dir: &Path, mut assembler: Assembler, @@ -129,11 +129,11 @@ fn compile_account_components( let standards_lib = miden_standards::StandardsLib::default(); assembler.link_static_library(standards_lib)?; - // Compile all components together as a single library under the "agglayer" namespace + // Compile all components together as a single library under the "miden::agglayer" namespace // This allows cross-references between components (e.g., bridge_out using - // agglayer::local_exit_tree) + // miden::agglayer::local_exit_tree) let agglayer_library = assembler - .assemble_library_from_dir(source_dir, "agglayer") + .assemble_library_from_dir(source_dir, "miden::agglayer") .expect("library assembly should succeed"); // Write the combined library From e3e2419656dc08fd2fee3c186e5865812791988b Mon Sep 17 00:00:00 2001 From: riemann Date: Fri, 26 Dec 2025 17:23:45 -0500 Subject: [PATCH 082/114] fix: update agglayer path in build.rs --- crates/miden-agglayer/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/miden-agglayer/src/lib.rs b/crates/miden-agglayer/src/lib.rs index abf35d85d0..c8d8a1be68 100644 --- a/crates/miden-agglayer/src/lib.rs +++ b/crates/miden-agglayer/src/lib.rs @@ -33,8 +33,7 @@ pub fn b2agg_script() -> Program { // Initialize the unified AggLayer library only once static AGGLAYER_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = - include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/agglayer.masl")); + let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/bridge/agglayer.masl")); Library::read_from_bytes(bytes).expect("Shipped AggLayer library is well-formed") }); From fc1cd16b955b43389623bba1dd4e0e2a4cf67f42 Mon Sep 17 00:00:00 2001 From: riemann Date: Fri, 26 Dec 2025 17:31:01 -0500 Subject: [PATCH 083/114] refactor: update agglayer paths in asset tests --- crates/miden-agglayer/src/mod.rs | 121 ------------------ crates/miden-agglayer/src/utils.rs | 2 +- .../tests/agglayer/asset_conversion.rs | 6 +- 3 files changed, 4 insertions(+), 125 deletions(-) delete mode 100644 crates/miden-agglayer/src/mod.rs diff --git a/crates/miden-agglayer/src/mod.rs b/crates/miden-agglayer/src/mod.rs deleted file mode 100644 index be1f75cab5..0000000000 --- a/crates/miden-agglayer/src/mod.rs +++ /dev/null @@ -1,121 +0,0 @@ -use alloc::vec::Vec; - -use miden_objects::account::{AccountComponent, StorageSlot}; -use miden_objects::assembly::Library; -use miden_objects::note::NoteScript; -use miden_objects::utils::Deserializable; -use miden_objects::utils::sync::LazyLock; -use miden_objects::vm::Program; - -pub mod utils; - -// AGGLAYER NOTE SCRIPTS -// ================================================================================================ - -// Initialize the B2AGG note script only once -static B2AGG_SCRIPT: LazyLock = LazyLock::new(|| { - let bytes = - include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer/note_scripts/B2AGG.masb")); - let program = Program::read_from_bytes(bytes).expect("Shipped B2AGG script is well-formed"); - NoteScript::new(program) -}); - -/// Returns the B2AGG (Bridge to AggLayer) note script. -pub fn b2agg_script() -> NoteScript { - B2AGG_SCRIPT.clone() -} - -// AGGLAYER ACCOUNT COMPONENTS -// ================================================================================================ - -// Initialize the unified AggLayer library only once -static AGGLAYER_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer.masl")); - Library::read_from_bytes(bytes).expect("Shipped AggLayer library is well-formed") -}); - -/// Returns the unified AggLayer Library containing all agglayer modules. -pub fn agglayer_library() -> Library { - AGGLAYER_LIBRARY.clone() -} - -/// Returns the Bridge Out Library. -/// -/// Note: This is now the same as agglayer_library() since all agglayer components -/// are compiled into a single library. -pub fn bridge_out_library() -> Library { - agglayer_library() -} - -/// Returns the Local Exit Tree Library. -/// -/// Note: This is now the same as agglayer_library() since all agglayer components -/// are compiled into a single library. -pub fn local_exit_tree_library() -> Library { - agglayer_library() -} - -/// Creates a Local Exit Tree component with the specified storage slots. -/// -/// This component uses the local_exit_tree library and can be added to accounts -/// that need to manage local exit tree functionality. -pub fn local_exit_tree_component(storage_slots: Vec) -> AccountComponent { - let library = local_exit_tree_library(); - - AccountComponent::new(library, storage_slots) - .expect("local_exit_tree component should satisfy the requirements of a valid account component") - .with_supports_all_types() -} - -/// Creates a Bridge Out component with the specified storage slots. -/// -/// This component uses the bridge_out library and can be added to accounts -/// that need to bridge assets out to the AggLayer. -pub fn bridge_out_component(storage_slots: Vec) -> AccountComponent { - let library = bridge_out_library(); - - AccountComponent::new(library, storage_slots) - .expect("bridge_out component should satisfy the requirements of a valid account component") - .with_supports_all_types() -} - -/// Creates a combined Bridge Out component that includes both bridge_out and local_exit_tree -/// modules. -/// -/// This is a convenience function that creates a component with multiple modules. -/// For more fine-grained control, use the individual component functions and combine them -/// using the AccountBuilder pattern. -pub fn bridge_out_with_local_exit_tree_component( - storage_slots: Vec, -) -> Vec { - vec![ - bridge_out_component(storage_slots.clone()), - local_exit_tree_component(vec![]), // local_exit_tree typically doesn't need storage slots - ] -} - -// Initialize the Asset Conversion library only once -static ASSET_CONVERSION_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = include_bytes!(concat!( - env!("OUT_DIR"), - "/assets/agglayer/account_components/asset_conversion.masl" - )); - Library::read_from_bytes(bytes).expect("Shipped Asset Conversion library is well-formed") -}); - -/// Returns the Asset Conversion Library. -pub fn asset_conversion_library() -> Library { - ASSET_CONVERSION_LIBRARY.clone() -} - -/// Creates an Asset Conversion component with the specified storage slots. -/// -/// This component uses the asset_conversion library and can be added to accounts -/// that need to convert assets between Miden and Ethereum formats. -pub fn asset_conversion_component(storage_slots: Vec) -> AccountComponent { - let library = asset_conversion_library(); - - AccountComponent::new(library, storage_slots) - .expect("asset_conversion component should satisfy the requirements of a valid account component") - .with_supports_all_types() -} diff --git a/crates/miden-agglayer/src/utils.rs b/crates/miden-agglayer/src/utils.rs index e9d616d928..b3b56dfdf8 100644 --- a/crates/miden-agglayer/src/utils.rs +++ b/crates/miden-agglayer/src/utils.rs @@ -1,7 +1,7 @@ use alloc::string::String; use alloc::vec::Vec; -use miden_core::Felt; +use miden_protocol::Felt; /// Convert 8 Felt values (u32 limbs in little-endian order) to U256 bytes in little-endian format. /// diff --git a/crates/miden-testing/tests/agglayer/asset_conversion.rs b/crates/miden-testing/tests/agglayer/asset_conversion.rs index 2e1ba5c567..6cec09d255 100644 --- a/crates/miden-testing/tests/agglayer/asset_conversion.rs +++ b/crates/miden-testing/tests/agglayer/asset_conversion.rs @@ -60,7 +60,7 @@ async fn test_convert_to_u256_helper( let script_code = format!( " use miden::core::sys - use agglayer::asset_conversion + use miden::agglayer::asset_conversion begin push.{}.{} @@ -137,7 +137,7 @@ async fn test_convert_to_u256_scaled_eth() -> anyhow::Result<()> { let script_code = format!( " use miden::core::sys - use agglayer::asset_conversion + use miden::agglayer::asset_conversion begin push.{}.{} @@ -179,7 +179,7 @@ async fn test_convert_to_u256_scaled_large_amount() -> anyhow::Result<()> { let script_code = format!( " use miden::core::sys - use agglayer::asset_conversion + use miden::agglayer::asset_conversion begin push.{}.{} From e3a017dc0b529606959aa9094eb23a158a02ad98 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Fri, 26 Dec 2025 17:52:16 -0500 Subject: [PATCH 084/114] refactor: rm `OLD_MAP_ROOT` from return of `set_map_item` (#2194) --- CHANGELOG.md | 1 + .../asm/kernels/transaction/api.masm | 9 ++-- .../asm/kernels/transaction/lib/account.masm | 44 ++++++++----------- .../asm/kernels/transaction/lib/faucet.masm | 4 +- .../asm/protocol/native_account.masm | 13 +++--- .../src/transaction/kernel/procedures.rs | 6 +-- .../ecdsa_k256_keccak_multisig.masm | 12 ++--- .../rpo_falcon_512_multisig.masm | 12 ++--- .../src/testing/mock_account_code.rs | 4 +- .../src/kernel_tests/tx/test_account.rs | 17 ++++--- .../src/kernel_tests/tx/test_account_delta.rs | 2 +- crates/miden-tx/src/executor/exec_host.rs | 6 +-- crates/miden-tx/src/host/tx_event.rs | 14 +++--- crates/miden-tx/src/prover/prover_host.rs | 13 +++--- docs/src/protocol_library.md | 2 +- 15 files changed, 77 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f06f41cdbf..f44c133564 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197)). - [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). +- [BREAKING] Removed OLD_MAP_ROOT from being returned when calling [`native_account::set_map_item`](crates/miden-lib/asm/miden/native_account.masm) ([#2194](https://github.com/0xMiden/miden-base/pull/2194)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-protocol/asm/kernels/transaction/api.masm b/crates/miden-protocol/asm/kernels/transaction/api.masm index e3a5edd3cc..d5f7891a4f 100644 --- a/crates/miden-protocol/asm/kernels/transaction/api.masm +++ b/crates/miden-protocol/asm/kernels/transaction/api.masm @@ -479,7 +479,7 @@ end #! slot. #! #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] -#! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] +#! Outputs: [OLD_VALUE, pad(12)] #! #! Where: #! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are @@ -488,7 +488,6 @@ end #! - NEW_VALUE is the value of the new map item for the respective KEY. #! - OLD_VALUE is the value of the old map item for the respective KEY. #! - KEY is the key of the new item. -#! - OLD_MAP_ROOT is the root of the old map before insertion #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. @@ -508,11 +507,11 @@ pub proc account_set_map_item # set the new map item exec.account::set_map_item - # => [OLD_MAP_ROOT, OLD_VALUE, pad(16)] + # => [OLD_VALUE, pad(16)] # truncate the stack - movupw.2 dropw movupw.2 dropw - # => [OLD_MAP_ROOT, OLD_VALUE, pad(8)] + swapw dropw + # => [OLD_VALUE, pad(12)] end #! Returns the vault root of the active account at the beginning of the transaction. diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/account.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm index e5c7dca37e..b2dcfa4bc7 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm @@ -605,7 +605,7 @@ end #! Stores NEW_VALUE under the specified KEY within the map contained in the given account storage slot. #! #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE] -#! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE] +#! Outputs: [OLD_VALUE] #! #! Where: #! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are @@ -613,8 +613,7 @@ end #! - the slot must point to the root of the storage map. #! - NEW_VALUE is the value to set under KEY. #! - KEY is the key to set. -#! - OLD_MAP_VALUE is the previous value of the item. -#! - OLD_MAP_ROOT is the root of the old map before insertion +#! - OLD_VALUE is the previous value of the item. #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. @@ -636,9 +635,9 @@ pub proc set_map_item exec.constants::get_storage_slot_type_map eq assert.err=ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT # => [slot_ptr, KEY, NEW_VALUE] - + exec.set_map_item_raw - # => [OLD_MAP_ROOT, OLD_MAP_VALUE] + # => [OLD_VALUE] end #! Returns the type of the storage slot at the provided index. @@ -1402,7 +1401,7 @@ proc insert_and_validate_storage_map # insert the key-value pair into account storage # this could be optimized to avoid reading and writing the root on every call - exec.set_map_item_raw dropw dropw + exec.set_map_item_raw dropw # => [remaining_entries, slot_ptr, MAP_ROOT] dup neq.0 @@ -1556,14 +1555,13 @@ end #! WARNING: This must only be used on the native account. #! #! Inputs: [slot_ptr, KEY, NEW_VALUE] -#! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE] +#! Outputs: [OLD_VALUE] #! #! Where: #! - slot_ptr is the pointer to a slot. #! - KEY is the key to set. #! - NEW_VALUE is the value to set under KEY. -#! - OLD_MAP_VALUE is the previous value of the item. -#! - OLD_MAP_ROOT is the root of the old map before insertion +#! - OLD_VALUE is the previous value of the item. #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. @@ -1573,7 +1571,7 @@ end #! - 0: slot_ptr #! - 4..8: OLD_MAP_VALUE #! - 8..12: OLD_MAP_ROOT -@locals(12) +@locals(8) proc set_map_item_raw # store slot_ptr until the end of the procedure dup loc_store.0 @@ -1586,10 +1584,6 @@ proc set_map_item_raw exec.get_item_raw # => [OLD_MAP_ROOT, KEY, NEW_VALUE] - # store OLD_MAP_ROOT until the end of the procedure - loc_storew_be.8 - # => [OLD_MAP_ROOT, KEY, NEW_VALUE] - # duplicate the KEY and the NEW_VALUE for account delta insertion and event emission dupw.2 dupw.2 # => [KEY, NEW_VALUE, OLD_MAP_ROOT, KEY, NEW_VALUE] @@ -1600,36 +1594,36 @@ proc set_map_item_raw # set the NEW_VALUE under HASHED_KEY in the tree exec.smt::set - # => [OLD_MAP_VALUE, NEW_ROOT, KEY, NEW_VALUE] + # => [OLD_VALUE, NEW_ROOT, KEY, NEW_VALUE] - # store OLD_MAP_VALUE until the end of the procedure + # store OLD_VALUE until the end of the procedure loc_storew_be.4 swapw - # => [NEW_ROOT, OLD_MAP_VALUE, KEY, NEW_VALUE] + # => [NEW_ROOT, OLD_VALUE, KEY, NEW_VALUE] # store NEW_ROOT into the map slot's VALUE loc_load.0 exec.set_item_raw - # => [OLD_MAP_VALUE, KEY, NEW_VALUE] + # => [OLD_VALUE, KEY, NEW_VALUE] swapw - # => [KEY, OLD_MAP_VALUE, NEW_VALUE] + # => [KEY, OLD_VALUE, NEW_VALUE] loc_load.0 - # => [slot_ptr, KEY, OLD_MAP_VALUE, NEW_VALUE] + # => [slot_ptr, KEY, OLD_VALUE, NEW_VALUE] # emit event to signal that an account storage map item is being updated emit.ACCOUNT_STORAGE_AFTER_SET_MAP_ITEM_EVENT - # => [slot_ptr, KEY, OLD_MAP_VALUE, NEW_VALUE] + # => [slot_ptr, KEY, OLD_VALUE, NEW_VALUE] # convert the slot ptr to a slot index for the delta API exec.slot_ptr_to_index - # => [slot_idx, KEY, OLD_MAP_VALUE, NEW_VALUE] + # => [slot_idx, KEY, OLD_VALUE, NEW_VALUE] exec.account_delta::set_map_item # => [] - # load OLD_MAP_ROOT and OLD_MAP_VALUE as return values on the stack - padw loc_loadw_be.4 padw loc_loadw_be.8 - # => [OLD_MAP_ROOT, OLD_MAP_VALUE] + # load OLD_VALUE as return value on the stack + padw loc_loadw_be.4 + # => [OLD_VALUE] end #! Gets the value of the storage slot located at the memory address specified by the provided diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm b/crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm index d1b1fd2407..e0cdb36314 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/faucet.masm @@ -151,7 +151,7 @@ proc mint_non_fungible_asset # => [faucet_slot_name_prefix, faucet_slot_name_suffix, ASSET_KEY, ASSET, ASSET] # insert the non-fungible asset into the tracking SMT - exec.account::set_map_item dropw + exec.account::set_map_item # => [OLD_VAL, ASSET] # Assert the `OLD_VAL` is an EMPTY_WORD, indicating that the non-fungible asset has not been @@ -199,7 +199,7 @@ proc burn_non_fungible_asset # => [faucet_storage_data_slot, ASSET_KEY, EMPTY_WORD, ASSET] # remove the non-fungible asset from the tracking SMT - exec.account::set_map_item dropw + exec.account::set_map_item # => [OLD_VAL, ASSET] # Assert the `OLD_VAL` is not an EMPTY_WORD, indicating that the non-fungible asset exists. We diff --git a/crates/miden-protocol/asm/protocol/native_account.masm b/crates/miden-protocol/asm/protocol/native_account.masm index 1ca40bdad0..197f52ba25 100644 --- a/crates/miden-protocol/asm/protocol/native_account.masm +++ b/crates/miden-protocol/asm/protocol/native_account.masm @@ -148,7 +148,7 @@ end #! Sets a map item in the native account storage. #! #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, VALUE] -#! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE] +#! Outputs: [OLD_VALUE] #! #! Where: #! - slot_id_{prefix, suffix} are the prefix and suffix felts of the slot identifier, which are @@ -156,8 +156,7 @@ end #! - the slot must point to the root of the storage map. #! - KEY is the key to set at VALUE. #! - VALUE is the value to set at KEY. -#! - OLD_MAP_ROOT is the old map root. -#! - OLD_MAP_VALUE is the old value at KEY. +#! - OLD_VALUE is the old value at KEY. #! #! Panics if: #! - a slot with the provided slot ID does not exist in account storage. @@ -181,11 +180,11 @@ pub proc set_map_item # => [offset, slot_id_prefix, slot_id_suffix, KEY, VALUE, pad(5)] syscall.exec_kernel_proc - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] + # => [OLD_VALUE, pad(12)] - # clean the stack - swapdw dropw dropw - # => [OLD_MAP_ROOT, OLD_MAP_VALUE] + # drop pad(12) + movdnw.3 dropw dropw dropw + # => [OLD_VALUE] end # VAULT diff --git a/crates/miden-protocol/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs index b8e805da30..0b7e317d6d 100644 --- a/crates/miden-protocol/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -34,7 +34,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_get_initial_map_item word!("0x7f00c7140a71d12d1162a9cf0143bdd64bbd7e8d63b115fc3a2b07338813f8ab"), // account_set_map_item - word!("0x77df8fd76b36f7750d968f138ae85ea880a6c0a3f21b6bfab88919a509c81880"), + word!("0x416c57bd6388b0c4da20409458588678811f15d60bc73bd939d4e26a48ed6874"), // account_get_initial_vault_root word!("0x46297d9ac95afd60c7ef1a065e024ad49aa4c019f6b3924191905449b244d4ec"), // account_get_vault_root @@ -60,9 +60,9 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_procedure word!("0xb0b63fdd01af0bcb4aacb2412e934cdc7691308647152d416c7ae4fc909da076"), // faucet_mint_asset - word!("0x76fc1dd0518f83d5aad0fecdda238529e2e0222267eebcb5e8598abc1b202cd5"), + word!("0x3e4959e0309e85566c11397f95f32a53617da002cbbe10a174640a31a03b2498"), // faucet_burn_asset - word!("0x024479e5265c131572464f5a41885d2fcdec8e29dcb9146900529665aed8e2e5"), + word!("0x8490d8b28e3192df25500da5a1af2ea133f7c34f11328fb38235c2d7147d11fa"), // faucet_get_total_fungible_asset_issuance word!("0xa1d900ff530770c3e56876fedd1b8f9f5ca491e2198524a973148ae3df88c581"), // faucet_is_non_fungible_asset_issued diff --git a/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm index 935e1f3e61..b03b896d6a 100644 --- a/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm +++ b/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm @@ -74,9 +74,9 @@ proc assert_new_tx(msg: BeWord) # Set the key value pair in the map to mark transaction as executed exec.native_account::set_map_item - # => [OLD_MAP_ROOT, [0, 0, 0, is_executed]] + # => [[0, 0, 0, is_executed]] - dropw drop drop drop + drop drop drop # => [is_executed] assertz.err=ERR_TX_ALREADY_EXECUTED @@ -119,9 +119,9 @@ proc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u3 # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers] exec.native_account::set_map_item - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, new_num_of_approvers] + # => [OLD_VALUE, i-1, new_num_of_approvers] - dropw dropw + dropw # => [i-1, new_num_of_approvers] dup.1 dup.1 @@ -210,9 +210,9 @@ pub proc update_signers_and_threshold(multisig_config_hash: BeWord) # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)] exec.native_account::set_map_item - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, pad(12)] + # => [OLD_VALUE, i-1, pad(12)] - dropw dropw + dropw # => [i-1, pad(12)] dup neq.0 diff --git a/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm b/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm index 681d176452..db47387ddf 100644 --- a/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm +++ b/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm @@ -74,9 +74,9 @@ proc assert_new_tx(msg: BeWord) # Set the key value pair in the map to mark transaction as executed exec.native_account::set_map_item - # => [OLD_MAP_ROOT, [0, 0, 0, is_executed]] + # => [[0, 0, 0, is_executed]] - dropw drop drop drop + drop drop drop # => [is_executed] assertz.err=ERR_TX_ALREADY_EXECUTED @@ -119,9 +119,9 @@ proc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u3 # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers] exec.native_account::set_map_item - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, new_num_of_approvers] + # => [OLD_VALUE, i-1, new_num_of_approvers] - dropw dropw + dropw # => [i-1, new_num_of_approvers] dup.1 dup.1 @@ -210,9 +210,9 @@ pub proc update_signers_and_threshold(multisig_config_hash: BeWord) # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)] exec.native_account::set_map_item - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, pad(12)] + # => [OLD_VALUE, i-1, pad(12)] - dropw dropw + dropw # => [i-1, pad(12)] dup neq.0 diff --git a/crates/miden-standards/src/testing/mock_account_code.rs b/crates/miden-standards/src/testing/mock_account_code.rs index 357ae92fbf..cabcb23028 100644 --- a/crates/miden-standards/src/testing/mock_account_code.rs +++ b/crates/miden-standards/src/testing/mock_account_code.rs @@ -64,10 +64,10 @@ const MOCK_ACCOUNT_CODE: &str = " end #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE, pad(6)] - #! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] + #! Outputs: [OLD_VALUE, pad(12)] pub proc set_map_item exec.native_account::set_map_item - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] + # => [OLD_VALUE, pad(12)] end #! Inputs: [slot_id_prefix, slot_id_suffix, KEY, pad(10)] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 64e9c1e905..342da526a3 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -744,18 +744,18 @@ async fn test_set_map_item() -> miette::Result<()> { # double check that the storage slot is indeed the new map push.SLOT_NAME[0..2] - # => [slot_id_prefix, slot_id_suffix, OLD_VALUE, OLD_MAP_ROOT] + # => [slot_id_prefix, slot_id_suffix, OLD_VALUE] # pad the stack repeat.14 push.0 movdn.2 end - # => [slot_id_prefix, slot_id_suffix, pad(14), OLD_VALUE, OLD_MAP_ROOT] + # => [slot_id_prefix, slot_id_suffix, pad(14), OLD_VALUE] call.mock_account::get_item - # => [MAP_ROOT, pad(12), OLD_VALUE, OLD_MAP_ROOT] + # => [MAP_ROOT, pad(12), OLD_VALUE] # truncate the stack repeat.3 swapw dropw end - # => [MAP_ROOT, OLD_VALUE, OLD_MAP_ROOT] + # => [MAP_ROOT, OLD_VALUE] exec.sys::truncate_stack end @@ -775,10 +775,15 @@ async fn test_set_map_item() -> miette::Result<()> { exec_output.get_stack_word_be(0), "get_item should return the updated root", ); + + let old_value_for_key = match slot.content() { + StorageSlotContent::Map(original_map) => original_map.get(&new_key), + _ => panic!("expected map"), + }; assert_eq!( - slot.content().value(), + old_value_for_key, exec_output.get_stack_word_be(4), - "get_item must return the new updated value", + "set_map_item must return the old value for the key (empty word for new key)", ); Ok(()) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index bf1ffe2849..bf2473d7f5 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -1126,7 +1126,7 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " # => [index, KEY, VALUE, pad(6)] call.account::set_map_item - # => [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)] + # => [OLD_VALUE, pad(12)] dropw dropw dropw dropw # => [] diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index d2af2b6974..b24aa39e5a 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -499,13 +499,13 @@ where TransactionEvent::AccountStorageAfterSetMapItem { slot_name, key, - old_map_value: prev_map_value, - new_map_value, + old_value: prev_map_value, + new_value, } => self.base_host.on_account_storage_after_set_map_item( slot_name, key, prev_map_value, - new_map_value, + new_value, ), TransactionEvent::AccountVaultBeforeAssetAccess { diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index ab16e8eff6..9964f7a5b4 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -64,8 +64,8 @@ pub(crate) enum TransactionEvent { AccountStorageAfterSetMapItem { slot_name: StorageSlotName, key: Word, - old_map_value: Word, - new_map_value: Word, + old_value: Word, + new_value: Word, }, /// The data necessary to request a storage map witness from the data store. @@ -289,11 +289,11 @@ impl TransactionEvent { }, TransactionEventId::AccountStorageAfterSetMapItem => { - // Expected stack state: [event, slot_ptr, KEY, OLD_MAP_VALUE, NEW_VALUE] + // Expected stack state: [event, slot_ptr, KEY, OLD_VALUE, NEW_VALUE] let slot_ptr = process.get_stack_item(1); let key = process.get_stack_word_be(2); - let old_map_value = process.get_stack_word_be(6); - let new_map_value = process.get_stack_word_be(10); + let old_value = process.get_stack_word_be(6); + let new_value = process.get_stack_word_be(10); // Resolve slot ID to slot name. let (slot_id, ..) = process.get_storage_slot(slot_ptr)?; @@ -303,8 +303,8 @@ impl TransactionEvent { Some(TransactionEvent::AccountStorageAfterSetMapItem { slot_name, key, - old_map_value, - new_map_value, + old_value, + new_value, }) }, diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index 5cdc3a9edc..de31f51da4 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -122,14 +122,11 @@ where TransactionEvent::AccountStorageAfterSetMapItem { slot_name, key, - old_map_value, - new_map_value, - } => self.base_host.on_account_storage_after_set_map_item( - slot_name, - key, - old_map_value, - new_map_value, - ), + old_value, + new_value, + } => self + .base_host + .on_account_storage_after_set_map_item(slot_name, key, old_value, new_value), // Access witnesses should be in the advice provider at proving time. TransactionEvent::AccountVaultBeforeAssetAccess { .. } => Ok(Vec::new()), diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index 6f2f9ef0cf..b67662d744 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -63,7 +63,7 @@ Native account procedures can be used to write to storage, add or remove assets | `incr_nonce` | Increments the nonce of the native account by one and returns the new nonce. Can only be called from auth procedures.

**Inputs:** `[]`
**Outputs:** `[final_nonce]` | Auth | | `compute_delta_commitment` | Computes the commitment to the native account's delta. Can only be called from auth procedures.

**Inputs:** `[]`
**Outputs:** `[DELTA_COMMITMENT]` | Auth | | `set_item` | Sets an item in the native account storage.

**Inputs:** `[slot_id_prefix, slot_id_suffix, VALUE]`
**Outputs:** `[OLD_VALUE]` | Native & Account | -| `set_map_item` | Sets VALUE under the specified KEY within the map contained in the given native account storage slot.

**Inputs:** `[slot_id_prefix, slot_id_suffix, KEY, VALUE]`
**Outputs:** `[OLD_MAP_ROOT, OLD_MAP_VALUE]` | Native & Account | +| `set_map_item` | Sets VALUE under the specified KEY within the map contained in the given native account storage slot.

**Inputs:** `[slot_id_prefix, slot_id_suffix, KEY, VALUE]`
**Outputs:** `[OLD_VALUE]` | Native & Account | | `add_asset` | Adds the specified asset to the vault. For fungible assets, returns the total after addition.

**Inputs:** `[ASSET]`
**Outputs:** `[ASSET']` | Native & Account | | `remove_asset` | Removes the specified asset from the vault.

**Inputs:** `[ASSET]`
**Outputs:** `[ASSET]` | Native & Account | | `was_procedure_called` | Returns 1 if a native account procedure was called during transaction execution, and 0 otherwise.

**Inputs:** `[PROC_ROOT]`
**Outputs:** `[was_called]` | Any | From 5ae7b8a68ad1d933783a667c425226b79defd86f Mon Sep 17 00:00:00 2001 From: igamigo Date: Tue, 30 Dec 2025 08:59:44 -0300 Subject: [PATCH 085/114] feat: follow-ups from #2193 (#2207) * feat: renames, doc improvements, submodule export * Update crates/miden-protocol/src/account/component/storage/type_registry.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Marti Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/account/component/metadata/mod.rs | 10 +-- .../src/account/component/storage/schema.rs | 77 +++++------------ .../storage/toml/init_storage_data.rs | 2 +- .../src/account/component/storage/toml/mod.rs | 2 +- .../account/component/storage/toml/tests.rs | 29 ++++++- .../component/storage/type_registry.rs | 83 +++++++++++++++---- .../account/component/storage/value_name.rs | 39 +++------ crates/miden-protocol/src/account/mod.rs | 2 +- .../miden-protocol/src/account/storage/mod.rs | 10 ++- docs/src/account/components.md | 29 ++++--- 10 files changed, 154 insertions(+), 129 deletions(-) diff --git a/crates/miden-protocol/src/account/component/metadata/mod.rs b/crates/miden-protocol/src/account/component/metadata/mod.rs index 619e8cc031..2f96c19274 100644 --- a/crates/miden-protocol/src/account/component/metadata/mod.rs +++ b/crates/miden-protocol/src/account/component/metadata/mod.rs @@ -27,10 +27,10 @@ use crate::AccountError; /// /// - The metadata's storage schema does not contain duplicate slot names. /// - The schema cannot contain protocol-reserved slot names. -/// - Each init-time value name uniquely identifies a single value. The expected init-time metadata -/// can be retrieved with [AccountComponentMetadata::schema_requirements()], which returns a map -/// from keys to [SchemaRequirement] (which indicates the expected value type and optional -/// defaults). +/// - Each init-time value name uniquely identifies a single value. The expected init-time +/// requirements can be retrieved with [AccountComponentMetadata::schema_requirements()], which +/// returns a map from keys to [SchemaRequirement] (which indicates the expected value type and +/// optional defaults). /// /// # Example /// @@ -123,7 +123,7 @@ impl AccountComponentMetadata { } } - /// Returns the init-time values's requirements for this schema. + /// Returns the init-time value requirements for this schema. /// /// These values are used for initializing storage slot values or storage map entries. For a /// full example, refer to the docs for [AccountComponentMetadata]. diff --git a/crates/miden-protocol/src/account/component/storage/schema.rs b/crates/miden-protocol/src/account/component/storage/schema.rs index d8b6f5a170..9ae69f7b7a 100644 --- a/crates/miden-protocol/src/account/component/storage/schema.rs +++ b/crates/miden-protocol/src/account/component/storage/schema.rs @@ -8,7 +8,8 @@ use miden_processor::DeserializationError; use super::type_registry::{SCHEMA_TYPE_REGISTRY, SchemaRequirement, SchemaTypeId}; use super::{InitStorageData, StorageValueName, WordValue}; -use crate::account::{AccountStorage, StorageMap, StorageSlot, StorageSlotName}; +use crate::account::storage::is_reserved_slot_name; +use crate::account::{StorageMap, StorageSlot, StorageSlotName}; use crate::errors::AccountComponentTemplateError; use crate::{Felt, FieldElement, Word}; @@ -83,7 +84,7 @@ impl AccountStorageSchema { let mut init_values = BTreeMap::new(); for (slot_name, schema) in self.slots.iter() { - if slot_name.id() == AccountStorage::faucet_sysdata_slot().id() { + if is_reserved_slot_name(slot_name) { return Err(AccountComponentTemplateError::ReservedSlotName(slot_name.clone())); } @@ -277,8 +278,9 @@ impl WordSchema { return Ok(()); } - let default_value = - default_value.map(|word| SCHEMA_TYPE_REGISTRY.display_word(r#type, word)); + let default_value = default_value.map(|word| { + SCHEMA_TYPE_REGISTRY.display_word(r#type, word).value().to_string() + }); if requirements .insert( @@ -320,7 +322,8 @@ impl WordSchema { default_value: Some(default_value), } = self { - validate_word_value(word_type_kind(r#type), r#type, *default_value) + SCHEMA_TYPE_REGISTRY + .validate_word_value(r#type, *default_value) .map_err(AccountComponentTemplateError::StorageValueParsingError)?; } @@ -356,7 +359,8 @@ impl WordSchema { .map_err(AccountComponentTemplateError::StorageValueParsingError)?; let felts: [Felt; 4] = felts.try_into().expect("length is 4"); let word = Word::from(felts); - validate_word_value(word_type_kind(r#type), r#type, word) + SCHEMA_TYPE_REGISTRY + .validate_word_value(r#type, word) .map_err(AccountComponentTemplateError::StorageValueParsingError)?; Ok(word) }, @@ -390,7 +394,7 @@ impl WordSchema { ) -> Result<(), AccountComponentTemplateError> { match self { WordSchema::Simple { r#type, .. } => { - validate_word_value(word_type_kind(r#type), r#type, word).map_err(|err| { + SCHEMA_TYPE_REGISTRY.validate_word_value(r#type, word).map_err(|err| { AccountComponentTemplateError::InvalidInitStorageValue( slot_prefix.clone(), format!("{label} does not match `{}`: {err}", r#type), @@ -400,12 +404,14 @@ impl WordSchema { WordSchema::Composite { value } => { for (index, felt_schema) in value.iter().enumerate() { let felt_type = felt_schema.felt_type(); - validate_felt_value(&felt_type, word[index]).map_err(|err| { - AccountComponentTemplateError::InvalidInitStorageValue( - slot_prefix.clone(), - format!("{label}[{index}] does not match `{felt_type}`: {err}"), - ) - })?; + SCHEMA_TYPE_REGISTRY.validate_felt_value(&felt_type, word[index]).map_err( + |err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("{label}[{index}] does not match `{felt_type}`: {err}"), + ) + }, + )?; } Ok(()) @@ -663,7 +669,8 @@ impl FeltSchema { } if let Some(value) = self.default_value { - validate_felt_value(&self.felt_type(), value) + SCHEMA_TYPE_REGISTRY + .validate_felt_value(&self.felt_type(), value) .map_err(AccountComponentTemplateError::StorageValueParsingError)?; } Ok(()) @@ -689,48 +696,6 @@ impl Deserializable for FeltSchema { } } -// VALUE VALIDATION HELPERS -// ================================================================================================ - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum WordTypeKind { - Word, - Felt, -} - -fn word_type_kind(schema_type: &SchemaTypeId) -> WordTypeKind { - if SCHEMA_TYPE_REGISTRY.contains_felt_type(schema_type) { - WordTypeKind::Felt - } else { - WordTypeKind::Word - } -} - -fn validate_word_value( - kind: WordTypeKind, - schema_type: &SchemaTypeId, - word: Word, -) -> Result<(), super::SchemaTypeError> { - match kind { - WordTypeKind::Word => Ok(()), - WordTypeKind::Felt => { - if word[0] != Felt::ZERO || word[1] != Felt::ZERO || word[2] != Felt::ZERO { - return Err(super::SchemaTypeError::ConversionError(format!( - "expected a word of the form [0, 0, 0, ] for type `{schema_type}`" - ))); - } - validate_felt_value(schema_type, word[3]) - }, - } -} - -fn validate_felt_value( - schema_type: &SchemaTypeId, - felt: Felt, -) -> Result<(), super::SchemaTypeError> { - SCHEMA_TYPE_REGISTRY.validate_felt_value(schema_type, felt) -} - /// Describes the schema for a storage value slot. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ValueSlotSchema { diff --git a/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs index 99e2e7af41..72a4a79044 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs @@ -158,7 +158,7 @@ pub enum InitStorageDataError { DuplicateKey(String), #[error( - "invalid input for `{key}`: unsupported array value (len {len}); expected either a map entry list (array of inline tables with `key` and `value`) or a 4-element word array of strings" + "invalid input for `{key}`: unsupported array value (length {len}); expected either a map entry list (array of inline tables with `key` and `value`) or a 4-element word array of strings" )] ArraysNotSupported { key: String, len: usize }, diff --git a/crates/miden-protocol/src/account/component/storage/toml/mod.rs b/crates/miden-protocol/src/account/component/storage/toml/mod.rs index 130f57d954..1269e7db8b 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/mod.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/mod.rs @@ -492,6 +492,6 @@ impl WordValue { } pub(super) fn from_word(schema_type: &SchemaTypeId, word: Word) -> Self { - WordValue::Atomic(SCHEMA_TYPE_REGISTRY.display_word(schema_type, word)) + WordValue::Atomic(SCHEMA_TYPE_REGISTRY.display_word(schema_type, word).value().to_string()) } } diff --git a/crates/miden-protocol/src/account/component/storage/toml/tests.rs b/crates/miden-protocol/src/account/component/storage/toml/tests.rs index 5d491a53f3..419992673e 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/tests.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/tests.rs @@ -65,7 +65,8 @@ fn from_toml_rejects_non_string_atomics() { #[test] fn test_error_on_array() { let toml_str = r#" - "demo::token_metadata.v" = ["1", "2", "3", "4", "5"] + ["demo::token_metadata"] + v = ["1", "2", "3", "4", "5"] "#; let err = InitStorageData::from_toml(toml_str).unwrap_err(); @@ -74,9 +75,6 @@ fn test_error_on_array() { InitStorageDataError::ArraysNotSupported { key, len } if key == "demo::token_metadata.v" && *len == 5 ); - let msg = err.to_string(); - assert!(msg.contains("demo::token_metadata.v")); - assert!(msg.contains("len 5")); } #[test] @@ -202,6 +200,29 @@ fn metadata_from_toml_rejects_typed_fields_in_static_map_values() { ); } +#[test] +fn metadata_from_toml_rejects_short_composite_schema() { + let toml_str = r#" + name = "Test Component" + description = "Test description" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::short_composite" + type = [ + { type = "u8", name = "a" }, + { type = "void" }, + { type = "void" }, + ] + "#; + assert_matches::assert_matches!( + AccountComponentMetadata::from_toml(toml_str), + Err(AccountComponentTemplateError::InvalidSchema(msg)) + if msg.contains("array of 4 elements") + ); +} + #[test] fn metadata_from_toml_rejects_reserved_slot_names() { let reserved_slot = AccountStorage::faucet_sysdata_slot().as_str(); diff --git a/crates/miden-protocol/src/account/component/storage/type_registry.rs b/crates/miden-protocol/src/account/component/storage/type_registry.rs index fe94417574..45512f5e95 100644 --- a/crates/miden-protocol/src/account/component/storage/type_registry.rs +++ b/crates/miden-protocol/src/account/component/storage/type_registry.rs @@ -5,7 +5,7 @@ use core::error::Error; use core::fmt::{self, Display}; use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; -use miden_core::{Felt, Word}; +use miden_core::{Felt, FieldElement, Word}; use miden_crypto::dsa::{ecdsa_k256_keccak, falcon512_rpo}; use miden_processor::DeserializationError; use thiserror::Error; @@ -491,10 +491,10 @@ impl WordType for ecdsa_k256_keccak::PublicKey { // ================================================================================================ /// Type alias for a function that converts a string into a [`Felt`] value. -type FeltTypeConverter = fn(&str) -> Result; +type FeltFromStrConverter = fn(&str) -> Result; /// Type alias for a function that converts a string into a [`Word`]. -type WordTypeConverter = fn(&str) -> Result; +type WordFromStrConverter = fn(&str) -> Result; /// Type alias for a function that converts a [`Felt`] into a canonical string representation. type FeltTypeDisplayer = fn(Felt) -> Result; @@ -502,6 +502,30 @@ type FeltTypeDisplayer = fn(Felt) -> Result; /// Type alias for a function that converts a [`Word`] into a canonical string representation. type WordTypeDisplayer = fn(Word) -> Result; +/// Result of a word display conversion. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum WordDisplay { + Word(String), + Felt(String), + Hex(String), +} + +impl WordDisplay { + pub fn value(&self) -> &str { + match self { + WordDisplay::Word(v) => v, + WordDisplay::Felt(v) => v, + WordDisplay::Hex(v) => v, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum TypeKind { + Word, + Felt, +} + // SCHEMA TYPE REGISTRY // ================================================================================================ @@ -512,8 +536,8 @@ type WordTypeDisplayer = fn(Word) -> Result; /// corresponding storage values. #[derive(Clone, Debug, Default)] pub struct SchemaTypeRegistry { - felt: BTreeMap, - word: BTreeMap, + felt: BTreeMap, + word: BTreeMap, felt_display: BTreeMap, word_display: BTreeMap, } @@ -577,6 +601,29 @@ impl SchemaTypeRegistry { display(felt).map(|_| ()) } + // VALUE VALIDATION HELPERS + // ============================================================================================ + + /// Validates that the given [`Word`] conforms to the specified schema type. + pub fn validate_word_value( + &self, + type_name: &SchemaTypeId, + word: Word, + ) -> Result<(), SchemaTypeError> { + match self.type_kind(type_name) { + TypeKind::Word => Ok(()), + TypeKind::Felt => { + // Felt types stored as words must have the form [0, 0, 0, ] + if word[0] != Felt::ZERO || word[1] != Felt::ZERO || word[2] != Felt::ZERO { + return Err(SchemaTypeError::ConversionError(format!( + "expected a word of the form [0, 0, 0, ] for type `{type_name}`" + ))); + } + self.validate_felt_value(type_name, word[3]) + }, + } + } + /// Converts a [`Felt`] into a canonical string representation for the given schema type. /// /// This is intended for serializing schemas to TOML (e.g. default values). @@ -588,24 +635,20 @@ impl SchemaTypeRegistry { .unwrap_or_else(|| format!("0x{:x}", felt.as_int())) } - /// Converts a [`Word`] into a canonical string representation for the given schema type. - /// - /// Tries displaying as word if the converter is known, otherwise tests as a felt type, and - /// if it doesn't work, the word as hex is returned - // TODO: This should return richer information (how it was converted, if it succededs as word, - // etc.) - #[allow(dead_code)] - pub fn display_word(&self, type_name: &SchemaTypeId, word: Word) -> String { + /// Converts a [`Word`] into a canonical string representation and reports how it was produced. + pub fn display_word(&self, type_name: &SchemaTypeId, word: Word) -> WordDisplay { if let Some(display) = self.word_display.get(type_name) { - return display(word).unwrap_or_else(|_| word.to_string()); + let value = display(word).unwrap_or_else(|_| word.to_string()); + return WordDisplay::Word(value); } // Treat any registered felt type as a word type by zero-padding the remaining felts. if self.contains_felt_type(type_name) { - return self.display_felt(type_name, word[3]); + let value = self.display_felt(type_name, word[3]); + return WordDisplay::Felt(value); } - word.to_hex() + WordDisplay::Hex(word.to_hex()) } /// Attempts to parse a string into a `Word` using the registered converter for the given type @@ -642,6 +685,14 @@ impl SchemaTypeRegistry { self.felt.contains_key(type_name) } + fn type_kind(&self, type_name: &SchemaTypeId) -> TypeKind { + if self.contains_felt_type(type_name) { + TypeKind::Felt + } else { + TypeKind::Word + } + } + /// Returns `true` if a `WordType` is registered for the given type. /// /// This also returns `true` for any registered felt type (as those can be embedded into a word diff --git a/crates/miden-protocol/src/account/component/storage/value_name.rs b/crates/miden-protocol/src/account/component/storage/value_name.rs index 2d3a418576..bb27c3764a 100644 --- a/crates/miden-protocol/src/account/component/storage/value_name.rs +++ b/crates/miden-protocol/src/account/component/storage/value_name.rs @@ -31,7 +31,8 @@ impl StorageValueName { } } - /// Adds a field-name suffix to a slot-name key, separated by a period. + /// Adds a field-name suffix to a slot-name key, separated by a period, that identifies a + /// specific element (e.g., "basic_faucet::metadata.decimals") pub fn with_suffix(self, suffix: &str) -> Result { let mut key = self; @@ -49,32 +50,9 @@ impl StorageValueName { Ok(key) } - fn validate_slot_prefix(prefix: &str) -> Result { - match StorageSlotName::new(prefix) { - Ok(slot_name) => Ok(slot_name), - Err(err) => { - let invalid_char = match err { - StorageSlotNameError::UnexpectedColon - | StorageSlotNameError::TooShort - | StorageSlotNameError::TooLong => ':', - StorageSlotNameError::UnexpectedUnderscore => '_', - StorageSlotNameError::InvalidCharacter => prefix - .chars() - .find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == ':')) - .unwrap_or(':'), - }; - - Err(StorageValueNameError::InvalidCharacter { - part: prefix.to_string(), - character: invalid_char, - }) - }, - } - } - fn validate_field_segment(segment: &str) -> Result<(), StorageValueNameError> { if segment.is_empty() { - return Err(StorageValueNameError::EmptySegment); + return Err(StorageValueNameError::EmptySuffix); } if let Some(offending_char) = @@ -129,7 +107,7 @@ impl FromStr for StorageValueName { fn from_str(value: &str) -> Result { if value.is_empty() { - return Err(StorageValueNameError::EmptySegment); + return Err(StorageValueNameError::EmptySuffix); } // `StorageValueName` represents: @@ -140,7 +118,7 @@ impl FromStr for StorageValueName { Self::validate_field_segment(field)?; if slot.is_empty() || field.is_empty() { - return Err(StorageValueNameError::EmptySegment); + return Err(StorageValueNameError::EmptySuffix); } (slot, Some(field)) @@ -148,7 +126,8 @@ impl FromStr for StorageValueName { None => (value, None), }; - let slot_name = Self::validate_slot_prefix(slot)?; + let slot_name = + StorageSlotName::new(slot).map_err(StorageValueNameError::InvalidSlotName)?; let field = match field { Some(field) => { Self::validate_field_segment(field)?; @@ -213,7 +192,9 @@ impl Deserializable for StorageValueName { #[derive(Debug, Error)] pub enum StorageValueNameError { #[error("key segment is empty")] - EmptySegment, + EmptySuffix, #[error("key segment '{part}' contains invalid character '{character}'")] InvalidCharacter { part: String, character: char }, + #[error("invalid storage slot name")] + InvalidSlotName(#[source] StorageSlotNameError), } diff --git a/crates/miden-protocol/src/account/mod.rs b/crates/miden-protocol/src/account/mod.rs index 95d6eac982..7f94221a46 100644 --- a/crates/miden-protocol/src/account/mod.rs +++ b/crates/miden-protocol/src/account/mod.rs @@ -46,7 +46,7 @@ pub use delta::{ StorageSlotDelta, }; -mod storage; +pub mod storage; pub use storage::{ AccountStorage, AccountStorageHeader, diff --git a/crates/miden-protocol/src/account/storage/mod.rs b/crates/miden-protocol/src/account/storage/mod.rs index 6947fd4629..ddba04875f 100644 --- a/crates/miden-protocol/src/account/storage/mod.rs +++ b/crates/miden-protocol/src/account/storage/mod.rs @@ -33,6 +33,14 @@ static FAUCET_SYSDATA_SLOT_NAME: LazyLock = LazyLock::new(|| { .expect("storage slot name should be valid") }); +static RESERVED_SLOT_NAMES: LazyLock> = + LazyLock::new(|| vec![FAUCET_SYSDATA_SLOT_NAME.clone()]); + +/// Returns `true` if the provided slot name is reserved by the protocol. +pub fn is_reserved_slot_name(slot_name: &StorageSlotName) -> bool { + RESERVED_SLOT_NAMES.iter().any(|reserved| reserved.id() == slot_name.id()) +} + // ACCOUNT STORAGE // ================================================================================================ @@ -127,7 +135,7 @@ impl AccountStorage { let AccountComponent { storage_slots, .. } = component; storage_slots.into_iter() }) { - if component_slot.name() == Self::faucet_sysdata_slot() { + if is_reserved_slot_name(component_slot.name()) { return Err(AccountError::StorageSlotNameMustNotBeFaucetSysdata); } diff --git a/docs/src/account/components.md b/docs/src/account/components.md index aad4412298..ca88a272ac 100644 --- a/docs/src/account/components.md +++ b/docs/src/account/components.md @@ -86,20 +86,6 @@ The metadata header specifies four fields: - `version`: A semantic version of this component schema - `supported-types`: Specifies the types of accounts on which the component can be used. Valid values are `FungibleFaucet`, `NonFungibleFaucet`, `RegularAccountUpdatableCode` and `RegularAccountImmutableCode` -##### Word schema example - -```toml -[[storage.slots]] -name = "demo::faucet_id" -description = "Account ID of the registered faucet" -type = [ - { type = "felt", name = "prefix", description = "Faucet ID prefix" }, - { type = "felt", name = "suffix", description = "Faucet ID suffix" }, - { type = "void" }, - { type = "void" }, -] -``` - #### Storage entries An account component schema can contain multiple storage entries, each describing either a @@ -112,7 +98,7 @@ In TOML, these are declared using dotted array keys: **Value-slot** entries describe their schema via `WordSchema`. A value type can be either: -- **Simple**: defined through the `type = ""` field, indicating the expected `SchemaTypeId` for the entire word. The value is supplied at instantiation time via `InitStorageData`. +- **Simple**: defined through the `type = ""` field, indicating the expected `SchemaTypeId` for the entire word. The value is supplied at instantiation time via `InitStorageData`. Felt types are stored as full words in the following layout: `[0, 0, 0, ]`. - **Composite**: provided through `type = [ ... ]`, which contains exactly four `FeltSchema` descriptors. Each element is either a named typed field (optionally with `default-value`) or a `void` element for reserved/padding zeros. Composite schema entries reuse the existing TOML structure for four-element words, while simple schemas rely on `type`. In our example, the `token_metadata` slot uses a composite schema (`type = [...]`) mixing typed fields (`max_supply`, `decimals`) with defaults (`symbol`) and a reserved/padding `void` element. @@ -131,6 +117,19 @@ Simple schemas accept `word` (default) and word-shaped types such as `miden::sta Simple schemas can also use any felt type (e.g. `u8`, `u16`, `u32`, `felt`, `miden::standards::fungible_faucets::metadata::token_symbol`, `void`). The value is parsed as a felt and stored as a word with the parsed felt in the last element and the remaining elements set to `0`. +##### Word schema example + +```toml +[[storage.slots]] +name = "demo::faucet_id" +description = "Account ID of the registered faucet" +type = [ + { type = "felt", name = "prefix", description = "Faucet ID prefix" }, + { type = "felt", name = "suffix", description = "Faucet ID suffix" }, + { type = "void" }, + { type = "void" }, +] +``` ##### Felt types From e062f7522e118c19b54b5ae496a9bf4a70debd78 Mon Sep 17 00:00:00 2001 From: Odinson Date: Tue, 6 Jan 2026 15:43:52 +0530 Subject: [PATCH 086/114] feat:import constants directly instead of getter procedures (#2221) --- .../asm/kernels/transaction/lib/account.masm | 18 +-- .../transaction/lib/account_delta.masm | 4 +- .../kernels/transaction/lib/constants.masm | 148 ++---------------- .../asm/kernels/transaction/lib/epilogue.masm | 4 +- .../asm/kernels/transaction/lib/memory.masm | 14 +- .../asm/kernels/transaction/lib/note.masm | 4 +- .../kernels/transaction/lib/output_note.masm | 4 +- .../asm/kernels/transaction/lib/prologue.masm | 24 +-- crates/miden-protocol/asm/protocol/note.masm | 13 +- .../asm/shared_utils/util/note.masm | 16 +- .../src/kernel_tests/tx/test_output_note.rs | 4 +- 11 files changed, 61 insertions(+), 192 deletions(-) diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/account.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm index b2dcfa4bc7..5d16dacc7f 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm @@ -1,7 +1,10 @@ use $kernel::account_delta use $kernel::account_id use $kernel::asset_vault -use $kernel::constants +use $kernel::constants::ACCOUNT_PROCEDURE_DATA_LENGTH +use $kernel::constants::EMPTY_SMT_ROOT +use $kernel::constants::STORAGE_SLOT_TYPE_MAP +use $kernel::constants::STORAGE_SLOT_TYPE_VALUE use $kernel::memory use miden::core::collections::smt use miden::core::collections::sorted_array @@ -120,9 +123,6 @@ const ACCOUNT_TREE_DEPTH=64 # The number of field elements it takes to store one account storage slot. const ACCOUNT_STORAGE_SLOT_DATA_LENGTH=8 -# The number of field elements it takes to store one account procedure. -const ACCOUNT_PROCEDURE_DATA_LENGTH=4 - # The offset of the slot type in the storage slot. const ACCOUNT_SLOT_TYPE_OFFSET=1 @@ -538,7 +538,7 @@ pub proc set_item # => [slot_type, slot_ptr, VALUE] # assert slot_type is value - exec.constants::get_storage_slot_type_value eq + push.STORAGE_SLOT_TYPE_VALUE eq assert.err=ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT # => [slot_ptr, VALUE] @@ -632,7 +632,7 @@ pub proc set_map_item # => [slot_type, slot_ptr, KEY, NEW_VALUE] # assert slot_type is map - exec.constants::get_storage_slot_type_map eq + push.STORAGE_SLOT_TYPE_MAP eq assert.err=ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT # => [slot_ptr, KEY, NEW_VALUE] @@ -1310,7 +1310,7 @@ pub proc insert_new_storage dup exec.get_storage_slot_type # => [slot_type, slot_idx] - exec.constants::get_storage_slot_type_map eq + push.STORAGE_SLOT_TYPE_MAP eq # => [is_map_slot_type, slot_idx] if.true @@ -1354,7 +1354,7 @@ proc insert_and_validate_storage_map # overwrite the map root with the root of an empty SMT, so we can insert the entries of # the map into an empty map and then check whether the resulting root matches MAP_ROOT. - exec.constants::get_empty_smt_root + push.EMPTY_SMT_ROOT dup.8 # => [slot_ptr, EMPTY_SMT_ROOT, MAP_ROOT, slot_ptr] @@ -1674,7 +1674,7 @@ proc get_map_item_raw # => [slot_type, slot_ptr, KEY] # assert slot_type is map - exec.constants::get_storage_slot_type_map eq + push.STORAGE_SLOT_TYPE_MAP eq assert.err=ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT # => [slot_ptr, KEY] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm index 220b431df5..79ff5365ff 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm @@ -1,7 +1,7 @@ use $kernel::account use $kernel::asset use $kernel::asset_vault -use $kernel::constants +use $kernel::constants::STORAGE_SLOT_TYPE_VALUE use $kernel::link_map use $kernel::memory use miden::core::crypto::hashes::rpo256 @@ -149,7 +149,7 @@ proc update_slot_delta # => [storage_slot_type, slot_idx, RATE, RATE, PERM] # check if slot is of type value - exec.constants::get_storage_slot_type_value eq + push.STORAGE_SLOT_TYPE_VALUE eq # => [is_value_slot_type, slot_idx, RATE, RATE, PERM] if.true diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/constants.masm b/crates/miden-protocol/asm/kernels/transaction/lib/constants.masm index fb0dfb08f9..5c58398d3c 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/constants.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/constants.masm @@ -2,152 +2,36 @@ # ================================================================================================= # The number of elements in a Word -const WORD_SIZE=4 +pub const WORD_SIZE = 4 # The maximum number of input values associated with a single note. -const MAX_INPUTS_PER_NOTE=1024 +pub const MAX_INPUTS_PER_NOTE = 1024 # The maximum number of assets that can be stored in a single note. -const MAX_ASSETS_PER_NOTE=256 +pub const MAX_ASSETS_PER_NOTE = 256 # The maximum number of notes that can be consumed in a single transaction. -const MAX_INPUT_NOTES_PER_TX=1024 +pub const MAX_INPUT_NOTES_PER_TX = 1024 # The size of the memory segment allocated to each note. -const NOTE_MEM_SIZE=2048 +pub const NOTE_MEM_SIZE = 2048 # The depth of the Merkle tree used to commit to notes produced in a block. -const NOTE_TREE_DEPTH=16 +pub const NOTE_TREE_DEPTH = 16 # The maximum number of notes that can be created in a single transaction. -const MAX_OUTPUT_NOTES_PER_TX=1024 +pub const MAX_OUTPUT_NOTES_PER_TX = 1024 -# TYPES -# ================================================================================================= - -# Type of storage slot item in the account storage -const STORAGE_SLOT_TYPE_VALUE=0 -const STORAGE_SLOT_TYPE_MAP=1 -const STORAGE_SLOT_TYPE_ARRAY=2 +# The number of field elements per account procedure data entry (procedure MAST root). +pub const ACCOUNT_PROCEDURE_DATA_LENGTH = 4 -# PROCEDURES +# TYPES # ================================================================================================= -#! Returns the number of elements in a Word. -#! -#! Inputs: [] -#! Outputs: [word_size] -#! -#! Where: -#! - word_size is the number of elements in a Word. -pub proc get_word_size - push.WORD_SIZE -end - -#! Returns the max allowed number of input values per note. -#! -#! Inputs: [] -#! Outputs: [max_inputs_per_note] -#! -#! Where: -#! - max_inputs_per_note is the max inputs per note. -pub use ::$kernel::util::note::get_max_inputs_per_note - -#! Returns the max allowed number of assets per note. -#! -#! Inputs: [] -#! Outputs: [max_assets_per_note] -#! -#! Where: -#! - max_assets_per_note is the max assets per note. -pub proc get_max_assets_per_note - push.MAX_ASSETS_PER_NOTE -end - -#! Returns the maximum number of notes that can be consumed in a single transaction. -#! -#! Inputs: [] -#! Outputs: [max_num_input_notes] -#! -#! Where: -#! - max_num_input_notes is the max number of input notes. -pub proc get_max_num_input_notes - push.MAX_INPUT_NOTES_PER_TX -end - -#! Returns the size of the memory segment allocated to each note. -#! -#! Inputs: [] -#! Outputs: [note_mem_size] -#! -#! Where: -#! - note_mem_size is the size of the memory segment allocated to each note. -pub proc get_note_mem_size - push.NOTE_MEM_SIZE -end +# Root of an empty Sparse Merkle Tree +pub const EMPTY_SMT_ROOT = [15321474589252129342, 17373224439259377994, 15071539326562317628, 3312677166725950353] -#! Returns the depth of the Merkle tree used to commit to notes produced in a block. -#! -#! Inputs: [] -#! Outputs: [note_tree_depth] -#! -#! Where: -#! - note_tree_depth is the depth of the Merkle tree used to commit to notes produced in a block. -pub proc get_note_tree_depth - push.NOTE_TREE_DEPTH -end - -#! Returns the maximum number of notes that can be created in a single transaction. -#! -#! Inputs: [] -#! Outputs: [max_num_output_notes] -#! -#! Where: -#! - max_num_output_notes is the max number of notes that can be created in a single transaction. -pub proc get_max_num_output_notes - push.MAX_OUTPUT_NOTES_PER_TX -end - -#! Returns the root of an empty Sparse Merkle Tree. -#! -#! Inputs: [] -#! Outputs: [EMPTY_SMT_ROOT] -#! -#! Where: -#! - EMPTY_SMT_ROOT is the root of an empty Sparse Merkle Tree. -pub proc get_empty_smt_root - push.15321474589252129342.17373224439259377994.15071539326562317628.3312677166725950353 -end - -#! Returns the type of storage slot value in the account storage. -#! -#! Inputs: [] -#! Outputs: [type_storage_value] -#! -#! Where: -#! - type_storage_value is the type of storage slot item in the account storage. -pub proc get_storage_slot_type_value - push.STORAGE_SLOT_TYPE_VALUE -end - -#! Returns the type of storage slot map in the account storage. -#! -#! Inputs: [] -#! Outputs: [type_storage_map] -#! -#! Where: -#! - type_storage_map is the type of storage slot item in the account storage. -pub proc get_storage_slot_type_map - push.STORAGE_SLOT_TYPE_MAP -end - -#! Returns the type of storage slot array in the account storage. -#! -#! Inputs: [] -#! Outputs: [type_storage_array] -#! -#! Where: -#! - type_storage_array is the type of storage slot item in the account storage. -pub proc get_storage_slot_type_array - push.STORAGE_SLOT_TYPE_ARRAY -end +# Type of storage slot item in the account storage +pub const STORAGE_SLOT_TYPE_VALUE = 0 +pub const STORAGE_SLOT_TYPE_MAP = 1 +pub const STORAGE_SLOT_TYPE_ARRAY = 2 diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm index 196f4aeaa6..ef969001b5 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm @@ -1,7 +1,7 @@ use $kernel::account use $kernel::account_delta use $kernel::asset_vault -use $kernel::constants +use $kernel::constants::NOTE_MEM_SIZE use $kernel::memory use $kernel::note @@ -187,7 +187,7 @@ proc build_output_vault # => [note_data_ptr, output_note_end_ptr] # increment output note pointer and check if we should loop again - exec.constants::get_note_mem_size add dup.1 dup.1 neq + push.NOTE_MEM_SIZE add dup.1 dup.1 neq # => [should_loop, output_note_ptr, output_notes_end_ptr] end diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm index b1d93d415f..7d16cf6cbb 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm @@ -1,4 +1,6 @@ -use $kernel::constants +use $kernel::constants::ACCOUNT_PROCEDURE_DATA_LENGTH +use $kernel::constants::MAX_ASSETS_PER_NOTE +use $kernel::constants::NOTE_MEM_SIZE use miden::core::mem # ERRORS @@ -166,10 +168,6 @@ const NATIVE_ACCOUNT_DATA_PTR=8192 # The length of the memory interval that the account data occupies. const ACCOUNT_DATA_LENGTH=8192 -# The number of field elements it takes to store one account procedure. -# TODO: Duplicated in account.masm, can we remove that? -const ACCOUNT_PROCEDURE_DATA_LENGTH=4 - # The offsets at which the account data is stored relative to the start of the account data segment. const ACCT_ID_AND_NONCE_OFFSET=0 const ACCT_NONCE_OFFSET=3 @@ -1513,7 +1511,7 @@ end #! - idx is the index of the input note. #! - note_ptr is the memory address of the data segment for the input note with `idx`. pub proc get_input_note_ptr - exec.constants::get_note_mem_size mul add.INPUT_NOTE_DATA_SECTION_OFFSET + push.NOTE_MEM_SIZE mul add.INPUT_NOTE_DATA_SECTION_OFFSET end #! Set the note id of the input note. @@ -1834,7 +1832,7 @@ end #! - i is the index of the output note. #! - ptr is the memory address of the data segment for output note i. pub proc get_output_note_ptr - exec.constants::get_note_mem_size mul add.OUTPUT_NOTE_SECTION_OFFSET + push.NOTE_MEM_SIZE mul add.OUTPUT_NOTE_SECTION_OFFSET end #! Returns the output note recipient. @@ -1922,7 +1920,7 @@ pub proc set_output_note_num_assets # => [note_ptr + offset, num_assets] # check note number of assets limit - dup.1 exec.constants::get_max_assets_per_note lt assert.err=ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT + dup.1 push.MAX_ASSETS_PER_NOTE lt assert.err=ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT mem_store end diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm index a1917db667..5a3faf962d 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm @@ -1,6 +1,6 @@ use miden::core::crypto::hashes::rpo256 -use $kernel::constants +use $kernel::constants::NOTE_MEM_SIZE use $kernel::memory # ERRORS @@ -31,7 +31,7 @@ pub proc increment_active_input_note_ptr # => [orig_input_note_ptr] # increment the pointer - exec.constants::get_note_mem_size add + push.NOTE_MEM_SIZE add # => [active_input_note_ptr] # set the active input note pointer to the incremented value diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index 44a0076621..1da9d7eab2 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -2,7 +2,7 @@ use $kernel::account use $kernel::memory use $kernel::note use $kernel::asset -use $kernel::constants +use $kernel::constants::MAX_OUTPUT_NOTES_PER_TX use miden::core::word # CONSTANTS @@ -372,7 +372,7 @@ proc increment_num_output_notes # => [note_idx] # assert that there is space for a new note - dup exec.constants::get_max_num_output_notes lt + dup push.MAX_OUTPUT_NOTES_PER_TX lt assert.err=ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT # => [note_idx] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm index 9dc1a86926..615240d824 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm @@ -7,7 +7,13 @@ use $kernel::account use $kernel::account_delta use $kernel::account_id use $kernel::asset_vault -use $kernel::constants +use $kernel::constants::EMPTY_SMT_ROOT +use $kernel::constants::MAX_ASSETS_PER_NOTE +use $kernel::constants::MAX_INPUT_NOTES_PER_TX +use $kernel::constants::MAX_INPUTS_PER_NOTE +use $kernel::constants::NOTE_TREE_DEPTH +use $kernel::constants::STORAGE_SLOT_TYPE_MAP +use $kernel::constants::STORAGE_SLOT_TYPE_VALUE use $kernel::memory # CONSTS @@ -307,7 +313,7 @@ proc validate_new_account # => [ACCT_VAULT_ROOT] # push empty vault root onto stack - exec.constants::get_empty_smt_root + push.EMPTY_SMT_ROOT # => [EMPTY_VAULT_ROOT, ACCT_VAULT_ROOT] assert_eqw.err=ERR_PROLOGUE_NEW_ACCOUNT_VAULT_MUST_BE_EMPTY @@ -336,18 +342,18 @@ proc validate_new_account # => [slot_type] # assert the fungible faucet reserved slot type == value - exec.constants::get_storage_slot_type_value eq + push.STORAGE_SLOT_TYPE_VALUE eq assert.err=ERR_PROLOGUE_NEW_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE # => [] else # assert the non-fungible faucet reserved slot is initialized correctly (root of # empty SMT) - exec.constants::get_empty_smt_root + push.EMPTY_SMT_ROOT assert_eqw.err=ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_MUST_BE_VALID_EMPTY_SMT # => [slot_type] # assert the non-fungible faucet reserved slot type == map - exec.constants::get_storage_slot_type_map eq + push.STORAGE_SLOT_TYPE_MAP eq assert.err=ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_RESERVED_SLOT_INVALID_TYPE # => [] end @@ -560,7 +566,7 @@ proc authenticate_note # => [NOTE_COMMITMENT, note_index, NOTE_ROOT] # get the depth of the note tree - exec.constants::get_note_tree_depth movdn.4 + push.NOTE_TREE_DEPTH movdn.4 # => [NOTE_COMMITMENT, depth, note_index, NOTE_ROOT] # verify the note commitment @@ -655,7 +661,7 @@ proc process_note_inputs_length # => [inputs_len, note_ptr] # validate the input length - dup exec.::$kernel::util::note::get_max_inputs_per_note lte + dup push.MAX_INPUTS_PER_NOTE lte assert.err=ERR_PROLOGUE_NUMBER_OF_NOTE_INPUTS_EXCEEDED_LIMIT # => [inputs_len, note_ptr] @@ -684,7 +690,7 @@ proc process_note_assets adv_push.1 # => [assets_count, note_ptr] - dup exec.constants::get_max_assets_per_note lte + dup push.MAX_ASSETS_PER_NOTE lte assert.err=ERR_PROLOGUE_NUMBER_OF_NOTE_ASSETS_EXCEEDS_LIMIT # => [assets_count, note_ptr] @@ -978,7 +984,7 @@ proc process_input_notes_data # assert the number of input notes is within limits; since max number of input notes is # expected to be smaller than 2^32, we can use a more efficient u32 comparison dup - exec.constants::get_max_num_input_notes + push.MAX_INPUT_NOTES_PER_TX u32assert2.err=ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT u32lte assert.err=ERR_PROLOGUE_NUMBER_OF_INPUT_NOTES_EXCEEDS_LIMIT # => [num_notes] diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index ea7ac724f4..284c68d4ea 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -33,8 +33,8 @@ const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceede #! #! Invocation: exec pub proc compute_inputs_commitment - # check that number of inputs is less than or equal to 1024 - dup.1 push.1024 u32assert2.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT + # check that number of inputs is less than or equal to MAX_INPUTS_PER_NOTE + dup.1 push.MAX_INPUTS_PER_NOTE u32assert2.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT u32lte assert.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT # => [inputs_ptr, num_inputs] @@ -47,13 +47,8 @@ pub proc compute_inputs_commitment # => [INPUTS_COMMITMENT] end -#! Returns the max allowed number of input values per note. -#! -#! Stack: [] -#! Output: [max_inputs_per_note] -#! -#! - max_inputs_per_note is the max inputs per note. -pub use ::miden::protocol::util::note::get_max_inputs_per_note +# Re-export the max inputs per note constant. +pub use ::miden::protocol::util::note::MAX_INPUTS_PER_NOTE #! Writes the assets data stored in the advice map to the memory specified by the provided #! destination pointer. diff --git a/crates/miden-protocol/asm/shared_utils/util/note.masm b/crates/miden-protocol/asm/shared_utils/util/note.masm index 00a4ea88cf..8c502a8f71 100644 --- a/crates/miden-protocol/asm/shared_utils/util/note.masm +++ b/crates/miden-protocol/asm/shared_utils/util/note.masm @@ -2,18 +2,4 @@ # ================================================================================================= # The maximum number of input values associated with a single note. -const MAX_INPUTS_PER_NOTE=1024 - -# PROCEDURES -# ================================================================================================= - -#! Returns the max allowed number of input values per note. -#! -#! Inputs: [] -#! Outputs: [max_inputs_per_note] -#! -#! Where: -#! - max_inputs_per_note is the max inputs per note. -pub proc get_max_inputs_per_note - push.MAX_INPUTS_PER_NOTE -end +pub const MAX_INPUTS_PER_NOTE = 1024 diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index a7c48b76b5..9797291131 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -175,12 +175,12 @@ async fn test_create_note_too_many_notes() -> anyhow::Result<()> { let code = format!( " use miden::protocol::output_note - use $kernel::constants + use $kernel::constants::MAX_OUTPUT_NOTES_PER_TX use $kernel::memory use $kernel::prologue begin - exec.constants::get_max_num_output_notes + push.MAX_OUTPUT_NOTES_PER_TX exec.memory::set_num_output_notes exec.prologue::prepare_transaction From 30cc8c3d4898c83dbd6ed513e6ab3c0ff681585c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= <4142+huitseeker@users.noreply.github.com> Date: Tue, 6 Jan 2026 23:27:06 +0100 Subject: [PATCH 087/114] fix: ensure cargo-msrv is properly installed for MSRV check (#2234) Two issues caused the MSRV check to fail: 1. ~/.cargo/bin wasn't in PATH when check-msrv.sh ran 2. Cached cargo-msrv from rust-cache was stale/broken Fix: add PATH export and force-reinstall cargo-msrv. --- .github/workflows/msrv.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index aa9b9d6f79..9f2f6afa02 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -30,7 +30,8 @@ jobs: with: tool: cargo-binstall - name: Install cargo-msrv - run: cargo binstall --no-confirm cargo-msrv + run: cargo binstall --no-confirm --force cargo-msrv - name: Check MSRV for each workspace member run: | + export PATH="$HOME/.cargo/bin:$PATH" ./scripts/check-msrv.sh From 24f61141ad3a54e7a49cd0f3ce53ea38da31a989 Mon Sep 17 00:00:00 2001 From: Farukest Date: Wed, 7 Jan 2026 02:25:39 +0300 Subject: [PATCH 088/114] feat: add AccountId::parse() for hex and bech32 formats (#2223) --- CHANGELOG.md | 1 + .../src/account/account_id/mod.rs | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f44c133564..f7626770e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2160](https://github.com/0xMiden/miden-base/pull/2160), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). - [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). - Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). +- Add `AccountId::parse()` helper function to parse both hex and bech32 formats ([#2223](https://github.com/0xMiden/miden-base/pull/2223)). ### Changes diff --git a/crates/miden-protocol/src/account/account_id/mod.rs b/crates/miden-protocol/src/account/account_id/mod.rs index 2c94bb9850..334dc25a54 100644 --- a/crates/miden-protocol/src/account/account_id/mod.rs +++ b/crates/miden-protocol/src/account/account_id/mod.rs @@ -317,6 +317,29 @@ impl AccountId { .map(|(network_id, account_id)| (network_id, AccountId::V0(account_id))) } + /// Parses a string into an [`AccountId`]. + /// + /// This function supports parsing from both hex (`0x...`) and bech32 formats. + /// + /// # Returns + /// + /// Returns a tuple of the parsed [`AccountId`] and an optional [`NetworkId`]. + /// - For hex strings: `NetworkId` is `None`. + /// - For bech32 strings: `NetworkId` is `Some(...)`. + /// + /// # Errors + /// + /// Returns an error if the string cannot be parsed as either hex or bech32 format. + pub fn parse(s: &str) -> Result<(Self, Option), AccountIdError> { + if let Some(hex_digits) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) { + // Normalize to lowercase "0x" prefix for from_hex + let normalized = format!("0x{hex_digits}"); + Self::from_hex(&normalized).map(|id| (id, None)) + } else { + Self::from_bech32(s).map(|(network_id, id)| (id, Some(network_id))) + } + } + /// Decodes the data from the bech32 byte iterator into an [`AccountId`]. pub(crate) fn from_bech32_byte_iter(byte_iter: ByteIter<'_>) -> Result { AccountIdV0::from_bech32_byte_iter(byte_iter).map(AccountId::V0) @@ -651,4 +674,61 @@ mod tests { AccountIdError::Bech32DecodeError(Bech32Error::InvalidDataLength { .. }) ); } + + #[test] + fn parse_hex_string() { + let account_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap(); + let hex_string = account_id.to_hex(); + + let (parsed_id, network_id) = AccountId::parse(&hex_string).unwrap(); + + assert_eq!(parsed_id, account_id); + assert!(network_id.is_none()); + } + + #[test] + fn parse_hex_string_uppercase() { + let account_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap(); + // Keep "0x" prefix lowercase, only uppercase the hex digits + let hex_string = account_id.to_hex(); + let hex_string = format!("0x{}", hex_string[2..].to_uppercase()); + + let (parsed_id, network_id) = AccountId::parse(&hex_string).unwrap(); + + assert_eq!(parsed_id, account_id); + assert!(network_id.is_none()); + } + + #[test] + fn parse_hex_string_uppercase_prefix() { + let account_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap(); + // Use "0X" prefix (uppercase X) with uppercase hex digits + let hex_string = account_id.to_hex(); + let hex_string = format!("0X{}", hex_string[2..].to_uppercase()); + + let (parsed_id, network_id) = AccountId::parse(&hex_string).unwrap(); + + assert_eq!(parsed_id, account_id); + assert!(network_id.is_none()); + } + + #[test] + fn parse_bech32_string() { + let account_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap(); + let bech32_string = account_id.to_bech32(NetworkId::Mainnet); + + let (parsed_id, parsed_network_id) = AccountId::parse(&bech32_string).unwrap(); + + assert_eq!(parsed_id, account_id); + assert_eq!(parsed_network_id, Some(NetworkId::Mainnet)); + } + + #[test] + fn parse_invalid_string() { + let error = AccountId::parse("invalid_string").unwrap_err(); + assert_matches!(error, AccountIdError::Bech32DecodeError(_)); + + let error = AccountId::parse("0xinvalid").unwrap_err(); + assert_matches!(error, AccountIdError::AccountIdHexParseError(_)); + } } From 2d814868d910475acb84890078dd200c845b412c Mon Sep 17 00:00:00 2001 From: Marti Date: Wed, 7 Jan 2026 12:29:53 +0100 Subject: [PATCH 089/114] chore: organize `miden-standards` (#2227) * chore: rename auth to auth_scheme * chore: split up asm/account_components * chore: only use recursive masm finder * chore: rename to remove "_recursive" * chore: align build.rs files * chore: adjust comment --- crates/miden-protocol/build.rs | 14 ++-- .../{ => auth}/ecdsa_k256_keccak.masm | 0 .../{ => auth}/ecdsa_k256_keccak_acl.masm | 0 .../ecdsa_k256_keccak_multisig.masm | 0 .../{ => auth}/no_auth.masm | 0 .../{ => auth}/rpo_falcon_512.masm | 0 .../{ => auth}/rpo_falcon_512_acl.masm | 0 .../{ => auth}/rpo_falcon_512_multisig.masm | 0 .../{ => faucets}/basic_fungible_faucet.masm | 0 .../network_fungible_faucet.masm | 0 .../{ => wallets}/basic_wallet.masm | 0 crates/miden-standards/build.rs | 29 +++++--- .../src/account/components/mod.rs | 68 +++++++++++-------- crates/miden-standards/src/account/mod.rs | 2 +- .../src/{auth.rs => auth_scheme.rs} | 0 crates/miden-standards/src/lib.rs | 4 +- 16 files changed, 69 insertions(+), 48 deletions(-) rename crates/miden-standards/asm/account_components/{ => auth}/ecdsa_k256_keccak.masm (100%) rename crates/miden-standards/asm/account_components/{ => auth}/ecdsa_k256_keccak_acl.masm (100%) rename crates/miden-standards/asm/account_components/{ => auth}/ecdsa_k256_keccak_multisig.masm (100%) rename crates/miden-standards/asm/account_components/{ => auth}/no_auth.masm (100%) rename crates/miden-standards/asm/account_components/{ => auth}/rpo_falcon_512.masm (100%) rename crates/miden-standards/asm/account_components/{ => auth}/rpo_falcon_512_acl.masm (100%) rename crates/miden-standards/asm/account_components/{ => auth}/rpo_falcon_512_multisig.masm (100%) rename crates/miden-standards/asm/account_components/{ => faucets}/basic_fungible_faucet.masm (100%) rename crates/miden-standards/asm/account_components/{ => faucets}/network_fungible_faucet.masm (100%) rename crates/miden-standards/asm/account_components/{ => wallets}/basic_wallet.masm (100%) rename crates/miden-standards/src/{auth.rs => auth_scheme.rs} (100%) diff --git a/crates/miden-protocol/build.rs b/crates/miden-protocol/build.rs index 27d1b96d4b..a15237e295 100644 --- a/crates/miden-protocol/build.rs +++ b/crates/miden-protocol/build.rs @@ -551,7 +551,7 @@ pub(crate) static EVENT_NAME_LUT: ::miden_utils_sync::LazyLock>(dir_path: P) -> Result> { @@ -620,12 +621,9 @@ mod shared { let path = dir_path.as_ref(); if path.is_dir() { - let entries = fs::read_dir(path) - .into_diagnostic() - .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; - for entry in entries { - let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; - let file_path = file.path(); + for entry in WalkDir::new(path) { + let entry = entry.into_diagnostic()?; + let file_path = entry.path().to_path_buf(); if is_masm_file(&file_path).into_diagnostic()? { files.push(file_path); } diff --git a/crates/miden-standards/asm/account_components/ecdsa_k256_keccak.masm b/crates/miden-standards/asm/account_components/auth/ecdsa_k256_keccak.masm similarity index 100% rename from crates/miden-standards/asm/account_components/ecdsa_k256_keccak.masm rename to crates/miden-standards/asm/account_components/auth/ecdsa_k256_keccak.masm diff --git a/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_acl.masm b/crates/miden-standards/asm/account_components/auth/ecdsa_k256_keccak_acl.masm similarity index 100% rename from crates/miden-standards/asm/account_components/ecdsa_k256_keccak_acl.masm rename to crates/miden-standards/asm/account_components/auth/ecdsa_k256_keccak_acl.masm diff --git a/crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm b/crates/miden-standards/asm/account_components/auth/ecdsa_k256_keccak_multisig.masm similarity index 100% rename from crates/miden-standards/asm/account_components/ecdsa_k256_keccak_multisig.masm rename to crates/miden-standards/asm/account_components/auth/ecdsa_k256_keccak_multisig.masm diff --git a/crates/miden-standards/asm/account_components/no_auth.masm b/crates/miden-standards/asm/account_components/auth/no_auth.masm similarity index 100% rename from crates/miden-standards/asm/account_components/no_auth.masm rename to crates/miden-standards/asm/account_components/auth/no_auth.masm diff --git a/crates/miden-standards/asm/account_components/rpo_falcon_512.masm b/crates/miden-standards/asm/account_components/auth/rpo_falcon_512.masm similarity index 100% rename from crates/miden-standards/asm/account_components/rpo_falcon_512.masm rename to crates/miden-standards/asm/account_components/auth/rpo_falcon_512.masm diff --git a/crates/miden-standards/asm/account_components/rpo_falcon_512_acl.masm b/crates/miden-standards/asm/account_components/auth/rpo_falcon_512_acl.masm similarity index 100% rename from crates/miden-standards/asm/account_components/rpo_falcon_512_acl.masm rename to crates/miden-standards/asm/account_components/auth/rpo_falcon_512_acl.masm diff --git a/crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm b/crates/miden-standards/asm/account_components/auth/rpo_falcon_512_multisig.masm similarity index 100% rename from crates/miden-standards/asm/account_components/rpo_falcon_512_multisig.masm rename to crates/miden-standards/asm/account_components/auth/rpo_falcon_512_multisig.masm diff --git a/crates/miden-standards/asm/account_components/basic_fungible_faucet.masm b/crates/miden-standards/asm/account_components/faucets/basic_fungible_faucet.masm similarity index 100% rename from crates/miden-standards/asm/account_components/basic_fungible_faucet.masm rename to crates/miden-standards/asm/account_components/faucets/basic_fungible_faucet.masm diff --git a/crates/miden-standards/asm/account_components/network_fungible_faucet.masm b/crates/miden-standards/asm/account_components/faucets/network_fungible_faucet.masm similarity index 100% rename from crates/miden-standards/asm/account_components/network_fungible_faucet.masm rename to crates/miden-standards/asm/account_components/faucets/network_fungible_faucet.masm diff --git a/crates/miden-standards/asm/account_components/basic_wallet.masm b/crates/miden-standards/asm/account_components/wallets/basic_wallet.masm similarity index 100% rename from crates/miden-standards/asm/account_components/basic_wallet.masm rename to crates/miden-standards/asm/account_components/wallets/basic_wallet.masm diff --git a/crates/miden-standards/build.rs b/crates/miden-standards/build.rs index 7f05079ddd..f819b74ae2 100644 --- a/crates/miden-standards/build.rs +++ b/crates/miden-standards/build.rs @@ -135,7 +135,7 @@ fn compile_note_scripts(source_dir: &Path, target_dir: &Path, assembler: Assembl // ================================================================================================ /// Compiles the account components in `source_dir` into MASL libraries and stores the compiled -/// files in `target_dir`. +/// files in `target_dir`, preserving the subdirectory structure. fn compile_account_components( source_dir: &Path, target_dir: &Path, @@ -153,7 +153,7 @@ fn compile_account_components( .expect("file stem should be valid UTF-8") .to_owned(); - let component_source_code = fs::read_to_string(masm_file_path) + let component_source_code = fs::read_to_string(&masm_file_path) .expect("reading the component's MASM source code should succeed"); let named_source = NamedSource::new(component_name.clone(), component_source_code); @@ -163,8 +163,19 @@ fn compile_account_components( .assemble_library([named_source]) .expect("library assembly should succeed"); + // Preserve the subdirectory structure: compute relative path from source_dir + let relative_dir = masm_file_path + .parent() + .and_then(|p| p.strip_prefix(source_dir).ok()) + .unwrap_or(Path::new("")); + + let output_dir = target_dir.join(relative_dir); + if !output_dir.exists() { + fs::create_dir_all(&output_dir).unwrap(); + } + let component_file_path = - target_dir.join(component_name).with_extension(Library::LIBRARY_EXTENSION); + output_dir.join(component_name).with_extension(Library::LIBRARY_EXTENSION); component_library.write_to_file(component_file_path).into_diagnostic()?; } @@ -282,7 +293,8 @@ mod shared { Ok(()) } - /// Returns a vector with paths to all MASM files in the specified directory. + /// Returns a vector with paths to all MASM files in the specified directory and its + /// subdirectories. /// /// All non-MASM files are skipped. pub fn get_masm_files>(dir_path: P) -> Result> { @@ -290,12 +302,9 @@ mod shared { let path = dir_path.as_ref(); if path.is_dir() { - let entries = fs::read_dir(path) - .into_diagnostic() - .wrap_err_with(|| format!("failed to read directory {}", path.display()))?; - for entry in entries { - let file = entry.into_diagnostic().wrap_err("failed to read directory entry")?; - let file_path = file.path(); + for entry in WalkDir::new(path) { + let entry = entry.into_diagnostic()?; + let file_path = entry.path().to_path_buf(); if is_masm_file(&file_path).into_diagnostic()? { files.push(file_path); } diff --git a/crates/miden-standards/src/account/components/mod.rs b/crates/miden-standards/src/account/components/mod.rs index e97dc8b4df..de1edcb37d 100644 --- a/crates/miden-standards/src/account/components/mod.rs +++ b/crates/miden-standards/src/account/components/mod.rs @@ -10,18 +10,26 @@ use miden_protocol::utils::sync::LazyLock; use crate::account::interface::AccountComponentInterface; +// WALLET LIBRARIES +// ================================================================================================ + // Initialize the Basic Wallet library only once. static BASIC_WALLET_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = - include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/basic_wallet.masl")); + let bytes = include_bytes!(concat!( + env!("OUT_DIR"), + "/assets/account_components/wallets/basic_wallet.masl" + )); Library::read_from_bytes(bytes).expect("Shipped Basic Wallet library is well-formed") }); +// AUTH LIBRARIES +// ================================================================================================ + /// Initialize the ECDSA K256 Keccak library only once. static ECDSA_K256_KECCAK_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/ecdsa_k256_keccak.masl" + "/assets/account_components/auth/ecdsa_k256_keccak.masl" )); Library::read_from_bytes(bytes).expect("Shipped Ecdsa K256 Keccak library is well-formed") }); @@ -30,7 +38,7 @@ static ECDSA_K256_KECCAK_LIBRARY: LazyLock = LazyLock::new(|| { static ECDSA_K256_KECCAK_ACL_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/ecdsa_k256_keccak_acl.masl" + "/assets/account_components/auth/ecdsa_k256_keccak_acl.masl" )); Library::read_from_bytes(bytes).expect("Shipped Ecdsa K256 Keccak ACL library is well-formed") }); @@ -39,7 +47,7 @@ static ECDSA_K256_KECCAK_ACL_LIBRARY: LazyLock = LazyLock::new(|| { static ECDSA_K256_KECCAK_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/ecdsa_k256_keccak_multisig.masl" + "/assets/account_components/auth/ecdsa_k256_keccak_multisig.masl" )); Library::read_from_bytes(bytes) .expect("Shipped Multisig Ecdsa K256 Keccak library is well-formed") @@ -47,51 +55,57 @@ static ECDSA_K256_KECCAK_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| // Initialize the Rpo Falcon 512 library only once. static RPO_FALCON_512_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = - include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/rpo_falcon_512.masl")); - Library::read_from_bytes(bytes).expect("Shipped Rpo Falcon 512 library is well-formed") -}); - -// Initialize the Basic Fungible Faucet library only once. -static BASIC_FUNGIBLE_FAUCET_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/basic_fungible_faucet.masl" + "/assets/account_components/auth/rpo_falcon_512.masl" )); - Library::read_from_bytes(bytes).expect("Shipped Basic Fungible Faucet library is well-formed") + Library::read_from_bytes(bytes).expect("Shipped Rpo Falcon 512 library is well-formed") }); -// Initialize the Network Fungible Faucet library only once. -static NETWORK_FUNGIBLE_FAUCET_LIBRARY: LazyLock = LazyLock::new(|| { +// Initialize the Rpo Falcon 512 ACL library only once. +static RPO_FALCON_512_ACL_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/network_fungible_faucet.masl" + "/assets/account_components/auth/rpo_falcon_512_acl.masl" )); - Library::read_from_bytes(bytes).expect("Shipped Network Fungible Faucet library is well-formed") + Library::read_from_bytes(bytes).expect("Shipped Rpo Falcon 512 ACL library is well-formed") }); -// Initialize the Rpo Falcon 512 ACL library only once. -static RPO_FALCON_512_ACL_LIBRARY: LazyLock = LazyLock::new(|| { +// Initialize the Multisig Rpo Falcon 512 library only once. +static RPO_FALCON_512_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/rpo_falcon_512_acl.masl" + "/assets/account_components/auth/rpo_falcon_512_multisig.masl" )); - Library::read_from_bytes(bytes).expect("Shipped Rpo Falcon 512 ACL library is well-formed") + Library::read_from_bytes(bytes).expect("Shipped Multisig Rpo Falcon 512 library is well-formed") }); // Initialize the NoAuth library only once. static NO_AUTH_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/no_auth.masl")); + let bytes = + include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/auth/no_auth.masl")); Library::read_from_bytes(bytes).expect("Shipped NoAuth library is well-formed") }); -// Initialize the Multisig Rpo Falcon 512 library only once. -static RPO_FALCON_512_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { +// FAUCET LIBRARIES +// ================================================================================================ + +// Initialize the Basic Fungible Faucet library only once. +static BASIC_FUNGIBLE_FAUCET_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/rpo_falcon_512_multisig.masl" + "/assets/account_components/faucets/basic_fungible_faucet.masl" )); - Library::read_from_bytes(bytes).expect("Shipped Multisig Rpo Falcon 512 library is well-formed") + Library::read_from_bytes(bytes).expect("Shipped Basic Fungible Faucet library is well-formed") +}); + +// Initialize the Network Fungible Faucet library only once. +static NETWORK_FUNGIBLE_FAUCET_LIBRARY: LazyLock = LazyLock::new(|| { + let bytes = include_bytes!(concat!( + env!("OUT_DIR"), + "/assets/account_components/faucets/network_fungible_faucet.masl" + )); + Library::read_from_bytes(bytes).expect("Shipped Network Fungible Faucet library is well-formed") }); /// Returns the Basic Wallet Library. diff --git a/crates/miden-standards/src/account/mod.rs b/crates/miden-standards/src/account/mod.rs index 60683d1392..eea3a92141 100644 --- a/crates/miden-standards/src/account/mod.rs +++ b/crates/miden-standards/src/account/mod.rs @@ -1,4 +1,4 @@ -use super::auth::AuthScheme; +use super::auth_scheme::AuthScheme; pub mod auth; pub mod components; diff --git a/crates/miden-standards/src/auth.rs b/crates/miden-standards/src/auth_scheme.rs similarity index 100% rename from crates/miden-standards/src/auth.rs rename to crates/miden-standards/src/auth_scheme.rs diff --git a/crates/miden-standards/src/lib.rs b/crates/miden-standards/src/lib.rs index 8818e57389..fdcec02916 100644 --- a/crates/miden-standards/src/lib.rs +++ b/crates/miden-standards/src/lib.rs @@ -6,8 +6,8 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; -mod auth; -pub use auth::AuthScheme; +mod auth_scheme; +pub use auth_scheme::AuthScheme; pub mod account; pub mod code_builder; From 6b69e816a8fbb83239309ddc7656ebfd3591f7a9 Mon Sep 17 00:00:00 2001 From: Desant pivo Date: Thu, 8 Jan 2026 09:13:25 +0100 Subject: [PATCH 090/114] refactor: close gap in account memory layout (#2190) * Update memory.rs * Update memory.masm * Update crates/miden-lib/src/transaction/memory.rs Co-authored-by: Philipp Gackstatter * Update crates/miden-lib/src/transaction/memory.rs Co-authored-by: Philipp Gackstatter * Update memory.rs * Update test_fpi.rs * Update memory.masm * Update kernel_procedures.rs * fix: make format * Update memory.masm * Update account.masm * Update memory.rs * Update account.masm --------- Co-authored-by: Philipp Gackstatter --- .../asm/kernels/transaction/lib/account.masm | 24 ++--- .../asm/kernels/transaction/lib/memory.masm | 42 ++++---- .../src/transaction/kernel/memory.rs | 101 +++++++++--------- .../src/transaction/kernel/procedures.rs | 40 +++---- .../src/kernel_tests/tx/test_fpi.rs | 14 +-- 5 files changed, 109 insertions(+), 112 deletions(-) diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/account.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm index 5d16dacc7f..68bf69d047 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm @@ -444,7 +444,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. pub proc get_item # get account storage slots section offset - exec.memory::get_account_storage_slots_section_ptr + exec.memory::get_account_active_storage_slots_section_ptr # => [acct_storage_slots_section_offset, slot_id_prefix, slot_id_suffix] exec.find_storage_slot @@ -470,7 +470,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. pub proc get_typed_item # get account storage slots section offset - exec.memory::get_account_storage_slots_section_ptr + exec.memory::get_account_active_storage_slots_section_ptr # => [acct_storage_slots_section_offset, slot_id_prefix, slot_id_suffix] exec.find_storage_slot @@ -527,7 +527,7 @@ pub proc set_item emit.ACCOUNT_STORAGE_BEFORE_SET_ITEM_EVENT # => [slot_id_prefix, slot_id_suffix, VALUE] - exec.memory::get_account_storage_slots_section_ptr + exec.memory::get_account_active_storage_slots_section_ptr # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, VALUE] exec.find_storage_slot @@ -575,7 +575,7 @@ end #! - a slot with the provided slot ID does not exist in account storage. #! - the requested storage slot type is not map. pub proc get_map_item - exec.memory::get_account_storage_slots_section_ptr + exec.memory::get_account_active_storage_slots_section_ptr # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY] exec.get_map_item_raw @@ -620,7 +620,7 @@ end #! - the storage slot type is not map. #! - no map with the root of the slot is found. pub proc set_map_item - exec.memory::get_account_storage_slots_section_ptr + exec.memory::get_account_active_storage_slots_section_ptr # => [storage_slots_ptr, slot_id_prefix, slot_id_suffix, KEY, NEW_VALUE] # resolve the slot name to its pointer @@ -656,7 +656,7 @@ pub proc get_storage_slot_type # => [offset] # compute storage slot ptr - exec.memory::get_native_account_storage_slots_ptr + exec.memory::get_native_account_active_storage_slots_ptr add # => [slot_ptr] @@ -1185,7 +1185,7 @@ pub proc save_account_storage_data # AS => [[STORAGE_SLOT_DATA]] # setup acct_storage_slots_ptr and end_ptr for reading from advice stack - mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH exec.memory::get_account_storage_slots_section_ptr + mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH exec.memory::get_account_active_storage_slots_section_ptr dup movdn.2 add swap # OS => [acct_storage_slots_ptr, end_ptr, STORAGE_COMMITMENT] # AS => [[STORAGE_SLOT_DATA]] @@ -1345,7 +1345,7 @@ end #! Outputs: [] proc insert_and_validate_storage_map mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH - exec.memory::get_account_storage_slots_section_ptr + exec.memory::get_account_active_storage_slots_section_ptr add # => [slot_ptr] @@ -1471,7 +1471,7 @@ pub proc get_item_delta # => [offset] # get account storage slots section offset - exec.memory::get_native_account_storage_slots_ptr + exec.memory::get_native_account_active_storage_slots_ptr # => [storage_slots_ptr, offset] dup.1 add dup @@ -1515,7 +1515,7 @@ pub proc get_slot_id mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH # => [offset] - exec.memory::get_account_storage_slots_section_ptr + exec.memory::get_account_active_storage_slots_section_ptr add # => [slot_ptr] @@ -1741,7 +1741,7 @@ end #! - slot_ptr is the pointer to a "current" storage slot in the native account's memory section. #! - slot_idx is the index of the storage slot. proc slot_ptr_to_index - exec.memory::get_account_storage_slots_section_ptr + exec.memory::get_account_active_storage_slots_section_ptr sub # => [slot_offset] @@ -1915,7 +1915,7 @@ proc refresh_storage_commitment # => [num_storage_slots] # setup start and end ptr - mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH exec.memory::get_account_storage_slots_section_ptr + mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH exec.memory::get_account_active_storage_slots_section_ptr dup movdn.2 add swap # => [start_ptr, end_ptr] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm index 7d16cf6cbb..5d0ce1d1b0 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm @@ -175,17 +175,17 @@ const ACCT_VAULT_ROOT_OFFSET=4 const ACCT_STORAGE_COMMITMENT_OFFSET=8 const ACCT_CODE_COMMITMENT_OFFSET=12 const ACCT_CORE_DATA_SECTION_END_OFFSET=16 -const NUM_ACCT_PROCEDURES_OFFSET=28 +const ACCT_NUM_PROCEDURES_OFFSET=28 const ACCT_PROCEDURES_SECTION_OFFSET=32 -const ACCT_PROCEDURES_CALL_TRACKING_OFFSET=2084 -const NUM_ACCT_STORAGE_SLOTS_OFFSET=2340 -const ACCT_STORAGE_SLOTS_SECTION_OFFSET=2344 +const ACCT_PROCEDURES_CALL_TRACKING_OFFSET=1060 +const ACCT_NUM_STORAGE_SLOTS_OFFSET=1316 +# Offset at which a copy of the initial account's storage slot section is kept. This section is +# only present for the native account. +const ACCT_INITIAL_STORAGE_SLOTS_SECTION_OFFSET=1320 -# Offset at which an exact copy of the account's storage slot section starting at -# ACCT_STORAGE_SLOTS_SECTION_OFFSET is kept. This keeps the initial values of the account storage -# slots accessible to enable computing a diff between the initial and current account storage slots -# for use in the account delta. This section is only present for the native account. -const ACCT_INITIAL_STORAGE_SLOTS_SECTION_OFFSET=4384 +# Offset at which the account's active storage slot section is kept. This section contains the +# current values of the account storage slots. +const ACCT_ACTIVE_STORAGE_SLOTS_SECTION_OFFSET=2340 # NATIVE ACCOUNT DELTA # ------------------------------------------------------------------------------------------------- @@ -1220,7 +1220,7 @@ end #! Where: #! - num_procedures is the number of procedures contained in the account code. pub proc get_num_account_procedures - exec.get_active_account_data_ptr add.NUM_ACCT_PROCEDURES_OFFSET + exec.get_active_account_data_ptr add.ACCT_NUM_PROCEDURES_OFFSET mem_load end @@ -1232,7 +1232,7 @@ end #! Where: #! - num_procedures is the number of procedures contained in the account code. pub proc set_num_account_procedures - exec.get_active_account_data_ptr add.NUM_ACCT_PROCEDURES_OFFSET + exec.get_active_account_data_ptr add.ACCT_NUM_PROCEDURES_OFFSET mem_store end @@ -1344,7 +1344,7 @@ end #! Where: #! - num_storage_slots is the number of storage slots contained in the account storage. pub proc get_num_storage_slots - exec.get_active_account_data_ptr add.NUM_ACCT_STORAGE_SLOTS_OFFSET + exec.get_active_account_data_ptr add.ACCT_NUM_STORAGE_SLOTS_OFFSET mem_load end @@ -1356,7 +1356,7 @@ end #! Where: #! - num_storage_slots is the number of storage slots contained in the account storage. pub proc set_num_storage_slots - exec.get_active_account_data_ptr add.NUM_ACCT_STORAGE_SLOTS_OFFSET + exec.get_active_account_data_ptr add.ACCT_NUM_STORAGE_SLOTS_OFFSET mem_store end @@ -1367,8 +1367,8 @@ end #! #! Where: #! - storage_slots_section_ptr is the memory pointer to the account storage slots section. -pub proc get_account_storage_slots_section_ptr - exec.get_active_account_data_ptr add.ACCT_STORAGE_SLOTS_SECTION_OFFSET +pub proc get_account_active_storage_slots_section_ptr + exec.get_active_account_data_ptr add.ACCT_ACTIVE_STORAGE_SLOTS_SECTION_OFFSET end #! Returns the memory pointer to the native account's storage slots section. @@ -1378,8 +1378,8 @@ end #! #! Where: #! - storage_slots_section_ptr is the memory pointer to the native account's storage slots section. -pub proc get_native_account_storage_slots_ptr - exec.get_native_account_data_ptr add.ACCT_STORAGE_SLOTS_SECTION_OFFSET +pub proc get_native_account_active_storage_slots_ptr + exec.get_native_account_data_ptr add.ACCT_ACTIVE_STORAGE_SLOTS_SECTION_OFFSET end #! Returns the memory pointer to the initial storage slots of the native account. @@ -1407,7 +1407,7 @@ end pub proc get_account_initial_storage_slots_ptr # For foreign account, use the regular storage slots pointer since foreign accounts are # read-only and initial == current - exec.get_account_storage_slots_section_ptr + exec.get_account_active_storage_slots_section_ptr # => [account_storage_slots_ptr] # For native account, use the initial storage slots pointer @@ -1465,12 +1465,12 @@ end #! Outputs: [] pub proc mem_copy_native_account_initial_storage_slots exec.get_native_account_initial_storage_slots_ptr - exec.get_native_account_storage_slots_ptr - # => [storage_slots_section_ptr, initial_storage_slots_ptr] + exec.get_native_account_active_storage_slots_ptr + # => [active_storage_slots_ptr, initial_storage_slots_ptr] # each slot takes up two words exec.get_num_storage_slots mul.2 - # => [num_storage_slot_words, storage_slots_section_ptr, initial_storage_slots_ptr] + # => [num_storage_slot_words, active_storage_slots_ptr, initial_storage_slots_ptr] exec.mem::memcopy_words # => [] diff --git a/crates/miden-protocol/src/transaction/kernel/memory.rs b/crates/miden-protocol/src/transaction/kernel/memory.rs index d143a96bc5..9cbfcd5754 100644 --- a/crates/miden-protocol/src/transaction/kernel/memory.rs +++ b/crates/miden-protocol/src/transaction/kernel/memory.rs @@ -12,57 +12,49 @@ pub type StorageSlot = u8; // General layout // -// Here the "end address" is the last memory address occupied by the current data -// -// | Section | Start address, pointer (word pointer) | End address, pointer (word pointer) | Comment | -// | ------------------ | :-----------------------------------: | :---------------------------------: | ------------------------------------------ | -// | Bookkeeping | 0 (0) | 88 (22) | | -// | Global inputs | 400 (100) | 439 (109) | | -// | Block header | 800 (200) | 843 (210) | | -// | Partial blockchain | 1_200 (300) | 1_331? (332?) | | -// | Kernel data | 1_600 (400) | 1_739 (434) | 34 procedures in total, 4 elements each | -// | Accounts data | 8_192 (2048) | 532_479 (133_119) | 64 accounts max, 8192 elements each | -// | Account delta | 532_480 (133_120) | 532_742 (133_185) | | -// | Input notes | 4_194_304 (1_048_576) | 6_356_991 (1_589_247) | nullifiers data segment + 1024 input notes | -// | | | | max, 2048 elements each | -// | Output notes | 16_777_216 (4_194_304) | 18_874_367 (4_718_591) | 1024 output notes max, 2048 elements each | -// | Link Map Memory | 33_554_432 (8_388_608) | 67_108_863 (16_777_215) | Enough for 2_097_151 key-value pairs | +// | Section | Start address | Size in elements | Comment | +// | ------------------ | ------------- | ---------------- | ------------------------------------------ | +// | Bookkeeping | 0 | 89 | | +// | Global inputs | 400 | 40 | | +// | Block header | 800 | 44 | | +// | Partial blockchain | 1_200 | 132 | | +// | Kernel data | 1_600 | 140 | 34 procedures in total, 4 elements each | +// | Accounts data | 8_192 | 524_288 | 64 accounts max, 8192 elements each | +// | Account delta | 532_480 | 263 | | +// | Input notes | 4_194_304 | 2_162_688 | nullifiers data segment + 1024 input notes | +// | | | | max, 2048 elements each | +// | Output notes | 16_777_216 | 2_097_152 | 1024 output notes max, 2048 elements each | +// | Link Map Memory | 33_554_432 | 33_554_432 | Enough for 2_097_151 key-value pairs | // Relative layout of one account // -// Here the "end pointer" is the last memory pointer occupied by the current data -// -// TODO: Rearrange the memory sections that follow account procedures to be contiguous to it. -// -// | Section | Start address, pointer (word pointer) | End address, pointer (word pointer) | Comment | -// | ------------------ | :-----------------------------------: | :---------------------------------: | ----------------------------------- | -// | ID and nonce | 0 (0) | 3 (0) | | -// | Vault root | 4 (1) | 7 (1) | | -// | Storage commitment | 8 (2) | 11 (2) | | -// | Code commitment | 12 (3) | 15 (3) | | -// | Padding | 16 (4) | 27 (6) | | -// | Num procedures | 28 (7) | 31 (7) | | -// | Procedures roots | 32 (8) | 1_055 (263) | 256 procedures max, 4 elements each | -// | Padding | 2_080 (520) | 2_083 (520) | | -// | Proc tracking | 2_084 (521) | 2_339 (584) | 256 procedures max, 1 element each | -// | Num storage slots | 2_340 (585) | 2_343 (585) | | -// | Storage slot info | 2_344 (586) | 4_383 (1095) | 255 slots max, 8 elements each | -// | Initial slot info | 4_384 (1096) | 6_423 (1545) | Only present on the native account | -// | Padding | 6_424 (1545) | 8_191 (2047) | | +// | Section | Start address | Size in elements | Comment | +// | ------------------ | ------------- | ---------------- | -------------------------------------- | +// | ID and nonce | 0 | 4 | | +// | Vault root | 4 | 4 | | +// | Storage commitment | 8 | 4 | | +// | Code commitment | 12 | 4 | | +// | Padding | 16 | 12 | | +// | Num procedures | 28 | 4 | | +// | Procedures roots | 32 | 1_024 | 256 procedures max, 4 elements each | +// | Padding | 1_056 | 4 | | +// | Proc tracking | 1_060 | 256 | 256 procedures max, 1 element each | +// | Num storage slots | 1_316 | 4 | | +// | Initial slot info | 1_320 | 1_020 | Only initialized on the native account | +// | Active slot info | 2_340 | 1_020 | 255 slots max, 8 elements each | +// | Padding | 3_360 | 4_832 | | // -// Storage slot info is laid out as [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE]. +// Storage slots are laid out as [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE]. // Relative layout of the native account's delta. // -// Here the "end pointer" is the last memory pointer occupied by the current data -// // For now each Storage Map pointer (a link map ptr) occupies a single element. // -// | Section | Start address (word pointer) | End address (word pointer) | Comment | -// | ---------------------------- | :--------------------------: | :------------------------: | ----------------------------------- | -// | Fungible Asset Delta Ptr | 0 (0) | 3 (0) | | -// | Non-Fungible Asset Delta Ptr | 4 (1) | 7 (1) | | -// | Storage Map Delta Ptrs | 8 (2) | 263 (65) | Max 255 storage map deltas | +// | Section | Start address | Size in elements | Comment | +// | ---------------------------- | ------------- | ---------------- | ----------------------------------- | +// | Fungible Asset Delta Ptr | 0 | 4 | | +// | Non-Fungible Asset Delta Ptr | 4 | 4 | | +// | Storage Map Delta Ptrs | 8 | 256 | Max 255 storage map deltas | // BOOKKEEPING // ------------------------------------------------------------------------------------------------ @@ -269,12 +261,12 @@ pub const NATIVE_ACCT_CODE_COMMITMENT_PTR: MemoryAddress = /// The offset at which the number of procedures contained in the account code is stored relative to /// the start of the account data segment. -pub const NUM_ACCT_PROCEDURES_OFFSET: MemoryAddress = 28; +pub const ACCT_NUM_PROCEDURES_OFFSET: MemoryAddress = 28; /// The memory address at which the number of procedures contained in the account code is stored in /// the native account. pub const NATIVE_NUM_ACCT_PROCEDURES_PTR: MemoryAddress = - NATIVE_ACCOUNT_DATA_PTR + NUM_ACCT_PROCEDURES_OFFSET; + NATIVE_ACCOUNT_DATA_PTR + ACCT_NUM_PROCEDURES_OFFSET; /// The offset at which the account procedures section begins relative to the start of the account /// data segment. @@ -286,7 +278,7 @@ pub const NATIVE_ACCT_PROCEDURES_SECTION_PTR: MemoryAddress = /// The offset at which the account procedures call tracking section begins relative to the start of /// the account data segment. -pub const ACCT_PROCEDURES_CALL_TRACKING_OFFSET: MemoryAddress = 2084; +pub const ACCT_PROCEDURES_CALL_TRACKING_OFFSET: MemoryAddress = 1060; /// The memory address at which the account procedures call tracking section begins in the native /// account. @@ -295,16 +287,12 @@ pub const NATIVE_ACCT_PROCEDURES_CALL_TRACKING_PTR: MemoryAddress = /// The offset at which the number of storage slots contained in the account storage is stored /// relative to the start of the account data segment. -pub const NUM_ACCT_STORAGE_SLOTS_OFFSET: MemoryAddress = 2340; +pub const ACCT_NUM_STORAGE_SLOTS_OFFSET: MemoryAddress = 1316; /// The memory address at which number of storage slots contained in the account storage is stored /// in the native account. pub const NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR: MemoryAddress = - NATIVE_ACCOUNT_DATA_PTR + NUM_ACCT_STORAGE_SLOTS_OFFSET; - -/// The offset at which the account storage slots section begins relative to the start of the -/// account data segment. -pub const ACCT_STORAGE_SLOTS_SECTION_OFFSET: MemoryAddress = 2344; + NATIVE_ACCOUNT_DATA_PTR + ACCT_NUM_STORAGE_SLOTS_OFFSET; /// The number of elements that each storage slot takes up in memory. pub const ACCT_STORAGE_SLOT_NUM_ELEMENTS: u8 = 8; @@ -321,9 +309,16 @@ pub const ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET: u8 = 3; /// The offset of the slot value in the storage slot. pub const ACCT_STORAGE_SLOT_VALUE_OFFSET: u8 = 4; -/// The memory address at which the account storage slots section begins in the native account. +/// The offset at which the account's active storage slots section begins relative to the start of +/// the account data segment. +/// +/// This section contains the current values of the account storage slots. +pub const ACCT_ACTIVE_STORAGE_SLOTS_SECTION_OFFSET: MemoryAddress = 2340; + +/// The memory address at which the account's active storage slots section begins in the native +/// account. pub const NATIVE_ACCT_STORAGE_SLOTS_SECTION_PTR: MemoryAddress = - NATIVE_ACCOUNT_DATA_PTR + ACCT_STORAGE_SLOTS_SECTION_OFFSET; + NATIVE_ACCOUNT_DATA_PTR + ACCT_ACTIVE_STORAGE_SLOTS_SECTION_OFFSET; // NOTES DATA // ================================================================================================ diff --git a/crates/miden-protocol/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs index 0b7e317d6d..ad580b495f 100644 --- a/crates/miden-protocol/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -8,41 +8,41 @@ use crate::{Word, word}; /// Hashes of all dynamically executed kernel procedures. pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_get_initial_commitment - word!("0x1c95a0386ebf3645c6271253a4ae49ea4be8610dea7b4436c58951277a75f0c1"), + word!("0x45c5a29a7420ecd4394c8cffe822f31781cb0a5e30aa2aa179d143df5710f23e"), // account_compute_commitment - word!("0x1aed40e2cc4d3798448f4efdce1a14c9598611da065eebe58432f144c3bca9de"), + word!("0x5c228332186b23e8e6dcb22751150ef8e350657d2a0c7e7934b30dea6ecb8a7f"), // account_get_id word!("0xd76288f2e94b9e6a8f7eeee45c4ee0a23997d78496f6132e3f55681efea809c4"), // account_get_nonce word!("0x4a1f11db21ddb1f0ebf7c9fd244f896a95e99bb136008185da3e7d6aa85827a3"), // account_incr_nonce - word!("0x8bcc1f43091608403ea8d2fdefbcbfc799b41a0ff3fccc29b9873a23ff8edfbd"), + word!("0xd1316d21f95e20385283ac72914585a4a3281ff69935540d130fd1ace25ec9ae"), // account_get_code_commitment - word!("0x54406e5633978e8319003d8d65527a48c0d0a4828263aa30f40687a2bbfd7be0"), + word!("0x8c044d53df35c71425c806b8997c6541e0aac70d7880657e9354b9854626ce3e"), // account_get_initial_storage_commitment word!("0x91377c2852feb7a2798e54d7dbaa2d97000270ec4c0d0888b26d720a25ae0e84"), // account_compute_storage_commitment - word!("0xaa54d5c070ad5e2a39c9b8b3a17aaa3c9e9387c35c59f72c965060ba91e4f748"), + word!("0x8732c9765d2b35f0d9f26dcec349f18a7234c9e988057a14e358f97ea123cb5f"), // account_get_item - word!("0x9a4c4b33fde23e9fbe4c78b3430a058cc7ef8b5e487de19fa77d9d52a934e903"), + word!("0xdc6917a6d797c0717a56255e3e94e6d4f1317e92862c7331c3070d82402828ec"), // account_get_initial_item - word!("0x182573f7527df3b1e0b7cd28a7a2cb722776654d90219792806923bffe9d74df"), + word!("0x1853416c007dc75de04c25aaf2376fa7e98d6c010a46bb90e504491f5634ee12"), // account_set_item - word!("0x24c88b3ec4b67ff8f58fba5f5a01117347c39efe7ec4e3236d6d38bd238ad99b"), + word!("0x29392c01f8953e4e4f6dd8eba69c53bd5f4ff7f54beeaab2e86d8ef7c8d982a0"), // account_get_map_item - word!("0xba1f281fe8c8ef584bfc1615962cbde4573ac01173bb113a5c79bcecf4eb1c65"), + word!("0x791d4be34dc2ea7b4574ba756e2d5103a24e4a961212a6d95168071157423c9c"), // account_get_initial_map_item - word!("0x7f00c7140a71d12d1162a9cf0143bdd64bbd7e8d63b115fc3a2b07338813f8ab"), + word!("0xfbc6fcd521aaed1018466126a795eaf820cfc48e6ef3761f21a4363ef259324b"), // account_set_map_item - word!("0x416c57bd6388b0c4da20409458588678811f15d60bc73bd939d4e26a48ed6874"), + word!("0xc4bccf37cd3961f402e46f34f1597092dd083c6d282fed94ec8b914ea2920363"), // account_get_initial_vault_root word!("0x46297d9ac95afd60c7ef1a065e024ad49aa4c019f6b3924191905449b244d4ec"), // account_get_vault_root word!("0x42a2bfb8eac4fce9bbf75ea15215b00729faeeaf7fff784692948d3f618a9bb7"), // account_add_asset - word!("0xb0f56deca8a478de98114c0aaafd96732f9be5db6fa94c1d4e0cf71ed5958e53"), + word!("0xef3d652f73a6e3a88737cf08732537552b2423d5461880df50665264944c4891"), // account_remove_asset - word!("0xe70870b0f7baca27f3c6311ed322159af037b2bb0c7e90c4ac5ac0b5feaabc8c"), + word!("0x680c8d9df19de7dee201a658dcd7005d1f0c85c7d4729a6bc228ed0c2d48678f"), // account_get_balance word!("0x6a5eb788fd2beec7555874f978a4dd2f2c4f5d8088cd33e148c61450e4510fe1"), // account_get_initial_balance @@ -50,23 +50,23 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // account_has_non_fungible_asset word!("0xffe57961158c8e5f8a3aaa773943ee208fac7ed4786a7c8b6fed04ba54f39111"), // account_compute_delta_commitment - word!("0x5152e37bf0e1b5e0673f129cc84eb97b1c4cfb85127002169e0a2d827145f872"), + word!("0xedd96b5ee329219e0dc2085b5249c02ce732500fca27719085aa99e1263f6113"), // account_get_num_procedures word!("0x53b5ec38b7841948762c258010e6e07ad93963bcaac2d83813f8edb6710dc720"), // account_get_procedure_root word!("0xa45796077477599813ea54d88a83dfca3e64b1c112117970d63a1c24f7cfef5a"), // account_was_procedure_called - word!("0x23ccd2f94111eabb33481664f8ca5a400b1518380a5adadabd2cee7a825a0465"), + word!("0xd9e08c27f3f1e7d01d257cbb4583ecf14f5e362d9c19e39ada8bc19dcc820d45"), // account_has_procedure word!("0xb0b63fdd01af0bcb4aacb2412e934cdc7691308647152d416c7ae4fc909da076"), // faucet_mint_asset - word!("0x3e4959e0309e85566c11397f95f32a53617da002cbbe10a174640a31a03b2498"), + word!("0xdb32b55a283114bb22d2f7fe867eb9ac745c49c45413cc4155d66df062c022ec"), // faucet_burn_asset - word!("0x8490d8b28e3192df25500da5a1af2ea133f7c34f11328fb38235c2d7147d11fa"), + word!("0xa5b7f8c0261ee87a36f7b4d67f35d4369b6f139d0bb75aa24b8275b0f69dbb4e"), // faucet_get_total_fungible_asset_issuance - word!("0xa1d900ff530770c3e56876fedd1b8f9f5ca491e2198524a973148ae3df88c581"), + word!("0x0953a2f2ec88ad0b7008c3d71aca46ebfcbb58a8ffdf59390616497c6693e8ab"), // faucet_is_non_fungible_asset_issued - word!("0xff77f7a2fabbbba5ef996242963569a84c2ef4614d6e8178e56ab4ae284673c7"), + word!("0xd2131c592fd7565bbf5d555e73a022c4ec1b5bc70219724e26424ff662e02b1a"), // input_note_get_metadata word!("0x7ad3e94585e7a397ee27443c98b376ed8d4ba762122af6413fde9314c00a6219"), // input_note_get_assets_info @@ -104,7 +104,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // tx_get_block_timestamp word!("0x7903185b847517debb6c2072364e3e757b99ee623e97c2bd0a4661316c5c5418"), // tx_start_foreign_context - word!("0xa44d1bcc1c42ec52df448ebf9362aefc61223a374aa14cad6cf51d406c4b1c4c"), + word!("0xfddca78a0611aa0badc95ef57d5d7485d076eab7d3a505347a26db13d40dc18e"), // tx_end_foreign_context word!("0xaa0018aa8da890b73511879487f65553753fb7df22de380dd84c11e6f77eec6f"), // tx_get_expiration_delta diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 42b0cb4098..1f7ed8c1cf 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -28,15 +28,15 @@ use miden_protocol::testing::account_id::{ use miden_protocol::testing::storage::STORAGE_LEAVES_2; use miden_protocol::transaction::memory::{ ACCOUNT_DATA_LENGTH, + ACCT_ACTIVE_STORAGE_SLOTS_SECTION_OFFSET, ACCT_CODE_COMMITMENT_OFFSET, ACCT_ID_AND_NONCE_OFFSET, + ACCT_NUM_PROCEDURES_OFFSET, + ACCT_NUM_STORAGE_SLOTS_OFFSET, ACCT_PROCEDURES_SECTION_OFFSET, ACCT_STORAGE_COMMITMENT_OFFSET, - ACCT_STORAGE_SLOTS_SECTION_OFFSET, ACCT_VAULT_ROOT_OFFSET, NATIVE_ACCOUNT_DATA_PTR, - NUM_ACCT_PROCEDURES_OFFSET, - NUM_ACCT_STORAGE_SLOTS_OFFSET, }; use miden_protocol::{FieldElement, Word, ZERO}; use miden_standards::code_builder::CodeBuilder; @@ -1820,7 +1820,7 @@ fn foreign_account_data_memory_assertions( ); assert_eq!( - exec_output.get_kernel_mem_word(foreign_account_data_ptr + NUM_ACCT_STORAGE_SLOTS_OFFSET), + exec_output.get_kernel_mem_word(foreign_account_data_ptr + ACCT_NUM_STORAGE_SLOTS_OFFSET), Word::from([u16::try_from(foreign_account.storage().slots().len()).unwrap(), 0, 0, 0]), ); @@ -1832,14 +1832,16 @@ fn foreign_account_data_memory_assertions( { assert_eq!( exec_output.get_kernel_mem_word( - foreign_account_data_ptr + ACCT_STORAGE_SLOTS_SECTION_OFFSET + (i as u32) * 4 + foreign_account_data_ptr + + ACCT_ACTIVE_STORAGE_SLOTS_SECTION_OFFSET + + (i as u32) * 4 ), Word::try_from(elements).unwrap(), ) } assert_eq!( - exec_output.get_kernel_mem_word(foreign_account_data_ptr + NUM_ACCT_PROCEDURES_OFFSET), + exec_output.get_kernel_mem_word(foreign_account_data_ptr + ACCT_NUM_PROCEDURES_OFFSET), Word::from([u16::try_from(foreign_account.code().num_procedures()).unwrap(), 0, 0, 0]), ); From e7043f5a731a9d0280bb91aec42bfc41b1b59ccf Mon Sep 17 00:00:00 2001 From: Marti Date: Thu, 8 Jan 2026 19:24:59 +0100 Subject: [PATCH 091/114] chore: add error messages to asserts (#2239) --- .../src/kernel_tests/tx/test_account.rs | 10 +++---- .../src/kernel_tests/tx/test_active_note.rs | 26 +++++++++---------- .../src/kernel_tests/tx/test_epilogue.rs | 6 ++--- .../src/kernel_tests/tx/test_fpi.rs | 6 ++--- .../src/kernel_tests/tx/test_note.rs | 4 +-- .../src/kernel_tests/tx/test_tx.rs | 6 ++--- crates/miden-testing/src/utils.rs | 6 ++--- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 342da526a3..47cdc60564 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -700,7 +700,7 @@ async fn test_set_item() -> anyhow::Result<()> { exec.account::get_item push.{new_value} - assert_eqw + assert_eqw.err="new value did not match" end "#, ); @@ -1009,7 +1009,7 @@ async fn test_get_vault_root() -> anyhow::Result<()> { // get the initial vault root let code = format!( - " + r#" use miden::protocol::active_account use $kernel::prologue @@ -1019,9 +1019,9 @@ async fn test_get_vault_root() -> anyhow::Result<()> { # get the initial vault root exec.active_account::get_initial_vault_root push.{expected_vault_root} - assert_eqw + assert_eqw.err="initial vault root mismatch" end - ", + "#, expected_vault_root = &account.vault().root(), ); tx_context.execute_code(&code).await?; @@ -1046,7 +1046,7 @@ async fn test_get_vault_root() -> anyhow::Result<()> { # get the current vault root exec.active_account::get_vault_root push.{expected_vault_root} - assert_eqw.err="actual vault root is not equal to the expected one" + assert_eqw.err="vault root mismatch" end "#, fungible_asset = Word::from(&fungible_asset), diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index b78a247283..f24642ae3e 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -198,10 +198,10 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { let mut code = String::new(); for asset in note.assets().iter() { code += &format!( - " + r#" # assert the asset is correct - dup padw movup.4 mem_loadw_be push.{asset} assert_eqw push.4 add - ", + dup padw movup.4 mem_loadw_be push.{asset} assert_eqw.err="asset mismatch" push.4 add + "#, asset = Word::from(asset) ); } @@ -210,7 +210,7 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { // calling get_assets should return assets at the specified address let code = format!( - " + r#" use miden::core::sys use $kernel::prologue @@ -228,10 +228,10 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { exec.active_note::get_assets # assert the number of assets is correct - eq.{note_0_num_assets} assert + eq.{note_0_num_assets} assert.err="unexpected num assets for note 0" # assert the pointer is returned - dup eq.{DEST_POINTER_NOTE_0} assert + dup eq.{DEST_POINTER_NOTE_0} assert.err="unexpected dest ptr for note 0" # asset memory assertions {NOTE_0_ASSET_ASSERTIONS} @@ -251,10 +251,10 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { exec.active_note::get_assets # assert the number of assets is correct - eq.{note_1_num_assets} assert + eq.{note_1_num_assets} assert.err="unexpected num assets for note 1" # assert the pointer is returned - dup eq.{DEST_POINTER_NOTE_1} assert + dup eq.{DEST_POINTER_NOTE_1} assert.err="unexpected dest ptr for note 1" # asset memory assertions {NOTE_1_ASSET_ASSERTIONS} @@ -285,7 +285,7 @@ async fn test_active_note_get_assets() -> anyhow::Result<()> { # truncate the stack exec.sys::truncate_stack end - ", + "#, note_0_num_assets = notes.get_note(0).note().assets().num_assets(), note_1_num_assets = notes.get_note(1).note().assets().num_assets(), NOTE_0_ASSET_ASSERTIONS = construct_asset_assertions(notes.get_note(0).note()), @@ -340,7 +340,7 @@ async fn test_active_note_get_inputs() -> anyhow::Result<()> { let note0 = tx_context.input_notes().get_note(0).note(); let code = format!( - " + r#" use $kernel::prologue use $kernel::note->note_internal use miden::protocol::active_note @@ -360,10 +360,10 @@ async fn test_active_note_get_inputs() -> anyhow::Result<()> { push.{NOTE_0_PTR} exec.active_note::get_inputs # => [num_inputs, dest_ptr] - eq.{num_inputs} assert + eq.{num_inputs} assert.err="unexpected num inputs" # => [dest_ptr] - dup eq.{NOTE_0_PTR} assert + dup eq.{NOTE_0_PTR} assert.err="unexpected dest ptr" # => [dest_ptr] # apply note 1 inputs assertions @@ -374,7 +374,7 @@ async fn test_active_note_get_inputs() -> anyhow::Result<()> { drop # => [] end - ", + "#, num_inputs = note0.inputs().num_values(), inputs_assertions = construct_inputs_assertions(note0), NOTE_0_PTR = 100000000, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index e2563cc412..16f1c564ff 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -329,7 +329,7 @@ async fn test_block_expiration_height_monotonically_decreases() -> anyhow::Resul push.{value_2} exec.tx::update_expiration_block_delta - push.{min_value} exec.tx::get_expiration_delta assert_eq + push.{min_value} exec.tx::get_expiration_delta assert_eq.err=\"expiration delta mismatch\" exec.epilogue::finalize_transaction @@ -398,7 +398,7 @@ async fn test_no_expiration_delta_set() -> anyhow::Result<()> { begin exec.prologue::prepare_transaction - exec.tx::get_expiration_delta assertz + exec.tx::get_expiration_delta assertz.err=\"expiration delta should be unset\" exec.epilogue::finalize_transaction @@ -449,7 +449,7 @@ async fn test_epilogue_increment_nonce_success() -> anyhow::Result<()> { dropw dropw dropw dropw exec.memory::get_account_nonce - push.{expected_nonce} assert_eq + push.{expected_nonce} assert_eq.err="nonce mismatch" end "#, mock_value_slot0 = &*MOCK_VALUE_SLOT0, diff --git a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs index 1f7ed8c1cf..addda7b449 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_fpi.rs @@ -610,7 +610,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { # => [STORAGE_VALUE] # assert the correctness of the obtained value - push.1.2.3.4 assert_eqw + push.1.2.3.4 assert_eqw.err="foreign proc returned unexpected value" # => [] # get an item from the storage map @@ -636,7 +636,7 @@ async fn test_fpi_execute_foreign_procedure() -> anyhow::Result<()> { # => [MAP_VALUE] # assert the correctness of the obtained value - push.1.2.3.4 assert_eqw + push.1.2.3.4 assert_eqw.err="foreign proc returned unexpected value" # => [] # truncate the stack @@ -1318,7 +1318,7 @@ async fn test_nested_fpi_stack_overflow() -> anyhow::Result<()> { drop drop drop # make sure that the resulting value equals 1 - assert + assert.err="expected value to be 1" end "#, mock_value_slot0 = mock_value_slot0.name(), diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 8bee856271..5d761de2d1 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -123,14 +123,14 @@ async fn test_note_script_and_note_args() -> miette::Result<()> { .unwrap() }; - let code = " + let code = " use $kernel::prologue use $kernel::memory use $kernel::note begin exec.prologue::prepare_transaction - exec.memory::get_num_input_notes push.2 assert_eq + exec.memory::get_num_input_notes push.2 assert_eq.err=\"unexpected number of input notes\" exec.note::prepare_note drop # => [NOTE_ARGS0, pad(11), pad(16)] repeat.11 movup.4 drop end diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index 7354f55c90..1714c4b9bb 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -680,7 +680,7 @@ async fn test_tx_script_inputs() -> anyhow::Result<()> { let tx_script_input_key = Word::from([9999, 8888, 9999, 8888u32]); let tx_script_input_value = Word::from([9, 8, 7, 6u32]); let tx_script_src = format!( - " + r#" begin # push the tx script input key onto the stack push.{tx_script_input_key} @@ -689,9 +689,9 @@ async fn test_tx_script_inputs() -> anyhow::Result<()> { adv.push_mapval adv_loadw # assert that the value is correct - push.{tx_script_input_value} assert_eqw + push.{tx_script_input_value} assert_eqw.err="tx script input value mismatch" end - " + "#, ); let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index 11007500a9..fc5f3aa249 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -128,7 +128,7 @@ pub fn create_p2any_note( code_body.push_str("dropw dropw dropw dropw"); let code = format!( - " + r#" use mock::account use miden::protocol::active_note use miden::standards::wallets::basic->wallet @@ -138,12 +138,12 @@ pub fn create_p2any_note( push.0 exec.active_note::get_assets # [num_assets, dest_ptr] # runtime-check we got the expected count - push.{num_assets} assert_eq # [dest_ptr] + push.{num_assets} assert_eq.err="unexpected number of assets" # [dest_ptr] {code_body} dropw dropw dropw dropw end - ", + "#, num_assets = assets.len(), ); From 2a09b42cd03a4c0fe7bf25a9cc3d9fccc280143c Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:55:05 -0500 Subject: [PATCH 092/114] feat: add Agglayer `CLAIM` note & bridging in functionality (#2188) --- crates/miden-agglayer/Cargo.toml | 1 + .../asm/bridge/agglayer_faucet.masm | 260 ++++++++ .../asm/bridge/asset_conversion.masm | 8 + .../miden-agglayer/asm/bridge/bridge_in.masm | 43 ++ .../miden-agglayer/asm/bridge/bridge_out.masm | 1 + .../asm/bridge/crypto_utils.masm | 83 +++ .../asm/bridge/local_exit_tree.masm | 3 +- .../asm/note_scripts/CLAIM.masm | 211 +++++++ crates/miden-agglayer/build.rs | 48 +- crates/miden-agglayer/src/errors/agglayer.rs | 6 + crates/miden-agglayer/src/lib.rs | 594 +++++++++++++++++- crates/miden-agglayer/src/utils.rs | 84 +++ crates/miden-testing/src/mock_chain/chain.rs | 5 +- .../miden-testing/tests/agglayer/bridge_in.rs | 205 ++++++ crates/miden-testing/tests/agglayer/mod.rs | 1 + 15 files changed, 1532 insertions(+), 21 deletions(-) create mode 100644 crates/miden-agglayer/asm/bridge/agglayer_faucet.masm create mode 100644 crates/miden-agglayer/asm/bridge/bridge_in.masm create mode 100644 crates/miden-agglayer/asm/bridge/crypto_utils.masm create mode 100644 crates/miden-agglayer/asm/note_scripts/CLAIM.masm create mode 100644 crates/miden-testing/tests/agglayer/bridge_in.rs diff --git a/crates/miden-agglayer/Cargo.toml b/crates/miden-agglayer/Cargo.toml index e453e032a9..0bae5deba1 100644 --- a/crates/miden-agglayer/Cargo.toml +++ b/crates/miden-agglayer/Cargo.toml @@ -24,6 +24,7 @@ testing = ["miden-protocol/testing"] miden-assembly = { workspace = true } miden-core = { workspace = true } miden-protocol = { workspace = true } +miden-standards = { workspace = true } miden-utils-sync = { workspace = true } [dev-dependencies] diff --git a/crates/miden-agglayer/asm/bridge/agglayer_faucet.masm b/crates/miden-agglayer/asm/bridge/agglayer_faucet.masm new file mode 100644 index 0000000000..db22b900ad --- /dev/null +++ b/crates/miden-agglayer/asm/bridge/agglayer_faucet.masm @@ -0,0 +1,260 @@ +use miden::agglayer::bridge_in +use miden::agglayer::asset_conversion +use miden::protocol::active_account +use miden::protocol::active_note +use miden::standards::faucets +use miden::protocol::note +use miden::protocol::tx +use miden::core::mem + + +# CONSTANTS +# ================================================================================================= + +# The slot in this component's storage layout where the bridge account ID is stored. +const BRIDGE_ID_SLOT = word("miden::agglayer::faucet") + +const PROOF_DATA_WORD_LEN = 134 +const LEAF_DATA_WORD_LEN = 6 +const OUTPUT_NOTE_DATA_WORD_LEN = 2 + +const PROOF_DATA_START_PTR = 0 +const LEAF_DATA_START_PTR = 536 +const OUTPUT_NOTE_DATA_START_PTR = 568 + +# Memory Addresses +const PROOF_DATA_KEY_MEM_ADDR = 700 +const LEAF_DATA_KEY_MEM_ADDR = 704 +const OUTPUT_NOTE_DATA_MEM_ADDR = 708 +const CLAIM_NOTE_DATA_MEM_ADDR = 712 + +const OUTPUT_NOTE_INPUTS_MEM_ADDR = 0 +const OUTPUT_NOTE_TAG_MEM_ADDR = 574 +const OUTPUT_NOTE_SERIAL_NUM_MEM_ADDR = 568 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 = 548 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 = 552 + +# P2ID output note constants +const P2ID_SCRIPT_ROOT = [7588674509004260508, 4058706621878288170, 5607159951796201570, 5541281552524512743] +const P2ID_NOTE_NUM_INPUTS = 2 +const OUTPUT_NOTE_TYPE_PUBLIC = 1 +const EXECUTION_HINT_ALWAYS = 1 +const OUTPUT_NOTE_AUX = 0 + +const P2ID_OUTPUT_NOTE_AMOUNT_MEM_PTR = 611 +# ERRORS +# ================================================================================================= + +const ERR_INVALID_CLAIM_PROOF = "invalid claim proof" + +#! Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY] +#! Outputs: [] +#! +#! Panics if: +#! - the bridge account ID is not properly configured in storage. +#! - the foreign procedure invocation fails. +#! - the claim proof validation fails. +#! +#! Invocation: exec +proc validate_claim + # Get bridge_in::check_claim_proof procedure MAST root + procref.bridge_in::check_claim_proof + # => [BRIDGE_PROC_MAST_ROOT] + + push.BRIDGE_ID_SLOT[0..2] + # => [bridge_id_idx, BRIDGE_PROC_MAST_ROOT] + + # Get Bridge AccountId + exec.active_account::get_item + # => [bridge_account_id_prefix, bridge_account_id_suffix, 0, 0, BRIDGE_PROC_MAST_ROOT] + + movup.2 drop movup.2 drop + # => [bridge_account_id_prefix, bridge_account_id_suffix, BRIDGE_PROC_MAST_ROOT] + + # Call check_claim_proof procedure on Bridge + # Calling: bridge_in::check_claim_proof + exec.tx::execute_foreign_procedure + # => [validation_result] + + # Assert valid proof data + assert.err=ERR_INVALID_CLAIM_PROOF drop + # => [] +end + +# Inputs: [] +# Outputs: [U256[0], U256[1]] +proc get_raw_claim_amount + padw mem_loadw_be.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 + padw mem_loadw_be.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 +end + +# Inputs: [U256[0], U256[1]] +# Outputs: [amount] +proc scale_down_amount + repeat.7 drop end +end + +# Inputs: [] +# Outputs: [prefix, suffix] +proc get_destination_account_id + mem_load.543 mem_load.544 +end + +# Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY] +# Outputs: [] +proc batch_pipe_double_words + # 1) Verify PROOF_DATA_KEY + mem_storew_be.PROOF_DATA_KEY_MEM_ADDR + adv.push_mapval + # => [PROOF_DATA_KEY] + + push.PROOF_DATA_START_PTR push.PROOF_DATA_WORD_LEN + exec.mem::pipe_double_words_preimage_to_memory drop + + # 2) Verify LEAF_DATA_KEY + mem_storew_be.LEAF_DATA_KEY_MEM_ADDR + adv.push_mapval + # => [LEAF_DATA_KEY] + + push.LEAF_DATA_START_PTR push.LEAF_DATA_WORD_LEN + exec.mem::pipe_double_words_preimage_to_memory drop + + # 3) Verify OUTPUT_NOTE_DATA_KEY + mem_storew_be.OUTPUT_NOTE_DATA_MEM_ADDR + adv.push_mapval + # => [OUTPUT_NOTE_DATA_KEY] + + push.OUTPUT_NOTE_DATA_START_PTR push.OUTPUT_NOTE_DATA_WORD_LEN + exec.mem::pipe_double_words_preimage_to_memory drop +end + +#! Builds a P2ID output note for the claim recipient. +#! +#! This procedure expects the claim data to be already written to memory via batch_pipe_double_words. +#! It reads the destination account ID, amount, and other note parameters from memory to construct +#! the output note. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Note: This procedure will be refactored in a follow-up to use leaf data to build the output note. +proc build_p2id_output_note + # Build P2ID output note + push.P2ID_SCRIPT_ROOT[0..4] + # => [SCRIPT_ROOT] + + swapw mem_loadw_be.OUTPUT_NOTE_SERIAL_NUM_MEM_ADDR + # => [SERIAL_NUM, SCRIPT_ROOT] + + push.P2ID_NOTE_NUM_INPUTS + # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT] + + exec.get_destination_account_id + # => [account_id_prefix, account_id_suffix, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT] + + mem_store.0 mem_store.1 + # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT] + + push.OUTPUT_NOTE_INPUTS_MEM_ADDR + # => [inputs_ptr = 0, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT] + + exec.note::build_recipient + # => [RECIPIENT] + + push.EXECUTION_HINT_ALWAYS push.OUTPUT_NOTE_TYPE_PUBLIC push.OUTPUT_NOTE_AUX + # => [aux, note_type, execution_hint, RECIPIENT] + + mem_load.OUTPUT_NOTE_TAG_MEM_ADDR + # => [tag, aux, execution_hint, RECIPIENT] + + exec.get_raw_claim_amount + # => [AMOUNT[1], AMOUNT[0], tag, aux, note_type, execution_hint, RECIPIENT] + + # TODO: implement scale down logic; stubbed out for now + exec.asset_conversion::scale_u256_to_native_amount + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT] + + exec.faucets::distribute + # => [pad(16)] +end + +#! Validates a claim against the AggLayer bridge and mints the corresponding asset to the recipient. +#! +#! This procedure validates the rollup exit root Merkle Proof via FPI against the agglayer bridge, +#! and if validation passes, mints the asset and creates an output note for the recipient. +#! +#! TODO: Expand this description to cover the double-spend protection mechanism in detail. +#! Double-spend can be prevented in two ways: +#! 1) While it's possible to create two identical P2ID notes, only one can actually be consumed. +#! If the claim note is consumed twice, only one P2ID output note will be successfully consumed. +#! 2) We can have a mapping in the bridge or in the faucet that stores consumed claim proofs +#! as a hash -> bool value (similar to how it's done in the agglayer solidity contract). +#! +#! Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY, pad(4)] +#! Outputs: [pad(16)] +#! +#! Advice map: { +#! PROOF_DATA_KEY => [ +#! smtProofLocalExitRoot[256], // SMT proof for local exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! smtProofRollupExitRoot[256], // SMT proof for rollup exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! globalIndex[8], // Global index (8 felts, uint256 as 8 u32 felts) +#! mainnetExitRoot[8], // Mainnet exit root hash (8 felts, bytes32 as 8 u32 felts) +#! rollupExitRoot[8], // Rollup exit root hash (8 felts, bytes32 as 8 u32 felts) +#! ], +#! LEAF_DATA_KEY => [ +#! originNetwork[1], // Origin network identifier (1 felt, uint32) +#! originTokenAddress[5], // Origin token address (5 felts, address as 5 u32 felts) +#! destinationNetwork[1], // Destination network identifier (1 felt, uint32) +#! destinationAddress[5], // Destination address (5 felts, address as 5 u32 felts) +#! amount[8], // Amount of tokens (8 felts, uint256 as 8 u32 felts) +#! metadata[8], // ABI encoded metadata (8 felts, fixed size) +#! EMPTY_WORD // padding +#! ], +#! OUTPUT_NOTE_DATA_KEY => [ +#! output_p2id_serial_num[4], // P2ID note serial number (4 felts, Word) +#! agglayer_faucet_account_id[2], // Agglayer faucet account ID (2 felts, prefix and suffix) +#! output_note_tag[1], // P2ID output note tag +#! ] +#! } +#! +#! Panics if: +#! - the rollup exit root Merkle Proof validation via FPI fails. +#! - any of the validations in faucets::distribute fail. +#! +#! Invocation: call +pub proc claim + # Check AdviceMap values hash to keys & write CLAIM inputs & DATA_KEYs to global memory + exec.batch_pipe_double_words + # => [] + + # VALIDATE CLAIM + mem_loadw_be.LEAF_DATA_KEY_MEM_ADDR padw + mem_loadw_be.PROOF_DATA_KEY_MEM_ADDR + # => [PROOF_DATA_KEY, LEAF_DATA_KEY] + + # Errors on invalid proof + exec.validate_claim + # => [] + + # Create P2ID output note + exec.build_p2id_output_note + # => [] +end + +#! Burns the fungible asset from the active note. +#! +#! This procedure retrieves the asset from the active note and burns it. The note must contain +#! exactly one asset, which must be a fungible asset issued by this faucet. +#! +#! Inputs: [pad(16)] +#! Outputs: [pad(16)] +#! +#! Panics if: +#! - the procedure is not called from a note context (active_note::get_assets will fail). +#! - the note does not contain exactly one asset. +#! - the transaction is executed against an account which is not a fungible asset faucet. +#! - the transaction is executed against a faucet which is not the origin of the specified asset. +#! - the amount about to be burned is greater than the outstanding supply of the asset. +#! +#! Invocation: call +pub use ::miden::standards::faucets::basic_fungible::burn diff --git a/crates/miden-agglayer/asm/bridge/asset_conversion.masm b/crates/miden-agglayer/asm/bridge/asset_conversion.masm index 7596846022..e4f59f17d4 100644 --- a/crates/miden-agglayer/asm/bridge/asset_conversion.masm +++ b/crates/miden-agglayer/asm/bridge/asset_conversion.masm @@ -104,3 +104,11 @@ pub proc scale_native_amount_to_u256 padw swapw # => [RESULT_U256[0], RESULT_U256[1]] end + +#! TODO: implement scaling down +#! +#! Inputs: [U256[0], U256[1]] +#! Outputs: [amount] +pub proc scale_u256_to_native_amount + repeat.7 drop end +end diff --git a/crates/miden-agglayer/asm/bridge/bridge_in.masm b/crates/miden-agglayer/asm/bridge/bridge_in.masm new file mode 100644 index 0000000000..1862fc5e35 --- /dev/null +++ b/crates/miden-agglayer/asm/bridge/bridge_in.masm @@ -0,0 +1,43 @@ +use miden::agglayer::crypto_utils + +# Inputs: [] +# Output: [GER_ROOT[8]] +pub proc get_rollup_exit_root + # Push dummy GER (8 elements) + push.0.0.0.0.0.0.0.0 # dummy GER +end + +#! Checks the validity of the GET proof +#! +#! Inputs: +#! Operand stack: [PROOF_DATA_KEY, LEAF_DATA_KEY, pad(8)] +#! Advice map: { +#! PROOF_DATA_KEY => [ +#! smtProofLocalExitRoot[256], // SMT proof for local exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! smtProofRollupExitRoot[256], // SMT proof for rollup exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! globalIndex[8], // Global index (8 felts, uint256 as 8 u32 felts) +#! mainnetExitRoot[8], // Mainnet exit root hash (8 felts, bytes32 as 8 u32 felts) +#! rollupExitRoot[8], // Rollup exit root hash (8 felts, bytes32 as 8 u32 felts) +#! ], +#! LEAF_DATA_KEY => [ +#! originNetwork[1], // Origin network identifier (1 felt, uint32) +#! originTokenAddress[5], // Origin token address (5 felts, address as 5 u32 felts) +#! destinationNetwork[1], // Destination network identifier (1 felt, uint32) +#! destinationAddress[5], // Destination address (5 felts, address as 5 u32 felts) +#! amount[8], // Amount of tokens (8 felts, uint256 as 8 u32 felts) +#! metadata[8], // ABI encoded metadata (8 felts, fixed size) +#! EMPTY_WORD // padding +#! ], +#! } +#! +#! Invocation: call +pub proc check_claim_proof + exec.get_rollup_exit_root + # => [GER_ROOT[8], CLAIM_NOTE_RPO_COMMITMENT] + + # Check CLAIM note proof data against current GER + exec.crypto_utils::verify_claim_proof + # => [is_valid_claim_proof] + + swap drop +end diff --git a/crates/miden-agglayer/asm/bridge/bridge_out.masm b/crates/miden-agglayer/asm/bridge/bridge_out.masm index 9f8f401014..3b8043e7c1 100644 --- a/crates/miden-agglayer/asm/bridge/bridge_out.masm +++ b/crates/miden-agglayer/asm/bridge/bridge_out.masm @@ -160,3 +160,4 @@ pub proc bridge_out exec.create_burn_note # => [] end + diff --git a/crates/miden-agglayer/asm/bridge/crypto_utils.masm b/crates/miden-agglayer/asm/bridge/crypto_utils.masm new file mode 100644 index 0000000000..f0020785d1 --- /dev/null +++ b/crates/miden-agglayer/asm/bridge/crypto_utils.masm @@ -0,0 +1,83 @@ +use miden::core::crypto::hashes::keccak256 + +#! Given the leaf data returns the leaf value. +#! +#! Inputs: [leaf_type, origin_network, ORIGIN_ADDRESS, destination_network, DESTINATION_ADDRESS, amount, METADATA_HASH] +#! Outputs: [LEAF_VALUE] +#! +#! Where: +#! - leaf_type is the leaf type: [0] transfer Ether / ERC20 tokens, [1] message. +#! - origin_network is the origin network identifier. +#! - ORIGIN_ADDRESS is the origin token address (5 elements) +#! - destination_network is the destination network identifier. +#! - DESTINATION_ADDRESS is the destination address (5 elements). +#! - amount is the amount: [0] Amount of tokens/ether, [1] Amount of ether. +#! - METADATA_HASH is the hash of the metadata (8 elements). +#! - LEAF_VALUE is the computed leaf value (8 elements). +#! +#! This function computes the keccak256 hash of the abi.encodePacked data. +#! +#! Invocation: exec +pub proc get_leaf_value + # TODO: implement getLeafValue() + # https://github.com/agglayer/agglayer-contracts/blob/e468f9b0967334403069aa650d9f1164b1731ebb/contracts/v2/lib/DepositContractV2.sol#L22 + + # stubbed out: + push.1.1.1.1 + push.1.1.1.1 + + # exec.keccak256::hash_bytes + # => [LEAF_VALUE[8]] +end + +#! Verify leaf and checks that it has not been claimed. +#! +#! This procedure verifies that a claim proof is valid against the Global Exit Tree (GET) +#! and that the leaf has not been previously claimed. +#! +#! Inputs: +#! Operand stack: [GER_ROOT[8], CLAIM_PROOF_RPO_COMMITMENT, pad(12)] +#! Advice map: { +#! PROOF_DATA_KEY => [ +#! smtProofLocalExitRoot[256], // SMT proof for local exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! smtProofRollupExitRoot[256], // SMT proof for rollup exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! globalIndex[8], // Global index (8 felts, uint256 as 8 u32 felts) +#! mainnetExitRoot[8], // Mainnet exit root hash (8 felts, bytes32 as 8 u32 felts) +#! rollupExitRoot[8], // Rollup exit root hash (8 felts, bytes32 as 8 u32 felts) +#! ], +#! LEAF_DATA_KEY => [ +#! originNetwork[1], // Origin network identifier (1 felt, uint32) +#! originTokenAddress[5], // Origin token address (5 felts, address as 5 u32 felts) +#! destinationNetwork[1], // Destination network identifier (1 felt, uint32) +#! destinationAddress[5], // Destination address (5 felts, address as 5 u32 felts) +#! amount[8], // Amount of tokens (8 felts, uint256 as 8 u32 felts) +#! metadata[8], // ABI encoded metadata (8 felts, fixed size) +#! EMPTY_WORD // padding +#! ], +#! } +#! Outputs: +#! Operand stack: [is_valid] +#! +#! Where: +#! - RPO_CLAIM_NOTE_INPUTS_COMMITMENT is the RPO hash commitment of all claim note inputs +#! - leafType is the leaf type: [0] transfer Ether / ERC20 tokens, [1] message +#! - originNetwork is the origin network identifier (u32 as Felt) +#! - originAddress is the origin address (5 felts representing address) +#! - destinationNetwork is the destination network identifier (u32 as Felt) +#! - destinationAddress is the destination address (5 felts representing address) +#! - amount is the amount of tokens (u256 as Felt) +#! - metadata is the metadata (4 felts representing 4 u32 0 values) +#! - index is the index of the leaf (u32 as Felt) +#! - claimRoot is the claim root (8 felts representing bytes32) +#! - smtProof is the SMT proof data (570 felts) +#! - is_valid is 1 if the leaf is valid and not claimed, 0 otherwise +#! +#! Invocation: exec +pub proc verify_claim_proof + # TODO: Implement actual Global Exit Tree proof verification + + # For now, drop all inputs and return 1 (valid) + dropw dropw dropw dropw + push.1 +end + diff --git a/crates/miden-agglayer/asm/bridge/local_exit_tree.masm b/crates/miden-agglayer/asm/bridge/local_exit_tree.masm index 083b7ed330..89e744507b 100644 --- a/crates/miden-agglayer/asm/bridge/local_exit_tree.masm +++ b/crates/miden-agglayer/asm/bridge/local_exit_tree.masm @@ -116,4 +116,5 @@ pub proc add_asset_message exec.write_mmr_frontier_root # => [] -end \ No newline at end of file +end + diff --git a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm new file mode 100644 index 0000000000..83c41a65bc --- /dev/null +++ b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm @@ -0,0 +1,211 @@ +use miden::agglayer::agglayer_faucet -> agg_faucet +use miden::protocol::account_id +use miden::protocol::active_account +use miden::protocol::active_note +use miden::protocol::note +use miden::core::crypto::hashes::keccak256 +use miden::core::crypto::hashes::rpo256 +use miden::core::mem + +# CONSTANTS +# ================================================================================================= + +const PROOF_DATA_SIZE = 536 +const LEAF_DATA_SIZE = 24 +const OUTPUT_NOTE_SIZE = 8 + +const PROOF_DATA_START_PTR = 0 +const LEAF_DATA_START_PTR = 536 +const OUTPUT_NOTE_DATA_START_PTR = 568 + +const TARGET_FAUCET_PREFIX_MEM_ADDR = 572 +const TARGET_FAUCET_SUFFIX_MEM_ADDR = 573 + +# ERRORS +# ================================================================================================= + +const ERR_CLAIM_TARGET_ACCT_MISMATCH = "CLAIM's target account address and transaction address do not match" + +#! Asserts that the consuming account matches the target agglayer faucet account. +#! +#! This procedure ensures that only the specified agglayer faucet account can consume +#! this CLAIM note. It assumes that the note inputs have already been loaded into memory +#! via active_note::get_inputs. +#! +#! Inputs: [] +#! Output: [] +#! +#! Panics if: +#! - The consuming account ID does not match the target faucet account ID stored in memory +proc assert_aggfaucet_is_consumer + # Load target faucet ID (assumes active_note::get_inputs has been called) + mem_load.TARGET_FAUCET_SUFFIX_MEM_ADDR mem_load.TARGET_FAUCET_PREFIX_MEM_ADDR + # => [target_faucet_prefix, target_faucet_suffix] + + exec.active_account::get_id + # => [account_id_prefix, account_id_suffix, target_faucet_prefix, target_faucet_suffix] + + # ensure only the specified target faucet can consume this CLAIM note, not any other account + exec.account_id::is_equal assert.err=ERR_CLAIM_TARGET_ACCT_MISMATCH + # => [] +end + +#! Reads claim data from memory and inserts it into the advice map under three separate keys. +#! +#! This procedure organizes the claim note data into three logical groups and inserts them +#! into the advice map under separate keys for easier access. +#! +#! Inputs: [] +#! Outputs: [PROOF_DATA_KEY, LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY] +#! +#! Advice map entries created: +#! PROOF_DATA_KEY => [ +#! smtProofLocalExitRoot[256], // SMT proof for local exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! smtProofRollupExitRoot[256], // SMT proof for rollup exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! globalIndex[8], // Global index (8 felts, uint256 as 8 u32 felts) +#! mainnetExitRoot[8], // Mainnet exit root hash (8 felts, bytes32 as 8 u32 felts) +#! rollupExitRoot[8], // Rollup exit root hash (8 felts, bytes32 as 8 u32 felts) +#! ] +#! +#! LEAF_DATA_KEY => [ +#! originNetwork[1], // Origin network identifier (1 felt, uint32) +#! originTokenAddress[5], // Origin token address (5 felts, address as 5 u32 felts) +#! destinationNetwork[1], // Destination network identifier (1 felt, uint32) +#! destinationAddress[5], // Destination address (5 felts, address as 5 u32 felts) +#! amount[8], // Amount of tokens (8 felts, uint256 as 8 u32 felts) +#! metadata[8], // ABI encoded metadata (8 felts, fixed size) +#! EMPTY_WORD // padding +#! ] +#! +#! TODO: Will be removed in future PR +#! OUTPUT_NOTE_DATA_KEY => [ +#! output_p2id_serial_num[4], // P2ID note serial number (4 felts, Word) +#! target_faucet_account_id[2], // Target faucet account ID (2 felts, prefix and suffix) +#! output_note_tag[1], // P2ID output note tag +#! ] +#! +#! Invocation: exec +proc write_claim_data_into_advice_map_by_key + # 1) Get OUTPUT_NOTE_DATA_KEY + push.OUTPUT_NOTE_SIZE push.OUTPUT_NOTE_DATA_START_PTR + exec.rpo256::hash_elements + # => [OUTPUT_NOTE_DATA_KEY] + + push.OUTPUT_NOTE_SIZE add.OUTPUT_NOTE_DATA_START_PTR push.OUTPUT_NOTE_DATA_START_PTR + movdn.5 movdn.5 + # => [OUTPUT_NOTE_DATA_KEY, start_ptr, end_ptr] + + adv.insert_mem + # OS => [OUTPUT_NOTE_DATA_KEY, start_ptr, end_ptr, pad(16)] + # AM => {OUTPUT_NOTE_DATA_KEY: mem[start_ptr..end_ptr] } + + movup.4 drop movup.4 drop + # => [OUTPUT_NOTE_DATA_KEY] + + # 2) Get LEAF_DATA_KEY + push.LEAF_DATA_SIZE push.LEAF_DATA_START_PTR + exec.rpo256::hash_elements + # => [LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY] + + push.LEAF_DATA_SIZE add.LEAF_DATA_START_PTR push.LEAF_DATA_START_PTR + movdn.5 movdn.5 + # => [LEAF_DATA_KEY, start_ptr, end_ptr, OUTPUT_NOTE_DATA_KEY] + + adv.insert_mem + # OS => [LEAF_DATA_KEY, start_ptr, end_ptr] + # AM => {LEAF_DATA_KEY: mem[start_ptr..end_ptr] } + movup.4 drop movup.4 drop + # => [LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY] + + # 3) Get PROOF_DATA_KEY + push.PROOF_DATA_SIZE push.PROOF_DATA_START_PTR + exec.rpo256::hash_elements + # => [PROOF_DATA_KEY, LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY] + + push.PROOF_DATA_SIZE push.PROOF_DATA_START_PTR + movdn.5 movdn.5 + # => [PROOF_DATA_KEY, start_ptr, end_ptr, LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY] + + adv.insert_mem + # OS => [PROOF_DATA_KEY, start_ptr, end_ptr, LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY] + # AM => {PROOF_DATA_KEY: mem[start_ptr..end_ptr] } + + movup.4 drop movup.4 drop + # => [PROOF_DATA_KEY, LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY] +end + +#! Agglayer Faucet CLAIM script: claims assets by calling the agglayer faucet's claim function. +#! +#! This note can only be consumed by the specific agglayer faucet account whose ID is provided +#! in the note inputs (target_faucet_account_id). Upon consumption, it will create a P2ID note. +#! +#! Requires that the account exposes: +#! - agglayer::agglayer_faucet::claim procedure. +#! +#! Inputs: [ARGS, pad(12)] +#! Outputs: [pad(16)] +#! +#! NoteInputs layout (575 felts total): +#! - smtProofLocalExitRoot [0..255] : 256 felts +#! - smtProofRollupExitRoot [256..511]: 256 felts +#! - globalIndex [512..519]: 8 felts +#! - mainnetExitRoot [520..527]: 8 felts +#! - rollupExitRoot [528..535]: 8 felts +#! - originNetwork [536] : 1 felt +#! - originTokenAddress [537..541]: 5 felts +#! - destinationNetwork [542] : 1 felt +#! - destinationAddress [543..547]: 5 felts +#! - amount [548..555]: 8 felts +#! - metadata [556..563]: 8 felts +#! - EMPTY_WORD [564..567]: 4 felts +#! - output_p2id_serial_num [568..571]: 4 felts +#! - target_faucet_account_id [572..573]: 2 felts +#! - output_note_tag [574] : 1 felt +#! +#! Where: +#! - smtProofLocalExitRoot: SMT proof for local exit root (bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! - smtProofRollupExitRoot: SMT proof for rollup exit root (bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! - globalIndex: Global index (uint256 as 8 u32 felts). This is a packed "locator" for the leaf being claimed: +#! - mainnetFlag (1 bit): 1 = leaf came from L1 (Mainnet Exit Tree), 0 = leaf came from an L2 rollup +#! - rollupIndex (32 bits): which rollup (only used when mainnetFlag=0) +#! - localRootIndex (32 bits): leaf index / depositCount in the origin chain's Local Exit Tree +#! - Top 191 bits are ignored (not required to be zero), so indexers must decode it exactly like the contract does +#! - mainnetExitRoot: Mainnet exit root hash (bytes32 as 8 u32 felts) +#! - rollupExitRoot: Rollup exit root hash (bytes32 as 8 u32 felts) +#! - originNetwork: Origin network identifier (uint32) +#! - originTokenAddress: Origin token address (address as 5 u32 felts) +#! - destinationNetwork: Destination network identifier (uint32) +#! - destinationAddress: Destination address (address as 5 u32 felts) +#! - amount: Amount of tokens (uint256 as 8 u32 felts) +#! - metadata: ABI encoded metadata (fixed size) +#! - EMPTY_WORD: Padding word +#! - output_p2id_serial_num: P2ID note serial number (Word) +#! - target_faucet_account_id: Target agglayer faucet account ID (prefix and suffix). Only this specific +#! account can consume the note - any other account will cause a panic. +#! - output_note_tag: P2ID output note tag +#! +#! Panics if: +#! - account does not expose claim procedure. +#! - target faucet account ID does not match the consuming account ID. +begin + dropw + # => [pad(16)] + + # Load CLAIM note inputs into memory, starting at address 0 + push.0 exec.active_note::get_inputs drop drop + # => [pad(16)] + + # Check consuming account == aggfaucet + exec.assert_aggfaucet_is_consumer + # => [pad(16)] + + exec.write_claim_data_into_advice_map_by_key + # => [PROOF_DATA_KEY, LEAF_DATA_KEY, OUTPUT_NOTE_DATA_KEY, pad(4)] + + # Call the Aggfaucet Claim procedure + call.agg_faucet::claim + # => [pad(16), pad(12)] + + dropw dropw dropw + # => [pad(16)] +end diff --git a/crates/miden-agglayer/build.rs b/crates/miden-agglayer/build.rs index fda54fd73c..fbd2cd06e7 100644 --- a/crates/miden-agglayer/build.rs +++ b/crates/miden-agglayer/build.rs @@ -47,16 +47,11 @@ fn main() -> Result<()> { // set target directory to {OUT_DIR}/assets let target_dir = Path::new(&build_dir).join(ASSETS_DIR); - let mut assembler = TransactionKernel::assembler(); - - // compile bridge components first and add the library to the assembler - let agglayer_lib = compile_bridge_components( - &source_dir.join(ASM_BRIDGE_DIR), - &target_dir.join(ASM_BRIDGE_DIR), - assembler.clone(), - )?; + // compile agglayer library + let agglayer_lib = + compile_agglayer_lib(&source_dir, &target_dir, TransactionKernel::assembler())?; - // Add the agglayer library to the assembler for note scripts compilation + let mut assembler = TransactionKernel::assembler(); assembler.link_static_library(agglayer_lib)?; // compile note scripts @@ -71,6 +66,31 @@ fn main() -> Result<()> { Ok(()) } +// COMPILE AGGLAYER LIB +// ================================================================================================ + +/// Reads the MASM files from "{source_dir}/bridge" directory, compiles them into a Miden +/// assembly library, saves the library into "{target_dir}/agglayer.masl", and returns the compiled +/// library. +fn compile_agglayer_lib( + source_dir: &Path, + target_dir: &Path, + mut assembler: Assembler, +) -> Result { + let source_dir = source_dir.join(ASM_BRIDGE_DIR); + + // Add the miden-standards library to the assembler so agglayer components can use it + let standards_lib = miden_standards::StandardsLib::default(); + assembler.link_static_library(standards_lib)?; + + let agglayer_lib = assembler.assemble_library_from_dir(source_dir, "miden::agglayer")?; + + let output_file = target_dir.join("agglayer").with_extension(Library::LIBRARY_EXTENSION); + agglayer_lib.write_to_file(output_file).into_diagnostic()?; + + Ok(agglayer_lib) +} + // COMPILE EXECUTABLE MODULES // ================================================================================================ @@ -111,12 +131,14 @@ fn compile_note_scripts( Ok(()) } -// COMPILE BRIDGE COMPONENTS +// COMPILE ACCOUNT COMPONENTS (DEPRECATED) // ================================================================================================ /// Compiles the bridge components in `source_dir` into MASL libraries and stores the compiled /// files in `target_dir`. -fn compile_bridge_components( +/// +/// NOTE: This function is deprecated and replaced by compile_agglayer_lib +fn _compile_bridge_components( source_dir: &Path, target_dir: &Path, mut assembler: Assembler, @@ -132,9 +154,7 @@ fn compile_bridge_components( // Compile all components together as a single library under the "miden::agglayer" namespace // This allows cross-references between components (e.g., bridge_out using // miden::agglayer::local_exit_tree) - let agglayer_library = assembler - .assemble_library_from_dir(source_dir, "miden::agglayer") - .expect("library assembly should succeed"); + let agglayer_library = assembler.assemble_library_from_dir(source_dir, "miden::agglayer")?; // Write the combined library let library_path = target_dir.join("agglayer").with_extension(Library::LIBRARY_EXTENSION); diff --git a/crates/miden-agglayer/src/errors/agglayer.rs b/crates/miden-agglayer/src/errors/agglayer.rs index d9ec4a3875..8f70572329 100644 --- a/crates/miden-agglayer/src/errors/agglayer.rs +++ b/crates/miden-agglayer/src/errors/agglayer.rs @@ -14,5 +14,11 @@ pub const ERR_B2AGG_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_s /// Error Message: "B2AGG script expects exactly 6 note inputs" pub const ERR_B2AGG_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("B2AGG script expects exactly 6 note inputs"); +/// Error Message: "CLAIM's target account address and transaction address do not match" +pub const ERR_CLAIM_TARGET_ACCT_MISMATCH: MasmError = MasmError::from_static_str("CLAIM's target account address and transaction address do not match"); + +/// Error Message: "invalid claim proof" +pub const ERR_INVALID_CLAIM_PROOF: MasmError = MasmError::from_static_str("invalid claim proof"); + /// Error Message: "maximum scaling factor is 18" pub const ERR_SCALE_AMOUNT_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("maximum scaling factor is 18"); diff --git a/crates/miden-agglayer/src/lib.rs b/crates/miden-agglayer/src/lib.rs index c8d8a1be68..65765d1482 100644 --- a/crates/miden-agglayer/src/lib.rs +++ b/crates/miden-agglayer/src/lib.rs @@ -7,13 +7,40 @@ use alloc::vec::Vec; use miden_assembly::Library; use miden_assembly::utils::Deserializable; -use miden_core::Program; -use miden_protocol::account::{AccountComponent, StorageSlot}; +use miden_core::{Felt, FieldElement, Program, Word}; +use miden_protocol::NoteError; +use miden_protocol::account::{ + Account, + AccountBuilder, + AccountComponent, + AccountId, + AccountStorageMode, + AccountType, + StorageSlot, + StorageSlotName, +}; +use miden_protocol::asset::TokenSymbol; +use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::note::{ + Note, + NoteAssets, + NoteExecutionHint, + NoteInputs, + NoteMetadata, + NoteRecipient, + NoteScript, + NoteTag, + NoteType, +}; +use miden_standards::account::auth::NoAuth; +use miden_standards::account::faucets::NetworkFungibleFaucet; use miden_utils_sync::LazyLock; pub mod errors; pub mod utils; +use utils::{bytes32_to_felts, ethereum_address_to_felts}; + // AGGLAYER NOTE SCRIPTS // ================================================================================================ @@ -28,12 +55,24 @@ pub fn b2agg_script() -> Program { B2AGG_SCRIPT.clone() } +// Initialize the CLAIM note script only once +static CLAIM_SCRIPT: LazyLock = LazyLock::new(|| { + let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/note_scripts/CLAIM.masb")); + let program = Program::read_from_bytes(bytes).expect("Shipped CLAIM script is well-formed"); + NoteScript::new(program) +}); + +/// Returns the CLAIM (Bridge from AggLayer) note script. +pub fn claim_script() -> NoteScript { + CLAIM_SCRIPT.clone() +} + // AGGLAYER ACCOUNT COMPONENTS // ================================================================================================ // Initialize the unified AggLayer library only once static AGGLAYER_LIBRARY: LazyLock = LazyLock::new(|| { - let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/bridge/agglayer.masl")); + let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer.masl")); Library::read_from_bytes(bytes).expect("Shipped AggLayer library is well-formed") }); @@ -82,6 +121,47 @@ pub fn bridge_out_component(storage_slots: Vec) -> AccountComponent .with_supports_all_types() } +/// Returns the Bridge In Library. +/// +/// Note: This is now the same as agglayer_library() since all agglayer components +/// are compiled into a single library. +pub fn bridge_in_library() -> Library { + agglayer_library() +} + +/// Creates a Bridge In component with the specified storage slots. +/// +/// This component uses the agglayer library and can be added to accounts +/// that need to bridge assets in from the AggLayer. +pub fn bridge_in_component(storage_slots: Vec) -> AccountComponent { + let library = bridge_in_library(); + + AccountComponent::new(library, storage_slots) + .expect("bridge_in component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} + +/// Returns the Agglayer Faucet Library. +/// +/// Note: This is now the same as agglayer_library() since all agglayer components +/// are compiled into a single library. +pub fn agglayer_faucet_library() -> Library { + agglayer_library() +} + +/// Creates an Agglayer Faucet component with the specified storage slots. +/// +/// This component combines network faucet functionality with bridge validation +/// via Foreign Procedure Invocation (FPI). It provides a "claim" procedure that +/// validates CLAIM notes against a bridge MMR account before minting assets. +pub fn agglayer_faucet_component(storage_slots: Vec) -> AccountComponent { + let library = agglayer_faucet_library(); + + AccountComponent::new(library, storage_slots) + .expect("agglayer_faucet component should satisfy the requirements of a valid account component") + .with_supports_all_types() +} + /// Creates a combined Bridge Out component that includes both bridge_out and local_exit_tree /// modules. /// @@ -108,3 +188,511 @@ pub fn asset_conversion_component(storage_slots: Vec) -> AccountCom .expect("asset_conversion component should satisfy the requirements of a valid account component") .with_supports_all_types() } + +// AGGLAYER ACCOUNT CREATION HELPERS +// ================================================================================================ + +/// Creates a bridge account component with the standard bridge storage slot. +/// +/// This is a convenience function that creates the bridge storage slot with the standard +/// name "miden::agglayer::bridge" and returns the bridge_out component. +/// +/// # Returns +/// Returns an [`AccountComponent`] configured for bridge operations with MMR validation. +pub fn create_bridge_account_component() -> AccountComponent { + let bridge_storage_slot_name = StorageSlotName::new("miden::agglayer::bridge") + .expect("Bridge storage slot name should be valid"); + let bridge_storage_slots = vec![StorageSlot::with_empty_map(bridge_storage_slot_name)]; + bridge_out_component(bridge_storage_slots) +} + +/// Creates an agglayer faucet account component with the specified configuration. +/// +/// This function creates all the necessary storage slots for an agglayer faucet: +/// - Network faucet metadata slot (max_supply, decimals, token_symbol) +/// - Bridge account reference slot for FPI validation +/// +/// # Parameters +/// - `token_symbol`: The symbol for the fungible token (e.g., "AGG") +/// - `decimals`: Number of decimal places for the token +/// - `max_supply`: Maximum supply of the token +/// - `bridge_account_id`: The account ID of the bridge account for validation +/// +/// # Returns +/// Returns an [`AccountComponent`] configured for agglayer faucet operations. +/// +/// # Panics +/// Panics if the token symbol is invalid or storage slot names are malformed. +pub fn create_agglayer_faucet_component( + token_symbol: &str, + decimals: u8, + max_supply: Felt, + bridge_account_id: AccountId, +) -> AccountComponent { + // Create network faucet metadata slot: [max_supply, decimals, token_symbol, 0] + let token_symbol = TokenSymbol::new(token_symbol).expect("Token symbol should be valid"); + let metadata_word = + Word::new([max_supply, Felt::from(decimals), token_symbol.into(), FieldElement::ZERO]); + let metadata_slot = + StorageSlot::with_value(NetworkFungibleFaucet::metadata_slot().clone(), metadata_word); + + // Create agglayer-specific bridge storage slot + let bridge_account_id_word = Word::new([ + Felt::new(0), + Felt::new(0), + bridge_account_id.suffix(), + bridge_account_id.prefix().as_felt(), + ]); + let agglayer_storage_slot_name = StorageSlotName::new("miden::agglayer::faucet") + .expect("Agglayer faucet storage slot name should be valid"); + let bridge_slot = StorageSlot::with_value(agglayer_storage_slot_name, bridge_account_id_word); + + // Combine all storage slots for the agglayer faucet component + let agglayer_storage_slots = vec![metadata_slot, bridge_slot]; + agglayer_faucet_component(agglayer_storage_slots) +} + +/// Creates a complete bridge account builder with the standard configuration. +pub fn create_bridge_account_builder(seed: Word) -> AccountBuilder { + let bridge_component = create_bridge_account_component(); + Account::builder(seed.into()) + .storage_mode(AccountStorageMode::Public) + .with_component(bridge_component) +} + +/// Creates a new bridge account with the standard configuration. +/// +/// This creates a new account suitable for production use. +pub fn create_bridge_account(seed: Word) -> Account { + create_bridge_account_builder(seed) + .with_auth_component(AccountComponent::from(NoAuth)) + .build() + .expect("Bridge account should be valid") +} + +/// Creates an existing bridge account with the standard configuration. +/// +/// This creates an existing account suitable for testing scenarios. +#[cfg(any(feature = "testing", test))] +pub fn create_existing_bridge_account(seed: Word) -> Account { + create_bridge_account_builder(seed) + .with_auth_component(AccountComponent::from(NoAuth)) + .build_existing() + .expect("Bridge account should be valid") +} + +/// Creates a complete agglayer faucet account builder with the specified configuration. +pub fn create_agglayer_faucet_builder( + seed: Word, + token_symbol: &str, + decimals: u8, + max_supply: Felt, + bridge_account_id: AccountId, +) -> AccountBuilder { + let agglayer_component = + create_agglayer_faucet_component(token_symbol, decimals, max_supply, bridge_account_id); + + Account::builder(seed.into()) + .account_type(AccountType::FungibleFaucet) + .storage_mode(AccountStorageMode::Network) + .with_component(agglayer_component) +} + +/// Creates a new agglayer faucet account with the specified configuration. +/// +/// This creates a new account suitable for production use. +pub fn create_agglayer_faucet( + seed: Word, + token_symbol: &str, + decimals: u8, + max_supply: Felt, + bridge_account_id: AccountId, +) -> Account { + create_agglayer_faucet_builder(seed, token_symbol, decimals, max_supply, bridge_account_id) + .with_auth_component(AccountComponent::from(NoAuth)) + .build() + .expect("Agglayer faucet account should be valid") +} + +/// Creates an existing agglayer faucet account with the specified configuration. +/// +/// This creates an existing account suitable for testing scenarios. +#[cfg(any(feature = "testing", test))] +pub fn create_existing_agglayer_faucet( + seed: Word, + token_symbol: &str, + decimals: u8, + max_supply: Felt, + bridge_account_id: AccountId, +) -> Account { + create_agglayer_faucet_builder(seed, token_symbol, decimals, max_supply, bridge_account_id) + .with_auth_component(AccountComponent::from(NoAuth)) + .build_existing() + .expect("Agglayer faucet account should be valid") +} + +// AGGLAYER NOTE CREATION HELPERS +// ================================================================================================ + +/// Parameters for creating a CLAIM note. +/// +/// This struct groups all the parameters needed to create a CLAIM note that exactly +/// matches the agglayer claimAsset function signature. +pub struct ClaimNoteParams<'a, R: FeltRng> { + /// AGGLAYER claimAsset function parameters + /// SMT proof for local exit root (bytes32\[_DEPOSIT_CONTRACT_TREE_DEPTH\]) + pub smt_proof_local_exit_root: Vec, + /// SMT proof for rollup exit root (bytes32\[_DEPOSIT_CONTRACT_TREE_DEPTH\]) + pub smt_proof_rollup_exit_root: Vec, + /// Global index (uint256 as 8 u32 felts) + pub global_index: [Felt; 8], + /// Mainnet exit root hash (bytes32 as 32-byte array) + pub mainnet_exit_root: &'a [u8; 32], + /// Rollup exit root hash (bytes32 as 32-byte array) + pub rollup_exit_root: &'a [u8; 32], + /// Origin network identifier (uint32) + pub origin_network: Felt, + /// Origin token address (address as 20-byte array) + pub origin_token_address: &'a [u8; 20], + /// Destination network identifier (uint32) + pub destination_network: Felt, + /// Destination address (address as 20-byte array) + pub destination_address: &'a [u8; 20], + /// Amount of tokens (uint256 as 8 u32 felts) + pub amount: [Felt; 8], + /// ABI encoded metadata (fixed size of 8 felts) + pub metadata: [Felt; 8], + /// CLAIM note required parameters + /// CLAIM note sender account id + pub claim_note_creator_account_id: AccountId, + /// Agglayer faucet AccountId + pub agglayer_faucet_account_id: AccountId, + /// Output P2ID note tag + pub output_note_tag: NoteTag, + /// P2ID note serial number (4 felts as Word) + pub p2id_serial_number: Word, + /// TODO: remove and use destination_address: [u8; 20] + pub destination_account_id: AccountId, + /// RNG for creating CLAIM note serial number + pub rng: &'a mut R, +} + +/// Generates a CLAIM note - a note that instructs an agglayer faucet to validate and mint assets. +/// +/// # Parameters +/// - `params`: The parameters for creating the CLAIM note (including RNG) +/// +/// # Errors +/// Returns an error if note creation fails. +pub fn create_claim_note(params: ClaimNoteParams<'_, R>) -> Result { + // Validate SMT proof lengths - each should be 256 felts (32 bytes32 values * 8 u32 per bytes32) + if params.smt_proof_local_exit_root.len() != 256 { + return Err(NoteError::other(alloc::format!( + "SMT proof local exit root must be exactly 256 felts, got {}", + params.smt_proof_local_exit_root.len() + ))); + } + if params.smt_proof_rollup_exit_root.len() != 256 { + return Err(NoteError::other(alloc::format!( + "SMT proof rollup exit root must be exactly 256 felts, got {}", + params.smt_proof_rollup_exit_root.len() + ))); + } + // Create claim inputs matching exactly the agglayer claimAsset function parameters + let mut claim_inputs = vec![]; + + // 1) PROOF DATA + // smtProofLocalExitRoot (256 felts) - first SMT proof parameter + claim_inputs.extend(params.smt_proof_local_exit_root); + // smtProofRollupExitRoot (256 felts) - second SMT proof parameter + claim_inputs.extend(params.smt_proof_rollup_exit_root); + + // globalIndex (uint256 as 8 u32 felts) + claim_inputs.extend(params.global_index); + + // mainnetExitRoot (bytes32 as 8 u32 felts) + let mainnet_exit_root_felts = bytes32_to_felts(params.mainnet_exit_root); + claim_inputs.extend(mainnet_exit_root_felts); + + // rollupExitRoot (bytes32 as 8 u32 felts) + let rollup_exit_root_felts = bytes32_to_felts(params.rollup_exit_root); + claim_inputs.extend(rollup_exit_root_felts); + + // 2) LEAF DATA + // originNetwork (uint32 as Felt) + claim_inputs.push(params.origin_network); + + // originTokenAddress (address as 5 u32 felts) + let origin_token_address_felts = ethereum_address_to_felts(params.origin_token_address); + claim_inputs.extend(origin_token_address_felts); + + // destinationNetwork (uint32 as Felt) + claim_inputs.push(params.destination_network); + + // destinationAddress (address as 5 u32 felts) + // Use AccountId prefix and suffix directly to get [suffix, prefix, 0, 0, 0] + // TODO: refactor to use destination_address: [u8; 20] instead once conversion function + // exists [u8; 20] -> [address as 5 Felts] + let destination_address_felts = vec![ + params.destination_account_id.prefix().as_felt(), + params.destination_account_id.suffix(), + Felt::new(0), + Felt::new(0), + Felt::new(0), + ]; + claim_inputs.extend(destination_address_felts); + + // amount (uint256 as 8 u32 felts) + claim_inputs.extend(params.amount); + + // metadata (fixed size of 8 felts) + claim_inputs.extend(params.metadata); + + let padding = vec![Felt::ZERO; 4]; + claim_inputs.extend(padding); + + // 3) CLAIM NOTE DATA + // TODO: deterministically compute serial number of p2id hash(GER, leaf index) + // output_p2id_serial_num (4 felts as Word) + claim_inputs.extend(params.p2id_serial_number); + + // agglayer_faucet_account_id (2 felts: prefix and suffix) + claim_inputs.push(params.agglayer_faucet_account_id.prefix().as_felt()); + claim_inputs.push(params.agglayer_faucet_account_id.suffix()); + + // output note tag + claim_inputs.push(params.output_note_tag.as_u32().into()); + + let inputs = NoteInputs::new(claim_inputs)?; + + // Use a default tag since we don't have agg_faucet_id anymore + let tag = NoteTag::for_local_use_case(0, 0)?; + + let claim_script = claim_script(); + let serial_num = params.rng.draw_word(); + + let note_type = NoteType::Public; + let execution_hint = NoteExecutionHint::always(); + + // Use a default sender since we don't have sender anymore - create from destination address + let metadata = NoteMetadata::new( + params.claim_note_creator_account_id, + note_type, + tag, + execution_hint, + Felt::ZERO, + )?; + let assets = NoteAssets::new(vec![])?; + let recipient = NoteRecipient::new(serial_num, claim_script, inputs); + + Ok(Note::new(assets, metadata, recipient)) +} + +// CONVERSION FUNCTIONS +// ================================================================================================ + +/// Converts an AccountId (u32 prefix, u32 suffix) into a bytes20 EVM address. +/// +/// The conversion uses the format: [prefix, suffix, 0, 0, 0] as 5 u32 values, +/// then takes the first 20 bytes to form the EVM address. +pub fn account_id_to_evm_address(account_id: AccountId) -> [u8; 20] { + let mut address = [0u8; 20]; + + // Convert prefix and suffix to u64, then to bytes (big-endian) + let prefix_u64 = account_id.prefix().as_felt().as_int(); + let suffix_u64 = account_id.suffix().as_int(); + + let prefix_bytes = prefix_u64.to_be_bytes(); + let suffix_bytes = suffix_u64.to_be_bytes(); + + // Copy last 4 bytes from prefix (u32 portion) + address[0..4].copy_from_slice(&prefix_bytes[4..8]); + // Copy last 4 bytes from suffix (u32 portion) + address[4..8].copy_from_slice(&suffix_bytes[4..8]); + // Remaining 12 bytes stay as zeros + + address +} + +/// Converts a bytes20 EVM address into an AccountId. +/// +/// The conversion extracts the first 8 bytes as prefix (u32) and suffix (u32), +/// with the remaining bytes ignored (treated as zeros). +pub fn evm_address_to_account_id(address: &[u8; 20]) -> AccountId { + // Extract first 8 bytes and convert to u32 values + let mut prefix_bytes = [0u8; 8]; + let mut suffix_bytes = [0u8; 8]; + + // Copy first 4 bytes to prefix (pad with zeros) + prefix_bytes[4..8].copy_from_slice(&address[0..4]); + // Copy next 4 bytes to suffix (pad with zeros) + suffix_bytes[4..8].copy_from_slice(&address[4..8]); + + let prefix = u64::from_be_bytes(prefix_bytes); + let suffix = u64::from_be_bytes(suffix_bytes); + + // Create AccountId from the extracted values + // Note: This creates a basic account ID - in practice you might want to use + // a specific account type and storage mode + AccountId::new_unchecked([Felt::new(prefix), Felt::new(suffix)]) +} + +/// Converts an AccountId to a bytes20 address that will produce [prefix, suffix, 0, 0, 0] +/// when processed by ethereum_address_to_felts(). +/// +/// This function creates a 20-byte address where: +/// - Bytes 0-3: AccountId prefix as u32 (big-endian) +/// - Bytes 4-7: AccountId suffix as u32 (big-endian) +/// - Bytes 8-19: Zero padding +/// +/// When ethereum_address_to_felts() processes this address, it will extract: +/// - u32\[0\] from bytes 0-3: prefix +/// - u32\[1\] from bytes 4-7: suffix +/// - u32\[2\] from bytes 8-11: zeros +/// - u32\[3\] from bytes 12-15: zeros +/// - u32\[4\] from bytes 16-19: zeros +/// +/// This results in [prefix, suffix, 0, 0, 0] as desired. +pub fn account_id_to_destination_bytes(account_id: AccountId) -> [u8; 20] { + let mut address = [0u8; 20]; + + // Get prefix and suffix as u64 values, then convert to u32 + let prefix = account_id.prefix().as_felt().as_int() as u32; + let suffix = account_id.suffix().as_int() as u32; + + // Pack prefix into first 4 bytes (u32, big-endian) + address[0..4].copy_from_slice(&prefix.to_be_bytes()); + + // Pack suffix into next 4 bytes (u32, big-endian) + address[4..8].copy_from_slice(&suffix.to_be_bytes()); + + // Remaining 12 bytes stay as zeros + // This will result in [prefix, suffix, 0, 0, 0] when processed by ethereum_address_to_felts() + + address +} + +// TESTING HELPERS +// ================================================================================================ + +#[cfg(any(feature = "testing", test))] +/// Type alias for the complex return type of claim_note_test_inputs. +/// +/// Contains: +/// - smt_proof_local_exit_root: `Vec` (256 felts) +/// - smt_proof_rollup_exit_root: `Vec` (256 felts) +/// - global_index: [Felt; 8] +/// - mainnet_exit_root: [u8; 32] +/// - rollup_exit_root: [u8; 32] +/// - origin_network: Felt +/// - origin_token_address: [u8; 20] +/// - destination_network: Felt +/// - destination_address: [u8; 20] +/// - amount: [Felt; 8] +/// - metadata: [Felt; 8] +pub type ClaimNoteTestInputs = ( + Vec, + Vec, + [Felt; 8], + [u8; 32], + [u8; 32], + Felt, + [u8; 20], + Felt, + [u8; 20], + [Felt; 8], + [Felt; 8], +); + +#[cfg(any(feature = "testing", test))] +/// Returns dummy test inputs for creating CLAIM notes. +/// +/// This is a convenience function for testing that provides realistic dummy data +/// for all the agglayer claimAsset function inputs. +/// +/// # Parameters +/// - `amount`: The amount as a single Felt for Miden operations +/// - `destination_account_id`: The destination account ID to convert to address bytes +/// +/// # Returns +/// A tuple containing: +/// - smt_proof_local_exit_root: `Vec` (256 felts) +/// - smt_proof_rollup_exit_root: `Vec` (256 felts) +/// - global_index: [Felt; 8] +/// - mainnet_exit_root: [u8; 32] +/// - rollup_exit_root: [u8; 32] +/// - origin_network: Felt +/// - origin_token_address: [u8; 20] +/// - destination_network: Felt +/// - destination_address: [u8; 20] +/// - amount: [Felt; 8] +/// - metadata: [Felt; 8] +pub fn claim_note_test_inputs( + amount: Felt, + destination_account_id: AccountId, +) -> ClaimNoteTestInputs { + // Create SMT proofs with 256 felts each (32 bytes32 values * 8 u32 per bytes32) + let smt_proof_local_exit_root = vec![Felt::new(0); 256]; + let smt_proof_rollup_exit_root = vec![Felt::new(0); 256]; + let global_index = [ + Felt::new(12345), + Felt::new(0), + Felt::new(0), + Felt::new(0), + Felt::new(0), + Felt::new(0), + Felt::new(0), + Felt::new(0), + ]; + + let mainnet_exit_root: [u8; 32] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, + ]; + + let rollup_exit_root: [u8; 32] = [ + 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, + ]; + + let origin_network = Felt::new(1); + + let origin_token_address: [u8; 20] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, + ]; + + let destination_network = Felt::new(2); + + // Convert AccountId to destination address bytes + let destination_address = account_id_to_destination_bytes(destination_account_id); + + // Convert amount Felt to u256 array for agglayer + let amount_u256 = [ + amount, + Felt::new(0), + Felt::new(0), + Felt::new(0), + Felt::new(0), + Felt::new(0), + Felt::new(0), + Felt::new(0), + ]; + let metadata: [Felt; 8] = [Felt::new(0); 8]; + + ( + smt_proof_local_exit_root, + smt_proof_rollup_exit_root, + global_index, + mainnet_exit_root, + rollup_exit_root, + origin_network, + origin_token_address, + destination_network, + destination_address, + amount_u256, + metadata, + ) +} diff --git a/crates/miden-agglayer/src/utils.rs b/crates/miden-agglayer/src/utils.rs index b3b56dfdf8..3b8cd683c2 100644 --- a/crates/miden-agglayer/src/utils.rs +++ b/crates/miden-agglayer/src/utils.rs @@ -20,6 +20,7 @@ pub fn felts_to_u256_bytes(limbs: [Felt; 8]) -> [u8; 32] { bytes } + /// Converts an Ethereum address (20 bytes) into a vector of 5 Felt values. /// /// An Ethereum address is 20 bytes, which we split into 5 u32 values (4 bytes each). @@ -117,6 +118,68 @@ pub fn ethereum_address_string_to_felts(address_str: &str) -> Result, Ok(ethereum_address_to_felts(&address_bytes)) } +/// Converts a bytes32 value (32 bytes) into a vector of 8 Felt values. +/// +/// A bytes32 value is 32 bytes, which we split into 8 u32 values (4 bytes each). +/// The bytes are distributed as follows: +/// - u32\[0\]: bytes 0-3 +/// - u32\[1\]: bytes 4-7 +/// - u32\[2\]: bytes 8-11 +/// - u32\[3\]: bytes 12-15 +/// - u32\[4\]: bytes 16-19 +/// - u32\[5\]: bytes 20-23 +/// - u32\[6\]: bytes 24-27 +/// - u32\[7\]: bytes 28-31 +/// +/// # Arguments +/// * `bytes32` - A 32-byte value (e.g., hash, root) +/// +/// # Returns +/// A vector of 8 Felt values representing the bytes32 value +/// +/// # Panics +/// Panics if the input is not exactly 32 bytes +pub fn bytes32_to_felts(bytes32: &[u8; 32]) -> Vec { + let mut result = Vec::with_capacity(8); + + // Convert each 4-byte chunk to a u32 (big-endian) + for i in 0..8 { + let start = i * 4; + let chunk = &bytes32[start..start + 4]; + let value = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + result.push(Felt::new(value as u64)); + } + + result +} + +/// Converts a vector of 8 Felt values back into a 32-byte array. +/// +/// # Arguments +/// * `felts` - A vector of 8 Felt values representing a bytes32 value +/// +/// # Returns +/// A Result containing a 32-byte array, or an error string +/// +/// # Errors +/// Returns an error if the vector doesn't contain exactly 8 felts +pub fn felts_to_bytes32(felts: &[Felt]) -> Result<[u8; 32], String> { + if felts.len() != 8 { + return Err(alloc::format!("Expected 8 felts for bytes32, got {}", felts.len())); + } + + let mut bytes32 = [0u8; 32]; + + for (i, felt) in felts.iter().enumerate() { + let value = felt.as_int() as u32; + let bytes = value.to_be_bytes(); + let start = i * 4; + bytes32[start..start + 4].copy_from_slice(&bytes); + } + + Ok(bytes32) +} + #[cfg(test)] mod tests { use alloc::vec; @@ -172,4 +235,25 @@ mod tests { let result = ethereum_address_string_to_felts(address_str); assert!(result.is_err()); } + + #[test] + fn test_bytes32_to_felts_basic() { + let bytes32: [u8; 32] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + ]; + + let result = bytes32_to_felts(&bytes32); + assert_eq!(result.len(), 8); + assert_eq!(result[0], Felt::new(0x12345678)); + assert_eq!(result[1], Felt::new(0x9abcdef0)); + } + + #[test] + fn test_felts_to_bytes32_invalid_length() { + let felts = vec![Felt::new(1), Felt::new(2)]; // Only 2 felts + let result = felts_to_bytes32(&felts); + assert!(result.is_err()); + } } diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index 5e94bc5df9..c893f1fb2b 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -713,9 +713,8 @@ impl MockChain { /// Gets foreign account inputs to execute FPI transactions. /// - /// Only used internally and so does not need to be public. - #[cfg(test)] - pub(crate) fn get_foreign_account_inputs( + /// Used in tests to get foreign account inputs for FPI calls. + pub fn get_foreign_account_inputs( &self, account_id: AccountId, ) -> anyhow::Result<(Account, AccountWitness)> { diff --git a/crates/miden-testing/tests/agglayer/bridge_in.rs b/crates/miden-testing/tests/agglayer/bridge_in.rs new file mode 100644 index 0000000000..0b428ce430 --- /dev/null +++ b/crates/miden-testing/tests/agglayer/bridge_in.rs @@ -0,0 +1,205 @@ +extern crate alloc; + +use core::slice; + +use miden_agglayer::{ + ClaimNoteParams, + claim_note_test_inputs, + create_claim_note, + create_existing_agglayer_faucet, + create_existing_bridge_account, +}; +use miden_protocol::Felt; +use miden_protocol::account::Account; +use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::note::{ + Note, + NoteAssets, + NoteExecutionHint, + NoteInputs, + NoteMetadata, + NoteRecipient, + NoteTag, + NoteType, +}; +use miden_protocol::transaction::OutputNote; +use miden_standards::account::wallets::BasicWallet; +use miden_standards::note::WellKnownNote; +use miden_testing::{AccountState, Auth, MockChain}; +use rand::Rng; + +/// Tests the bridge-in flow: CLAIM note -> Aggfaucet (FPI to Bridge) -> P2ID note created. +#[tokio::test] +async fn test_bridge_in_claim_to_p2id() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + // CREATE BRIDGE ACCOUNT (with bridge_out component for MMR validation) + // -------------------------------------------------------------------------------------------- + let bridge_seed = builder.rng_mut().draw_word(); + let bridge_account = create_existing_bridge_account(bridge_seed); + builder.add_account(bridge_account.clone())?; + + // CREATE AGGLAYER FAUCET ACCOUNT (with agglayer_faucet component) + // -------------------------------------------------------------------------------------------- + let token_symbol = "AGG"; + let decimals = 8u8; + let max_supply = Felt::new(1000000); + let agglayer_faucet_seed = builder.rng_mut().draw_word(); + + let agglayer_faucet = create_existing_agglayer_faucet( + agglayer_faucet_seed, + token_symbol, + decimals, + max_supply, + bridge_account.id(), + ); + builder.add_account(agglayer_faucet.clone())?; + + // CREATE USER ACCOUNT TO RECEIVE P2ID NOTE + // -------------------------------------------------------------------------------------------- + let user_account_builder = + Account::builder(builder.rng_mut().random()).with_component(BasicWallet); + let user_account = builder.add_account_from_builder( + Auth::IncrNonce, + user_account_builder, + AccountState::Exists, + )?; + + // CREATE CLAIM NOTE WITH P2ID OUTPUT NOTE DETAILS + // -------------------------------------------------------------------------------------------- + + // Define amount values for the test + let amount_felt = Felt::new(100); + + // Create CLAIM note using the helper function with new agglayer claimAsset inputs + let ( + smt_proof_local_exit_root, + smt_proof_rollup_exit_root, + global_index, + mainnet_exit_root, + rollup_exit_root, + origin_network, + origin_token_address, + destination_network, + destination_address, + amount_u256, + metadata, + ) = claim_note_test_inputs(amount_felt, user_account.id()); + + let aux = Felt::new(0); + + // Generate a serial number for the P2ID note + let serial_num = builder.rng_mut().draw_word(); + + let claim_params = ClaimNoteParams { + smt_proof_local_exit_root, + smt_proof_rollup_exit_root, + global_index, + mainnet_exit_root: &mainnet_exit_root, + rollup_exit_root: &rollup_exit_root, + origin_network, + origin_token_address: &origin_token_address, + destination_network, + destination_address: &destination_address, + amount: amount_u256, + metadata, + claim_note_creator_account_id: user_account.id(), + agglayer_faucet_account_id: agglayer_faucet.id(), + output_note_tag: NoteTag::from_account_id(user_account.id()), + p2id_serial_number: serial_num, + destination_account_id: user_account.id(), + rng: builder.rng_mut(), + }; + + // Create P2ID note for the user account (similar to network faucet test) + let p2id_script = WellKnownNote::P2ID.script(); + let p2id_inputs = vec![user_account.id().suffix(), user_account.id().prefix().as_felt()]; + let note_inputs = NoteInputs::new(p2id_inputs)?; + let p2id_recipient = NoteRecipient::new(serial_num, p2id_script.clone(), note_inputs); + + let claim_note = create_claim_note(claim_params)?; + + // Add the claim note to the builder before building the mock chain + builder.add_output_note(OutputNote::Full(claim_note.clone())); + + // BUILD MOCK CHAIN WITH ALL ACCOUNTS + // -------------------------------------------------------------------------------------------- + let mut mock_chain = builder.clone().build()?; + mock_chain.prove_next_block()?; + + // CREATE EXPECTED P2ID NOTE FOR VERIFICATION + // -------------------------------------------------------------------------------------------- + let mint_asset: Asset = FungibleAsset::new(agglayer_faucet.id(), amount_felt.into())?.into(); + let output_note_tag = NoteTag::from_account_id(user_account.id()); + let expected_p2id_note = Note::new( + NoteAssets::new(vec![mint_asset])?, + NoteMetadata::new( + agglayer_faucet.id(), + NoteType::Public, + output_note_tag, + NoteExecutionHint::always(), + aux, + )?, + p2id_recipient, + ); + + // EXECUTE CLAIM NOTE AGAINST AGGLAYER FAUCET (with FPI to Bridge) + // -------------------------------------------------------------------------------------------- + let foreign_account_inputs = mock_chain.get_foreign_account_inputs(bridge_account.id())?; + + let tx_context = mock_chain + .build_tx_context(agglayer_faucet.id(), &[], &[claim_note])? + .add_note_script(p2id_script) + .foreign_accounts(vec![foreign_account_inputs]) + .build()?; + + let executed_transaction = tx_context.execute().await?; + + // VERIFY P2ID NOTE WAS CREATED + // -------------------------------------------------------------------------------------------- + + // Check that exactly one P2ID note was created by the faucet + assert_eq!(executed_transaction.output_notes().num_notes(), 1); + let output_note = executed_transaction.output_notes().get_note(0); + + // Verify the output note contains the minted fungible asset + let expected_asset = FungibleAsset::new(agglayer_faucet.id(), amount_felt.into())?; + + // Verify note metadata properties + assert_eq!(output_note.metadata().sender(), agglayer_faucet.id()); + assert_eq!(output_note.metadata().note_type(), NoteType::Public); + assert_eq!(output_note.id(), expected_p2id_note.id()); + + // Extract the full note from the OutputNote enum for detailed verification + let full_note = match output_note { + OutputNote::Full(note) => note, + _ => panic!("Expected OutputNote::Full variant for public note"), + }; + + // Verify note structure and asset content + let expected_asset_obj = Asset::from(expected_asset); + assert_eq!(full_note, &expected_p2id_note); + assert!(full_note.assets().iter().any(|asset| asset == &expected_asset_obj)); + + // Apply the transaction to the mock chain + mock_chain.add_pending_executed_transaction(&executed_transaction)?; + mock_chain.prove_next_block()?; + + // CONSUME THE OUTPUT NOTE WITH TARGET ACCOUNT + // -------------------------------------------------------------------------------------------- + // Consume the output note with target account + let mut user_account_mut = user_account.clone(); + let consume_tx_context = mock_chain + .build_tx_context(user_account_mut.clone(), &[], slice::from_ref(&expected_p2id_note))? + .build()?; + let consume_executed_transaction = consume_tx_context.execute().await?; + + user_account_mut.apply_delta(consume_executed_transaction.account_delta())?; + + // Verify the account's vault now contains the expected fungible asset + let balance = user_account_mut.vault().get_balance(agglayer_faucet.id())?; + assert_eq!(balance, expected_asset.amount()); + + Ok(()) +} diff --git a/crates/miden-testing/tests/agglayer/mod.rs b/crates/miden-testing/tests/agglayer/mod.rs index 72354aa6c9..b238560b86 100644 --- a/crates/miden-testing/tests/agglayer/mod.rs +++ b/crates/miden-testing/tests/agglayer/mod.rs @@ -1,2 +1,3 @@ pub mod asset_conversion; +mod bridge_in; mod bridge_out; From e4116a07c06024b901466e670d4d072fa924ac55 Mon Sep 17 00:00:00 2001 From: Farukest Date: Tue, 13 Jan 2026 01:06:27 +0300 Subject: [PATCH 093/114] feat: move standard note scripts into standard library (#2255) * feat: move standard note scripts into standard library Move note script logic from standalone files in `note_scripts/` directory to `miden::standards::notes` namespace modules. This enables dynamic access to script roots via `procref` instead of hardcoding values. Changes: - Add new modules under `standards/notes/`: p2id, p2ide, swap, burn, mint - Each module contains a `pub proc main` with the script logic - Original note script files now serve as minimal wrappers calling `exec.::main` Closes #2243 * chore: add changelog entry for note scripts refactoring * docs: fix namespace references in note script documentation * chore: update docs contracts->standards --------- Co-authored-by: Marti --- CHANGELOG.md | 2 +- .../asm/note_scripts/BURN.masm | 27 +--- .../asm/note_scripts/MINT.masm | 127 +-------------- .../asm/note_scripts/P2ID.masm | 52 +------ .../asm/note_scripts/P2IDE.masm | 143 +---------------- .../asm/note_scripts/SWAP.masm | 111 +------------- .../asm/standards/notes/burn.masm | 28 ++++ .../asm/standards/notes/mint.masm | 128 ++++++++++++++++ .../asm/standards/notes/p2id.masm | 53 +++++++ .../asm/standards/notes/p2ide.masm | 144 ++++++++++++++++++ .../asm/standards/notes/swap.masm | 112 ++++++++++++++ .../src/account/auth/ecdsa_k256_keccak.rs | 2 +- .../src/account/auth/rpo_falcon_512.rs | 2 +- .../src/account/faucets/basic_fungible.rs | 2 +- .../src/account/faucets/network_fungible.rs | 2 +- .../src/account/wallets/mod.rs | 2 +- docs/src/account/code.md | 2 +- docs/src/note.md | 10 +- 18 files changed, 487 insertions(+), 462 deletions(-) create mode 100644 crates/miden-standards/asm/standards/notes/burn.masm create mode 100644 crates/miden-standards/asm/standards/notes/mint.masm create mode 100644 crates/miden-standards/asm/standards/notes/p2id.masm create mode 100644 crates/miden-standards/asm/standards/notes/p2ide.masm create mode 100644 crates/miden-standards/asm/standards/notes/swap.masm diff --git a/CHANGELOG.md b/CHANGELOG.md index f7626770e3..1edd194eba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). -- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197)). +- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197), [#2255](https://github.com/0xMiden/miden-base/pull/2255)). - [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Removed OLD_MAP_ROOT from being returned when calling [`native_account::set_map_item`](crates/miden-lib/asm/miden/native_account.masm) ([#2194](https://github.com/0xMiden/miden-base/pull/2194)). diff --git a/crates/miden-standards/asm/note_scripts/BURN.masm b/crates/miden-standards/asm/note_scripts/BURN.masm index 9282d5249f..07426b1bc5 100644 --- a/crates/miden-standards/asm/note_scripts/BURN.masm +++ b/crates/miden-standards/asm/note_scripts/BURN.masm @@ -1,28 +1,5 @@ -use miden::standards::faucets +use miden::standards::notes::burn -#! BURN script: burns the asset from the note by calling the faucet's burn procedure. -#! This note can be executed against any faucet account that exposes the faucets::burn procedure -#! (e.g., basic fungible faucet or network fungible faucet). -#! -#! The burn procedure in the faucet already handles all necessary validations including: -#! - Checking that the note contains exactly one asset -#! - Verifying the asset is a fungible asset issued by this faucet -#! - Ensuring the amount to burn doesn't exceed the outstanding supply -#! -#! Requires that the account exposes: -#! - burn procedure (from the faucets interface). -#! -#! Inputs: [ARGS, pad(12)] -#! Outputs: [pad(16)] -#! -#! Panics if: -#! - account does not expose burn procedure. -#! - any of the validations in the burn procedure fail. begin - dropw - # => [pad(16)] - - # Call the faucet's burn procedure which handles all validations - call.faucets::burn - # => [pad(16)] + exec.burn::main end diff --git a/crates/miden-standards/asm/note_scripts/MINT.masm b/crates/miden-standards/asm/note_scripts/MINT.masm index 8d6281b9ee..272fc4ab9a 100644 --- a/crates/miden-standards/asm/note_scripts/MINT.masm +++ b/crates/miden-standards/asm/note_scripts/MINT.masm @@ -1,128 +1,5 @@ -use miden::protocol::active_note -use miden::protocol::note -use miden::standards::faucets::network_fungible->network_faucet +use miden::standards::notes::mint -# CONSTANTS -# ================================================================================================= - -const MINT_NOTE_NUM_INPUTS_PRIVATE=8 -const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 - -const OUTPUT_NOTE_TYPE_PUBLIC=1 -const OUTPUT_NOTE_TYPE_PRIVATE=2 - -const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 -const OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 - -# ERRORS -# ================================================================================================= -const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" - -#! Network Faucet MINT script: mints assets by calling the network faucet's distribute function. -#! This note is intended to be executed against a network fungible faucet account. -#! -#! Requires that the account exposes: -#! - miden::contracts::faucets::network_fungible::distribute procedure. -#! -#! Inputs: [ARGS, pad(12)] -#! Outputs: [pad(16)] -#! -#! Note inputs support two modes. Depending on the number of note inputs, -#! a private or public note is created on consumption of the MINT note: -#! -#! Private mode (8 inputs) - creates a private note: -#! - execution_hint: Execution hint for the output note -#! - aux: Auxiliary data for the output note -#! - tag: Note tag for the output note -#! - amount: The amount to mint -#! - RECIPIENT: The recipient digest (4 elements) -#! -#! Public mode (12+ inputs) - creates a public note with variable-length inputs: -#! - execution_hint: Execution hint for the output note -#! - aux: Auxiliary data for the output note -#! - tag: Note tag for the output note -#! - amount: The amount to mint -#! - SCRIPT_ROOT: Script root of the output note (4 elements) -#! - SERIAL_NUM: Serial number of the output note (4 elements) -#! - [INPUTS]: Variable-length inputs for the output note (Vec) -#! The number of output note inputs = num_mint_note_inputs - 12 -#! -#! Panics if: -#! - account does not expose distribute procedure. -#! - the number of inputs is not exactly 8 for private or less than 12 for public output notes. begin - dropw - # => [pad(16)] - # Load note inputs into memory starting at address 0 - push.0 exec.active_note::get_inputs - # => [total_inputs, inputs_ptr, pad(16)] - - dup - # => [num_inputs, num_inputs, inputs_ptr, pad(16)] - - u32assert2.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS - u32gte.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC - # => [is_public_output_note, total_inputs, inputs_ptr, pad(16)] - - if.true - # public output note creation - # => [total_inputs, inputs_ptr, pad(16)] - - movdn.9 drop - # => [EMPTY_WORD, EMPTY_WORD, total_inputs, pad(8)] - - mem_loadw_be.4 - # => [SCRIPT_ROOT, EMPTY_WORD, total_inputs, pad(8)] - - swapw mem_loadw_be.8 - # => [SERIAL_NUM, SCRIPT_ROOT, total_inputs, pad(8)] - - # compute variable length note inputs for the output note - movup.8 sub.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC - # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] - - push.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR - # => [inputs_ptr = 12, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] - - exec.note::build_recipient - # => [RECIPIENT, pad(12)] - - swapw mem_loadw_be.0 - # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - push.OUTPUT_NOTE_TYPE_PUBLIC - # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - movdn.3 - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - - else - # private output note creation - - eq.MINT_NOTE_NUM_INPUTS_PRIVATE assert.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS drop - # => [inputs_ptr, pad(16)] - - drop - # => [pad(16)] - - mem_loadw_be.4 - # => [RECIPIENT, pad(12)] - - swapw mem_loadw_be.0 - # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - push.OUTPUT_NOTE_TYPE_PRIVATE - # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - movdn.3 - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - - end - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - - call.network_faucet::distribute - # => [pad(17))] - - drop - # => [pad(16)] + exec.mint::main end diff --git a/crates/miden-standards/asm/note_scripts/P2ID.masm b/crates/miden-standards/asm/note_scripts/P2ID.masm index 6e0e455400..d3050ea305 100644 --- a/crates/miden-standards/asm/note_scripts/P2ID.masm +++ b/crates/miden-standards/asm/note_scripts/P2ID.masm @@ -1,53 +1,5 @@ -use miden::protocol::active_account -use miden::protocol::account_id -use miden::protocol::active_note -use miden::standards::wallets::basic->basic_wallet +use miden::standards::notes::p2id -# ERRORS -# ================================================================================================= - -const ERR_P2ID_WRONG_NUMBER_OF_INPUTS="P2ID note expects exactly 2 note inputs" - -const ERR_P2ID_TARGET_ACCT_MISMATCH="P2ID's target account address and transaction address do not match" - -#! Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account -#! matches target account ID specified by the note inputs. -#! -#! Requires that the account exposes: -#! - miden::contracts::wallets::basic::receive_asset procedure. -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Note inputs are assumed to be as follows: -#! - target_account_id is the ID of the account for which the note is intended. -#! -#! Panics if: -#! - Account does not expose miden::contracts::wallets::basic::receive_asset procedure. -#! - Account ID of executing account is not equal to the Account ID specified via note inputs. -#! - The same non-fungible asset already exists in the account. -#! - Adding a fungible asset would result in amount overflow, i.e., the total amount would be -#! greater than 2^63. begin - # store the note inputs to memory starting at address 0 - padw push.0 exec.active_note::get_inputs - # => [num_inputs, inputs_ptr, EMPTY_WORD] - - # make sure the number of inputs is 2 - eq.2 assert.err=ERR_P2ID_WRONG_NUMBER_OF_INPUTS - # => [inputs_ptr, EMPTY_WORD] - - # read the target account ID from the note inputs - mem_loadw_be drop drop - # => [target_account_id_prefix, target_account_id_suffix] - - exec.active_account::get_id - # => [account_id_prefix, account_id_suffix, target_account_id_prefix, target_account_id_suffix, ...] - - # ensure account_id = target_account_id, fails otherwise - exec.account_id::is_equal assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH - # => [] - - exec.basic_wallet::add_assets_to_account - # => [] + exec.p2id::main end diff --git a/crates/miden-standards/asm/note_scripts/P2IDE.masm b/crates/miden-standards/asm/note_scripts/P2IDE.masm index 5a033ba0ef..6d65869eef 100644 --- a/crates/miden-standards/asm/note_scripts/P2IDE.masm +++ b/crates/miden-standards/asm/note_scripts/P2IDE.masm @@ -1,144 +1,5 @@ -use miden::protocol::active_account -use miden::protocol::account_id -use miden::protocol::active_note -use miden::protocol::tx -use miden::standards::wallets::basic->basic_wallet +use miden::standards::notes::p2ide -# ERRORS -# ================================================================================================= - -const ERR_P2IDE_WRONG_NUMBER_OF_INPUTS="P2IDE note expects exactly 4 note inputs" - -const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER="failed to reclaim P2IDE note because the reclaiming account is not the sender" - -const ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED="failed to reclaim P2IDE note because the reclaim block height is not reached yet" - -const ERR_P2IDE_RECLAIM_DISABLED="P2IDE reclaim is disabled" - -const ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note because the note is still timelocked" - -# HELPER PROCEDURES -# ================================================================================================= - -#! Helper procedure to check if the P2IDE note is unlocked. -#! -#! Inputs: [current_block_height, timelock_block_height] -#! Outputs: [current_block_height] -proc verify_unlocked - dup movdn.2 - # => [current_block_height, timelock_block_height, current_block_height] - - # check timelock_block_height <= current_block_height - lte assert.err=ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED - # => [current_block_height] -end - -#! Helper procedure which adds the note assets to the sender account. -#! -#! Checks if P2IDE reclaim is enabled and if true, if reclaim height has been reached. -#! -#! Inputs: [account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height] -#! Outputs: [] -#! -#! Panics if: -#! - the reclaim of the active note is disabled. -#! - the reclaim block height is not reached yet. -#! - the account attempting to reclaim the note is not the sender account. -proc reclaim_note - # check that the reclaim of the active note is enabled - movup.3 dup neq.0 assert.err=ERR_P2IDE_RECLAIM_DISABLED - # => [reclaim_block_height, account_id_prefix, account_id_suffix, current_block_height] - - # now check that sender is allowed to reclaim, reclaim block height <= current block height - movup.3 - # => [current_block_height, reclaim_block_height, account_id_prefix, account_id_suffix] - - lte assert.err=ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED - # => [account_id_prefix, account_id_suffix] - - # if active account is not the target, we need to ensure it is the sender - exec.active_note::get_sender - # => [sender_account_id_prefix, sender_account_id_suffix, account_id_prefix, account_id_suffix] - - # ensure active account ID = sender account ID - exec.account_id::is_equal assert.err=ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER - # => [] - - # add note assets to account - exec.basic_wallet::add_assets_to_account - # => [] -end - -#! Extended Pay-to-ID note script (Reclaimable & Timelockable) -#! -#! Adds all assets from the note to the account if all of the following conditions are true: -#! - The transaction's reference block number is greater than or equal to the note's timelock block height. -#! - Any of the following conditions is true: -#! - The account ID against which the transaction is executed matches the note's target account id. -#! - The account ID against which the transaction is executed matches the note's sender account id and -#! the transaction's reference block number is greater than or equal to the note's reclaim block height. -#! -#! Requires that the account exposes: -#! - miden::contracts::wallets::basic::receive_asset procedure. -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Note inputs are assumed to be as follows: -#! - target_account_id is the ID of the account for which the note is intended. -#! - reclaim_block_height is the block height at which the note can be reclaimed by the sender. -#! - timelock_block_height is the block height at which the note can be consumed by the target. -#! -#! Panics if: -#! - The account does not expose miden::contracts::wallets::basic::receive_asset procedure. -#! - The note is consumed before the timelock expired, i.e. the transaction's reference block -#! number is less than the timelock block height. -#! - Before reclaim block height: the account ID of the executing account is not equal to the specified -#! account ID. -#! - At and after reclaim block height: the account ID of the executing account is not equal to -#! the specified account ID or sender account ID. -#! - The same non-fungible asset already exists in the account. -#! - Adding a fungible asset would result in an amount overflow, i.e., the total amount would be -#! greater than 2^63. begin - # store the note inputs to memory starting at address 0 - push.0 exec.active_note::get_inputs - # => [num_inputs, inputs_ptr] - - # make sure the number of inputs is 4 - eq.4 assert.err=ERR_P2IDE_WRONG_NUMBER_OF_INPUTS - # => [inputs_ptr] - - # read the reclaim block height, timelock_block_height, and target account ID from the note inputs - mem_loadw_be - # => [timelock_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] - - # read the current block number - exec.tx::get_block_number - # => [current_block_height, timelock_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] - - # fails if note is locked - exec.verify_unlocked - # => [current_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] - - # get active account id - exec.active_account::get_id dup.1 dup.1 - # => [account_id_prefix, account_id_suffix, account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] - - # determine if the active account is the target account - movup.7 movup.7 exec.account_id::is_equal - # => [is_target, account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height] - - if.true - # we can safely consume the note since the active account is the target of the note - dropw exec.basic_wallet::add_assets_to_account - # => [] - - else - # checks if active account is sender and if reclaim is enabled - exec.reclaim_note - # => [] - end - - # => [] + exec.p2ide::main end diff --git a/crates/miden-standards/asm/note_scripts/SWAP.masm b/crates/miden-standards/asm/note_scripts/SWAP.masm index 4c1c2ee8f2..7d4f95b31b 100644 --- a/crates/miden-standards/asm/note_scripts/SWAP.masm +++ b/crates/miden-standards/asm/note_scripts/SWAP.masm @@ -1,112 +1,5 @@ -use miden::protocol::active_note -use miden::protocol::output_note -use miden::standards::wallets::basic->wallet +use miden::standards::notes::swap -# CONSTANTS -# ================================================================================================= - -const SWAP_NOTE_INPUTS_NUMBER=12 - -# ERRORS -# ================================================================================================= - -const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" - -const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" - -#! Swap script: adds an asset from the note into consumers account and -#! creates a note consumable by note issuer containing requested ASSET. -#! -#! Requires that the account exposes: -#! - miden::contracts::wallets::basic::receive_asset procedure. -#! - miden::contracts::wallets::basic::move_asset_to_note procedure. -#! -#! Inputs: [ARGS] -#! Outputs: [] -#! -#! Note inputs are assumed to be as follows: -#! - REQUESTED_ASSET -#! - PAYBACK_NOTE_RECIPIENT -#! - payback_note_execution_hint -#! - payback_note_type -#! - payback_note_aux -#! - payback_note_tag -#! -#! Panics if: -#! - account does not expose miden::contracts::wallets::basic::receive_asset procedure. -#! - account does not expose miden::contracts::wallets::basic::move_asset_to_note procedure. -#! - account vault does not contain the requested asset. -#! - adding a fungible asset would result in amount overflow, i.e., the total amount would be -#! greater than 2^63. begin - # dropping note args - dropw - # => [] - - # --- create a payback note with the requested asset ---------------- - - # store note inputs into memory starting at address 0 - push.0 exec.active_note::get_inputs - # => [num_inputs, inputs_ptr] - - # make sure the number of inputs is 12 - eq.SWAP_NOTE_INPUTS_NUMBER assert.err=ERR_SWAP_WRONG_NUMBER_OF_INPUTS - # => [inputs_ptr] - - # load REQUESTED_ASSET - mem_loadw_be - # => [REQUESTED_ASSET] - - # load PAYBACK_NOTE_RECIPIENT - padw mem_loadw_be.4 - # => [PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] - - # load payback P2ID details - padw mem_loadw_be.8 - # => [tag, aux, note_type, execution_hint, PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] - - # create payback P2ID note - exec.output_note::create - # => [note_idx, REQUESTED_ASSET] - - movdn.4 - # => [REQUESTED_ASSET, note_idx] - - # padding stack with 11 zeros - repeat.11 - push.0 - movdn.5 - end - # => [REQUESTED_ASSET, note_idx, pad(11)] - - # move asset to the note - call.wallet::move_asset_to_note - # => [REQUESTED_ASSET, note_idx, pad(11)] - - dropw drop push.0 - # => [pad(12)] - - # --- move assets from the SWAP note into the account ------------------------- - - # store the number of note assets to memory starting at address 0 - push.0 exec.active_note::get_assets - # => [num_assets, ptr, pad(12)] - - # make sure the number of assets is 1 - assert.err=ERR_SWAP_WRONG_NUMBER_OF_ASSETS - # => [ptr, pad(12)] - - # load the ASSET - mem_loadw_be - # => [ASSET, pad(12)] - - # add the ASSET to the account - call.wallet::receive_asset - # => [pad(16)] - - # clearing the stack of padded 0s - repeat.4 - dropw - end - # => [] + exec.swap::main end diff --git a/crates/miden-standards/asm/standards/notes/burn.masm b/crates/miden-standards/asm/standards/notes/burn.masm new file mode 100644 index 0000000000..20696cc7bc --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/burn.masm @@ -0,0 +1,28 @@ +use miden::standards::faucets + +#! BURN script: burns the asset from the note by calling the faucet's burn procedure. +#! This note can be executed against any faucet account that exposes the faucets::burn procedure +#! (e.g., basic fungible faucet or network fungible faucet). +#! +#! The burn procedure in the faucet already handles all necessary validations including: +#! - Checking that the note contains exactly one asset +#! - Verifying the asset is a fungible asset issued by this faucet +#! - Ensuring the amount to burn doesn't exceed the outstanding supply +#! +#! Requires that the account exposes: +#! - burn procedure (from the faucets interface). +#! +#! Inputs: [ARGS, pad(12)] +#! Outputs: [pad(16)] +#! +#! Panics if: +#! - account does not expose burn procedure. +#! - any of the validations in the burn procedure fail. +pub proc main + dropw + # => [pad(16)] + + # Call the faucet's burn procedure which handles all validations + call.faucets::burn + # => [pad(16)] +end diff --git a/crates/miden-standards/asm/standards/notes/mint.masm b/crates/miden-standards/asm/standards/notes/mint.masm new file mode 100644 index 0000000000..575bc04c3d --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/mint.masm @@ -0,0 +1,128 @@ +use miden::protocol::active_note +use miden::protocol::note +use miden::standards::faucets::network_fungible->network_faucet + +# CONSTANTS +# ================================================================================================= + +const MINT_NOTE_NUM_INPUTS_PRIVATE=8 +const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 + +const OUTPUT_NOTE_TYPE_PUBLIC=1 +const OUTPUT_NOTE_TYPE_PRIVATE=2 + +const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 +const OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 + +# ERRORS +# ================================================================================================= +const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" + +#! Network Faucet MINT script: mints assets by calling the network faucet's distribute function. +#! This note is intended to be executed against a network fungible faucet account. +#! +#! Requires that the account exposes: +#! - miden::standards::faucets::network_fungible::distribute procedure. +#! +#! Inputs: [ARGS, pad(12)] +#! Outputs: [pad(16)] +#! +#! Note inputs support two modes. Depending on the number of note inputs, +#! a private or public note is created on consumption of the MINT note: +#! +#! Private mode (8 inputs) - creates a private note: +#! - execution_hint: Execution hint for the output note +#! - aux: Auxiliary data for the output note +#! - tag: Note tag for the output note +#! - amount: The amount to mint +#! - RECIPIENT: The recipient digest (4 elements) +#! +#! Public mode (12+ inputs) - creates a public note with variable-length inputs: +#! - execution_hint: Execution hint for the output note +#! - aux: Auxiliary data for the output note +#! - tag: Note tag for the output note +#! - amount: The amount to mint +#! - SCRIPT_ROOT: Script root of the output note (4 elements) +#! - SERIAL_NUM: Serial number of the output note (4 elements) +#! - [INPUTS]: Variable-length inputs for the output note (Vec) +#! The number of output note inputs = num_mint_note_inputs - 12 +#! +#! Panics if: +#! - account does not expose distribute procedure. +#! - the number of inputs is not exactly 8 for private or less than 12 for public output notes. +pub proc main + dropw + # => [pad(16)] + # Load note inputs into memory starting at address 0 + push.0 exec.active_note::get_inputs + # => [total_inputs, inputs_ptr, pad(16)] + + dup + # => [num_inputs, num_inputs, inputs_ptr, pad(16)] + + u32assert2.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS + u32gte.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC + # => [is_public_output_note, total_inputs, inputs_ptr, pad(16)] + + if.true + # public output note creation + # => [total_inputs, inputs_ptr, pad(16)] + + movdn.9 drop + # => [EMPTY_WORD, EMPTY_WORD, total_inputs, pad(8)] + + mem_loadw_be.4 + # => [SCRIPT_ROOT, EMPTY_WORD, total_inputs, pad(8)] + + swapw mem_loadw_be.8 + # => [SERIAL_NUM, SCRIPT_ROOT, total_inputs, pad(8)] + + # compute variable length note inputs for the output note + movup.8 sub.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC + # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] + + push.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR + # => [inputs_ptr = 12, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] + + exec.note::build_recipient + # => [RECIPIENT, pad(12)] + + swapw mem_loadw_be.0 + # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + push.OUTPUT_NOTE_TYPE_PUBLIC + # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + movdn.3 + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + + else + # private output note creation + + eq.MINT_NOTE_NUM_INPUTS_PRIVATE assert.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS drop + # => [inputs_ptr, pad(16)] + + drop + # => [pad(16)] + + mem_loadw_be.4 + # => [RECIPIENT, pad(12)] + + swapw mem_loadw_be.0 + # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + push.OUTPUT_NOTE_TYPE_PRIVATE + # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + movdn.3 + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + + end + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + + call.network_faucet::distribute + # => [pad(17))] + + drop + # => [pad(16)] +end diff --git a/crates/miden-standards/asm/standards/notes/p2id.masm b/crates/miden-standards/asm/standards/notes/p2id.masm new file mode 100644 index 0000000000..b315ad4e5a --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/p2id.masm @@ -0,0 +1,53 @@ +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::standards::wallets::basic->basic_wallet + +# ERRORS +# ================================================================================================= + +const ERR_P2ID_WRONG_NUMBER_OF_INPUTS="P2ID note expects exactly 2 note inputs" + +const ERR_P2ID_TARGET_ACCT_MISMATCH="P2ID's target account address and transaction address do not match" + +#! Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account +#! matches target account ID specified by the note inputs. +#! +#! Requires that the account exposes: +#! - miden::standards::wallets::basic::receive_asset procedure. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Note inputs are assumed to be as follows: +#! - target_account_id is the ID of the account for which the note is intended. +#! +#! Panics if: +#! - Account does not expose miden::standards::wallets::basic::receive_asset procedure. +#! - Account ID of executing account is not equal to the Account ID specified via note inputs. +#! - The same non-fungible asset already exists in the account. +#! - Adding a fungible asset would result in amount overflow, i.e., the total amount would be +#! greater than 2^63. +pub proc main + # store the note inputs to memory starting at address 0 + padw push.0 exec.active_note::get_inputs + # => [num_inputs, inputs_ptr, EMPTY_WORD] + + # make sure the number of inputs is 2 + eq.2 assert.err=ERR_P2ID_WRONG_NUMBER_OF_INPUTS + # => [inputs_ptr, EMPTY_WORD] + + # read the target account ID from the note inputs + mem_loadw_be drop drop + # => [target_account_id_prefix, target_account_id_suffix] + + exec.active_account::get_id + # => [account_id_prefix, account_id_suffix, target_account_id_prefix, target_account_id_suffix, ...] + + # ensure account_id = target_account_id, fails otherwise + exec.account_id::is_equal assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH + # => [] + + exec.basic_wallet::add_assets_to_account + # => [] +end diff --git a/crates/miden-standards/asm/standards/notes/p2ide.masm b/crates/miden-standards/asm/standards/notes/p2ide.masm new file mode 100644 index 0000000000..1345bc4acb --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/p2ide.masm @@ -0,0 +1,144 @@ +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::protocol::tx +use miden::standards::wallets::basic->basic_wallet + +# ERRORS +# ================================================================================================= + +const ERR_P2IDE_WRONG_NUMBER_OF_INPUTS="P2IDE note expects exactly 4 note inputs" + +const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER="failed to reclaim P2IDE note because the reclaiming account is not the sender" + +const ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED="failed to reclaim P2IDE note because the reclaim block height is not reached yet" + +const ERR_P2IDE_RECLAIM_DISABLED="P2IDE reclaim is disabled" + +const ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note because the note is still timelocked" + +# HELPER PROCEDURES +# ================================================================================================= + +#! Helper procedure to check if the P2IDE note is unlocked. +#! +#! Inputs: [current_block_height, timelock_block_height] +#! Outputs: [current_block_height] +proc verify_unlocked + dup movdn.2 + # => [current_block_height, timelock_block_height, current_block_height] + + # check timelock_block_height <= current_block_height + lte assert.err=ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED + # => [current_block_height] +end + +#! Helper procedure which adds the note assets to the sender account. +#! +#! Checks if P2IDE reclaim is enabled and if true, if reclaim height has been reached. +#! +#! Inputs: [account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height] +#! Outputs: [] +#! +#! Panics if: +#! - the reclaim of the active note is disabled. +#! - the reclaim block height is not reached yet. +#! - the account attempting to reclaim the note is not the sender account. +proc reclaim_note + # check that the reclaim of the active note is enabled + movup.3 dup neq.0 assert.err=ERR_P2IDE_RECLAIM_DISABLED + # => [reclaim_block_height, account_id_prefix, account_id_suffix, current_block_height] + + # now check that sender is allowed to reclaim, reclaim block height <= current block height + movup.3 + # => [current_block_height, reclaim_block_height, account_id_prefix, account_id_suffix] + + lte assert.err=ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED + # => [account_id_prefix, account_id_suffix] + + # if active account is not the target, we need to ensure it is the sender + exec.active_note::get_sender + # => [sender_account_id_prefix, sender_account_id_suffix, account_id_prefix, account_id_suffix] + + # ensure active account ID = sender account ID + exec.account_id::is_equal assert.err=ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER + # => [] + + # add note assets to account + exec.basic_wallet::add_assets_to_account + # => [] +end + +#! Extended Pay-to-ID note script (Reclaimable & Timelockable) +#! +#! Adds all assets from the note to the account if all of the following conditions are true: +#! - The transaction's reference block number is greater than or equal to the note's timelock block height. +#! - Any of the following conditions is true: +#! - The account ID against which the transaction is executed matches the note's target account id. +#! - The account ID against which the transaction is executed matches the note's sender account id and +#! the transaction's reference block number is greater than or equal to the note's reclaim block height. +#! +#! Requires that the account exposes: +#! - miden::standards::wallets::basic::receive_asset procedure. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Note inputs are assumed to be as follows: +#! - target_account_id is the ID of the account for which the note is intended. +#! - reclaim_block_height is the block height at which the note can be reclaimed by the sender. +#! - timelock_block_height is the block height at which the note can be consumed by the target. +#! +#! Panics if: +#! - The account does not expose miden::standards::wallets::basic::receive_asset procedure. +#! - The note is consumed before the timelock expired, i.e. the transaction's reference block +#! number is less than the timelock block height. +#! - Before reclaim block height: the account ID of the executing account is not equal to the specified +#! account ID. +#! - At and after reclaim block height: the account ID of the executing account is not equal to +#! the specified account ID or sender account ID. +#! - The same non-fungible asset already exists in the account. +#! - Adding a fungible asset would result in an amount overflow, i.e., the total amount would be +#! greater than 2^63. +pub proc main + # store the note inputs to memory starting at address 0 + push.0 exec.active_note::get_inputs + # => [num_inputs, inputs_ptr] + + # make sure the number of inputs is 4 + eq.4 assert.err=ERR_P2IDE_WRONG_NUMBER_OF_INPUTS + # => [inputs_ptr] + + # read the reclaim block height, timelock_block_height, and target account ID from the note inputs + mem_loadw_be + # => [timelock_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] + + # read the current block number + exec.tx::get_block_number + # => [current_block_height, timelock_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] + + # fails if note is locked + exec.verify_unlocked + # => [current_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] + + # get active account id + exec.active_account::get_id dup.1 dup.1 + # => [account_id_prefix, account_id_suffix, account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] + + # determine if the active account is the target account + movup.7 movup.7 exec.account_id::is_equal + # => [is_target, account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height] + + if.true + # we can safely consume the note since the active account is the target of the note + dropw exec.basic_wallet::add_assets_to_account + # => [] + + else + # checks if active account is sender and if reclaim is enabled + exec.reclaim_note + # => [] + end + + # => [] +end diff --git a/crates/miden-standards/asm/standards/notes/swap.masm b/crates/miden-standards/asm/standards/notes/swap.masm new file mode 100644 index 0000000000..f95d718e92 --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/swap.masm @@ -0,0 +1,112 @@ +use miden::protocol::active_note +use miden::protocol::output_note +use miden::standards::wallets::basic->wallet + +# CONSTANTS +# ================================================================================================= + +const SWAP_NOTE_INPUTS_NUMBER=12 + +# ERRORS +# ================================================================================================= + +const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" + +const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" + +#! Swap script: adds an asset from the note into consumers account and +#! creates a note consumable by note issuer containing requested ASSET. +#! +#! Requires that the account exposes: +#! - miden::standards::wallets::basic::receive_asset procedure. +#! - miden::standards::wallets::basic::move_asset_to_note procedure. +#! +#! Inputs: [ARGS] +#! Outputs: [] +#! +#! Note inputs are assumed to be as follows: +#! - REQUESTED_ASSET +#! - PAYBACK_NOTE_RECIPIENT +#! - payback_note_execution_hint +#! - payback_note_type +#! - payback_note_aux +#! - payback_note_tag +#! +#! Panics if: +#! - account does not expose miden::standards::wallets::basic::receive_asset procedure. +#! - account does not expose miden::standards::wallets::basic::move_asset_to_note procedure. +#! - account vault does not contain the requested asset. +#! - adding a fungible asset would result in amount overflow, i.e., the total amount would be +#! greater than 2^63. +pub proc main + # dropping note args + dropw + # => [] + + # --- create a payback note with the requested asset ---------------- + + # store note inputs into memory starting at address 0 + push.0 exec.active_note::get_inputs + # => [num_inputs, inputs_ptr] + + # make sure the number of inputs is 12 + eq.SWAP_NOTE_INPUTS_NUMBER assert.err=ERR_SWAP_WRONG_NUMBER_OF_INPUTS + # => [inputs_ptr] + + # load REQUESTED_ASSET + mem_loadw_be + # => [REQUESTED_ASSET] + + # load PAYBACK_NOTE_RECIPIENT + padw mem_loadw_be.4 + # => [PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] + + # load payback P2ID details + padw mem_loadw_be.8 + # => [tag, aux, note_type, execution_hint, PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] + + # create payback P2ID note + exec.output_note::create + # => [note_idx, REQUESTED_ASSET] + + movdn.4 + # => [REQUESTED_ASSET, note_idx] + + # padding stack with 11 zeros + repeat.11 + push.0 + movdn.5 + end + # => [REQUESTED_ASSET, note_idx, pad(11)] + + # move asset to the note + call.wallet::move_asset_to_note + # => [REQUESTED_ASSET, note_idx, pad(11)] + + dropw drop push.0 + # => [pad(12)] + + # --- move assets from the SWAP note into the account ------------------------- + + # store the number of note assets to memory starting at address 0 + push.0 exec.active_note::get_assets + # => [num_assets, ptr, pad(12)] + + # make sure the number of assets is 1 + assert.err=ERR_SWAP_WRONG_NUMBER_OF_ASSETS + # => [ptr, pad(12)] + + # load the ASSET + mem_loadw_be + # => [ASSET, pad(12)] + + # add the ASSET to the account + call.wallet::receive_asset + # => [pad(16)] + + # clearing the stack of padded 0s + repeat.4 + dropw + end + # => [] +end diff --git a/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs index a187b57729..e26661972c 100644 --- a/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs @@ -12,7 +12,7 @@ static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing the ECDSA K256 Keccak signature scheme for authentication /// of transactions. /// -/// It reexports the procedures from `miden::contracts::auth::ecdsa_k256_keccak`. When linking +/// It reexports the procedures from `miden::standards::auth::ecdsa_k256_keccak`. When linking /// against this component, the `miden` library (i.e. /// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: diff --git a/crates/miden-standards/src/account/auth/rpo_falcon_512.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512.rs index 305f5c7c46..a65d9e0394 100644 --- a/crates/miden-standards/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512.rs @@ -12,7 +12,7 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing the RpoFalcon512 signature scheme for authentication of /// transactions. /// -/// It reexports the procedures from `miden::contracts::auth::rpo_falcon512`. When linking against +/// It reexports the procedures from `miden::standards::auth::rpo_falcon512`. When linking against /// this component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must /// be available to the assembler which is the case when using [`CodeBuilder`][builder]. The /// procedures of this component are: diff --git a/crates/miden-standards/src/account/faucets/basic_fungible.rs b/crates/miden-standards/src/account/faucets/basic_fungible.rs index 329937ebd4..57c29385e8 100644 --- a/crates/miden-standards/src/account/faucets/basic_fungible.rs +++ b/crates/miden-standards/src/account/faucets/basic_fungible.rs @@ -42,7 +42,7 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic fungible faucet. /// -/// It reexports the procedures from `miden::contracts::faucets::basic_fungible`. When linking +/// It reexports the procedures from `miden::standards::faucets::basic_fungible`. When linking /// against this component, the `miden` library (i.e. /// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: diff --git a/crates/miden-standards/src/account/faucets/network_fungible.rs b/crates/miden-standards/src/account/faucets/network_fungible.rs index 912e89e863..502e8582df 100644 --- a/crates/miden-standards/src/account/faucets/network_fungible.rs +++ b/crates/miden-standards/src/account/faucets/network_fungible.rs @@ -43,7 +43,7 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing a network fungible faucet. /// -/// It reexports the procedures from `miden::contracts::faucets::network_fungible`. When linking +/// It reexports the procedures from `miden::standards::faucets::network_fungible`. When linking /// against this component, the `miden` library (i.e. /// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: diff --git a/crates/miden-standards/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs index fc7c4f0773..fe412229a7 100644 --- a/crates/miden-standards/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -41,7 +41,7 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic wallet. /// -/// It reexports the procedures from `miden::contracts::wallets::basic`. When linking against this +/// It reexports the procedures from `miden::standards::wallets::basic`. When linking against this /// component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must be /// available to the assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: diff --git a/docs/src/account/code.md b/docs/src/account/code.md index 2b22c67b06..ed6d60ab1e 100644 --- a/docs/src/account/code.md +++ b/docs/src/account/code.md @@ -17,7 +17,7 @@ Every Miden `Account` is essentially a smart contract. The `Code` defines the ac ## Interface -An account's code is typically the result of merging multiple [account components](./components). This results in a set of procedures that make up the _interface_ of the account. As an example, a typical wallet uses the so-called _basic wallet_ interface, which is defined in `miden::contracts::wallets::basic`. It consists of the `receive_asset` and `move_asset_to_note` procedures. If an account has this interface, i.e. this set of procedures, it can consume standard [P2ID notes](../note#p2id-pay-to-id). If it doesn't, it can't consume this type of note. So, adhering to standard interfaces such as the basic wallet will generally make an account more interoperable. +An account's code is typically the result of merging multiple [account components](./components). This results in a set of procedures that make up the _interface_ of the account. As an example, a typical wallet uses the so-called _basic wallet_ interface, which is defined in `miden::standards::wallets::basic`. It consists of the `receive_asset` and `move_asset_to_note` procedures. If an account has this interface, i.e. this set of procedures, it can consume standard [P2ID notes](../note#p2id-pay-to-id). If it doesn't, it can't consume this type of note. So, adhering to standard interfaces such as the basic wallet will generally make an account more interoperable. ## Authentication diff --git a/docs/src/note.md b/docs/src/note.md index 2137a7db1b..20f30201f2 100644 --- a/docs/src/note.md +++ b/docs/src/note.md @@ -121,7 +121,7 @@ Only those who know the RECIPIENT’s pre-image can consume the `Note`. For priv The [transaction prologue](transaction) requires all necessary data to compute the `Note` hash. This setup allows scenario-specific restrictions on who may consume a `Note`. -For a practical example, refer to the [SWAP note script](https://github.com/0xMiden/miden-base/blob/next/crates/miden-standards/asm/note_scripts/SWAP.masm), where the RECIPIENT ensures that only a defined target can consume the swapped asset. +For a practical example, refer to the [SWAP note script](https://github.com/0xMiden/miden-base/blob/next/crates/miden-standards/asm/standards/notes/swap.masm), where the RECIPIENT ensures that only a defined target can consume the swapped asset. #### Note nullifier ensuring private consumption @@ -156,7 +156,7 @@ The P2ID note script implements a simple pay-to-account-ID pattern. It adds all - **Purpose:** Direct asset transfer to a specific account ID - **Inputs:** Requires exactly 2 note inputs containing the target account ID - **Validation:** Ensures the consuming account's ID matches the target account ID specified in the note -- **Requirements:** Target account must expose the `miden::contracts::wallets::basic::receive_asset` procedure +- **Requirements:** Target account must expose the `miden::standards::wallets::basic::receive_asset` procedure **Use case:** Simple, direct payments where you want to send assets to a known account ID. @@ -174,7 +174,7 @@ The P2IDE note script extends P2ID with additional features including time-locki - **Time-lock:** Note cannot be consumed until the specified block height is reached - **Reclaim:** Original sender can reclaim the note after the reclaim block height if not consumed by target - **Validation:** Complex logic to handle both target consumption and sender reclaim scenarios -- **Requirements:** Account must expose the `miden::contracts::wallets::basic::receive_asset` procedure +- **Requirements:** Account must expose the `miden::standards::wallets::basic::receive_asset` procedure **Use cases:** @@ -198,8 +198,8 @@ The SWAP note script implements atomic asset swapping functionality. 1. Creates a payback note containing the requested asset for the original note issuer 2. Adds the note's asset to the consuming account's vault - **Requirements:** Account must expose both: - - `miden::contracts::wallets::basic::receive_asset` procedure - - `miden::contracts::wallets::basic::move_asset_to_note` procedure + - `miden::standards::wallets::basic::receive_asset` procedure + - `miden::standards::wallets::basic::move_asset_to_note` procedure **Use case:** Decentralized asset trading where two parties want to exchange different assets atomically. From bd8c4f67dd87f1150a167b7d7d1798f205af3bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= <4142+huitseeker@users.noreply.github.com> Date: Tue, 13 Jan 2026 21:09:29 +0100 Subject: [PATCH 094/114] chore: move MSRV check from PR CI to release workflows (#2233) Previously, the Minimum Supported Rust Version (MSRV) check ran on every pull request and push to next. This was expensive because cargo-msrv downloads additional Rust toolchains to verify compatibility. Now the MSRV check only runs: - On push to main/next (in release-plz-dry-run.yml) - When publishing a release (in release-plz.yml) This makes PR CI faster and uses less memory. Any MSRV issues will still be caught before publication, and can be fixed locally using the scripts/check-msrv.sh script. Changes: - Delete .github/workflows/msrv.yml - Add MSRV check steps to release-plz-dry-run.yml - Add MSRV check steps to release-plz.yml --- .github/workflows/msrv.yml | 37 ----------------------- .github/workflows/release-plz-dry-run.yml | 19 +++++++++++- .github/workflows/release-plz.yml | 15 ++++++++- 3 files changed, 32 insertions(+), 39 deletions(-) delete mode 100644 .github/workflows/msrv.yml diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml deleted file mode 100644 index 9f2f6afa02..0000000000 --- a/.github/workflows/msrv.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Check MSRV - -on: - push: - branches: [next] - pull_request: - types: [opened, reopened, synchronize] - -# Limits workflow concurrency to only the latest commit in the PR. -concurrency: - group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" - cancel-in-progress: true - -permissions: - contents: read - -jobs: - # Check MSRV (aka `rust-version`) in `Cargo.toml` is valid for workspace members - msrv: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Cleanup large tools for build space - uses: ./.github/actions/cleanup-runner - - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y jq - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - uses: taiki-e/install-action@v2 - with: - tool: cargo-binstall - - name: Install cargo-msrv - run: cargo binstall --no-confirm --force cargo-msrv - - name: Check MSRV for each workspace member - run: | - export PATH="$HOME/.cargo/bin:$PATH" - ./scripts/check-msrv.sh diff --git a/.github/workflows/release-plz-dry-run.yml b/.github/workflows/release-plz-dry-run.yml index b41850da1f..be787b1270 100644 --- a/.github/workflows/release-plz-dry-run.yml +++ b/.github/workflows/release-plz-dry-run.yml @@ -7,6 +7,10 @@ on: push: branches: [main, next] +concurrency: + group: "${{ github.workflow }} @ ${{ github.ref }}" + cancel-in-progress: true + jobs: release-plz-dry-run-release: name: Release-plz dry-run @@ -17,9 +21,22 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y jq - name: Update Rust toolchain + run: rustup update --no-self-update + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: cargo-binstall + - name: Install cargo-msrv + run: cargo binstall --no-confirm --force cargo-msrv + - name: Check MSRV for each workspace member run: | - rustup update --no-self-update + export PATH="$HOME/.cargo/bin:$PATH" + ./scripts/check-msrv.sh - name: Run release-plz uses: release-plz/action@v0.5 with: diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 78218b7a64..a9de6bdeb8 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -34,9 +34,22 @@ jobs: exit 1 fi echo "Release tag matches main HEAD — continuing." + - name: Cleanup large tools for build space + uses: ./.github/actions/cleanup-runner + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y jq - name: Update Rust toolchain + run: rustup update --no-self-update + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: cargo-binstall + - name: Install cargo-msrv + run: cargo binstall --no-confirm --force cargo-msrv + - name: Check MSRV for each workspace member run: | - rustup update --no-self-update + export PATH="$HOME/.cargo/bin:$PATH" + ./scripts/check-msrv.sh - name: Run release-plz uses: release-plz/action@v0.5 with: From 20014df8b7e64648cf771a56572e47f78911b712 Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Wed, 14 Jan 2026 11:39:57 +1300 Subject: [PATCH 095/114] feat: Read foreign account inputs and witnesses from transaction inputs (#2246) --- CHANGELOG.md | 1 + .../src/account/storage/header.rs | 167 +++++++- .../src/account/storage/map/mod.rs | 16 +- .../src/account/storage/slot/slot_id.rs | 22 + .../src/account/storage/slot/type.rs | 28 ++ .../src/block/account_tree/mod.rs | 7 + crates/miden-protocol/src/errors/mod.rs | 36 ++ .../src/transaction/inputs/mod.rs | 267 ++++++++++++- .../src/transaction/inputs/tests.rs | 375 ++++++++++++++++++ .../src/transaction/kernel/advice_inputs.rs | 17 +- crates/miden-tx/src/executor/exec_host.rs | 25 +- crates/miden-tx/src/executor/mod.rs | 3 + crates/miden-tx/src/host/tx_event.rs | 6 +- 13 files changed, 949 insertions(+), 21 deletions(-) create mode 100644 crates/miden-protocol/src/transaction/inputs/tests.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1edd194eba..2ef5f4d4f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). - Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). - Add `AccountId::parse()` helper function to parse both hex and bech32 formats ([#2223](https://github.com/0xMiden/miden-base/pull/2223)). +- Add `read_foreign_account_inputs()`, `read_vault_asset_witnesses()`, and `read_storage_map_witness()` for `TransactionInputs` ([#2246](https://github.com/0xMiden/miden-base/pull/2246)). ### Changes diff --git a/crates/miden-protocol/src/account/storage/header.rs b/crates/miden-protocol/src/account/storage/header.rs index 8f48bfe646..e6cb794ebf 100644 --- a/crates/miden-protocol/src/account/storage/header.rs +++ b/crates/miden-protocol/src/account/storage/header.rs @@ -1,3 +1,5 @@ +use alloc::collections::BTreeMap; +use alloc::format; use alloc::string::ToString; use alloc::vec::Vec; @@ -150,6 +152,49 @@ impl AccountStorageHeader { ::to_elements(self) } + /// Reconstructs an [`AccountStorageHeader`] from field elements with provided slot names. + /// + /// The elements are expected to be groups of 8 elements per slot: + /// `[[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE]` + pub fn try_from_elements( + elements: &[Felt], + slot_names: &BTreeMap, + ) -> Result { + if !elements.len().is_multiple_of(StorageSlot::NUM_ELEMENTS) { + return Err(AccountError::other( + "storage header elements length must be divisible by 8", + )); + } + + let mut slots = Vec::new(); + for chunk in elements.chunks_exact(StorageSlot::NUM_ELEMENTS) { + // Parse slot type from second element. + let slot_type_felt = chunk[1]; + let slot_type = slot_type_felt.try_into()?; + + // Parse slot ID from third and fourth elements. + let slot_id_suffix = chunk[2]; + let slot_id_prefix = chunk[3]; + let parsed_slot_id = StorageSlotId::new(slot_id_suffix, slot_id_prefix); + + // Retrieve slot name from the map. + let slot_name = slot_names.get(&parsed_slot_id).cloned().ok_or(AccountError::other( + format!("slot name not found for slot ID {}", parsed_slot_id), + ))?; + + // Parse slot value from last 4 elements. + let slot_value = Word::new([chunk[4], chunk[5], chunk[6], chunk[7]]); + + let slot_header = StorageSlotHeader::new(slot_name, slot_type, slot_value); + slots.push(slot_header); + } + + // Sort slots by ID. + slots.sort_by_key(|slot| slot.id()); + + Self::new(slots) + } + /// Returns the commitment to the [`AccountStorage`] this header represents. pub fn to_commitment(&self) -> Word { ::to_commitment(self) @@ -301,12 +346,15 @@ impl Deserializable for StorageSlotHeader { #[cfg(test)] mod tests { + use alloc::collections::BTreeMap; + use alloc::string::ToString; + use miden_core::Felt; use miden_core::utils::{Deserializable, Serializable}; use super::AccountStorageHeader; use crate::Word; - use crate::account::{AccountStorage, StorageSlotType}; + use crate::account::{AccountStorage, StorageSlotHeader, StorageSlotName, StorageSlotType}; use crate::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; #[test] @@ -344,4 +392,121 @@ mod tests { // assert deserialized == storage header assert_eq!(storage_header, deserialized); } + + #[test] + fn test_to_elements_from_elements_empty() { + // Construct empty header. + let empty_header = AccountStorageHeader::new(vec![]).unwrap(); + let empty_elements = empty_header.to_elements(); + + // Call from_elements. + let empty_slot_names = BTreeMap::new(); + let reconstructed_empty = + AccountStorageHeader::try_from_elements(&empty_elements, &empty_slot_names).unwrap(); + assert_eq!(empty_header, reconstructed_empty); + } + + #[test] + fn test_to_elements_from_elements_single_slot() { + // Construct single slot header. + let slot_name1 = StorageSlotName::new("test::value::slot1".to_string()).unwrap(); + let slot1 = StorageSlotHeader::new( + slot_name1, + StorageSlotType::Value, + Word::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]), + ); + + let single_slot_header = AccountStorageHeader::new(vec![slot1.clone()]).unwrap(); + let single_elements = single_slot_header.to_elements(); + + // Call from_elements. + let slot_names = BTreeMap::from([(slot1.id(), slot1.name().clone())]); + let reconstructed_single = + AccountStorageHeader::try_from_elements(&single_elements, &slot_names).unwrap(); + + assert_eq!(single_slot_header, reconstructed_single); + } + + #[test] + fn test_to_elements_from_elements_multiple_slot() { + // Construct multi slot header. + let slot_name2 = StorageSlotName::new("test::map::slot2".to_string()).unwrap(); + let slot_name3 = StorageSlotName::new("test::value::slot3".to_string()).unwrap(); + + let slot2 = StorageSlotHeader::new( + slot_name2, + StorageSlotType::Map, + Word::new([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]), + ); + let slot3 = StorageSlotHeader::new( + slot_name3, + StorageSlotType::Value, + Word::new([Felt::new(9), Felt::new(10), Felt::new(11), Felt::new(12)]), + ); + + let mut slots = vec![slot2, slot3]; + slots.sort_by_key(|slot| slot.id()); + let multi_slot_header = AccountStorageHeader::new(slots.clone()).unwrap(); + let multi_elements = multi_slot_header.to_elements(); + + // Call from_elements. + let slot_names = BTreeMap::from([ + (slots[0].id(), slots[0].name.clone()), + (slots[1].id(), slots[1].name.clone()), + ]); + let reconstructed_multi = + AccountStorageHeader::try_from_elements(&multi_elements, &slot_names).unwrap(); + + assert_eq!(multi_slot_header, reconstructed_multi); + } + + #[test] + fn test_from_elements_errors() { + // Test with invalid length (not divisible by 8). + let invalid_elements = vec![Felt::new(1), Felt::new(2), Felt::new(3)]; + let empty_slot_names = BTreeMap::new(); + assert!( + AccountStorageHeader::try_from_elements(&invalid_elements, &empty_slot_names).is_err() + ); + + // Test with invalid slot type. + let mut invalid_type_elements = vec![crate::ZERO; 8]; + invalid_type_elements[1] = Felt::new(5); // Invalid slot type. + assert!( + AccountStorageHeader::try_from_elements(&invalid_type_elements, &empty_slot_names) + .is_err() + ); + } + + #[test] + fn test_from_elements_with_slot_names() { + use alloc::collections::BTreeMap; + + // Create original slot with known name. + let slot_name1 = StorageSlotName::new("test::value::slot1".to_string()).unwrap(); + let slot1 = StorageSlotHeader::new( + slot_name1.clone(), + StorageSlotType::Value, + Word::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]), + ); + + // Serialize the single slot to elements + let elements = slot1.to_elements(); + + // Create slot names map using the slot's ID + let mut slot_names = BTreeMap::new(); + slot_names.insert(slot1.id(), slot_name1.clone()); + + // Test from_elements with provided slot names on raw slot elements. + let reconstructed_header = + AccountStorageHeader::try_from_elements(&elements, &slot_names).unwrap(); + + // Verify that the original slot names are preserved. + assert_eq!(reconstructed_header.slots().count(), 1); + let reconstructed_slot = reconstructed_header.slots().next().unwrap(); + + assert_eq!(slot_name1.as_str(), reconstructed_slot.name().as_str()); + assert_eq!(slot1.slot_type(), reconstructed_slot.slot_type()); + assert_eq!(slot1.value(), reconstructed_slot.value()); + } } diff --git a/crates/miden-protocol/src/account/storage/map/mod.rs b/crates/miden-protocol/src/account/storage/map/mod.rs index 2882496190..31bb7fa355 100644 --- a/crates/miden-protocol/src/account/storage/map/mod.rs +++ b/crates/miden-protocol/src/account/storage/map/mod.rs @@ -8,7 +8,7 @@ use crate::account::StorageMapDelta; use crate::crypto::merkle::InnerNodeInfo; use crate::crypto::merkle::smt::{LeafIndex, SMT_DEPTH, Smt, SmtLeaf}; use crate::errors::StorageMapError; -use crate::{AccountError, Felt, Hasher}; +use crate::{AccountError, Hasher}; mod partial; pub use partial::PartialStorageMap; @@ -204,16 +204,22 @@ impl StorageMap { self.entries } + // UTILITY FUNCTIONS + // -------------------------------------------------------------------------------------------- + /// Hashes the given key to get the key of the SMT. pub fn hash_key(raw_key: Word) -> Word { Hasher::hash_elements(raw_key.as_elements()) } - // TODO: Replace with https://github.com/0xMiden/crypto/issues/515 once implemented. + /// Returns leaf index of a raw map key. + pub fn map_key_to_leaf_index(raw_key: Word) -> LeafIndex { + Self::hash_key(raw_key).into() + } + /// Returns the leaf index of a map key. - pub fn hashed_map_key_to_leaf_index(hashed_map_key: Word) -> Felt { - // The third element in an SMT key is the index. - hashed_map_key[3] + pub fn hashed_map_key_to_leaf_index(hashed_map_key: Word) -> LeafIndex { + hashed_map_key.into() } } diff --git a/crates/miden-protocol/src/account/storage/slot/slot_id.rs b/crates/miden-protocol/src/account/storage/slot/slot_id.rs index 1fcaf52e4e..b22b46b2dd 100644 --- a/crates/miden-protocol/src/account/storage/slot/slot_id.rs +++ b/crates/miden-protocol/src/account/storage/slot/slot_id.rs @@ -5,6 +5,13 @@ use core::hash::Hash; use miden_core::utils::hash_string_to_word; use crate::Felt; +use crate::utils::serde::{ + ByteReader, + ByteWriter, + Deserializable, + DeserializationError, + Serializable, +}; /// The partial hash of a [`StorageSlotName`](super::StorageSlotName). /// @@ -91,6 +98,21 @@ impl Display for StorageSlotId { } } +impl Serializable for StorageSlotId { + fn write_into(&self, target: &mut W) { + self.suffix.write_into(target); + self.prefix.write_into(target); + } +} + +impl Deserializable for StorageSlotId { + fn read_from(source: &mut R) -> Result { + let suffix = Felt::read_from(source)?; + let prefix = Felt::read_from(source)?; + Ok(StorageSlotId::new(suffix, prefix)) + } +} + // TESTS // ================================================================================================ diff --git a/crates/miden-protocol/src/account/storage/slot/type.rs b/crates/miden-protocol/src/account/storage/slot/type.rs index 9fac8cada1..32da9c7e27 100644 --- a/crates/miden-protocol/src/account/storage/slot/type.rs +++ b/crates/miden-protocol/src/account/storage/slot/type.rs @@ -1,6 +1,8 @@ use alloc::string::ToString; use core::fmt::Display; +use miden_core::{ONE, ZERO}; + use crate::utils::serde::{ ByteReader, ByteWriter, @@ -54,6 +56,20 @@ impl TryFrom for StorageSlotType { } } +impl TryFrom for StorageSlotType { + type Error = AccountError; + + fn try_from(value: Felt) -> Result { + if value == ZERO { + Ok(StorageSlotType::Value) + } else if value == ONE { + Ok(StorageSlotType::Map) + } else { + Err(AccountError::other("invalid storage slot type")) + } + } +} + impl Display for StorageSlotType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { @@ -100,6 +116,7 @@ mod tests { use miden_core::utils::{Deserializable, Serializable}; use crate::account::StorageSlotType; + use crate::{Felt, FieldElement}; #[test] fn test_serde_account_storage_slot_type() { @@ -112,4 +129,15 @@ mod tests { assert_eq!(type_0, deserialized_0); assert_eq!(type_1, deserialized_1); } + + #[test] + fn test_storage_slot_type_from_felt() { + let felt = Felt::ZERO; + let slot_type = StorageSlotType::try_from(felt).unwrap(); + assert_eq!(slot_type, StorageSlotType::Value); + + let felt = Felt::ONE; + let slot_type = StorageSlotType::try_from(felt).unwrap(); + assert_eq!(slot_type, StorageSlotType::Map); + } } diff --git a/crates/miden-protocol/src/block/account_tree/mod.rs b/crates/miden-protocol/src/block/account_tree/mod.rs index f48ce04425..2bff2b5f44 100644 --- a/crates/miden-protocol/src/block/account_tree/mod.rs +++ b/crates/miden-protocol/src/block/account_tree/mod.rs @@ -1,6 +1,8 @@ use alloc::string::ToString; use alloc::vec::Vec; +use miden_crypto::merkle::smt::LeafIndex; + use crate::Word; use crate::account::{AccountId, AccountIdPrefix}; use crate::crypto::merkle::MerkleError; @@ -46,6 +48,11 @@ pub fn smt_key_to_account_id(key: Word) -> AccountId { .expect("account tree should only contain valid IDs") } +/// Converts an AccountId to an SMT leaf index for use with MerkleStore operations. +pub fn account_id_to_smt_index(account_id: AccountId) -> LeafIndex { + account_id_to_smt_key(account_id).into() +} + // ACCOUNT TREE // ================================================================================================ diff --git a/crates/miden-protocol/src/errors/mod.rs b/crates/miden-protocol/src/errors/mod.rs index 2dfd04eaa2..73c2b0688a 100644 --- a/crates/miden-protocol/src/errors/mod.rs +++ b/crates/miden-protocol/src/errors/mod.rs @@ -8,6 +8,7 @@ use miden_assembly::diagnostics::reporting::PrintDiagnostic; use miden_core::mast::MastForestError; use miden_core::{EventId, Felt}; use miden_crypto::merkle::mmr::MmrError; +use miden_crypto::merkle::smt::{SmtLeafError, SmtProofError}; use miden_crypto::utils::HexParseError; use miden_processor::DeserializationError; use thiserror::Error; @@ -694,6 +695,41 @@ pub enum TransactionInputError { TooManyInputNotes(usize), } +// TRANSACTION INPUTS EXTRACTION ERROR +// =============================================================================================== + +#[derive(Debug, Error)] +pub enum TransactionInputsExtractionError { + #[error("specified foreign account id matches the transaction input's account id")] + AccountNotForeign, + #[error("foreign account data not found in advice map for account {0}")] + ForeignAccountNotFound(AccountId), + #[error("foreign account code not found for account {0}")] + ForeignAccountCodeNotFound(AccountId), + #[error("storage header data not found in advice map for account {0}")] + StorageHeaderNotFound(AccountId), + #[error("failed to handle account data")] + AccountError(#[from] AccountError), + #[error("failed to handle merkle data")] + MerkleError(#[from] MerkleError), + #[error("failed to handle account tree data")] + AccountTreeError(#[from] AccountTreeError), + #[error("missing vault root from Merkle store")] + MissingVaultRoot, + #[error("missing storage map root from Merkle store")] + MissingMapRoot, + #[error("failed to construct SMT proof")] + SmtProofError(#[from] SmtProofError), + #[error("failed to construct asset witness")] + AssetError(#[from] AssetError), + #[error("failed to handle storage map data")] + StorageMapError(#[from] StorageMapError), + #[error("failed to convert elements to leaf index: {0}")] + LeafConversionError(String), + #[error("failed to construct SMT leaf")] + SmtLeafError(#[from] SmtLeafError), +} + // TRANSACTION OUTPUT ERROR // =============================================================================================== diff --git a/crates/miden-protocol/src/transaction/inputs/mod.rs b/crates/miden-protocol/src/transaction/inputs/mod.rs index 7e131e360a..ef3d6b367d 100644 --- a/crates/miden-protocol/src/transaction/inputs/mod.rs +++ b/crates/miden-protocol/src/transaction/inputs/mod.rs @@ -1,21 +1,41 @@ +use alloc::collections::{BTreeMap, BTreeSet}; use alloc::vec::Vec; use core::fmt::Debug; use miden_core::utils::{Deserializable, Serializable}; +use miden_crypto::merkle::NodeIndex; +use miden_crypto::merkle::smt::{LeafIndex, SmtLeaf, SmtProof}; use super::PartialBlockchain; -use crate::TransactionInputError; -use crate::account::{AccountCode, PartialAccount}; -use crate::asset::AssetWitness; +use crate::account::{ + AccountCode, + AccountHeader, + AccountId, + AccountStorageHeader, + PartialAccount, + PartialStorage, + StorageMap, + StorageMapWitness, + StorageSlotId, + StorageSlotName, +}; +use crate::asset::{AssetVaultKey, AssetWitness, PartialVault}; +use crate::block::account_tree::{AccountWitness, account_id_to_smt_index}; use crate::block::{BlockHeader, BlockNumber}; +use crate::crypto::merkle::SparseMerklePath; +use crate::errors::TransactionInputsExtractionError; use crate::note::{Note, NoteInclusionProof}; -use crate::transaction::{TransactionArgs, TransactionScript}; +use crate::transaction::{TransactionAdviceInputs, TransactionArgs, TransactionScript}; +use crate::{Felt, TransactionInputError, Word}; + +#[cfg(test)] +mod tests; mod account; pub use account::AccountInputs; mod notes; -use miden_processor::AdviceInputs; +use miden_processor::{AdviceInputs, SMT_DEPTH}; pub use notes::{InputNote, InputNotes, ToInputNoteCommitments}; // TRANSACTION INPUTS @@ -33,6 +53,8 @@ pub struct TransactionInputs { foreign_account_code: Vec, /// Pre-fetched asset witnesses for note assets and the fee asset. asset_witnesses: Vec, + /// Storage slot names for foreign accounts. + foreign_account_slot_names: BTreeMap, } impl TransactionInputs { @@ -89,6 +111,7 @@ impl TransactionInputs { advice_inputs: AdviceInputs::default(), foreign_account_code: Vec::new(), asset_witnesses: Vec::new(), + foreign_account_slot_names: BTreeMap::new(), }) } @@ -110,6 +133,15 @@ impl TransactionInputs { self } + /// Replaces the transaction inputs and assigns the given foreign account slot names. + pub fn with_foreign_account_slot_names( + mut self, + foreign_account_slot_names: BTreeMap, + ) -> Self { + self.foreign_account_slot_names = foreign_account_slot_names; + self + } + /// Replaces the transaction inputs and assigns the given advice inputs. pub fn with_advice_inputs(mut self, advice_inputs: AdviceInputs) -> Self { self.set_advice_inputs(advice_inputs); @@ -183,6 +215,11 @@ impl TransactionInputs { &self.asset_witnesses } + /// Returns the foreign account storage slot names. + pub fn foreign_account_slot_names(&self) -> &BTreeMap { + &self.foreign_account_slot_names + } + /// Returns the advice inputs to be consumed in the transaction. pub fn advice_inputs(&self) -> &AdviceInputs { &self.advice_inputs @@ -193,6 +230,167 @@ impl TransactionInputs { &self.tx_args } + // DATA EXTRACTORS + // -------------------------------------------------------------------------------------------- + + /// Reads the storage map witness for the given account and map key. + pub fn read_storage_map_witness( + &self, + map_root: Word, + map_key: Word, + ) -> Result { + // Convert map key into the index at which the key-value pair for this key is stored + let leaf_index = StorageMap::map_key_to_leaf_index(map_key); + + // Construct sparse Merkle path. + let merkle_path = self.advice_inputs.store.get_path(map_root, leaf_index.into())?; + let sparse_path = SparseMerklePath::from_sized_iter(merkle_path.path)?; + + // Construct SMT leaf. + let merkle_node = self.advice_inputs.store.get_node(map_root, leaf_index.into())?; + let smt_leaf_elements = self + .advice_inputs + .map + .get(&merkle_node) + .ok_or(TransactionInputsExtractionError::MissingVaultRoot)?; + let smt_leaf = smt_leaf_from_elements(smt_leaf_elements, leaf_index)?; + + // Construct SMT proof and witness. + let smt_proof = SmtProof::new(sparse_path, smt_leaf)?; + let storage_witness = StorageMapWitness::new(smt_proof, [map_key])?; + + Ok(storage_witness) + } + + /// Reads the vault asset witnesses for the given account and vault keys. + pub fn read_vault_asset_witnesses( + &self, + vault_root: Word, + vault_keys: BTreeSet, + ) -> Result, TransactionInputsExtractionError> { + let mut asset_witnesses = Vec::new(); + for vault_key in vault_keys { + let smt_index = vault_key.to_leaf_index(); + // Construct sparse Merkle path. + let merkle_path = self.advice_inputs.store.get_path(vault_root, smt_index.into())?; + let sparse_path = SparseMerklePath::from_sized_iter(merkle_path.path)?; + + // Construct SMT leaf. + let merkle_node = self.advice_inputs.store.get_node(vault_root, smt_index.into())?; + let smt_leaf_elements = self + .advice_inputs + .map + .get(&merkle_node) + .ok_or(TransactionInputsExtractionError::MissingVaultRoot)?; + let smt_leaf = smt_leaf_from_elements(smt_leaf_elements, smt_index)?; + + // Construct SMT proof and witness. + let smt_proof = SmtProof::new(sparse_path, smt_leaf)?; + let asset_witness = AssetWitness::new(smt_proof)?; + asset_witnesses.push(asset_witness); + } + Ok(asset_witnesses) + } + + /// Reads AccountInputs for a foreign account from the advice inputs. + /// + /// This function reverses the process of [`TransactionAdviceInputs::add_foreign_accounts`] by: + /// 1. Reading the account header from the advice map using the account_id_key. + /// 2. Building a PartialAccount from the header and foreign account code. + /// 3. Creating an AccountWitness. + pub fn read_foreign_account_inputs( + &self, + account_id: AccountId, + ) -> Result { + if account_id == self.account().id() { + return Err(TransactionInputsExtractionError::AccountNotForeign); + } + + // Read the account header elements from the advice map. + let account_id_key = TransactionAdviceInputs::account_id_map_key(account_id); + let header_elements = self + .advice_inputs + .map + .get(&account_id_key) + .ok_or(TransactionInputsExtractionError::ForeignAccountNotFound(account_id))?; + + // Parse the header from elements. + let header = AccountHeader::try_from_elements(header_elements)?; + + // Construct and return account inputs. + let partial_account = self.read_foreign_partial_account(&header)?; + let witness = self.read_foreign_account_witness(&header)?; + Ok(AccountInputs::new(partial_account, witness)) + } + + /// Reads a foreign partial account from the advice inputs based on the account ID corresponding + /// to the provided header. + fn read_foreign_partial_account( + &self, + header: &AccountHeader, + ) -> Result { + // Derive the partial vault from the header. + let partial_vault = PartialVault::new(header.vault_root()); + + // Find the corresponding foreign account code. + let account_code = self + .foreign_account_code + .iter() + .find(|code| code.commitment() == header.code_commitment()) + .ok_or(TransactionInputsExtractionError::ForeignAccountCodeNotFound(header.id()))? + .clone(); + + // Try to get storage header from advice map using storage commitment as key. + let storage_header_elements = self + .advice_inputs + .map + .get(&header.storage_commitment()) + .ok_or(TransactionInputsExtractionError::StorageHeaderNotFound(header.id()))?; + + // Get slot names for this foreign account, or use empty map if not available. + let storage_header = AccountStorageHeader::try_from_elements( + storage_header_elements, + self.foreign_account_slot_names(), + )?; + + // Build partial storage. + let partial_storage = PartialStorage::new(storage_header, [])?; + + // Create the partial account. + let partial_account = PartialAccount::new( + header.id(), + header.nonce(), + account_code, + partial_storage, + partial_vault, + None, // We know that foreign accounts are existing accounts so a seed is not required. + )?; + + Ok(partial_account) + } + + /// Reads a foreign account witness from the advice inputs based on the account ID corresponding + /// to the provided header. + fn read_foreign_account_witness( + &self, + header: &AccountHeader, + ) -> Result { + // Get the account tree root from the block header. + let account_tree_root = self.block_header.account_root(); + let leaf_index: NodeIndex = account_id_to_smt_index(header.id()).into(); + + // Get the Merkle path from the merkle store. + let merkle_path = self.advice_inputs.store.get_path(account_tree_root, leaf_index)?; + + // Convert the Merkle path to SparseMerklePath. + let sparse_path = SparseMerklePath::from_sized_iter(merkle_path.path)?; + + // Create the account witness. + let witness = AccountWitness::new(header.id(), header.commitment(), sparse_path)?; + + Ok(witness) + } + // CONVERSIONS // -------------------------------------------------------------------------------------------- @@ -222,6 +420,9 @@ impl TransactionInputs { } } +// SERIALIZATION / DESERIALIZATION +// ================================================================================================ + impl Serializable for TransactionInputs { fn write_into(&self, target: &mut W) { self.account.write_into(target); @@ -232,6 +433,7 @@ impl Serializable for TransactionInputs { self.advice_inputs.write_into(target); self.foreign_account_code.write_into(target); self.asset_witnesses.write_into(target); + self.foreign_account_slot_names.write_into(target); } } @@ -247,6 +449,8 @@ impl Deserializable for TransactionInputs { let advice_inputs = AdviceInputs::read_from(source)?; let foreign_account_code = Vec::::read_from(source)?; let asset_witnesses = Vec::::read_from(source)?; + let foreign_account_slot_names = + BTreeMap::::read_from(source)?; Ok(TransactionInputs { account, @@ -257,6 +461,7 @@ impl Deserializable for TransactionInputs { advice_inputs, foreign_account_code, asset_witnesses, + foreign_account_slot_names, }) } } @@ -264,6 +469,58 @@ impl Deserializable for TransactionInputs { // HELPER FUNCTIONS // ================================================================================================ +// TODO(sergerad): Move this fn to crypto SmtLeaf::try_from_elements. +pub fn smt_leaf_from_elements( + elements: &[Felt], + leaf_index: LeafIndex, +) -> Result { + use miden_crypto::merkle::smt::SmtLeaf; + + // Based on the miden-crypto SMT leaf serialization format. + + if elements.is_empty() { + return Ok(SmtLeaf::new_empty(leaf_index)); + } + + // Elements should be organized into a contiguous array of K/V Words (4 Felts each). + if !elements.len().is_multiple_of(8) { + return Err(TransactionInputsExtractionError::LeafConversionError( + "invalid SMT leaf format: elements length must be divisible by 8".into(), + )); + } + + let num_entries = elements.len() / 8; + + if num_entries == 1 { + // Single entry. + let key = Word::new([elements[0], elements[1], elements[2], elements[3]]); + let value = Word::new([elements[4], elements[5], elements[6], elements[7]]); + Ok(SmtLeaf::new_single(key, value)) + } else { + // Multiple entries. + let mut entries = Vec::with_capacity(num_entries); + // Read k/v pairs from each entry. + for i in 0..num_entries { + let base_idx = i * 8; + let key = Word::new([ + elements[base_idx], + elements[base_idx + 1], + elements[base_idx + 2], + elements[base_idx + 3], + ]); + let value = Word::new([ + elements[base_idx + 4], + elements[base_idx + 5], + elements[base_idx + 6], + elements[base_idx + 7], + ]); + entries.push((key, value)); + } + let leaf = SmtLeaf::new_multiple(entries)?; + Ok(leaf) + } +} + /// Validates whether the provided note belongs to the note tree of the specified block. fn validate_is_in_block( note: &Note, diff --git a/crates/miden-protocol/src/transaction/inputs/tests.rs b/crates/miden-protocol/src/transaction/inputs/tests.rs new file mode 100644 index 0000000000..09500ef25b --- /dev/null +++ b/crates/miden-protocol/src/transaction/inputs/tests.rs @@ -0,0 +1,375 @@ +use alloc::string::ToString; +use alloc::vec; +use std::collections::BTreeMap; +use std::vec::Vec; + +use miden_core::utils::{Deserializable, Serializable}; + +use crate::account::{ + AccountCode, + AccountHeader, + AccountId, + AccountStorageHeader, + PartialAccount, + PartialStorage, + StorageSlotHeader, + StorageSlotName, + StorageSlotType, +}; +use crate::asset::PartialVault; +use crate::errors::TransactionInputsExtractionError; +use crate::testing::account_id::{ + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, +}; +use crate::transaction::TransactionInputs; +use crate::{Felt, Word}; + +#[test] +fn test_read_foreign_account_inputs_missing_data() { + let native_account_id = + AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + let foreign_account_id = + AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2).unwrap(); + + // Create minimal transaction inputs with empty advice map. + let code = AccountCode::mock(); + let storage_header = AccountStorageHeader::new(vec![]).unwrap(); + let partial_storage = PartialStorage::new(storage_header, []).unwrap(); + let partial_vault = PartialVault::new(Word::default()); + let partial_account = PartialAccount::new( + native_account_id, + Felt::new(10), + code, + partial_storage, + partial_vault, + None, + ) + .unwrap(); + + let tx_inputs = TransactionInputs { + account: partial_account, + block_header: crate::block::BlockHeader::mock(0, None, None, &[], Word::default()), + blockchain: crate::transaction::PartialBlockchain::default(), + input_notes: crate::transaction::InputNotes::new(vec![]).unwrap(), + tx_args: crate::transaction::TransactionArgs::default(), + advice_inputs: crate::vm::AdviceInputs::default(), + foreign_account_code: Vec::new(), + asset_witnesses: Vec::new(), + foreign_account_slot_names: BTreeMap::new(), + }; + + // Try to read foreign account that doesn't exist in advice map. + let result = tx_inputs.read_foreign_account_inputs(foreign_account_id); + + assert!( + matches!(result, Err(TransactionInputsExtractionError::ForeignAccountNotFound(id)) if id == foreign_account_id) + ); +} + +#[test] +fn test_read_foreign_account_inputs_with_storage_data() { + use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2; + + let native_account_id = + AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + let foreign_account_id = + AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2).unwrap(); + + // Create minimal transaction inputs with proper advice map. + let code = AccountCode::mock(); + let storage_header = AccountStorageHeader::new(vec![]).unwrap(); + let partial_storage = PartialStorage::new(storage_header, []).unwrap(); + let partial_vault = PartialVault::new(Word::default()); + let partial_account = PartialAccount::new( + native_account_id, + Felt::new(10), + code.clone(), + partial_storage, + partial_vault, + None, + ) + .unwrap(); + + // Create foreign account header and storage data. + let foreign_header = AccountHeader::new( + foreign_account_id, + Felt::new(5), + Word::default(), + Word::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]), + code.commitment(), + ); + + // Create storage slots with test data. + let slot_name1 = StorageSlotName::new("test::slot1::value".to_string()).unwrap(); + let slot_name2 = StorageSlotName::new("test::slot2::value".to_string()).unwrap(); + let slot1 = StorageSlotHeader::new( + slot_name1, + StorageSlotType::Value, + Word::new([Felt::new(10), Felt::new(20), Felt::new(30), Felt::new(40)]), + ); + let slot2 = StorageSlotHeader::new( + slot_name2, + StorageSlotType::Map, + Word::new([Felt::new(50), Felt::new(60), Felt::new(70), Felt::new(80)]), + ); + + let mut slots = vec![slot1, slot2]; + slots.sort_by_key(|slot| slot.id()); + let foreign_storage_header = AccountStorageHeader::new(slots.clone()).unwrap(); + + // Create advice inputs with both account header and storage header. + let mut advice_inputs = crate::vm::AdviceInputs::default(); + let account_id_key = + crate::transaction::TransactionAdviceInputs::account_id_map_key(foreign_account_id); + advice_inputs.map.insert(account_id_key, foreign_header.as_elements().to_vec()); + advice_inputs + .map + .insert(foreign_header.storage_commitment(), foreign_storage_header.to_elements()); + + let foreign_account_slot_names = BTreeMap::from([ + (slots[0].id(), slots[0].name().clone()), + (slots[1].id(), slots[1].name().clone()), + ]); + let tx_inputs = TransactionInputs { + account: partial_account, + block_header: crate::block::BlockHeader::mock(0, None, None, &[], Word::default()), + blockchain: crate::transaction::PartialBlockchain::default(), + input_notes: crate::transaction::InputNotes::new(vec![]).unwrap(), + tx_args: crate::transaction::TransactionArgs::default(), + advice_inputs, + foreign_account_code: vec![code], + asset_witnesses: Vec::new(), + foreign_account_slot_names, + }; + + // Try to read foreign account with storage data. + // Should succeed and create partial account with proper storage. + let account_inputs = tx_inputs.read_foreign_account_inputs(foreign_account_id).unwrap(); + assert_eq!(account_inputs.id(), foreign_account_id); + assert_eq!(account_inputs.account().nonce(), Felt::new(5)); + + // Verify storage was properly reconstructed. + let storage = account_inputs.account().storage(); + assert_eq!(storage.header().slots().count(), 2); + + // Verify witness data is valid. + let witness = account_inputs.witness(); + assert_eq!(witness.id(), foreign_account_id); + + // Verify the witness can compute a valid account root. + let computed_root = account_inputs.compute_account_root(); + assert!( + computed_root.is_ok(), + "Failed to compute account root from witness: {:?}", + computed_root.err() + ); + + // Test that the witness path has the expected depth for SMT. + assert_eq!(witness.path().depth(), 64, "Witness path should have SMT depth of 64"); +} + +#[test] +fn test_read_foreign_account_inputs_with_proper_witness() { + use crate::block::account_tree::AccountTree; + use crate::crypto::merkle::smt::Smt; + use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2; + + let native_account_id = + AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + let foreign_account_id = + AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2).unwrap(); + + // Create a native account. + let code = AccountCode::mock(); + let storage_header = AccountStorageHeader::new(vec![]).unwrap(); + let partial_storage = PartialStorage::new(storage_header, []).unwrap(); + let partial_vault = PartialVault::new(Word::default()); + let native_account = PartialAccount::new( + native_account_id, + Felt::new(10), + code.clone(), + partial_storage, + partial_vault, + None, + ) + .unwrap(); + + // Create a foreign account with proper commitment. + let foreign_header = AccountHeader::new( + foreign_account_id, + Felt::new(5), + Word::default(), + Word::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]), + code.commitment(), + ); + + // Create storage header for the foreign account. + let foreign_storage_header = AccountStorageHeader::new(vec![]).unwrap(); + + // Create an account tree and insert both accounts to get proper Merkle paths. + let mut account_tree = AccountTree::::default(); + + // Insert native account. + let native_commitment = AccountHeader::from(&native_account).commitment(); + account_tree.insert(native_account_id, native_commitment).unwrap(); + + // Insert foreign account. + let _foreign_partial_account = PartialAccount::new( + foreign_account_id, + Felt::new(5), + code.clone(), + PartialStorage::new(foreign_storage_header.clone(), []).unwrap(), + PartialVault::new(Word::default()), + None, + ) + .unwrap(); + account_tree.insert(foreign_account_id, foreign_header.commitment()).unwrap(); + + // Get the account tree root and create witness. + let account_tree_root = account_tree.root(); + let foreign_witness = account_tree.open(foreign_account_id); + + // Create advice inputs with proper Merkle store data. + let mut advice_inputs = crate::vm::AdviceInputs::default(); + + // Add account header to advice map. + let account_id_key = + crate::transaction::TransactionAdviceInputs::account_id_map_key(foreign_account_id); + advice_inputs.map.insert(account_id_key, foreign_header.as_elements().to_vec()); + + // Add storage header to advice map. + advice_inputs + .map + .insert(foreign_header.storage_commitment(), foreign_storage_header.to_elements()); + + // Add authenticated nodes from the witness to the Merkle store. + advice_inputs.store.extend(foreign_witness.authenticated_nodes()); + + // Add the account leaf to the advice map (needed for witness verification). + let leaf = foreign_witness.leaf(); + advice_inputs.map.insert(leaf.hash(), leaf.to_elements()); + + // Create block header with the account tree root. + let block_header = crate::block::BlockHeader::mock(0, None, None, &[], account_tree_root); + + let tx_inputs = TransactionInputs { + account: native_account, + block_header, + blockchain: crate::transaction::PartialBlockchain::default(), + input_notes: crate::transaction::InputNotes::new(vec![]).unwrap(), + tx_args: crate::transaction::TransactionArgs::default(), + advice_inputs, + foreign_account_code: vec![code], + asset_witnesses: Vec::new(), + foreign_account_slot_names: BTreeMap::new(), + }; + + // Test reading foreign account inputs. + // Should succeed and create proper witness. + let account_inputs = tx_inputs.read_foreign_account_inputs(foreign_account_id).unwrap(); + assert_eq!(account_inputs.id(), foreign_account_id); + assert_eq!(account_inputs.account().nonce(), Felt::new(5)); + + // Verify witness data. + let witness = account_inputs.witness(); + assert_eq!(witness.id(), foreign_account_id); + + // Verify the witness contains the expected account ID and can compute a root. + let computed_root = account_inputs.compute_account_root(); + assert!( + computed_root.is_ok(), + "Failed to compute account root from witness: {:?}", + computed_root.err() + ); + + // The computed root should be consistent - we're mainly testing that + // the witness was properly reconstructed from the Merkle store data. + let _computed_root_value = computed_root.unwrap(); + + // Test that the witness path has the expected depth (64 for SMT). + assert_eq!(witness.path().depth(), 64, "Witness path should have SMT depth of 64"); +} + +#[test] +fn test_transaction_inputs_serialization_with_foreign_slot_names() { + use miden_core::Felt; + + use crate::account::{ + AccountCode, + AccountId, + AccountStorageHeader, + PartialAccount, + PartialStorage, + StorageSlotName, + }; + use crate::asset::PartialVault; + use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE; + + // Create test account IDs + let native_account_id = + AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + + // Create some slot names and IDs. + let slot_name_1 = StorageSlotName::new("test::slot1::value".to_string()).unwrap(); + let slot_name_2 = StorageSlotName::new("test::slot2::map".to_string()).unwrap(); + let slot_name_3 = StorageSlotName::new("another::slot::value".to_string()).unwrap(); + + let slot_id_1 = slot_name_1.id(); + let slot_id_2 = slot_name_2.id(); + let slot_id_3 = slot_name_3.id(); + + // Create foreign account slot names map. + let mut foreign_account_slot_names = BTreeMap::new(); + foreign_account_slot_names.insert(slot_id_1, slot_name_1.clone()); + foreign_account_slot_names.insert(slot_id_2, slot_name_2.clone()); + foreign_account_slot_names.insert(slot_id_3, slot_name_3.clone()); + + // Create a basic TransactionInputs with foreign slot names. + let code = AccountCode::mock(); + let storage_header = AccountStorageHeader::new(vec![]).unwrap(); + let partial_storage = PartialStorage::new(storage_header, []).unwrap(); + let partial_vault = PartialVault::new(Word::default()); + let partial_account = PartialAccount::new( + native_account_id, + Felt::new(10), + code, + partial_storage, + partial_vault, + None, + ) + .unwrap(); + + let original_tx_inputs = TransactionInputs { + account: partial_account, + block_header: crate::block::BlockHeader::mock(0, None, None, &[], Word::default()), + blockchain: crate::transaction::PartialBlockchain::default(), + input_notes: crate::transaction::InputNotes::new(vec![]).unwrap(), + tx_args: crate::transaction::TransactionArgs::default(), + advice_inputs: crate::vm::AdviceInputs::default(), + foreign_account_code: Vec::new(), + asset_witnesses: Vec::new(), + foreign_account_slot_names, + }; + + // Test serialization roundtrip. + let serialized = original_tx_inputs.to_bytes(); + let deserialized = TransactionInputs::read_from_bytes(&serialized).unwrap(); + + // Verify that foreign account slot names are preserved. + assert_eq!( + original_tx_inputs.foreign_account_slot_names(), + deserialized.foreign_account_slot_names() + ); + + // Verify specific slot names. + let deserialized_slots = deserialized.foreign_account_slot_names(); + + // Check slots. + assert_eq!(deserialized_slots.get(&slot_id_1).unwrap(), &slot_name_1); + assert_eq!(deserialized_slots.get(&slot_id_2).unwrap(), &slot_name_2); + assert_eq!(deserialized_slots.get(&slot_id_3).unwrap(), &slot_name_3); + + // Verify the entire structure is identical. + assert_eq!(original_tx_inputs, deserialized); +} diff --git a/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs index c536f6e188..6f1423e37a 100644 --- a/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs +++ b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs @@ -106,6 +106,16 @@ impl TransactionAdviceInputs { .into_iter() } + // PUBLIC UTILITIES + // -------------------------------------------------------------------------------------------- + + /// Returns the advice map key where: + /// - the seed for native accounts is stored. + /// - the account header for foreign accounts is stored. + pub fn account_id_map_key(id: AccountId) -> Word { + Word::from([id.suffix(), id.prefix().as_felt(), ZERO, ZERO]) + } + // MUTATORS // -------------------------------------------------------------------------------------------- @@ -410,13 +420,6 @@ impl TransactionAdviceInputs { fn extend_merkle_store(&mut self, iter: impl Iterator) { self.0.store.extend(iter); } - - /// Returns the advice map key where: - /// - the seed for native accounts is stored. - /// - the account header for foreign accounts is stored. - fn account_id_map_key(id: AccountId) -> Word { - Word::from([id.suffix(), id.prefix().as_felt(), ZERO, ZERO]) - } } // CONVERSIONS diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index b24aa39e5a..7f4fa78114 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -13,7 +13,14 @@ use miden_processor::{ ProcessState, }; use miden_protocol::account::auth::PublicKeyCommitment; -use miden_protocol::account::{AccountCode, AccountDelta, AccountId, PartialAccount}; +use miden_protocol::account::{ + AccountCode, + AccountDelta, + AccountId, + PartialAccount, + StorageSlotId, + StorageSlotName, +}; use miden_protocol::assembly::debuginfo::Location; use miden_protocol::assembly::{SourceFile, SourceManagerSync, SourceSpan}; use miden_protocol::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; @@ -77,6 +84,9 @@ where /// This is required for re-executing the transaction, e.g. as part of transaction proving. accessed_foreign_account_code: Vec, + /// Storage slot names for foreign accounts accessed during transaction execution. + foreign_account_slot_names: BTreeMap, + /// Contains generated signatures (as a message |-> signature map) required for transaction /// execution. Once a signature was created for a given message, it is inserted into this map. /// After transaction execution, these can be inserted into the advice inputs to re-execute the @@ -127,6 +137,7 @@ where authenticator, ref_block, accessed_foreign_account_code: Vec::new(), + foreign_account_slot_names: BTreeMap::new(), generated_signatures: BTreeMap::new(), initial_fee_asset_balance, source_manager, @@ -141,6 +152,11 @@ where &self.tx_progress } + /// Returns a reference to the foreign account slot names collected during execution. + pub fn foreign_account_slot_names(&self) -> &BTreeMap { + &self.foreign_account_slot_names + } + // EVENT HANDLERS // -------------------------------------------------------------------------------------------- @@ -163,6 +179,11 @@ where let mut tx_advice_inputs = TransactionAdviceInputs::default(); tx_advice_inputs.add_foreign_accounts([&foreign_account_inputs]); + // Extract and store slot names for this foreign account and store. + foreign_account_inputs.storage().header().slots().for_each(|slot| { + self.foreign_account_slot_names.insert(slot.id(), slot.name().clone()); + }); + self.base_host.load_foreign_account_code(foreign_account_inputs.code()); // Add the foreign account's code to the list of accessed code. @@ -409,6 +430,7 @@ where Vec, BTreeMap>, TransactionProgress, + BTreeMap, ) { let (account_delta, input_notes, output_notes) = self.base_host.into_parts(); @@ -419,6 +441,7 @@ where self.accessed_foreign_account_code, self.generated_signatures, self.tx_progress, + self.foreign_account_slot_names, ) } } diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 105dbe7ff2..af7b87678d 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -367,6 +367,7 @@ fn build_executed_transaction Result { // Note that the account delta does not contain the removed transaction fee, so it is the // "pre-fee" delta of the transaction. + let ( pre_fee_account_delta, _input_notes, @@ -374,6 +375,7 @@ fn build_executed_transaction( } let active_account_id = process.get_active_account_id()?; - let hashed_map_key = StorageMap::hash_key(map_key); - let leaf_index = StorageMap::hashed_map_key_to_leaf_index(hashed_map_key); + let leaf_index: Felt = StorageMap::map_key_to_leaf_index(map_key) + .value() + .try_into() + .expect("expected key index to be a felt"); // For the native account we need to explicitly request the initial map root, // while for foreign accounts the current map root is always the initial one. From b82dc0b0f8f220f9c4839309ce11ef13582d2cf7 Mon Sep 17 00:00:00 2001 From: Marti Date: Wed, 14 Jan 2026 00:37:53 +0100 Subject: [PATCH 096/114] feat: insert unpadded note inputs into `advice_inputs` (#2232) --- CHANGELOG.md | 1 + .../asm/protocol/active_note.masm | 57 ++++++++++++---- crates/miden-protocol/asm/protocol/note.masm | 16 ++--- crates/miden-protocol/src/note/inputs.rs | 47 +++---------- .../src/transaction/kernel/memory.rs | 2 +- .../src/kernel_tests/tx/test_note.rs | 67 +++++++++++-------- docs/src/protocol_library.md | 1 - 7 files changed, 100 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ef5f4d4f7..eae7a5205b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Changes +- No longer pad the note inputs on insertion into advice map ([#2232](https://github.com/0xMiden/miden-base/pull/2232)). - Added proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index 3a32919912..9cda06a621 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -1,3 +1,4 @@ +use miden::core::crypto::hashes::rpo256 use miden::core::mem use miden::protocol::kernel_proc_offsets @@ -95,7 +96,7 @@ end #! #! Where: #! - dest_ptr is the memory address to write the note inputs. -#! - NOTE_INPUTS_COMMITMENT is the sequential hash of the padded note's inputs. +#! - NOTE_INPUTS_COMMITMENT is the commitment to the note's inputs. #! - INPUTS is the data corresponding to the note's inputs. #! #! Panics if: @@ -274,7 +275,11 @@ end #! Operand stack: [num_inputs, dest_ptr] proc write_inputs_to_memory # load the inputs from the advice map to the advice stack - adv.push_mapvaln + # we pad the number of inputs to the next multiple of 8 so that we can use the + # `pipe_double_words_to_memory` instruction. The padded zeros don't affect the commitment + # computation. + + adv.push_mapvaln.8 # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [advice_num_inputs, [INPUT_VALUES]] @@ -283,12 +288,6 @@ proc write_inputs_to_memory # OS => [num_inputs, advice_num_inputs, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] - # Validate the note inputs length. Round up the number of inputs to the next multiple of 8: that - # value should be equal to the length obtained from the `adv.push_mapvaln` procedure. - u32divmod.8 neq.0 add mul.8 - # OS => [rounded_up_num_inputs, advice_num_inputs, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] - # AS => [[INPUT_VALUES]] - assert_eq.err=ERR_NOTE_INVALID_NUMBER_OF_INPUTS # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] @@ -303,13 +302,45 @@ proc write_inputs_to_memory # OS => [even_num_words, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] - # prepare the stack for the `pipe_preimage_to_memory` procedure - dup.6 swap - # OS => [even_num_words, dest_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # compute the end pointer for writing the padded inputs (even_num_words * 4 elements) + dup.6 swap mul.4 add + # OS => [end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # AS => [[INPUT_VALUES]] + + # prepare the stack for the `pipe_double_words_to_memory` procedure. + # + # To match `rpo256::hash_elements` (used for NOTE_INPUTS_COMMITMENT), we set the first capacity + # element to `num_inputs % 8`. + dup.6 dup.6 + # OS => [num_inputs, write_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # AS => [[INPUT_VALUES]] + + u32divmod.8 swap drop + # OS => [num_inputs_mod_8, write_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # AS => [[INPUT_VALUES]] + + push.0.0.0 + # OS => [A, write_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr], where A = [0, 0, 0, num_inputs_mod_8] + # AS => [[INPUT_VALUES]] + + padw padw + # OS => [PAD, PAD, A, write_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] # write the inputs from the advice stack into memory - exec.mem::pipe_preimage_to_memory drop - # OS => [num_inputs, dest_ptr] + exec.mem::pipe_double_words_to_memory + # OS => [PERM, PERM, PERM, end_ptr', NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [] + + # extract the computed commitment from the hasher state + exec.rpo256::squeeze_digest + # OS => [COMPUTED_COMMITMENT, end_ptr', NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + + # drop end_ptr' + movup.4 drop + # OS => [COMPUTED_COMMITMENT, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + + # validate that the inputs written to memory match the inputs commitment + assert_eqw.err=ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT + # => [num_inputs, dest_ptr] end diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index 284c68d4ea..85730c9528 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -3,6 +3,9 @@ use miden::core::crypto::hashes::rpo256 use miden::core::math::u64 use miden::core::mem +# Re-export the max inputs per note constant. +pub use ::miden::protocol::util::note::MAX_INPUTS_PER_NOTE + # ERRORS # ================================================================================================= @@ -16,8 +19,6 @@ const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceede #! This procedure checks that the provided number of note inputs is within limits and then computes #! the commitment. #! -#! Notice that the note inputs are padded with zeros in case their number is not a multiple of 8. -#! #! If the number of note inputs is 0, procedure returns the empty word: [0, 0, 0, 0]. #! #! Inputs: [inputs_ptr, num_inputs] @@ -38,18 +39,11 @@ pub proc compute_inputs_commitment u32lte assert.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT # => [inputs_ptr, num_inputs] - # compute the inputs commitment - # - # TODO: refactor the hash computation process as part of the - # https://github.com/0xMiden/miden-base/issues/2036 and - # https://github.com/0xMiden/miden-base/issues/2129 - exec.rpo256::pad_and_hash_elements + # compute the inputs commitment (over the unpadded values) + exec.rpo256::hash_elements # => [INPUTS_COMMITMENT] end -# Re-export the max inputs per note constant. -pub use ::miden::protocol::util::note::MAX_INPUTS_PER_NOTE - #! Writes the assets data stored in the advice map to the memory specified by the provided #! destination pointer. #! diff --git a/crates/miden-protocol/src/note/inputs.rs b/crates/miden-protocol/src/note/inputs.rs index 57b3b9365d..7d7b04a0c6 100644 --- a/crates/miden-protocol/src/note/inputs.rs +++ b/crates/miden-protocol/src/note/inputs.rs @@ -8,7 +8,7 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{Felt, Hasher, MAX_INPUTS_PER_NOTE, WORD_SIZE, Word, ZERO}; +use crate::{Felt, Hasher, MAX_INPUTS_PER_NOTE, Word}; // NOTE INPUTS // ================================================================================================ @@ -18,9 +18,8 @@ use crate::{Felt, Hasher, MAX_INPUTS_PER_NOTE, WORD_SIZE, Word, ZERO}; /// A note can be associated with up to 1024 input values. Each value is represented by a single /// field element. Thus, note input values can contain up to ~8 KB of data. /// -/// All inputs associated with a note can be reduced to a single commitment which is computed by -/// first padding the inputs with ZEROs to the next multiple of 8, and then by computing a -/// sequential hash of the resulting elements. +/// All inputs associated with a note can be reduced to a single commitment which is computed as an +/// RPO256 hash over the input elements. #[derive(Clone, Debug)] pub struct NoteInputs { values: Vec, @@ -40,7 +39,9 @@ impl NoteInputs { return Err(NoteError::TooManyInputs(values.len())); } - Ok(pad_and_build(values)) + let commitment = Hasher::hash_elements(&values); + + Ok(Self { values, commitment }) } // PUBLIC ACCESSORS @@ -69,19 +70,14 @@ impl NoteInputs { } /// Returns the note's input as a vector of field elements. - /// - /// The format is `INPUTS || PADDING`, where: - /// - /// - INPUTS is the variable inputs for the note - /// - PADDING is the optional padding to align the data with a 2WORD boundary pub fn to_elements(&self) -> Vec { - pad_inputs(&self.values) + self.values.to_vec() } } impl Default for NoteInputs { fn default() -> Self { - pad_and_build(vec![]) + Self::new(vec![]).expect("empty values should be valid") } } @@ -111,31 +107,6 @@ impl TryFrom> for NoteInputs { } } -// HELPER FUNCTIONS -// ================================================================================================ - -/// Returns a vector with built from the provided inputs and padded to the next multiple of 8. -fn pad_inputs(inputs: &[Felt]) -> Vec { - const BLOCK_SIZE: usize = WORD_SIZE * 2; - - let padded_len = inputs.len().next_multiple_of(BLOCK_SIZE); - let mut padded_inputs = Vec::with_capacity(padded_len); - padded_inputs.extend(inputs.iter()); - padded_inputs.resize(padded_len, ZERO); - - padded_inputs -} - -/// Pad `values` and returns a new `NoteInputs`. -fn pad_and_build(values: Vec) -> NoteInputs { - let commitment = { - let padded_values = pad_inputs(&values); - Hasher::hash_elements(&padded_values) - }; - - NoteInputs { values, commitment } -} - // SERIALIZATION // ================================================================================================ @@ -168,7 +139,7 @@ mod tests { fn test_input_ordering() { // inputs are provided in reverse stack order let inputs = vec![Felt::new(1), Felt::new(2), Felt::new(3)]; - // we expect the inputs to be padded to length 16 and to remain in reverse stack order. + // we expect the inputs to remain in reverse stack order. let expected_ordering = vec![Felt::new(1), Felt::new(2), Felt::new(3)]; let note_inputs = NoteInputs::new(inputs).expect("note created should succeed"); diff --git a/crates/miden-protocol/src/transaction/kernel/memory.rs b/crates/miden-protocol/src/transaction/kernel/memory.rs index 9cbfcd5754..f5db5d4e1e 100644 --- a/crates/miden-protocol/src/transaction/kernel/memory.rs +++ b/crates/miden-protocol/src/transaction/kernel/memory.rs @@ -358,7 +358,7 @@ pub const NOTE_MEM_SIZE: MemoryAddress = 2048; // // Notice that note input values are not loaded to the memory, only their length. In order to obtain // the input values the advice map should be used: they are stored there as -// `INPUTS_COMMITMENT -> INPUTS || PADDING`. +// `INPUTS_COMMITMENT -> INPUTS`. // // As opposed to the asset values, input values are never used in kernel memory, so their presence // there is unnecessary. diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 5d761de2d1..c8dec803f5 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -29,7 +29,7 @@ use miden_protocol::testing::account_id::{ }; use miden_protocol::transaction::memory::ACTIVE_INPUT_NOTE_PTR; use miden_protocol::transaction::{OutputNote, TransactionArgs}; -use miden_protocol::{Felt, Word, ZERO}; +use miden_protocol::{Felt, Hasher, Word, ZERO}; use miden_standards::account::wallets::BasicWallet; use miden_standards::code_builder::CodeBuilder; use miden_standards::testing::note::NoteBuilder; @@ -198,43 +198,38 @@ async fn test_build_recipient() -> anyhow::Result<()> { // Define test values as Words let word_1 = Word::from([1, 2, 3, 4u32]); let word_2 = Word::from([5, 6, 7, 8u32]); - let word_3 = Word::from([9, 10, 11, 12u32]); - let word_4 = Word::from([13, 14, 15, 16u32]); const BASE_ADDR: u32 = 4000; let code = format!( " use miden::core::sys - use miden::protocol::note begin # put the values that will be hashed into the memory push.{word_1} push.{base_addr} mem_storew_be dropw push.{word_2} push.{addr_1} mem_storew_be dropw - push.{word_3} push.{addr_2} mem_storew_be dropw - push.{word_4} push.{addr_3} mem_storew_be dropw - # Test with 4 values + # Test with 4 values (needs padding to 8) push.{script_root} # SCRIPT_ROOT push.{serial_num} # SERIAL_NUM push.4.4000 # num_inputs, inputs_ptr exec.note::build_recipient # => [RECIPIENT_4] - # Test with 5 values + # Test with 5 values (needs padding to 8) push.{script_root} # SCRIPT_ROOT push.{serial_num} # SERIAL_NUM push.5.4000 # num_inputs, inputs_ptr exec.note::build_recipient # => [RECIPIENT_5, RECIPIENT_4] - # Test with 13 values + # Test with 8 values (no padding needed - exactly one rate block) push.{script_root} # SCRIPT_ROOT push.{serial_num} # SERIAL_NUM - push.13.4000 # num_inputs, inputs_ptr + push.8.4000 # num_inputs, inputs_ptr exec.note::build_recipient - # => [RECIPIENT_13, RECIPIENT_5, RECIPIENT_4] + # => [RECIPIENT_8, RECIPIENT_5, RECIPIENT_4] # truncate the stack exec.sys::truncate_stack @@ -242,38 +237,56 @@ async fn test_build_recipient() -> anyhow::Result<()> { ", word_1 = word_1, word_2 = word_2, - word_3 = word_3, - word_4 = word_4, base_addr = BASE_ADDR, addr_1 = BASE_ADDR + 4, - addr_2 = BASE_ADDR + 8, - addr_3 = BASE_ADDR + 12, script_root = note_script.root(), serial_num = serial_num, ); let exec_output = &tx_context.execute_code(&code).await?; - // Create expected recipients and get their digests - let note_inputs_4 = NoteInputs::new(word_1.to_vec())?; - let recipient_4 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_4); + // Create expected NoteInputs for each test case + let inputs_4 = word_1.to_vec(); + let note_inputs_4 = NoteInputs::new(inputs_4.clone())?; let mut inputs_5 = word_1.to_vec(); inputs_5.push(word_2[0]); - let note_inputs_5 = NoteInputs::new(inputs_5)?; - let recipient_5 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_5); + let note_inputs_5 = NoteInputs::new(inputs_5.clone())?; - let mut inputs_13 = word_1.to_vec(); - inputs_13.extend_from_slice(&word_2.to_vec()); - inputs_13.extend_from_slice(&word_3.to_vec()); - inputs_13.push(word_4[0]); - let note_inputs_13 = NoteInputs::new(inputs_13)?; - let recipient_13 = NoteRecipient::new(serial_num, note_script, note_inputs_13); + let mut inputs_8 = word_1.to_vec(); + inputs_8.extend_from_slice(&word_2.to_vec()); + let note_inputs_8 = NoteInputs::new(inputs_8.clone())?; + + // Create expected recipients and get their digests + let recipient_4 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_4.clone()); + let recipient_5 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_5.clone()); + let recipient_8 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_8.clone()); + + for note_inputs in [ + (note_inputs_4, inputs_4.clone()), + (note_inputs_5, inputs_5.clone()), + (note_inputs_8, inputs_8.clone()), + ] { + let inputs_advice_map_key = note_inputs.0.commitment(); + assert_eq!( + exec_output.advice.get_mapped_values(&inputs_advice_map_key).unwrap(), + note_inputs.1, + "advice entry with note inputs should contain the unpadded values" + ); + + let num_inputs_advice_map_key = + Hasher::hash_elements(note_inputs.0.commitment().as_elements()); + assert_eq!( + exec_output.advice.get_mapped_values(&num_inputs_advice_map_key).unwrap(), + &[Felt::from(note_inputs.0.num_values())], + "advice entry with num note inputs should contain the original number of values" + ); + } let mut expected_stack = alloc::vec::Vec::new(); expected_stack.extend_from_slice(recipient_4.digest().as_elements()); expected_stack.extend_from_slice(recipient_5.digest().as_elements()); - expected_stack.extend_from_slice(recipient_13.digest().as_elements()); + expected_stack.extend_from_slice(recipient_8.digest().as_elements()); expected_stack.reverse(); assert_eq!(exec_output.stack[0..12], expected_stack); diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index b67662d744..01bc92dee6 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -117,7 +117,6 @@ Note utility procedures can be used to compute the required utility data or writ | Procedure | Description | Context | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `compute_inputs_commitment` | Computes the commitment to the output note inputs starting at the specified memory address.

**Inputs:** `[inputs_ptr, num_inputs]`
**Outputs:** `[INPUTS_COMMITMENT]` | Any | -| `get_max_inputs_per_note` | Returns the max allowed number of input values per note.

**Inputs:** `[]`
**Outputs:** `[max_inputs_per_note]` | Any | | `write_assets_to_memory` | Writes the assets data stored in the advice map to the memory specified by the provided destination pointer.

**Inputs:** `[ASSETS_COMMITMENT, num_assets, dest_ptr]`
**Outputs:** `[num_assets, dest_ptr]` | Any | | `build_recipient_hash` | Returns the `RECIPIENT` for a specified `SERIAL_NUM`, `SCRIPT_ROOT`, and inputs commitment.

**Inputs:** `[SERIAL_NUM, SCRIPT_ROOT, INPUT_COMMITMENT]`
**Outputs:** `[RECIPIENT]` | Any | | `build_recipient` | Builds the recipient hash from note inputs, script root, and serial number.

**Inputs:** `[inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT]`
**Outputs:** `[RECIPIENT]` | Any | From 8c622e19c07d3fc7965c3d6806bfdc4ef6cbbfff Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 15 Jan 2026 08:28:55 +0100 Subject: [PATCH 097/114] feat: Refactor `NoteTag` to contain an arbitrary `u32` (#2219) * feat: Define `NoteTag` to wrap arbitrary `u32` * feat: Rework note tag docs and constructors * chore: Remove use case constructors and refactor swap tag * feat: Remove note tag validation * feat: Rename account ID conversion to account target * feat: Update note tag docs * chore: add changelog * chore: Make `NoteMetadata::new` infallible * chore: address review comments on docs * chore: remove outdated note tag rules in metadata docs * chore: remove `create_mock_notes` (#2236) * chore: Rename `create_random_note` to `*_default_*` and add pub helper * chore: replace create_mock_notes in test_epilogue * chore: Remove `create_mock_notes` --------- Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> --- CHANGELOG.md | 1 + .../kernels/transaction/lib/output_note.masm | 41 +- .../asm/protocol/output_note.masm | 5 + .../miden-protocol/src/address/address_id.rs | 7 +- crates/miden-protocol/src/address/mod.rs | 17 +- .../src/address/routing_parameters.rs | 21 +- crates/miden-protocol/src/errors/mod.rs | 8 +- crates/miden-protocol/src/errors/tx_kernel.rs | 6 +- crates/miden-protocol/src/note/file.rs | 3 +- crates/miden-protocol/src/note/metadata.rs | 23 +- crates/miden-protocol/src/note/mod.rs | 7 +- crates/miden-protocol/src/note/note_tag.rs | 560 ++++-------------- .../src/testing/mock_util_lib.rs | 14 +- crates/miden-protocol/src/testing/note.rs | 5 +- .../src/transaction/kernel/procedures.rs | 2 +- .../src/account/interface/test.rs | 20 +- crates/miden-standards/src/note/mod.rs | 22 +- crates/miden-standards/src/note/utils.rs | 63 +- crates/miden-standards/src/testing/note.rs | 4 +- .../miden-testing/src/kernel_tests/tx/mod.rs | 76 +-- .../src/kernel_tests/tx/test_account.rs | 2 +- .../src/kernel_tests/tx/test_account_delta.rs | 13 +- .../kernel_tests/tx/test_account_interface.rs | 5 +- .../src/kernel_tests/tx/test_active_note.rs | 5 +- .../src/kernel_tests/tx/test_epilogue.rs | 161 +++-- .../src/kernel_tests/tx/test_lazy_loading.rs | 4 +- .../src/kernel_tests/tx/test_note.rs | 19 +- .../src/kernel_tests/tx/test_output_note.rs | 25 +- .../src/kernel_tests/tx/test_tx.rs | 15 +- crates/miden-testing/tests/lib.rs | 3 +- crates/miden-testing/tests/scripts/faucet.rs | 29 +- .../miden-testing/tests/scripts/send_note.rs | 8 +- crates/miden-testing/tests/scripts/swap.rs | 4 +- docs/src/note.md | 21 +- 34 files changed, 387 insertions(+), 832 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eae7a5205b..1c2f6d9d20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Removed OLD_MAP_ROOT from being returned when calling [`native_account::set_map_item`](crates/miden-lib/asm/miden/native_account.masm) ([#2194](https://github.com/0xMiden/miden-base/pull/2194)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). +- [BREAKING] Refactor note tags to be arbitrary `u32` values and drop previous validation ([#2219](https://github.com/0xMiden/miden-base/pull/2219)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index 1da9d7eab2..7e4bb51916 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -13,9 +13,6 @@ const PUBLIC_NOTE=1 # 0b01 const PRIVATE_NOTE=2 # 0b10 const ENCRYPTED_NOTE=3 # 0b11 -# The note type must be PUBLIC, unless the high bits are `0b11`. (See the table below.) -const LOCAL_ANY_PREFIX=3 # 0b11 - # ERRORS # ================================================================================================= @@ -31,29 +28,7 @@ const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding a fungible asset to a note c const ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS="non-fungible asset that already exists in the note cannot be added again" -# The 2 highest bits in the u32 tag have the following meaning: -# -# | Prefix | Name | [`NoteExecutionMode`] | Target | Allowed [`NoteType`] | -# | :----: | :--------------------: | :-------------------: | :----------------------: | :------------------: | -# | `0b00` | `NetworkAccount` | Network | Network Account | [`NoteType::Public`] | -# | `0b01` | `NetworkUseCase` | Network | Use case | [`NoteType::Public`] | -# | `0b10` | `LocalPublicAny` | Local | Any | [`NoteType::Public`] | -# | `0b11` | `LocalAny` | Local | Any | Any | -# -# Execution: Is a hint for the network, to check if the note can be consumed by a network controlled -# account -# Target: Is a hint for the type of target. Use case means the note may be consumed by anyone, -# specific means there is a specific target for the note (the target may be a public key, a user -# that knows some secret, or a specific account ID) -# -# Only the note type from the above list is enforced. The other values are only hints intended as a -# best effort optimization strategy. A badly formatted note may 1. not be consumed because honest -# users won't see the note 2. generate slightly more load as extra validation is performed for the -# invalid tags. None of these scenarios have any significant impact. - -const ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX="invalid note type for the given note tag prefix" - -const ERR_NOTE_TAG_MUST_BE_U32="the note's tag must fit into a u32 so the 32 most significant bits must be zero" +const ERR_NOTE_TAG_MUST_BE_U32="the note's tag must fit into a u32 so the 32 most significant bits of the felt must be zero" # EVENTS # ================================================================================================= @@ -86,9 +61,8 @@ const NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") #! - note_idx is the index of the created note. #! #! Panics if: -#! - the note_type is not valid. -#! - the note_tag is not an u32. -#! - the note_tag starts with anything but 0b11 and note_type is not public. +#! - the note_type is unknown. +#! - the note tag is not a u32. #! - the number of output notes exceeds the maximum limit of 1024. pub proc create emit.NOTE_BEFORE_CREATED_EVENT @@ -278,16 +252,7 @@ pub proc build_metadata dup.2 eq.PRIVATE_NOTE dup.3 eq.PUBLIC_NOTE or assert.err=ERR_NOTE_INVALID_TYPE # => [tag, aux, note_type, execution_hint] - # copy data to validate the tag - dup.2 push.PUBLIC_NOTE dup.1 dup.3 - # => [tag, note_type, public_note, note_type, tag, aux, note_type, execution_hint] - u32assert.err=ERR_NOTE_TAG_MUST_BE_U32 - # => [tag, note_type, public_note, note_type, tag, aux, note_type, execution_hint] - - # enforce the note type depending on the tag' bits - u32shr.30 eq.LOCAL_ANY_PREFIX cdrop - assert_eq.err=ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX # => [tag, aux, note_type, execution_hint] # Split execution hint into its tag and payload parts as they are encoded in separate elements diff --git a/crates/miden-protocol/asm/protocol/output_note.masm b/crates/miden-protocol/asm/protocol/output_note.masm index 3257be6172..6b1093d0cb 100644 --- a/crates/miden-protocol/asm/protocol/output_note.masm +++ b/crates/miden-protocol/asm/protocol/output_note.masm @@ -17,6 +17,11 @@ use miden::protocol::note #! - RECIPIENT is the recipient of the note. #! - note_idx is the index of the created note. #! +#! Panics if: +#! - the note_type is unknown. +#! - the note tag is not a u32. +#! - the number of output notes exceeds the maximum limit of 1024. +#! #! Invocation: exec pub proc create # pad the stack before the syscall to prevent accidental modification of the deeper stack diff --git a/crates/miden-protocol/src/address/address_id.rs b/crates/miden-protocol/src/address/address_id.rs index 2974e66ac3..7b13ae73c2 100644 --- a/crates/miden-protocol/src/address/address_id.rs +++ b/crates/miden-protocol/src/address/address_id.rs @@ -31,14 +31,15 @@ impl AddressId { /// Returns the default tag length of the ID. /// /// This is guaranteed to be in range `0..=30` (e.g. the maximum of - /// [`NoteTag::MAX_LOCAL_TAG_LENGTH`] and [`NoteTag::DEFAULT_NETWORK_TAG_LENGTH`]). + /// [`NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH`] and + /// [`NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH`]). pub fn default_note_tag_len(&self) -> u8 { match self { AddressId::AccountId(id) => { if id.storage_mode() == AccountStorageMode::Network { - NoteTag::DEFAULT_NETWORK_TAG_LENGTH + NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH } else { - NoteTag::DEFAULT_LOCAL_TAG_LENGTH + NoteTag::DEFAULT_LOCAL_ACCOUNT_TARGET_TAG_LENGTH } }, } diff --git a/crates/miden-protocol/src/address/mod.rs b/crates/miden-protocol/src/address/mod.rs index 6e9c864a36..93ef42b210 100644 --- a/crates/miden-protocol/src/address/mod.rs +++ b/crates/miden-protocol/src/address/mod.rs @@ -85,8 +85,8 @@ impl Address { /// # Errors /// /// Returns an error if: - /// - The tag length routing parameter is not [`NoteTag::DEFAULT_NETWORK_TAG_LENGTH`] for - /// network accounts. + /// - The tag length routing parameter is not + /// [`NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH`] for network accounts. pub fn with_routing_parameters( mut self, routing_params: RoutingParameters, @@ -95,7 +95,7 @@ impl Address { match self.id { AddressId::AccountId(account_id) => { if account_id.storage_mode() == AccountStorageMode::Network - && tag_len != NoteTag::DEFAULT_NETWORK_TAG_LENGTH + && tag_len != NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH { return Err(AddressError::CustomTagLengthNotAllowedForNetworkAccounts( tag_len, @@ -126,7 +126,8 @@ impl Address { /// Returns the preferred tag length. /// /// This is guaranteed to be in range `0..=30` (e.g. the maximum of - /// [`NoteTag::MAX_LOCAL_TAG_LENGTH`] and [`NoteTag::DEFAULT_NETWORK_TAG_LENGTH`]). + /// [`NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH`] and + /// [`NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH`]). pub fn note_tag_len(&self) -> u8 { self.routing_params .as_ref() @@ -143,8 +144,8 @@ impl Address { match id.storage_mode() { AccountStorageMode::Network => NoteTag::from_network_account_id(id), AccountStorageMode::Private | AccountStorageMode::Public => { - NoteTag::from_local_account_id(id, note_tag_len) - .expect("address should validate that tag len does not exceed MAX_LOCAL_TAG_LENGTH bits") + NoteTag::with_custom_account_target(id, note_tag_len) + .expect("address should validate that tag len does not exceed MAX_ACCOUNT_TARGET_TAG_LENGTH bits") } } }, @@ -302,7 +303,7 @@ mod tests { // Encode/Decode with routing parameters should be valid. address = address.with_routing_parameters( RoutingParameters::new(AddressInterface::BasicWallet) - .with_note_tag_len(NoteTag::DEFAULT_NETWORK_TAG_LENGTH)?, + .with_note_tag_len(NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH)?, )?; let bech32_string = address.encode(network_id.clone()); @@ -431,7 +432,7 @@ mod tests { let account_id = AccountIdBuilder::new().account_type(account_type).build_with_rng(rng); let address = Address::new(account_id).with_routing_parameters( RoutingParameters::new(AddressInterface::BasicWallet) - .with_note_tag_len(NoteTag::DEFAULT_NETWORK_TAG_LENGTH)?, + .with_note_tag_len(NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH)?, )?; let serialized = address.to_bytes(); diff --git a/crates/miden-protocol/src/address/routing_parameters.rs b/crates/miden-protocol/src/address/routing_parameters.rs index 5b5899efea..3d9d640ee0 100644 --- a/crates/miden-protocol/src/address/routing_parameters.rs +++ b/crates/miden-protocol/src/address/routing_parameters.rs @@ -87,16 +87,16 @@ impl RoutingParameters { /// The tag length determines how many bits of the address ID are encoded into [`NoteTag`]s of /// notes targeted to this address. This lets the receiver choose their level of privacy. A /// higher tag length makes the address ID more uniquely identifiable and reduces privacy, - /// while a shorter length increases privacy at the cost of matching more notes - /// published onchain. + /// while a shorter length increases privacy at the cost of matching more notes published + /// onchain. /// /// # Errors /// /// Returns an error if: - /// - The tag length exceeds the maximum of [`NoteTag::MAX_LOCAL_TAG_LENGTH`] and - /// [`NoteTag::DEFAULT_NETWORK_TAG_LENGTH`]. + /// - The tag length exceeds the maximum of [`NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH`] and + /// [`NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH`]. pub fn with_note_tag_len(mut self, note_tag_len: u8) -> Result { - if note_tag_len > NoteTag::MAX_LOCAL_TAG_LENGTH { + if note_tag_len > NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH { return Err(AddressError::TagLengthTooLarge(note_tag_len)); } @@ -110,7 +110,8 @@ impl RoutingParameters { /// Returns the note tag length preference. /// /// This is guaranteed to be in range `0..=30` (e.g. the maximum of - /// [`NoteTag::MAX_LOCAL_TAG_LENGTH`] and [`NoteTag::DEFAULT_NETWORK_TAG_LENGTH`]). + /// [`NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH`] and + /// [`NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH`]). pub fn note_tag_len(&self) -> Option { self.note_tag_len } @@ -467,11 +468,11 @@ mod tests { // Test case 4: Explicit tag length set to max let params_tag_max = RoutingParameters::new(AddressInterface::BasicWallet) - .with_note_tag_len(NoteTag::MAX_LOCAL_TAG_LENGTH)?; + .with_note_tag_len(NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)?; let encoded = params_tag_max.encode_to_string(); let decoded = RoutingParameters::decode(encoded)?; assert_eq!(params_tag_max, decoded); - assert_eq!(decoded.note_tag_len(), Some(NoteTag::MAX_LOCAL_TAG_LENGTH)); + assert_eq!(decoded.note_tag_len(), Some(NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)); Ok(()) } @@ -504,11 +505,11 @@ mod tests { // Test case 4: Explicit tag length set to max let params_tag_max = RoutingParameters::new(AddressInterface::BasicWallet) - .with_note_tag_len(NoteTag::MAX_LOCAL_TAG_LENGTH)?; + .with_note_tag_len(NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)?; let serialized = params_tag_max.to_bytes(); let deserialized = RoutingParameters::read_from_bytes(&serialized)?; assert_eq!(params_tag_max, deserialized); - assert_eq!(deserialized.note_tag_len(), Some(NoteTag::MAX_LOCAL_TAG_LENGTH)); + assert_eq!(deserialized.note_tag_len(), Some(NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)); Ok(()) } diff --git a/crates/miden-protocol/src/errors/mod.rs b/crates/miden-protocol/src/errors/mod.rs index 73c2b0688a..e9939ba1aa 100644 --- a/crates/miden-protocol/src/errors/mod.rs +++ b/crates/miden-protocol/src/errors/mod.rs @@ -295,11 +295,11 @@ pub enum AccountTreeError { #[derive(Debug, Error)] pub enum AddressError { #[error("tag length {0} should be {expected} bits for network accounts", - expected = NoteTag::DEFAULT_NETWORK_TAG_LENGTH + expected = NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH )] CustomTagLengthNotAllowedForNetworkAccounts(u8), #[error("tag length {0} is too large, must be less than or equal to {max}", - max = NoteTag::MAX_LOCAL_TAG_LENGTH + max = NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH )] TagLengthTooLarge(u8), #[error("unknown address interface `{0}`")] @@ -541,7 +541,7 @@ pub enum PartialAssetVaultError { #[derive(Debug, Error)] pub enum NoteError { - #[error("note tag length {0} exceeds the maximum of {max}", max = NoteTag::MAX_LOCAL_TAG_LENGTH)] + #[error("note tag length {0} exceeds the maximum of {max}", max = NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)] NoteTagLengthTooLarge(u8), #[error("duplicate fungible asset from issuer {0} in note")] DuplicateFungibleAsset(AccountId), @@ -553,8 +553,6 @@ pub enum NoteError { AddFungibleAssetBalanceError(#[source] AssetError), #[error("note sender is not a valid account ID")] NoteSenderInvalidAccountId(#[source] AccountIdError), - #[error("note tag use case {0} must be less than 2^{exp}", exp = NoteTag::MAX_USE_CASE_ID_EXPONENT)] - NoteTagUseCaseTooLarge(u16), #[error( "note execution hint tag {0} must be in range {from}..={to}", from = NoteExecutionHint::NONE_TAG, diff --git a/crates/miden-protocol/src/errors/tx_kernel.rs b/crates/miden-protocol/src/errors/tx_kernel.rs index 8394873749..9aaad06249 100644 --- a/crates/miden-protocol/src/errors/tx_kernel.rs +++ b/crates/miden-protocol/src/errors/tx_kernel.rs @@ -156,14 +156,12 @@ pub const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROC pub const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED: MasmError = MasmError::from_static_str("adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807"); /// Error Message: "failed to find note at the given index; index must be within [0, num_of_notes]" pub const ERR_NOTE_INVALID_INDEX: MasmError = MasmError::from_static_str("failed to find note at the given index; index must be within [0, num_of_notes]"); -/// Error Message: "invalid note type for the given note tag prefix" -pub const ERR_NOTE_INVALID_NOTE_TYPE_FOR_NOTE_TAG_PREFIX: MasmError = MasmError::from_static_str("invalid note type for the given note tag prefix"); /// Error Message: "invalid note type" pub const ERR_NOTE_INVALID_TYPE: MasmError = MasmError::from_static_str("invalid note type"); /// Error Message: "number of assets in a note exceed 255" pub const ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT: MasmError = MasmError::from_static_str("number of assets in a note exceed 255"); -/// Error Message: "the note's tag must fit into a u32 so the 32 most significant bits must be zero" -pub const ERR_NOTE_TAG_MUST_BE_U32: MasmError = MasmError::from_static_str("the note's tag must fit into a u32 so the 32 most significant bits must be zero"); +/// Error Message: "the note's tag must fit into a u32 so the 32 most significant bits of the felt must be zero" +pub const ERR_NOTE_TAG_MUST_BE_U32: MasmError = MasmError::from_static_str("the note's tag must fit into a u32 so the 32 most significant bits of the felt must be zero"); /// Error Message: "requested output note index should be less than the total number of created output notes" pub const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("requested output note index should be less than the total number of created output notes"); diff --git a/crates/miden-protocol/src/note/file.rs b/crates/miden-protocol/src/note/file.rs index 48a77902aa..c00317cc49 100644 --- a/crates/miden-protocol/src/note/file.rs +++ b/crates/miden-protocol/src/note/file.rs @@ -178,8 +178,7 @@ mod tests { NoteTag::from(123), crate::note::NoteExecutionHint::None, Felt::new(0), - ) - .unwrap(); + ); Note::new(NoteAssets::new(vec![asset]).unwrap(), metadata, recipient) } diff --git a/crates/miden-protocol/src/note/metadata.rs b/crates/miden-protocol/src/note/metadata.rs index 31cdd14bcd..3705201a3f 100644 --- a/crates/miden-protocol/src/note/metadata.rs +++ b/crates/miden-protocol/src/note/metadata.rs @@ -20,11 +20,6 @@ use super::{ /// Metadata associated with a note. /// -/// Note type and tag must be internally consistent according to the following rules: -/// -/// - For private and encrypted notes, the two most significant bits of the tag must be `0b11`. -/// - For public notes, the two most significant bits of the tag can be set to any value. -/// /// # Word layout & validity /// /// [`NoteMetadata`] can be encoded into a [`Word`] with the following layout: @@ -65,25 +60,21 @@ pub struct NoteMetadata { } impl NoteMetadata { - /// Returns a new [NoteMetadata] instantiated with the specified parameters. - /// - /// # Errors - /// Returns an error if the note type and note tag are inconsistent. + /// Returns a new [`NoteMetadata`] instantiated with the specified parameters. pub fn new( sender: AccountId, note_type: NoteType, tag: NoteTag, execution_hint: NoteExecutionHint, aux: Felt, - ) -> Result { - let tag = tag.validate(note_type)?; - Ok(Self { + ) -> Self { + Self { sender, note_type, tag, aux, execution_hint, - }) + } } /// Returns the account which created the note. @@ -162,7 +153,7 @@ impl TryFrom for NoteMetadata { let (execution_hint, note_tag) = unmerge_note_tag_and_hint_payload(elements[2], execution_hint_tag)?; - Self::new(sender, note_type, note_tag, execution_hint, elements[3]) + Ok(Self::new(sender, note_type, note_tag, execution_hint, elements[3])) } } @@ -309,7 +300,7 @@ mod tests { // produces valid felts. let sender = AccountId::try_from(ACCOUNT_ID_MAX_ONES).unwrap(); let note_type = NoteType::Public; - let tag = NoteTag::from_account_id(sender); + let tag = NoteTag::with_account_target(sender); let aux = Felt::try_from(0xffff_ffff_0000_0000u64).unwrap(); for execution_hint in [ @@ -318,7 +309,7 @@ mod tests { NoteExecutionHint::on_block_slot(10, 11, 12), NoteExecutionHint::after_block((u32::MAX - 1).into()).unwrap(), ] { - let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux).unwrap(); + let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux); NoteMetadata::read_from_bytes(&metadata.to_bytes()) .context(format!("failed for execution hint {execution_hint:?}"))?; } diff --git a/crates/miden-protocol/src/note/mod.rs b/crates/miden-protocol/src/note/mod.rs index 1ecf14bfa9..a87252bcbf 100644 --- a/crates/miden-protocol/src/note/mod.rs +++ b/crates/miden-protocol/src/note/mod.rs @@ -27,7 +27,7 @@ mod note_id; pub use note_id::NoteId; mod note_tag; -pub use note_tag::{NoteExecutionMode, NoteTag}; +pub use note_tag::NoteTag; mod note_type; pub use note_type::NoteType; @@ -156,11 +156,6 @@ impl Note { pub fn commitment(&self) -> Word { self.header.commitment() } - - /// Returns true if this note is intended to be executed by the network rather than a user. - pub fn is_network_note(&self) -> bool { - self.metadata().tag().execution_mode() == NoteExecutionMode::Network - } } // AS REF diff --git a/crates/miden-protocol/src/note/note_tag.rs b/crates/miden-protocol/src/note/note_tag.rs index 4da8f012fb..3c1ee2242a 100644 --- a/crates/miden-protocol/src/note/note_tag.rs +++ b/crates/miden-protocol/src/note/note_tag.rs @@ -9,163 +9,117 @@ use super::{ Deserializable, DeserializationError, NoteError, - NoteType, Serializable, }; use crate::account::AccountStorageMode; -// CONSTANTS -// ================================================================================================ -const NETWORK_EXECUTION: u8 = 0; -const LOCAL_EXECUTION: u8 = 1; - -// The 2 most significant bits are set to `0b00`. -const NETWORK_ACCOUNT: u32 = 0; -// The 2 most significant bits are set to `0b01`. -const NETWORK_PUBLIC_USECASE: u32 = 0x4000_0000; -// The 2 most significant bits are set to `0b10`. -const LOCAL_PUBLIC_ANY: u32 = 0x8000_0000; -// The 2 most significant bits are set to `0b11`. -const LOCAL_ANY: u32 = 0xc000_0000; - -/// [super::Note]'s execution mode hints. -/// -/// The execution hints are _not_ enforced, therefore function only as hints. For example, if a -/// note's tag is created with the [NoteExecutionMode::Network], further validation is necessary to -/// check the account_id is known, that the account's state is public on chain, and the account is -/// controlled by the network. -/// -/// The goal of the hint is to allow for a network node to quickly filter notes that are not -/// intended for network execution, and skip the validation steps mentioned above. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum NoteExecutionMode { - Network = NETWORK_EXECUTION, - Local = LOCAL_EXECUTION, -} - // NOTE TAG // ================================================================================================ -/// [NoteTag]`s are best effort filters for notes registered with the network. +/// [`NoteTag`]s are 32-bits of data that serve as best-effort filters for notes. /// -/// Tags are light-weight values used to speed up queries. The 2 most significant bits of the tags -/// have the following interpretation: +/// Tags enable quick lookups for notes related to particular use cases, scripts, or account +/// prefixes. /// -/// | Prefix | Name | [`NoteExecutionMode`] | Target | Allowed [`NoteType`] | -/// | :----: | :--------------------: | :-------------------: | :----------------------: | :------------------: | -/// | `0b00` | `NetworkAccount` | Network | Network Account | [`NoteType::Public`] | -/// | `0b01` | `NetworkUseCase` | Network | Use case | [`NoteType::Public`] | -/// | `0b10` | `LocalPublicAny` | Local | Any | [`NoteType::Public`] | -/// | `0b11` | `LocalAny` | Local | Any | Any | +/// ## Account Targets /// -/// Where: +/// A note targeted at an account is a note that is intended or even enforced to be consumed by a +/// specific account. One example is a P2ID note that can only be consumed by a specific account ID. +/// The tag for such a note should make it easy for the receiver to find the note. Therefore, the +/// tag encodes a certain number of bits of the receiver account's ID, by convention. Notably, it +/// may not encode the full 32 bits of the target account's ID to preserve the receiver's privacy. +/// See also the section on privacy below. /// -/// - [`NoteExecutionMode`] is set to [`NoteExecutionMode::Network`] to hint a [`Note`](super::Note) -/// should be consumed by the network. These notes will be further validated and if possible -/// consumed by it. -/// - Target describes how to further interpret the bits in the tag. -/// - For tags with a specific target, the rest of the tag is interpreted as a partial -/// [`AccountId`]. For network accounts these are the first 30 bits of the ID while for local -/// account targets, the first 14 bits are used - a trade-off between privacy and uniqueness. -/// - For use case values, the meaning of the rest of the tag is not specified by the protocol and -/// can be used by applications built on top of the rollup. +/// Because this convention is widely used, the note tag provides a dedicated constructor for this: +/// [`NoteTag::with_account_target`]. /// -/// The note type is the only value enforced by the protocol. The rationale is that any note -/// intended to be consumed by the network must be public to have all the details available. The -/// public note for local execution is intended to allow users to search for notes that can be -/// consumed right away, without requiring an off-band communication channel. +/// ## Use Case Tags /// -/// **Note on Type Safety** +/// Use case notes are notes that are not intended to be consumed by a specific account, but by +/// anyone willing to fulfill the note's contract. One example is a SWAP note that trades one asset +/// against another. Such a use case note can define the structure of their note tags. A sensible +/// structure for a SWAP note could be: +/// - encoding the 2 bits of the note's type. +/// - encoding the note script root, i.e. making it identifiable as a SWAP note, for example by +/// using 16 bits of the SWAP script root. +/// - encoding the SWAP pair, for example by using 8 bits of the offered asset faucet ID and 8 bits +/// of the requested asset faucet ID. /// -/// Each enum variant contains the raw encoding of the note tag, where the first two bits -/// _should_ correspond to the variant's prefix (as defined in the table above). However, because -/// enum variants are always public, it is possible to instantiate this enum where this invariant -/// does not hold, e.g. `NoteTag::NetworkAccount(0b11...)`. For that reason, the enum variants -/// should take precedence in case of such a mismatch and the inner value **should not be accessed -/// directly**. Instead, only rely on [`NoteTag::as_u32`] to access the encoded value, which will -/// always return the correct value. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub enum NoteTag { - /// Represents a tag for a note intended for network execution, targeted at a network account. - /// The note must be public. - NetworkAccount(u32), - /// Represents a tag for a note intended for network execution for a public use case. The note - /// must be public. - NetworkUseCase(u16, u16), - /// Represents a tag for a note intended for local execution. - /// - /// This is used for two purposes: - /// - A public use case. - /// - A note targeted at any type of account. - /// - /// In all cases, the note must be **public**. - LocalPublicAny(u32), - /// Represents a tag for a note intended for local execution. - /// - /// This is used for two purposes: - /// - A private use case. - /// - A note targeted at any type of account. - /// - /// In all cases, the note can be of any type. - LocalAny(u32), -} +/// This allows clients to search for a public SWAP note that trades USDC against ETH only through +/// the note tag. Since tags are not validated in any way and only act as best-effort filters, +/// further local filtering is almost always necessary. For example, there could easily be a +/// collision on the 8 bits used in SWAP tag's faucet IDs. +/// +/// ## Privacy vs Efficiency +/// +/// Using note tags strikes a balance between privacy and efficiency. Without tags, querying a +/// specific note ID reveals a user's interest to the node. Conversely, downloading and filtering +/// all registered notes locally is highly inefficient. Tags allow users to adjust their level of +/// privacy by choosing how broadly or narrowly they define their search criteria, letting them find +/// the right balance between revealing too much information and incurring excessive computational +/// overhead. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] +pub struct NoteTag(u32); impl NoteTag { // CONSTANTS // -------------------------------------------------------------------------------------------- - /// The exponent of the maximum allowed use case id. In other words, 2^exponent is the maximum - /// allowed use case id. - pub(crate) const MAX_USE_CASE_ID_EXPONENT: u8 = 14; /// The default note tag length for an account ID with local execution. - pub const DEFAULT_LOCAL_TAG_LENGTH: u8 = 14; + pub const DEFAULT_LOCAL_ACCOUNT_TARGET_TAG_LENGTH: u8 = 14; /// The default note tag length for an account ID with network execution. - pub const DEFAULT_NETWORK_TAG_LENGTH: u8 = 30; + pub const DEFAULT_NETWORK_ACCOUNT_TARGET_TAG_LENGTH: u8 = 30; /// The maximum number of bits that can be encoded into the tag for local accounts. - pub const MAX_LOCAL_TAG_LENGTH: u8 = 30; + pub const MAX_ACCOUNT_TARGET_TAG_LENGTH: u8 = 30; // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Returns a new [NoteTag::NetworkAccount] or [NoteTag::LocalAny] instantiated from the - /// specified account ID. + /// Creates a new [`NoteTag`] from an arbitrary `u32`. + pub const fn new(tag: u32) -> Self { + Self(tag) + } + + /// Constructs a note tag that targets the given `account_id`. /// /// The tag is constructed as follows: /// /// - For local execution ([`AccountStorageMode::Private`] or [`AccountStorageMode::Public`]), - /// the two most significant bits are set to `0b11`, which allows for any note type to be - /// used. The following 14 bits are set to the most significant bits of the account ID, and - /// the remaining 16 bits are set to 0. + /// the two most significant bits are set to `0b00`. The following 14 bits are set to the most + /// significant bits of the account ID, and the remaining 16 bits are set to 0. /// - For network execution ([`AccountStorageMode::Network`]), the most significant bits are set /// to `0b00` and the remaining bits are set to the 30 most significant bits of the account /// ID. - pub fn from_account_id(account_id: AccountId) -> Self { + pub fn with_account_target(account_id: AccountId) -> Self { match account_id.storage_mode() { AccountStorageMode::Network => Self::from_network_account_id(account_id), AccountStorageMode::Private | AccountStorageMode::Public => { - // safe to unwrap since DEFAULT_LOCAL_TAG_LENGTH < MAX_LOCAL_TAG_LENGTH - Self::from_local_account_id(account_id, Self::DEFAULT_LOCAL_TAG_LENGTH).unwrap() + // safe to unwrap since DEFAULT_LOCAL_ACCOUNT_TARGET_TAG_LENGTH < + // MAX_ACCOUNT_TARGET_TAG_LENGTH + Self::with_custom_account_target( + account_id, + Self::DEFAULT_LOCAL_ACCOUNT_TARGET_TAG_LENGTH, + ) + .unwrap() }, } } - /// Constructs a [`NoteTag::LocalAny`] from the given `account_id` and `tag_len`. + /// Constructs a note tag that targets the given `account_id` with a custom `tag_len`. /// - /// The tag is constructed as follows: - /// - /// - The two most significant bits are set to `0b11` to indicate a [LOCAL_ANY] tag. + /// The tag is constructed by: + /// - Setting the two most significant bits to zero. /// - The next `tag_len` bits are set to the most significant bits of the account ID prefix. /// - The remaining bits are set to zero. /// /// # Errors /// - /// Returns an error if `tag_len` is larger than [`NoteTag::MAX_LOCAL_TAG_LENGTH`]. - pub(crate) fn from_local_account_id( + /// Returns an error if `tag_len` is larger than [`NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH`]. + pub fn with_custom_account_target( account_id: AccountId, tag_len: u8, ) -> Result { - if tag_len > Self::MAX_LOCAL_TAG_LENGTH { + if tag_len > Self::MAX_ACCOUNT_TARGET_TAG_LENGTH { return Err(NoteError::NoteTagLengthTooLarge(tag_len)); } @@ -183,15 +137,14 @@ impl NoteTag { // [2 zero bits | remaining high bits (tag_len bits) | (30 - tag_len) zero bits]. let high_bits = high_bits & (u32::MAX << (32 - 2 - tag_len)); - // Set the local execution tag in the two most significant bits. - Ok(Self::LocalAny(LOCAL_ANY | high_bits)) + Ok(Self(high_bits)) } - /// Constructs a [`NoteTag::NetworkAccount`] from the specified `account_id`. + /// Constructs a network account note tag from the specified `account_id`. /// /// The tag is constructed as follows: /// - /// - The two most significant bits are set to `0b00` to indicate a [NETWORK_ACCOUNT] tag. + /// - The two most significant bits are set to `0b00`. /// - The remaining bits are set to the 30 most significant bits of the account ID. pub(crate) fn from_network_account_id(account_id: AccountId) -> Self { let prefix_id: u64 = account_id.prefix().into(); @@ -202,125 +155,15 @@ impl NoteTag { // This is equivalent to the following layout, interpreted as a u32: // [2 zero bits | remaining high bits (30 bits)]. - // The two most significant zero bits match the tag we need for network - Self::NetworkAccount(high_bits as u32) - } - - /// Returns a new [`NoteTag::NetworkUseCase`] or [`NoteTag::LocalPublicAny`] - /// instantiated for a custom use case which requires a public note. - /// - /// The public use_case tag requires a [NoteType::Public] note. - /// - /// The two high bits are set to the `b10` or `b01` depending on the execution hint, the next 14 - /// bits are set to the `use_case_id`, and the low 16 bits are set to `payload`. - /// - /// # Errors - /// - /// - If `use_case_id` is larger than or equal to $2^{14}$. - pub fn for_public_use_case( - use_case_id: u16, - payload: u16, - execution: NoteExecutionMode, - ) -> Result { - if (use_case_id >> 14) != 0 { - return Err(NoteError::NoteTagUseCaseTooLarge(use_case_id)); - } - - match execution { - NoteExecutionMode::Network => { - let use_case_bits = (NETWORK_PUBLIC_USECASE >> 16) as u16 | use_case_id; - Ok(Self::NetworkUseCase(use_case_bits, payload)) - }, - NoteExecutionMode::Local => { - let tag_u32 = LOCAL_PUBLIC_ANY | ((use_case_id as u32) << 16) | (payload as u32); - Ok(Self::LocalPublicAny(tag_u32)) - }, - } - } - - /// Returns a new [`NoteTag::LocalAny`] instantiated for a custom local use case. - /// - /// The local use_case tag is the only tag type that allows for [NoteType::Private] notes. - /// - /// The two high bits are set to the `b11`, the next 14 bits are set to the `use_case_id`, and - /// the low 16 bits are set to `payload`. - /// - /// # Errors - /// - /// - If `use_case_id` is larger than or equal to 2^14. - pub fn for_local_use_case(use_case_id: u16, payload: u16) -> Result { - if (use_case_id >> NoteTag::MAX_USE_CASE_ID_EXPONENT) != 0 { - return Err(NoteError::NoteTagUseCaseTooLarge(use_case_id)); - } - - let use_case_bits = (use_case_id as u32) << 16; - let payload_bits = payload as u32; - - Ok(Self::LocalAny(LOCAL_ANY | use_case_bits | payload_bits)) + Self(high_bits as u32) } // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns true if the note is intended for execution by a specific account, i.e. - /// [`NoteTag::NetworkAccount`] - pub fn is_single_target(&self) -> bool { - matches!(self, NoteTag::NetworkAccount(_)) - } - - /// Returns note execution mode defined by this tag. - /// - /// If the most significant bit of the tag is 0 the note is intended for local execution; - /// otherwise, the note is intended for network execution. - pub fn execution_mode(&self) -> NoteExecutionMode { - match self { - NoteTag::NetworkAccount(_) | NoteTag::NetworkUseCase(..) => NoteExecutionMode::Network, - NoteTag::LocalAny(_) | NoteTag::LocalPublicAny(..) => NoteExecutionMode::Local, - } - } - /// Returns the inner u32 value of this tag. pub fn as_u32(&self) -> u32 { - // Note that we always set the two most significant bits to the prefix corresponding to the - // enum variant. See the note on type safety on the NoteTag docs. - - /// Masks out the two most significant bits, leaving all lower bits untouched. - const LOW_BITS_MASK: u32 = 0x3fff_ffff; - match self { - NoteTag::NetworkAccount(tag) => *tag & LOW_BITS_MASK, - NoteTag::NetworkUseCase(use_case_bits, payload_bits) => { - ((*use_case_bits as u32) << 16 | *payload_bits as u32) & LOW_BITS_MASK - | NETWORK_PUBLIC_USECASE - }, - NoteTag::LocalPublicAny(tag) => (*tag & LOW_BITS_MASK) | LOCAL_PUBLIC_ANY, - NoteTag::LocalAny(tag) => (*tag & LOW_BITS_MASK) | LOCAL_ANY, - } - } - - // UTILITY METHODS - // -------------------------------------------------------------------------------------------- - - /// Returns an error if this tag is not consistent with the specified note type, and self - /// otherwise. - pub fn validate(&self, note_type: NoteType) -> Result { - if self.execution_mode() == NoteExecutionMode::Network && note_type != NoteType::Public { - return Err(NoteError::NetworkExecutionRequiresPublicNote(note_type)); - } - - // Ensure the note is public if the note tag requires it. - if self.requires_public_note() && note_type != NoteType::Public { - Err(NoteError::PublicNoteRequired(note_type)) - } else { - Ok(*self) - } - } - - /// Returns `true` if the note tag requires a public note. - fn requires_public_note(&self) -> bool { - matches!( - self, - NoteTag::NetworkAccount(_) | NoteTag::NetworkUseCase(_, _) | NoteTag::LocalPublicAny(_) - ) + self.0 } } @@ -334,19 +177,8 @@ impl fmt::Display for NoteTag { // ================================================================================================ impl From for NoteTag { - fn from(value: u32) -> Self { - // Mask out the two most significant bits. - match value & 0xc0000000 { - NETWORK_ACCOUNT => Self::NetworkAccount(value), - NETWORK_PUBLIC_USECASE => Self::NetworkUseCase((value >> 16) as u16, value as u16), - LOCAL_ANY => Self::LocalAny(value), - LOCAL_PUBLIC_ANY => Self::LocalPublicAny(value), - _ => { - // This branch should be unreachable because `prefix` is derived from - // the top 2 bits of a u32, which can only be 0, 1, 2, or 3. - unreachable!("Invalid value encountered: {:b}", value); - }, - } + fn from(tag: u32) -> Self { + Self::new(tag) } } @@ -354,14 +186,14 @@ impl From for NoteTag { // ================================================================================================ impl From for u32 { - fn from(value: NoteTag) -> Self { - value.as_u32() + fn from(tag: NoteTag) -> Self { + tag.as_u32() } } impl From for Felt { - fn from(value: NoteTag) -> Self { - Felt::from(value.as_u32()) + fn from(tag: NoteTag) -> Self { + Felt::from(tag.as_u32()) } } @@ -377,7 +209,7 @@ impl Serializable for NoteTag { impl Deserializable for NoteTag { fn read_from(source: &mut R) -> Result { let tag = u32::read_from(source)?; - Ok(Self::from(tag)) + Ok(Self::new(tag)) } } @@ -387,18 +219,8 @@ impl Deserializable for NoteTag { #[cfg(test)] mod tests { - use assert_matches::assert_matches; - - use super::{NoteExecutionMode, NoteTag}; - use crate::NoteError; + use super::NoteTag; use crate::account::AccountId; - use crate::note::NoteType; - use crate::note::note_tag::{ - LOCAL_ANY, - LOCAL_PUBLIC_ANY, - NETWORK_ACCOUNT, - NETWORK_PUBLIC_USECASE, - }; use crate::testing::account_id::{ ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET, ACCOUNT_ID_NETWORK_NON_FUNGIBLE_FAUCET, @@ -448,223 +270,43 @@ mod tests { AccountId::try_from(ACCOUNT_ID_NETWORK_NON_FUNGIBLE_FAUCET).unwrap(), ]; - for account_id in network_accounts { - let tag = NoteTag::from_account_id(account_id); - assert!(tag.is_single_target()); - assert_eq!(tag.execution_mode(), NoteExecutionMode::Network); - - tag.validate(NoteType::Public) - .expect("network execution should require notes to be public"); - assert_matches!( - tag.validate(NoteType::Private), - Err(NoteError::NetworkExecutionRequiresPublicNote(NoteType::Private)) + for account_id in private_accounts.iter().chain(public_accounts.iter()) { + let tag = NoteTag::with_account_target(*account_id); + assert_eq!(tag.as_u32() >> 30, 0, "two most significant bits should be zero"); + assert_eq!(tag.as_u32() << 16, 0, "16 least significant bits should be zero"); + assert_eq!( + (account_id.prefix().as_u64() >> 50) as u32, + tag.as_u32() >> 16, + "14 most significant bits should match" ); - assert_matches!( - tag.validate(NoteType::Encrypted), - Err(NoteError::NetworkExecutionRequiresPublicNote(NoteType::Encrypted)) - ); - } - - for account_id in private_accounts { - let tag = NoteTag::from_account_id(account_id); - assert!(!tag.is_single_target()); - assert_eq!(tag.execution_mode(), NoteExecutionMode::Local); - - // for local execution[`NoteExecutionMode::Local`], all notes are allowed - tag.validate(NoteType::Public) - .expect("local execution should support public notes"); - tag.validate(NoteType::Private) - .expect("local execution should support private notes"); - tag.validate(NoteType::Encrypted) - .expect("local execution should support encrypted notes"); - } - - for account_id in public_accounts { - let tag = NoteTag::from_account_id(account_id); - assert!(!tag.is_single_target()); - assert_eq!(tag.execution_mode(), NoteExecutionMode::Local); - - // for local execution[`NoteExecutionMode::Local`], all notes are allowed - tag.validate(NoteType::Public) - .expect("local execution should support public notes"); - tag.validate(NoteType::Private) - .expect("local execution should support private notes"); - tag.validate(NoteType::Encrypted) - .expect("local execution should support encrypted notes"); } for account_id in network_accounts { - let tag = NoteTag::from_account_id(account_id); - assert!(tag.is_single_target()); - assert_eq!(tag.execution_mode(), NoteExecutionMode::Network); - - // for network execution[`NoteExecutionMode::Network`], only public notes are allowed - tag.validate(NoteType::Public) - .expect("network execution should support public notes"); - assert_matches!( - tag.validate(NoteType::Private), - Err(NoteError::NetworkExecutionRequiresPublicNote(NoteType::Private)) - ); - assert_matches!( - tag.validate(NoteType::Encrypted), - Err(NoteError::NetworkExecutionRequiresPublicNote(NoteType::Encrypted)) + let tag = NoteTag::with_account_target(account_id); + assert_eq!(tag.as_u32() >> 30, 0, "two most significant bits should be zero"); + assert_eq!( + account_id.prefix().as_u64() >> 34, + tag.as_u32() as u64, + "30 most significant bits should match" ); } } #[test] - fn from_private_account_id() { - /// Private Account ID with the following bit pattern in the first and second byte: - /// 0b11001100_01010101 - /// ^^^^^^^^ ^^^^^^ <- 14 bits of the local tag. - const PRIVATE_ACCOUNT_INT: u128 = ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE - | 0x0055_0000_0000_0000_0000_0000_0000_0000; - let private_account_id = AccountId::try_from(PRIVATE_ACCOUNT_INT).unwrap(); - - // Expected private tag of variant `NoteTag::LocalAny`. - let expected_private_tag = 0b11110011_00010101_00000000_00000000; - - assert_eq!(NoteTag::from_account_id(private_account_id).as_u32(), expected_private_tag); - } - - #[test] - fn from_public_account_id() { - /// Public Account ID with the following bit pattern in the first and second byte: - /// 0b10101010_01010101 - /// ^^^^^^^^ ^^^^^^ <- 14 bits of the local tag. - const PUBLIC_ACCOUNT_INT: u128 = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE - | 0x0055_ccaa_0000_0000_0000_0000_0000_0000; - let public_account_id = AccountId::try_from(PUBLIC_ACCOUNT_INT).unwrap(); - - // Expected public tag of variant `NoteTag::LocalAny`. - let expected_public_local_tag = 0b11101010_10010101_00000000_00000000u32; - - assert_eq!(NoteTag::from_account_id(public_account_id).as_u32(), expected_public_local_tag); - } - - #[test] - fn from_network_account_id() { - /// Network Account ID with the following bit pattern in the first four bytes: - /// 0b10101010_11001100_01110111_11001100 - /// ^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^ <- 30 bits of the network tag. - const NETWORK_ACCOUNT_INT: u128 = ACCOUNT_ID_REGULAR_NETWORK_ACCOUNT_IMMUTABLE_CODE - | 0x00cc_77cc_0000_0000_0000_0000_0000_0000; - let network_account_id = AccountId::try_from(NETWORK_ACCOUNT_INT).unwrap(); - - // Expected network tag of variant `NoteTag::NetworkAccount`. - let expected_network_tag = 0b00101010_10110011_00011101_11110011; - - assert_eq!(NoteTag::from_account_id(network_account_id).as_u32(), expected_network_tag); - } - - #[test] - fn for_public_use_case() { - // NETWORK - // ---------------------------------------------------------------------------------------- - let tag = NoteTag::for_public_use_case(0b0, 0b0, NoteExecutionMode::Network).unwrap(); - assert_eq!(tag.as_u32(), 0b01000000_00000000_00000000_00000000u32); - - tag.validate(NoteType::Public).unwrap(); - - assert_matches!( - tag.validate(NoteType::Private).unwrap_err(), - NoteError::NetworkExecutionRequiresPublicNote(NoteType::Private) - ); - assert_matches!( - tag.validate(NoteType::Encrypted).unwrap_err(), - NoteError::NetworkExecutionRequiresPublicNote(NoteType::Encrypted) - ); - - let tag = NoteTag::for_public_use_case(0b1, 0b0, NoteExecutionMode::Network).unwrap(); - assert_eq!(tag.as_u32(), 0b01000000_00000001_00000000_00000000u32); - - let tag = NoteTag::for_public_use_case(0b0, 0b1, NoteExecutionMode::Network).unwrap(); - assert_eq!(tag.as_u32(), 0b01000000_00000000_00000000_00000001u32); - - let tag = NoteTag::for_public_use_case(1 << 13, 0b0, NoteExecutionMode::Network).unwrap(); - assert_eq!(tag.as_u32(), 0b01100000_00000000_00000000_00000000u32); - - // LOCAL - // ---------------------------------------------------------------------------------------- - let tag = NoteTag::for_public_use_case(0b0, 0b0, NoteExecutionMode::Local).unwrap(); - assert_eq!(tag.as_u32(), 0b10000000_00000000_00000000_00000000u32); - - tag.validate(NoteType::Public).unwrap(); - assert_matches!( - tag.validate(NoteType::Private).unwrap_err(), - NoteError::PublicNoteRequired(NoteType::Private) - ); - assert_matches!( - tag.validate(NoteType::Encrypted).unwrap_err(), - NoteError::PublicNoteRequired(NoteType::Encrypted) - ); - - let tag = NoteTag::for_public_use_case(0b0, 0b1, NoteExecutionMode::Local).unwrap(); - assert_eq!(tag.as_u32(), 0b10000000_00000000_00000000_00000001u32); - - let tag = NoteTag::for_public_use_case(0b1, 0b0, NoteExecutionMode::Local).unwrap(); - assert_eq!(tag.as_u32(), 0b10000000_00000001_00000000_00000000u32); - - let tag = NoteTag::for_public_use_case(1 << 13, 0b0, NoteExecutionMode::Local).unwrap(); - assert_eq!(tag.as_u32(), 0b10100000_00000000_00000000_00000000u32); - - assert_matches!( - NoteTag::for_public_use_case(1 << 15, 0b0, NoteExecutionMode::Local).unwrap_err(), - NoteError::NoteTagUseCaseTooLarge(use_case) if use_case == 1 << 15 - ); - assert_matches!( - NoteTag::for_public_use_case(1 << 14, 0b0, NoteExecutionMode::Local).unwrap_err(), - NoteError::NoteTagUseCaseTooLarge(use_case) if use_case == 1 << 14 - ); - } - - #[test] - fn for_private_use_case() { - let tag = NoteTag::for_local_use_case(0b0, 0b0).unwrap(); + fn from_custom_account_target() -> anyhow::Result<()> { + let account_id = AccountId::try_from(ACCOUNT_ID_SENDER)?; + let tag = NoteTag::with_custom_account_target( + account_id, + NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH, + )?; + + assert_eq!(tag.as_u32() >> 30, 0, "two most significant bits should be zero"); assert_eq!( - tag.as_u32() >> 30, - LOCAL_ANY >> 30, - "local use case prefix should be local any" + (account_id.prefix().as_u64() >> 34) as u32, + tag.as_u32(), + "30 most significant bits should match" ); - assert_eq!(tag.as_u32(), 0b11000000_00000000_00000000_00000000u32); - - tag.validate(NoteType::Public) - .expect("local execution should support public notes"); - tag.validate(NoteType::Private) - .expect("local execution should support private notes"); - tag.validate(NoteType::Encrypted) - .expect("local execution should support encrypted notes"); - - let tag = NoteTag::for_local_use_case(0b0, 0b1).unwrap(); - assert_eq!(tag.as_u32(), 0b11000000_00000000_00000000_00000001u32); - - let tag = NoteTag::for_local_use_case(0b1, 0b0).unwrap(); - assert_eq!(tag.as_u32(), 0b11000000_00000001_00000000_00000000u32); - let tag = NoteTag::for_local_use_case(1 << 13, 0b0).unwrap(); - assert_eq!(tag.as_u32(), 0b11100000_00000000_00000000_00000000u32); - - assert_matches!( - NoteTag::for_local_use_case(1 << 15, 0b0).unwrap_err(), - NoteError::NoteTagUseCaseTooLarge(use_case) if use_case == 1 << 15 - ); - assert_matches!( - NoteTag::for_local_use_case(1 << 14, 0b0).unwrap_err(), - NoteError::NoteTagUseCaseTooLarge(use_case) if use_case == 1 << 14 - ); - } - - /// Tests that as_u32 returns the correct prefix independent of the inner value. - #[test] - fn note_tag_as_u32() { - const HIGH_BITS_MASK: u32 = 0xc000_0000; - - assert_eq!(NoteTag::NetworkAccount(u32::MAX).as_u32() & HIGH_BITS_MASK, NETWORK_ACCOUNT); - assert_eq!( - NoteTag::NetworkUseCase(u16::MAX, u16::MAX).as_u32() & HIGH_BITS_MASK, - NETWORK_PUBLIC_USECASE - ); - assert_eq!(NoteTag::LocalPublicAny(u32::MAX).as_u32() & HIGH_BITS_MASK, LOCAL_PUBLIC_ANY); - assert_eq!(NoteTag::LocalAny(0).as_u32() & HIGH_BITS_MASK, LOCAL_ANY); + Ok(()) } } diff --git a/crates/miden-protocol/src/testing/mock_util_lib.rs b/crates/miden-protocol/src/testing/mock_util_lib.rs index 9337fde0f5..b319d2982c 100644 --- a/crates/miden-protocol/src/testing/mock_util_lib.rs +++ b/crates/miden-protocol/src/testing/mock_util_lib.rs @@ -7,9 +7,9 @@ use crate::utils::sync::LazyLock; const MOCK_UTIL_LIBRARY_CODE: &str = " use miden::protocol::output_note - # Inputs: [] - # Outputs: [note_idx] - pub proc create_random_note + #! Inputs: [] + #! Outputs: [note_idx] + pub proc create_default_note push.1.2.3.4 # = RECIPIENT push.1 # = NoteExecutionHint::Always push.2 # = NoteType::Private @@ -21,10 +21,10 @@ const MOCK_UTIL_LIBRARY_CODE: &str = " # => [note_idx] end - # Inputs: [ASSET] - # Outputs: [] - pub proc create_random_note_with_asset - exec.create_random_note + #! Inputs: [ASSET] + #! Outputs: [] + pub proc create_default_note_with_asset + exec.create_default_note # => [note_idx, ASSET] movdn.4 diff --git a/crates/miden-protocol/src/testing/note.rs b/crates/miden-protocol/src/testing/note.rs index 1ffd6c8168..13c8a9464e 100644 --- a/crates/miden-protocol/src/testing/note.rs +++ b/crates/miden-protocol/src/testing/note.rs @@ -28,11 +28,10 @@ impl Note { let metadata = NoteMetadata::new( sender_id, NoteType::Private, - NoteTag::from_account_id(sender_id), + NoteTag::with_account_target(sender_id), NoteExecutionHint::Always, ZERO, - ) - .unwrap(); + ); let inputs = NoteInputs::new(Vec::new()).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, inputs); diff --git a/crates/miden-protocol/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs index ad580b495f..e32f63b8dc 100644 --- a/crates/miden-protocol/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -80,7 +80,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // input_note_get_recipient word!("0xd3c255177f9243bb1a523a87615bbe76dd5a3605fcae87eb9d3a626d4ecce33c"), // output_note_create - word!("0x6562a9d1d8605d158415a4dcd4c8fd48fc2b6c21ec64c188db9def16b991a560"), + word!("0xa40e074cd5bd330b0af04c55b839da1e60e72583136f45d3e1bbc8b847865c3a"), // output_note_get_metadata word!("0xde4a5b57f9d53692459383e6cf6302ef3602a348896ed6ab6fdf67e07fa483ff"), // output_note_get_assets_info diff --git a/crates/miden-standards/src/account/interface/test.rs b/crates/miden-standards/src/account/interface/test.rs index 5c86dedab7..5b63a933ad 100644 --- a/crates/miden-standards/src/account/interface/test.rs +++ b/crates/miden-standards/src/account/interface/test.rs @@ -251,15 +251,14 @@ fn test_basic_wallet_custom_notes() { let sender_account_id = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into().unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); - let tag = NoteTag::from_account_id(wallet_account.id()); + let tag = NoteTag::with_account_target(wallet_account.id()); let metadata = NoteMetadata::new( sender_account_id, NoteType::Public, tag, NoteExecutionHint::always(), Default::default(), - ) - .unwrap(); + ); let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " @@ -341,15 +340,14 @@ fn test_basic_fungible_faucet_custom_notes() { let sender_account_id = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into().unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); - let tag = NoteTag::from_account_id(faucet_account.id()); + let tag = NoteTag::with_account_target(faucet_account.id()); let metadata = NoteMetadata::new( sender_account_id, NoteType::Public, tag, NoteExecutionHint::always(), Default::default(), - ) - .unwrap(); + ); let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " @@ -451,15 +449,14 @@ fn test_custom_account_custom_notes() { .expect("failed to create wallet account"); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); - let tag = NoteTag::from_account_id(target_account.id()); + let tag = NoteTag::with_account_target(target_account.id()); let metadata = NoteMetadata::new( sender_account.id(), NoteType::Public, tag, NoteExecutionHint::always(), Default::default(), - ) - .unwrap(); + ); let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " @@ -562,15 +559,14 @@ fn test_custom_account_multiple_components_custom_notes() { .expect("failed to create wallet account"); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); - let tag = NoteTag::from_account_id(target_account.id()); + let tag = NoteTag::with_account_target(target_account.id()); let metadata = NoteMetadata::new( sender_account.id(), NoteType::Public, tag, NoteExecutionHint::always(), Default::default(), - ) - .unwrap(); + ); let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " diff --git a/crates/miden-standards/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs index 2dbf1db2a6..1ad2c012ae 100644 --- a/crates/miden-standards/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -49,9 +49,9 @@ pub fn create_p2id_note( let serial_num = rng.draw_word(); let recipient = utils::build_p2id_recipient(target, serial_num)?; - let tag = NoteTag::from_account_id(target); + let tag = NoteTag::with_account_target(target); - let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux)?; + let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux); let vault = NoteAssets::new(assets)?; Ok(Note::new(vault, metadata, recipient)) @@ -83,14 +83,14 @@ pub fn create_p2ide_note( let serial_num = rng.draw_word(); let recipient = utils::build_p2ide_recipient(target, reclaim_height, timelock_height, serial_num)?; - let tag = NoteTag::from_account_id(target); + let tag = NoteTag::with_account_target(target); let execution_hint = match timelock_height { Some(height) => NoteExecutionHint::after_block(height)?, None => NoteExecutionHint::always(), }; - let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?; + let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux); let vault = NoteAssets::new(assets)?; Ok(Note::new(vault, metadata, recipient)) @@ -126,7 +126,7 @@ pub fn create_swap_note( let payback_recipient_word: Word = payback_recipient.digest(); let requested_asset_word: Word = requested_asset.into(); - let payback_tag = NoteTag::from_account_id(sender); + let payback_tag = NoteTag::with_account_target(sender); let inputs = NoteInputs::new(vec![ requested_asset_word[0], @@ -144,12 +144,12 @@ pub fn create_swap_note( ])?; // build the tag for the SWAP use case - let tag = build_swap_tag(swap_note_type, &offered_asset, &requested_asset)?; + let tag = build_swap_tag(swap_note_type, &offered_asset, &requested_asset); let serial_num = rng.draw_word(); // build the outgoing note let metadata = - NoteMetadata::new(sender, swap_note_type, tag, NoteExecutionHint::always(), swap_note_aux)?; + NoteMetadata::new(sender, swap_note_type, tag, NoteExecutionHint::always(), swap_note_aux); let assets = NoteAssets::new(vec![offered_asset])?; let recipient = NoteRecipient::new(serial_num, note_script, inputs); let note = Note::new(assets, metadata, recipient); @@ -200,9 +200,9 @@ pub fn create_mint_note( // Convert MintNoteInputs to NoteInputs let inputs = NoteInputs::from(mint_inputs); - let tag = NoteTag::from_account_id(faucet_id); + let tag = NoteTag::with_account_target(faucet_id); - let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?; + let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux); let assets = NoteAssets::new(vec![])?; // MINT notes have no assets let recipient = NoteRecipient::new(serial_num, note_script, inputs); @@ -246,9 +246,9 @@ pub fn create_burn_note( let execution_hint = NoteExecutionHint::always(); let inputs = NoteInputs::new(vec![])?; - let tag = NoteTag::from_account_id(faucet_id); + let tag = NoteTag::with_account_target(faucet_id); - let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?; + let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux); let assets = NoteAssets::new(vec![fungible_asset])?; // BURN notes contain the asset to burn let recipient = NoteRecipient::new(serial_num, note_script, inputs); diff --git a/crates/miden-standards/src/note/utils.rs b/crates/miden-standards/src/note/utils.rs index 6e3e408983..1d9fb780ee 100644 --- a/crates/miden-standards/src/note/utils.rs +++ b/crates/miden-standards/src/note/utils.rs @@ -1,7 +1,7 @@ use miden_protocol::account::AccountId; use miden_protocol::asset::Asset; use miden_protocol::block::BlockNumber; -use miden_protocol::note::{NoteExecutionMode, NoteInputs, NoteRecipient, NoteTag, NoteType}; +use miden_protocol::note::{NoteInputs, NoteRecipient, NoteTag, NoteType}; use miden_protocol::{Felt, NoteError, Word}; use super::well_known_note::WellKnownNote; @@ -47,18 +47,26 @@ pub fn build_p2ide_recipient( /// Returns a note tag for a swap note with the specified parameters. /// -/// Use case ID for the returned tag is set to 0. +/// The tag is laid out as follows: /// -/// Tag payload is constructed by taking asset tags (8 bits of each faucet ID) and concatenating -/// them together as offered_asset_tag + requested_asset tag. +/// ```text +/// [ +/// note_type (2 bits) | script_root (14 bits) +/// | offered_asset_faucet_id (8 bits) | requested_asset_faucet_id (8 bits) +/// ] +/// ``` /// -/// Network execution hint for the returned tag is set to `Local`. +/// The script root serves as the use case identifier of the SWAP tag. pub fn build_swap_tag( note_type: NoteType, offered_asset: &Asset, requested_asset: &Asset, -) -> Result { - const SWAP_USE_CASE_ID: u16 = 0; +) -> NoteTag { + let swap_root_bytes = WellKnownNote::SWAP.script().root().as_bytes(); + // Construct the swap use case ID from the 14 most significant bits of the script root. This + // leaves the two most significant bits zero. + let mut swap_use_case_id = (swap_root_bytes[0] as u16) << 6; + swap_use_case_id |= (swap_root_bytes[1] >> 2) as u16; // Get bits 0..8 from the faucet IDs of both assets which will form the tag payload. let offered_asset_id: u64 = offered_asset.faucet_id_prefix().into(); @@ -67,13 +75,12 @@ pub fn build_swap_tag( let requested_asset_id: u64 = requested_asset.faucet_id_prefix().into(); let requested_asset_tag = (requested_asset_id >> 56) as u8; - let payload = ((offered_asset_tag as u16) << 8) | (requested_asset_tag as u16); + let asset_pair = ((offered_asset_tag as u16) << 8) | (requested_asset_tag as u16); - let execution = NoteExecutionMode::Local; - match note_type { - NoteType::Public => NoteTag::for_public_use_case(SWAP_USE_CASE_ID, payload, execution), - _ => NoteTag::for_local_use_case(SWAP_USE_CASE_ID, payload), - } + let tag = + ((note_type as u8 as u32) << 30) | ((swap_use_case_id as u32) << 16) | asset_pair as u32; + + NoteTag::new(tag) } #[cfg(test)] @@ -129,16 +136,24 @@ mod tests { // The fungible ID starts with 0xcdb1. // The non fungible ID starts with 0xabec. // The expected tag payload is thus 0xcdab. - let expected_tag_payload = 0xcdab; - - let actual_tag = - build_swap_tag(NoteType::Public, &offered_asset, &requested_asset).unwrap(); - - // 0 is the SWAP use case ID. - let expected_tag = - NoteTag::for_public_use_case(0, expected_tag_payload, NoteExecutionMode::Local) - .unwrap(); - - assert_eq!(actual_tag, expected_tag); + let expected_asset_pair = 0xcdab; + + let note_type = NoteType::Public; + let actual_tag = build_swap_tag(note_type, &offered_asset, &requested_asset); + + assert_eq!(actual_tag.as_u32() as u16, expected_asset_pair, "asset pair should match"); + assert_eq!((actual_tag.as_u32() >> 30) as u8, note_type as u8, "note type should match"); + // Check the 8 bits of the first script root byte. + assert_eq!( + (actual_tag.as_u32() >> 22) as u8, + WellKnownNote::SWAP.script().root().as_bytes()[0], + "swap script root byte 0 should match" + ); + // Extract the 6 bits of the second script root byte and shift for comparison. + assert_eq!( + ((actual_tag.as_u32() & 0b00000000_00111111_00000000_00000000) >> 16) as u8, + WellKnownNote::SWAP.script().root().as_bytes()[1] >> 2, + "swap script root byte 1 should match with the lower two bits set to zero" + ); } } diff --git a/crates/miden-standards/src/testing/note.rs b/crates/miden-standards/src/testing/note.rs index 16451b9687..5d6a508900 100644 --- a/crates/miden-standards/src/testing/note.rs +++ b/crates/miden-standards/src/testing/note.rs @@ -57,7 +57,7 @@ impl NoteBuilder { note_execution_hint: NoteExecutionHint::None, serial_num, // The note tag is not under test, so we choose a value that is always valid. - tag: NoteTag::from_account_id(sender), + tag: NoteTag::with_account_target(sender), code: DEFAULT_NOTE_CODE.to_string(), aux: ZERO, dyn_libraries: Vec::new(), @@ -160,7 +160,7 @@ impl NoteBuilder { self.tag, self.note_execution_hint, self.aux, - )?; + ); let inputs = NoteInputs::new(self.inputs)?; let recipient = NoteRecipient::new(self.serial_num, note_script, inputs); diff --git a/crates/miden-testing/src/kernel_tests/tx/mod.rs b/crates/miden-testing/src/kernel_tests/tx/mod.rs index e59b033d0a..c3a9f84f77 100644 --- a/crates/miden-testing/src/kernel_tests/tx/mod.rs +++ b/crates/miden-testing/src/kernel_tests/tx/mod.rs @@ -1,5 +1,3 @@ -use alloc::string::String; - use anyhow::Context; use miden_processor::ContextId; use miden_processor::fast::ExecutionOutput; @@ -11,19 +9,7 @@ use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_SENDER, }; -use miden_protocol::testing::storage::prepare_assets; -use miden_protocol::transaction::memory::{ - self, - MemoryOffset, - NOTE_MEM_SIZE, - NUM_OUTPUT_NOTES_PTR, - OUTPUT_NOTE_ASSETS_OFFSET, - OUTPUT_NOTE_DIRTY_FLAG_OFFSET, - OUTPUT_NOTE_METADATA_OFFSET, - OUTPUT_NOTE_NUM_ASSETS_OFFSET, - OUTPUT_NOTE_RECIPIENT_OFFSET, - OUTPUT_NOTE_SECTION_OFFSET, -}; +use miden_protocol::transaction::memory::{self, MemoryOffset}; use miden_protocol::vm::StackInputs; use miden_protocol::{Felt, Word, ZERO}; @@ -117,66 +103,6 @@ pub fn input_note_data_ptr(note_idx: u32) -> memory::MemoryAddress { memory::INPUT_NOTE_DATA_SECTION_OFFSET + note_idx * memory::NOTE_MEM_SIZE } -/// Returns MASM code that defines a procedure called `create_mock_notes` which creates the notes -/// specified in `notes`, which stores output note metadata in the transaction host's memory. -pub fn create_mock_notes_procedure(notes: &[Note]) -> String { - if notes.is_empty() { - return String::new(); - } - - let mut script = String::from( - "proc create_mock_notes - # remove padding from prologue - dropw dropw dropw dropw - ", - ); - - for (idx, note) in notes.iter().enumerate() { - let metadata = Word::from(note.metadata()); - let recipient = note.recipient().digest(); - let assets = prepare_assets(note.assets()); - let num_assets = assets.len(); - let note_offset = (idx as u32) * NOTE_MEM_SIZE; - - assert!(num_assets == 1, "notes are expected to have one asset only"); - - script.push_str(&format!( - " - # populate note {idx} - push.{metadata} - push.{OUTPUT_NOTE_SECTION_OFFSET} push.{note_offset} push.{OUTPUT_NOTE_METADATA_OFFSET} add add mem_storew_be dropw - - push.{recipient} - push.{OUTPUT_NOTE_SECTION_OFFSET} push.{note_offset} push.{OUTPUT_NOTE_RECIPIENT_OFFSET} add add mem_storew_be dropw - - push.{num_assets} - push.{OUTPUT_NOTE_SECTION_OFFSET} push.{note_offset} push.{OUTPUT_NOTE_NUM_ASSETS_OFFSET} add add mem_store - - push.1 # dirty flag should be `1` by default - push.{OUTPUT_NOTE_SECTION_OFFSET} push.{note_offset} push.{OUTPUT_NOTE_DIRTY_FLAG_OFFSET} add add mem_store - - push.{first_asset} - push.{OUTPUT_NOTE_SECTION_OFFSET} push.{note_offset} push.{OUTPUT_NOTE_ASSETS_OFFSET} add add mem_storew_be dropw - ", - idx = idx, - metadata = metadata, - recipient = recipient, - num_assets = num_assets, - first_asset = assets[0], - note_offset = note_offset, - )); - } - script.push_str(&format!( - "# set num output notes - push.{count} push.{NUM_OUTPUT_NOTES_PTR} mem_store - end - ", - count = notes.len(), - )); - - script -} - // HELPER STRUCTURE // ================================================================================================ diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 47cdc60564..d0146be16b 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -1267,7 +1267,7 @@ async fn test_get_init_balance_subtraction() -> anyhow::Result<()> { begin # create random note and move the asset into it - exec.util::create_random_note + exec.util::create_default_note # => [note_idx] push.{REMOVED_ASSET} diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index bf2473d7f5..da54be8b2c 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -599,22 +599,15 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { let removed_assets = [removed_asset_1, removed_asset_2, removed_asset_3]; let tag1 = - NoteTag::from_account_id(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?); - let tag2 = NoteTag::for_local_use_case(0, 0)?; - let tag3 = NoteTag::for_local_use_case(0, 0)?; + NoteTag::with_account_target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?); + let tag2 = NoteTag::default(); + let tag3 = NoteTag::default(); let tags = [tag1, tag2, tag3]; let aux_array = [Felt::new(27), Felt::new(28), Felt::new(29)]; let note_types = [NoteType::Private; 3]; - tag1.validate(NoteType::Private) - .expect("note tag 1 should support private notes"); - tag2.validate(NoteType::Private) - .expect("note tag 2 should support private notes"); - tag3.validate(NoteType::Private) - .expect("note tag 3 should support private notes"); - let execution_hint_1 = Felt::from(NoteExecutionHint::always()); let execution_hint_2 = Felt::from(NoteExecutionHint::none()); let execution_hint_3 = Felt::from(NoteExecutionHint::on_block_slot(1, 1, 1)); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs index 8be831ff05..ef250d634c 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs @@ -793,10 +793,9 @@ fn create_p2ide_note_with_inputs(inputs: impl IntoIterator, sender: NoteInputs::new(inputs.into_iter().map(Felt::new).collect()).unwrap(), ); - let tag = NoteTag::from_account_id(sender); + let tag = NoteTag::with_account_target(sender); let metadata = - NoteMetadata::new(sender, NoteType::Public, tag, NoteExecutionHint::always(), ZERO) - .unwrap(); + NoteMetadata::new(sender, NoteType::Public, tag, NoteExecutionHint::always(), ZERO); Note::new(NoteAssets::default(), metadata, recipient) } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index f24642ae3e..b23404dd27 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -402,15 +402,14 @@ async fn test_active_note_get_exactly_8_inputs() -> anyhow::Result<()> { // prepare note data let serial_num = RpoRandomCoin::new(Word::from([4u32; 4])).draw_word(); - let tag = NoteTag::from_account_id(target_id); + let tag = NoteTag::with_account_target(target_id); let metadata = NoteMetadata::new( sender_id, NoteType::Public, tag, NoteExecutionHint::always(), Default::default(), - ) - .context("failed to create metadata")?; + ); let vault = NoteAssets::new(vec![]).context("failed to create input note assets")?; let note_script = CodeBuilder::default() .compile_note_script("begin nop end") diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 16f1c564ff..79c7b608dc 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -1,6 +1,8 @@ use alloc::string::ToString; use alloc::vec::Vec; +use std::borrow::ToOwned; +use miden_processor::crypto::RpoRandomCoin; use miden_processor::{Felt, ONE}; use miden_protocol::Word; use miden_protocol::account::{Account, AccountDelta, AccountStorageDelta, AccountVaultDelta}; @@ -17,7 +19,6 @@ use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, - ACCOUNT_ID_SENDER, }; use miden_protocol::testing::storage::MOCK_VALUE_SLOT0; use miden_protocol::transaction::memory::{ @@ -30,9 +31,9 @@ use miden_standards::code_builder::CodeBuilder; use miden_standards::testing::mock_account::MockAccountExt; use miden_standards::testing::note::NoteBuilder; -use super::{ZERO, create_mock_notes_procedure}; +use super::ZERO; use crate::kernel_tests::tx::ExecutionOutputExt; -use crate::utils::{create_public_p2any_note, create_spawn_note}; +use crate::utils::{create_p2any_note, create_public_p2any_note}; use crate::{ Auth, MockChain, @@ -42,49 +43,56 @@ use crate::{ assert_transaction_executor_error, }; +/// Tests that the return values from the tx kernel main.masm program match the expected values. #[tokio::test] async fn test_epilogue() -> anyhow::Result<()> { let account = Account::mock(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, Auth::IncrNonce); - let tx_context = { - let output_note_1 = create_public_p2any_note( - ACCOUNT_ID_SENDER.try_into().unwrap(), - [FungibleAsset::mock(100)], - ); - - // input_note_1 is needed for maintaining cohesion of involved assets - let input_note_1 = create_public_p2any_note( - ACCOUNT_ID_SENDER.try_into().unwrap(), - [FungibleAsset::mock(100)], - ); - let input_note_2 = create_spawn_note([&output_note_1])?; - TransactionContextBuilder::new(account.clone()) - .extend_input_notes(vec![input_note_1, input_note_2]) - .extend_expected_output_notes(vec![OutputNote::Full(output_note_1)]) - .build()? - }; - - let output_notes_data_procedure = - create_mock_notes_procedure(tx_context.expected_output_notes()); + let asset = FungibleAsset::mock(100); + let output_note_1 = create_public_p2any_note(account.id(), [asset]); + // input_note_1 is needed for maintaining cohesion of involved assets + let input_note_1 = + create_public_p2any_note(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1.try_into().unwrap(), [asset]); + + let tx_context = TransactionContextBuilder::new(account.clone()) + .extend_input_notes(vec![input_note_1]) + .extend_expected_output_notes(vec![OutputNote::Full(output_note_1.clone())]) + .build()?; let code = format!( " use $kernel::prologue use $kernel::account use $kernel::epilogue - - {output_notes_data_procedure} + use miden::protocol::output_note + use miden::core::sys begin exec.prologue::prepare_transaction - exec.create_mock_notes + push.{recipient} + push.{note_execution_hint} + push.{note_type} + push.{aux} + push.{tag} + exec.output_note::create + # => [note_idx] + + push.{asset} + exec.output_note::add_asset + # => [] exec.epilogue::finalize_transaction # truncate the stack - repeat.13 movup.13 drop end + exec.sys::truncate_stack end - " + ", + recipient = output_note_1.recipient().digest(), + note_execution_hint = Felt::from(output_note_1.metadata().execution_hint()), + note_type = Felt::from(output_note_1.metadata().note_type()), + aux = output_note_1.metadata().aux(), + tag = Felt::from(output_note_1.metadata().tag()), + asset = Word::from(asset) ); let exec_output = tx_context.execute_code(&code).await?; @@ -144,48 +152,74 @@ async fn test_epilogue() -> anyhow::Result<()> { Ok(()) } +/// Tests that the output note memory section is correctly populated during finalize_transaction. #[tokio::test] async fn test_compute_output_note_id() -> anyhow::Result<()> { - let tx_context = { - let account = - Account::mock(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, Auth::IncrNonce); - let output_note_1 = - create_public_p2any_note(ACCOUNT_ID_SENDER.try_into()?, [FungibleAsset::mock(100)]); - - // input_note_1 is needed for maintaining cohesion of involved assets - let input_note_1 = - create_public_p2any_note(ACCOUNT_ID_SENDER.try_into()?, [FungibleAsset::mock(100)]); - let input_note_2 = create_spawn_note([&output_note_1])?; - TransactionContextBuilder::new(account) - .extend_input_notes(vec![input_note_1, input_note_2]) - .extend_expected_output_notes(vec![OutputNote::Full(output_note_1)]) - .build()? - }; - - let output_notes_data_procedure = - create_mock_notes_procedure(tx_context.expected_output_notes()); - - for (note, i) in tx_context.expected_output_notes().iter().zip(0u32..) { - let code = format!( - " + let mut rng = RpoRandomCoin::new(Word::from([3, 4, 5, 6u32])); + let account = Account::mock(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, Auth::IncrNonce); + let mut assets = account.vault().assets(); + let asset0 = assets.next().unwrap(); + let asset1 = assets.next().unwrap(); + + let output_note0 = create_p2any_note(account.id(), NoteType::Private, [asset0], &mut rng); + let output_note1 = create_p2any_note(account.id(), NoteType::Private, [asset1], &mut rng); + + let tx_context = TransactionContextBuilder::new(account.clone()) + .extend_expected_output_notes(vec![ + OutputNote::Full(output_note0.clone()), + OutputNote::Full(output_note1.clone()), + ]) + .build()?; + + let mut code = " use $kernel::prologue use $kernel::epilogue - - {output_notes_data_procedure} + use miden::protocol::output_note + use miden::core::sys begin - exec.prologue::prepare_transaction - exec.create_mock_notes - exec.epilogue::finalize_transaction + exec.prologue::prepare_transaction" + .to_owned(); + + for note in tx_context.expected_output_notes() { + let asset = note.assets().iter().next().unwrap(); - # truncate the stack - repeat.13 movup.13 drop end - end + code.push_str(&format!( " - ); + push.{recipient} + push.{note_execution_hint} + push.{note_type} + push.{aux} + push.{tag} + exec.output_note::create + # => [note_idx] + + push.{asset} + call.::miden::standards::wallets::basic::move_asset_to_note + # => [] + ", + recipient = note.recipient().digest(), + note_execution_hint = Felt::from(note.metadata().execution_hint()), + note_type = Felt::from(note.metadata().note_type()), + aux = note.metadata().aux(), + tag = Felt::from(note.metadata().tag()), + asset = Word::from(asset) + )); + } - let exec_output = &tx_context.execute_code(&code).await?; + code.push_str( + " + exec.epilogue::finalize_transaction + + # truncate the stack + exec.sys::truncate_stack + end", + ); + + let exec_output = &tx_context.execute_code(&code).await?; + for (i, note) in tx_context.expected_output_notes().iter().enumerate() { + let i = i as u32; assert_eq!( note.assets().commitment(), exec_output.get_kernel_mem_word( @@ -202,6 +236,7 @@ async fn test_compute_output_note_id() -> anyhow::Result<()> { "NOTE_ID didn't match expected value", ); } + Ok(()) } @@ -232,7 +267,7 @@ async fn epilogue_fails_when_num_output_assets_exceed_num_input_assets() -> anyh begin # create a note with the output asset push.{OUTPUT_ASSET} - exec.util::create_random_note_with_asset + exec.util::create_default_note_with_asset # => [] end ", @@ -285,7 +320,7 @@ async fn epilogue_fails_when_num_input_assets_exceed_num_output_assets() -> anyh begin # create a note with the output asset push.{OUTPUT_ASSET} - exec.util::create_random_note_with_asset + exec.util::create_default_note_with_asset # => [] end ", @@ -530,7 +565,7 @@ async fn test_epilogue_execute_empty_transaction() -> anyhow::Result<()> { #[tokio::test] async fn test_epilogue_empty_transaction_with_empty_output_note() -> anyhow::Result<()> { let tag = - NoteTag::from_account_id(ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE.try_into()?); + NoteTag::with_account_target(ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE.try_into()?); let aux = Felt::new(26); let note_type = NoteType::Private; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs index 4372650953..cf0216fa6d 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_lazy_loading.rs @@ -96,7 +96,7 @@ async fn removing_fungible_assets_with_lazy_loading_succeeds() -> anyhow::Result # => [] # move asset to note to adhere to asset preservation rules - exec.util::create_random_note_with_asset + exec.util::create_default_note_with_asset # => [] push.{FUNGIBLE_ASSET2} @@ -104,7 +104,7 @@ async fn removing_fungible_assets_with_lazy_loading_succeeds() -> anyhow::Result # => [ASSET] # move asset to note to adhere to asset preservation rules - exec.util::create_random_note_with_asset + exec.util::create_default_note_with_asset # => [] end ", diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index c8dec803f5..f4ceaea6ac 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -15,7 +15,6 @@ use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, - NoteExecutionMode, NoteInputs, NoteMetadata, NoteRecipient, @@ -392,22 +391,18 @@ async fn test_build_metadata() -> miette::Result<()> { let test_metadata1 = NoteMetadata::new( sender, NoteType::Private, - NoteTag::from_account_id(receiver), + NoteTag::with_account_target(receiver), NoteExecutionHint::after_block(500.into()) .map_err(|e| miette::miette!("Failed to create execution hint: {}", e))?, Felt::try_from(1u64 << 63).map_err(|e| miette::miette!("Failed to convert felt: {}", e))?, - ) - .map_err(|e| miette::miette!("Failed to create metadata: {}", e))?; + ); let test_metadata2 = NoteMetadata::new( sender, NoteType::Public, - // Use largest allowed use_case_id. - NoteTag::for_public_use_case((1 << 14) - 1, u16::MAX, NoteExecutionMode::Local) - .map_err(|e| miette::miette!("Failed to create note tag: {}", e))?, + NoteTag::new(u32::MAX), NoteExecutionHint::on_block_slot(u8::MAX, u8::MAX, u8::MAX), Felt::try_from(0u64).map_err(|e| miette::miette!("Failed to convert felt: {}", e))?, - ) - .map_err(|e| miette::miette!("Failed to create metadata: {}", e))?; + ); for (iteration, test_metadata) in [test_metadata1, test_metadata2].into_iter().enumerate() { let code = format!( @@ -545,14 +540,14 @@ async fn test_public_key_as_note_input() -> anyhow::Result<()> { .build_existing()?; let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); - let tag = NoteTag::from_account_id(target_account.id()); + let tag = NoteTag::with_account_target(target_account.id()); let metadata = NoteMetadata::new( sender_account.id(), NoteType::Public, tag, NoteExecutionHint::always(), Default::default(), - )?; + ); let vault = NoteAssets::new(vec![])?; let note_script = CodeBuilder::default().compile_note_script("begin nop end")?; let recipient = @@ -573,7 +568,7 @@ async fn test_build_note_tag_for_network_account() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let account_id = AccountId::try_from(ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET)?; - let expected_tag = NoteTag::from_account_id(account_id).as_u32(); + let expected_tag = NoteTag::with_account_target(account_id).as_u32(); let prefix: u64 = account_id.prefix().into(); let suffix: u64 = account_id.suffix().into(); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index 9797291131..a998663174 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -13,7 +13,6 @@ use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, - NoteExecutionMode, NoteInputs, NoteMetadata, NoteRecipient, @@ -58,7 +57,7 @@ async fn test_create_note() -> anyhow::Result<()> { let recipient = Word::from([0, 1, 2, 3u32]); let aux = Felt::new(27); - let tag = NoteTag::from_account_id(account_id); + let tag = NoteTag::with_account_target(account_id); let code = format!( " @@ -107,7 +106,7 @@ async fn test_create_note() -> anyhow::Result<()> { tag, NoteExecutionHint::after_block(23.into())?, Felt::new(27), - )? + ) .into(); assert_eq!( @@ -129,7 +128,7 @@ async fn test_create_note_with_invalid_tag() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let invalid_tag = Felt::new((NoteType::Public as u64) << 62); - let valid_tag: Felt = NoteTag::for_local_use_case(0, 0).unwrap().into(); + let valid_tag: Felt = NoteTag::default().into(); // Test invalid tag assert!(tx_context.execute_code(¬e_creation_script(invalid_tag)).await.is_err()); @@ -193,7 +192,7 @@ async fn test_create_note_too_many_notes() -> anyhow::Result<()> { call.output_note::create end ", - tag = NoteTag::for_local_use_case(1234, 5678).unwrap(), + tag = NoteTag::new(1234 << 16 | 5678), recipient = Word::from([0, 1, 2, 3u32]), execution_hint_always = Felt::from(NoteExecutionHint::always()), PUBLIC_NOTE = NoteType::Public as u8, @@ -255,7 +254,7 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { // create output note 1 let output_serial_no_1 = Word::from([8u32; 4]); - let output_tag_1 = NoteTag::from_account_id(network_account); + let output_tag_1 = NoteTag::with_account_target(network_account); let assets = NoteAssets::new(vec![input_asset_1])?; let metadata = NoteMetadata::new( tx_context.tx_inputs().account().id(), @@ -263,14 +262,14 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { output_tag_1, NoteExecutionHint::Always, ZERO, - )?; + ); let inputs = NoteInputs::new(vec![])?; let recipient = NoteRecipient::new(output_serial_no_1, input_note_1.script().clone(), inputs); let output_note_1 = Note::new(assets, metadata, recipient); // create output note 2 let output_serial_no_2 = Word::from([11u32; 4]); - let output_tag_2 = NoteTag::from_account_id(local_account); + let output_tag_2 = NoteTag::with_account_target(local_account); let assets = NoteAssets::new(vec![input_asset_2])?; let metadata = NoteMetadata::new( tx_context.tx_inputs().account().id(), @@ -278,7 +277,7 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { output_tag_2, NoteExecutionHint::after_block(123.into())?, ZERO, - )?; + ); let inputs = NoteInputs::new(vec![])?; let recipient = NoteRecipient::new(output_serial_no_2, input_note_2.script().clone(), inputs); let output_note_2 = Note::new(assets, metadata, recipient); @@ -392,7 +391,7 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?; let recipient = Word::from([0, 1, 2, 3u32]); let aux = Felt::new(27); - let tag = NoteTag::from_account_id(faucet_id); + let tag = NoteTag::with_account_target(faucet_id); let asset = Word::from(FungibleAsset::new(faucet_id, 10)?); let code = format!( @@ -454,7 +453,7 @@ async fn test_create_note_and_add_multiple_assets() -> anyhow::Result<()> { let recipient = Word::from([0, 1, 2, 3u32]); let aux = Felt::new(27); - let tag = NoteTag::from_account_id(faucet_2); + let tag = NoteTag::with_account_target(faucet_2); let asset = Word::from(FungibleAsset::new(faucet, 10)?); let asset_2 = Word::from(FungibleAsset::new(faucet_2, 20)?); @@ -541,7 +540,7 @@ async fn test_create_note_and_add_same_nft_twice() -> anyhow::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; let recipient = Word::from([0, 1, 2, 3u32]); - let tag = NoteTag::for_public_use_case(999, 777, NoteExecutionMode::Local).unwrap(); + let tag = NoteTag::new(999 << 16 | 777); let non_fungible_asset = NonFungibleAsset::mock(&[1, 2, 3]); let encoded = Word::from(non_fungible_asset); @@ -631,7 +630,7 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { // create output note let output_serial_no = Word::from([0, 1, 2, 3u32]); let aux = Felt::new(27); - let tag = NoteTag::for_public_use_case(42, 42, NoteExecutionMode::Network).unwrap(); + let tag = NoteTag::new(42 << 16 | 42); let single_input = 2; let inputs = NoteInputs::new(vec![Felt::new(single_input)]).unwrap(); let input_commitment = inputs.commitment(); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index 1714c4b9bb..d9641af5e5 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -22,7 +22,6 @@ use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, - NoteExecutionMode, NoteHeader, NoteId, NoteInputs, @@ -198,11 +197,11 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { .expect("asset is valid"), ); - let tag1 = NoteTag::from_account_id( + let tag1 = NoteTag::with_account_target( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap(), ); - let tag2 = NoteTag::for_public_use_case(0, 0, NoteExecutionMode::Local).unwrap(); - let tag3 = NoteTag::for_public_use_case(0, 0, NoteExecutionMode::Local).unwrap(); + let tag2 = NoteTag::default(); + let tag3 = NoteTag::default(); let aux1 = Felt::new(27); let aux2 = Felt::new(28); let aux3 = Felt::new(29); @@ -211,10 +210,6 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let note_type2 = NoteType::Public; let note_type3 = NoteType::Public; - tag1.validate(note_type1).expect("note tag 1 should support private notes"); - tag2.validate(note_type2).expect("note tag 2 should support public notes"); - tag3.validate(note_type3).expect("note tag 3 should support public notes"); - // In this test we create 3 notes. Note 1 is private, Note 2 is public and Note 3 is public // without assets. @@ -223,7 +218,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let note_script_2 = CodeBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; let inputs_2 = NoteInputs::new(vec![ONE])?; let metadata_2 = - NoteMetadata::new(account_id, note_type2, tag2, NoteExecutionHint::none(), aux2)?; + NoteMetadata::new(account_id, note_type2, tag2, NoteExecutionHint::none(), aux2); let vault_2 = NoteAssets::new(vec![removed_asset_3, removed_asset_4])?; let recipient_2 = NoteRecipient::new(serial_num_2, note_script_2, inputs_2); let expected_output_note_2 = Note::new(vault_2, metadata_2, recipient_2); @@ -238,7 +233,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { tag3, NoteExecutionHint::on_block_slot(1, 2, 3), aux3, - )?; + ); let vault_3 = NoteAssets::new(vec![])?; let recipient_3 = NoteRecipient::new(serial_num_3, note_script_3, inputs_3); let expected_output_note_3 = Note::new(vault_3, metadata_3, recipient_3); diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index 37339b3b25..0c3c1d790f 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -64,8 +64,7 @@ pub fn get_note_with_fungible_asset_and_script( let vault = NoteAssets::new(vec![fungible_asset.into()]).unwrap(); let metadata = - NoteMetadata::new(sender_id, NoteType::Public, 1.into(), NoteExecutionHint::Always, ZERO) - .unwrap(); + NoteMetadata::new(sender_id, NoteType::Public, 1.into(), NoteExecutionHint::Always, ZERO); let inputs = NoteInputs::new(vec![]).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, inputs); diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 38b378a903..d6edecea48 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -17,7 +17,6 @@ use miden_protocol::note::{ Note, NoteAssets, NoteExecutionHint, - NoteExecutionMode, NoteId, NoteInputs, NoteMetadata, @@ -127,7 +126,7 @@ pub fn verify_minted_output_note( params.tag, params.note_execution_hint, params.aux - )? + ) ); Ok(()) @@ -145,18 +144,13 @@ async fn minting_fungible_asset_on_existing_faucet_succeeds() -> anyhow::Result< let params = FaucetTestParams { recipient: Word::from([0, 1, 2, 3u32]), - tag: NoteTag::for_local_use_case(0, 0).unwrap(), + tag: NoteTag::default(), aux: Felt::new(27), note_execution_hint: NoteExecutionHint::on_block_slot(5, 6, 7), note_type: NoteType::Private, amount: Felt::new(100), }; - params - .tag - .validate(params.note_type) - .expect("note tag should support private notes"); - let executed_transaction = execute_mint_transaction(&mut mock_chain, faucet.clone(), ¶ms).await?; verify_minted_output_note(&executed_transaction, &faucet, ¶ms)?; @@ -230,18 +224,13 @@ async fn minting_fungible_asset_on_new_faucet_succeeds() -> anyhow::Result<()> { let params = FaucetTestParams { recipient: Word::from([0, 1, 2, 3u32]), - tag: NoteTag::for_local_use_case(0, 0).unwrap(), + tag: NoteTag::default(), aux: Felt::new(27), note_execution_hint: NoteExecutionHint::on_block_slot(5, 6, 7), note_type: NoteType::Private, amount: Felt::new(100), }; - params - .tag - .validate(params.note_type) - .expect("note tag should support private notes"); - let executed_transaction = execute_mint_transaction(&mut mock_chain, faucet.clone(), ¶ms).await?; verify_minted_output_note(&executed_transaction, &faucet, ¶ms)?; @@ -324,7 +313,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul // Parameters for the PUBLIC note that will be created by the faucet let recipient_account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER)?; let amount = Felt::new(75); - let tag = NoteTag::for_public_use_case(0, 0, NoteExecutionMode::Local)?; + let tag = NoteTag::default(); let aux = Felt::new(27); let note_execution_hint = NoteExecutionHint::on_block_slot(5, 6, 7); let note_type = NoteType::Public; @@ -357,7 +346,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let output_script_root = note_recipient.script().root(); let asset = FungibleAsset::new(faucet.id(), amount.into())?; - let metadata = NoteMetadata::new(faucet.id(), note_type, tag, note_execution_hint, aux)?; + let metadata = NoteMetadata::new(faucet.id(), note_type, tag, note_execution_hint, aux); let expected_note = Note::new(NoteAssets::new(vec![asset.into()])?, metadata, note_recipient); let trigger_note_script_code = format!( @@ -422,7 +411,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let mut rng = RpoRandomCoin::new([Felt::from(1u32); 4].into()); let trigger_note = NoteBuilder::new(faucet.id(), &mut rng) .note_type(NoteType::Private) - .tag(NoteTag::for_local_use_case(0, 0)?.into()) + .tag(NoteTag::default().into()) .note_execution_hint(NoteExecutionHint::always()) .aux(Felt::new(0)) .serial_number(Word::from([1, 2, 3, 4u32])) @@ -532,7 +521,7 @@ async fn network_faucet_mint() -> anyhow::Result<()> { let aux = Felt::new(27); let serial_num = Word::default(); - let output_note_tag = NoteTag::from_account_id(target_account.id()); + let output_note_tag = NoteTag::with_account_target(target_account.id()); let p2id_mint_output_note = create_p2id_note_exact( faucet.id(), target_account.id(), @@ -715,7 +704,7 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow // Create MINT note based on note type let mint_inputs = match note_type { NoteType::Private => { - let output_note_tag = NoteTag::from_account_id(target_account.id()); + let output_note_tag = NoteTag::with_account_target(target_account.id()); let recipient = p2id_mint_output_note.recipient().digest(); MintNoteInputs::new_private( recipient, @@ -726,7 +715,7 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow ) }, NoteType::Public => { - let output_note_tag = NoteTag::from_account_id(target_account.id()); + let output_note_tag = NoteTag::with_account_target(target_account.id()); let p2id_script = WellKnownNote::P2ID.script(); let p2id_inputs = vec![target_account.id().suffix(), target_account.id().prefix().as_felt()]; diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index 083993039d..84d8c02934 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -35,14 +35,14 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let sender_account_interface = AccountInterface::from_account(&sender_basic_wallet_account); - let tag = NoteTag::from_account_id(sender_basic_wallet_account.id()); + let tag = NoteTag::with_account_target(sender_basic_wallet_account.id()); let metadata = NoteMetadata::new( sender_basic_wallet_account.id(), NoteType::Public, tag, NoteExecutionHint::always(), Default::default(), - )?; + ); let assets = NoteAssets::new(vec![sent_asset]).unwrap(); let note_script = CodeBuilder::default().compile_note_script("begin nop end").unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); @@ -95,14 +95,14 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { let sender_account_interface = AccountInterface::from_account(&sender_basic_fungible_faucet_account); - let tag = NoteTag::from_account_id(sender_basic_fungible_faucet_account.id()); + let tag = NoteTag::with_account_target(sender_basic_fungible_faucet_account.id()); let metadata = NoteMetadata::new( sender_basic_fungible_faucet_account.id(), NoteType::Public, tag, NoteExecutionHint::always(), Default::default(), - )?; + ); let assets = NoteAssets::new(vec![Asset::Fungible( FungibleAsset::new(sender_basic_fungible_faucet_account.id(), 10).unwrap(), )])?; diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index bce9193a6c..777adb0a69 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -358,9 +358,9 @@ pub fn create_p2id_note_exact( ) -> Result { let recipient = utils::build_p2id_recipient(target, serial_num)?; - let tag = NoteTag::from_account_id(target); + let tag = NoteTag::with_account_target(target); - let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux)?; + let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux); let vault = NoteAssets::new(assets)?; Ok(Note::new(vault, metadata, recipient)) diff --git a/docs/src/note.md b/docs/src/note.md index 20f30201f2..276007652a 100644 --- a/docs/src/note.md +++ b/docs/src/note.md @@ -98,7 +98,26 @@ After validation notes become "live" and eligible for consumption. If creation a Clients often need to find specific notes of interest. Miden allows clients to query the `Note` database using `Note` tags. These lightweight, 32-bit data fields serve as best-effort filters, enabling quick lookups for notes related to particular use cases, scripts, or account prefixes. -Using `Note` tags strikes a balance between privacy and efficiency. Without tags, querying a specific `Note` ID reveals a user’s interest to the operator. Conversely, downloading and filtering all registered notes locally is highly inefficient. Tags allow users to adjust their level of privacy by choosing how broadly or narrowly they define their search criteria, letting them find the right balance between revealing too much information and incurring excessive computational overhead. +While note tags can be arbitrarily constructed from 32 bits of data, there are two categories of tags that many notes fit into. + +#### Account Targets + +A note targeted at an account is a note that is intended or even enforced to be consumed by a specific account. One example is a P2ID note that can only be consumed by a specific account ID. The tag for such a note should make it easy for the receiver to find the note. Therefore, the tag encodes a certain number of bits of the receiver account's ID, by convention. Notably, it may not encode the full 32 bits of the target account's ID to preserve the receiver's privacy. See also the section on privacy below. + +#### Use Case Tags + +Use case notes are notes that are not intended to be consumed by a specific account, but by anyone willing to fulfill the note's contract. One example is a SWAP note that trades one asset against another. Such a use case note can define the structure of their note tags. A sensible structure for a SWAP note could be: +- encoding the 2 bits of the note's type. +- encoding the note script root, i.e. making it identifiable as a SWAP note, for example by + using 16 bits of the SWAP script root. +- encoding the SWAP pair, for example by using 8 bits of the offered asset faucet ID and 8 bits + of the requested asset faucet ID. + +This allows clients to search for a public SWAP note that trades USDC against ETH only through the note tag. Since tags are not validated in any way and only act as best-effort filters, further local filtering is almost always necessary. For example, there could easily be a collision on the 8 bits used in SWAP tag's faucet IDs. + +#### Privacy vs Efficiency + +Using `Note` tags strikes a balance between privacy and efficiency. Without tags, querying a specific `Note` ID reveals a user's interest to the operator. Conversely, downloading and filtering all registered notes locally is highly inefficient. Tags allow users to adjust their level of privacy by choosing how broadly or narrowly they define their search criteria, letting them find the right balance between revealing too much information and incurring excessive computational overhead. ### Note consumption From d92a8303a68217b0e3435cce41eb4bd7de678263 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 15 Jan 2026 08:47:44 +0100 Subject: [PATCH 098/114] feat: add tx kernel support for `NoteAttachment` (#2249) * feat: Implement `NoteAttachment` * chore: Precompute `NoteHeader` in `PartialNote` * feat: Remove aux, exec hint from `NoteMetadata`; add attachment * feat: refactor output note memory layout and metadata building * chore: adapt tx event extraction to new output_note::create stack state * chore: prepare `compute_output_notes_commitment` for attachment hashing * chore: Update input notes commitment to include attachment * chore: include attachment in output notes commitment * chore: return attachment and header from `input_note_get_metadata` * chore: return attachment and header from `output_note_get_metadata` * fix: compilation errors in `miden-testing` * chore: regenerate kernel procedure hashes * chore: add changelog * chore: rename attachment_types to attachment_type_info for clarity * chore: Move content to word encoding to method * chore: rename `Raw` -> `Word` and `Commitment` -> `Array` * chore: Rename `NoteAttachmentCommitment` to `*Array` --- CHANGELOG.md | 1 + .../asm/kernels/transaction/api.masm | 44 +- .../asm/kernels/transaction/lib/memory.masm | 150 +++--- .../asm/kernels/transaction/lib/note.masm | 66 ++- .../kernels/transaction/lib/output_note.masm | 144 +++-- .../asm/kernels/transaction/lib/prologue.masm | 54 +- .../asm/protocol/active_note.masm | 33 +- .../asm/protocol/input_note.masm | 38 +- crates/miden-protocol/asm/protocol/note.masm | 14 +- .../asm/protocol/output_note.masm | 11 +- crates/miden-protocol/src/batch/note_tree.rs | 2 +- crates/miden-protocol/src/block/block_body.rs | 2 +- crates/miden-protocol/src/block/note_tree.rs | 8 +- .../src/block/proposed_block.rs | 2 +- crates/miden-protocol/src/errors/mod.rs | 16 +- crates/miden-protocol/src/note/attachment.rs | 491 ++++++++++++++++++ crates/miden-protocol/src/note/file.rs | 9 +- crates/miden-protocol/src/note/header.rs | 68 +-- crates/miden-protocol/src/note/metadata.rs | 448 ++++++++-------- crates/miden-protocol/src/note/mod.rs | 29 +- crates/miden-protocol/src/note/partial.rs | 25 +- .../src/testing/block_note_tree.rs | 2 +- crates/miden-protocol/src/testing/note.rs | 5 +- .../src/transaction/kernel/advice_inputs.rs | 3 +- .../src/transaction/kernel/memory.rs | 53 +- .../src/transaction/kernel/procedures.rs | 18 +- .../miden-protocol/src/transaction/outputs.rs | 49 +- .../src/transaction/proven_tx.rs | 4 +- .../src/transaction/tx_header.rs | 7 +- .../src/account/interface/test.rs | 41 +- crates/miden-standards/src/note/mod.rs | 35 +- crates/miden-standards/src/testing/note.rs | 10 +- .../block/proven_block_success.rs | 2 +- .../kernel_tests/tx/test_account_interface.rs | 6 +- .../src/kernel_tests/tx/test_active_note.rs | 23 +- .../src/kernel_tests/tx/test_epilogue.rs | 2 +- .../src/kernel_tests/tx/test_input_note.rs | 14 +- .../src/kernel_tests/tx/test_note.rs | 44 +- .../src/kernel_tests/tx/test_output_note.rs | 94 ++-- .../src/kernel_tests/tx/test_prologue.rs | 15 +- .../src/kernel_tests/tx/test_tx.rs | 17 +- crates/miden-testing/src/mock_chain/chain.rs | 2 +- crates/miden-testing/tests/lib.rs | 7 +- crates/miden-testing/tests/scripts/faucet.rs | 20 +- .../miden-testing/tests/scripts/send_note.rs | 18 +- crates/miden-testing/tests/scripts/swap.rs | 19 +- crates/miden-tx/src/errors/mod.rs | 2 - crates/miden-tx/src/executor/exec_host.rs | 2 +- crates/miden-tx/src/host/kernel_process.rs | 12 + crates/miden-tx/src/host/tx_event.rs | 56 +- crates/miden-tx/src/prover/prover_host.rs | 2 +- 51 files changed, 1344 insertions(+), 895 deletions(-) create mode 100644 crates/miden-protocol/src/note/attachment.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c2f6d9d20..505965b8ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). - Add `AccountId::parse()` helper function to parse both hex and bech32 formats ([#2223](https://github.com/0xMiden/miden-base/pull/2223)). - Add `read_foreign_account_inputs()`, `read_vault_asset_witnesses()`, and `read_storage_map_witness()` for `TransactionInputs` ([#2246](https://github.com/0xMiden/miden-base/pull/2246)). +- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249)). ### Changes diff --git a/crates/miden-protocol/asm/kernels/transaction/api.masm b/crates/miden-protocol/asm/kernels/transaction/api.masm index d5f7891a4f..8d12410b65 100644 --- a/crates/miden-protocol/asm/kernels/transaction/api.masm +++ b/crates/miden-protocol/asm/kernels/transaction/api.masm @@ -972,14 +972,15 @@ end #! Returns the metadata of the specified input note. #! #! Inputs: [is_active_note, note_index, pad(14)] -#! Outputs: [METADATA, pad(12)] +#! Outputs: [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] #! #! Where: #! - is_active_note is the boolean flag indicating whether we should return the metadata from #! the active note or from the note with the specified index. #! - note_index is the index of the input note whose metadata should be returned. Notice that if #! is_active_note is 1, note_index is ignored. -#! - METADATA is the metadata of the specified input note. +#! - METADATA_HEADER is the metadata header of the specified input note. +#! - NOTE_ATTACHMENT is the attachment of the specified input note. #! #! Panics if: #! - the note index is greater or equal to the total number of input notes. @@ -998,13 +999,21 @@ pub proc input_note_get_metadata dup neq.0 assert.err=ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED # => [input_note_ptr, pad(15)] + # make stack truncation at the end of the procedure easier + push.0 swap + # => [input_note_ptr, pad(16)] + # get the metadata - exec.memory::get_input_note_metadata - # => [METADATA, pad(15)] + dup exec.memory::get_input_note_metadata_header + # => [METADATA_HEADER, input_note_ptr, pad(16)] + + # get the attachment + movup.4 exec.memory::get_input_note_attachment + # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(16)] # truncate the stack - swapw drop drop drop movdn.4 - # => [METADATA, pad(12)] + swapdw dropw dropw + # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] end #! Returns the serial number of the specified input note. @@ -1244,11 +1253,12 @@ end #! Returns the metadata of the output note with the specified index. #! #! Inputs: [note_index, pad(15)] -#! Outputs: [METADATA, pad(12)] +#! Outputs: [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] #! #! Where: #! - note_index is the index of the output note whose metadata should be returned. -#! - METADATA is the metadata of the output note. +#! - METADATA_HEADER is the metadata header of the specified output note. +#! - NOTE_ATTACHMENT is the attachment of the specified output note. #! #! Panics if: #! - the note index is greater or equal to the total number of output notes. @@ -1258,18 +1268,26 @@ pub proc output_note_get_metadata # assert that the provided note index is less than the total number of output notes exec.output_note::assert_note_index_in_bounds # => [note_index, pad(15)] - + # get the note data pointer by the provided index exec.memory::get_output_note_ptr # => [note_ptr, pad(15)] + # make stack truncation at the end of the procedure easier + push.0 swap + # => [note_ptr, pad(16)] + # get the metadata - exec.memory::get_output_note_metadata - # => [METADATA, pad(15)] + dup exec.memory::get_output_note_metadata_header + # => [METADATA_HEADER, note_ptr, pad(16)] + + # get the attachment + movup.4 exec.memory::get_output_note_attachment + # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(16)] # truncate the stack - swapw drop drop drop movdn.4 - # => [METADATA, pad(12)] + swapdw dropw dropw + # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] end # TRANSACTION diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm index 5d0ce1d1b0..b1ccb974e3 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm @@ -224,11 +224,12 @@ const INPUT_NOTE_SCRIPT_ROOT_OFFSET=8 const INPUT_NOTE_INPUTS_COMMITMENT_OFFSET=12 const INPUT_NOTE_ASSETS_COMMITMENT_OFFSET=16 const INPUT_NOTE_RECIPIENT_OFFSET=20 -const INPUT_NOTE_METADATA_OFFSET=24 -const INPUT_NOTE_ARGS_OFFSET=28 -const INPUT_NOTE_NUM_INPUTS_OFFSET=32 -const INPUT_NOTE_NUM_ASSETS_OFFSET=36 -const INPUT_NOTE_ASSETS_OFFSET=40 +const INPUT_NOTE_METADATA_HEADER_OFFSET=24 +const INPUT_NOTE_ATTACHMENT_OFFSET=28 +const INPUT_NOTE_ARGS_OFFSET=32 +const INPUT_NOTE_NUM_INPUTS_OFFSET=36 +const INPUT_NOTE_NUM_ASSETS_OFFSET=40 +const INPUT_NOTE_ASSETS_OFFSET=44 # OUTPUT NOTES # ------------------------------------------------------------------------------------------------- @@ -238,12 +239,13 @@ const OUTPUT_NOTE_SECTION_OFFSET=16777216 # The offsets at which data of an output note is stored relative to the start of its data segment. const OUTPUT_NOTE_ID_OFFSET=0 -const OUTPUT_NOTE_METADATA_OFFSET=4 -const OUTPUT_NOTE_RECIPIENT_OFFSET=8 -const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=12 -const OUTPUT_NOTE_NUM_ASSETS_OFFSET=16 -const OUTPUT_NOTE_DIRTY_FLAG_OFFSET=17 -const OUTPUT_NOTE_ASSETS_OFFSET=20 +const OUTPUT_NOTE_METADATA_HEADER_OFFSET=4 +const OUTPUT_NOTE_ATTACHMENT_OFFSET=8 +const OUTPUT_NOTE_RECIPIENT_OFFSET=12 +const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=16 +const OUTPUT_NOTE_NUM_ASSETS_OFFSET=20 +const OUTPUT_NOTE_DIRTY_FLAG_OFFSET=21 +const OUTPUT_NOTE_ASSETS_OFFSET=24 # LINK MAP MEMORY # ------------------------------------------------------------------------------------------------- @@ -1607,27 +1609,54 @@ end #! Returns the metadata of an input note located at the specified memory address. #! #! Inputs: [note_ptr] -#! Outputs: [METADATA] +#! Outputs: [NOTE_METADATA_HEADER] #! #! Where: #! - note_ptr is the memory address at which the input note data begins. -#! - METADATA is the metadata of the input note. -pub proc get_input_note_metadata +#! - NOTE_METADATA_HEADER is the metadata header of the input note. +pub proc get_input_note_metadata_header padw - movup.4 add.INPUT_NOTE_METADATA_OFFSET + movup.4 add.INPUT_NOTE_METADATA_HEADER_OFFSET mem_loadw_be end #! Sets the metadata for an input note located at the specified memory address. #! -#! Inputs: [note_ptr, NOTE_METADATA] -#! Outputs: [NOTE_METADATA] +#! Inputs: [note_ptr, NOTE_METADATA_HEADER] +#! Outputs: [NOTE_METADATA_HEADER] #! #! Where: #! - note_ptr is the memory address at which the input note data begins. -#! - NOTE_METADATA is the metadata of the input note. -pub proc set_input_note_metadata - add.INPUT_NOTE_METADATA_OFFSET +#! - NOTE_METADATA_HEADER is the metadata header of the input note. +pub proc set_input_note_metadata_header + add.INPUT_NOTE_METADATA_HEADER_OFFSET + mem_storew_be +end + +#! Returns the attachment of an input note located at the specified memory address. +#! +#! Inputs: [note_ptr] +#! Outputs: [NOTE_ATTACHMENT] +#! +#! Where: +#! - note_ptr is the memory address at which the input note data begins. +#! - NOTE_ATTACHMENT is the attachment of the input note. +pub proc get_input_note_attachment + padw + movup.4 add.INPUT_NOTE_ATTACHMENT_OFFSET + mem_loadw_be +end + +#! Sets the attachment for an input note located at the specified memory address. +#! +#! Inputs: [note_ptr, NOTE_ATTACHMENT] +#! Outputs: [NOTE_ATTACHMENT] +#! +#! Where: +#! - note_ptr is the memory address at which the input note data begins. +#! - NOTE_ATTACHMENT is the attachment of the input note. +pub proc set_input_note_attachment + add.INPUT_NOTE_ATTACHMENT_OFFSET mem_storew_be end @@ -1778,36 +1807,6 @@ pub proc get_input_note_serial_num mem_loadw_be end -#! Returns the sender for the input note located at the specified memory address. -#! -#! Inputs: [note_ptr] -#! Outputs: [sender_id_prefix, sender_id_suffix] -#! -#! Where: -#! - note_ptr is the memory address at which the input note data begins. -#! - sender is the sender for the input note. -pub proc get_input_note_sender - padw - movup.4 add.INPUT_NOTE_METADATA_OFFSET - mem_loadw_be - # => [aux, merged_tag_hint_payload, merged_sender_id_type_hint_tag, sender_id_prefix] - - drop drop - # => [merged_sender_id_type_hint_tag, sender_id_prefix] - - # extract suffix of sender from merged layout, which means clearing the least significant byte - u32split swap - # => [merged_lo, merged_hi, sender_id_prefix] - - # clear least significant byte - u32and.0xffffff00 swap - # => [sender_id_suffix_hi, sender_id_suffix_lo, sender_id_prefix] - - # reassemble the suffix by multiplying the high part with 2^32 and adding the lo part - mul.0x0100000000 add swap - # => [sender_id_prefix, sender_id_suffix] -end - # OUTPUT NOTES # ------------------------------------------------------------------------------------------------- @@ -1865,31 +1864,60 @@ end #! Returns the output note's metadata. #! #! Inputs: [note_ptr] -#! Outputs: [METADATA] +#! Outputs: [METADATA_HEADER] #! #! Where: -#! - METADATA is the note metadata. +#! - METADATA_HEADER is the note metadata header. #! - note_ptr is the memory address at which the output note data begins. -pub proc get_output_note_metadata +pub proc get_output_note_metadata_header padw # => [0, 0, 0, 0, note_ptr] - movup.4 add.OUTPUT_NOTE_METADATA_OFFSET + movup.4 add.OUTPUT_NOTE_METADATA_HEADER_OFFSET # => [(note_ptr + offset), 0, 0, 0, 0] mem_loadw_be - # => [METADATA] + # => [METADATA_HEADER] +end + +#! Sets the output note's metadata header. +#! +#! Inputs: [note_ptr, METADATA_HEADER] +#! Outputs: [METADATA_HEADER] +#! +#! Where: +#! - METADATA_HEADER is the note metadata header. +#! - note_ptr is the memory address at which the output note data begins. +pub proc set_output_note_metadata_header + add.OUTPUT_NOTE_METADATA_HEADER_OFFSET + mem_storew_be +end + +#! Returns the output note's attachment. +#! +#! Inputs: [note_ptr] +#! Outputs: [ATTACHMENT] +#! +#! Where: +#! - ATTACHMENT is the note attachment. +#! - note_ptr is the memory address at which the output note data begins. +pub proc get_output_note_attachment + padw + movup.4 add.OUTPUT_NOTE_ATTACHMENT_OFFSET + mem_loadw_be + # => [ATTACHMENT] end -#! Sets the output note's metadata. +#! Sets the output note's attachment. #! -#! Inputs: [note_ptr, METADATA] -#! Outputs: [METADATA] +#! Inputs: [note_ptr, ATTACHMENT] +#! Outputs: [] #! #! Where: -#! - METADATA is the note metadata. +#! - ATTACHMENT is the note attachment. #! - note_ptr is the memory address at which the output note data begins. -pub proc set_output_note_metadata - add.OUTPUT_NOTE_METADATA_OFFSET +pub proc set_output_note_attachment + add.OUTPUT_NOTE_ATTACHMENT_OFFSET mem_storew_be + dropw end #! Returns the number of assets in the output note. diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm index 5a3faf962d..baf51d9a5f 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm @@ -209,53 +209,63 @@ end pub proc compute_output_notes_commitment # get the number of output notes from memory exec.memory::get_num_output_notes - # => [num_notes, ...] + # => [num_notes] - # calculate the address at which we should stop looping - exec.memory::get_output_note_ptr - # => [end_ptr, ...] - - # compute pointer for first address - push.0 exec.memory::get_output_note_ptr - # => [first_note_ptr, end_ptr, ...] + # initialize the output note index at which to start the commitment computation + push.0 + # => [current_index = 0, num_notes] # prepare stack for hashing padw padw padw - # => [PERM, PERM, PERM, first_note_ptr, end_ptr, ...] + # => [PERM, PERM, PERM, current_index, num_notes] - # check if the number of output notes is greater then 0. Conditional for the while loop. - dup.13 dup.13 neq - # => [PERM, PERM, PERM, first_note_ptr, end_ptr, ...] + # starting looping if num_notes != 0 + dup.13 neq.0 + # => [should_loop, PERM, PERM, PERM, current_index, num_notes] # loop and hash output notes while.true + dup.12 exec.memory::get_output_note_ptr + # => [current_note_ptr, PERM, PERM, PERM, current_index, num_notes] + # compute and save output note ID to memory (this also computes the note's asset commitment) - dup.12 exec.compute_output_note_id - # => [NOTE_ID, PERM, PERM, PERM, note_ptr, end_ptr, ...] + dup exec.compute_output_note_id + # => [NOTE_ID, current_note_ptr, PERM, PERM, PERM, current_index, num_notes] + + dup.4 exec.memory::get_output_note_metadata_header + # => [NOTE_METADATA_HEADER, NOTE_ID, current_note_ptr, PERM, PERM, PERM, current_index, num_notes] + + movup.8 exec.memory::get_output_note_attachment + # => [NOTE_ATTACHMENT, NOTE_METADATA_HEADER, NOTE_ID, current_note_ptr, PERM, PERM, PERM, current_index, num_notes] + + # compute hash(NOTE_METADATA_HEADER || NOTE_ATTACHMENT) + exec.rpo256::merge + # => [NOTE_METADATA_COMMITMENT, NOTE_ID, current_note_ptr, PERM, PERM, PERM, current_index, num_notes] - # drop output note ID from stack (it will be read from memory by the next instruction) - dropw - # => [PERM, PERM, PERM, note_ptr, end_ptr, ...] + # replace rate words with note ID and metadata commitment + swapdw dropw dropw + # => [NOTE_METADATA_COMMITMENT, NOTE_ID, PERM, current_index, num_notes] - # permute over (note_id, note_metadata) - mem_stream hperm - # => [PERM, PERM, PERM, note_ptr + 8, end_ptr, ...] + # permute over (note_id, note_metadata_commitment) + exec.rpo256::permute + # => [PERM, PERM, PERM, current_index, num_notes] - # increment output note pointer - movup.12 add.OUTPUT_NOTE_HASHING_MEM_DIFF - # => [note_ptr + 2048, PERM, PERM, PERM, end_ptr, ...] + # increment current_index + movup.12 add.1 movdn.12 + # => [PERM, PERM, PERM, current_index + 1, num_notes] - # check if we should loop again - dup movdn.13 dup.14 neq - # => [should_loop, PERM, PERM, PERM, note_ptr + 512, end_ptr, ...] + # continue looping if current_index != num_notes + dup.13 dup.13 neq + # => [should_loop, PERM, PERM, PERM, current_index + 1, num_notes] end + # => [PERM, PERM, PERM, current_index + 1, num_notes] # extract digest exec.rpo256::squeeze_digest - # => [OUTPUT_NOTES_COMMITMENT, end_ptr, end_ptr, ...] + # => [OUTPUT_NOTES_COMMITMENT, current_index + 1, num_notes] # drop accessory variables from stack movup.4 drop movup.4 drop - # => [OUTPUT_NOTES_COMMITMENT, ...] + # => [OUTPUT_NOTES_COMMITMENT] end diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index 7e4bb51916..f122f079b7 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -13,6 +13,16 @@ const PUBLIC_NOTE=1 # 0b01 const PRIVATE_NOTE=2 # 0b10 const ENCRYPTED_NOTE=3 # 0b11 +# Constants for note attachment content types +const ATTACHMENT_CONTENT_TYPE_NONE=0 +const ATTACHMENT_CONTENT_TYPE_WORD=1 +const ATTACHMENT_CONTENT_TYPE_ARRAY=2 + +# The default value of the felt at index 3 in the note metadata header when a new note is created. +# All zeros sets the attachment content type to None and the user-defined attachment type to +# "untyped". +const ATTACHMENT_DEFAULT_TYPE_INFO=0 + # ERRORS # ================================================================================================= @@ -46,6 +56,7 @@ const NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") # OUTPUT NOTE PROCEDURES # ================================================================================================= +# TODO(note_attachment): Remove aux and execution hint parameters. #! Creates a new note and returns the index of the note. #! #! Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] @@ -53,10 +64,8 @@ const NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") #! #! Where: #! - tag is the note tag which can be used by the recipient(s) to identify notes intended for them. -#! - aux is the arbitrary user-defined value. #! - note_type is the type of the note, which defines how the note is to be stored (e.g., on-chain #! or off-chain). -#! - execution_hint is the hint which specifies when a note is ready to be consumed. #! - RECIPIENT defines spend conditions for the note. #! - note_idx is the index of the created note. #! @@ -65,29 +74,47 @@ const NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") #! - the note tag is not a u32. #! - the number of output notes exceeds the maximum limit of 1024. pub proc create + # temporary: drop aux + swap drop + # => [tag, note_type, execution_hint, RECIPIENT] + + # temporary: drop execution_hint + movup.2 drop + # => [tag, note_type, RECIPIENT] + emit.NOTE_BEFORE_CREATED_EVENT + # => [tag, note_type, RECIPIENT] - exec.build_metadata - # => [NOTE_METADATA, RECIPIENT] + exec.build_metadata_header + # => [NOTE_METADATA_HEADER, RECIPIENT] # get the index for the next note to be created and increment counter exec.increment_num_output_notes dup movdn.9 - # => [note_idx, NOTE_METADATA, RECIPIENT, note_idx] + # => [note_idx, NOTE_METADATA_HEADER, RECIPIENT, note_idx] # get a pointer to the memory address at which the note will be stored exec.memory::get_output_note_ptr - # => [note_ptr, NOTE_METADATA, RECIPIENT, note_idx] + # => [note_ptr, NOTE_METADATA_HEADER, RECIPIENT, note_idx] movdn.4 - # => [NOTE_METADATA, note_ptr, RECIPIENT, note_idx] + # => [NOTE_METADATA_HEADER, note_ptr, RECIPIENT, note_idx] # emit event to signal that a new note is created emit.NOTE_AFTER_CREATED_EVENT # set the metadata for the output note dup.4 - # => [note_ptr, NOTE_METADATA, note_ptr, RECIPIENT, note_idx] - exec.memory::set_output_note_metadata dropw + # => [note_ptr, NOTE_METADATA_HEADER, note_ptr, RECIPIENT, note_idx] + + exec.memory::set_output_note_metadata_header dropw + # => [note_ptr, RECIPIENT, note_idx] + + # set the attachment value of a new note to an empty word + # note that the attachment content type is set to None by build_metadata_header + padw dup.4 + # => [note_ptr, EMPTY_WORD, note_ptr, RECIPIENT, note_idx] + + exec.memory::set_output_note_attachment # => [note_ptr, RECIPIENT, note_idx] # set the RECIPIENT for the output note @@ -229,99 +256,54 @@ end # HELPER PROCEDURES # ================================================================================================= -#! Builds the stack into the NOTE_METADATA word, encoding the note type and execution hint into a -#! single element. +#! Builds the provided inputs into the NOTE_METADATA_HEADER word. +#! +#! - The sender ID is set to the native account's ID. +#! - The attachment type is set to 0 (meaning untyped by convention) and the attachment content +#! type is set to None. +#! #! Note that this procedure is only exported so it can be tested. It should not be called from #! non-test code. #! -#! Inputs: [tag, aux, note_type, execution_hint] -#! Outputs: [NOTE_METADATA] +#! Inputs: [tag, note_type] +#! Outputs: [NOTE_METADATA_HEADER] #! #! Where: #! - tag is the note tag which can be used by the recipient(s) to identify notes intended for them. -#! - aux is the arbitrary user-defined value. #! - note_type is the type of the note, which defines how the note is to be stored (e.g., on-chain #! or off-chain). -#! - execution_hint is the hint which specifies when a note is ready to be consumed. -#! - NOTE_METADATA is the metadata associated with a note. -pub proc build_metadata - # Validate the note type. +#! - NOTE_METADATA_HEADER is the metadata associated with a note. +pub proc build_metadata_header + # Validate that note type is private or public. # -------------------------------------------------------------------------------------------- - # NOTE: encrypted notes are currently unsupported - dup.2 eq.PRIVATE_NOTE dup.3 eq.PUBLIC_NOTE or assert.err=ERR_NOTE_INVALID_TYPE - # => [tag, aux, note_type, execution_hint] - - u32assert.err=ERR_NOTE_TAG_MUST_BE_U32 - # => [tag, aux, note_type, execution_hint] - - # Split execution hint into its tag and payload parts as they are encoded in separate elements - # of the metadata. - # -------------------------------------------------------------------------------------------- + dup.1 eq.PRIVATE_NOTE dup.2 eq.PUBLIC_NOTE or assert.err=ERR_NOTE_INVALID_TYPE + # => [tag, note_type] - # the execution_hint is laid out like this: [26 zero bits | payload (32 bits) | tag (6 bits)] - movup.3 - # => [execution_hint, tag, aux, note_type] - dup u32split drop - # => [execution_hint_lo, execution_hint, tag, aux, note_type] - - # mask out the lower 6 execution hint tag bits. - u32and.0x3f - # => [execution_hint_tag, execution_hint, tag, aux, note_type] - - # compute the payload by subtracting the tag value so the lower 6 bits are zero - # note that this results in the following layout: [26 zero bits | payload (32 bits) | 6 zero bits] - swap - # => [execution_hint, execution_hint_tag, tag, aux, note_type] - dup.1 - # => [execution_hint_tag, execution_hint, execution_hint_tag, tag, aux, note_type] - sub - # => [execution_hint_payload, execution_hint_tag, tag, aux, note_type] - - # Merge execution hint payload and note tag. + # Validate the note tag fits into a u32. # -------------------------------------------------------------------------------------------- - # we need to move the payload to the upper 32 bits of the felt - # we only need to shift by 26 bits because the payload is already shifted left by 6 bits - # we shift the payload by multiplying with 2^26 - # this results in the lower 32 bits being zero which is where the note tag will be added - mul.0x04000000 - # => [execution_hint_payload, execution_hint_tag, tag, aux, note_type] - - # add the tag to the payload to produce the merged value - movup.2 add - # => [note_tag_hint_payload, execution_hint_tag, aux, note_type] + u32assert.err=ERR_NOTE_TAG_MUST_BE_U32 + # => [tag, note_type] - # Merge sender_id_suffix, note_type and execution_hint_tag. + # Merge note type and sender ID suffix. # -------------------------------------------------------------------------------------------- - exec.account::get_id - # => [sender_id_prefix, sender_id_suffix, note_tag_hint_payload, execution_hint_tag, aux, note_type] - - movup.5 - # => [note_type, sender_id_prefix, sender_id_suffix, note_tag_hint_payload, execution_hint_tag, aux] - # multiply by 2^6 to shift the two note_type bits left by 6 bits. - mul.0x40 - # => [shifted_note_type, sender_id_prefix, sender_id_suffix, note_tag_hint_payload, execution_hint_tag, aux] - - # merge execution_hint_tag into the note_type - # this produces an 8-bit value with the layout: [note_type (2 bits) | execution_hint_tag (6 bits)] - movup.4 add - # => [merged_note_type_execution_hint_tag, sender_id_prefix, sender_id_suffix, note_tag_hint_payload, aux] + exec.account::get_id swap + # => [sender_id_suffix, sender_id_prefix, tag, note_type] - # merge sender_id_suffix into this value - movup.2 add - # => [sender_id_suffix_type_and_hint_tag, sender_id_prefix, note_tag_hint_payload, aux] + # the lower bits of an account ID suffix are guaranteed to be zero, so we can safely use that + # space to encode the note type + movup.3 add swap + # => [sender_id_prefix, sender_id_suffix_and_note_type, tag] - # Rearrange elements to produce the final note metadata layout. + # Build metadata header. # -------------------------------------------------------------------------------------------- - swap movdn.3 - # => [sender_id_suffix_type_and_hint_tag, note_tag_hint_payload, aux, sender_id_prefix] - swap - # => [note_tag_hint_payload, sender_id_suffix_type_and_hint_tag, aux, sender_id_prefix] movup.2 - # => [NOTE_METADATA = [aux, note_tag_hint_payload, sender_id_suffix_type_and_hint_tag, sender_id_prefix]] + push.ATTACHMENT_DEFAULT_TYPE_INFO + # => [attachment_type_info, tag, sender_id_prefix, sender_id_suffix_and_note_type] + # => [NOTE_METADATA_HEADER] end #! Increments the number of output notes by one. Returns the index of the next note to be created. diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm index 615240d824..2da165d876 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm @@ -519,7 +519,7 @@ end #! Advice stack: [] #! #! Where: -#! - NOTE_COMMITMENT is the input note's commitment computed as `hash(NOTE_ID || NOTE_METADATA)`. +#! - NOTE_COMMITMENT is the input note's commitment computed as `hash(NOTE_ID || NOTE_METADATA_COMMITMENT)`. #! - block_num is the leaf position in the MMR chain of the block which created the input note. #! - BLOCK_SUB_COMMITMENT is the sub_commitment of the block which created the input note. #! - NOTE_ROOT is the merkle root of the notes tree containing the input note. @@ -614,33 +614,39 @@ proc process_input_note_details # => [NULLIFIER] end -#! Copies the note's metadata and args from the advice stack to memory. +#! Copies the note's metadata header and attachment as well as note args from the advice stack to +#! memory. #! #! Notes: #! - The note's ARGS are not authenticated, these are optional arguments the user can provide when #! consuming the note. #! - The note's metadata is authenticated, so the data is returned in the stack. The value is used -#! to compute the NOTE_COMMITMENT as `hash(NOTE_ID || NOTE_METADATA)`, which is the leaf value of the -#! note's tree in the contained in the block header. The NOTE_COMMITMENT is either verified by this -#! kernel, or delayed to be verified by another kernel (e.g. block or batch kernels). +#! to compute the NOTE_COMMITMENT as `hash(NOTE_ID || NOTE_METADATA_COMMITMENT)`, where +#! `NOTE_METADATA_COMMITMENT` is `hash(NOTE_METADATA_HEADER || NOTE_METADATA_ATTACHMENT)`. The +#! NOTE_COMMITMENT is the leaf value of the block note tree contained in the block header and is +#! either verified by this kernel, or delayed to be verified by another kernel (e.g. batch or +#! block kernels). #! #! Inputs: #! Operand stack: [note_ptr] -#! Advice stack: [NOTE_ARGS, NOTE_METADATA] +#! Advice stack: [NOTE_ARGS, NOTE_METADATA_HEADER, NOTE_ATTACHMENT] #! Outputs: -#! Operand stack: [NOTE_METADATA] +#! Operand stack: [NOTE_ATTACHMENT, NOTE_METADATA_HEADER] #! Advice stack: [] #! #! Where: #! - note_ptr is the memory location for the input note. #! - NOTE_ARGS are the user arguments passed to the note. -#! - NOTE_METADATA is the note's metadata. +#! - NOTE_METADATA_HEADER is the note's metadata. proc process_note_args_and_metadata padw adv_loadw dup.4 exec.memory::set_input_note_args dropw # => [note_ptr] - padw adv_loadw movup.4 exec.memory::set_input_note_metadata - # => [NOTE_METADATA] + padw adv_loadw dup.4 exec.memory::set_input_note_metadata_header + # => [NOTE_METADATA_HEADER, note_ptr] + + padw adv_loadw movup.8 exec.memory::set_input_note_attachment + # => [NOTE_ATTACHMENT, NOTE_METADATA_HEADER] end #! Checks that the number of note inputs is within limit and stores it to memory. @@ -849,8 +855,9 @@ end #! SCRIPT_ROOT, #! INPUTS_COMMITMENT, #! ASSETS_COMMITMENT, -#! ARGS, -#! NOTE_METADATA, +#! NOTE_ARGS, +#! NOTE_METADATA_HEADER, +#! NOTE_ATTACHMENT, #! assets_count, #! ASSET_0, ..., ASSET_N, #! is_authenticated, @@ -872,8 +879,9 @@ end #! - SCRIPT_ROOT is the note's script root. #! - INPUTS_COMMITMENT is the sequential hash of the padded note's inputs. #! - ASSETS_COMMITMENT is the sequential hash of the padded note's assets. -#! - NOTE_METADATA is the note's metadata. -#! - ARGS is the user arguments passed to the note. +#! - NOTE_METADATA_HEADER is the note's metadata header. +#! - NOTE_ATTACHMENT is the note's attachment. +#! - NOTE_ARGS are the user arguments passed to the note. #! - assets_count is the note's assets count. #! - ASSET_0, ..., ASSET_N are the padded note's assets. #! - is_authenticated is the boolean indicating if the note contains an authentication proof. @@ -902,38 +910,42 @@ proc process_input_note # => [note_ptr, NULLIFIER, HASHER_CAPACITY] dup exec.process_note_args_and_metadata - # => [NOTE_METADATA, note_ptr, NULLIFIER, HASHER_CAPACITY] + # => [NOTE_ATTACHMENT, NOTE_METADATA_HEADER, note_ptr, NULLIFIER, HASHER_CAPACITY] + + # compute hash(NOTE_METADATA_HEADER || NOTE_ATTACHMENT) + exec.rpo256::merge + # => [NOTE_METADATA_COMMITMENT, note_ptr, NULLIFIER, HASHER_CAPACITY] movup.4 - # => [note_ptr, NOTE_METADATA, NULLIFIER, HASHER_CAPACITY] + # => [note_ptr, NOTE_METADATA_COMMITMENT, NULLIFIER, HASHER_CAPACITY] # note inputs len # --------------------------------------------------------------------------------------------- exec.process_note_inputs_length - # => [note_ptr, NOTE_METADATA, NULLIFIER, HASHER_CAPACITY] + # => [note_ptr, NOTE_METADATA_COMMITMENT, NULLIFIER, HASHER_CAPACITY] # note assets # --------------------------------------------------------------------------------------------- dup exec.process_note_assets dup exec.add_input_note_assets_to_vault - # => [note_ptr, NOTE_METADATA, NULLIFIER, HASHER_CAPACITY] + # => [note_ptr, NOTE_METADATA_COMMITMENT, NULLIFIER, HASHER_CAPACITY] # note id # --------------------------------------------------------------------------------------------- dup exec.compute_input_note_id - # => [NOTE_ID, note_ptr, NOTE_METADATA, NULLIFIER, HASHER_CAPACITY] + # => [NOTE_ID, note_ptr, NOTE_METADATA_COMMITMENT, NULLIFIER, HASHER_CAPACITY] # save note id to memory movup.4 exec.memory::set_input_note_id - # => [NOTE_ID, NOTE_METADATA, NULLIFIER, HASHER_CAPACITY] + # => [NOTE_ID, NOTE_METADATA_COMMITMENT, NULLIFIER, HASHER_CAPACITY] # note authentication # --------------------------------------------------------------------------------------------- - # NOTE_COMMITMENT: `hash(NOTE_ID || NOTE_METADATA)` + # NOTE_COMMITMENT: `hash(NOTE_ID || NOTE_METADATA_COMMITMENT)` swapw hmerge # => [NOTE_COMMITMENT, NULLIFIER, HASHER_CAPACITY] diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index 9cda06a621..77865a19bc 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -131,10 +131,11 @@ end #! Returns the metadata of the active note. #! #! Inputs: [] -#! Outputs: [METADATA] +#! Outputs: [NOTE_ATTACHMENT, METADATA_HEADER] #! #! Where: -#! - METADATA is the metadata of the active note. +#! - METADATA_HEADER is the metadata header of the specified input note. +#! - NOTE_ATTACHMENT is the attachment of the specified input note. #! #! Panics if: #! - no note is currently active. @@ -153,11 +154,11 @@ pub proc get_metadata # => [offset, is_active_note = 1, pad(14)] syscall.exec_kernel_proc - # => [METADATA, pad(12)] + # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] # clean the stack - swapdw dropw dropw swapw dropw - # => [METADATA] + swapdw dropw dropw + # => [NOTE_ATTACHMENT, METADATA_HEADER] end #! Returns the sender of the active note. @@ -173,26 +174,12 @@ end #! #! Invocation: exec pub proc get_sender - # pad the stack - padw padw padw push.0.0 - # => [pad(14)] - - # push the flag indicating that we want to request metadata from the active note - push.1 - # => [is_active_note = 1, pad(14)] - - exec.kernel_proc_offsets::input_note_get_metadata_offset - # => [offset, is_active_note = 1, pad(14)] + # get metadata and drop attachment + exec.get_metadata dropw + # => [METADATA_HEADER] - syscall.exec_kernel_proc - # => [METADATA, pad(12)] - - # extract the sender ID from the metadata word + # extract the sender ID from the metadata header exec.note::extract_sender_from_metadata - # => [sender_id_prefix, sender_id_suffix, pad(12)] - - # clean the stack - swapw dropw swapw dropw movdn.5 movdn.5 dropw # => [sender_id_prefix, sender_id_suffix] end diff --git a/crates/miden-protocol/asm/protocol/input_note.masm b/crates/miden-protocol/asm/protocol/input_note.masm index 9a88dc7636..a08d5a5dd2 100644 --- a/crates/miden-protocol/asm/protocol/input_note.masm +++ b/crates/miden-protocol/asm/protocol/input_note.masm @@ -122,11 +122,12 @@ end #! Returns the metadata of the input note with the specified index. #! #! Inputs: [note_index] -#! Outputs: [METADATA] +#! Outputs: [NOTE_ATTACHMENT, METADATA_HEADER] #! #! Where: #! - note_index is the index of the input note whose metadata should be returned. -#! - METADATA is the metadata of the input note. +#! - METADATA_HEADER is the metadata header of the specified input note. +#! - NOTE_ATTACHMENT is the attachment of the specified input note. #! #! Panics if: #! - the note index is greater or equal to the total number of input notes. @@ -150,11 +151,11 @@ pub proc get_metadata # => [offset, is_active_note = 0, note_index, pad(13)] syscall.exec_kernel_proc - # => [METADATA, pad(12)] + # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] # clean the stack - swapdw dropw dropw swapw dropw - # => [METADATA] + swapdw dropw dropw + # => [NOTE_ATTACHMENT, METADATA_HEADER] end #! Returns the sender of the input note with the specified index. @@ -171,31 +172,12 @@ end #! #! Invocation: exec pub proc get_sender - # start padding the stack - push.0 swap - # => [note_index, 0] - - # push the flag indicating that we want to request metadata from the note with the specified - # index - push.0 - # => [is_active_note = 0, note_index, 0] - - exec.kernel_proc_offsets::input_note_get_metadata_offset - # => [offset, is_active_note = 0, note_index, 0] - - # pad the stack - padw swapw padw padw swapdw - # => [offset, is_active_note = 0, note_index, pad(13)] - - syscall.exec_kernel_proc - # => [METADATA, pad(12)] + # get metadata and drop attachment + exec.get_metadata dropw + # => [METADATA_HEADER] - # extract the sender ID from the metadata word + # extract the sender ID from the metadata header exec.note::extract_sender_from_metadata - # => [sender_id_prefix, sender_id_suffix, pad(12)] - - # clean the stack - swapw dropw swapw dropw movdn.5 movdn.5 dropw # => [sender_id_prefix, sender_id_suffix] end diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index 85730c9528..13cb6f6ef3 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -191,21 +191,21 @@ pub proc build_recipient_hash # [RECIPIENT] end -#! Extracts the sender ID from the provided metadata word. +#! Extracts the sender ID from the provided metadata header. #! -#! Inputs: [METADATA] +#! Inputs: [METADATA_HEADER] #! Outputs: [sender_id_prefix, sender_id_suffix] #! #! Where: -#! - METADATA is the metadata of some note. +#! - METADATA_HEADER is the metadata of a note. #! - sender_{prefix,suffix} are the prefix and suffix felts of the sender ID of the note which #! metadata was provided. pub proc extract_sender_from_metadata - # => [aux, merged_tag_hint_payload, merged_sender_id_type_hint_tag, sender_id_prefix] + # => [attachment_type_info, tag, sender_id_prefix, sender_id_suffix_and_note_type] - # drop aux felt and the felt containing tag, execution hint and payload - drop drop - # => [merged_sender_id_type_hint_tag, sender_id_prefix] + # drop attachment type info and tag + drop drop swap + # => [sender_id_suffix_and_note_type, sender_id_prefix] # extract suffix of sender from merged layout, which means clearing the least significant byte exec.account_id::shape_suffix diff --git a/crates/miden-protocol/asm/protocol/output_note.masm b/crates/miden-protocol/asm/protocol/output_note.masm index 6b1093d0cb..36ec079f43 100644 --- a/crates/miden-protocol/asm/protocol/output_note.masm +++ b/crates/miden-protocol/asm/protocol/output_note.masm @@ -175,11 +175,12 @@ end #! Returns the metadata of the output note with the specified index. #! #! Inputs: [note_index] -#! Outputs: [METADATA] +#! Outputs: [NOTE_ATTACHMENT, METADATA_HEADER] #! #! Where: #! - note_index is the index of the output note whose metadata should be returned. -#! - METADATA is the metadata of the output note. +#! - METADATA_HEADER is the metadata header of the specified output note. +#! - NOTE_ATTACHMENT is the attachment of the specified output note. #! #! Panics if: #! - the note index is greater or equal to the total number of output notes. @@ -198,9 +199,9 @@ pub proc get_metadata # => [offset, note_index, pad(14)] syscall.exec_kernel_proc - # => [METADATA, pad(12)] + # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] # clean the stack - swapdw dropw dropw swapw dropw - # => [METADATA] + swapdw dropw dropw + # => [NOTE_ATTACHMENT, METADATA_HEADER] end diff --git a/crates/miden-protocol/src/batch/note_tree.rs b/crates/miden-protocol/src/batch/note_tree.rs index de473ee0f6..7897856389 100644 --- a/crates/miden-protocol/src/batch/note_tree.rs +++ b/crates/miden-protocol/src/batch/note_tree.rs @@ -8,7 +8,7 @@ use crate::{BATCH_NOTE_TREE_DEPTH, EMPTY_WORD, Word}; /// Wrapper over [SimpleSmt] for batch note tree. /// -/// Value of each leaf is computed as: `hash(note_id || note_metadata)`. +/// Value of each leaf is computed as: `hash(note_id || note_metadata_commitment)`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct BatchNoteTree(SimpleSmt); diff --git a/crates/miden-protocol/src/block/block_body.rs b/crates/miden-protocol/src/block/block_body.rs index 8392a5ba7f..53b86741cb 100644 --- a/crates/miden-protocol/src/block/block_body.rs +++ b/crates/miden-protocol/src/block/block_body.rs @@ -114,7 +114,7 @@ impl BlockBody { pub fn compute_block_note_tree(&self) -> BlockNoteTree { let entries = self .output_notes() - .map(|(note_index, note)| (note_index, note.id(), *note.metadata())); + .map(|(note_index, note)| (note_index, note.id(), note.metadata())); // SAFETY: We only construct block bodies that: // - do not contain duplicates diff --git a/crates/miden-protocol/src/block/note_tree.rs b/crates/miden-protocol/src/block/note_tree.rs index 0e919073f1..497aab12ba 100644 --- a/crates/miden-protocol/src/block/note_tree.rs +++ b/crates/miden-protocol/src/block/note_tree.rs @@ -29,18 +29,18 @@ impl BlockNoteTree { /// /// Entry format: (note_index, note_id, note_metadata). /// - /// Value of each leaf is computed as: `hash(note_id || note_metadata)`. + /// Value of each leaf is computed as: `hash(note_id || note_metadata_commitment)`. /// All leaves omitted from the entries list are set to [crate::EMPTY_WORD]. /// /// # Errors /// Returns an error if: /// - The number of entries exceeds the maximum notes tree capacity, that is 2^16. /// - The provided entries contain multiple values for the same key. - pub fn with_entries( - entries: impl IntoIterator, + pub fn with_entries<'metadata>( + entries: impl IntoIterator, ) -> Result { let leaves = entries.into_iter().map(|(index, note_id, metadata)| { - (index.leaf_index_value() as u64, compute_note_commitment(note_id, &metadata)) + (index.leaf_index_value() as u64, compute_note_commitment(note_id, metadata)) }); SimpleSmt::with_leaves(leaves).map(Self) diff --git a/crates/miden-protocol/src/block/proposed_block.rs b/crates/miden-protocol/src/block/proposed_block.rs index 800c4239a3..129f94912b 100644 --- a/crates/miden-protocol/src/block/proposed_block.rs +++ b/crates/miden-protocol/src/block/proposed_block.rs @@ -423,7 +423,7 @@ impl ProposedBlock { "max batches in block and max notes in batches should be enforced", ), note.id(), - *note.metadata(), + note.metadata(), ) }) }); diff --git a/crates/miden-protocol/src/errors/mod.rs b/crates/miden-protocol/src/errors/mod.rs index e9939ba1aa..828f4c2430 100644 --- a/crates/miden-protocol/src/errors/mod.rs +++ b/crates/miden-protocol/src/errors/mod.rs @@ -34,7 +34,14 @@ use crate::address::AddressType; use crate::asset::AssetVaultKey; use crate::batch::BatchId; use crate::block::BlockNumber; -use crate::note::{NoteAssets, NoteExecutionHint, NoteTag, NoteType, Nullifier}; +use crate::note::{ + NoteAssets, + NoteAttachmentArray, + NoteExecutionHint, + NoteTag, + NoteType, + Nullifier, +}; use crate::transaction::{TransactionEventId, TransactionId}; use crate::{ ACCOUNT_UPDATE_MAX_SIZE, @@ -586,6 +593,13 @@ pub enum NoteError { TooManyInputs(usize), #[error("note tag requires a public note but the note is of type {0}")] PublicNoteRequired(NoteType), + #[error( + "note attachment cannot commit to more than {} elements", + NoteAttachmentArray::MAX_NUM_ELEMENTS + )] + NoteAttachmentArraySizeExceeded(usize), + #[error("unknown note attachment content type {0}")] + UnknownNoteAttachmentContentType(u8), #[error("{error_msg}")] Other { error_msg: Box, diff --git a/crates/miden-protocol/src/note/attachment.rs b/crates/miden-protocol/src/note/attachment.rs new file mode 100644 index 0000000000..9f67ce92b5 --- /dev/null +++ b/crates/miden-protocol/src/note/attachment.rs @@ -0,0 +1,491 @@ +use alloc::string::ToString; +use alloc::vec::Vec; + +use crate::crypto::SequentialCommit; +use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; +use crate::{Felt, Hasher, NoteError, Word}; + +// NOTE ATTACHMENT +// ================================================================================================ + +/// The optional attachment for a [`Note`](super::Note). +/// +/// An attachment is a _public_ extension to a note's [`NoteMetadata`](super::NoteMetadata). +/// +/// Example use cases: +/// - Communicate the [`NoteDetails`](super::NoteDetails) of a private note in encrypted form. +/// - In the context of network transactions, encode the ID of the network account that should +/// consume the note. +/// - Communicate details to the receiver of a _private_ note to allow deriving the +/// [`NoteDetails`](super::NoteDetails) of that note. For instance, the payback note of a partial +/// swap note can be private, but the receiver needs to know additional details to fully derive +/// the content of the payback note. They can neither fetch those details from the network, since +/// the note is private, nor is a side-channel available. The note attachment can encode those +/// details. +/// +/// These use cases require different amounts of data, e.g. an account ID takes up just two felts +/// while the details of an encrypted note require many felts. To accommodate these cases, both a +/// computationally efficient [`NoteAttachmentContent::Word`] as well as a more flexible +/// [`NoteAttachmentContent::Array`] variant are available. See the type's docs for more +/// details. +/// +/// Next to the content, a note attachment can optionally specify a [`NoteAttachmentType`]. This +/// allows a note attachment to describe itself. For example, a network account target attachment +/// can be identified by a standardized type. For cases when the attachment type is known from +/// content or typing is otherwise undesirable, [`NoteAttachmentType::untyped`] can be used. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct NoteAttachment { + attachment_type: NoteAttachmentType, + content: NoteAttachmentContent, +} + +impl NoteAttachment { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`NoteAttachment`] from a user-defined type and the provided content. + pub fn new(attachment_type: NoteAttachmentType, content: NoteAttachmentContent) -> Self { + Self { attachment_type, content } + } + + /// Creates a new note attachment with content [`NoteAttachmentContent::Word`] from the provided + /// word. + pub fn new_word(attachment_type: NoteAttachmentType, word: Word) -> Self { + Self::new(attachment_type, NoteAttachmentContent::new_word(word)) + } + + /// Creates a new note attachment with content [`NoteAttachmentContent::Array`] from the + /// provided set of elements. + /// + /// # Errors + /// + /// Returns an error if: + /// - The maximum number of elements exceeds [`NoteAttachmentArray::MAX_NUM_ELEMENTS`]. + pub fn new_array( + attachment_type: NoteAttachmentType, + elements: Vec, + ) -> Result { + NoteAttachmentContent::new_array(elements) + .map(|content| Self::new(attachment_type, content)) + } + + /// Creates a new [`NoteAttachment`] from the provided content and using + /// [`NoteAttachmentType::untyped`]. + pub fn new_untyped(content: NoteAttachmentContent) -> Self { + Self { + attachment_type: NoteAttachmentType::untyped(), + content, + } + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the attachment type. + pub fn attachment_type(&self) -> NoteAttachmentType { + self.attachment_type + } + + /// Returns a reference to the attachment content. + pub fn content(&self) -> &NoteAttachmentContent { + &self.content + } +} + +impl Serializable for NoteAttachment { + fn write_into(&self, target: &mut W) { + self.attachment_type().write_into(target); + self.content().write_into(target); + } +} + +impl Deserializable for NoteAttachment { + fn read_from(source: &mut R) -> Result { + let attachment_type = NoteAttachmentType::read_from(source)?; + let content = NoteAttachmentContent::read_from(source)?; + + Ok(Self::new(attachment_type, content)) + } +} + +/// The content of a [`NoteAttachment`]. +/// +/// If a note attachment is not required, [`NoteAttachmentContent::None`] should be used. +/// +/// When a single [`Word`] has sufficient space, [`NoteAttachmentContent::Word`] should be used, as +/// it does not require any hashing. The word itself is encoded into the +/// [`NoteMetadata`](super::NoteMetadata). +/// +/// If the space of a [`Word`] is insufficient, the more flexible +/// [`NoteAttachmentContent::Array`] variant can be used. It contains a set of field elements +/// where only their sequential hash is encoded into the [`NoteMetadata`](super::NoteMetadata). +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub enum NoteAttachmentContent { + /// Signals the absence of a note attachment. + #[default] + None, + + /// A note attachment consisting of a single [`Word`]. + Word(Word), + + /// A note attachment consisting of the commitment to a set of felts. + Array(NoteAttachmentArray), +} + +impl NoteAttachmentContent { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`NoteAttachmentContent::Word`] containing an empty word. + pub fn empty_word() -> Self { + Self::Word(Word::empty()) + } + + /// Creates a new [`NoteAttachmentContent::Word`] from the provided word. + pub fn new_word(word: Word) -> Self { + Self::Word(word) + } + + /// Creates a new [`NoteAttachmentContent::Array`] from the provided elements. + /// + /// # Errors + /// + /// Returns an error if: + /// - The maximum number of elements exceeds [`NoteAttachmentArray::MAX_NUM_ELEMENTS`]. + pub fn new_array(elements: Vec) -> Result { + NoteAttachmentArray::new(elements).map(Self::from) + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the [`NoteAttachmentContentType`]. + pub fn content_type(&self) -> NoteAttachmentContentType { + match self { + NoteAttachmentContent::None => NoteAttachmentContentType::None, + NoteAttachmentContent::Word(_) => NoteAttachmentContentType::Word, + NoteAttachmentContent::Array(_) => NoteAttachmentContentType::Array, + } + } + + /// Returns the [`NoteAttachmentContent`] encoded to a [`Word`]. + /// + /// See the type-level documentation for more details. + pub fn to_word(&self) -> Word { + match self { + NoteAttachmentContent::None => Word::empty(), + NoteAttachmentContent::Word(word) => *word, + NoteAttachmentContent::Array(attachment_commitment) => { + attachment_commitment.commitment() + }, + } + } +} + +impl Serializable for NoteAttachmentContent { + fn write_into(&self, target: &mut W) { + self.content_type().write_into(target); + + match self { + NoteAttachmentContent::None => (), + NoteAttachmentContent::Word(word) => { + word.write_into(target); + }, + NoteAttachmentContent::Array(attachment_commitment) => { + attachment_commitment.num_elements().write_into(target); + target.write_many(&attachment_commitment.elements); + }, + } + } +} + +impl Deserializable for NoteAttachmentContent { + fn read_from(source: &mut R) -> Result { + let content_type = NoteAttachmentContentType::read_from(source)?; + + match content_type { + NoteAttachmentContentType::None => Ok(NoteAttachmentContent::None), + NoteAttachmentContentType::Word => { + let word = Word::read_from(source)?; + Ok(NoteAttachmentContent::Word(word)) + }, + NoteAttachmentContentType::Array => { + let num_elements = u16::read_from(source)?; + let elements = source.read_many(num_elements as usize)?; + Self::new_array(elements) + .map_err(|err| DeserializationError::InvalidValue(err.to_string())) + }, + } + } +} + +// NOTE ATTACHMENT COMMITMENT +// ================================================================================================ + +/// The type contained in [`NoteAttachmentContent::Array`] that commits to a set of field +/// elements. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NoteAttachmentArray { + elements: Vec, + commitment: Word, +} + +impl NoteAttachmentArray { + // CONSTANTS + // -------------------------------------------------------------------------------------------- + + /// The maximum size of a note attachment that commits to a set of elements. + /// + /// Each element holds roughly 8 bytes of data and so this allows for a maximum of + /// 2048 * 8 = 2^14 = 16384 bytes. + pub const MAX_NUM_ELEMENTS: u16 = 2048; + + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`NoteAttachmentArray`] from the provided elements. + /// + /// # Errors + /// + /// Returns an error if: + /// - The maximum number of elements exceeds [`NoteAttachmentArray::MAX_NUM_ELEMENTS`]. + pub fn new(elements: Vec) -> Result { + if elements.len() > Self::MAX_NUM_ELEMENTS as usize { + return Err(NoteError::NoteAttachmentArraySizeExceeded(elements.len())); + } + + let commitment = Hasher::hash_elements(&elements); + Ok(Self { elements, commitment }) + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns a reference to the elements this note attachment commits to. + pub fn as_slice(&self) -> &[Felt] { + &self.elements + } + + /// Returns the number of elements this note attachment commits to. + pub fn num_elements(&self) -> u16 { + u16::try_from(self.elements.len()).expect("type should enforce that size fits in u16") + } + + /// Returns the commitment over the contained field elements. + pub fn commitment(&self) -> Word { + self.commitment + } +} + +impl SequentialCommit for NoteAttachmentArray { + type Commitment = Word; + + fn to_elements(&self) -> Vec { + self.elements.clone() + } + + fn to_commitment(&self) -> Self::Commitment { + self.commitment + } +} + +impl From for NoteAttachmentContent { + fn from(attachment_commitment: NoteAttachmentArray) -> Self { + NoteAttachmentContent::Array(attachment_commitment) + } +} + +// NOTE ATTACHMENT TYPE +// ================================================================================================ + +/// The user-defined type of a [`NoteAttachment`]. +/// +/// A note attachment type is an arbitrary 32-bit unsigned integer. +/// +/// Value `0` is reserved to signal that an attachment is untyped. That is, no attempt should be +/// made to guess the type of the attachment. Whenever the type of attachment is not standardized or +/// interoperability is unimportant, this untyped value can be used. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NoteAttachmentType(u32); + +impl NoteAttachmentType { + // CONSTANTS + // -------------------------------------------------------------------------------------------- + + /// The reserved value to signal an untyped note attachment. + const UNTYPED: u32 = 0; + + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`NoteAttachmentType`] from a `u32`. + pub const fn new(attachment_type: u32) -> Self { + Self(attachment_type) + } + + /// Returns the [`NoteAttachmentType`] that signals an untyped note attachment. + pub const fn untyped() -> Self { + Self(Self::UNTYPED) + } + + /// Returns `true` if the attachment is untyped, `false` otherwise. + pub const fn is_untyped(&self) -> bool { + self.0 == Self::UNTYPED + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the note attachment type as a u32. + pub const fn as_u32(&self) -> u32 { + self.0 + } +} + +impl Default for NoteAttachmentType { + /// Returns [`NoteAttachmentType::untyped`]. + fn default() -> Self { + Self::untyped() + } +} + +impl core::fmt::Display for NoteAttachmentType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_fmt(format_args!("{}", self.0)) + } +} + +impl Serializable for NoteAttachmentType { + fn write_into(&self, target: &mut W) { + self.as_u32().write_into(target); + } +} + +impl Deserializable for NoteAttachmentType { + fn read_from(source: &mut R) -> Result { + let attachment_type = u32::read_from(source)?; + Ok(Self::new(attachment_type)) + } +} + +// NOTE ATTACHMENT CONTENT TYPE +// ================================================================================================ + +/// The type of [`NoteAttachmentContent`]. +/// +/// See its docs for more details on each type. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[repr(u8)] +pub enum NoteAttachmentContentType { + /// Signals the absence of a note attachment. + #[default] + None = Self::NONE, + + /// A note attachment consisting of a single [`Word`]. + Word = Self::WORD, + + /// A note attachment consisting of the commitment to a set of felts. + Array = Self::ARRAY, +} + +impl NoteAttachmentContentType { + // CONSTANTS + // -------------------------------------------------------------------------------------------- + + const NONE: u8 = 0; + const WORD: u8 = 1; + const ARRAY: u8 = 2; + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the content type as a u8. + pub const fn as_u8(&self) -> u8 { + *self as u8 + } + + /// Returns `true` if the content type is `None`, `false` otherwise. + pub const fn is_none(&self) -> bool { + matches!(self, Self::None) + } + + /// Returns `true` if the content type is `Word`, `false` otherwise. + pub const fn is_word(&self) -> bool { + matches!(self, Self::Word) + } + + /// Returns `true` if the content type is `Array`, `false` otherwise. + pub const fn is_array(&self) -> bool { + matches!(self, Self::Array) + } +} + +impl TryFrom for NoteAttachmentContentType { + type Error = NoteError; + + fn try_from(value: u8) -> Result { + match value { + Self::NONE => Ok(Self::None), + Self::WORD => Ok(Self::Word), + Self::ARRAY => Ok(Self::Array), + _ => Err(NoteError::UnknownNoteAttachmentContentType(value)), + } + } +} + +impl Serializable for NoteAttachmentContentType { + fn write_into(&self, target: &mut W) { + self.as_u8().write_into(target); + } +} + +impl Deserializable for NoteAttachmentContentType { + fn read_from(source: &mut R) -> Result { + let content_type = u8::read_from(source)?; + Self::try_from(content_type) + .map_err(|err| DeserializationError::InvalidValue(err.to_string())) + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use assert_matches::assert_matches; + + use super::*; + + #[rstest::rstest] + #[case::attachment_none(NoteAttachment::default())] + #[case::attachment_word(NoteAttachment::new_word(NoteAttachmentType::new(1), Word::from([3, 4, 5, 6u32])))] + #[case::attachment_array(NoteAttachment::new_array( + NoteAttachmentType::new(u32::MAX), + vec![Felt::new(5), Felt::new(6), Felt::new(7)], + )?)] + #[test] + fn note_attachment_serde(#[case] attachment: NoteAttachment) -> anyhow::Result<()> { + assert_eq!(attachment, NoteAttachment::read_from_bytes(&attachment.to_bytes())?); + Ok(()) + } + + #[test] + fn note_attachment_commitment_fails_on_too_many_elements() -> anyhow::Result<()> { + let too_many_elements = (NoteAttachmentArray::MAX_NUM_ELEMENTS as usize) + 1; + let elements = vec![Felt::from(1u32); too_many_elements]; + let err = NoteAttachmentArray::new(elements).unwrap_err(); + + assert_matches!(err, NoteError::NoteAttachmentArraySizeExceeded(len) => { + len == too_many_elements + }); + + Ok(()) + } + + #[test] + fn note_attachment_content_type_fails_on_unknown_variant() -> anyhow::Result<()> { + let err = NoteAttachmentContentType::try_from(3u8).unwrap_err(); + assert_matches!(err, NoteError::UnknownNoteAttachmentContentType(3u8)); + Ok(()) + } +} diff --git a/crates/miden-protocol/src/note/file.rs b/crates/miden-protocol/src/note/file.rs index c00317cc49..62cd7ba863 100644 --- a/crates/miden-protocol/src/note/file.rs +++ b/crates/miden-protocol/src/note/file.rs @@ -137,7 +137,6 @@ impl Deserializable for NoteFile { mod tests { use alloc::vec::Vec; - use miden_core::Felt; use miden_core::utils::{Deserializable, Serializable}; use crate::Word; @@ -172,13 +171,7 @@ mod tests { let recipient = NoteRecipient::new(serial_num, script, note_inputs); let asset = Asset::Fungible(FungibleAsset::new(faucet, 100).unwrap()); - let metadata = NoteMetadata::new( - faucet, - NoteType::Public, - NoteTag::from(123), - crate::note::NoteExecutionHint::None, - Felt::new(0), - ); + let metadata = NoteMetadata::new(faucet, NoteType::Public, NoteTag::from(123)); Note::new(NoteAssets::new(vec![asset]).unwrap(), metadata, recipient) } diff --git a/crates/miden-protocol/src/note/header.rs b/crates/miden-protocol/src/note/header.rs index 93ec96df71..04aac21de4 100644 --- a/crates/miden-protocol/src/note/header.rs +++ b/crates/miden-protocol/src/note/header.rs @@ -1,11 +1,8 @@ -use alloc::vec::Vec; - use super::{ ByteReader, ByteWriter, Deserializable, DeserializationError, - Felt, NoteId, NoteMetadata, Serializable, @@ -19,7 +16,7 @@ use crate::Hasher; /// Holds the strictly required, public information of a note. /// /// See [NoteId] and [NoteMetadata] for additional details. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NoteHeader { note_id: NoteId, note_metadata: NoteMetadata, @@ -43,9 +40,14 @@ impl NoteHeader { &self.note_metadata } + /// Consumes self and returns the note header's metadata. + pub fn into_metadata(self) -> NoteMetadata { + self.note_metadata + } + /// Returns a commitment to the note and its metadata. /// - /// > hash(NOTE_ID || NOTE_METADATA) + /// > hash(NOTE_ID || NOTE_METADATA_COMMITMENT) /// /// This value is used primarily for authenticating notes consumed when they are consumed /// in a transaction. @@ -59,64 +61,12 @@ impl NoteHeader { /// Returns a commitment to the note and its metadata. /// -/// > hash(NOTE_ID || NOTE_METADATA) +/// > hash(NOTE_ID || NOTE_METADATA_COMMITMENT) /// /// This value is used primarily for authenticating notes consumed when they are consumed /// in a transaction. pub fn compute_note_commitment(id: NoteId, metadata: &NoteMetadata) -> Word { - Hasher::merge(&[id.as_word(), Word::from(metadata)]) -} - -// CONVERSIONS FROM NOTE HEADER -// ================================================================================================ - -impl From for [Felt; 8] { - fn from(note_header: NoteHeader) -> Self { - (¬e_header).into() - } -} - -impl From for [Word; 2] { - fn from(note_header: NoteHeader) -> Self { - (¬e_header).into() - } -} - -impl From for [u8; 64] { - fn from(note_header: NoteHeader) -> Self { - (¬e_header).into() - } -} - -impl From<&NoteHeader> for [Felt; 8] { - fn from(note_header: &NoteHeader) -> Self { - let mut elements: [Felt; 8] = Default::default(); - elements[..4].copy_from_slice(note_header.note_id.as_elements()); - elements[4..].copy_from_slice(Word::from(note_header.metadata()).as_elements()); - elements - } -} - -impl From<&NoteHeader> for [Word; 2] { - fn from(note_header: &NoteHeader) -> Self { - let mut elements: [Word; 2] = Default::default(); - elements[0].copy_from_slice(note_header.note_id.as_elements()); - elements[1].copy_from_slice(Word::from(note_header.metadata()).as_elements()); - elements - } -} - -impl From<&NoteHeader> for [u8; 64] { - fn from(note_header: &NoteHeader) -> Self { - let mut elements: [u8; 64] = [0; 64]; - let note_metadata_bytes = Word::from(note_header.metadata()) - .iter() - .flat_map(|x| x.as_int().to_le_bytes()) - .collect::>(); - elements[..32].copy_from_slice(¬e_header.note_id.as_bytes()); - elements[32..].copy_from_slice(¬e_metadata_bytes); - elements - } + Hasher::merge(&[id.as_word(), metadata.to_commitment()]) } // SERIALIZATION diff --git a/crates/miden-protocol/src/note/metadata.rs b/crates/miden-protocol/src/note/metadata.rs index 3705201a3f..e641e7b150 100644 --- a/crates/miden-protocol/src/note/metadata.rs +++ b/crates/miden-protocol/src/note/metadata.rs @@ -1,6 +1,3 @@ -use alloc::string::ToString; - -use super::execution_hint::NoteExecutionHint; use super::{ AccountId, ByteReader, @@ -8,40 +5,62 @@ use super::{ Deserializable, DeserializationError, Felt, - NoteError, NoteTag, NoteType, Serializable, Word, }; +use crate::note::{ + NoteAttachment, + NoteAttachmentContentType, + NoteAttachmentType, + NoteExecutionHint, +}; +use crate::{Hasher, NoteError}; // NOTE METADATA // ================================================================================================ -/// Metadata associated with a note. +/// The metadata associated with a note. +/// +/// Note metadata consists of two parts: +/// - The header of the metadata, which consists of: +/// - the sender of the note +/// - the [`NoteType`] +/// - the [`NoteTag`] +/// - type information about the [`NoteAttachment`]. +/// - The optional [`NoteAttachment`]. /// /// # Word layout & validity /// -/// [`NoteMetadata`] can be encoded into a [`Word`] with the following layout: +/// [`NoteMetadata`] can be encoded into two words, a header and an attachment word. +/// +/// The header word has the following layout: /// /// ```text +/// 0th felt: [sender_id_suffix (56 bits) | 6 zero bits | note_type (2 bit)] /// 1st felt: [sender_id_prefix (64 bits)] -/// 2nd felt: [sender_id_suffix (56 bits) | note_type (2 bits) | note_execution_hint_tag (6 bits)] -/// 3rd felt: [note_execution_hint_payload (32 bits) | note_tag (32 bits)] -/// 4th felt: [aux (64 bits)] +/// 2nd felt: [32 zero bits | note_tag (32 bits)] +/// 3rd felt: [30 zero bits | attachment_content_type (2 bits) | attachment_type (32 bits)] /// ``` /// -/// The rationale for the above layout is to ensure the validity of each felt: -/// - 1st felt: Is equivalent to the prefix of the account ID so it inherits its validity. -/// - 2nd felt: The lower 8 bits of the account ID suffix are `0` by construction, so that they can -/// be overwritten with other data. The suffix is designed such that it retains its felt validity -/// even if all of its lower 8 bits are be set to `1`. This is because the most significant bit is -/// always zero. -/// - 3rd felt: The note execution hint payload must contain at least one `0` bit in its encoding, -/// so the upper 32 bits of the felt will contain at least one `0` bit making the entire felt -/// valid. -/// - 4th felt: The `aux` value must be a felt itself. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// The felt validity of each part of the layout is guaranteed: +/// - 1st felt: The lower 8 bits of the account ID suffix are `0` by construction, so that they can +/// be overwritten with other data. The suffix' most significant bit must be zero such that the +/// entire felt retains its validity even if all of its lower 8 bits are be set to `1`. So the +/// note type can be comfortably encoded. +/// - 2nd felt: Is equivalent to the prefix of the account ID so it inherits its validity. +/// - 3rd felt: The upper 32 bits are always zero. +/// - 4th felt: The upper 30 bits are always zero. +/// +/// The value of the attachment word depends on the +/// [`NoteAttachmentContentType`](crate::note::NoteAttachmentContentType): +/// - [`NoteAttachmentContentType::None`](crate::note::NoteAttachmentContentType::None): Empty word. +/// - [`NoteAttachmentContentType::Word`](crate::note::NoteAttachmentContentType::Word): The raw +/// word itself. +/// - [`NoteAttachmentContentType::Array`](crate::note::NoteAttachmentContentType::Array): The +/// commitment to the elements. +#[derive(Clone, Debug, Eq, PartialEq)] pub struct NoteMetadata { /// The ID of the account which created the note. sender: AccountId, @@ -52,31 +71,29 @@ pub struct NoteMetadata { /// A value which can be used by the recipient(s) to identify notes intended for them. tag: NoteTag, - /// An arbitrary user-defined value. - aux: Felt, - - /// Specifies when a note is ready to be consumed. - execution_hint: NoteExecutionHint, + /// The optional attachment of a note's metadata. + /// + /// Defaults to [`NoteAttachment::default`]. + attachment: NoteAttachment, } impl NoteMetadata { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + /// Returns a new [`NoteMetadata`] instantiated with the specified parameters. - pub fn new( - sender: AccountId, - note_type: NoteType, - tag: NoteTag, - execution_hint: NoteExecutionHint, - aux: Felt, - ) -> Self { + pub fn new(sender: AccountId, note_type: NoteType, tag: NoteTag) -> Self { Self { sender, note_type, tag, - aux, - execution_hint, + attachment: NoteAttachment::default(), } } + // ACCESSORS + // -------------------------------------------------------------------------------------------- + /// Returns the account which created the note. pub fn sender(&self) -> AccountId { self.sender @@ -92,68 +109,78 @@ impl NoteMetadata { self.tag } + // TODO(note_attachment): Remove this method. + // Method left for temporary compatibility. /// Returns the execution hint associated with the note. pub fn execution_hint(&self) -> NoteExecutionHint { - self.execution_hint + NoteExecutionHint::always() } + // TODO(note_attachment): Remove this method. + // Method left for temporary compatibility. /// Returns the note's aux field. pub fn aux(&self) -> Felt { - self.aux + Felt::from(0u32) + } + + /// Returns the attachment of the note. + pub fn attachment(&self) -> &NoteAttachment { + &self.attachment } /// Returns `true` if the note is private. pub fn is_private(&self) -> bool { self.note_type == NoteType::Private } -} -impl From for Word { - /// Convert a [`NoteMetadata`] into a [`Word`]. + /// Returns the header of a [`NoteMetadata`] as a [`Word`]. /// - /// The produced layout of the word is documented on the [`NoteMetadata`] type. - fn from(metadata: NoteMetadata) -> Self { - (&metadata).into() + /// See [`NoteMetadata`] docs for more details. + fn to_header(&self) -> NoteMetadataHeader { + NoteMetadataHeader { + sender: self.sender, + note_type: self.note_type, + tag: self.tag, + attachment_content_type: self.attachment().content().content_type(), + attachment_type: self.attachment.attachment_type(), + } } -} -impl From<&NoteMetadata> for Word { - /// Convert a [`NoteMetadata`] into a [`Word`]. + /// Returns the [`Word`] that represents the header of a [`NoteMetadata`]. /// - /// The produced layout of the word is documented on the [`NoteMetadata`] type. - fn from(metadata: &NoteMetadata) -> Self { - let mut elements = Word::empty(); - elements[0] = metadata.sender.prefix().as_felt(); - elements[1] = merge_id_type_and_hint_tag( - metadata.sender.suffix(), - metadata.note_type, - metadata.execution_hint, - ); - elements[2] = merge_note_tag_and_hint_payload(metadata.execution_hint, metadata.tag); - elements[3] = metadata.aux; - elements + /// See [`NoteMetadata`] docs for more details. + pub fn to_header_word(&self) -> Word { + Word::from(self.to_header()) } -} - -impl TryFrom for NoteMetadata { - type Error = NoteError; - /// Tries to decode a [`Word`] into a [`NoteMetadata`]. + /// Returns the [`Word`] that represents the attachment of a [`NoteMetadata`]. /// - /// The expected layout of the word is documented on the [`NoteMetadata`] type. - fn try_from(elements: Word) -> Result { - let sender_id_prefix: Felt = elements[0]; + /// See [`NoteMetadata`] docs for more details. + pub fn to_attachment_word(&self) -> Word { + self.attachment.content().to_word() + } - let (sender_id_suffix, note_type, execution_hint_tag) = - unmerge_id_type_and_hint_tag(elements[1])?; + /// Returns the commitment to the note metadata, which is defined as: + /// + /// ```text + /// hash(NOTE_METADATA_HEADER || NOTE_METADATA_ATTACHMENT) + /// ``` + pub fn to_commitment(&self) -> Word { + Hasher::merge(&[self.to_header_word(), self.to_attachment_word()]) + } - let sender = AccountId::try_from([sender_id_prefix, sender_id_suffix]) - .map_err(NoteError::NoteSenderInvalidAccountId)?; + // MUTATORS + // -------------------------------------------------------------------------------------------- - let (execution_hint, note_tag) = - unmerge_note_tag_and_hint_payload(elements[2], execution_hint_tag)?; + /// Overwrites the note's attachment with the provided one. + pub fn set_attachment(&mut self, attachment: NoteAttachment) { + self.attachment = attachment; + } - Ok(Self::new(sender, note_type, note_tag, execution_hint, elements[3])) + /// Overwrites the note's attachment with the provided one. + pub fn with_attachment(mut self, attachment: NoteAttachment) -> Self { + self.attachment = attachment; + self } } @@ -162,125 +189,165 @@ impl TryFrom for NoteMetadata { impl Serializable for NoteMetadata { fn write_into(&self, target: &mut W) { - Word::from(self).write_into(target); + self.note_type().write_into(target); + self.sender().write_into(target); + self.tag().write_into(target); + self.attachment().write_into(target); } } impl Deserializable for NoteMetadata { fn read_from(source: &mut R) -> Result { - let word = Word::read_from(source)?; - Self::try_from(word).map_err(|err| DeserializationError::InvalidValue(err.to_string())) + let note_type = NoteType::read_from(source)?; + let sender = AccountId::read_from(source)?; + let tag = NoteTag::read_from(source)?; + let attachment = NoteAttachment::read_from(source)?; + + Ok(NoteMetadata::new(sender, note_type, tag).with_attachment(attachment)) + } +} + +// NOTE METADATA HEADER +// ================================================================================================ + +/// The header representation of [`NoteMetadata`]. +/// +/// See the metadata's type for details on this type's [`Word`] layout. +/// +/// This is intended to be a private type meant for encapsulating the conversion from and to words. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct NoteMetadataHeader { + sender: AccountId, + note_type: NoteType, + tag: NoteTag, + attachment_content_type: NoteAttachmentContentType, + attachment_type: NoteAttachmentType, +} + +impl From for Word { + fn from(header: NoteMetadataHeader) -> Self { + let mut metadata = Word::empty(); + + metadata[0] = merge_sender_suffix_and_note_type(header.sender.suffix(), header.note_type); + metadata[1] = header.sender.prefix().as_felt(); + metadata[2] = Felt::from(header.tag); + metadata[3] = + merge_attachment_type_info(header.attachment_content_type, header.attachment_type); + + metadata + } +} + +impl TryFrom for NoteMetadataHeader { + type Error = NoteError; + + /// Decodes a [`NoteMetadataHeader`] from a [`Word`]. + fn try_from(word: Word) -> Result { + let (sender_suffix, note_type) = unmerge_sender_suffix_and_note_type(word[0])?; + let sender_prefix = word[1]; + let tag = u32::try_from(word[2]).map(NoteTag::new).map_err(|_| { + NoteError::other("failed to convert note tag from metadata header to u32") + })?; + let (attachment_content_type, attachment_type) = unmerge_attachment_type_info(word[3])?; + + let sender = AccountId::try_from([sender_prefix, sender_suffix]).map_err(|source| { + NoteError::other_with_source("failed to decode account ID from metadata header", source) + })?; + + Ok(Self { + sender, + note_type, + tag, + attachment_content_type, + attachment_type, + }) } } // HELPER FUNCTIONS // ================================================================================================ -/// Merges the suffix of an [`AccountId`], a [`NoteType`] and the tag of a -/// [`NoteExecutionHint`] into a single [`Felt`]. +/// Merges the suffix of an [`AccountId`] and the [`NoteType`] into a single [`Felt`]. /// /// The layout is as follows: /// /// ```text -/// [sender_id_suffix (56 bits) | note_type (2 bits) | note_execution_hint_tag (6 bits)] +/// [sender_id_suffix (56 bits) | 6 zero bits | note_type (2 bits)] /// ``` /// -/// One of the upper 16 bits is guaranteed to be zero due to the guarantees of the epoch in the -/// account ID. +/// The most significant bit of the suffix is guaranteed to be zero, so the felt retains its +/// validity. /// -/// Note that `sender_id_suffix` is the suffix of the sender's account ID. -fn merge_id_type_and_hint_tag( - sender_id_suffix: Felt, - note_type: NoteType, - note_execution_hint: NoteExecutionHint, -) -> Felt { +/// The `sender_id_suffix` is the suffix of the sender's account ID. +fn merge_sender_suffix_and_note_type(sender_id_suffix: Felt, note_type: NoteType) -> Felt { let mut merged = sender_id_suffix.as_int(); - let type_bits = note_type as u8; - let (tag_bits, _) = note_execution_hint.into_parts(); + let note_type_byte = note_type as u8; + debug_assert!(note_type_byte < 4, "note type must not contain values >= 4"); + merged |= note_type_byte as u64; - debug_assert!(type_bits & 0b1111_1100 == 0, "note type must not contain values >= 4"); - debug_assert!( - tag_bits & 0b1100_0000 == 0, - "note execution hint tag must not contain values >= 64" - ); - - // Note: The least significant byte of the second AccountId felt is zero by construction so we - // can overwrite it. - merged |= (type_bits << 6) as u64; - merged |= tag_bits as u64; - - // SAFETY: The most significant bit of the suffix is zero by construction so the bytes will be a + // SAFETY: The most significant bit of the suffix is zero by construction so the u64 will be a // valid felt. Felt::try_from(merged).expect("encoded value should be a valid felt") } -/// Unmerges the given felt into the suffix of an [`AccountId`], a [`NoteType`] and the tag of -/// a [`NoteExecutionHint`]. -fn unmerge_id_type_and_hint_tag(element: Felt) -> Result<(Felt, NoteType, u8), NoteError> { - let element = element.as_int(); - - // Cut off the least significant byte. - let least_significant_byte = element as u8; - let note_type_bits = (least_significant_byte & 0b1100_0000) >> 6; - let tag_bits = least_significant_byte & 0b0011_1111; +/// Unmerges the sender ID suffix and note type. +fn unmerge_sender_suffix_and_note_type(element: Felt) -> Result<(Felt, NoteType), NoteError> { + const NOTE_TYPE_MASK: u8 = 0b11; + // Inverts the note type mask. + const SENDER_SUFFIX_MASK: u64 = !(NOTE_TYPE_MASK as u64); - let note_type = NoteType::try_from(note_type_bits)?; + let note_type_byte = element.as_int() as u8 & NOTE_TYPE_MASK; + let note_type = NoteType::try_from(note_type_byte).map_err(|source| { + NoteError::other_with_source("failed to decode note type from metadata header", source) + })?; - // Set least significant byte to zero. - let element = element & 0xffff_ffff_ffff_ff00; + // No bits were set so felt should still be valid. + let sender_suffix = + Felt::try_from(element.as_int() & SENDER_SUFFIX_MASK).expect("felt should still be valid"); - // SAFETY: The input was a valid felt and we cleared additional bits and did not set any - // bits, so it must still be a valid felt. - let sender_id_suffix = Felt::try_from(element).expect("element should still be valid"); - - Ok((sender_id_suffix, note_type, tag_bits)) + Ok((sender_suffix, note_type)) } -/// Merges the [`NoteExecutionHint`] payload and a [`NoteTag`] into a single [`Felt`]. +/// Merges the [`NoteAttachmentType`] and [`NoteAttachmentContentType`] into a single [`Felt`]. /// /// The layout is as follows: /// /// ```text -/// [note_execution_hint_payload (32 bits) | note_tag (32 bits)] +/// [30 zero bits | attachment_content_type (2 bits) | attachment_type (32 bits)] /// ``` -/// -/// One of the upper 32 bits is guaranteed to be zero. -fn merge_note_tag_and_hint_payload( - note_execution_hint: NoteExecutionHint, - note_tag: NoteTag, +fn merge_attachment_type_info( + attachment_content_type: NoteAttachmentContentType, + attachment_type: NoteAttachmentType, ) -> Felt { - let (_, payload) = note_execution_hint.into_parts(); - let note_tag: u32 = note_tag.into(); - - debug_assert_ne!( - payload, - u32::MAX, - "payload should never be u32::MAX as it would produce an invalid felt" + debug_assert!( + attachment_content_type.as_u8() < 4, + "attachment content type should fit into two bits" ); + let mut merged = (attachment_content_type.as_u8() as u64) << 32; + let attachment_type = attachment_type.as_u32(); + merged |= attachment_type as u64; - let felt_int = ((payload as u64) << 32) | (note_tag as u64); - - // SAFETY: The payload is guaranteed to never be u32::MAX so at least one of the upper 32 bits - // is zero, hence the felt is valid even if note_tag is u32::MAX. - Felt::try_from(felt_int).expect("bytes should be a valid felt") + Felt::try_from(merged).expect("the upper bit should be zero and the felt therefore valid") } -/// Unmerges the given felt into a [`NoteExecutionHint`] payload and a [`NoteTag`] and constructs a -/// [`NoteExecutionHint`] from the unmerged payload and the given `note_execution_hint_tag`. -fn unmerge_note_tag_and_hint_payload( +/// Unmerges the attachment content type and attachment type. +fn unmerge_attachment_type_info( element: Felt, - note_execution_hint_tag: u8, -) -> Result<(NoteExecutionHint, NoteTag), NoteError> { - let element = element.as_int(); - - let payload = (element >> 32) as u32; - let note_tag = (element & 0xffff_ffff) as u32; - - let execution_hint = NoteExecutionHint::from_parts(note_execution_hint_tag, payload)?; - let note_tag = NoteTag::from(note_tag); - - Ok((execution_hint, note_tag)) +) -> Result<(NoteAttachmentContentType, NoteAttachmentType), NoteError> { + let attachment_type = element.as_int() as u32; + let attachment_content_type = (element.as_int() >> 32) as u8; + + let attachment_type = NoteAttachmentType::new(attachment_type); + let attachment_content_type = NoteAttachmentContentType::try_from(attachment_content_type) + .map_err(|source| { + NoteError::other_with_source( + "failed to decode attachment content type from metadata header", + source, + ) + })?; + + Ok((attachment_content_type, attachment_type)) } // TESTS @@ -289,79 +356,34 @@ fn unmerge_note_tag_and_hint_payload( #[cfg(test)] mod tests { - use anyhow::Context; - use super::*; + use crate::note::NoteAttachmentType; use crate::testing::account_id::ACCOUNT_ID_MAX_ONES; + #[rstest::rstest] + #[case::attachment_none(NoteAttachment::default())] + #[case::attachment_raw(NoteAttachment::new_word(NoteAttachmentType::new(0), Word::from([3, 4, 5, 6u32])))] + #[case::attachment_commitment(NoteAttachment::new_array( + NoteAttachmentType::new(u32::MAX), + vec![Felt::new(5), Felt::new(6), Felt::new(7)], + )?)] #[test] - fn note_metadata_serde() -> anyhow::Result<()> { + fn note_metadata_serde(#[case] attachment: NoteAttachment) -> anyhow::Result<()> { // Use the Account ID with the maximum one bits to test if the merge function always // produces valid felts. let sender = AccountId::try_from(ACCOUNT_ID_MAX_ONES).unwrap(); let note_type = NoteType::Public; - let tag = NoteTag::with_account_target(sender); - let aux = Felt::try_from(0xffff_ffff_0000_0000u64).unwrap(); - - for execution_hint in [ - NoteExecutionHint::always(), - NoteExecutionHint::none(), - NoteExecutionHint::on_block_slot(10, 11, 12), - NoteExecutionHint::after_block((u32::MAX - 1).into()).unwrap(), - ] { - let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux); - NoteMetadata::read_from_bytes(&metadata.to_bytes()) - .context(format!("failed for execution hint {execution_hint:?}"))?; - } + let tag = NoteTag::new(u32::MAX); + let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment); - Ok(()) - } + // Serialization Roundtrip + let deserialized = NoteMetadata::read_from_bytes(&metadata.to_bytes())?; + assert_eq!(deserialized, metadata); - #[test] - fn merge_and_unmerge_id_type_and_hint() { - // Use the Account ID with the maximum one bits to test if the merge function always - // produces valid felts. - let sender = AccountId::try_from(ACCOUNT_ID_MAX_ONES).unwrap(); - let sender_id_suffix = sender.suffix(); + // Metadata Header Roundtrip + let header = NoteMetadataHeader::try_from(metadata.to_header_word())?; + assert_eq!(header, metadata.to_header()); - let note_type = NoteType::Public; - let note_execution_hint = NoteExecutionHint::OnBlockSlot { - round_len: 10, - slot_len: 11, - slot_offset: 12, - }; - - let merged_value = - merge_id_type_and_hint_tag(sender_id_suffix, note_type, note_execution_hint); - let (extracted_suffix, extracted_note_type, extracted_note_execution_hint_tag) = - unmerge_id_type_and_hint_tag(merged_value).unwrap(); - - assert_eq!(note_type, extracted_note_type); - assert_eq!(note_execution_hint.into_parts().0, extracted_note_execution_hint_tag); - assert_eq!(sender_id_suffix, extracted_suffix); - - let note_type = NoteType::Private; - let note_execution_hint = NoteExecutionHint::Always; - - let merged_value = - merge_id_type_and_hint_tag(sender_id_suffix, note_type, note_execution_hint); - let (extracted_suffix, extracted_note_type, extracted_note_execution_hint_tag) = - unmerge_id_type_and_hint_tag(merged_value).unwrap(); - - assert_eq!(note_type, extracted_note_type); - assert_eq!(note_execution_hint.into_parts().0, extracted_note_execution_hint_tag); - assert_eq!(sender_id_suffix, extracted_suffix); - - let note_type = NoteType::Private; - let note_execution_hint = NoteExecutionHint::None; - - let merged_value = - merge_id_type_and_hint_tag(sender_id_suffix, note_type, note_execution_hint); - let (extracted_suffix, extracted_note_type, extracted_note_execution_hint_tag) = - unmerge_id_type_and_hint_tag(merged_value).unwrap(); - - assert_eq!(note_type, extracted_note_type); - assert_eq!(note_execution_hint.into_parts().0, extracted_note_execution_hint_tag); - assert_eq!(sender_id_suffix, extracted_suffix); + Ok(()) } } diff --git a/crates/miden-protocol/src/note/mod.rs b/crates/miden-protocol/src/note/mod.rs index a87252bcbf..7f5a3dcdca 100644 --- a/crates/miden-protocol/src/note/mod.rs +++ b/crates/miden-protocol/src/note/mod.rs @@ -20,6 +20,15 @@ pub use inputs::NoteInputs; mod metadata; pub use metadata::NoteMetadata; +mod attachment; +pub use attachment::{ + NoteAttachment, + NoteAttachmentArray, + NoteAttachmentContent, + NoteAttachmentContentType, + NoteAttachmentType, +}; + mod execution_hint; pub use execution_hint::{AfterBlockNumber, NoteExecutionHint}; @@ -149,7 +158,7 @@ impl Note { /// Returns a commitment to the note and its metadata. /// - /// > hash(NOTE_ID || NOTE_METADATA) + /// > hash(NOTE_ID || NOTE_METADATA_COMMITMENT) /// /// This value is used primarily for authenticating notes consumed when the are consumed /// in a transaction. @@ -170,12 +179,6 @@ impl AsRef for Note { // CONVERSIONS FROM NOTE // ================================================================================================ -impl From<&Note> for NoteHeader { - fn from(note: &Note) -> Self { - note.header - } -} - impl From for NoteHeader { fn from(note: Note) -> Self { note.header @@ -197,17 +200,7 @@ impl From for NoteDetails { impl From for PartialNote { fn from(note: Note) -> Self { let (assets, recipient, ..) = note.details.into_parts(); - PartialNote::new(*note.header.metadata(), recipient.digest(), assets) - } -} - -impl From<&Note> for PartialNote { - fn from(note: &Note) -> Self { - PartialNote::new( - *note.header.metadata(), - note.details.recipient().digest(), - note.details.assets().clone(), - ) + PartialNote::new(note.header.into_metadata(), recipient.digest(), assets) } } diff --git a/crates/miden-protocol/src/note/partial.rs b/crates/miden-protocol/src/note/partial.rs index 1b39af8a53..03553c3d2e 100644 --- a/crates/miden-protocol/src/note/partial.rs +++ b/crates/miden-protocol/src/note/partial.rs @@ -23,7 +23,7 @@ use crate::Word; /// and generally does not have enough info to execute the note. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PartialNote { - metadata: NoteMetadata, + header: NoteHeader, recipient_digest: Word, assets: NoteAssets, } @@ -31,7 +31,9 @@ pub struct PartialNote { impl PartialNote { /// Returns a new [PartialNote] instantiated from the provided parameters. pub fn new(metadata: NoteMetadata, recipient_digest: Word, assets: NoteAssets) -> Self { - Self { metadata, recipient_digest, assets } + let note_id = NoteId::new(recipient_digest, assets.commitment()); + let header = NoteHeader::new(note_id, metadata); + Self { header, recipient_digest, assets } } /// Returns the ID corresponding to this note. @@ -41,7 +43,7 @@ impl PartialNote { /// Returns the metadata associated with this note. pub fn metadata(&self) -> &NoteMetadata { - &self.metadata + self.header.metadata() } /// Returns the digest of the recipient associated with this note. @@ -55,17 +57,10 @@ impl PartialNote { pub fn assets(&self) -> &NoteAssets { &self.assets } -} - -impl From<&PartialNote> for NoteHeader { - fn from(note: &PartialNote) -> Self { - NoteHeader::new(note.id(), note.metadata) - } -} -impl From for NoteHeader { - fn from(note: PartialNote) -> Self { - NoteHeader::new(note.id(), note.metadata) + /// Returns the [`NoteHeader`] of this note. + pub fn header(&self) -> &NoteHeader { + &self.header } } @@ -74,7 +69,9 @@ impl From for NoteHeader { impl Serializable for PartialNote { fn write_into(&self, target: &mut W) { - self.metadata.write_into(target); + // Serialize only metadata since the note ID in the header can be recomputed from the + // remaining data. + self.header().metadata().write_into(target); self.recipient_digest.write_into(target); self.assets.write_into(target) } diff --git a/crates/miden-protocol/src/testing/block_note_tree.rs b/crates/miden-protocol/src/testing/block_note_tree.rs index 9779c06c57..3304527f9a 100644 --- a/crates/miden-protocol/src/testing/block_note_tree.rs +++ b/crates/miden-protocol/src/testing/block_note_tree.rs @@ -19,7 +19,7 @@ impl BlockNoteTree { // SAFETY: This is only called from test code. Reconsider if this changes. let block_note_index = BlockNoteIndex::new(batch_idx, *note_idx_in_batch) .expect("output note batch indices should fit into a block"); - (block_note_index, note.id(), *note.metadata()) + (block_note_index, note.id(), note.metadata()) }) }); diff --git a/crates/miden-protocol/src/testing/note.rs b/crates/miden-protocol/src/testing/note.rs index 13c8a9464e..56f2bedf84 100644 --- a/crates/miden-protocol/src/testing/note.rs +++ b/crates/miden-protocol/src/testing/note.rs @@ -1,11 +1,11 @@ use alloc::vec::Vec; +use crate::Word; use crate::assembly::Assembler; use crate::asset::FungibleAsset; use crate::note::{ Note, NoteAssets, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -14,7 +14,6 @@ use crate::note::{ NoteType, }; use crate::testing::account_id::ACCOUNT_ID_SENDER; -use crate::{Word, ZERO}; pub const DEFAULT_NOTE_CODE: &str = "begin nop end"; @@ -29,8 +28,6 @@ impl Note { sender_id, NoteType::Private, NoteTag::with_account_target(sender_id), - NoteExecutionHint::Always, - ZERO, ); let inputs = NoteInputs::new(Vec::new()).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, inputs); diff --git a/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs index 6f1423e37a..067626a143 100644 --- a/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs +++ b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs @@ -359,7 +359,8 @@ impl TransactionAdviceInputs { note_data.extend(*recipient.inputs().commitment()); note_data.extend(*assets.commitment()); note_data.extend(*note_arg); - note_data.extend(Word::from(note.metadata())); + note_data.extend(note.metadata().to_header_word()); + note_data.extend(note.metadata().to_attachment_word()); note_data.push(recipient.inputs().num_values().into()); note_data.push((assets.num_assets() as u32).into()); note_data.extend(assets.to_padded_assets()); diff --git a/crates/miden-protocol/src/transaction/kernel/memory.rs b/crates/miden-protocol/src/transaction/kernel/memory.rs index f5db5d4e1e..8b33b214ae 100644 --- a/crates/miden-protocol/src/transaction/kernel/memory.rs +++ b/crates/miden-protocol/src/transaction/kernel/memory.rs @@ -342,14 +342,14 @@ pub const NOTE_MEM_SIZE: MemoryAddress = 2048; // // Here `n` represents number of input notes. // -// Each nullifier occupies a single word. A data section for each note consists of exactly 512 -// words and is laid out like so: +// Each nullifier occupies a single word. A data section for each note consists of exactly 2048 +// elements and is laid out like so: // -// ┌──────┬────────┬────────┬────────┬────────────┬───────────┬──────┬───────┬────────┬────────┬───────┬─────┬───────┬─────────┬ -// │ NOTE │ SERIAL │ SCRIPT │ INPUTS │ ASSETS | RECIPIENT │ META │ NOTE │ NUM │ NUM │ ASSET │ ... │ ASSET │ PADDING │ -// │ ID │ NUM │ ROOT │ HASH │ COMMITMENT | │ DATA │ ARGS │ INPUTS │ ASSETS │ 0 │ │ n │ │ -// ├──────┼────────┼────────┼────────┼────────────┼───────────┼──────┼───────┼────────┼────────┼───────┼─────┼───────┼─────────┤ -// 0 4 8 12 16 20 24 28 32 36 40 + 4n +// ┌──────┬────────┬────────┬────────┬────────────┬───────────┬──────────┬────────────┬───────┬────────┬────────┬───────┬─────┬───────┬─────────┬ +// │ NOTE │ SERIAL │ SCRIPT │ INPUTS │ ASSETS | RECIPIENT │ METADATA │ ATTACHMENT │ NOTE │ NUM │ NUM │ ASSET │ ... │ ASSET │ PADDING │ +// │ ID │ NUM │ ROOT │ HASH │ COMMITMENT | │ HEADER │ │ ARGS │ INPUTS │ ASSETS │ 0 │ │ n │ │ +// ├──────┼────────┼────────┼────────┼────────────┼───────────┼──────────┼────────────┼───────┼────────┼────────┼───────┼─────┼───────┼─────────┤ +// 0 4 8 12 16 20 24 28 32 36 40 44 + 4n // // - NUM_INPUTS is encoded as [num_inputs, 0, 0, 0]. // - NUM_ASSETS is encoded as [num_assets, 0, 0, 0]. @@ -382,12 +382,15 @@ pub const INPUT_NOTE_SCRIPT_ROOT_OFFSET: MemoryOffset = 8; pub const INPUT_NOTE_INPUTS_COMMITMENT_OFFSET: MemoryOffset = 12; pub const INPUT_NOTE_ASSETS_COMMITMENT_OFFSET: MemoryOffset = 16; pub const INPUT_NOTE_RECIPIENT_OFFSET: MemoryOffset = 20; -pub const INPUT_NOTE_METADATA_OFFSET: MemoryOffset = 24; -pub const INPUT_NOTE_ARGS_OFFSET: MemoryOffset = 28; -pub const INPUT_NOTE_NUM_INPUTS_OFFSET: MemoryOffset = 32; -pub const INPUT_NOTE_NUM_ASSETS_OFFSET: MemoryOffset = 36; -pub const INPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 40; +pub const INPUT_NOTE_METADATA_HEADER_OFFSET: MemoryOffset = 24; +pub const INPUT_NOTE_ATTACHMENT_OFFSET: MemoryOffset = 28; +pub const INPUT_NOTE_ARGS_OFFSET: MemoryOffset = 32; +pub const INPUT_NOTE_NUM_INPUTS_OFFSET: MemoryOffset = 36; +pub const INPUT_NOTE_NUM_ASSETS_OFFSET: MemoryOffset = 40; +pub const INPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 44; +#[allow(clippy::empty_line_after_outer_attr)] +#[rustfmt::skip] // OUTPUT NOTES DATA // ------------------------------------------------------------------------------------------------ // Output notes section contains data of all notes produced by a transaction. The section starts at @@ -401,11 +404,11 @@ pub const INPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 40; // The total number of output notes for a transaction is stored in the bookkeeping section of the // memory. Data section of each note is laid out like so: // -// ┌──────┬──────────┬───────────┬────────────┬────────────────┬─────────┬─────┬─────────┬─────────┐ -// │ NOTE │ METADATA │ RECIPIENT │ ASSETS │ NUM ASSETS │ ASSET 0 │ ... │ ASSET n │ PADDING │ -// | ID | | | COMMITMENT | AND DIRTY FLAG | | | | | -// ├──────┼──────────┼───────────┼────────────┼────────────────┼─────────┼─────┼─────────┼─────────┤ -// 0 1 2 3 4 5 5 + n +// ┌──────┬──────────┬────────────┬───────────┬────────────┬────────────────┬─────────┬─────┬─────────┬─────────┐ +// │ NOTE │ METADATA │ METADATA │ RECIPIENT │ ASSETS │ NUM ASSETS │ ASSET 0 │ ... │ ASSET n │ PADDING │ +// | ID | HEADER | ATTACHMENT | | COMMITMENT | AND DIRTY FLAG | | | | | +// ├──────┼──────────┼────────────┼───────────┼────────────┼────────────────┼─────────┼─────┼─────────┼─────────┤ +// 0 1 2 3 4 5 6 6 + n // // The NUM_ASSETS_AND_DIRTY_FLAG word has the following layout: // `[num_assets, assets_commitment_dirty_flag, 0, 0]`, where: @@ -421,17 +424,15 @@ pub const INPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 40; /// The memory address at which the output notes section begins. pub const OUTPUT_NOTE_SECTION_OFFSET: MemoryOffset = 16_777_216; -/// The size of the core output note data segment. -pub const OUTPUT_NOTE_CORE_DATA_SIZE: MemSize = 16; - /// The offsets at which data of an output note is stored relative to the start of its data segment. pub const OUTPUT_NOTE_ID_OFFSET: MemoryOffset = 0; -pub const OUTPUT_NOTE_METADATA_OFFSET: MemoryOffset = 4; -pub const OUTPUT_NOTE_RECIPIENT_OFFSET: MemoryOffset = 8; -pub const OUTPUT_NOTE_ASSET_COMMITMENT_OFFSET: MemoryOffset = 12; -pub const OUTPUT_NOTE_NUM_ASSETS_OFFSET: MemoryOffset = 16; -pub const OUTPUT_NOTE_DIRTY_FLAG_OFFSET: MemoryOffset = 17; -pub const OUTPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 20; +pub const OUTPUT_NOTE_METADATA_HEADER_OFFSET: MemoryOffset = 4; +pub const OUTPUT_NOTE_ATTACHMENT_OFFSET: MemoryOffset = 8; +pub const OUTPUT_NOTE_RECIPIENT_OFFSET: MemoryOffset = 12; +pub const OUTPUT_NOTE_ASSET_COMMITMENT_OFFSET: MemoryOffset = 16; +pub const OUTPUT_NOTE_NUM_ASSETS_OFFSET: MemoryOffset = 20; +pub const OUTPUT_NOTE_DIRTY_FLAG_OFFSET: MemoryOffset = 21; +pub const OUTPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 24; // LINK MAP // ------------------------------------------------------------------------------------------------ diff --git a/crates/miden-protocol/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs index e32f63b8dc..31cf42e8e8 100644 --- a/crates/miden-protocol/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -68,27 +68,27 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // faucet_is_non_fungible_asset_issued word!("0xd2131c592fd7565bbf5d555e73a022c4ec1b5bc70219724e26424ff662e02b1a"), // input_note_get_metadata - word!("0x7ad3e94585e7a397ee27443c98b376ed8d4ba762122af6413fde9314c00a6219"), + word!("0x447b342e38855a9402cde0ea52ecb5e4c1fe542b535a5364cb5caa8e94c82442"), // input_note_get_assets_info - word!("0x159439fe48dbc11e674c5d05830d0408dcfa033c26e85e01256002c6cbc07e9a"), + word!("0xe0817bed99fb61180e705b2c9e5ca8c8f0c62864953247a56acbc65b7d58c2d5"), // input_note_get_script_root word!("0x527036257e58c3a84cf0aa170fb3f219a4553db17d269279355ad164a2b90ac5"), // input_note_get_inputs_info - word!("0xdd8bbf4cdb48051da346bc89760b77fdf4c948904276a99d96409922a00bd322"), + word!("0xb7f45ec34f7708355551dcf1f82c9c40e2c19252f8d5c98dcf9ef1aa0a3eb878"), // input_note_get_serial_number word!("0x25815e02b7976d8e5c297dde60d372cc142c81f702f424ac0920190528c547ee"), // input_note_get_recipient word!("0xd3c255177f9243bb1a523a87615bbe76dd5a3605fcae87eb9d3a626d4ecce33c"), // output_note_create - word!("0xa40e074cd5bd330b0af04c55b839da1e60e72583136f45d3e1bbc8b847865c3a"), + word!("0x9689eb055efa7153b4498e433056d99cda07a8afd4dac39c9cc82ef8cf22b3fb"), // output_note_get_metadata - word!("0xde4a5b57f9d53692459383e6cf6302ef3602a348896ed6ab6fdf67e07fa483ff"), + word!("0x3db8427f20eb70603b72aa574a986506eb7216312004aeaf8b2a7e55d0049a48"), // output_note_get_assets_info - word!("0x7e5d726b5f25f6cfd533bd0294853f3fceea62c41e5f2fd68919d8d53a48b3f8"), + word!("0xd2078081e19843b0c2500b9483f9888fa0a2c33a5155064aa97819696d7ae157"), // output_note_get_recipient - word!("0xc824115ed79a2e1670daed8c18fba1bc15f54c5ec0ec6699de69a00b21d9df92"), + word!("0x1ce137f0c5be72832970e6c818968a789f65b97db34515bfebb767705f28db67"), // output_note_add_asset - word!("0x9b6929d1ce24b3a97c6fb098f2fa8d0958beb15f91e268b9c787194b0a977a0d"), + word!("0xaf22383e4390f4f15a429768f79aa445f8a535bb21b0807172b9ef2de063d9d1"), // tx_get_num_input_notes word!("0xfcc186d4b65c584f3126dda1460b01eef977efd76f9e36f972554af28e33c685"), // tx_get_input_notes_commitment @@ -96,7 +96,7 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ // tx_get_num_output_notes word!("0x2511fca9c078cd96e526fd488d1362cbfd597eb3db8452aedb00beffee9782b4"), // tx_get_output_notes_commitment - word!("0xd5b22dae48ec4b20ed479f2c43573d34930720886371ef6b484310a3bea4e818"), + word!("0xbb94a5220ae82a2b45ab0eca2fc653d1a8f0967507a675430bd4caedc022334c"), // tx_get_block_commitment word!("0xe474b491a64d222397fcf83ee5db7b048061988e5e83ce99b91bae6fd75a3522"), // tx_get_block_number diff --git a/crates/miden-protocol/src/transaction/outputs.rs b/crates/miden-protocol/src/transaction/outputs.rs index 410bbf5449..4be4b2b9d7 100644 --- a/crates/miden-protocol/src/transaction/outputs.rs +++ b/crates/miden-protocol/src/transaction/outputs.rs @@ -102,6 +102,7 @@ pub struct OutputNotes { impl OutputNotes { // CONSTRUCTOR // -------------------------------------------------------------------------------------------- + /// Returns new [OutputNotes] instantiated from the provide vector of notes. /// /// # Errors @@ -120,7 +121,7 @@ impl OutputNotes { } } - let commitment = Self::compute_commitment(notes.iter().map(NoteHeader::from)); + let commitment = Self::compute_commitment(notes.iter().map(OutputNote::header)); Ok(Self { notes, commitment }) } @@ -163,9 +164,13 @@ impl OutputNotes { /// Computes a commitment to output notes. /// - /// For a non-empty list of notes, this is a sequential hash of (note_id, metadata) tuples for - /// the notes created in a transaction. For an empty list, [EMPTY_WORD] is returned. - pub(crate) fn compute_commitment(notes: impl ExactSizeIterator) -> Word { + /// - For an empty list, [`Word::empty`] is returned. + /// - For a non-empty list of notes, this is a sequential hash of (note_id, metadata_commitment) + /// tuples for the notes created in a transaction, where `metadata_commitment` is the return + /// value of [`NoteMetadata::to_commitment`]. + pub(crate) fn compute_commitment<'header>( + notes: impl ExactSizeIterator, + ) -> Word { if notes.len() == 0 { return Word::empty(); } @@ -173,7 +178,7 @@ impl OutputNotes { let mut elements: Vec = Vec::with_capacity(notes.len() * 8); for note_header in notes { elements.extend_from_slice(note_header.id().as_elements()); - elements.extend_from_slice(Word::from(note_header.metadata()).as_elements()); + elements.extend_from_slice(note_header.metadata().to_commitment().as_elements()); } Hasher::hash_elements(&elements) @@ -278,40 +283,30 @@ impl OutputNote { pub fn shrink(&self) -> Self { match self { OutputNote::Full(note) if note.metadata().is_private() => { - OutputNote::Header(*note.header()) + OutputNote::Header(note.header().clone()) }, - OutputNote::Partial(note) => OutputNote::Header(note.into()), + OutputNote::Partial(note) => OutputNote::Header(note.header().clone()), _ => self.clone(), } } + /// Returns a reference to the [`NoteHeader`] of this note. + pub fn header(&self) -> &NoteHeader { + match self { + OutputNote::Full(note) => note.header(), + OutputNote::Partial(note) => note.header(), + OutputNote::Header(header) => header, + } + } + /// Returns a commitment to the note and its metadata. /// - /// > hash(NOTE_ID || NOTE_METADATA) + /// > hash(NOTE_ID || NOTE_METADATA_COMMITMENT) pub fn commitment(&self) -> Word { compute_note_commitment(self.id(), self.metadata()) } } -// CONVERSIONS -// ------------------------------------------------------------------------------------------------ - -impl From for NoteHeader { - fn from(value: OutputNote) -> Self { - (&value).into() - } -} - -impl From<&OutputNote> for NoteHeader { - fn from(value: &OutputNote) -> Self { - match value { - OutputNote::Full(note) => note.into(), - OutputNote::Partial(note) => note.into(), - OutputNote::Header(note) => *note, - } - } -} - // SERIALIZATION // ------------------------------------------------------------------------------------------------ diff --git a/crates/miden-protocol/src/transaction/proven_tx.rs b/crates/miden-protocol/src/transaction/proven_tx.rs index 0e25ba6e5d..e0fde4d293 100644 --- a/crates/miden-protocol/src/transaction/proven_tx.rs +++ b/crates/miden-protocol/src/transaction/proven_tx.rs @@ -636,7 +636,7 @@ impl From<&InputNote> for InputNoteCommitment { }, InputNote::Unauthenticated { note } => Self { nullifier: note.nullifier(), - header: Some(*note.header()), + header: Some(note.header().clone()), }, } } @@ -654,7 +654,7 @@ impl ToInputNoteCommitments for InputNoteCommitment { } fn note_commitment(&self) -> Option { - self.header.map(|header| header.commitment()) + self.header.as_ref().map(NoteHeader::commitment) } } diff --git a/crates/miden-protocol/src/transaction/tx_header.rs b/crates/miden-protocol/src/transaction/tx_header.rs index 4b7adafad6..210d722e4b 100644 --- a/crates/miden-protocol/src/transaction/tx_header.rs +++ b/crates/miden-protocol/src/transaction/tx_header.rs @@ -10,6 +10,7 @@ use crate::transaction::{ ExecutedTransaction, InputNoteCommitment, InputNotes, + OutputNote, OutputNotes, ProvenTransaction, TransactionId, @@ -56,7 +57,7 @@ impl TransactionHeader { fee: FungibleAsset, ) -> Self { let input_notes_commitment = input_notes.commitment(); - let output_notes_commitment = OutputNotes::compute_commitment(output_notes.iter().copied()); + let output_notes_commitment = OutputNotes::compute_commitment(output_notes.iter()); let id = TransactionId::new( initial_state_commitment, @@ -166,7 +167,7 @@ impl From<&ProvenTransaction> for TransactionHeader { tx.account_update().initial_state_commitment(), tx.account_update().final_state_commitment(), tx.input_notes().clone(), - tx.output_notes().iter().map(NoteHeader::from).collect(), + tx.output_notes().iter().map(OutputNote::header).cloned().collect(), tx.fee(), ) } @@ -181,7 +182,7 @@ impl From<&ExecutedTransaction> for TransactionHeader { tx.initial_account().initial_commitment(), tx.final_account().commitment(), tx.input_notes().to_commitments(), - tx.output_notes().iter().map(NoteHeader::from).collect(), + tx.output_notes().iter().map(OutputNote::header).cloned().collect(), tx.fee(), ) } diff --git a/crates/miden-standards/src/account/interface/test.rs b/crates/miden-standards/src/account/interface/test.rs index 5b63a933ad..22aa38c005 100644 --- a/crates/miden-standards/src/account/interface/test.rs +++ b/crates/miden-standards/src/account/interface/test.rs @@ -6,7 +6,6 @@ use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -252,13 +251,7 @@ fn test_basic_wallet_custom_notes() { let sender_account_id = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into().unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); let tag = NoteTag::with_account_target(wallet_account.id()); - let metadata = NoteMetadata::new( - sender_account_id, - NoteType::Public, - tag, - NoteExecutionHint::always(), - Default::default(), - ); + let metadata = NoteMetadata::new(sender_account_id, NoteType::Public, tag); let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " @@ -285,7 +278,7 @@ fn test_basic_wallet_custom_notes() { "; let note_script = CodeBuilder::default().compile_note_script(compatible_source_code).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); - let compatible_custom_note = Note::new(vault.clone(), metadata, recipient); + let compatible_custom_note = Note::new(vault.clone(), metadata.clone(), recipient); assert_eq!( NoteAccountCompatibility::Maybe, wallet_account_interface.is_compatible_with(&compatible_custom_note) @@ -341,13 +334,7 @@ fn test_basic_fungible_faucet_custom_notes() { let sender_account_id = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into().unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); let tag = NoteTag::with_account_target(faucet_account.id()); - let metadata = NoteMetadata::new( - sender_account_id, - NoteType::Public, - tag, - NoteExecutionHint::always(), - Default::default(), - ); + let metadata = NoteMetadata::new(sender_account_id, NoteType::Public, tag); let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " @@ -372,7 +359,7 @@ fn test_basic_fungible_faucet_custom_notes() { "; let note_script = CodeBuilder::default().compile_note_script(compatible_source_code).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); - let compatible_custom_note = Note::new(vault.clone(), metadata, recipient); + let compatible_custom_note = Note::new(vault.clone(), metadata.clone(), recipient); assert_eq!( NoteAccountCompatibility::Maybe, faucet_account_interface.is_compatible_with(&compatible_custom_note) @@ -450,13 +437,7 @@ fn test_custom_account_custom_notes() { let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); let tag = NoteTag::with_account_target(target_account.id()); - let metadata = NoteMetadata::new( - sender_account.id(), - NoteType::Public, - tag, - NoteExecutionHint::always(), - Default::default(), - ); + let metadata = NoteMetadata::new(sender_account.id(), NoteType::Public, tag); let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " @@ -484,7 +465,7 @@ fn test_custom_account_custom_notes() { .compile_note_script(compatible_source_code) .unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); - let compatible_custom_note = Note::new(vault.clone(), metadata, recipient); + let compatible_custom_note = Note::new(vault.clone(), metadata.clone(), recipient); assert_eq!( NoteAccountCompatibility::Maybe, target_account_interface.is_compatible_with(&compatible_custom_note) @@ -560,13 +541,7 @@ fn test_custom_account_multiple_components_custom_notes() { let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); let tag = NoteTag::with_account_target(target_account.id()); - let metadata = NoteMetadata::new( - sender_account.id(), - NoteType::Public, - tag, - NoteExecutionHint::always(), - Default::default(), - ); + let metadata = NoteMetadata::new(sender_account.id(), NoteType::Public, tag); let vault = NoteAssets::new(vec![FungibleAsset::mock(100)]).unwrap(); let compatible_source_code = " @@ -600,7 +575,7 @@ fn test_custom_account_multiple_components_custom_notes() { .compile_note_script(compatible_source_code) .unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, NoteInputs::default()); - let compatible_custom_note = Note::new(vault.clone(), metadata, recipient); + let compatible_custom_note = Note::new(vault.clone(), metadata.clone(), recipient); assert_eq!( NoteAccountCompatibility::Maybe, target_account_interface.is_compatible_with(&compatible_custom_note) diff --git a/crates/miden-standards/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs index 1ad2c012ae..c31f6266c1 100644 --- a/crates/miden-standards/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -43,7 +43,8 @@ pub fn create_p2id_note( target: AccountId, assets: Vec, note_type: NoteType, - aux: Felt, + // TODO(note_attachment): Replace with note attachment. + _aux: Felt, rng: &mut R, ) -> Result { let serial_num = rng.draw_word(); @@ -51,7 +52,7 @@ pub fn create_p2id_note( let tag = NoteTag::with_account_target(target); - let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux); + let metadata = NoteMetadata::new(sender, note_type, tag); let vault = NoteAssets::new(assets)?; Ok(Note::new(vault, metadata, recipient)) @@ -77,7 +78,8 @@ pub fn create_p2ide_note( reclaim_height: Option, timelock_height: Option, note_type: NoteType, - aux: Felt, + // TODO(note_attachment): Replace with note attachment. + _aux: Felt, rng: &mut R, ) -> Result { let serial_num = rng.draw_word(); @@ -85,12 +87,7 @@ pub fn create_p2ide_note( utils::build_p2ide_recipient(target, reclaim_height, timelock_height, serial_num)?; let tag = NoteTag::with_account_target(target); - let execution_hint = match timelock_height { - Some(height) => NoteExecutionHint::after_block(height)?, - None => NoteExecutionHint::always(), - }; - - let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux); + let metadata = NoteMetadata::new(sender, note_type, tag); let vault = NoteAssets::new(assets)?; Ok(Note::new(vault, metadata, recipient)) @@ -110,8 +107,10 @@ pub fn create_swap_note( offered_asset: Asset, requested_asset: Asset, swap_note_type: NoteType, - swap_note_aux: Felt, + // TODO(note_attachment): Replace with note attachment. + _swap_note_aux: Felt, payback_note_type: NoteType, + // TODO(note_attachment): Replace with note attachment. payback_note_aux: Felt, rng: &mut R, ) -> Result<(Note, NoteDetails), NoteError> { @@ -148,8 +147,7 @@ pub fn create_swap_note( let serial_num = rng.draw_word(); // build the outgoing note - let metadata = - NoteMetadata::new(sender, swap_note_type, tag, NoteExecutionHint::always(), swap_note_aux); + let metadata = NoteMetadata::new(sender, swap_note_type, tag); let assets = NoteAssets::new(vec![offered_asset])?; let recipient = NoteRecipient::new(serial_num, note_script, inputs); let note = Note::new(assets, metadata, recipient); @@ -187,7 +185,8 @@ pub fn create_mint_note( faucet_id: AccountId, sender: AccountId, mint_inputs: MintNoteInputs, - aux: Felt, + // TODO(note_attachment): Replace with note attachment. + _aux: Felt, rng: &mut R, ) -> Result { let note_script = WellKnownNote::MINT.script(); @@ -195,14 +194,13 @@ pub fn create_mint_note( // MINT notes are always public for network execution let note_type = NoteType::Public; - let execution_hint = NoteExecutionHint::always(); // Convert MintNoteInputs to NoteInputs let inputs = NoteInputs::from(mint_inputs); let tag = NoteTag::with_account_target(faucet_id); - let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux); + let metadata = NoteMetadata::new(sender, note_type, tag); let assets = NoteAssets::new(vec![])?; // MINT notes have no assets let recipient = NoteRecipient::new(serial_num, note_script, inputs); @@ -234,7 +232,8 @@ pub fn create_burn_note( sender: AccountId, faucet_id: AccountId, fungible_asset: Asset, - aux: Felt, + // TODO(note_attachment): Replace with note attachment. + _aux: Felt, rng: &mut R, ) -> Result { let note_script = WellKnownNote::BURN.script(); @@ -242,13 +241,11 @@ pub fn create_burn_note( // BURN notes are always public let note_type = NoteType::Public; - // Use always execution hint for BURN notes - let execution_hint = NoteExecutionHint::always(); let inputs = NoteInputs::new(vec![])?; let tag = NoteTag::with_account_target(faucet_id); - let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux); + let metadata = NoteMetadata::new(sender, note_type, tag); let assets = NoteAssets::new(vec![fungible_asset])?; // BURN notes contain the asset to burn let recipient = NoteRecipient::new(serial_num, note_script, inputs); diff --git a/crates/miden-standards/src/testing/note.rs b/crates/miden-standards/src/testing/note.rs index 5d6a508900..185fa9f762 100644 --- a/crates/miden-standards/src/testing/note.rs +++ b/crates/miden-standards/src/testing/note.rs @@ -31,10 +31,12 @@ pub struct NoteBuilder { inputs: Vec, assets: Vec, note_type: NoteType, + // TODO(note_attachment): Remove. note_execution_hint: NoteExecutionHint, serial_num: Word, tag: NoteTag, code: String, + // TODO(note_attachment): Remove. aux: Felt, dyn_libraries: Vec, source_manager: Arc, @@ -154,13 +156,7 @@ impl NoteBuilder { .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, - self.tag, - self.note_execution_hint, - self.aux, - ); + let metadata = NoteMetadata::new(self.sender, self.note_type, self.tag); let inputs = NoteInputs::new(self.inputs)?; let recipient = NoteRecipient::new(self.serial_num, note_script, inputs); diff --git a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs index 6831529a7b..92ba3bb8b8 100644 --- a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs @@ -116,7 +116,7 @@ async fn proven_block_success() -> anyhow::Result<()> { ( BlockNoteIndex::new(batch_idx, note_idx_in_batch).unwrap(), note.id(), - *note.metadata(), + note.metadata(), ) }, )) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs index ef250d634c..dc4beb3519 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs @@ -10,7 +10,6 @@ use miden_protocol::crypto::rand::FeltRng; use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -24,7 +23,7 @@ use miden_protocol::testing::account_id::{ ACCOUNT_ID_SENDER, }; use miden_protocol::transaction::{InputNote, OutputNote, TransactionKernel}; -use miden_protocol::{Felt, StarkField, Word, ZERO}; +use miden_protocol::{Felt, StarkField, Word}; use miden_standards::note::{ NoteConsumptionStatus, WellKnownNote, @@ -794,8 +793,7 @@ fn create_p2ide_note_with_inputs(inputs: impl IntoIterator, sender: ); let tag = NoteTag::with_account_target(sender); - let metadata = - NoteMetadata::new(sender, NoteType::Public, tag, NoteExecutionHint::always(), ZERO); + let metadata = NoteMetadata::new(sender, NoteType::Public, tag); Note::new(NoteAssets::default(), metadata, recipient) } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs index b23404dd27..3b2301338f 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_active_note.rs @@ -8,7 +8,6 @@ use miden_protocol::errors::tx_kernel::ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_ use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -101,17 +100,23 @@ async fn test_active_note_get_metadata() -> anyhow::Result<()> { # get the metadata of the active note exec.active_note::get_metadata - # => [METADATA] + # => [NOTE_ATTACHMENT, METADATA_HEADER] - # assert this metadata - push.{METADATA} + push.{NOTE_ATTACHMENT} + assert_eqw.err="note 0 has incorrect note attachment" + # => [METADATA_HEADER] + + push.{METADATA_HEADER} assert_eqw.err="note 0 has incorrect metadata" + # => [] # truncate the stack swapw dropw end "#, - METADATA = Word::from(tx_context.input_notes().get_note(0).note().metadata()) + METADATA_HEADER = tx_context.input_notes().get_note(0).note().metadata().to_header_word(), + NOTE_ATTACHMENT = + tx_context.input_notes().get_note(0).note().metadata().to_attachment_word() ); tx_context.execute_code(&code).await?; @@ -403,13 +408,7 @@ async fn test_active_note_get_exactly_8_inputs() -> anyhow::Result<()> { // prepare note data let serial_num = RpoRandomCoin::new(Word::from([4u32; 4])).draw_word(); let tag = NoteTag::with_account_target(target_id); - let metadata = NoteMetadata::new( - sender_id, - NoteType::Public, - tag, - NoteExecutionHint::always(), - Default::default(), - ); + let metadata = NoteMetadata::new(sender_id, NoteType::Public, tag); let vault = NoteAssets::new(vec![]).context("failed to create input note assets")?; let note_script = CodeBuilder::default() .compile_note_script("begin nop end") diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 79c7b608dc..78eeaa951f 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -45,7 +45,7 @@ use crate::{ /// Tests that the return values from the tx kernel main.masm program match the expected values. #[tokio::test] -async fn test_epilogue() -> anyhow::Result<()> { +async fn test_transaction_epilogue() -> anyhow::Result<()> { let account = Account::mock(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, Auth::IncrNonce); let asset = FungibleAsset::mock(100); let output_note_1 = create_public_p2any_note(account.id(), [asset]); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs index d1c62d88bd..84a9d9322a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_input_note.rs @@ -120,16 +120,20 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { # get the metadata from the requested input note push.0 exec.input_note::get_metadata - # => [METADATA] + # => [NOTE_ATTACHMENT, METADATA_HEADER] - # assert the correctness of the metadata - push.{METADATA} - assert_eqw.err="note 0 has incorrect metadata" + push.{NOTE_ATTACHMENT} + assert_eqw.err="note 0 has incorrect note attachment" + # => [METADATA_HEADER] + + push.{METADATA_HEADER} + assert_eqw.err="note 0 has incorrect metadata header" # => [] end "#, RECIPIENT = p2id_note_1_asset.recipient().digest(), - METADATA = Word::from(p2id_note_1_asset.metadata()), + METADATA_HEADER = p2id_note_1_asset.metadata().to_header_word(), + NOTE_ATTACHMENT = p2id_note_1_asset.metadata().to_attachment_word(), ); let tx_script = CodeBuilder::default().compile_tx_script(code)?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index f4ceaea6ac..678b041a19 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -14,7 +14,6 @@ use miden_protocol::errors::MasmError; use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -381,28 +380,16 @@ async fn test_compute_inputs_commitment() -> anyhow::Result<()> { } #[tokio::test] -async fn test_build_metadata() -> miette::Result<()> { +async fn test_build_metadata_header() -> miette::Result<()> { let tx_context = TransactionContextBuilder::with_existing_mock_account().build().unwrap(); let sender = tx_context.account().id(); let receiver = AccountId::try_from(ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE) .map_err(|e| miette::miette!("Failed to convert account ID: {}", e))?; - let test_metadata1 = NoteMetadata::new( - sender, - NoteType::Private, - NoteTag::with_account_target(receiver), - NoteExecutionHint::after_block(500.into()) - .map_err(|e| miette::miette!("Failed to create execution hint: {}", e))?, - Felt::try_from(1u64 << 63).map_err(|e| miette::miette!("Failed to convert felt: {}", e))?, - ); - let test_metadata2 = NoteMetadata::new( - sender, - NoteType::Public, - NoteTag::new(u32::MAX), - NoteExecutionHint::on_block_slot(u8::MAX, u8::MAX, u8::MAX), - Felt::try_from(0u64).map_err(|e| miette::miette!("Failed to convert felt: {}", e))?, - ); + let test_metadata1 = + NoteMetadata::new(sender, NoteType::Private, NoteTag::with_account_target(receiver)); + let test_metadata2 = NoteMetadata::new(sender, NoteType::Public, NoteTag::new(u32::MAX)); for (iteration, test_metadata) in [test_metadata1, test_metadata2].into_iter().enumerate() { let code = format!( @@ -412,24 +399,27 @@ async fn test_build_metadata() -> miette::Result<()> { begin exec.prologue::prepare_transaction - push.{execution_hint} push.{note_type} push.{aux} push.{tag} - exec.output_note::build_metadata + push.{note_type} + push.{tag} + exec.output_note::build_metadata_header # truncate the stack swapw dropw end ", - execution_hint = Felt::from(test_metadata.execution_hint()), note_type = Felt::from(test_metadata.note_type()), - aux = test_metadata.aux(), tag = test_metadata.tag(), ); - let exec_output = tx_context.execute_code(&code).await.unwrap(); + let exec_output = tx_context.execute_code(&code).await?; let metadata_word = exec_output.get_stack_word_be(0); - assert_eq!(Word::from(test_metadata), metadata_word, "failed in iteration {iteration}"); + assert_eq!( + test_metadata.to_header_word(), + metadata_word, + "failed in iteration {iteration}" + ); } Ok(()) @@ -541,13 +531,7 @@ async fn test_public_key_as_note_input() -> anyhow::Result<()> { let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); let tag = NoteTag::with_account_target(target_account.id()); - let metadata = NoteMetadata::new( - sender_account.id(), - NoteType::Public, - tag, - NoteExecutionHint::always(), - Default::default(), - ); + let metadata = NoteMetadata::new(sender_account.id(), NoteType::Public, tag); let vault = NoteAssets::new(vec![])?; let note_script = CodeBuilder::default().compile_note_script("begin nop end")?; let recipient = diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index a998663174..f9a430920c 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -35,7 +35,8 @@ use miden_protocol::transaction::memory::{ NOTE_MEM_SIZE, NUM_OUTPUT_NOTES_PTR, OUTPUT_NOTE_ASSETS_OFFSET, - OUTPUT_NOTE_METADATA_OFFSET, + OUTPUT_NOTE_ATTACHMENT_OFFSET, + OUTPUT_NOTE_METADATA_HEADER_OFFSET, OUTPUT_NOTE_RECIPIENT_OFFSET, OUTPUT_NOTE_SECTION_OFFSET, }; @@ -100,19 +101,21 @@ async fn test_create_note() -> anyhow::Result<()> { "recipient must be stored at the correct memory location", ); - let expected_note_metadata: Word = NoteMetadata::new( - account_id, - NoteType::Public, - tag, - NoteExecutionHint::after_block(23.into())?, - Felt::new(27), - ) - .into(); + let metadata = NoteMetadata::new(account_id, NoteType::Public, tag); + let expected_metadata_header = metadata.to_header_word(); + let expected_note_attachment = metadata.to_attachment_word(); + + assert_eq!( + exec_output + .get_kernel_mem_word(OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_METADATA_HEADER_OFFSET), + expected_metadata_header, + "metadata header must be stored at the correct memory location", + ); assert_eq!( - exec_output.get_kernel_mem_word(OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_METADATA_OFFSET), - expected_note_metadata, - "metadata must be stored at the correct memory location", + exec_output.get_kernel_mem_word(OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_ATTACHMENT_OFFSET), + expected_note_attachment, + "attachment must be stored at the correct memory location", ); assert_eq!( @@ -256,13 +259,8 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { let output_serial_no_1 = Word::from([8u32; 4]); let output_tag_1 = NoteTag::with_account_target(network_account); let assets = NoteAssets::new(vec![input_asset_1])?; - let metadata = NoteMetadata::new( - tx_context.tx_inputs().account().id(), - NoteType::Public, - output_tag_1, - NoteExecutionHint::Always, - ZERO, - ); + let metadata = + NoteMetadata::new(tx_context.tx_inputs().account().id(), NoteType::Public, output_tag_1); let inputs = NoteInputs::new(vec![])?; let recipient = NoteRecipient::new(output_serial_no_1, input_note_1.script().clone(), inputs); let output_note_1 = Note::new(assets, metadata, recipient); @@ -271,13 +269,8 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { let output_serial_no_2 = Word::from([11u32; 4]); let output_tag_2 = NoteTag::with_account_target(local_account); let assets = NoteAssets::new(vec![input_asset_2])?; - let metadata = NoteMetadata::new( - tx_context.tx_inputs().account().id(), - NoteType::Public, - output_tag_2, - NoteExecutionHint::after_block(123.into())?, - ZERO, - ); + let metadata = + NoteMetadata::new(tx_context.tx_inputs().account().id(), NoteType::Public, output_tag_2); let inputs = NoteInputs::new(vec![])?; let recipient = NoteRecipient::new(output_serial_no_2, input_note_2.script().clone(), inputs); let output_note_2 = Note::new(assets, metadata, recipient); @@ -363,21 +356,30 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { "The test creates two notes", ); assert_eq!( - NoteMetadata::try_from( - exec_output - .get_kernel_mem_word(OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_METADATA_OFFSET) - ) - .unwrap(), - *output_note_1.metadata(), - "Validate the output note 1 metadata", + exec_output + .get_kernel_mem_word(OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_METADATA_HEADER_OFFSET), + output_note_1.metadata().to_header_word(), + "Validate the output note 1 metadata header", + ); + assert_eq!( + exec_output.get_kernel_mem_word(OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_ATTACHMENT_OFFSET), + output_note_1.metadata().to_attachment_word(), + "Validate the output note 1 attachment", + ); + + assert_eq!( + exec_output.get_kernel_mem_word( + OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_METADATA_HEADER_OFFSET + NOTE_MEM_SIZE + ), + output_note_2.metadata().to_header_word(), + "Validate the output note 2 metadata header", ); assert_eq!( - NoteMetadata::try_from(exec_output.get_kernel_mem_word( - OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_METADATA_OFFSET + NOTE_MEM_SIZE - )) - .unwrap(), - *output_note_2.metadata(), - "Validate the output note 1 metadata", + exec_output.get_kernel_mem_word( + OUTPUT_NOTE_SECTION_OFFSET + OUTPUT_NOTE_ATTACHMENT_OFFSET + NOTE_MEM_SIZE + ), + output_note_2.metadata().to_attachment_word(), + "Validate the output note 2 attachment", ); assert_eq!(exec_output.get_stack_word_be(0), expected_output_notes_commitment); @@ -890,11 +892,14 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { # get the metadata (the only existing note has 0'th index) push.0 exec.output_note::get_metadata - # => [METADATA] + # => [NOTE_ATTACHMENT, METADATA_HEADER] + + push.{NOTE_ATTACHMENT} + assert_eqw.err="requested note has incorrect note attachment" + # => [METADATA_HEADER] - # assert the correctness of the metadata - push.{METADATA} - assert_eqw.err="requested note has incorrect metadata" + push.{METADATA_HEADER} + assert_eqw.err="requested note has incorrect metadata header" # => [] # truncate the stack @@ -903,7 +908,8 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { "#, output_note = create_output_note(&output_note), RECIPIENT = output_note.recipient().digest(), - METADATA = Word::from(output_note.metadata()), + METADATA_HEADER = output_note.metadata().to_header_word(), + NOTE_ATTACHMENT = output_note.metadata().to_attachment_word(), ); let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs index d04ba7b65f..07ad25bf3d 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_prologue.rs @@ -42,9 +42,10 @@ use miden_protocol::transaction::memory::{ INPUT_NOTE_ARGS_OFFSET, INPUT_NOTE_ASSETS_COMMITMENT_OFFSET, INPUT_NOTE_ASSETS_OFFSET, + INPUT_NOTE_ATTACHMENT_OFFSET, INPUT_NOTE_ID_OFFSET, INPUT_NOTE_INPUTS_COMMITMENT_OFFSET, - INPUT_NOTE_METADATA_OFFSET, + INPUT_NOTE_METADATA_HEADER_OFFSET, INPUT_NOTE_NULLIFIER_SECTION_PTR, INPUT_NOTE_NUM_ASSETS_OFFSET, INPUT_NOTE_RECIPIENT_OFFSET, @@ -501,9 +502,15 @@ fn input_notes_memory_assertions( ); assert_eq!( - exec_output.get_note_mem_word(note_idx, INPUT_NOTE_METADATA_OFFSET), - Word::from(note.metadata()), - "note metadata should be stored at the correct offset" + exec_output.get_note_mem_word(note_idx, INPUT_NOTE_METADATA_HEADER_OFFSET), + note.metadata().to_header_word(), + "note metadata header should be stored at the correct offset" + ); + + assert_eq!( + exec_output.get_note_mem_word(note_idx, INPUT_NOTE_ATTACHMENT_OFFSET), + note.metadata().to_attachment_word(), + "note attachment should be stored at the correct offset" ); assert_eq!( diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index d9641af5e5..8d3cfbe6e0 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -217,8 +217,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let serial_num_2 = Word::from([1, 2, 3, 4u32]); let note_script_2 = CodeBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; let inputs_2 = NoteInputs::new(vec![ONE])?; - let metadata_2 = - NoteMetadata::new(account_id, note_type2, tag2, NoteExecutionHint::none(), aux2); + let metadata_2 = NoteMetadata::new(account_id, note_type2, tag2); let vault_2 = NoteAssets::new(vec![removed_asset_3, removed_asset_4])?; let recipient_2 = NoteRecipient::new(serial_num_2, note_script_2, inputs_2); let expected_output_note_2 = Note::new(vault_2, metadata_2, recipient_2); @@ -227,13 +226,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let serial_num_3 = Word::from([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]); let note_script_3 = CodeBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; let inputs_3 = NoteInputs::new(vec![ONE, Felt::new(2)])?; - let metadata_3 = NoteMetadata::new( - account_id, - note_type3, - tag3, - NoteExecutionHint::on_block_slot(1, 2, 3), - aux3, - ); + let metadata_3 = NoteMetadata::new(account_id, note_type3, tag3); let vault_3 = NoteAssets::new(vec![])?; let recipient_3 = NoteRecipient::new(serial_num_3, note_script_3, inputs_3); let expected_output_note_3 = Note::new(vault_3, metadata_3, recipient_3); @@ -374,10 +367,10 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let resulting_output_note_2 = executed_transaction.output_notes().get_note(1); let expected_note_id_2 = expected_output_note_2.id(); - let expected_note_metadata_2 = expected_output_note_2.metadata(); + let expected_note_metadata_2 = expected_output_note_2.metadata().clone(); assert_eq!( - NoteHeader::from(resulting_output_note_2), - NoteHeader::new(expected_note_id_2, *expected_note_metadata_2) + *resulting_output_note_2.header(), + NoteHeader::new(expected_note_id_2, expected_note_metadata_2) ); // assert that the expected output note 3 is present and has no assets diff --git a/crates/miden-testing/src/mock_chain/chain.rs b/crates/miden-testing/src/mock_chain/chain.rs index 5e94bc5df9..bfcaefd60a 100644 --- a/crates/miden-testing/src/mock_chain/chain.rs +++ b/crates/miden-testing/src/mock_chain/chain.rs @@ -908,7 +908,7 @@ impl MockChain { created_note.id(), MockChainNote::Private( created_note.id(), - *created_note.metadata(), + created_note.metadata().clone(), note_inclusion_proof, ), ); diff --git a/crates/miden-testing/tests/lib.rs b/crates/miden-testing/tests/lib.rs index 0c3c1d790f..8bda709733 100644 --- a/crates/miden-testing/tests/lib.rs +++ b/crates/miden-testing/tests/lib.rs @@ -5,13 +5,13 @@ mod scripts; mod wallet; use miden_processor::utils::Deserializable; +use miden_protocol::Word; use miden_protocol::account::AccountId; use miden_protocol::asset::FungibleAsset; use miden_protocol::crypto::utils::Serializable; use miden_protocol::note::{Note, NoteAssets, NoteInputs, NoteMetadata, NoteRecipient, NoteType}; use miden_protocol::testing::account_id::ACCOUNT_ID_SENDER; use miden_protocol::transaction::{ExecutedTransaction, ProvenTransaction}; -use miden_protocol::{Word, ZERO}; use miden_standards::code_builder::CodeBuilder; use miden_tx::{ LocalTransactionProver, @@ -56,15 +56,12 @@ pub fn get_note_with_fungible_asset_and_script( fungible_asset: FungibleAsset, note_script: &str, ) -> Note { - use miden_protocol::note::NoteExecutionHint; - let note_script = CodeBuilder::default().compile_note_script(note_script).unwrap(); let serial_num = Word::from([1, 2, 3, 4u32]); let sender_id = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap(); let vault = NoteAssets::new(vec![fungible_asset.into()]).unwrap(); - let metadata = - NoteMetadata::new(sender_id, NoteType::Public, 1.into(), NoteExecutionHint::Always, ZERO); + let metadata = NoteMetadata::new(sender_id, NoteType::Public, 1.into()); let inputs = NoteInputs::new(vec![]).unwrap(); let recipient = NoteRecipient::new(serial_num, note_script, inputs); diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index d6edecea48..e842eff371 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -120,13 +120,7 @@ pub fn verify_minted_output_note( assert_eq!(output_note.id(), id); assert_eq!( output_note.metadata(), - &NoteMetadata::new( - faucet.id(), - params.note_type, - params.tag, - params.note_execution_hint, - params.aux - ) + &NoteMetadata::new(faucet.id(), params.note_type, params.tag) ); Ok(()) @@ -314,8 +308,8 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let recipient_account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER)?; let amount = Felt::new(75); let tag = NoteTag::default(); - let aux = Felt::new(27); - let note_execution_hint = NoteExecutionHint::on_block_slot(5, 6, 7); + // TODO(note_attachment): Replace with attachment. + // let aux = Felt::new(27); let note_type = NoteType::Public; // Create a simple output note script @@ -346,7 +340,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let output_script_root = note_recipient.script().root(); let asset = FungibleAsset::new(faucet.id(), amount.into())?; - let metadata = NoteMetadata::new(faucet.id(), note_type, tag, note_execution_hint, aux); + let metadata = NoteMetadata::new(faucet.id(), note_type, tag); let expected_note = Note::new(NoteAssets::new(vec![asset.into()])?, metadata, note_recipient); let trigger_note_script_code = format!( @@ -377,9 +371,9 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul # => [RECIPIENT] # Now call distribute with the computed recipient - push.{note_execution_hint} + push.0 # note_execution_hint push.{note_type} - push.{aux} + push.0 # aux push.{tag} push.{amount} # => [amount, tag, aux, note_type, execution_hint, RECIPIENT] @@ -401,9 +395,7 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul input6 = note_inputs.values()[6], script_root = output_script_root, serial_num = serial_num, - aux = aux, tag = u32::from(tag), - note_execution_hint = Felt::from(note_execution_hint), amount = amount, ); diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index 84d8c02934..b272d1f963 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -7,7 +7,6 @@ use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -36,13 +35,7 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let sender_account_interface = AccountInterface::from_account(&sender_basic_wallet_account); let tag = NoteTag::with_account_target(sender_basic_wallet_account.id()); - let metadata = NoteMetadata::new( - sender_basic_wallet_account.id(), - NoteType::Public, - tag, - NoteExecutionHint::always(), - Default::default(), - ); + let metadata = NoteMetadata::new(sender_basic_wallet_account.id(), NoteType::Public, tag); let assets = NoteAssets::new(vec![sent_asset]).unwrap(); let note_script = CodeBuilder::default().compile_note_script("begin nop end").unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); @@ -96,13 +89,8 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { AccountInterface::from_account(&sender_basic_fungible_faucet_account); let tag = NoteTag::with_account_target(sender_basic_fungible_faucet_account.id()); - let metadata = NoteMetadata::new( - sender_basic_fungible_faucet_account.id(), - NoteType::Public, - tag, - NoteExecutionHint::always(), - Default::default(), - ); + let metadata = + NoteMetadata::new(sender_basic_fungible_faucet_account.id(), NoteType::Public, tag); let assets = NoteAssets::new(vec![Asset::Fungible( FungibleAsset::new(sender_basic_fungible_faucet_account.id(), 10).unwrap(), )])?; diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index 777adb0a69..157da3d039 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -1,15 +1,7 @@ use anyhow::Context; use miden_protocol::account::{Account, AccountId, AccountStorageMode, AccountType}; use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset}; -use miden_protocol::note::{ - Note, - NoteAssets, - NoteDetails, - NoteExecutionHint, - NoteMetadata, - NoteTag, - NoteType, -}; +use miden_protocol::note::{Note, NoteAssets, NoteDetails, NoteMetadata, NoteTag, NoteType}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, @@ -138,7 +130,7 @@ async fn consume_swap_note_private_payback_note() -> anyhow::Result<()> { let full_payback_note = Note::new( payback_note.assets().clone(), - *output_payback_note.metadata(), + output_payback_note.metadata().clone(), payback_note.recipient().clone(), ); @@ -217,7 +209,7 @@ async fn consume_swap_note_public_payback_note() -> anyhow::Result<()> { let full_payback_note = Note::new( payback_note.assets().clone(), - *output_payback_note.metadata(), + output_payback_note.metadata().clone(), payback_note.recipient().clone(), ); @@ -353,14 +345,15 @@ pub fn create_p2id_note_exact( target: AccountId, assets: Vec, note_type: NoteType, - aux: Felt, + // TODO(note_attachment): Replace with attachment. + _aux: Felt, serial_num: Word, ) -> Result { let recipient = utils::build_p2id_recipient(target, serial_num)?; let tag = NoteTag::with_account_target(target); - let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux); + let metadata = NoteMetadata::new(sender, note_type, tag); let vault = NoteAssets::new(assets)?; Ok(Note::new(vault, metadata, recipient)) diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 5be724e9a2..2ea4c2f16a 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -226,8 +226,6 @@ pub enum TransactionKernelError { "note inputs data extracted from the advice map by the event handler is not well formed" )] MalformedNoteInputs(#[source] NoteError), - #[error("note metadata created by the event handler is not well formed")] - MalformedNoteMetadata(#[source] NoteError), #[error( "note script data `{data:?}` extracted from the advice map by the event handler is not well formed" )] diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 7f4fa78114..3cd6412eb4 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -565,7 +565,7 @@ where self.base_host.on_account_push_procedure_index(code_commitment, procedure_root) }, - TransactionEvent::NoteAfterCreated { note_idx, metadata, recipient_data } => { + TransactionEvent::NoteBeforeCreated { note_idx, metadata, recipient_data } => { match recipient_data { RecipientData::Digest(recipient_digest) => { self.base_host.output_note_from_recipient_digest( diff --git a/crates/miden-tx/src/host/kernel_process.rs b/crates/miden-tx/src/host/kernel_process.rs index 50dbb83840..5d948de35a 100644 --- a/crates/miden-tx/src/host/kernel_process.rs +++ b/crates/miden-tx/src/host/kernel_process.rs @@ -10,6 +10,7 @@ use miden_protocol::transaction::memory::{ ACCT_STORAGE_SLOT_VALUE_OFFSET, ACTIVE_INPUT_NOTE_PTR, NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, + NUM_OUTPUT_NOTES_PTR, }; use miden_protocol::{Hasher, Word}; @@ -31,6 +32,9 @@ pub(super) trait TransactionKernelProcess { #[allow(dead_code)] fn get_num_storage_slots(&self) -> Result; + /// Returns the current number of output notes. + fn get_num_output_notes(&self) -> u64; + fn get_vault_root(&self, vault_root_ptr: Felt) -> Result; fn get_active_note_id(&self) -> Result, TransactionKernelError>; @@ -129,6 +133,14 @@ impl<'a> TransactionKernelProcess for ProcessState<'a> { Ok(num_storage_slots_felt.as_int()) } + fn get_num_output_notes(&self) -> u64 { + // Read the number from memory or default to 0 if the location hasn't been accessed + // previously (e.g. when no notes have been created yet). + self.get_mem_value(self.ctx(), NUM_OUTPUT_NOTES_PTR) + .map(|num_output_notes| num_output_notes.as_int()) + .unwrap_or(0) + } + /// Returns the ID of the active note, or None if the note execution hasn't started yet or has /// already ended. /// diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index ea7807ecf6..3515f92e32 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -3,7 +3,15 @@ use alloc::vec::Vec; use miden_processor::{AdviceMutation, ProcessState, RowIndex}; use miden_protocol::account::{AccountId, StorageMap, StorageSlotName, StorageSlotType}; use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; -use miden_protocol::note::{NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript}; +use miden_protocol::note::{ + NoteId, + NoteInputs, + NoteMetadata, + NoteRecipient, + NoteScript, + NoteTag, + NoteType, +}; use miden_protocol::transaction::{TransactionEventId, TransactionSummary}; use miden_protocol::vm::EventId; use miden_protocol::{Felt, Hasher, Word}; @@ -97,7 +105,7 @@ pub(crate) enum TransactionEvent { procedure_root: Word, }, - NoteAfterCreated { + NoteBeforeCreated { /// The note index extracted from the stack. note_idx: usize, /// The note metadata extracted from the stack. @@ -325,16 +333,16 @@ impl TransactionEvent { }) }, - TransactionEventId::NoteBeforeCreated => None, + TransactionEventId::NoteBeforeCreated => { + // Expected stack state: [event, tag, note_type, RECIPIENT] + let tag = process.get_stack_item(1); + let note_type = process.get_stack_item(2); + let recipient_digest = process.get_stack_word_be(3); - TransactionEventId::NoteAfterCreated => { - // Expected stack state: [event, NOTE_METADATA, note_ptr, RECIPIENT, note_idx] - let metadata_word = process.get_stack_word_be(1); - let metadata = NoteMetadata::try_from(metadata_word) - .map_err(TransactionKernelError::MalformedNoteMetadata)?; + let sender = base_host.native_account_id(); + let metadata = build_note_metadata(sender, note_type, tag)?; - let recipient_digest = process.get_stack_word_be(6); - let note_idx = process.get_stack_item(10).as_int() as usize; + let note_idx = process.get_num_output_notes() as usize; // try to read the full recipient from the advice provider let recipient_data = if process.has_advice_map_entry(recipient_digest) { @@ -379,9 +387,11 @@ impl TransactionEvent { RecipientData::Digest(recipient_digest) }; - Some(TransactionEvent::NoteAfterCreated { note_idx, metadata, recipient_data }) + Some(TransactionEvent::NoteBeforeCreated { note_idx, metadata, recipient_data }) }, + TransactionEventId::NoteAfterCreated => None, + TransactionEventId::NoteBeforeAddAsset => { // Expected stack state: [event, ASSET, note_ptr, num_of_assets, note_idx] let note_idx = process.get_stack_item(7).as_int() as usize; @@ -658,6 +668,30 @@ fn extract_tx_summary<'store, STORE>( // HELPER FUNCTIONS // ================================================================================================ +/// Builds the note metadata from sender, note type and tag if all inputs are valid. +fn build_note_metadata( + sender: AccountId, + note_type: Felt, + tag: Felt, +) -> Result { + let note_type = u8::try_from(note_type) + .map_err(|_| TransactionKernelError::other("failed to decode note_type into u8")) + .and_then(|note_type_byte| { + NoteType::try_from(note_type_byte).map_err(|source| { + TransactionKernelError::other_with_source( + "failed to decode note_type from u8", + source, + ) + }) + })?; + + let tag = u32::try_from(tag) + .map_err(|_| TransactionKernelError::other("failed to decode note tag into u32")) + .map(NoteTag::new)?; + + Ok(NoteMetadata::new(sender, note_type, tag)) +} + /// Extracts a word from a slice of field elements. #[inline(always)] fn extract_word(commitments: &[Felt], start: usize) -> Word { diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index de31f51da4..470a1195cb 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -140,7 +140,7 @@ where self.base_host.on_account_push_procedure_index(code_commitment, procedure_root) }, - TransactionEvent::NoteAfterCreated { note_idx, metadata, recipient_data } => { + TransactionEvent::NoteBeforeCreated { note_idx, metadata, recipient_data } => { match recipient_data { RecipientData::Digest(recipient_digest) => self .base_host From de42e844bf6f99a184caf76ff3affaafc082ac93 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 15 Jan 2026 09:07:43 +0100 Subject: [PATCH 099/114] feat: add `output_not_set_attachment` kernel API (#2252) * feat: Implement `output_note_set_attachment` kernel API * feat: Implement `miden::protocol` set_attachment wrappers * feat: Emit event during `set_attachment` for tx host * chore: add tests for `set_attachment` APIs * chore: regenerate kernel procedure hashes * chore: add changelog * chore: rename `Raw` -> `Word` and `Commitment` -> `Array` * fix: renamed attachment content variants * chore: make attachment content type constants public * chore: move note ptr to idx conversion to helper * chore: merge attachment validation procedures into one * chore: simplify setting output note attachment type info * chore: make `set_attachment` public * fix: commitment to array rename * chore: require attachment_type == 0 when content type is None * chore: enforce none / attachment type restriction in `NoteAttachment` --- CHANGELOG.md | 1 + .../asm/kernels/transaction/api.masm | 27 +++ .../asm/kernels/transaction/lib/memory.masm | 14 ++ .../kernels/transaction/lib/output_note.masm | 144 ++++++++++++++ .../asm/protocol/kernel_proc_offsets.masm | 36 ++-- .../asm/protocol/output_note.masm | 97 ++++++++++ crates/miden-protocol/src/errors/mod.rs | 2 + crates/miden-protocol/src/errors/tx_kernel.rs | 8 + crates/miden-protocol/src/note/attachment.rs | 26 ++- .../src/transaction/kernel/procedures.rs | 4 +- .../src/transaction/kernel/tx_event_id.rs | 4 + crates/miden-standards/src/testing/note.rs | 12 +- .../src/kernel_tests/tx/test_output_note.rs | 180 ++++++++++++++++++ crates/miden-tx/src/errors/mod.rs | 6 + crates/miden-tx/src/executor/exec_host.rs | 5 + crates/miden-tx/src/host/mod.rs | 17 +- crates/miden-tx/src/host/note_builder.rs | 14 +- crates/miden-tx/src/host/tx_event.rs | 119 +++++++++++- crates/miden-tx/src/prover/prover_host.rs | 5 + 19 files changed, 697 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 505965b8ba..5d8677c7d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Add `AccountId::parse()` helper function to parse both hex and bech32 formats ([#2223](https://github.com/0xMiden/miden-base/pull/2223)). - Add `read_foreign_account_inputs()`, `read_vault_asset_witnesses()`, and `read_storage_map_witness()` for `TransactionInputs` ([#2246](https://github.com/0xMiden/miden-base/pull/2246)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249)). +- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252)). ### Changes diff --git a/crates/miden-protocol/asm/kernels/transaction/api.masm b/crates/miden-protocol/asm/kernels/transaction/api.masm index 8d12410b65..a6643f5ddb 100644 --- a/crates/miden-protocol/asm/kernels/transaction/api.masm +++ b/crates/miden-protocol/asm/kernels/transaction/api.masm @@ -1189,6 +1189,33 @@ pub proc output_note_add_asset # => [pad(16)] end +#! Sets the attachment of the note specified by the index. +#! +#! Inputs: [note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(9)] +#! Outputs: [pad(16)] +#! +#! Where: +#! - note_idx is the index of the note on which the attachment is set. +#! - attachment_content_type is the content type of the attachment. +#! - attachment_type is the user-defined type of the attachment. +#! - ATTACHMENT is the attachment to be set. +#! +#! Panics if: +#! - the procedure is called when the active account is not the native one. +#! - the note index points to a non-existent output note. +#! - any of the attachment types does not fit into a u32. +#! - the attachment content type is an unknown variant. +#! +#! Invocation: dynexec +pub proc output_note_set_attachment + # check that this procedure was executed against the native account + exec.memory::assert_native_account + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(9)] + + exec.output_note::set_attachment + # => [pad(16)] +end + #! Returns the information about assets in the output note with the specified index. #! #! Inputs: [note_index, pad(15)] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm index b1ccb974e3..78213758af 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm @@ -240,6 +240,7 @@ const OUTPUT_NOTE_SECTION_OFFSET=16777216 # The offsets at which data of an output note is stored relative to the start of its data segment. const OUTPUT_NOTE_ID_OFFSET=0 const OUTPUT_NOTE_METADATA_HEADER_OFFSET=4 +const OUTPUT_NOTE_METADATA_ATTACHMENT_TYPE_INFO_OFFSET=OUTPUT_NOTE_METADATA_HEADER_OFFSET + 3 const OUTPUT_NOTE_ATTACHMENT_OFFSET=8 const OUTPUT_NOTE_RECIPIENT_OFFSET=12 const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=16 @@ -1891,6 +1892,19 @@ pub proc set_output_note_metadata_header mem_storew_be end +#! Sets the output note's attachment type info in the metadata header. +#! +#! Inputs: [note_ptr, attachment_type_info] +#! Outputs: [] +#! +#! Where: +#! - attachment_type_info is the type information of the attachment that will be overwritten. +#! - note_ptr is the memory address at which the output note data begins. +pub proc set_output_note_attachment_type_info + add.OUTPUT_NOTE_METADATA_ATTACHMENT_TYPE_INFO_OFFSET + mem_store +end + #! Returns the output note's attachment. #! #! Inputs: [note_ptr] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index f122f079b7..91c75a6900 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -3,6 +3,7 @@ use $kernel::memory use $kernel::note use $kernel::asset use $kernel::constants::MAX_OUTPUT_NOTES_PER_TX +use miden::core::mem use miden::core::word # CONSTANTS @@ -23,6 +24,9 @@ const ATTACHMENT_CONTENT_TYPE_ARRAY=2 # "untyped". const ATTACHMENT_DEFAULT_TYPE_INFO=0 +#! The default attachment type, representing an untyped attachment. +const ATTACHMENT_TYPE_UNTYPED=0 + # ERRORS # ================================================================================================= @@ -32,6 +36,14 @@ const ERR_NOTE_INVALID_TYPE="invalid note type" const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested output note index should be less than the total number of created output notes" +const ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_TYPES="attachment types must fit into u32s" + +const ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_CONTENT_TYPE="attachment content type variant must be between 0 and 2" + +const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_HAVE_UNTYPED_ATTACHMENT_TYPE="attachment type of content type none must be 0" + +const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_BE_EMPTY_WORD="attachment of content type none must be set to an empty word" + const ERR_NOTE_INVALID_INDEX="failed to find note at the given index; index must be within [0, num_of_notes]" const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807" @@ -53,6 +65,9 @@ const NOTE_BEFORE_ADD_ASSET_EVENT=event("miden::note::before_add_asset") # Event emitted after an ASSET is added to a note const NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset") +# Event emitted before an ATTACHMENT is added to a note +const NOTE_BEFORE_SET_ATTACHMENT_EVENT=event("miden::note::before_set_attachment") + # OUTPUT NOTE PROCEDURES # ================================================================================================= @@ -185,6 +200,7 @@ end #! - ASSET can be a fungible or non-fungible asset. #! #! Panics if: +#! - the note index points to a non-existent output note. #! - the ASSET is malformed (e.g., invalid faucet ID). #! - the max amount of fungible assets is exceeded. #! - the non-fungible asset already exists in the note. @@ -239,6 +255,51 @@ pub proc add_asset # => [] end +#! Sets the attachment of the note specified by the index. +#! +#! Inputs: [note_idx, attachment_content_type, attachment_type, ATTACHMENT] +#! Outputs: [] +#! +#! Where: +#! - note_idx is the index of the note on which the attachment is set. +#! - attachment_content_type is the content type of the attachment. +#! - attachment_type is the user-defined type of the attachment. +#! - ATTACHMENT is the attachment to be set. +#! +#! Panics if: +#! - the note index points to a non-existent output note. +#! - any of the attachment types does not fit into a u32. +#! - the attachment content type is an unknown variant. +pub proc set_attachment + dup exec.memory::get_num_output_notes lte assert.err=ERR_NOTE_INVALID_INDEX + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + + exec.memory::get_output_note_ptr dup + # => [note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT] + + dupw.1 + # => [ATTACHMENT, note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT] + + dup.7 dup.7 + # => [attachment_content_type, attachment_type, ATTACHMENT, note_ptr, note_ptr, + # attachment_content_type, attachment_type, ATTACHMENT] + + exec.validate_attachment + # => [note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT] + + movdn.3 movdn.3 + # => [attachment_content_type, attachment_type, note_ptr, note_ptr, ATTACHMENT] + + emit.NOTE_BEFORE_SET_ATTACHMENT_EVENT + # => [attachment_content_type, attachment_type, note_ptr, note_ptr, ATTACHMENT] + + exec.set_attachment_type_info + # => [note_ptr, ATTACHMENT] + + exec.memory::set_output_note_attachment + # => [] +end + #! Assert that the provided note index is less than the total number of output notes. #! #! Inputs: [note_index] @@ -306,6 +367,89 @@ pub proc build_metadata_header # => [NOTE_METADATA_HEADER] end +#! Validate the ATTACHMENT against the content type. +#! +#! Inputs: [attachment_content_type, attachment_type, ATTACHMENT] +#! Outputs: [] +#! +#! Where: +#! - attachment_type is the user-defined type of the attachment. +#! - attachment_content_type is the content type of the attachment. +#! - ATTACHMENT is the attachment to validate. +#! +#! Panics if: +#! - any of the attachment types does not fit into a u32. +#! - the attachment content type is an unknown variant. +#! - the content type is None and the ATTACHMENT is not an empty word. +proc validate_attachment + u32assert2.err=ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_TYPES + # => [attachment_content_type, attachment_type, ATTACHMENT] + + # assert that the attachment content type is valid + dup u32lte.ATTACHMENT_CONTENT_TYPE_ARRAY + assert.err=ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_CONTENT_TYPE + # => [attachment_content_type, attachment_type, ATTACHMENT] + + eq.ATTACHMENT_CONTENT_TYPE_NONE + # => [is_attachment_none, attachment_type, ATTACHMENT] + + if.true + eq.ATTACHMENT_TYPE_UNTYPED + assert.err=ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_HAVE_UNTYPED_ATTACHMENT_TYPE + # => [ATTACHMENT] + + padw assert_eqw.err=ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_BE_EMPTY_WORD + # => [] + else + drop dropw + # => [] + end + # => [] +end + +#! Sets an output note's attachment type info in the metadata header. +#! +#! WARNING: The attachment types must be valid. +#! +#! Inputs: [attachment_content_type, attachment_type, note_ptr] +#! Outputs: [] +#! +#! Where: +#! - attachment_content_type is the content type of the attachment. +#! - attachment_type is the user-defined type of the attachment. +#! - note_ptr is the memory address at which the output note data begins. +proc set_attachment_type_info + exec.merge_attachment_type_info + # => [attachment_type_info, note_ptr] + + swap + # => [note_ptr, attachment_type_info] + + exec.memory::set_output_note_attachment_type_info + # => [] +end + +#! Merges the attachment types into a single felt with the following layout: +#! +#! [30 zero bits | attachment_content_type (2 bits) | attachment_type (32 bits)] +#! +#! WARNING: The attachment types must be valid. +#! +#! Inputs: [attachment_content_type, attachment_type] +#! Outputs: [attachment_type_info] +#! +#! Where: +#! - attachment_content_type is the content type of the attachment. +#! - attachment_type is the user-defined type of the attachment. +#! - attachment_type_info is the felt constructed from the inputs. +proc merge_attachment_type_info + # shift the content type 32 bits to the left, which is the same as multiplying by 2^32 + # and set the lower bits to the attachment_type, which is done by adding the values together + mul.0x100000000 + add + # => [attachment_type_info] +end + #! Increments the number of output notes by one. Returns the index of the next note to be created. #! #! Inputs: [] diff --git a/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm b/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm index be593b3ec1..ae698f2df7 100644 --- a/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm +++ b/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm @@ -67,29 +67,30 @@ const OUTPUT_NOTE_GET_METADATA_OFFSET=37 const OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET=38 const OUTPUT_NOTE_GET_RECIPIENT_OFFSET=39 const OUTPUT_NOTE_ADD_ASSET_OFFSET=40 +const OUTPUT_NOTE_SET_ATTACHMENT_OFFSET=41 ### Tx ########################################## # input notes -const TX_GET_NUM_INPUT_NOTES_OFFSET=41 -const TX_GET_INPUT_NOTES_COMMITMENT_OFFSET=42 +const TX_GET_NUM_INPUT_NOTES_OFFSET=42 +const TX_GET_INPUT_NOTES_COMMITMENT_OFFSET=43 # output notes -const TX_GET_NUM_OUTPUT_NOTES_OFFSET=43 -const TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET=44 +const TX_GET_NUM_OUTPUT_NOTES_OFFSET=44 +const TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET=45 # block info -const TX_GET_BLOCK_COMMITMENT_OFFSET=45 -const TX_GET_BLOCK_NUMBER_OFFSET=46 -const TX_GET_BLOCK_TIMESTAMP_OFFSET=47 +const TX_GET_BLOCK_COMMITMENT_OFFSET=46 +const TX_GET_BLOCK_NUMBER_OFFSET=47 +const TX_GET_BLOCK_TIMESTAMP_OFFSET=48 # foreign context -const TX_START_FOREIGN_CONTEXT_OFFSET=48 -const TX_END_FOREIGN_CONTEXT_OFFSET=49 +const TX_START_FOREIGN_CONTEXT_OFFSET=49 +const TX_END_FOREIGN_CONTEXT_OFFSET=50 # expiration data -const TX_GET_EXPIRATION_DELTA_OFFSET=50 # accessor -const TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=51 # mutator +const TX_GET_EXPIRATION_DELTA_OFFSET=51 # accessor +const TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=52 # mutator # ACCESSORS # ------------------------------------------------------------------------------------------------- @@ -484,6 +485,19 @@ pub proc output_note_add_asset_offset push.OUTPUT_NOTE_ADD_ASSET_OFFSET end +#! Returns the offset of the `output_note_set_attachment` kernel procedure. +#! +#! Inputs: [] +#! Outputs: [proc_offset] +#! +#! Where: +#! - proc_offset is the offset of the `output_note_set_attachment` kernel procedure required to get +#! the address where this procedure is stored. +pub proc output_note_set_attachment_offset + push.OUTPUT_NOTE_SET_ATTACHMENT_OFFSET +end + + #! Returns the offset of the `output_note_get_assets_info` kernel procedure. #! #! Inputs: [] diff --git a/crates/miden-protocol/asm/protocol/output_note.masm b/crates/miden-protocol/asm/protocol/output_note.masm index 36ec079f43..d4f5356e39 100644 --- a/crates/miden-protocol/asm/protocol/output_note.masm +++ b/crates/miden-protocol/asm/protocol/output_note.masm @@ -1,6 +1,14 @@ use miden::protocol::kernel_proc_offsets use miden::protocol::note +# CONSTANTS +# ================================================================================================= + +# Constants for note attachment content types +pub const ATTACHMENT_CONTENT_TYPE_NONE=0 +pub const ATTACHMENT_CONTENT_TYPE_WORD=1 +pub const ATTACHMENT_CONTENT_TYPE_ARRAY=2 + # PROCEDURES # ================================================================================================= @@ -139,6 +147,95 @@ pub proc add_asset # => [] end +#! Sets the attachment of the note specified by the index. +#! +#! Inputs: [note_idx, attachment_content_type, attachment_type, ATTACHMENT] +#! Outputs: [] +#! +#! Where: +#! - note_idx is the index of the note on which the attachment is set. +#! - attachment_content_type is the content type of the attachment. +#! - attachment_type is the user-defined type of the attachment. +#! - ATTACHMENT is the attachment to be set. +#! +#! Panics if: +#! - the procedure is called when the active account is not the native one. +#! - the note index points to a non-existent output note. +#! - any of the attachment types does not fit into a u32. +#! - the attachment content type is an unknown variant. +#! +#! Invocation: exec +pub proc set_attachment + exec.kernel_proc_offsets::output_note_set_attachment_offset + # => [offset, note_idx, attachment_content_type, attachment_type, ATTACHMENT] + + # pad the stack before the syscall + padw padw swapdw + # => [offset, note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(8)] + + syscall.exec_kernel_proc + # => [pad(16)] + + # remove excess PADs from the stack + dropw dropw dropw dropw + # => [] +end + + + +#! Sets the attachment of the note specified by the note index to the provided word. +#! +#! This overwrites any previously set attachment. +#! +#! Inputs: [note_idx, attachment_type, ATTACHMENT] +#! Outputs: [] +#! +#! Where: +#! - note_idx is the index of the note on which the attachment is set. +#! - attachment_type is the user-defined type of the attachment. +#! - ATTACHMENT is the raw attachment to set. +#! +#! Panics if: +#! - the procedure is called when the active account is not the native one. +#! - the note index points to a non-existent output note. +#! - the attachment_type does not fit into a u32. +#! +#! Invocation: exec +pub proc set_word_attachment + push.ATTACHMENT_CONTENT_TYPE_WORD swap + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + + exec.set_attachment + # => [] +end + +#! Sets the attachment of the note specified by the note index to the provided ATTACHMENT which +#! commits to an array of felts. +#! +#! This overwrites any previously set attachment. +#! +#! Inputs: [note_idx, attachment_type, ATTACHMENT] +#! Outputs: [] +#! +#! Where: +#! - note_idx is the index of the note on which the attachment is set. +#! - attachment_type is the user-defined type of the attachment. +#! - ATTACHMENT is the commitment of the set of elements that form the note attachment. +#! +#! Panics if: +#! - the procedure is called when the active account is not the native one. +#! - the note index points to a non-existent output note. +#! - the attachment_type does not fit into a u32. +#! +#! Invocation: exec +pub proc set_array_attachment + push.ATTACHMENT_CONTENT_TYPE_ARRAY swap + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + + exec.set_attachment + # => [] +end + #! Returns the recipient of the output note with the specified index. #! #! Inputs: [note_index] diff --git a/crates/miden-protocol/src/errors/mod.rs b/crates/miden-protocol/src/errors/mod.rs index 828f4c2430..2134272f37 100644 --- a/crates/miden-protocol/src/errors/mod.rs +++ b/crates/miden-protocol/src/errors/mod.rs @@ -600,6 +600,8 @@ pub enum NoteError { NoteAttachmentArraySizeExceeded(usize), #[error("unknown note attachment content type {0}")] UnknownNoteAttachmentContentType(u8), + #[error("note attachment of type None must have untyped attachment type")] + NoneAttachmentMustHaveUntypedAttachmentType, #[error("{error_msg}")] Other { error_msg: Box, diff --git a/crates/miden-protocol/src/errors/tx_kernel.rs b/crates/miden-protocol/src/errors/tx_kernel.rs index 9aaad06249..05846fb567 100644 --- a/crates/miden-protocol/src/errors/tx_kernel.rs +++ b/crates/miden-protocol/src/errors/tx_kernel.rs @@ -163,8 +163,16 @@ pub const ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT: MasmError = MasmError::from_stati /// Error Message: "the note's tag must fit into a u32 so the 32 most significant bits of the felt must be zero" pub const ERR_NOTE_TAG_MUST_BE_U32: MasmError = MasmError::from_static_str("the note's tag must fit into a u32 so the 32 most significant bits of the felt must be zero"); +/// Error Message: "attachment of content type none must be set to an empty word" +pub const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_BE_EMPTY_WORD: MasmError = MasmError::from_static_str("attachment of content type none must be set to an empty word"); +/// Error Message: "attachment type of content type none must be 0" +pub const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_HAVE_UNTYPED_ATTACHMENT_TYPE: MasmError = MasmError::from_static_str("attachment type of content type none must be 0"); /// Error Message: "requested output note index should be less than the total number of created output notes" pub const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("requested output note index should be less than the total number of created output notes"); +/// Error Message: "attachment types must fit into u32s" +pub const ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_TYPES: MasmError = MasmError::from_static_str("attachment types must fit into u32s"); +/// Error Message: "attachment content type variant must be between 0 and 2" +pub const ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_CONTENT_TYPE: MasmError = MasmError::from_static_str("attachment content type variant must be between 0 and 2"); /// Error Message: "existing accounts must have a non-zero nonce" pub const ERR_PROLOGUE_EXISTING_ACCOUNT_MUST_HAVE_NON_ZERO_NONCE: MasmError = MasmError::from_static_str("existing accounts must have a non-zero nonce"); diff --git a/crates/miden-protocol/src/note/attachment.rs b/crates/miden-protocol/src/note/attachment.rs index 9f67ce92b5..11d50c6542 100644 --- a/crates/miden-protocol/src/note/attachment.rs +++ b/crates/miden-protocol/src/note/attachment.rs @@ -44,14 +44,24 @@ impl NoteAttachment { // -------------------------------------------------------------------------------------------- /// Creates a new [`NoteAttachment`] from a user-defined type and the provided content. - pub fn new(attachment_type: NoteAttachmentType, content: NoteAttachmentContent) -> Self { - Self { attachment_type, content } + pub fn new( + attachment_type: NoteAttachmentType, + content: NoteAttachmentContent, + ) -> Result { + if content.content_type().is_none() && !attachment_type.is_untyped() { + return Err(NoteError::NoneAttachmentMustHaveUntypedAttachmentType); + } + + Ok(Self { attachment_type, content }) } /// Creates a new note attachment with content [`NoteAttachmentContent::Word`] from the provided /// word. pub fn new_word(attachment_type: NoteAttachmentType, word: Word) -> Self { - Self::new(attachment_type, NoteAttachmentContent::new_word(word)) + Self { + attachment_type, + content: NoteAttachmentContent::new_word(word), + } } /// Creates a new note attachment with content [`NoteAttachmentContent::Array`] from the @@ -65,8 +75,7 @@ impl NoteAttachment { attachment_type: NoteAttachmentType, elements: Vec, ) -> Result { - NoteAttachmentContent::new_array(elements) - .map(|content| Self::new(attachment_type, content)) + NoteAttachmentContent::new_array(elements).map(|content| Self { attachment_type, content }) } /// Creates a new [`NoteAttachment`] from the provided content and using @@ -104,7 +113,8 @@ impl Deserializable for NoteAttachment { let attachment_type = NoteAttachmentType::read_from(source)?; let content = NoteAttachmentContent::read_from(source)?; - Ok(Self::new(attachment_type, content)) + Self::new(attachment_type, content) + .map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } @@ -290,8 +300,8 @@ impl SequentialCommit for NoteAttachmentArray { } impl From for NoteAttachmentContent { - fn from(attachment_commitment: NoteAttachmentArray) -> Self { - NoteAttachmentContent::Array(attachment_commitment) + fn from(array: NoteAttachmentArray) -> Self { + NoteAttachmentContent::Array(array) } } diff --git a/crates/miden-protocol/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs index 31cf42e8e8..0e028eaafe 100644 --- a/crates/miden-protocol/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -6,7 +6,7 @@ use crate::{Word, word}; // ================================================================================================ /// Hashes of all dynamically executed kernel procedures. -pub const KERNEL_PROCEDURES: [Word; 52] = [ +pub const KERNEL_PROCEDURES: [Word; 53] = [ // account_get_initial_commitment word!("0x45c5a29a7420ecd4394c8cffe822f31781cb0a5e30aa2aa179d143df5710f23e"), // account_compute_commitment @@ -89,6 +89,8 @@ pub const KERNEL_PROCEDURES: [Word; 52] = [ word!("0x1ce137f0c5be72832970e6c818968a789f65b97db34515bfebb767705f28db67"), // output_note_add_asset word!("0xaf22383e4390f4f15a429768f79aa445f8a535bb21b0807172b9ef2de063d9d1"), + // output_note_set_attachment + word!("0xf832b327839619a98ee3992c9abf1cec0ede01c3a25019b6874bc27bc4614405"), // tx_get_num_input_notes word!("0xfcc186d4b65c584f3126dda1460b01eef977efd76f9e36f972554af28e33c685"), // tx_get_input_notes_commitment diff --git a/crates/miden-protocol/src/transaction/kernel/tx_event_id.rs b/crates/miden-protocol/src/transaction/kernel/tx_event_id.rs index da233ae3ef..1b6373bbf4 100644 --- a/crates/miden-protocol/src/transaction/kernel/tx_event_id.rs +++ b/crates/miden-protocol/src/transaction/kernel/tx_event_id.rs @@ -51,6 +51,8 @@ pub enum TransactionEventId { NoteBeforeAddAsset = NOTE_BEFORE_ADD_ASSET, NoteAfterAddAsset = NOTE_AFTER_ADD_ASSET, + NoteBeforeSetAttachment = NOTE_BEFORE_SET_ATTACHMENT, + AuthRequest = AUTH_REQUEST, PrologueStart = PROLOGUE_START, @@ -154,6 +156,8 @@ impl TryFrom for TransactionEventId { NOTE_BEFORE_ADD_ASSET => Ok(TransactionEventId::NoteBeforeAddAsset), NOTE_AFTER_ADD_ASSET => Ok(TransactionEventId::NoteAfterAddAsset), + NOTE_BEFORE_SET_ATTACHMENT => Ok(TransactionEventId::NoteBeforeSetAttachment), + AUTH_REQUEST => Ok(TransactionEventId::AuthRequest), PROLOGUE_START => Ok(TransactionEventId::PrologueStart), diff --git a/crates/miden-standards/src/testing/note.rs b/crates/miden-standards/src/testing/note.rs index 185fa9f762..b7a14c99b8 100644 --- a/crates/miden-standards/src/testing/note.rs +++ b/crates/miden-standards/src/testing/note.rs @@ -9,6 +9,7 @@ use miden_protocol::asset::Asset; use miden_protocol::note::{ Note, NoteAssets, + NoteAttachment, NoteExecutionHint, NoteInputs, NoteMetadata, @@ -38,6 +39,7 @@ pub struct NoteBuilder { code: String, // TODO(note_attachment): Remove. aux: Felt, + attachment: NoteAttachment, dyn_libraries: Vec, source_manager: Arc, } @@ -62,6 +64,7 @@ impl NoteBuilder { tag: NoteTag::with_account_target(sender), code: DEFAULT_NOTE_CODE.to_string(), aux: ZERO, + attachment: NoteAttachment::default(), dyn_libraries: Vec::new(), source_manager: Arc::new(DefaultSourceManager::default()), } @@ -115,6 +118,12 @@ impl NoteBuilder { self } + /// Overwrites the attachment. + pub fn attachment(mut self, attachment: NoteAttachment) -> Self { + self.attachment = attachment; + self + } + /// Extends the set of dynamically linked libraries that are passed to the assembler at /// build-time. pub fn dynamically_linked_libraries( @@ -156,7 +165,8 @@ impl NoteBuilder { .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, self.tag); + let metadata = NoteMetadata::new(self.sender, self.note_type, self.tag) + .with_attachment(self.attachment); let inputs = NoteInputs::new(self.inputs)?; let recipient = NoteRecipient::new(self.serial_num, note_script, inputs); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index f9a430920c..5335c63d29 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -12,6 +12,8 @@ use miden_protocol::errors::tx_kernel::{ use miden_protocol::note::{ Note, NoteAssets, + NoteAttachment, + NoteAttachmentType, NoteExecutionHint, NoteInputs, NoteMetadata, @@ -45,6 +47,7 @@ use miden_protocol::{Felt, Word, ZERO}; use miden_standards::code_builder::CodeBuilder; use miden_standards::note::create_p2id_note; use miden_standards::testing::mock_account::MockAccountExt; +use miden_standards::testing::note::NoteBuilder; use super::{TestSetup, setup_test}; use crate::kernel_tests::tx::ExecutionOutputExt; @@ -1031,6 +1034,183 @@ async fn test_get_assets() -> anyhow::Result<()> { Ok(()) } +#[tokio::test] +async fn test_set_none_attachment() -> anyhow::Result<()> { + let account = Account::mock(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, Auth::IncrNonce); + let rng = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])); + let attachment = NoteAttachment::default(); + let output_note = + OutputNote::Full(NoteBuilder::new(account.id(), rng).attachment(attachment).build()?); + + let tx_script = format!( + " + use miden::protocol::output_note + + begin + push.{RECIPIENT} + push.{note_execution_hint} + push.{note_type} + push.{aux} + push.{tag} + exec.output_note::create + # => [note_idx] + + push.{ATTACHMENT} + push.{attachment_type} + push.{attachment_content_type} + movup.6 + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + exec.output_note::set_attachment + # => [] + + # truncate the stack + swapdw dropw dropw + end + ", + RECIPIENT = output_note.recipient().unwrap().digest(), + note_type = output_note.metadata().note_type() as u8, + aux = output_note.metadata().aux(), + note_execution_hint = Felt::from(output_note.metadata().execution_hint()), + tag = output_note.metadata().tag().as_u32(), + ATTACHMENT = output_note.metadata().to_attachment_word(), + attachment_content_type = + output_note.metadata().attachment().content().content_type().as_u8(), + attachment_type = output_note.metadata().attachment().attachment_type().as_u32(), + ); + + let tx_script = CodeBuilder::new().compile_tx_script(tx_script)?; + + let tx = TransactionContextBuilder::new(account) + .extend_expected_output_notes(vec![output_note.clone()]) + .tx_script(tx_script) + .build()? + .execute() + .await?; + + let actual_note = tx.output_notes().get_note(0); + assert_eq!(actual_note.header(), output_note.header()); + assert_eq!(actual_note.assets(), output_note.assets()); + + Ok(()) +} + +#[tokio::test] +async fn test_set_word_attachment() -> anyhow::Result<()> { + let account = Account::mock(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, Auth::IncrNonce); + let rng = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])); + let attachment = + NoteAttachment::new_word(NoteAttachmentType::new(u32::MAX), Word::from([3, 4, 5, 6u32])); + let output_note = + OutputNote::Full(NoteBuilder::new(account.id(), rng).attachment(attachment).build()?); + + let tx_script = format!( + " + use miden::protocol::output_note + + begin + push.{RECIPIENT} + push.{note_execution_hint} + push.{note_type} + push.{aux} + push.{tag} + exec.output_note::create + # => [note_idx] + + push.{ATTACHMENT} + push.{attachment_type} + movup.5 + # => [note_idx, attachment_type, ATTACHMENT] + exec.output_note::set_word_attachment + # => [] + + # truncate the stack + swapdw dropw dropw + end + ", + RECIPIENT = output_note.recipient().unwrap().digest(), + note_type = output_note.metadata().note_type() as u8, + aux = output_note.metadata().aux(), + note_execution_hint = Felt::from(output_note.metadata().execution_hint()), + tag = output_note.metadata().tag().as_u32(), + attachment_type = output_note.metadata().attachment().attachment_type().as_u32(), + ATTACHMENT = output_note.metadata().to_attachment_word(), + ); + + let tx_script = CodeBuilder::new().compile_tx_script(tx_script)?; + + let tx = TransactionContextBuilder::new(account) + .extend_expected_output_notes(vec![output_note.clone()]) + .tx_script(tx_script) + .build()? + .execute() + .await?; + + let actual_note = tx.output_notes().get_note(0); + assert_eq!(actual_note.header(), output_note.header()); + assert_eq!(actual_note.assets(), output_note.assets()); + + Ok(()) +} + +#[tokio::test] +async fn test_set_array_attachment() -> anyhow::Result<()> { + let account = Account::mock(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, Auth::IncrNonce); + let rng = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])); + let elements = [3, 4, 5, 6, 7, 8, 9u32].map(Felt::from).to_vec(); + let attachment = NoteAttachment::new_array(NoteAttachmentType::new(42), elements.clone())?; + let output_note = + OutputNote::Full(NoteBuilder::new(account.id(), rng).attachment(attachment).build()?); + + let tx_script = format!( + " + use miden::protocol::output_note + + begin + push.{RECIPIENT} + push.{note_execution_hint} + push.{note_type} + push.{aux} + push.{tag} + exec.output_note::create + # => [note_idx] + + push.{ATTACHMENT} + push.{attachment_type} + movup.5 + # => [note_idx, attachment_type, ATTACHMENT] + exec.output_note::set_array_attachment + # => [] + + # truncate the stack + swapdw dropw dropw + end + ", + RECIPIENT = output_note.recipient().unwrap().digest(), + note_type = output_note.metadata().note_type() as u8, + aux = output_note.metadata().aux(), + note_execution_hint = Felt::from(output_note.metadata().execution_hint()), + tag = output_note.metadata().tag().as_u32(), + attachment_type = output_note.metadata().attachment().attachment_type().as_u32(), + ATTACHMENT = output_note.metadata().to_attachment_word(), + ); + + let tx_script = CodeBuilder::new().compile_tx_script(tx_script)?; + + let tx = TransactionContextBuilder::new(account) + .extend_expected_output_notes(vec![output_note.clone()]) + .tx_script(tx_script) + .extend_advice_map(vec![(output_note.metadata().to_attachment_word(), elements)]) + .build()? + .execute() + .await?; + + let actual_note = tx.output_notes().get_note(0); + assert_eq!(actual_note.header(), output_note.header()); + assert_eq!(actual_note.assets(), output_note.assets()); + + Ok(()) +} + // HELPER FUNCTIONS // ================================================================================================ diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 2ea4c2f16a..94300ea96d 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -241,6 +241,12 @@ pub enum TransactionKernelError { "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider" )] PublicNoteMissingDetails(NoteMetadata, Word), + #[error("attachment provided to set_attachment must be empty when content type is None")] + NoteAttachmentNoneIsNotEmpty, + #[error( + "commitment of note attachment {actual} does not match attachment {provided} provided to set_attachment" + )] + NoteAttachmentArrayMismatch { actual: Word, provided: Word }, #[error( "note input data in advice provider contains fewer elements ({actual}) than specified ({specified}) by its inputs length" )] diff --git a/crates/miden-tx/src/executor/exec_host.rs b/crates/miden-tx/src/executor/exec_host.rs index 3cd6412eb4..1679190c92 100644 --- a/crates/miden-tx/src/executor/exec_host.rs +++ b/crates/miden-tx/src/executor/exec_host.rs @@ -600,6 +600,11 @@ where self.base_host.on_note_before_add_asset(note_idx, asset) }, + TransactionEvent::NoteBeforeSetAttachment { note_idx, attachment } => self + .base_host + .on_note_before_set_attachment(note_idx, attachment) + .map(|_| Vec::new()), + TransactionEvent::AuthRequest { pub_key_hash, tx_summary, signature } => { if let Some(signature) = signature { Ok(self.base_host.on_auth_requested(signature)) diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index 56d3940848..019a3a0291 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -50,7 +50,7 @@ use miden_protocol::account::{ StorageSlotName, }; use miden_protocol::asset::Asset; -use miden_protocol::note::{NoteId, NoteMetadata, NoteRecipient}; +use miden_protocol::note::{NoteAttachment, NoteId, NoteMetadata, NoteRecipient}; use miden_protocol::transaction::{ InputNote, InputNotes, @@ -300,6 +300,21 @@ impl<'store, STORE> TransactionBaseHost<'store, STORE> { Ok(Vec::new()) } + /// Sets the attachment on the output note identified by the note index. + pub fn on_note_before_set_attachment( + &mut self, + note_idx: usize, + attachment: NoteAttachment, + ) -> Result, TransactionKernelError> { + let note_builder = self.output_notes.get_mut(¬e_idx).ok_or_else(|| { + TransactionKernelError::other(format!("failed to find output note {note_idx}")) + })?; + + note_builder.set_attachment(attachment); + + Ok(Vec::new()) + } + /// Pushes the index of the procedure root in the code identified by the commitment onto the /// advice stack. pub fn on_account_push_procedure_index( diff --git a/crates/miden-tx/src/host/note_builder.rs b/crates/miden-tx/src/host/note_builder.rs index 9e392c54ef..eac4f8a006 100644 --- a/crates/miden-tx/src/host/note_builder.rs +++ b/crates/miden-tx/src/host/note_builder.rs @@ -1,5 +1,12 @@ use miden_protocol::asset::Asset; -use miden_protocol::note::{Note, NoteAssets, NoteMetadata, NoteRecipient, PartialNote}; +use miden_protocol::note::{ + Note, + NoteAssets, + NoteAttachment, + NoteMetadata, + NoteRecipient, + PartialNote, +}; use super::{OutputNote, Word}; use crate::errors::TransactionKernelError; @@ -77,6 +84,11 @@ impl OutputNoteBuilder { Ok(()) } + /// Overwrites the attachment in the note's metadata. + pub fn set_attachment(&mut self, attachment: NoteAttachment) { + self.metadata.set_attachment(attachment); + } + /// Converts this builder to an [OutputNote]. /// /// Depending on the available information, this may result in [OutputNote::Full] or diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index 3515f92e32..ab8ddaa419 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -1,9 +1,14 @@ use alloc::vec::Vec; -use miden_processor::{AdviceMutation, ProcessState, RowIndex}; +use miden_processor::{AdviceMutation, AdviceProvider, ProcessState, RowIndex}; use miden_protocol::account::{AccountId, StorageMap, StorageSlotName, StorageSlotType}; use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; use miden_protocol::note::{ + NoteAttachment, + NoteAttachmentArray, + NoteAttachmentContent, + NoteAttachmentContentType, + NoteAttachmentType, NoteId, NoteInputs, NoteMetadata, @@ -12,6 +17,7 @@ use miden_protocol::note::{ NoteTag, NoteType, }; +use miden_protocol::transaction::memory::{NOTE_MEM_SIZE, OUTPUT_NOTE_SECTION_OFFSET}; use miden_protocol::transaction::{TransactionEventId, TransactionSummary}; use miden_protocol::vm::EventId; use miden_protocol::{Felt, Hasher, Word}; @@ -121,6 +127,13 @@ pub(crate) enum TransactionEvent { asset: Asset, }, + NoteBeforeSetAttachment { + /// The note index on which the attachment is set. + note_idx: usize, + /// The attachment that is set. + attachment: NoteAttachment, + }, + /// The data necessary to handle an auth request. AuthRequest { pub_key_hash: Word, @@ -409,6 +422,28 @@ impl TransactionEvent { TransactionEventId::NoteAfterAddAsset => None, + TransactionEventId::NoteBeforeSetAttachment => { + // Expected stack state: [ + // event, attachment_content_type, attachment_type, + // note_ptr, note_ptr, ATTACHMENT + // ] + + let attachment_content_type = process.get_stack_item(1); + let attachment_type = process.get_stack_item(2); + let note_ptr = process.get_stack_item(3); + let attachment = process.get_stack_word_be(5); + + let (note_idx, attachment) = extract_note_attachment( + attachment_content_type, + attachment_type, + attachment, + note_ptr, + process.advice_provider(), + )?; + + Some(TransactionEvent::NoteBeforeSetAttachment { note_idx, attachment }) + }, + TransactionEventId::AuthRequest => { // Expected stack state: [event, MESSAGE, PUB_KEY] let message = process.get_stack_word_be(1); @@ -692,6 +727,74 @@ fn build_note_metadata( Ok(NoteMetadata::new(sender, note_type, tag)) } +fn extract_note_attachment( + attachment_content_type: Felt, + attachment_type: Felt, + attachment: Word, + note_ptr: Felt, + advice_provider: &AdviceProvider, +) -> Result<(usize, NoteAttachment), TransactionKernelError> { + let note_idx = note_ptr_to_idx(note_ptr)?; + + let attachment_content_type = u8::try_from(attachment_content_type) + .map_err(|_| { + TransactionKernelError::other("failed to convert attachment content type to u8") + }) + .and_then(|content_type| { + NoteAttachmentContentType::try_from(content_type).map_err(|source| { + TransactionKernelError::other_with_source( + "failed to convert u8 to attachment content type", + source, + ) + }) + })?; + + let attachment_type = u32::try_from(attachment_type) + .map_err(|_| TransactionKernelError::other("failed to convert attachment type to u32")) + .map(NoteAttachmentType::new)?; + + let attachment_content = match attachment_content_type { + NoteAttachmentContentType::None => { + if !attachment.is_empty() { + return Err(TransactionKernelError::NoteAttachmentNoneIsNotEmpty); + } + NoteAttachmentContent::None + }, + NoteAttachmentContentType::Word => NoteAttachmentContent::Word(attachment), + NoteAttachmentContentType::Array => { + let elements = advice_provider.get_mapped_values(&attachment).ok_or_else(|| { + TransactionKernelError::other( + "elements of a note attachment commitment must be present in the advice provider", + ) + })?; + + let commitment_attachment = + NoteAttachmentArray::new(elements.to_vec()).map_err(|source| { + TransactionKernelError::other_with_source( + "failed to construct note attachment commitment", + source, + ) + })?; + + if commitment_attachment.commitment() != attachment { + return Err(TransactionKernelError::NoteAttachmentArrayMismatch { + actual: commitment_attachment.commitment(), + provided: attachment, + }); + } + + NoteAttachmentContent::Array(commitment_attachment) + }, + }; + + let attachment = + NoteAttachment::new(attachment_type, attachment_content).map_err(|source| { + TransactionKernelError::other_with_source("failed to extract note attachment", source) + })?; + + Ok((note_idx as usize, attachment)) +} + /// Extracts a word from a slice of field elements. #[inline(always)] fn extract_word(commitments: &[Felt], start: usize) -> Word { @@ -702,3 +805,17 @@ fn extract_word(commitments: &[Felt], start: usize) -> Word { commitments[start + 3], ]) } + +/// Converts the provided note ptr into the corresponding note index. +fn note_ptr_to_idx(note_ptr: Felt) -> Result { + u32::try_from(note_ptr) + .map_err(|_| TransactionKernelError::other("failed to convert note_ptr to u32")) + .and_then(|note_ptr| { + note_ptr + .checked_sub(OUTPUT_NOTE_SECTION_OFFSET) + .ok_or_else(|| { + TransactionKernelError::other("failed to calculate note_idx from note_ptr") + }) + .map(|note_ptr| note_ptr / NOTE_MEM_SIZE) + }) +} diff --git a/crates/miden-tx/src/prover/prover_host.rs b/crates/miden-tx/src/prover/prover_host.rs index 470a1195cb..db00cdf2d0 100644 --- a/crates/miden-tx/src/prover/prover_host.rs +++ b/crates/miden-tx/src/prover/prover_host.rs @@ -158,6 +158,11 @@ where self.base_host.on_note_before_add_asset(note_idx, asset).map(|_| Vec::new()) }, + TransactionEvent::NoteBeforeSetAttachment { note_idx, attachment } => self + .base_host + .on_note_before_set_attachment(note_idx, attachment) + .map(|_| Vec::new()), + TransactionEvent::AuthRequest { signature, .. } => { if let Some(signature) = signature { Ok(self.base_host.on_auth_requested(signature)) From b2b8c2000fe225e408aba94d78ebbb29b330c7f3 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 15 Jan 2026 09:26:02 +0100 Subject: [PATCH 100/114] feat: introduce standard `NetworkAccountTarget` attachment (#2257) * feat: Implement `NetworkAccountTarget` * chore: Let SPAWN note create output notes with attachment * chore: test creating note with NetworkAccountTarget attachment * chore: add changelog * fix: don't use super::* import * feat: Add `WellKnownNoteAttachment` * chore: Use 8-bit tag for exec hint; remove `AfterBlockNumber` * chore: improve network account target error --- CHANGELOG.md | 1 + crates/miden-protocol/src/note/attachment.rs | 12 ++ .../miden-protocol/src/note/execution_hint.rs | 96 ++-------- crates/miden-protocol/src/note/mod.rs | 2 +- crates/miden-standards/src/note/mod.rs | 6 + .../src/note/network_account_target.rs | 180 ++++++++++++++++++ .../src/note/well_known_note_attachment.rs | 18 ++ crates/miden-standards/src/testing/note.rs | 4 +- .../src/kernel_tests/tx/test_output_note.rs | 41 +++- crates/miden-testing/src/utils.rs | 15 ++ 10 files changed, 286 insertions(+), 89 deletions(-) create mode 100644 crates/miden-standards/src/note/network_account_target.rs create mode 100644 crates/miden-standards/src/note/well_known_note_attachment.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d8677c7d2..6cfb419ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Add `read_foreign_account_inputs()`, `read_vault_asset_witnesses()`, and `read_storage_map_witness()` for `TransactionInputs` ([#2246](https://github.com/0xMiden/miden-base/pull/2246)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252)). +- Introduce standard `NetworkAccountTarget` attachment for use in network transactions which replaces `NoteTag::NetworkAccount` ([#2257](https://github.com/0xMiden/miden-base/pull/2257)). ### Changes diff --git a/crates/miden-protocol/src/note/attachment.rs b/crates/miden-protocol/src/note/attachment.rs index 11d50c6542..f219e94f62 100644 --- a/crates/miden-protocol/src/note/attachment.rs +++ b/crates/miden-protocol/src/note/attachment.rs @@ -443,6 +443,18 @@ impl TryFrom for NoteAttachmentContentType { } } +impl core::fmt::Display for NoteAttachmentContentType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let output = match self { + NoteAttachmentContentType::None => "None", + NoteAttachmentContentType::Word => "Word", + NoteAttachmentContentType::Array => "Array", + }; + + f.write_str(output) + } +} + impl Serializable for NoteAttachmentContentType { fn write_into(&self, target: &mut W) { self.as_u8().write_into(target); diff --git a/crates/miden-protocol/src/note/execution_hint.rs b/crates/miden-protocol/src/note/execution_hint.rs index 90572de305..6dd14d05c8 100644 --- a/crates/miden-protocol/src/note/execution_hint.rs +++ b/crates/miden-protocol/src/note/execution_hint.rs @@ -15,7 +15,7 @@ use crate::{Felt, NoteError}; /// [`NoteExecutionHint`] can be encoded into a [`Felt`] with the following layout: /// /// ```text -/// [26 zero bits | payload (32 bits) | tag (6 bits)] +/// [24 zero bits | payload (32 bits) | tag (8 bits)] /// ``` /// /// This way, hints such as [NoteExecutionHint::Always], are represented by `Felt::new(1)`. @@ -27,10 +27,7 @@ pub enum NoteExecutionHint { /// The note's script can be executed at any time. Always, /// The note's script can be executed after the specified block number. - /// - /// The block number cannot be [`u32::MAX`] which is enforced through the [`AfterBlockNumber`] - /// type. - AfterBlock { block_num: AfterBlockNumber }, + AfterBlock { block_num: BlockNumber }, /// The note's script can be executed in the specified slot within the specified round. /// /// The slot is defined as follows: @@ -74,13 +71,8 @@ impl NoteExecutionHint { } /// Creates a [NoteExecutionHint::AfterBlock] variant based on the given `block_num` - /// - /// # Errors - /// - /// Returns an error if `block_num` is equal to [`u32::MAX`]. - pub fn after_block(block_num: BlockNumber) -> Result { - AfterBlockNumber::new(block_num) - .map(|block_number| NoteExecutionHint::AfterBlock { block_num: block_number }) + pub fn after_block(block_num: BlockNumber) -> Self { + NoteExecutionHint::AfterBlock { block_num } } /// Creates a [NoteExecutionHint::OnBlockSlot] for the given parameters. See the variants @@ -103,7 +95,7 @@ impl NoteExecutionHint { } Ok(NoteExecutionHint::Always) }, - Self::AFTER_BLOCK_TAG => NoteExecutionHint::after_block(payload.into()), + Self::AFTER_BLOCK_TAG => Ok(NoteExecutionHint::after_block(BlockNumber::from(payload))), Self::ON_BLOCK_SLOT_TAG => { let remainder = ((payload >> 24) & 0xff) as u8; if remainder != 0 { @@ -151,15 +143,7 @@ impl NoteExecutionHint { } } - /// Encodes the [`NoteExecutionHint`] into a 6-bit tag and a 32-bit payload. - /// - /// # Guarantees - /// - /// Since the tag has at most 6 bits, the returned byte is guaranteed to have its two most - /// significant bits set to `0`. - /// - /// The payload is guaranteed to contain at least one `0` bit to make encoding it into - /// [`NoteMetadata`](crate::note::NoteMetadata) safely possible. + /// Encodes the [`NoteExecutionHint`] into an 8-bit tag and a 32-bit payload. pub fn into_parts(&self) -> (u8, u32) { match self { NoteExecutionHint::None => (Self::NONE_TAG, 0), @@ -187,12 +171,13 @@ impl From for Felt { /// Tries to convert a `u64` into a [`NoteExecutionHint`] with the expected layout documented on the /// type. /// -/// Note: The upper 26 bits are not enforced to be zero. +/// Note: The upper 24 bits are not enforced to be zero. impl TryFrom for NoteExecutionHint { type Error = NoteError; fn try_from(value: u64) -> Result { - let tag = (value & 0b111111) as u8; - let payload = ((value >> 6) & 0xffffffff) as u32; + let tag = (value & 0b1111_1111) as u8; + // Shift the payload and cut off / ignore the upper 32 bits. + let payload = (value >> 8) as u32; Self::from_parts(tag, payload) } @@ -202,51 +187,7 @@ impl TryFrom for NoteExecutionHint { impl From for u64 { fn from(value: NoteExecutionHint) -> Self { let (tag, payload) = value.into_parts(); - ((payload as u64) << 6) | (tag as u64) - } -} - -// AFTER BLOCK NUMBER -// ================================================================================================ - -/// A wrapper around a block number which enforces that it is not `u32::MAX`. -/// -/// Used for the [`NoteExecutionHint::AfterBlock`] variant where this constraint is needed. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct AfterBlockNumber(BlockNumber); - -impl AfterBlockNumber { - /// Creates a new [`AfterBlockNumber`] from the given `block_number`. - /// - /// # Errors - /// - /// Returns an error if: - /// - `block_number` is equal to `u32::MAX`. - pub fn new(block_number: BlockNumber) -> Result { - if block_number.as_u32() == u32::MAX { - Err(NoteError::NoteExecutionHintAfterBlockCannotBeU32Max) - } else { - Ok(Self(block_number)) - } - } - - /// Returns the block number as a `u32`. - pub fn as_u32(&self) -> u32 { - self.0.as_u32() - } -} - -impl From for u32 { - fn from(block_number: AfterBlockNumber) -> Self { - block_number.0.as_u32() - } -} - -impl TryFrom for AfterBlockNumber { - type Error = NoteError; - - fn try_from(block_number: u32) -> Result { - Self::new(block_number.into()) + ((payload as u64) << 8) | (tag as u64) } } @@ -255,7 +196,6 @@ impl TryFrom for AfterBlockNumber { #[cfg(test)] mod tests { - use assert_matches::assert_matches; use super::*; @@ -269,7 +209,7 @@ mod tests { fn test_serialization_round_trip() { assert_hint_serde(NoteExecutionHint::None); assert_hint_serde(NoteExecutionHint::Always); - assert_hint_serde(NoteExecutionHint::after_block(15.into()).unwrap()); + assert_hint_serde(NoteExecutionHint::after_block(15.into())); assert_hint_serde(NoteExecutionHint::OnBlockSlot { round_len: 9, slot_len: 12, @@ -279,7 +219,7 @@ mod tests { #[test] fn test_encode_round_trip() { - let hint = NoteExecutionHint::after_block(15.into()).unwrap(); + let hint = NoteExecutionHint::after_block(15.into()); let hint_int: u64 = hint.into(); let decoded_hint: NoteExecutionHint = hint_int.try_into().unwrap(); assert_eq!(hint, decoded_hint); @@ -305,7 +245,7 @@ mod tests { let always = NoteExecutionHint::always(); assert!(always.can_be_consumed(100.into()).unwrap()); - let after_block = NoteExecutionHint::after_block(12345.into()).unwrap(); + let after_block = NoteExecutionHint::after_block(12345.into()); assert!(!after_block.can_be_consumed(12344.into()).unwrap()); assert!(after_block.can_be_consumed(12345.into()).unwrap()); @@ -331,12 +271,4 @@ mod tests { NoteExecutionHint::from_parts(10, 1).unwrap_err(); } - - #[test] - fn test_after_block_fails_on_u32_max() { - assert_matches!( - NoteExecutionHint::after_block(u32::MAX.into()).unwrap_err(), - NoteError::NoteExecutionHintAfterBlockCannotBeU32Max - ); - } } diff --git a/crates/miden-protocol/src/note/mod.rs b/crates/miden-protocol/src/note/mod.rs index 7f5a3dcdca..3c82e10e3d 100644 --- a/crates/miden-protocol/src/note/mod.rs +++ b/crates/miden-protocol/src/note/mod.rs @@ -30,7 +30,7 @@ pub use attachment::{ }; mod execution_hint; -pub use execution_hint::{AfterBlockNumber, NoteExecutionHint}; +pub use execution_hint::NoteExecutionHint; mod note_id; pub use note_id::NoteId; diff --git a/crates/miden-standards/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs index c31f6266c1..7b54636b87 100644 --- a/crates/miden-standards/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -21,6 +21,12 @@ use utils::build_swap_tag; pub mod mint_inputs; pub mod utils; +mod network_account_target; +pub use network_account_target::NetworkAccountTarget; + +mod well_known_note_attachment; +pub use well_known_note_attachment::WellKnownNoteAttachment; + mod well_known_note; pub use mint_inputs::MintNoteInputs; pub use well_known_note::{NoteConsumptionStatus, WellKnownNote}; diff --git a/crates/miden-standards/src/note/network_account_target.rs b/crates/miden-standards/src/note/network_account_target.rs new file mode 100644 index 0000000000..4c64073b78 --- /dev/null +++ b/crates/miden-standards/src/note/network_account_target.rs @@ -0,0 +1,180 @@ +use miden_protocol::account::AccountId; +use miden_protocol::note::{ + NoteAttachment, + NoteAttachmentContent, + NoteAttachmentContentType, + NoteAttachmentType, + NoteExecutionHint, +}; +use miden_protocol::{AccountIdError, NoteError, Word}; + +use crate::note::WellKnownNoteAttachment; + +// NETWORK ACCOUNT TARGET +// ================================================================================================ + +/// A [`NoteAttachment`] for notes targeted at network accounts. +/// +/// It can be encoded to and from a [`NoteAttachmentContent::Word`] with the following layout: +/// +/// ```text +/// - 0th felt: [target_id_suffix (56 bits) | 8 zero bits] +/// - 1st felt: [target_id_prefix (64 bits)] +/// - 2nd felt: [24 zero bits | exec_hint_payload (32 bits) | exec_hint_tag (8 bits)] +/// - 3rd felt: [64 zero bits] +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NetworkAccountTarget { + target_id: AccountId, + exec_hint: NoteExecutionHint, +} + +impl NetworkAccountTarget { + // CONSTANTS + // -------------------------------------------------------------------------------------------- + + /// The standardized type of [`NetworkAccountTarget`] attachments. + pub const ATTACHMENT_TYPE: NoteAttachmentType = + WellKnownNoteAttachment::NetworkAccountTarget.attachment_type(); + + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`NetworkAccountTarget`] from the provided parts. + /// + /// # Errors + /// + /// Returns an error if: + /// - the provided `target_id` does not have + /// [`AccountStorageMode::Network`](miden_protocol::account::AccountStorageMode::Network). + pub fn new( + target_id: AccountId, + exec_hint: NoteExecutionHint, + ) -> Result { + // TODO: Once AccountStorageMode::Network is removed, this should check is_public. + if !target_id.is_network() { + return Err(NetworkAccountTargetError::TargetNotNetwork(target_id)); + } + + Ok(Self { target_id, exec_hint }) + } + + // ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the [`AccountId`] at which the note is targeted. + pub fn target_id(&self) -> AccountId { + self.target_id + } + + /// Returns the [`NoteExecutionHint`] of the note. + pub fn execution_hint(&self) -> NoteExecutionHint { + self.exec_hint + } +} + +impl From for NoteAttachment { + fn from(network_attachment: NetworkAccountTarget) -> Self { + let mut word = Word::empty(); + word[0] = network_attachment.target_id.suffix(); + word[1] = network_attachment.target_id.prefix().as_felt(); + word[2] = network_attachment.exec_hint.into(); + + NoteAttachment::new_word(NetworkAccountTarget::ATTACHMENT_TYPE, word) + } +} + +impl TryFrom for NetworkAccountTarget { + type Error = NetworkAccountTargetError; + + fn try_from(attachment: NoteAttachment) -> Result { + if attachment.attachment_type() != Self::ATTACHMENT_TYPE { + return Err(NetworkAccountTargetError::AttachmentTypeMismatch( + attachment.attachment_type(), + )); + } + + match attachment.content() { + NoteAttachmentContent::Word(word) => { + let id_suffix = word[0]; + let id_prefix = word[1]; + let exec_hint = word[2]; + + let target_id = AccountId::try_from([id_prefix, id_suffix]) + .map_err(NetworkAccountTargetError::DecodeTargetId)?; + + let exec_hint = NoteExecutionHint::try_from(exec_hint.as_int()) + .map_err(NetworkAccountTargetError::DecodeExecutionHint)?; + + NetworkAccountTarget::new(target_id, exec_hint) + }, + _ => Err(NetworkAccountTargetError::AttachmentContentTypeMismatch( + attachment.content().content_type(), + )), + } + } +} + +// NETWORK ACCOUNT TARGET ERROR +// ================================================================================================ + +#[derive(Debug, thiserror::Error)] +pub enum NetworkAccountTargetError { + #[error("target account ID must be of type network account")] + TargetNotNetwork(AccountId), + #[error( + "attachment type {0} did not match expected type {expected}", + expected = NetworkAccountTarget::ATTACHMENT_TYPE + )] + AttachmentTypeMismatch(NoteAttachmentType), + #[error( + "attachment content type {0} did not match expected type {expected}", + expected = NoteAttachmentContentType::Word + )] + AttachmentContentTypeMismatch(NoteAttachmentContentType), + #[error("failed to decode target account ID")] + DecodeTargetId(#[source] AccountIdError), + #[error("failed to decode execution hint")] + DecodeExecutionHint(#[source] NoteError), +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use assert_matches::assert_matches; + use miden_protocol::account::AccountStorageMode; + use miden_protocol::testing::account_id::AccountIdBuilder; + + use super::*; + + #[test] + fn network_account_target_serde() -> anyhow::Result<()> { + let id = AccountIdBuilder::new() + .storage_mode(AccountStorageMode::Network) + .build_with_rng(&mut rand::rng()); + let network_account_target = NetworkAccountTarget::new(id, NoteExecutionHint::Always)?; + assert_eq!( + network_account_target, + NetworkAccountTarget::try_from(NoteAttachment::from(network_account_target))? + ); + + Ok(()) + } + + #[test] + fn network_account_target_fails_on_private_network_target_account() -> anyhow::Result<()> { + let id = AccountIdBuilder::new() + .storage_mode(AccountStorageMode::Private) + .build_with_rng(&mut rand::rng()); + let err = NetworkAccountTarget::new(id, NoteExecutionHint::Always).unwrap_err(); + + assert_matches!( + err, + NetworkAccountTargetError::TargetNotNetwork(account_id) if account_id == id + ); + + Ok(()) + } +} diff --git a/crates/miden-standards/src/note/well_known_note_attachment.rs b/crates/miden-standards/src/note/well_known_note_attachment.rs new file mode 100644 index 0000000000..1f1577ba2a --- /dev/null +++ b/crates/miden-standards/src/note/well_known_note_attachment.rs @@ -0,0 +1,18 @@ +use miden_protocol::note::NoteAttachmentType; + +/// The [`NoteAttachmentType`]s of well-known note attachmens. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum WellKnownNoteAttachment { + /// See [`NetworkAccountTarget`](crate::note::NetworkAccountTarget) for details. + NetworkAccountTarget, +} + +impl WellKnownNoteAttachment { + /// Returns the [`NoteAttachmentType`] of the well-known attachment. + pub const fn attachment_type(&self) -> NoteAttachmentType { + match self { + WellKnownNoteAttachment::NetworkAccountTarget => NoteAttachmentType::new(1u32), + } + } +} diff --git a/crates/miden-standards/src/testing/note.rs b/crates/miden-standards/src/testing/note.rs index b7a14c99b8..9ade886bd2 100644 --- a/crates/miden-standards/src/testing/note.rs +++ b/crates/miden-standards/src/testing/note.rs @@ -119,8 +119,8 @@ impl NoteBuilder { } /// Overwrites the attachment. - pub fn attachment(mut self, attachment: NoteAttachment) -> Self { - self.attachment = attachment; + pub fn attachment(mut self, attachment: impl Into) -> Self { + self.attachment = attachment.into(); self } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index 5335c63d29..f1ee66af05 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -45,13 +45,13 @@ use miden_protocol::transaction::memory::{ use miden_protocol::transaction::{OutputNote, OutputNotes}; use miden_protocol::{Felt, Word, ZERO}; use miden_standards::code_builder::CodeBuilder; -use miden_standards::note::create_p2id_note; +use miden_standards::note::{NetworkAccountTarget, create_p2id_note}; use miden_standards::testing::mock_account::MockAccountExt; use miden_standards::testing::note::NoteBuilder; use super::{TestSetup, setup_test}; use crate::kernel_tests::tx::ExecutionOutputExt; -use crate::utils::create_public_p2any_note; +use crate::utils::{create_public_p2any_note, create_spawn_note}; use crate::{Auth, MockChain, TransactionContextBuilder, assert_execution_error}; #[tokio::test] @@ -86,7 +86,7 @@ async fn test_create_note() -> anyhow::Result<()> { ", recipient = recipient, PUBLIC_NOTE = NoteType::Public as u8, - note_execution_hint = Felt::from(NoteExecutionHint::after_block(23.into()).unwrap()), + note_execution_hint = Felt::from(NoteExecutionHint::after_block(23.into())), tag = tag, ); @@ -681,7 +681,7 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { output_serial_no = output_serial_no, PUBLIC_NOTE = NoteType::Public as u8, tag = tag, - execution_hint = Felt::from(NoteExecutionHint::after_block(2.into()).unwrap()), + execution_hint = Felt::from(NoteExecutionHint::after_block(2.into())), aux = aux, ); @@ -1211,6 +1211,39 @@ async fn test_set_array_attachment() -> anyhow::Result<()> { Ok(()) } +/// Tests creating an output note with an attachment of type NetworkAccountTarget. +#[tokio::test] +async fn test_set_network_target_account_attachment() -> anyhow::Result<()> { + let account = Account::mock(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, Auth::IncrNonce); + let rng = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])); + let attachment = NetworkAccountTarget::new( + ACCOUNT_ID_NETWORK_NON_FUNGIBLE_FAUCET.try_into()?, + NoteExecutionHint::on_block_slot(5, 32, 3), + )?; + let output_note = NoteBuilder::new(account.id(), rng) + .note_type(NoteType::Private) + .attachment(attachment) + .build()?; + let spawn_note = create_spawn_note([&output_note])?; + + let tx = TransactionContextBuilder::new(account) + .extend_input_notes([spawn_note].to_vec()) + .build()? + .execute() + .await?; + + let actual_note = tx.output_notes().get_note(0); + assert_eq!(actual_note.header(), output_note.header()); + assert_eq!(actual_note.assets().unwrap(), output_note.assets()); + + // Make sure we can deserialize the attachment back into its original type. + let actual_attachment = + NetworkAccountTarget::try_from(actual_note.metadata().attachment().clone())?; + assert_eq!(actual_attachment, attachment); + + Ok(()) +} + // HELPER FUNCTIONS // ================================================================================================ diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index fc5f3aa249..9c51ba83e8 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -240,6 +240,21 @@ fn note_script_that_creates_notes<'note>( tag = note.metadata().tag(), )); + out.push_str(&format!( + " + push.{ATTACHMENT} + push.{attachment_type} + push.{attachment_content_type} + dup.6 + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT, note_idx] + exec.output_note::set_attachment + # => [note_idx] + ", + ATTACHMENT = note.metadata().to_attachment_word(), + attachment_type = note.metadata().attachment().attachment_type().as_u32(), + attachment_content_type = note.metadata().attachment().content().content_type().as_u8(), + )); + let assets_str = prepare_assets(note.assets()); for asset in assets_str { out.push_str(&format!( From 8b4d42a825c92f5329c4b6c4cdae9e485a2b5ce1 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 15 Jan 2026 09:59:10 +0100 Subject: [PATCH 101/114] chore: Remove `aux` and `execution_hint` parameters from `output_note::create` (#2260) * chore: remove aux and exec hint from protocol::output_note::create * fix: tests in `test_epilogue.rs` * fix: update calls to `output_note::create` in note tests * fix: update calls to `output_note::create` in tx and account_delta test * fix: remove aux and exec hint from `send_note_body` * chore: remove exec hint and aux from swap note * chore: update MINT note to new output_note::create signature * fix: remove aux/exec hint from faucet tests * fix: replace aux with attachment in p2id constructors * fix: replace aux with attachment in mint, burn, p2ide notes * chore: remove aux and exec hint metadata methods * chore: remove aux/exec hint from `NoteBuilder` * chore: add changelog * feat: Take attachment param in `faucets::distribute` * chore: update MINT note to new faucets::distribute params * feat: allow attachment for payback note in SWAP note * fix: calls to `distribute` in tests; remove mention of aux * Revert "feat: Take attachment param in `faucets::distribute`" This reverts commit 8c27281a77f07a3bbe0b257d7d434904cb979112. * chore: return note_idx from `distribute` * Partially revert chore: update MINT note to new faucets::distribute params * chore: update MINT note with new note inputs memory offets * chore: set attachment in MINT note * Partially revert fix: calls to `distribute` in tests; remove mention of aux * feat: Add attachment to note in `send_body` * chore: remove unnecessary `push.0` --- CHANGELOG.md | 1 + bin/bench-transaction/src/context_setups.rs | 7 +- .../asm/kernels/transaction/api.masm | 6 +- .../kernels/transaction/lib/output_note.masm | 11 +- .../asm/protocol/output_note.masm | 13 +- crates/miden-protocol/src/note/attachment.rs | 5 + crates/miden-protocol/src/note/metadata.rs | 21 +-- .../src/testing/mock_util_lib.rs | 6 +- .../src/transaction/kernel/procedures.rs | 2 +- .../asm/standards/faucets/basic_fungible.masm | 5 +- .../asm/standards/faucets/mod.masm | 31 ++--- .../standards/faucets/network_fungible.masm | 13 +- .../asm/standards/notes/mint.masm | 83 +++++++----- .../asm/standards/notes/swap.masm | 54 +++++--- .../src/account/interface/component.rs | 59 ++++++--- .../src/account/interface/test.rs | 15 ++- .../miden-standards/src/errors/standards.rs | 8 +- .../miden-standards/src/note/mint_inputs.rs | 87 +++++++----- crates/miden-standards/src/note/mod.rs | 63 ++++----- .../src/note/well_known_note.rs | 2 +- crates/miden-standards/src/testing/note.rs | 19 +-- .../block/proposed_block_errors.rs | 6 +- .../block/proven_block_success.rs | 11 +- .../src/kernel_tests/tx/test_account_delta.rs | 40 +----- .../src/kernel_tests/tx/test_epilogue.rs | 30 +---- .../src/kernel_tests/tx/test_note.rs | 3 +- .../src/kernel_tests/tx/test_output_note.rs | 125 ++++++------------ .../src/kernel_tests/tx/test_tx.rs | 106 ++++++++------- .../src/mock_chain/chain_builder.rs | 10 +- crates/miden-testing/src/utils.rs | 7 +- crates/miden-testing/tests/scripts/faucet.rs | 85 ++++-------- crates/miden-testing/tests/scripts/p2id.rs | 16 +-- .../miden-testing/tests/scripts/send_note.rs | 26 +++- crates/miden-testing/tests/scripts/swap.rs | 8 +- docs/src/note.md | 4 +- 35 files changed, 445 insertions(+), 543 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cfb419ddb..f5e2a552cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Add `read_foreign_account_inputs()`, `read_vault_asset_witnesses()`, and `read_storage_map_witness()` for `TransactionInputs` ([#2246](https://github.com/0xMiden/miden-base/pull/2246)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252)). +- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260)). - Introduce standard `NetworkAccountTarget` attachment for use in network transactions which replaces `NoteTag::NetworkAccount` ([#2257](https://github.com/0xMiden/miden-base/pull/2257)). ### Changes diff --git a/bin/bench-transaction/src/context_setups.rs b/bin/bench-transaction/src/context_setups.rs index 6a136174ee..d0ac16fce2 100644 --- a/bin/bench-transaction/src/context_setups.rs +++ b/bin/bench-transaction/src/context_setups.rs @@ -1,9 +1,9 @@ use anyhow::Result; +use miden_protocol::Word; use miden_protocol::asset::{Asset, FungibleAsset}; use miden_protocol::note::NoteType; use miden_protocol::testing::account_id::ACCOUNT_ID_SENDER; use miden_protocol::transaction::OutputNote; -use miden_protocol::{Felt, Word}; use miden_standards::code_builder::CodeBuilder; use miden_testing::{Auth, MockChain, TransactionContext}; @@ -31,11 +31,9 @@ pub fn tx_create_single_p2id_note() -> Result { begin # create an output note with fungible asset push.{RECIPIENT} - push.{note_execution_hint} push.{note_type} - push.0 # aux push.{tag} - call.output_note::create + exec.output_note::create # => [note_idx] # move the asset to the note @@ -49,7 +47,6 @@ pub fn tx_create_single_p2id_note() -> Result { end ", RECIPIENT = output_note.recipient().digest(), - note_execution_hint = Felt::from(output_note.metadata().execution_hint()), note_type = NoteType::Public as u8, tag = output_note.metadata().tag(), asset = Word::from(fungible_asset), diff --git a/crates/miden-protocol/asm/kernels/transaction/api.masm b/crates/miden-protocol/asm/kernels/transaction/api.masm index a6643f5ddb..65728bb526 100644 --- a/crates/miden-protocol/asm/kernels/transaction/api.masm +++ b/crates/miden-protocol/asm/kernels/transaction/api.masm @@ -1146,14 +1146,12 @@ end #! Creates a new note and returns its index. #! -#! Inputs: [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] +#! Inputs: [tag, note_type, RECIPIENT, pad(10)] #! Outputs: [note_idx, pad(15)] #! #! Where: #! - tag is the tag to be included in the note. -#! - aux is the auxiliary metadata to be included in the note. #! - note_type is the note storage type. -#! - execution_hint is the note execution hint tag and payload. #! - RECIPIENT is the recipient of the note. #! - note_idx is the index of the created note. #! @@ -1161,7 +1159,7 @@ end pub proc output_note_create # check that this procedure was executed against the native account exec.memory::assert_native_account - # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + # => [tag, note_type, RECIPIENT, pad(10)] exec.output_note::create # => [note_idx, pad(15)] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index 91c75a6900..3abd761149 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -71,10 +71,9 @@ const NOTE_BEFORE_SET_ATTACHMENT_EVENT=event("miden::note::before_set_attachment # OUTPUT NOTE PROCEDURES # ================================================================================================= -# TODO(note_attachment): Remove aux and execution hint parameters. #! Creates a new note and returns the index of the note. #! -#! Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] +#! Inputs: [tag, note_type, RECIPIENT] #! Outputs: [note_idx] #! #! Where: @@ -89,14 +88,6 @@ const NOTE_BEFORE_SET_ATTACHMENT_EVENT=event("miden::note::before_set_attachment #! - the note tag is not a u32. #! - the number of output notes exceeds the maximum limit of 1024. pub proc create - # temporary: drop aux - swap drop - # => [tag, note_type, execution_hint, RECIPIENT] - - # temporary: drop execution_hint - movup.2 drop - # => [tag, note_type, RECIPIENT] - emit.NOTE_BEFORE_CREATED_EVENT # => [tag, note_type, RECIPIENT] diff --git a/crates/miden-protocol/asm/protocol/output_note.masm b/crates/miden-protocol/asm/protocol/output_note.masm index d4f5356e39..23f56b8f38 100644 --- a/crates/miden-protocol/asm/protocol/output_note.masm +++ b/crates/miden-protocol/asm/protocol/output_note.masm @@ -14,14 +14,12 @@ pub const ATTACHMENT_CONTENT_TYPE_ARRAY=2 #! Creates a new note and returns the index of the note. #! -#! Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] +#! Inputs: [tag, note_type, RECIPIENT] #! Outputs: [note_idx] #! #! Where: #! - tag is the tag to be included in the note. -#! - aux is the auxiliary metadata to be included in the note. #! - note_type is the storage type of the note. -#! - execution_hint is the note's execution hint. #! - RECIPIENT is the recipient of the note. #! - note_idx is the index of the created note. #! @@ -34,11 +32,14 @@ pub const ATTACHMENT_CONTENT_TYPE_ARRAY=2 pub proc create # pad the stack before the syscall to prevent accidental modification of the deeper stack # elements - padw padw swapdw movup.8 drop - # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] + push.0 movdn.6 push.0 + # => [0, tag, note_type, RECIPIENT, 0] + + padw padw swapdw drop + # => [tag, note_type, RECIPIENT, pad(9)] exec.kernel_proc_offsets::output_note_create_offset - # => [offset, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] + # => [offset, tag, note_type, RECIPIENT, pad(9)] syscall.exec_kernel_proc # => [note_idx, pad(15)] diff --git a/crates/miden-protocol/src/note/attachment.rs b/crates/miden-protocol/src/note/attachment.rs index f219e94f62..a7a03fc4d3 100644 --- a/crates/miden-protocol/src/note/attachment.rs +++ b/crates/miden-protocol/src/note/attachment.rs @@ -95,6 +95,11 @@ impl NoteAttachment { self.attachment_type } + /// Returns the attachment content type. + pub fn content_type(&self) -> NoteAttachmentContentType { + self.content.content_type() + } + /// Returns a reference to the attachment content. pub fn content(&self) -> &NoteAttachmentContent { &self.content diff --git a/crates/miden-protocol/src/note/metadata.rs b/crates/miden-protocol/src/note/metadata.rs index e641e7b150..b41b74d527 100644 --- a/crates/miden-protocol/src/note/metadata.rs +++ b/crates/miden-protocol/src/note/metadata.rs @@ -10,12 +10,7 @@ use super::{ Serializable, Word, }; -use crate::note::{ - NoteAttachment, - NoteAttachmentContentType, - NoteAttachmentType, - NoteExecutionHint, -}; +use crate::note::{NoteAttachment, NoteAttachmentContentType, NoteAttachmentType}; use crate::{Hasher, NoteError}; // NOTE METADATA @@ -109,20 +104,6 @@ impl NoteMetadata { self.tag } - // TODO(note_attachment): Remove this method. - // Method left for temporary compatibility. - /// Returns the execution hint associated with the note. - pub fn execution_hint(&self) -> NoteExecutionHint { - NoteExecutionHint::always() - } - - // TODO(note_attachment): Remove this method. - // Method left for temporary compatibility. - /// Returns the note's aux field. - pub fn aux(&self) -> Felt { - Felt::from(0u32) - } - /// Returns the attachment of the note. pub fn attachment(&self) -> &NoteAttachment { &self.attachment diff --git a/crates/miden-protocol/src/testing/mock_util_lib.rs b/crates/miden-protocol/src/testing/mock_util_lib.rs index b319d2982c..f9d454f5ee 100644 --- a/crates/miden-protocol/src/testing/mock_util_lib.rs +++ b/crates/miden-protocol/src/testing/mock_util_lib.rs @@ -11,11 +11,9 @@ const MOCK_UTIL_LIBRARY_CODE: &str = " #! Outputs: [note_idx] pub proc create_default_note push.1.2.3.4 # = RECIPIENT - push.1 # = NoteExecutionHint::Always push.2 # = NoteType::Private - push.0 # = aux - push.0xc0000000 # = NoteTag::LocalAny - # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + push.0 # = NoteTag + # => [tag, note_type, RECIPIENT] exec.output_note::create # => [note_idx] diff --git a/crates/miden-protocol/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs index 0e028eaafe..3fba0621af 100644 --- a/crates/miden-protocol/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -80,7 +80,7 @@ pub const KERNEL_PROCEDURES: [Word; 53] = [ // input_note_get_recipient word!("0xd3c255177f9243bb1a523a87615bbe76dd5a3605fcae87eb9d3a626d4ecce33c"), // output_note_create - word!("0x9689eb055efa7153b4498e433056d99cda07a8afd4dac39c9cc82ef8cf22b3fb"), + word!("0xf6d7790691427c5d54ac9dd3b7ed1bb587fa6e864bdf7ba372f022a45c7caa47"), // output_note_get_metadata word!("0x3db8427f20eb70603b72aa574a986506eb7216312004aeaf8b2a7e55d0049a48"), // output_note_get_assets_info diff --git a/crates/miden-standards/asm/standards/faucets/basic_fungible.masm b/crates/miden-standards/asm/standards/faucets/basic_fungible.masm index cc447eeb2e..11fbb4353b 100644 --- a/crates/miden-standards/asm/standards/faucets/basic_fungible.masm +++ b/crates/miden-standards/asm/standards/faucets/basic_fungible.masm @@ -27,7 +27,7 @@ const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no #! Distributes freshly minted fungible assets to the provided recipient by creating a note. #! #! Inputs: [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] -#! Outputs: [pad(16)] +#! Outputs: [note_idx, pad(15)] #! #! Where: #! - amount is the amount to be minted and sent. @@ -37,6 +37,7 @@ const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no #! - execution_hint is the execution hint of the note that holds the asset. #! - RECIPIENT is the recipient of the asset, i.e., #! hash(hash(hash(serial_num, [0; 4]), script_root), input_commitment). +#! - note_idx is the index of the created note. #! #! Panics if: #! - the transaction is being executed against an account that is not a fungible asset faucet. @@ -45,7 +46,7 @@ const ERR_BASIC_FUNGIBLE_BURN_WRONG_NUMBER_OF_ASSETS="burn requires exactly 1 no #! Invocation: call pub proc distribute exec.faucets::distribute - # => [pad(16)] + # => [note_idx, pad(15)] end #! Burns the fungible asset from the active note. diff --git a/crates/miden-standards/asm/standards/faucets/mod.masm b/crates/miden-standards/asm/standards/faucets/mod.masm index f5b2d3461e..68a7d0652e 100644 --- a/crates/miden-standards/asm/standards/faucets/mod.masm +++ b/crates/miden-standards/asm/standards/faucets/mod.masm @@ -22,17 +22,16 @@ const METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") #! Distributes freshly minted fungible assets to the provided recipient by creating a note. #! -#! Inputs: [amount, tag, aux, note_type, execution_hint, RECIPIENT] -#! Outputs: [] +#! Inputs: [amount, tag, note_type, RECIPIENT] +#! Outputs: [note_idx] #! #! Where: #! - amount is the amount to be minted and sent. #! - tag is the tag to be included in the note. -#! - aux is the auxiliary data to be included in the note. #! - note_type is the type of the note that holds the asset. -#! - execution_hint is the execution hint of the note that holds the asset. #! - RECIPIENT is the recipient of the asset, i.e., #! hash(hash(hash(serial_num, [0; 4]), script_root), input_commitment). +#! - note_idx is the index of the created note. #! #! Panics if: #! - the transaction is being executed against an account that is not a fungible asset faucet. @@ -42,39 +41,41 @@ const METADATA_SLOT=word("miden::standards::fungible_faucets::metadata") pub proc distribute # get max supply of this faucet. We assume it is stored at pos 3 of slot 0 push.METADATA_SLOT[0..2] exec.active_account::get_item drop drop drop - # => [max_supply, amount, tag, aux, note_type, execution_hint, RECIPIENT] + # => [max_supply, amount, tag, note_type, RECIPIENT] # get total issuance of this faucet so far and add amount to be minted exec.faucet::get_total_issuance - # => [total_issuance, max_supply, amount, tag, aux, note_type, execution_hint, RECIPIENT] + # => [total_issuance, max_supply, amount, tag, note_type, RECIPIENT] # compute maximum amount that can be minted, max_mint_amount = max_supply - total_issuance sub - # => [max_supply - total_issuance, amount, tag, aux, note_type, execution_hint, RECIPIENT] + # => [max_supply - total_issuance, amount, tag, note_type, RECIPIENT] # check that amount =< max_supply - total_issuance, fails if otherwise dup.1 gte assert.err=ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT] + # => [amount, tag, note_type, RECIPIENT] # creating the asset exec.faucet::create_fungible_asset - # => [ASSET, tag, aux, note_type, execution_hint, RECIPIENT] + # => [ASSET, tag, note_type, RECIPIENT] # mint the asset; this is needed to satisfy asset preservation logic. exec.faucet::mint - # => [ASSET, tag, aux, note_type, execution_hint, RECIPIENT] + # => [ASSET, tag, note_type, RECIPIENT] - # store and drop the ASSET - movdnw.2 - # => [tag, aux, note_type, execution_hint, RECIPIENT, ASSET] + movdn.9 movdn.9 movdn.9 movdn.9 + # => [tag, note_type, RECIPIENT, ASSET] # create a note exec.output_note::create # => [note_idx, ASSET] # load the ASSET and add it to the note - movdn.4 exec.output_note::add_asset - # => [] + dup movdn.5 movdn.5 + # => [ASSET, note_idx, note_idx] + + exec.output_note::add_asset + # => [note_idx] end #! Burns the fungible asset from the active note. diff --git a/crates/miden-standards/asm/standards/faucets/network_fungible.masm b/crates/miden-standards/asm/standards/faucets/network_fungible.masm index 39395c628c..04cabea248 100644 --- a/crates/miden-standards/asm/standards/faucets/network_fungible.masm +++ b/crates/miden-standards/asm/standards/faucets/network_fungible.masm @@ -39,16 +39,15 @@ end #! This procedure first checks if the note sender is the owner of the faucet, and then #! mints the asset and creates an output note with that asset for the recipient. #! -#! Inputs: [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] -#! Outputs: [pad(16)] +#! Inputs: [amount, tag, note_type, RECIPIENT, pad(9)] +#! Outputs: [note_idx, pad(15)] #! #! Where: #! - amount is the amount to be minted and sent. #! - tag is the tag to be included in the note. -#! - aux is the auxiliary data to be included in the note. #! - note_type is the type of the note that holds the asset. -#! - execution_hint is the execution hint of the note that holds the asset. #! - RECIPIENT is the recipient of the asset. +#! - note_idx is the index of the created note. #! #! Panics if: #! - the note sender is not the owner of this faucet. @@ -57,13 +56,13 @@ end #! Invocation: call pub proc distribute exec.is_owner - # => [is_owner, amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] + # => [is_owner, amount, tag, note_type, RECIPIENT, pad(9)] assert.err=ERR_ONLY_OWNER_CAN_MINT - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] + # => [amount, tag, note_type, RECIPIENT, pad(9)] exec.faucets::distribute - # => [pad(16)] + # => [note_idx, pad(15)] end #! Burns the fungible asset from the active note. diff --git a/crates/miden-standards/asm/standards/notes/mint.masm b/crates/miden-standards/asm/standards/notes/mint.masm index 575bc04c3d..9d11b9211a 100644 --- a/crates/miden-standards/asm/standards/notes/mint.masm +++ b/crates/miden-standards/asm/standards/notes/mint.masm @@ -1,22 +1,28 @@ use miden::protocol::active_note use miden::protocol::note +use miden::protocol::output_note use miden::standards::faucets::network_fungible->network_faucet # CONSTANTS # ================================================================================================= -const MINT_NOTE_NUM_INPUTS_PRIVATE=8 -const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 +const MINT_NOTE_NUM_INPUTS_PRIVATE=12 +const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=16 const OUTPUT_NOTE_TYPE_PUBLIC=1 const OUTPUT_NOTE_TYPE_PRIVATE=2 -const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 -const OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 +# Memory Addresses of MINT note inputs +# The attachment is at the same memory address for both private and public inputs. +const ATTACHMENT_TYPE_ADDRESS=2 +const ATTACHMENT_CONTENT_TYPE_ADDRESS=3 +const ATTACHMENT_ADDRESS=4 +const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=16 # ERRORS # ================================================================================================= -const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" + +const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 12 inputs for private or 16+ inputs for public output notes" #! Network Faucet MINT script: mints assets by calling the network faucet's distribute function. #! This note is intended to be executed against a network fungible faucet account. @@ -30,26 +36,28 @@ const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for #! Note inputs support two modes. Depending on the number of note inputs, #! a private or public note is created on consumption of the MINT note: #! -#! Private mode (8 inputs) - creates a private note: -#! - execution_hint: Execution hint for the output note -#! - aux: Auxiliary data for the output note +#! Private mode (12 inputs) - creates a private note: #! - tag: Note tag for the output note #! - amount: The amount to mint +#! - attachment_content_type: The content type of the attachment. +#! - attachment_type: The user-defined type of the attachment. +#! - ATTACHMENT: The attachment to be set. #! - RECIPIENT: The recipient digest (4 elements) #! -#! Public mode (12+ inputs) - creates a public note with variable-length inputs: -#! - execution_hint: Execution hint for the output note -#! - aux: Auxiliary data for the output note +#! Public mode (16+ inputs) - creates a public note with variable-length inputs: #! - tag: Note tag for the output note #! - amount: The amount to mint +#! - attachment_content_type: The content type of the attachment. +#! - attachment_type: The user-defined type of the attachment. +#! - ATTACHMENT: The attachment to be set. #! - SCRIPT_ROOT: Script root of the output note (4 elements) #! - SERIAL_NUM: Serial number of the output note (4 elements) #! - [INPUTS]: Variable-length inputs for the output note (Vec) -#! The number of output note inputs = num_mint_note_inputs - 12 +#! The number of output note inputs = num_mint_note_inputs - 16 #! #! Panics if: #! - account does not expose distribute procedure. -#! - the number of inputs is not exactly 8 for private or less than 12 for public output notes. +#! - the number of inputs is not exactly 12 for private or less than 16 for public output notes. pub proc main dropw # => [pad(16)] @@ -71,10 +79,10 @@ pub proc main movdn.9 drop # => [EMPTY_WORD, EMPTY_WORD, total_inputs, pad(8)] - mem_loadw_be.4 + mem_loadw_be.8 # => [SCRIPT_ROOT, EMPTY_WORD, total_inputs, pad(8)] - swapw mem_loadw_be.8 + swapw mem_loadw_be.12 # => [SERIAL_NUM, SCRIPT_ROOT, total_inputs, pad(8)] # compute variable length note inputs for the output note @@ -82,20 +90,15 @@ pub proc main # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] push.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR - # => [inputs_ptr = 12, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] + # => [inputs_ptr, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] exec.note::build_recipient # => [RECIPIENT, pad(12)] - swapw mem_loadw_be.0 - # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - + # push note_type, and load tag and amount push.OUTPUT_NOTE_TYPE_PUBLIC - # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - movdn.3 - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - + mem_load.0 mem_load.1 + # => [amount, tag, note_type, RECIPIENT, pad(12)] else # private output note creation @@ -105,24 +108,32 @@ pub proc main drop # => [pad(16)] - mem_loadw_be.4 + mem_loadw_be.8 # => [RECIPIENT, pad(12)] - swapw mem_loadw_be.0 - # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - + # push note_type, and load tag and amount push.OUTPUT_NOTE_TYPE_PRIVATE - # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - movdn.3 - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - + mem_load.0 mem_load.1 + # => [amount, tag, note_type, RECIPIENT, pad(12)] end - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + # => [amount, tag, note_type, RECIPIENT, pad(12)] + # distribute expects 9 pad elements, returns 15 and 12 are provided here. + # so the total number of pads after calling is 12 + (15-9) = 18 call.network_faucet::distribute - # => [pad(17))] + # => [note_idx, pad(18))] + + padw mem_loadw_be.ATTACHMENT_ADDRESS + # => [ATTACHMENT, note_idx, pad(18))] + + mem_load.ATTACHMENT_TYPE_ADDRESS + mem_load.ATTACHMENT_CONTENT_TYPE_ADDRESS + movup.6 + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(18))] + + exec.output_note::set_attachment + # => [pad(18))] - drop + drop drop # => [pad(16)] end diff --git a/crates/miden-standards/asm/standards/notes/swap.masm b/crates/miden-standards/asm/standards/notes/swap.masm index f95d718e92..81f7479447 100644 --- a/crates/miden-standards/asm/standards/notes/swap.masm +++ b/crates/miden-standards/asm/standards/notes/swap.masm @@ -5,12 +5,20 @@ use miden::standards::wallets::basic->wallet # CONSTANTS # ================================================================================================= -const SWAP_NOTE_INPUTS_NUMBER=12 +const SWAP_NOTE_INPUTS_NUMBER=16 -# ERRORS +const PAYBACK_NOTE_TYPE_ADDRESS=0 +const PAYBACK_NOTE_TAG_ADDRESS=1 +const ATTACHMENT_TYPE_ADDRESS=2 +const ATTACHMENT_CONTENT_TYPE_ADDRESS=3 +const ATTACHMENT_ADDRESS=4 +const REQUESTED_ASSET_ADDRESS=8 +const PAYBACK_RECIPIENT_ADDRESS=12 + +# ERRORS # ================================================================================================= -const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" +const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 16 note inputs" const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" @@ -25,12 +33,13 @@ const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset #! Outputs: [] #! #! Note inputs are assumed to be as follows: -#! - REQUESTED_ASSET -#! - PAYBACK_NOTE_RECIPIENT -#! - payback_note_execution_hint #! - payback_note_type -#! - payback_note_aux #! - payback_note_tag +#! - attachment_type +#! - attachment_content_type +#! - ATTACHMENT +#! - REQUESTED_ASSET +#! - PAYBACK_RECIPIENT #! #! Panics if: #! - account does not expose miden::standards::wallets::basic::receive_asset procedure. @@ -38,6 +47,8 @@ const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset #! - account vault does not contain the requested asset. #! - adding a fungible asset would result in amount overflow, i.e., the total amount would be #! greater than 2^63. +#! - any of the attachment types does not fit into a u32. +#! - the attachment content type is an unknown variant. pub proc main # dropping note args dropw @@ -49,21 +60,21 @@ pub proc main push.0 exec.active_note::get_inputs # => [num_inputs, inputs_ptr] - # make sure the number of inputs is 12 + # check number of inputs eq.SWAP_NOTE_INPUTS_NUMBER assert.err=ERR_SWAP_WRONG_NUMBER_OF_INPUTS - # => [inputs_ptr] + drop + # => [] - # load REQUESTED_ASSET - mem_loadw_be + mem_loadw_be.REQUESTED_ASSET_ADDRESS # => [REQUESTED_ASSET] - # load PAYBACK_NOTE_RECIPIENT - padw mem_loadw_be.4 + padw mem_loadw_be.PAYBACK_RECIPIENT_ADDRESS # => [PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] # load payback P2ID details - padw mem_loadw_be.8 - # => [tag, aux, note_type, execution_hint, PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] + mem_load.PAYBACK_NOTE_TYPE_ADDRESS + mem_load.PAYBACK_NOTE_TAG_ADDRESS + # => [tag, note_type, PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] # create payback P2ID note exec.output_note::create @@ -83,7 +94,16 @@ pub proc main call.wallet::move_asset_to_note # => [REQUESTED_ASSET, note_idx, pad(11)] - dropw drop push.0 + dropw + # => [note_idx, pad(11)] + + mem_loadw_be.ATTACHMENT_ADDRESS + mem_load.ATTACHMENT_TYPE_ADDRESS + mem_load.ATTACHMENT_CONTENT_TYPE_ADDRESS + movup.6 + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + + exec.output_note::set_attachment # => [pad(12)] # --- move assets from the SWAP note into the account ------------------------- @@ -109,4 +129,4 @@ pub proc main dropw end # => [] -end +end \ No newline at end of file diff --git a/crates/miden-standards/src/account/interface/component.rs b/crates/miden-standards/src/account/interface/component.rs index 75bba9bb8f..14eb5e24ad 100644 --- a/crates/miden-standards/src/account/interface/component.rs +++ b/crates/miden-standards/src/account/interface/component.rs @@ -226,18 +226,16 @@ impl AccountComponentInterface { } body.push_str(&format!( - "push.{recipient} - push.{execution_hint} + " + push.{recipient} push.{note_type} - push.{aux} - push.{tag}\n", + push.{tag} + # => [tag, note_type, RECIPIENT, pad(16)] + ", recipient = partial_note.recipient_digest(), note_type = Felt::from(partial_note.metadata().note_type()), - execution_hint = Felt::from(partial_note.metadata().execution_hint()), - aux = partial_note.metadata().aux(), tag = Felt::from(partial_note.metadata().tag()), )); - // stack => [tag, aux, note_type, execution_hint, RECIPIENT] match self { AccountComponentInterface::BasicFungibleFaucet => { @@ -256,27 +254,36 @@ impl AccountComponentInterface { } body.push_str(&format!( - "push.{amount} - call.::miden::standards::faucets::basic_fungible::distribute dropw dropw drop\n", + " + push.{amount} + call.::miden::standards::faucets::basic_fungible::distribute + # => [note_idx, pad(25)] + swapdw dropw dropw swap drop + # => [note_idx, pad(16)]\n + ", amount = asset.unwrap_fungible().amount() )); - // stack => [] }, AccountComponentInterface::BasicWallet => { - body.push_str("call.::miden::protocol::output_note::create\n"); - // stack => [note_idx] + body.push_str( + " + exec.::miden::protocol::output_note::create + # => [note_idx, pad(16)]\n + ", + ); for asset in partial_note.assets().iter() { body.push_str(&format!( - "push.{asset} - call.::miden::standards::wallets::basic::move_asset_to_note dropw\n", + " + push.{asset} + # => [ASSET, note_idx, pad(16)] + call.::miden::standards::wallets::basic::move_asset_to_note + dropw + # => [note_idx, pad(16)]\n + ", asset = Word::from(*asset) )); - // stack => [note_idx] } - - body.push_str("dropw dropw dropw drop\n"); - // stack => [] }, _ => { return Err(AccountInterfaceError::UnsupportedInterface { @@ -284,6 +291,22 @@ impl AccountComponentInterface { }); }, } + + body.push_str(&format!( + " + push.{ATTACHMENT} + push.{attachment_type} + push.{attachment_content_type} + movup.6 + # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(16)] + exec.::miden::protocol::output_note::set_attachment + # => [pad(16)] + ", + ATTACHMENT = partial_note.metadata().to_attachment_word(), + attachment_type = partial_note.metadata().attachment().attachment_type().as_u32(), + attachment_content_type = + partial_note.metadata().attachment().content_type().as_u8(), + )); } Ok(body) diff --git a/crates/miden-standards/src/account/interface/test.rs b/crates/miden-standards/src/account/interface/test.rs index 22aa38c005..002ae1822f 100644 --- a/crates/miden-standards/src/account/interface/test.rs +++ b/crates/miden-standards/src/account/interface/test.rs @@ -6,6 +6,7 @@ use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; use miden_protocol::note::{ Note, NoteAssets, + NoteAttachment, NoteInputs, NoteMetadata, NoteRecipient, @@ -16,7 +17,7 @@ use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, }; -use miden_protocol::{Felt, NoteError, Word, ZERO}; +use miden_protocol::{Felt, NoteError, Word}; use crate::AuthScheme; use crate::account::auth::{ @@ -99,9 +100,9 @@ fn test_basic_wallet_default_notes() { offered_asset, requested_asset, NoteType::Public, - ZERO, + NoteAttachment::default(), NoteType::Public, - ZERO, + NoteAttachment::default(), &mut RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])), ) .unwrap(); @@ -192,9 +193,9 @@ fn test_custom_account_default_note() { offered_asset, requested_asset, NoteType::Public, - ZERO, + NoteAttachment::default(), NoteType::Public, - ZERO, + NoteAttachment::default(), &mut RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])), ) .unwrap(); @@ -225,9 +226,9 @@ fn test_required_asset_same_as_offered() { offered_asset, requested_asset, NoteType::Public, - ZERO, + NoteAttachment::default(), NoteType::Public, - ZERO, + NoteAttachment::default(), &mut RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])), ); diff --git a/crates/miden-standards/src/errors/standards.rs b/crates/miden-standards/src/errors/standards.rs index 3bfee4952e..85a861122f 100644 --- a/crates/miden-standards/src/errors/standards.rs +++ b/crates/miden-standards/src/errors/standards.rs @@ -18,8 +18,8 @@ pub const ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED: M /// Error Message: "number of approvers must be equal to or greater than threshold" pub const ERR_MALFORMED_MULTISIG_CONFIG: MasmError = MasmError::from_static_str("number of approvers must be equal to or greater than threshold"); -/// Error Message: "MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" -pub const ERR_MINT_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes"); +/// Error Message: "MINT script expects exactly 12 inputs for private or 16+ inputs for public output notes" +pub const ERR_MINT_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("MINT script expects exactly 12 inputs for private or 16+ inputs for public output notes"); /// Error Message: "note sender is not the owner of the faucet who can mint assets" pub const ERR_ONLY_OWNER_CAN_MINT: MasmError = MasmError::from_static_str("note sender is not the owner of the faucet who can mint assets"); @@ -42,8 +42,8 @@ pub const ERR_P2ID_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_st /// Error Message: "SWAP script requires exactly 1 note asset" pub const ERR_SWAP_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("SWAP script requires exactly 1 note asset"); -/// Error Message: "SWAP script expects exactly 12 note inputs" -pub const ERR_SWAP_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("SWAP script expects exactly 12 note inputs"); +/// Error Message: "SWAP script expects exactly 16 note inputs" +pub const ERR_SWAP_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("SWAP script expects exactly 16 note inputs"); /// Error Message: "failed to approve multisig transaction as it was already executed" pub const ERR_TX_ALREADY_EXECUTED: MasmError = MasmError::from_static_str("failed to approve multisig transaction as it was already executed"); diff --git a/crates/miden-standards/src/note/mint_inputs.rs b/crates/miden-standards/src/note/mint_inputs.rs index c5ddb2c63d..20ee801757 100644 --- a/crates/miden-standards/src/note/mint_inputs.rs +++ b/crates/miden-standards/src/note/mint_inputs.rs @@ -1,11 +1,13 @@ -use miden_protocol::note::{NoteExecutionHint, NoteInputs, NoteRecipient}; +use alloc::vec::Vec; + +use miden_protocol::note::{NoteAttachment, NoteInputs, NoteRecipient}; use miden_protocol::{Felt, MAX_INPUTS_PER_NOTE, NoteError, Word}; /// Represents the different input formats for MINT notes. -/// - Private: Creates a private output note using a precomputed recipient digest (8 MINT note +/// - Private: Creates a private output note using a precomputed recipient digest (12 MINT note /// inputs) /// - Public: Creates a public output note by providing script root, serial number, and -/// variable-length inputs (12+ MINT note inputs: 12 fixed + variable number of output note +/// variable-length inputs (16+ MINT note inputs: 16 fixed + variable number of output note /// inputs) #[derive(Debug, Clone, PartialEq, Eq)] pub enum MintNoteInputs { @@ -13,32 +15,23 @@ pub enum MintNoteInputs { recipient_digest: Word, amount: Felt, tag: Felt, - execution_hint: NoteExecutionHint, - aux: Felt, + attachment: NoteAttachment, }, Public { recipient: NoteRecipient, amount: Felt, tag: Felt, - execution_hint: NoteExecutionHint, - aux: Felt, + attachment: NoteAttachment, }, } impl MintNoteInputs { - pub fn new_private( - recipient_digest: Word, - amount: Felt, - tag: Felt, - execution_hint: NoteExecutionHint, - aux: Felt, - ) -> Self { + pub fn new_private(recipient_digest: Word, amount: Felt, tag: Felt) -> Self { Self::Private { recipient_digest, amount, tag, - execution_hint, - aux, + attachment: NoteAttachment::default(), } } @@ -46,13 +39,11 @@ impl MintNoteInputs { recipient: NoteRecipient, amount: Felt, tag: Felt, - execution_hint: NoteExecutionHint, - aux: Felt, ) -> Result { // Calculate total number of inputs that will be created: - // 12 fixed inputs (execution_hint, aux, tag, amount, SCRIPT_ROOT, SERIAL_NUM) + - // variable recipient inputs length - const FIXED_PUBLIC_INPUTS: usize = 12; + // 16 fixed inputs (tag, amount, attachment_type, attachment_content_type, ATTACHMENT, + // SCRIPT_ROOT, SERIAL_NUM) + variable recipient inputs length + const FIXED_PUBLIC_INPUTS: usize = 16; let total_inputs = FIXED_PUBLIC_INPUTS + recipient.inputs().num_values() as usize; if total_inputs > MAX_INPUTS_PER_NOTE { @@ -63,10 +54,29 @@ impl MintNoteInputs { recipient, amount, tag, - execution_hint, - aux, + attachment: NoteAttachment::default(), }) } + + /// Overwrites the [`NoteAttachment`] of the note inputs. + pub fn with_attachment(self, attachment: NoteAttachment) -> Self { + match self { + MintNoteInputs::Private { + recipient_digest, + amount, + tag, + attachment: _, + } => MintNoteInputs::Private { + recipient_digest, + amount, + tag, + attachment, + }, + MintNoteInputs::Public { recipient, amount, tag, attachment: _ } => { + MintNoteInputs::Public { recipient, amount, tag, attachment } + }, + } + } } impl From for NoteInputs { @@ -76,22 +86,31 @@ impl From for NoteInputs { recipient_digest, amount, tag, - execution_hint, - aux, + attachment, } => { - let mut input_values = vec![execution_hint.into(), aux, tag, amount]; + let attachment_type = Felt::from(attachment.attachment_type().as_u32()); + let attachment_content_type = Felt::from(attachment.content_type().as_u8()); + let attachment = attachment.content().to_word(); + + let mut input_values = Vec::with_capacity(12); + input_values.extend_from_slice(&[ + tag, + amount, + attachment_type, + attachment_content_type, + ]); + input_values.extend_from_slice(attachment.as_elements()); input_values.extend_from_slice(recipient_digest.as_elements()); NoteInputs::new(input_values) .expect("number of inputs should not exceed max inputs") }, - MintNoteInputs::Public { - recipient, - amount, - tag, - execution_hint, - aux, - } => { - let mut input_values = vec![execution_hint.into(), aux, tag, amount]; + MintNoteInputs::Public { recipient, amount, tag, attachment } => { + let attachment_type = Felt::from(attachment.attachment_type().as_u32()); + let attachment_content_type = Felt::from(attachment.content_type().as_u8()); + let attachment = attachment.content().to_word(); + + let mut input_values = vec![tag, amount, attachment_type, attachment_content_type]; + input_values.extend_from_slice(attachment.as_elements()); input_values.extend_from_slice(recipient.script().root().as_elements()); input_values.extend_from_slice(recipient.serial_num().as_elements()); input_values.extend_from_slice(recipient.inputs().values()); diff --git a/crates/miden-standards/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs index 7b54636b87..6ef31e3fc5 100644 --- a/crates/miden-standards/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -7,8 +7,8 @@ use miden_protocol::crypto::rand::FeltRng; use miden_protocol::note::{ Note, NoteAssets, + NoteAttachment, NoteDetails, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -49,8 +49,7 @@ pub fn create_p2id_note( target: AccountId, assets: Vec, note_type: NoteType, - // TODO(note_attachment): Replace with note attachment. - _aux: Felt, + attachment: NoteAttachment, rng: &mut R, ) -> Result { let serial_num = rng.draw_word(); @@ -58,7 +57,7 @@ pub fn create_p2id_note( let tag = NoteTag::with_account_target(target); - let metadata = NoteMetadata::new(sender, note_type, tag); + let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment); let vault = NoteAssets::new(assets)?; Ok(Note::new(vault, metadata, recipient)) @@ -84,8 +83,7 @@ pub fn create_p2ide_note( reclaim_height: Option, timelock_height: Option, note_type: NoteType, - // TODO(note_attachment): Replace with note attachment. - _aux: Felt, + attachment: NoteAttachment, rng: &mut R, ) -> Result { let serial_num = rng.draw_word(); @@ -93,14 +91,14 @@ pub fn create_p2ide_note( utils::build_p2ide_recipient(target, reclaim_height, timelock_height, serial_num)?; let tag = NoteTag::with_account_target(target); - let metadata = NoteMetadata::new(sender, note_type, tag); + let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment); let vault = NoteAssets::new(assets)?; Ok(Note::new(vault, metadata, recipient)) } /// Generates a SWAP note - swap of assets between two accounts - and returns the note as well as -/// [NoteDetails] for the payback note. +/// [`NoteDetails`] for the payback note. /// /// This script enables a swap of 2 assets between the `sender` account and any other account that /// is willing to consume the note. The consumer will receive the `offered_asset` and will create a @@ -113,11 +111,9 @@ pub fn create_swap_note( offered_asset: Asset, requested_asset: Asset, swap_note_type: NoteType, - // TODO(note_attachment): Replace with note attachment. - _swap_note_aux: Felt, + swap_note_attachment: NoteAttachment, payback_note_type: NoteType, - // TODO(note_attachment): Replace with note attachment. - payback_note_aux: Felt, + payback_note_attachment: NoteAttachment, rng: &mut R, ) -> Result<(Note, NoteDetails), NoteError> { if requested_asset == offered_asset { @@ -129,31 +125,32 @@ pub fn create_swap_note( let payback_serial_num = rng.draw_word(); let payback_recipient = utils::build_p2id_recipient(sender, payback_serial_num)?; - let payback_recipient_word: Word = payback_recipient.digest(); let requested_asset_word: Word = requested_asset.into(); let payback_tag = NoteTag::with_account_target(sender); - let inputs = NoteInputs::new(vec![ - requested_asset_word[0], - requested_asset_word[1], - requested_asset_word[2], - requested_asset_word[3], - payback_recipient_word[0], - payback_recipient_word[1], - payback_recipient_word[2], - payback_recipient_word[3], - NoteExecutionHint::always().into(), + let attachment_type = Felt::from(payback_note_attachment.attachment_type().as_u32()); + let attachment_content_type = Felt::from(payback_note_attachment.content_type().as_u8()); + let attachment = payback_note_attachment.content().to_word(); + + let mut inputs = Vec::with_capacity(16); + inputs.extend_from_slice(&[ payback_note_type.into(), - payback_note_aux, payback_tag.into(), - ])?; + attachment_type, + attachment_content_type, + ]); + inputs.extend_from_slice(attachment.as_elements()); + inputs.extend_from_slice(requested_asset_word.as_elements()); + inputs.extend_from_slice(payback_recipient.digest().as_elements()); + let inputs = NoteInputs::new(inputs)?; // build the tag for the SWAP use case let tag = build_swap_tag(swap_note_type, &offered_asset, &requested_asset); let serial_num = rng.draw_word(); // build the outgoing note - let metadata = NoteMetadata::new(sender, swap_note_type, tag); + let metadata = + NoteMetadata::new(sender, swap_note_type, tag).with_attachment(swap_note_attachment); let assets = NoteAssets::new(vec![offered_asset])?; let recipient = NoteRecipient::new(serial_num, note_script, inputs); let note = Note::new(assets, metadata, recipient); @@ -182,7 +179,7 @@ pub fn create_swap_note( /// - `faucet_id`: The account ID of the network faucet that will mint the assets /// - `sender`: The account ID of the note creator (must be the faucet owner) /// - `mint_inputs`: The input configuration specifying private or public output mode -/// - `aux`: Auxiliary data for the MINT note +/// - `attachment`: The [`NoteAttachment`] of the MINT note /// - `rng`: Random number generator for creating the serial number /// /// # Errors @@ -191,8 +188,7 @@ pub fn create_mint_note( faucet_id: AccountId, sender: AccountId, mint_inputs: MintNoteInputs, - // TODO(note_attachment): Replace with note attachment. - _aux: Felt, + attachment: NoteAttachment, rng: &mut R, ) -> Result { let note_script = WellKnownNote::MINT.script(); @@ -206,7 +202,7 @@ pub fn create_mint_note( let tag = NoteTag::with_account_target(faucet_id); - let metadata = NoteMetadata::new(sender, note_type, tag); + let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment); let assets = NoteAssets::new(vec![])?; // MINT notes have no assets let recipient = NoteRecipient::new(serial_num, note_script, inputs); @@ -229,7 +225,7 @@ pub fn create_mint_note( /// - `sender`: The account ID of the note creator /// - `faucet_id`: The account ID of the faucet that will burn the assets /// - `fungible_asset`: The fungible asset to be burned -/// - `aux`: Auxiliary data for the note +/// - `attachment`: The [`NoteAttachment`] of the BURN note /// - `rng`: Random number generator for creating the serial number /// /// # Errors @@ -238,8 +234,7 @@ pub fn create_burn_note( sender: AccountId, faucet_id: AccountId, fungible_asset: Asset, - // TODO(note_attachment): Replace with note attachment. - _aux: Felt, + attachment: NoteAttachment, rng: &mut R, ) -> Result { let note_script = WellKnownNote::BURN.script(); @@ -251,7 +246,7 @@ pub fn create_burn_note( let inputs = NoteInputs::new(vec![])?; let tag = NoteTag::with_account_target(faucet_id); - let metadata = NoteMetadata::new(sender, note_type, tag); + let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment); let assets = NoteAssets::new(vec![fungible_asset])?; // BURN notes contain the asset to burn let recipient = NoteRecipient::new(serial_num, note_script, inputs); diff --git a/crates/miden-standards/src/note/well_known_note.rs b/crates/miden-standards/src/note/well_known_note.rs index df0f219782..98d44c59e2 100644 --- a/crates/miden-standards/src/note/well_known_note.rs +++ b/crates/miden-standards/src/note/well_known_note.rs @@ -125,7 +125,7 @@ impl WellKnownNote { const P2IDE_NUM_INPUTS: usize = 4; /// Expected number of inputs of the SWAP note. - const SWAP_NUM_INPUTS: usize = 10; + const SWAP_NUM_INPUTS: usize = 16; /// Expected number of inputs of the MINT note (private mode). const MINT_NUM_INPUTS_PRIVATE: usize = 8; diff --git a/crates/miden-standards/src/testing/note.rs b/crates/miden-standards/src/testing/note.rs index 9ade886bd2..7312ed0ad9 100644 --- a/crates/miden-standards/src/testing/note.rs +++ b/crates/miden-standards/src/testing/note.rs @@ -10,7 +10,6 @@ use miden_protocol::note::{ Note, NoteAssets, NoteAttachment, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -18,7 +17,7 @@ use miden_protocol::note::{ NoteType, }; use miden_protocol::testing::note::DEFAULT_NOTE_CODE; -use miden_protocol::{Felt, NoteError, Word, ZERO}; +use miden_protocol::{Felt, NoteError, Word}; use rand::Rng; use crate::code_builder::CodeBuilder; @@ -32,13 +31,9 @@ pub struct NoteBuilder { inputs: Vec, assets: Vec, note_type: NoteType, - // TODO(note_attachment): Remove. - note_execution_hint: NoteExecutionHint, serial_num: Word, tag: NoteTag, code: String, - // TODO(note_attachment): Remove. - aux: Felt, attachment: NoteAttachment, dyn_libraries: Vec, source_manager: Arc, @@ -58,12 +53,10 @@ impl NoteBuilder { inputs: vec![], assets: vec![], note_type: NoteType::Public, - note_execution_hint: NoteExecutionHint::None, serial_num, // The note tag is not under test, so we choose a value that is always valid. tag: NoteTag::with_account_target(sender), code: DEFAULT_NOTE_CODE.to_string(), - aux: ZERO, attachment: NoteAttachment::default(), dyn_libraries: Vec::new(), source_manager: Arc::new(DefaultSourceManager::default()), @@ -87,11 +80,6 @@ impl NoteBuilder { self } - pub fn note_execution_hint(mut self, note_execution_hint: NoteExecutionHint) -> Self { - self.note_execution_hint = note_execution_hint; - self - } - pub fn tag(mut self, tag: u32) -> Self { self.tag = tag.into(); self @@ -113,11 +101,6 @@ impl NoteBuilder { self } - pub fn aux(mut self, aux: Felt) -> Self { - self.aux = aux; - self - } - /// Overwrites the attachment. pub fn attachment(mut self, attachment: impl Into) -> Self { self.attachment = attachment.into(); diff --git a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs index 18389782c2..4bb31ba9af 100644 --- a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs @@ -7,8 +7,8 @@ use miden_processor::crypto::MerklePath; use miden_protocol::asset::FungibleAsset; use miden_protocol::block::{BlockInputs, BlockNumber, ProposedBlock}; use miden_protocol::crypto::merkle::SparseMerklePath; -use miden_protocol::note::{NoteInclusionProof, NoteType}; -use miden_protocol::{MAX_BATCHES_PER_BLOCK, ProposedBlockError, ZERO}; +use miden_protocol::note::{NoteAttachment, NoteInclusionProof, NoteType}; +use miden_protocol::{MAX_BATCHES_PER_BLOCK, ProposedBlockError}; use miden_standards::note::create_p2id_note; use miden_tx::LocalTransactionProver; @@ -352,7 +352,7 @@ async fn proposed_block_fails_on_invalid_proof_or_missing_note_inclusion_referen account1.id(), vec![], NoteType::Private, - ZERO, + NoteAttachment::default(), builder.rng_mut(), )?; let spawn_note = builder.add_spawn_note([&p2id_note])?; diff --git a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs index 92ba3bb8b8..573b5e81d8 100644 --- a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs @@ -3,13 +3,12 @@ use std::collections::BTreeMap; use std::vec::Vec; use anyhow::Context; -use miden_protocol::ZERO; use miden_protocol::asset::FungibleAsset; use miden_protocol::batch::BatchNoteTree; use miden_protocol::block::account_tree::AccountTree; use miden_protocol::block::{BlockInputs, BlockNoteIndex, BlockNoteTree, ProposedBlock}; use miden_protocol::crypto::merkle::smt::Smt; -use miden_protocol::note::NoteType; +use miden_protocol::note::{NoteAttachment, NoteType}; use miden_protocol::transaction::InputNoteCommitment; use miden_standards::note::create_p2id_note; @@ -38,7 +37,7 @@ async fn proven_block_success() -> anyhow::Result<()> { account0.id(), vec![asset], NoteType::Private, - ZERO, + NoteAttachment::default(), builder.rng_mut(), )?; let output_note1 = create_p2id_note( @@ -46,7 +45,7 @@ async fn proven_block_success() -> anyhow::Result<()> { account1.id(), vec![asset], NoteType::Private, - ZERO, + NoteAttachment::default(), builder.rng_mut(), )?; let output_note2 = create_p2id_note( @@ -54,7 +53,7 @@ async fn proven_block_success() -> anyhow::Result<()> { account2.id(), vec![asset], NoteType::Private, - ZERO, + NoteAttachment::default(), builder.rng_mut(), )?; let output_note3 = create_p2id_note( @@ -62,7 +61,7 @@ async fn proven_block_success() -> anyhow::Result<()> { account3.id(), vec![asset], NoteType::Private, - ZERO, + NoteAttachment::default(), builder.rng_mut(), )?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs index da54be8b2c..e7e49b7efb 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_delta.rs @@ -18,7 +18,7 @@ use miden_protocol::account::{ StorageSlotName, }; use miden_protocol::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; -use miden_protocol::note::{Note, NoteExecutionHint, NoteTag, NoteType}; +use miden_protocol::note::{Note, NoteTag, NoteType}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, @@ -604,15 +604,8 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { let tag3 = NoteTag::default(); let tags = [tag1, tag2, tag3]; - let aux_array = [Felt::new(27), Felt::new(28), Felt::new(29)]; - let note_types = [NoteType::Private; 3]; - let execution_hint_1 = Felt::from(NoteExecutionHint::always()); - let execution_hint_2 = Felt::from(NoteExecutionHint::none()); - let execution_hint_3 = Felt::from(NoteExecutionHint::on_block_slot(1, 1, 1)); - let hints = [execution_hint_1, execution_hint_2, execution_hint_3]; - let mut send_asset_script = String::new(); for i in 0..3 { send_asset_script.push_str(&format!( @@ -620,18 +613,12 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { ### note {i} # prepare the stack for a new note creation push.0.1.2.3 # recipient - push.{EXECUTION_HINT} # note_execution_hint push.{NOTETYPE} # note_type - push.{aux} # aux push.{tag} # tag - # => [tag, aux, note_type, execution_hint, RECIPIENT] - - # pad the stack before calling the `create_note` - padw padw swapdw - # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + # => [tag, note_type, RECIPIENT] # create the note - call.output_note::create + exec.output_note::create # => [note_idx, pad(15)] # move an asset to the created note to partially deplete fungible asset balance @@ -642,9 +629,7 @@ async fn asset_and_storage_delta() -> anyhow::Result<()> { # clear the stack dropw dropw dropw dropw ", - EXECUTION_HINT = hints[i], NOTETYPE = note_types[i] as u8, - aux = aux_array[i], tag = tags[i], REMOVED_ASSET = Word::from(removed_assets[i]) )); @@ -1129,13 +1114,11 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " #! Outputs: [] proc create_note_with_asset push.0.1.2.3 # recipient - push.1 # note_execution_hint push.2 # note_type private - push.0 # aux push.0xC0000000 # tag - # => [tag, aux, note_type, execution_hint, RECIPIENT, ASSET] + # => [tag, note_type, RECIPIENT, ASSET] - exec.create_note + exec.output_note::create # => [note_idx, ASSET] movdn.4 @@ -1145,19 +1128,6 @@ const TEST_ACCOUNT_CONVENIENCE_WRAPPERS: &str = " # => [] end - #! Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] - #! Outputs: [note_idx] - proc create_note - repeat.8 push.0 movdn.8 end - # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - - call.output_note::create - # => [note_idx, pad(15)] - - repeat.15 swap drop end - # => [note_idx] - end - #! Inputs: [ASSET, note_idx] #! Outputs: [] proc move_asset_to_note diff --git a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs index 78eeaa951f..958727913e 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_epilogue.rs @@ -61,7 +61,6 @@ async fn test_transaction_epilogue() -> anyhow::Result<()> { let code = format!( " use $kernel::prologue - use $kernel::account use $kernel::epilogue use miden::protocol::output_note use miden::core::sys @@ -70,9 +69,7 @@ async fn test_transaction_epilogue() -> anyhow::Result<()> { exec.prologue::prepare_transaction push.{recipient} - push.{note_execution_hint} push.{note_type} - push.{aux} push.{tag} exec.output_note::create # => [note_idx] @@ -88,9 +85,7 @@ async fn test_transaction_epilogue() -> anyhow::Result<()> { end ", recipient = output_note_1.recipient().digest(), - note_execution_hint = Felt::from(output_note_1.metadata().execution_hint()), note_type = Felt::from(output_note_1.metadata().note_type()), - aux = output_note_1.metadata().aux(), tag = Felt::from(output_note_1.metadata().tag()), asset = Word::from(asset) ); @@ -187,9 +182,7 @@ async fn test_compute_output_note_id() -> anyhow::Result<()> { code.push_str(&format!( " push.{recipient} - push.{note_execution_hint} push.{note_type} - push.{aux} push.{tag} exec.output_note::create # => [note_idx] @@ -199,9 +192,7 @@ async fn test_compute_output_note_id() -> anyhow::Result<()> { # => [] ", recipient = note.recipient().digest(), - note_execution_hint = Felt::from(note.metadata().execution_hint()), note_type = Felt::from(note.metadata().note_type()), - aux = note.metadata().aux(), tag = Felt::from(note.metadata().tag()), asset = Word::from(asset) )); @@ -566,11 +557,10 @@ async fn test_epilogue_execute_empty_transaction() -> anyhow::Result<()> { async fn test_epilogue_empty_transaction_with_empty_output_note() -> anyhow::Result<()> { let tag = NoteTag::with_account_target(ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE.try_into()?); - let aux = Felt::new(26); let note_type = NoteType::Private; - // create an empty output note in the transaction script - let tx_script_source = format!( + // create an empty output note + let code = format!( r#" use miden::core::word use miden::protocol::output_note @@ -583,25 +573,19 @@ async fn test_epilogue_empty_transaction_with_empty_output_note() -> anyhow::Res # prepare the values for note creation push.1.2.3.4 # recipient - push.1 # note_execution_hint (NoteExecutionHint::Always) push.{note_type} # note_type - push.{aux} # aux push.{tag} # tag - # => [tag, aux, note_type, note_execution_hint, RECIPIENT] - - # pad the stack with zeros before calling the `create_note`. - padw padw swapdw - # => [tag, aux, execution_hint, note_type, RECIPIENT, pad(8)] + # => [tag, note_type, RECIPIENT] # create the note - call.output_note::create - # => [note_idx, GARBAGE(15)] + exec.output_note::create + # => [note_idx] # make sure that output note was created: compare the output note hash with an empty # word exec.note::compute_output_notes_commitment exec.word::eqz assertz.err="output note was created, but the output notes hash remains to be zeros" - # => [note_idx, GARBAGE(15)] + # => [note_idx] # clean the stack dropw dropw dropw dropw @@ -615,7 +599,7 @@ async fn test_epilogue_empty_transaction_with_empty_output_note() -> anyhow::Res let tx_context = TransactionContextBuilder::with_noop_auth_account().build()?; - let result = tx_context.execute_code(&tx_script_source).await.map(|_| ()); + let result = tx_context.execute_code(&code).await.map(|_| ()); // assert that even if the output note was created, the transaction is considered empty assert_execution_error!(result, ERR_EPILOGUE_EXECUTED_TRANSACTION_IS_EMPTY); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 678b041a19..49a5be0c60 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -399,8 +399,7 @@ async fn test_build_metadata_header() -> miette::Result<()> { begin exec.prologue::prepare_transaction - push.{note_type} - push.{tag} + push.{note_type} push.{tag} exec.output_note::build_metadata_header # truncate the stack diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index f1ee66af05..f63057b4a6 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -60,7 +60,6 @@ async fn test_create_note() -> anyhow::Result<()> { let account_id = tx_context.account().id(); let recipient = Word::from([0, 1, 2, 3u32]); - let aux = Felt::new(27); let tag = NoteTag::with_account_target(account_id); let code = format!( @@ -73,12 +72,10 @@ async fn test_create_note() -> anyhow::Result<()> { exec.prologue::prepare_transaction push.{recipient} - push.{note_execution_hint} push.{PUBLIC_NOTE} - push.{aux} push.{tag} - call.output_note::create + exec.output_note::create # truncate the stack swapdw dropw dropw @@ -86,7 +83,6 @@ async fn test_create_note() -> anyhow::Result<()> { ", recipient = recipient, PUBLIC_NOTE = NoteType::Public as u8, - note_execution_hint = Felt::from(NoteExecutionHint::after_block(23.into())), tag = tag, ); @@ -155,21 +151,17 @@ fn note_creation_script(tag: Felt) -> String { exec.prologue::prepare_transaction push.{recipient} - push.{execution_hint_always} push.{PUBLIC_NOTE} - push.{aux} push.{tag} - call.output_note::create + exec.output_note::create # clean the stack dropw dropw end ", recipient = Word::from([0, 1, 2, 3u32]), - execution_hint_always = Felt::from(NoteExecutionHint::always()), PUBLIC_NOTE = NoteType::Public as u8, - aux = ZERO, ) } @@ -190,19 +182,15 @@ async fn test_create_note_too_many_notes() -> anyhow::Result<()> { exec.prologue::prepare_transaction push.{recipient} - push.{execution_hint_always} push.{PUBLIC_NOTE} - push.{aux} push.{tag} - call.output_note::create + exec.output_note::create end ", tag = NoteTag::new(1234 << 16 | 5678), recipient = Word::from([0, 1, 2, 3u32]), - execution_hint_always = Felt::from(NoteExecutionHint::always()), PUBLIC_NOTE = NoteType::Public as u8, - aux = ZERO, ); let exec_output = tx_context.execute_code(&code).await; @@ -272,8 +260,13 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { let output_serial_no_2 = Word::from([11u32; 4]); let output_tag_2 = NoteTag::with_account_target(local_account); let assets = NoteAssets::new(vec![input_asset_2])?; + let attachment = NoteAttachment::new_array( + NoteAttachmentType::new(5), + [42, 43, 44, 45, 46u32].map(Felt::from).to_vec(), + )?; let metadata = - NoteMetadata::new(tx_context.tx_inputs().account().id(), NoteType::Public, output_tag_2); + NoteMetadata::new(tx_context.tx_inputs().account().id(), NoteType::Public, output_tag_2) + .with_attachment(attachment); let inputs = NoteInputs::new(vec![])?; let recipient = NoteRecipient::new(output_serial_no_2, input_note_2.script().clone(), inputs); let output_note_2 = Note::new(assets, metadata, recipient); @@ -295,34 +288,36 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { use $kernel::prologue begin - # => [BH, acct_id, IAH, NC] exec.prologue::prepare_transaction # => [] # create output note 1 push.{recipient_1} - push.{NOTE_EXECUTION_HINT_1} push.{PUBLIC_NOTE} - push.{aux_1} push.{tag_1} - call.output_note::create + exec.output_note::create # => [note_idx] push.{asset_1} - call.output_note::add_asset + exec.output_note::add_asset # => [] # create output note 2 push.{recipient_2} - push.{NOTE_EXECUTION_HINT_2} push.{PUBLIC_NOTE} - push.{aux_2} push.{tag_2} - call.output_note::create + exec.output_note::create # => [note_idx] - push.{asset_2} - call.output_note::add_asset + dup push.{asset_2} + exec.output_note::add_asset + # => [note_idx] + + push.{ATTACHMENT2} + push.{attachment_type2} + movup.5 + # => [note_idx, attachment_type, ATTACHMENT] + exec.output_note::set_array_attachment # => [] # compute the output notes commitment @@ -335,20 +330,18 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { end ", PUBLIC_NOTE = NoteType::Public as u8, - NOTE_EXECUTION_HINT_1 = Felt::from(output_note_1.metadata().execution_hint()), recipient_1 = output_note_1.recipient().digest(), tag_1 = output_note_1.metadata().tag(), - aux_1 = output_note_1.metadata().aux(), asset_1 = Word::from( **output_note_1.assets().iter().take(1).collect::>().first().unwrap() ), recipient_2 = output_note_2.recipient().digest(), - NOTE_EXECUTION_HINT_2 = Felt::from(output_note_2.metadata().execution_hint()), tag_2 = output_note_2.metadata().tag(), - aux_2 = output_note_2.metadata().aux(), asset_2 = Word::from( **output_note_2.assets().iter().take(1).collect::>().first().unwrap() ), + ATTACHMENT2 = output_note_2.metadata().to_attachment_word(), + attachment_type2 = output_note_2.metadata().attachment().attachment_type().as_u32(), ); let exec_output = &tx_context.execute_code(&code).await?; @@ -395,7 +388,6 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?; let recipient = Word::from([0, 1, 2, 3u32]); - let aux = Felt::new(27); let tag = NoteTag::with_account_target(faucet_id); let asset = Word::from(FungibleAsset::new(faucet_id, 10)?); @@ -409,12 +401,10 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { exec.prologue::prepare_transaction push.{recipient} - push.{NOTE_EXECUTION_HINT} push.{PUBLIC_NOTE} - push.{aux} push.{tag} - call.output_note::create + exec.output_note::create # => [note_idx] # assert that the index of the created note equals zero @@ -433,7 +423,6 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { ", recipient = recipient, PUBLIC_NOTE = NoteType::Public as u8, - NOTE_EXECUTION_HINT = Felt::from(NoteExecutionHint::always()), tag = tag, asset = asset, ); @@ -457,7 +446,6 @@ async fn test_create_note_and_add_multiple_assets() -> anyhow::Result<()> { let faucet_2 = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2)?; let recipient = Word::from([0, 1, 2, 3u32]); - let aux = Felt::new(27); let tag = NoteTag::with_account_target(faucet_2); let asset = Word::from(FungibleAsset::new(faucet, 10)?); @@ -478,10 +466,8 @@ async fn test_create_note_and_add_multiple_assets() -> anyhow::Result<()> { push.{recipient} push.{PUBLIC_NOTE} - push.{aux} push.{tag} - - call.output_note::create + exec.output_note::create # => [note_idx] # assert that the index of the created note equals zero @@ -558,31 +544,25 @@ async fn test_create_note_and_add_same_nft_twice() -> anyhow::Result<()> { exec.prologue::prepare_transaction # => [] - padw padw push.{recipient} - push.{execution_hint_always} push.{PUBLIC_NOTE} - push.{aux} push.{tag} - - call.output_note::create + exec.output_note::create # => [note_idx] dup push.{nft} # => [NFT, note_idx, note_idx] - call.output_note::add_asset + exec.output_note::add_asset # => [note_idx] push.{nft} - call.output_note::add_asset + exec.output_note::add_asset # => [] end ", recipient = recipient, PUBLIC_NOTE = NoteType::Public as u8, - execution_hint_always = Felt::from(NoteExecutionHint::always()), - aux = Felt::new(0), tag = tag, nft = encoded, ); @@ -634,7 +614,6 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { // create output note let output_serial_no = Word::from([0, 1, 2, 3u32]); - let aux = Felt::new(27); let tag = NoteTag::new(42 << 16 | 42); let single_input = 2; let inputs = NoteInputs::new(vec![Felt::new(single_input)]).unwrap(); @@ -643,46 +622,40 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { let recipient = NoteRecipient::new(output_serial_no, input_note_1.script().clone(), inputs); let code = format!( " + use $kernel::prologue use miden::protocol::output_note use miden::protocol::note - use $kernel::prologue + use miden::core::sys begin exec.prologue::prepare_transaction - # pad the stack before call - padw - # input push.{input_commitment} # SCRIPT_ROOT push.{script_root} # SERIAL_NUM push.{output_serial_no} - # => [SERIAL_NUM, SCRIPT_ROOT, INPUT_COMMITMENT, pad(4)] + # => [SERIAL_NUM, SCRIPT_ROOT, INPUT_COMMITMENT] exec.note::build_recipient_hash # => [RECIPIENT, pad(12)] - push.{execution_hint} push.{PUBLIC_NOTE} - push.{aux} push.{tag} - # => [tag, aux, note_type, execution_hint, RECIPIENT, pad(12)] + # => [tag, note_type, RECIPIENT] - call.output_note::create - # => [note_idx, pad(19)] + exec.output_note::create + # => [note_idx] # clean the stack - dropw dropw dropw dropw dropw + exec.sys::truncate_stack end ", script_root = input_note_1.script().clone().root(), output_serial_no = output_serial_no, PUBLIC_NOTE = NoteType::Public as u8, tag = tag, - execution_hint = Felt::from(NoteExecutionHint::after_block(2.into())), - aux = aux, ); let exec_output = &tx_context.execute_code(&code).await?; @@ -743,7 +716,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?, vec![fungible_asset_0], NoteType::Public, - Felt::new(0), + NoteAttachment::default(), &mut RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])), )?; @@ -752,7 +725,7 @@ async fn test_get_asset_info() -> anyhow::Result<()> { ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?, vec![fungible_asset_0, fungible_asset_1], NoteType::Public, - Felt::new(0), + NoteAttachment::default(), &mut RpoRandomCoin::new(Word::from([4, 3, 2, 1u32])), )?; @@ -764,11 +737,9 @@ async fn test_get_asset_info() -> anyhow::Result<()> { begin # create an output note with fungible asset 0 push.{RECIPIENT} - push.{note_execution_hint} push.{note_type} - push.0 # aux push.{tag} - call.output_note::create + exec.output_note::create # => [note_idx] # move the asset 0 to the note @@ -826,7 +797,6 @@ async fn test_get_asset_info() -> anyhow::Result<()> { "#, // output note RECIPIENT = output_note_1.recipient().digest(), - note_execution_hint = Felt::from(output_note_1.metadata().execution_hint()), note_type = NoteType::Public as u8, tag = output_note_1.metadata().tag(), asset_0 = Word::from(fungible_asset_0), @@ -868,7 +838,7 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?, vec![FungibleAsset::mock(5)], NoteType::Public, - Felt::new(0), + NoteAttachment::default(), &mut RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])), )?; @@ -1048,9 +1018,7 @@ async fn test_set_none_attachment() -> anyhow::Result<()> { begin push.{RECIPIENT} - push.{note_execution_hint} push.{note_type} - push.{aux} push.{tag} exec.output_note::create # => [note_idx] @@ -1069,8 +1037,6 @@ async fn test_set_none_attachment() -> anyhow::Result<()> { ", RECIPIENT = output_note.recipient().unwrap().digest(), note_type = output_note.metadata().note_type() as u8, - aux = output_note.metadata().aux(), - note_execution_hint = Felt::from(output_note.metadata().execution_hint()), tag = output_note.metadata().tag().as_u32(), ATTACHMENT = output_note.metadata().to_attachment_word(), attachment_content_type = @@ -1109,9 +1075,7 @@ async fn test_set_word_attachment() -> anyhow::Result<()> { begin push.{RECIPIENT} - push.{note_execution_hint} push.{note_type} - push.{aux} push.{tag} exec.output_note::create # => [note_idx] @@ -1129,8 +1093,6 @@ async fn test_set_word_attachment() -> anyhow::Result<()> { ", RECIPIENT = output_note.recipient().unwrap().digest(), note_type = output_note.metadata().note_type() as u8, - aux = output_note.metadata().aux(), - note_execution_hint = Felt::from(output_note.metadata().execution_hint()), tag = output_note.metadata().tag().as_u32(), attachment_type = output_note.metadata().attachment().attachment_type().as_u32(), ATTACHMENT = output_note.metadata().to_attachment_word(), @@ -1167,9 +1129,7 @@ async fn test_set_array_attachment() -> anyhow::Result<()> { begin push.{RECIPIENT} - push.{note_execution_hint} push.{note_type} - push.{aux} push.{tag} exec.output_note::create # => [note_idx] @@ -1187,8 +1147,6 @@ async fn test_set_array_attachment() -> anyhow::Result<()> { ", RECIPIENT = output_note.recipient().unwrap().digest(), note_type = output_note.metadata().note_type() as u8, - aux = output_note.metadata().aux(), - note_execution_hint = Felt::from(output_note.metadata().execution_hint()), tag = output_note.metadata().tag().as_u32(), attachment_type = output_note.metadata().attachment().attachment_type().as_u32(), ATTACHMENT = output_note.metadata().to_attachment_word(), @@ -1255,15 +1213,12 @@ fn create_output_note(note: &Note) -> String { " # create an output note push.{RECIPIENT} - push.{note_execution_hint} push.{note_type} - push.0 # aux push.{tag} - call.output_note::create + exec.output_note::create # => [note_idx] ", RECIPIENT = note.recipient().digest(), - note_execution_hint = Felt::from(note.metadata().execution_hint()), note_type = note.metadata().note_type() as u8, tag = Felt::from(note.metadata().tag()), ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index 8d3cfbe6e0..8a50ed7b37 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -21,7 +21,9 @@ use miden_protocol::block::BlockNumber; use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, + NoteAttachment, + NoteAttachmentContent, + NoteAttachmentType, NoteHeader, NoteId, NoteInputs, @@ -48,7 +50,7 @@ use miden_protocol::transaction::{ TransactionKernel, TransactionSummary, }; -use miden_protocol::{Felt, FieldElement, Hasher, ONE, Word}; +use miden_protocol::{Felt, Hasher, ONE, Word}; use miden_standards::AuthScheme; use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_standards::account::wallets::BasicWallet; @@ -202,9 +204,13 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { ); let tag2 = NoteTag::default(); let tag3 = NoteTag::default(); - let aux1 = Felt::new(27); - let aux2 = Felt::new(28); - let aux3 = Felt::new(29); + + let attachment2 = + NoteAttachment::new_word(NoteAttachmentType::new(28), Word::from([2, 3, 4, 5u32])); + let attachment3 = NoteAttachment::new_array( + NoteAttachmentType::new(29), + [6, 7, 8, 9u32].map(Felt::from).to_vec(), + )?; let note_type1 = NoteType::Private; let note_type2 = NoteType::Public; @@ -217,7 +223,8 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let serial_num_2 = Word::from([1, 2, 3, 4u32]); let note_script_2 = CodeBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; let inputs_2 = NoteInputs::new(vec![ONE])?; - let metadata_2 = NoteMetadata::new(account_id, note_type2, tag2); + let metadata_2 = + NoteMetadata::new(account_id, note_type2, tag2).with_attachment(attachment2.clone()); let vault_2 = NoteAssets::new(vec![removed_asset_3, removed_asset_4])?; let recipient_2 = NoteRecipient::new(serial_num_2, note_script_2, inputs_2); let expected_output_note_2 = Note::new(vault_2, metadata_2, recipient_2); @@ -226,7 +233,8 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let serial_num_3 = Word::from([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]); let note_script_3 = CodeBuilder::default().compile_note_script(DEFAULT_NOTE_CODE)?; let inputs_3 = NoteInputs::new(vec![ONE, Felt::new(2)])?; - let metadata_3 = NoteMetadata::new(account_id, note_type3, tag3); + let metadata_3 = + NoteMetadata::new(account_id, note_type3, tag3).with_attachment(attachment3.clone()); let vault_3 = NoteAssets::new(vec![])?; let recipient_3 = NoteRecipient::new(serial_num_3, note_script_3, inputs_3); let expected_output_note_3 = Note::new(vault_3, metadata_3, recipient_3); @@ -236,35 +244,22 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { use miden::standards::wallets::basic->wallet use miden::protocol::output_note - # Inputs: [tag, aux, note_type, execution_hint, RECIPIENT] - # Outputs: [note_idx] - proc create_note - # pad the stack before the call to prevent accidental modification of the deeper stack - # elements - padw padw swapdw - # => [tag, aux, execution_hint, note_type, RECIPIENT, pad(8)] - - call.output_note::create - # => [note_idx, pad(15)] - - # remove excess PADs from the stack - swapdw dropw dropw movdn.7 dropw drop drop drop - # => [note_idx] - end - - # Inputs: [ASSET, note_idx] - # Outputs: [ASSET, note_idx] + #! Wrapper around move_asset_to_note for use with exec. + #! + #! Inputs: [ASSET, note_idx] + #! Outputs: [note_idx] proc move_asset_to_note # pad the stack before call push.0.0.0 movdn.7 movdn.7 movdn.7 padw padw swapdw # => [ASSET, note_idx, pad(11)] call.wallet::move_asset_to_note - # => [ASSET, note_idx, pad(11)] + dropw + # => [note_idx, pad(11)] # remove excess PADs from the stack - swapdw dropw dropw swapw movdn.7 drop drop drop - # => [ASSET, note_idx] + repeat.11 swap drop end + # => [note_idx] end ## TRANSACTION SCRIPT @@ -274,47 +269,54 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { ## ------------------------------------------------------------------------------------ # partially deplete fungible asset balance push.0.1.2.3 # recipient - push.{EXECUTION_HINT_1} # note execution hint push.{NOTETYPE1} # note_type - push.{aux1} # aux push.{tag1} # tag - exec.create_note - # => [note_idx] + exec.output_note::create + # => [note_idx = 0] push.{REMOVED_ASSET_1} # asset_1 # => [ASSET, note_idx] - exec.move_asset_to_note dropw + exec.move_asset_to_note # => [note_idx] push.{REMOVED_ASSET_2} # asset_2 - exec.move_asset_to_note dropw drop + exec.move_asset_to_note + drop # => [] # send non-fungible asset push.{RECIPIENT2} # recipient - push.{EXECUTION_HINT_2} # note execution hint push.{NOTETYPE2} # note_type - push.{aux2} # aux push.{tag2} # tag - exec.create_note - # => [note_idx] + exec.output_note::create + # => [note_idx = 1] push.{REMOVED_ASSET_3} # asset_3 - exec.move_asset_to_note dropw + exec.move_asset_to_note # => [note_idx] push.{REMOVED_ASSET_4} # asset_4 - exec.move_asset_to_note dropw drop + exec.move_asset_to_note + # => [note_idx] + + push.{ATTACHMENT2} + push.{attachment_type2} + movup.5 + exec.output_note::set_word_attachment # => [] # create a public note without assets push.{RECIPIENT3} # recipient - push.{EXECUTION_HINT_3} # note execution hint push.{NOTETYPE3} # note_type - push.{aux3} # aux push.{tag3} # tag - exec.create_note drop + exec.output_note::create + # => [note_idx = 2] + + push.{ATTACHMENT3} + push.{attachment_type3} + movup.5 + exec.output_note::set_array_attachment # => [] end ", @@ -327,9 +329,10 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { NOTETYPE1 = note_type1 as u8, NOTETYPE2 = note_type2 as u8, NOTETYPE3 = note_type3 as u8, - EXECUTION_HINT_1 = Felt::from(NoteExecutionHint::always()), - EXECUTION_HINT_2 = Felt::from(NoteExecutionHint::none()), - EXECUTION_HINT_3 = Felt::from(NoteExecutionHint::on_block_slot(11, 22, 33)), + attachment_type2 = attachment2.attachment_type().as_u32(), + ATTACHMENT2 = attachment2.content().to_word(), + attachment_type3 = attachment3.attachment_type().as_u32(), + ATTACHMENT3 = attachment3.content().to_word(), ); let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; @@ -338,8 +341,13 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { // -------------------------------------------------------------------------------------------- // execute the transaction and get the witness + let NoteAttachmentContent::Array(array) = attachment3.content() else { + panic!("expected array attachment"); + }; + let tx_context = TransactionContextBuilder::new(executor_account) .tx_script(tx_script) + .extend_advice_map(vec![(attachment3.content().to_word(), array.as_slice().to_vec())]) .extend_expected_output_notes(vec![ OutputNote::Full(expected_output_note_2.clone()), OutputNote::Full(expected_output_note_3.clone()), @@ -449,7 +457,7 @@ async fn user_code_can_abort_transaction_with_summary() -> anyhow::Result<()> { account.id(), vec![], NoteType::Private, - Felt::ZERO, + NoteAttachment::default(), &mut rng, )?; let input_note = create_spawn_note(vec![&output_note])?; @@ -492,7 +500,7 @@ async fn tx_summary_commitment_is_signed_by_falcon_auth() -> anyhow::Result<()> account.id(), vec![], NoteType::Private, - Felt::ZERO, + NoteAttachment::default(), &mut rng, )?; let spawn_note = builder.add_spawn_note([&p2id_note])?; @@ -556,7 +564,7 @@ async fn tx_summary_commitment_is_signed_by_ecdsa_auth() -> anyhow::Result<()> { account.id(), vec![], NoteType::Private, - Felt::ZERO, + NoteAttachment::default(), &mut rng, )?; let spawn_note = builder.add_spawn_note([&p2id_note])?; diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 34be3a1044..4263e9ebed 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -42,11 +42,11 @@ use miden_protocol::block::{ }; use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey; use miden_protocol::crypto::merkle::smt::Smt; -use miden_protocol::note::{Note, NoteDetails, NoteType}; +use miden_protocol::note::{Note, NoteAttachment, NoteDetails, NoteType}; use miden_protocol::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; use miden_protocol::testing::random_signer::RandomBlockSigner; use miden_protocol::transaction::{OrderedTransactionHeaders, OutputNote, TransactionKernel}; -use miden_protocol::{Felt, FieldElement, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; +use miden_protocol::{Felt, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; use miden_standards::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; use miden_standards::account::wallets::BasicWallet; use miden_standards::note::{create_p2id_note, create_p2ide_note, create_swap_note}; @@ -553,7 +553,7 @@ impl MockChainBuilder { target_account_id, asset.to_vec(), note_type, - Felt::ZERO, + NoteAttachment::default(), &mut self.rng, )?; self.add_output_note(OutputNote::Full(note.clone())); @@ -604,9 +604,9 @@ impl MockChainBuilder { offered_asset, requested_asset, NoteType::Public, - Felt::ZERO, + NoteAttachment::default(), payback_note_type, - Felt::ZERO, + NoteAttachment::default(), &mut self.rng, )?; diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index 9c51ba83e8..d6b3468e0b 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -1,7 +1,6 @@ use alloc::string::String; use alloc::vec::Vec; -use miden_processor::Felt; use miden_processor::crypto::RpoRandomCoin; use miden_protocol::account::AccountId; use miden_protocol::asset::Asset; @@ -228,15 +227,11 @@ fn note_script_that_creates_notes<'note>( out.push_str(&format!( " push.{recipient} - push.{hint} push.{note_type} - push.{aux} push.{tag} - call.output_note::create\n", + exec.output_note::create\n", recipient = note.recipient().digest(), - hint = Felt::from(note.metadata().execution_hint()), note_type = note.metadata().note_type() as u8, - aux = note.metadata().aux(), tag = note.metadata().tag(), )); diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index e842eff371..46db71e594 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -16,7 +16,7 @@ use miden_protocol::asset::{Asset, FungibleAsset}; use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, + NoteAttachment, NoteId, NoteInputs, NoteMetadata, @@ -48,8 +48,6 @@ use crate::{get_note_with_fungible_asset_and_script, prove_and_verify_transactio pub struct FaucetTestParams { pub recipient: Word, pub tag: NoteTag, - pub aux: Felt, - pub note_execution_hint: NoteExecutionHint, pub note_type: NoteType, pub amount: Felt, } @@ -60,15 +58,13 @@ pub fn create_mint_script_code(params: &FaucetTestParams) -> String { " begin # pad the stack before call - push.0.0.0 padw + padw padw push.0 push.{recipient} - push.{note_execution_hint} push.{note_type} - push.{aux} push.{tag} push.{amount} - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] + # => [amount, tag, note_type, RECIPIENT, pad(9)] call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] @@ -79,9 +75,7 @@ pub fn create_mint_script_code(params: &FaucetTestParams) -> String { ", note_type = params.note_type as u8, recipient = params.recipient, - aux = params.aux, tag = u32::from(params.tag), - note_execution_hint = Felt::from(params.note_execution_hint), amount = params.amount, ) } @@ -139,8 +133,6 @@ async fn minting_fungible_asset_on_existing_faucet_succeeds() -> anyhow::Result< let params = FaucetTestParams { recipient: Word::from([0, 1, 2, 3u32]), tag: NoteTag::default(), - aux: Felt::new(27), - note_execution_hint: NoteExecutionHint::on_block_slot(5, 6, 7), note_type: NoteType::Private, amount: Felt::new(100), }; @@ -161,7 +153,6 @@ async fn faucet_contract_mint_fungible_asset_fails_exceeds_max_supply() -> anyho let mock_chain = builder.build()?; let recipient = Word::from([0, 1, 2, 3u32]); - let aux = Felt::new(27); let tag = Felt::new(4); let amount = Felt::new(250); @@ -169,14 +160,13 @@ async fn faucet_contract_mint_fungible_asset_fails_exceeds_max_supply() -> anyho " begin # pad the stack before call - push.0.0.0 padw + padw padw push.0 push.{recipient} push.{note_type} - push.{aux} push.{tag} push.{amount} - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] + # => [amount, tag, note_type, RECIPIENT, pad(9)] call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] @@ -219,8 +209,6 @@ async fn minting_fungible_asset_on_new_faucet_succeeds() -> anyhow::Result<()> { let params = FaucetTestParams { recipient: Word::from([0, 1, 2, 3u32]), tag: NoteTag::default(), - aux: Felt::new(27), - note_execution_hint: NoteExecutionHint::on_block_slot(5, 6, 7), note_type: NoteType::Private, amount: Felt::new(100), }; @@ -308,8 +296,6 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let recipient_account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER)?; let amount = Felt::new(75); let tag = NoteTag::default(); - // TODO(note_attachment): Replace with attachment. - // let aux = Felt::new(27); let note_type = NoteType::Public; // Create a simple output note script @@ -371,12 +357,10 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul # => [RECIPIENT] # Now call distribute with the computed recipient - push.0 # note_execution_hint push.{note_type} - push.0 # aux push.{tag} push.{amount} - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT] + # => [amount, tag, note_type, RECIPIENT] call.::miden::standards::faucets::basic_fungible::distribute # => [note_idx, pad(15)] @@ -404,8 +388,6 @@ async fn test_public_note_creation_with_script_from_datastore() -> anyhow::Resul let trigger_note = NoteBuilder::new(faucet.id(), &mut rng) .note_type(NoteType::Private) .tag(NoteTag::default().into()) - .note_execution_hint(NoteExecutionHint::always()) - .aux(Felt::new(0)) .serial_number(Word::from([1, 2, 3, 4u32])) .code(trigger_note_script_code) .build()?; @@ -510,7 +492,6 @@ async fn network_faucet_mint() -> anyhow::Result<()> { let amount = Felt::new(75); let mint_asset: Asset = FungibleAsset::new(faucet.id(), amount.into()).unwrap().into(); - let aux = Felt::new(27); let serial_num = Word::default(); let output_note_tag = NoteTag::with_account_target(target_account.id()); @@ -519,24 +500,22 @@ async fn network_faucet_mint() -> anyhow::Result<()> { target_account.id(), vec![mint_asset], NoteType::Private, - aux, serial_num, ) .unwrap(); let recipient = p2id_mint_output_note.recipient().digest(); // Create the MINT note using the helper function - let mint_inputs = MintNoteInputs::new_private( - recipient, - amount, - output_note_tag.into(), - NoteExecutionHint::always(), - aux, - ); + let mint_inputs = MintNoteInputs::new_private(recipient, amount, output_note_tag.into()); let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); - let mint_note = - create_mint_note(faucet.id(), faucet_owner_account_id, mint_inputs, aux, &mut rng)?; + let mint_note = create_mint_note( + faucet.id(), + faucet_owner_account_id, + mint_inputs, + NoteAttachment::default(), + &mut rng, + )?; // Add the MINT note to the mock chain builder.add_output_note(OutputNote::Full(mint_note.clone())); @@ -622,7 +601,7 @@ async fn network_faucet_burn() -> anyhow::Result<()> { faucet_owner_account_id, faucet.id(), fungible_asset.into(), - Felt::new(0), + NoteAttachment::default(), &mut rng, )?; @@ -679,7 +658,6 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow let amount = Felt::new(75); let mint_asset: Asset = FungibleAsset::new(faucet.id(), amount.into()).unwrap().into(); - let aux = Felt::new(27); let serial_num = Word::from([1, 2, 3, 4u32]); // Create the expected P2ID output note @@ -688,7 +666,6 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow target_account.id(), vec![mint_asset], note_type, - aux, serial_num, ) .unwrap(); @@ -698,13 +675,7 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow NoteType::Private => { let output_note_tag = NoteTag::with_account_target(target_account.id()); let recipient = p2id_mint_output_note.recipient().digest(); - MintNoteInputs::new_private( - recipient, - amount, - output_note_tag.into(), - NoteExecutionHint::always(), - aux, - ) + MintNoteInputs::new_private(recipient, amount, output_note_tag.into()) }, NoteType::Public => { let output_note_tag = NoteTag::with_account_target(target_account.id()); @@ -713,20 +684,19 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow vec![target_account.id().suffix(), target_account.id().prefix().as_felt()]; let note_inputs = NoteInputs::new(p2id_inputs)?; let recipient = NoteRecipient::new(serial_num, p2id_script, note_inputs); - MintNoteInputs::new_public( - recipient, - amount, - output_note_tag.into(), - NoteExecutionHint::always(), - aux, - )? + MintNoteInputs::new_public(recipient, amount, output_note_tag.into())? }, NoteType::Encrypted => unreachable!("Encrypted note type not used in this test"), }; let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); - let mint_note = - create_mint_note(faucet.id(), faucet_owner_account_id, mint_inputs.clone(), aux, &mut rng)?; + let mint_note = create_mint_note( + faucet.id(), + faucet_owner_account_id, + mint_inputs.clone(), + NoteAttachment::default(), + &mut rng, + )?; builder.add_output_note(OutputNote::Full(mint_note.clone())); let mut mock_chain = builder.build()?; @@ -750,12 +720,7 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow // For private notes, we can only compare basic properties since we get // OutputNote::Partial assert_eq!(output_note.id(), p2id_mint_output_note.id()); - assert_eq!(output_note.metadata().sender(), p2id_mint_output_note.metadata().sender()); - assert_eq!( - output_note.metadata().note_type(), - p2id_mint_output_note.metadata().note_type() - ); - assert_eq!(output_note.metadata().aux(), p2id_mint_output_note.metadata().aux()); + assert_eq!(output_note.metadata(), p2id_mint_output_note.metadata()); }, NoteType::Public => { // For public notes, we get OutputNote::Full and can compare key properties diff --git a/crates/miden-testing/tests/scripts/p2id.rs b/crates/miden-testing/tests/scripts/p2id.rs index 870bd7a193..7d5e438a2a 100644 --- a/crates/miden-testing/tests/scripts/p2id.rs +++ b/crates/miden-testing/tests/scripts/p2id.rs @@ -1,7 +1,7 @@ use miden_protocol::account::Account; use miden_protocol::asset::{Asset, AssetVault, FungibleAsset}; use miden_protocol::crypto::rand::RpoRandomCoin; -use miden_protocol::note::NoteType; +use miden_protocol::note::{NoteAttachment, NoteType}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, @@ -207,7 +207,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into()?, vec![FungibleAsset::mock(10)], NoteType::Public, - Felt::new(0), + NoteAttachment::default(), &mut RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])), )?; @@ -216,7 +216,7 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?, vec![FungibleAsset::mock(5)], NoteType::Public, - Felt::new(0), + NoteAttachment::default(), &mut RpoRandomCoin::new(Word::from([4, 3, 2, 1u32])), )?; @@ -225,22 +225,18 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { use miden::protocol::output_note begin push.{recipient_1} - push.{note_execution_hint_1} push.{note_type_1} - push.0 # aux push.{tag_1} - call.output_note::create + exec.output_note::create push.{asset_1} call.::miden::standards::wallets::basic::move_asset_to_note dropw dropw dropw dropw push.{recipient_2} - push.{note_execution_hint_2} push.{note_type_2} - push.0 # aux push.{tag_2} - call.output_note::create + exec.output_note::create push.{asset_2} call.::miden::standards::wallets::basic::move_asset_to_note @@ -251,12 +247,10 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { note_type_1 = NoteType::Public as u8, tag_1 = Felt::from(output_note_1.metadata().tag()), asset_1 = Word::from(FungibleAsset::mock(10)), - note_execution_hint_1 = Felt::from(output_note_1.metadata().execution_hint()), recipient_2 = output_note_2.recipient().digest(), note_type_2 = NoteType::Public as u8, tag_2 = Felt::from(output_note_2.metadata().tag()), asset_2 = Word::from(FungibleAsset::mock(5)), - note_execution_hint_2 = Felt::from(output_note_2.metadata().execution_hint()) ); let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index b272d1f963..ca890b1362 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -1,12 +1,13 @@ use core::slice; use std::collections::BTreeMap; -use miden_protocol::Word; use miden_protocol::asset::{Asset, FungibleAsset}; use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; use miden_protocol::note::{ Note, NoteAssets, + NoteAttachment, + NoteAttachmentType, NoteInputs, NoteMetadata, NoteRecipient, @@ -15,6 +16,7 @@ use miden_protocol::note::{ PartialNote, }; use miden_protocol::transaction::OutputNote; +use miden_protocol::{Felt, Word}; use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_standards::code_builder::CodeBuilder; use miden_testing::{Auth, MockChain}; @@ -35,7 +37,10 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let sender_account_interface = AccountInterface::from_account(&sender_basic_wallet_account); let tag = NoteTag::with_account_target(sender_basic_wallet_account.id()); - let metadata = NoteMetadata::new(sender_basic_wallet_account.id(), NoteType::Public, tag); + let elements = [9, 8, 7, 6, 5u32].map(Felt::from).to_vec(); + let attachment = NoteAttachment::new_array(NoteAttachmentType::new(42), elements.clone())?; + let metadata = NoteMetadata::new(sender_basic_wallet_account.id(), NoteType::Public, tag) + .with_attachment(attachment.clone()); let assets = NoteAssets::new(vec![sent_asset]).unwrap(); let note_script = CodeBuilder::default().compile_note_script("begin nop end").unwrap(); let serial_num = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])).draw_word(); @@ -51,8 +56,11 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let executed_transaction = mock_chain .build_tx_context(sender_basic_wallet_account.id(), &[], &[]) .expect("failed to build tx context") + // TODO: This shouldn't be necessary. The attachment should be included in the tx + // script's mast forest's advice map. + .extend_advice_map(vec![(attachment.content().to_word(), elements)]) .tx_script(send_note_transaction_script) - .extend_expected_output_notes(vec![OutputNote::Full(note)]) + .extend_expected_output_notes(vec![OutputNote::Full(note.clone())]) .build()? .execute() .await?; @@ -70,6 +78,7 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { sent_asset, "sent asset should be in removed assets" ); + assert_eq!(executed_transaction.output_notes().get_note(0), &OutputNote::Full(note)); Ok(()) } @@ -89,8 +98,10 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { AccountInterface::from_account(&sender_basic_fungible_faucet_account); let tag = NoteTag::with_account_target(sender_basic_fungible_faucet_account.id()); + let attachment = NoteAttachment::new_word(NoteAttachmentType::new(100), Word::empty()); let metadata = - NoteMetadata::new(sender_basic_fungible_faucet_account.id(), NoteType::Public, tag); + NoteMetadata::new(sender_basic_fungible_faucet_account.id(), NoteType::Public, tag) + .with_attachment(attachment); let assets = NoteAssets::new(vec![Asset::Fungible( FungibleAsset::new(sender_basic_fungible_faucet_account.id(), 10).unwrap(), )])?; @@ -105,13 +116,16 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { let send_note_transaction_script = sender_account_interface .build_send_notes_script(slice::from_ref(&partial_note), Some(expiration_delta))?; - let _executed_transaction = mock_chain + let executed_transaction = mock_chain .build_tx_context(sender_basic_fungible_faucet_account.id(), &[], &[]) .expect("failed to build tx context") .tx_script(send_note_transaction_script) - .extend_expected_output_notes(vec![OutputNote::Full(note)]) + .extend_expected_output_notes(vec![OutputNote::Full(note.clone())]) .build()? .execute() .await?; + + assert_eq!(executed_transaction.output_notes().get_note(0), &OutputNote::Full(note)); + Ok(()) } diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index 157da3d039..701aec7a1a 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -35,11 +35,9 @@ pub async fn prove_send_swap_note() -> anyhow::Result<()> { use miden::protocol::output_note begin push.{recipient} - push.{note_execution_hint} push.{note_type} - push.0 # aux push.{tag} - call.output_note::create + exec.output_note::create push.{asset} call.::miden::standards::wallets::basic::move_asset_to_note @@ -50,7 +48,6 @@ pub async fn prove_send_swap_note() -> anyhow::Result<()> { note_type = NoteType::Public as u8, tag = Felt::from(swap_note.metadata().tag()), asset = Word::from(offered_asset), - note_execution_hint = Felt::from(swap_note.metadata().execution_hint()) ); let tx_script = CodeBuilder::default().compile_tx_script(tx_script_src)?; @@ -182,7 +179,6 @@ async fn consume_swap_note_public_payback_note() -> anyhow::Result<()> { sender_account.id(), vec![requested_asset], payback_note_type, - Felt::new(0), payback_note.serial_num(), ) .unwrap(); @@ -345,8 +341,6 @@ pub fn create_p2id_note_exact( target: AccountId, assets: Vec, note_type: NoteType, - // TODO(note_attachment): Replace with attachment. - _aux: Felt, serial_num: Word, ) -> Result { let recipient = utils::build_p2id_recipient(target, serial_num)?; diff --git a/docs/src/note.md b/docs/src/note.md index 276007652a..916b5dac03 100644 --- a/docs/src/note.md +++ b/docs/src/note.md @@ -208,10 +208,10 @@ The SWAP note script implements atomic asset swapping functionality. **Key characteristics:** - **Purpose:** Atomic asset exchange between two parties -- **Inputs:** Requires exactly 12 note inputs specifying: +- **Inputs:** Requires exactly 16 note inputs specifying: - Requested asset details - Payback note recipient information - - Note creation parameters (execution hint, type, aux data, tag) + - Note creation parameters (type, tag, attachment) - **Assets:** Must contain exactly 1 asset to be swapped - **Mechanism:** 1. Creates a payback note containing the requested asset for the original note issuer From 778130dba30d703709b1e154237b58fbbba155eb Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 15 Jan 2026 10:29:53 +0100 Subject: [PATCH 102/114] chore: rename `NoteAttachmentType` and `NoteAttachmentContentType` (#2268) * feat: Pad elements in `NoteAttachmentArray` before committing * chore: Rename `NoteAttachmentType` -> `NoteAttachmentScheme` * chore: rename "attachment type info" to "attachment kind scheme" * chore: Rename `NoteAttachmentContentType` -> `NoteAttachmentKind` * chore: rename attachment scheme untyped to unknown * chore: Swap kind and scheme in procedure signatures * chore: add changelog * Revert "feat: Pad elements in `NoteAttachmentArray` before committing" This reverts commit cfe9702d27a358bd3d71c1aef2b44a7ab541b67e. * chore: rename `NoteAttachmentScheme::unknown` to `none` * fix: more content type -> attachment kind renames --- CHANGELOG.md | 1 + .../asm/kernels/transaction/api.masm | 12 +- .../asm/kernels/transaction/lib/memory.masm | 12 +- .../kernels/transaction/lib/output_note.masm | 143 +++++++------- crates/miden-protocol/asm/protocol/note.masm | 4 +- .../asm/protocol/output_note.masm | 42 ++-- crates/miden-protocol/src/errors/mod.rs | 8 +- crates/miden-protocol/src/errors/tx_kernel.rs | 16 +- crates/miden-protocol/src/note/attachment.rs | 179 +++++++++--------- crates/miden-protocol/src/note/metadata.rs | 87 ++++----- crates/miden-protocol/src/note/mod.rs | 4 +- .../src/transaction/kernel/procedures.rs | 2 +- .../asm/standards/notes/mint.masm | 18 +- .../asm/standards/notes/swap.masm | 20 +- .../src/account/interface/component.rs | 12 +- .../miden-standards/src/note/mint_inputs.rs | 19 +- crates/miden-standards/src/note/mod.rs | 8 +- .../src/note/network_account_target.rs | 34 ++-- .../src/note/well_known_note_attachment.rs | 10 +- .../src/kernel_tests/tx/test_output_note.rs | 37 ++-- .../src/kernel_tests/tx/test_tx.rs | 14 +- crates/miden-testing/src/utils.rs | 10 +- .../miden-testing/tests/scripts/send_note.rs | 6 +- crates/miden-tx/src/errors/mod.rs | 2 +- crates/miden-tx/src/host/tx_event.rs | 46 +++-- 25 files changed, 366 insertions(+), 380 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5e2a552cc..e27a4f5e54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260)). +- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260), [#2268](https://github.com/0xMiden/miden-base/pull/2268)). - Introduce standard `NetworkAccountTarget` attachment for use in network transactions which replaces `NoteTag::NetworkAccount` ([#2257](https://github.com/0xMiden/miden-base/pull/2257)). ### Changes diff --git a/crates/miden-protocol/asm/kernels/transaction/api.masm b/crates/miden-protocol/asm/kernels/transaction/api.masm index 65728bb526..a67556ad67 100644 --- a/crates/miden-protocol/asm/kernels/transaction/api.masm +++ b/crates/miden-protocol/asm/kernels/transaction/api.masm @@ -1189,26 +1189,26 @@ end #! Sets the attachment of the note specified by the index. #! -#! Inputs: [note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(9)] +#! Inputs: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(9)] #! Outputs: [pad(16)] #! #! Where: #! - note_idx is the index of the note on which the attachment is set. -#! - attachment_content_type is the content type of the attachment. -#! - attachment_type is the user-defined type of the attachment. +#! - attachment_scheme is the user-defined scheme of the attachment. +#! - attachment_kind is the kind of the attachment content. #! - ATTACHMENT is the attachment to be set. #! #! Panics if: #! - the procedure is called when the active account is not the native one. #! - the note index points to a non-existent output note. -#! - any of the attachment types does not fit into a u32. -#! - the attachment content type is an unknown variant. +#! - the attachment kind or scheme does not fit into a u32. +#! - the attachment kind is an unknown variant. #! #! Invocation: dynexec pub proc output_note_set_attachment # check that this procedure was executed against the native account exec.memory::assert_native_account - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(9)] + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(9)] exec.output_note::set_attachment # => [pad(16)] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm index 78213758af..2d9e11ef30 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm @@ -240,7 +240,7 @@ const OUTPUT_NOTE_SECTION_OFFSET=16777216 # The offsets at which data of an output note is stored relative to the start of its data segment. const OUTPUT_NOTE_ID_OFFSET=0 const OUTPUT_NOTE_METADATA_HEADER_OFFSET=4 -const OUTPUT_NOTE_METADATA_ATTACHMENT_TYPE_INFO_OFFSET=OUTPUT_NOTE_METADATA_HEADER_OFFSET + 3 +const OUTPUT_NOTE_METADATA_ATTACHMENT_KIND_SCHEME_OFFSET=OUTPUT_NOTE_METADATA_HEADER_OFFSET + 3 const OUTPUT_NOTE_ATTACHMENT_OFFSET=8 const OUTPUT_NOTE_RECIPIENT_OFFSET=12 const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=16 @@ -1892,16 +1892,16 @@ pub proc set_output_note_metadata_header mem_storew_be end -#! Sets the output note's attachment type info in the metadata header. +#! Sets the output note's attachment kind and scheme in the metadata header. #! -#! Inputs: [note_ptr, attachment_type_info] +#! Inputs: [note_ptr, attachment_kind_scheme] #! Outputs: [] #! #! Where: -#! - attachment_type_info is the type information of the attachment that will be overwritten. +#! - attachment_kind_scheme is the type information of the attachment that will be overwritten. #! - note_ptr is the memory address at which the output note data begins. -pub proc set_output_note_attachment_type_info - add.OUTPUT_NOTE_METADATA_ATTACHMENT_TYPE_INFO_OFFSET +pub proc set_output_note_attachment_kind_scheme + add.OUTPUT_NOTE_METADATA_ATTACHMENT_KIND_SCHEME_OFFSET mem_store end diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index 3abd761149..654e8ae6a3 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -14,18 +14,17 @@ const PUBLIC_NOTE=1 # 0b01 const PRIVATE_NOTE=2 # 0b10 const ENCRYPTED_NOTE=3 # 0b11 -# Constants for note attachment content types -const ATTACHMENT_CONTENT_TYPE_NONE=0 -const ATTACHMENT_CONTENT_TYPE_WORD=1 -const ATTACHMENT_CONTENT_TYPE_ARRAY=2 +# Constants for note attachment kinds +const ATTACHMENT_KIND_NONE=0 +const ATTACHMENT_KIND_WORD=1 +const ATTACHMENT_KIND_ARRAY=2 # The default value of the felt at index 3 in the note metadata header when a new note is created. -# All zeros sets the attachment content type to None and the user-defined attachment type to -# "untyped". -const ATTACHMENT_DEFAULT_TYPE_INFO=0 +# All zeros sets the attachment kind to None and the user-defined attachment scheme to "none". +const ATTACHMENT_DEFAULT_KIND_AND_SCHEME=0 -#! The default attachment type, representing an untyped attachment. -const ATTACHMENT_TYPE_UNTYPED=0 +#! The default attachment scheme, representing the absence of an attachment scheme. +const ATTACHMENT_SCHEME_NONE=0 # ERRORS # ================================================================================================= @@ -36,13 +35,13 @@ const ERR_NOTE_INVALID_TYPE="invalid note type" const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested output note index should be less than the total number of created output notes" -const ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_TYPES="attachment types must fit into u32s" +const ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_SCHEMES="attachment scheme and attachment kind must fit into u32s" -const ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_CONTENT_TYPE="attachment content type variant must be between 0 and 2" +const ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_KIND="attachment kind variant must be between 0 and 2" -const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_HAVE_UNTYPED_ATTACHMENT_TYPE="attachment type of content type none must be 0" +const ERR_OUTPUT_NOTE_ATTACHMENT_KIND_NONE_MUST_HAVE_ATTACHMENT_SCHEME_NONE="attachment kind none must have attachment scheme none" -const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_BE_EMPTY_WORD="attachment of content type none must be set to an empty word" +const ERR_OUTPUT_NOTE_ATTACHMENT_KIND_NONE_MUST_BE_EMPTY_WORD="attachment kind None requires ATTACHMENT to be set to an empty word" const ERR_NOTE_INVALID_INDEX="failed to find note at the given index; index must be within [0, num_of_notes]" @@ -116,7 +115,7 @@ pub proc create # => [note_ptr, RECIPIENT, note_idx] # set the attachment value of a new note to an empty word - # note that the attachment content type is set to None by build_metadata_header + # note that the attachment kind is set to None by build_metadata_header padw dup.4 # => [note_ptr, EMPTY_WORD, note_ptr, RECIPIENT, note_idx] @@ -248,43 +247,43 @@ end #! Sets the attachment of the note specified by the index. #! -#! Inputs: [note_idx, attachment_content_type, attachment_type, ATTACHMENT] +#! Inputs: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] #! Outputs: [] #! #! Where: #! - note_idx is the index of the note on which the attachment is set. -#! - attachment_content_type is the content type of the attachment. -#! - attachment_type is the user-defined type of the attachment. +#! - attachment_scheme is the user-defined scheme of the attachment. +#! - attachment_kind is the kind of the attachment content. #! - ATTACHMENT is the attachment to be set. #! #! Panics if: #! - the note index points to a non-existent output note. -#! - any of the attachment types does not fit into a u32. -#! - the attachment content type is an unknown variant. +#! - the attachment kind or scheme does not fit into a u32. +#! - the attachment kind is an unknown variant. pub proc set_attachment dup exec.memory::get_num_output_notes lte assert.err=ERR_NOTE_INVALID_INDEX - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] exec.memory::get_output_note_ptr dup - # => [note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT] + # => [note_ptr, note_ptr, attachment_scheme, attachment_kind, ATTACHMENT] dupw.1 - # => [ATTACHMENT, note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT] + # => [ATTACHMENT, note_ptr, note_ptr, attachment_scheme, attachment_kind, ATTACHMENT] dup.7 dup.7 - # => [attachment_content_type, attachment_type, ATTACHMENT, note_ptr, note_ptr, - # attachment_content_type, attachment_type, ATTACHMENT] + # => [attachment_scheme, attachment_kind, ATTACHMENT, note_ptr, note_ptr, + # attachment_scheme, attachment_kind, ATTACHMENT] exec.validate_attachment - # => [note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT] + # => [note_ptr, note_ptr, attachment_scheme, attachment_kind, ATTACHMENT] movdn.3 movdn.3 - # => [attachment_content_type, attachment_type, note_ptr, note_ptr, ATTACHMENT] + # => [attachment_scheme, attachment_kind, note_ptr, note_ptr, ATTACHMENT] emit.NOTE_BEFORE_SET_ATTACHMENT_EVENT - # => [attachment_content_type, attachment_type, note_ptr, note_ptr, ATTACHMENT] + # => [attachment_scheme, attachment_kind, note_ptr, note_ptr, ATTACHMENT] - exec.set_attachment_type_info + exec.set_attachment_kind_scheme # => [note_ptr, ATTACHMENT] exec.memory::set_output_note_attachment @@ -311,7 +310,7 @@ end #! Builds the provided inputs into the NOTE_METADATA_HEADER word. #! #! - The sender ID is set to the native account's ID. -#! - The attachment type is set to 0 (meaning untyped by convention) and the attachment content +#! - The attachment scheme is set to 0 (meaning none by convention) and the attachment content #! type is set to None. #! #! Note that this procedure is only exported so it can be tested. It should not be called from @@ -353,43 +352,43 @@ pub proc build_metadata_header # -------------------------------------------------------------------------------------------- movup.2 - push.ATTACHMENT_DEFAULT_TYPE_INFO - # => [attachment_type_info, tag, sender_id_prefix, sender_id_suffix_and_note_type] + push.ATTACHMENT_DEFAULT_KIND_AND_SCHEME + # => [attachment_kind_scheme, tag, sender_id_prefix, sender_id_suffix_and_note_type] # => [NOTE_METADATA_HEADER] end -#! Validate the ATTACHMENT against the content type. +#! Validate the ATTACHMENT against the attachment kind. #! -#! Inputs: [attachment_content_type, attachment_type, ATTACHMENT] +#! Inputs: [attachment_scheme, attachment_kind, ATTACHMENT] #! Outputs: [] #! #! Where: -#! - attachment_type is the user-defined type of the attachment. -#! - attachment_content_type is the content type of the attachment. +#! - attachment_scheme is the user-defined scheme of the attachment. +#! - attachment_kind is the kind of the attachment content. #! - ATTACHMENT is the attachment to validate. #! #! Panics if: -#! - any of the attachment types does not fit into a u32. -#! - the attachment content type is an unknown variant. -#! - the content type is None and the ATTACHMENT is not an empty word. +#! - the attachment kind or scheme does not fit into a u32. +#! - the attachment kind is an unknown variant. +#! - the attachment kind is None and the ATTACHMENT is not an empty word. proc validate_attachment - u32assert2.err=ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_TYPES - # => [attachment_content_type, attachment_type, ATTACHMENT] + u32assert2.err=ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_SCHEMES + # => [attachment_scheme, attachment_kind, ATTACHMENT] - # assert that the attachment content type is valid - dup u32lte.ATTACHMENT_CONTENT_TYPE_ARRAY - assert.err=ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_CONTENT_TYPE - # => [attachment_content_type, attachment_type, ATTACHMENT] + # assert that the attachment kind is valid + swap dup u32lte.ATTACHMENT_KIND_ARRAY + assert.err=ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_KIND + # => [attachment_kind, attachment_scheme, ATTACHMENT] - eq.ATTACHMENT_CONTENT_TYPE_NONE - # => [is_attachment_none, attachment_type, ATTACHMENT] + eq.ATTACHMENT_KIND_NONE + # => [is_attachment_none, attachment_scheme, ATTACHMENT] if.true - eq.ATTACHMENT_TYPE_UNTYPED - assert.err=ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_HAVE_UNTYPED_ATTACHMENT_TYPE + eq.ATTACHMENT_SCHEME_NONE + assert.err=ERR_OUTPUT_NOTE_ATTACHMENT_KIND_NONE_MUST_HAVE_ATTACHMENT_SCHEME_NONE # => [ATTACHMENT] - padw assert_eqw.err=ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_BE_EMPTY_WORD + padw assert_eqw.err=ERR_OUTPUT_NOTE_ATTACHMENT_KIND_NONE_MUST_BE_EMPTY_WORD # => [] else drop dropw @@ -398,47 +397,47 @@ proc validate_attachment # => [] end -#! Sets an output note's attachment type info in the metadata header. +#! Sets an output note's attachment kind and scheme in the note metadata header. #! -#! WARNING: The attachment types must be valid. +#! WARNING: The attachment scheme and kind must be valid. #! -#! Inputs: [attachment_content_type, attachment_type, note_ptr] +#! Inputs: [attachment_scheme, attachment_kind, note_ptr] #! Outputs: [] #! #! Where: -#! - attachment_content_type is the content type of the attachment. -#! - attachment_type is the user-defined type of the attachment. +#! - attachment_scheme is the user-defined type of the attachment. +#! - attachment_kind is the kind of the attachment content. #! - note_ptr is the memory address at which the output note data begins. -proc set_attachment_type_info - exec.merge_attachment_type_info - # => [attachment_type_info, note_ptr] +proc set_attachment_kind_scheme + exec.merge_attachment_kind_and_scheme + # => [attachment_kind_scheme, note_ptr] swap - # => [note_ptr, attachment_type_info] + # => [note_ptr, attachment_kind_scheme] - exec.memory::set_output_note_attachment_type_info + exec.memory::set_output_note_attachment_kind_scheme # => [] end -#! Merges the attachment types into a single felt with the following layout: +#! Merges the attachment kind and scheme into a single felt with the following layout: #! -#! [30 zero bits | attachment_content_type (2 bits) | attachment_type (32 bits)] +#! [30 zero bits | attachment_kind (2 bits) | attachment_scheme (32 bits)] #! -#! WARNING: The attachment types must be valid. +#! WARNING: The attachment scheme and kind must be valid. #! -#! Inputs: [attachment_content_type, attachment_type] -#! Outputs: [attachment_type_info] +#! Inputs: [attachment_scheme, attachment_kind] +#! Outputs: [attachment_kind_scheme] #! #! Where: -#! - attachment_content_type is the content type of the attachment. -#! - attachment_type is the user-defined type of the attachment. -#! - attachment_type_info is the felt constructed from the inputs. -proc merge_attachment_type_info - # shift the content type 32 bits to the left, which is the same as multiplying by 2^32 - # and set the lower bits to the attachment_type, which is done by adding the values together - mul.0x100000000 +#! - attachment_scheme is the user-defined scheme of the attachment. +#! - attachment_kind is the kind of the attachment content. +#! - attachment_kind_scheme is the felt constructed from the inputs. +proc merge_attachment_kind_and_scheme + # shift the attachment_kind 32 bits to the left, which is the same as multiplying by 2^32 + # and set the lower bits to the attachment_scheme, which is done by adding the values together + swap mul.0x100000000 add - # => [attachment_type_info] + # => [attachment_kind_scheme] end #! Increments the number of output notes by one. Returns the index of the next note to be created. diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index 13cb6f6ef3..c606b581a4 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -201,9 +201,9 @@ end #! - sender_{prefix,suffix} are the prefix and suffix felts of the sender ID of the note which #! metadata was provided. pub proc extract_sender_from_metadata - # => [attachment_type_info, tag, sender_id_prefix, sender_id_suffix_and_note_type] + # => [attachment_kind_scheme, tag, sender_id_prefix, sender_id_suffix_and_note_type] - # drop attachment type info and tag + # drop attachment kind, attachment scheme and tag drop drop swap # => [sender_id_suffix_and_note_type, sender_id_prefix] diff --git a/crates/miden-protocol/asm/protocol/output_note.masm b/crates/miden-protocol/asm/protocol/output_note.masm index 23f56b8f38..45a9f13479 100644 --- a/crates/miden-protocol/asm/protocol/output_note.masm +++ b/crates/miden-protocol/asm/protocol/output_note.masm @@ -4,10 +4,10 @@ use miden::protocol::note # CONSTANTS # ================================================================================================= -# Constants for note attachment content types -pub const ATTACHMENT_CONTENT_TYPE_NONE=0 -pub const ATTACHMENT_CONTENT_TYPE_WORD=1 -pub const ATTACHMENT_CONTENT_TYPE_ARRAY=2 +# Constants for note attachment kinds +pub const ATTACHMENT_KIND_NONE=0 +pub const ATTACHMENT_KIND_WORD=1 +pub const ATTACHMENT_KIND_ARRAY=2 # PROCEDURES # ================================================================================================= @@ -150,29 +150,29 @@ end #! Sets the attachment of the note specified by the index. #! -#! Inputs: [note_idx, attachment_content_type, attachment_type, ATTACHMENT] +#! Inputs: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] #! Outputs: [] #! #! Where: #! - note_idx is the index of the note on which the attachment is set. -#! - attachment_content_type is the content type of the attachment. -#! - attachment_type is the user-defined type of the attachment. +#! - attachment_scheme is the user-defined scheme of the attachment. +#! - attachment_kind is the kind of the attachment content. #! - ATTACHMENT is the attachment to be set. #! #! Panics if: #! - the procedure is called when the active account is not the native one. #! - the note index points to a non-existent output note. -#! - any of the attachment types does not fit into a u32. -#! - the attachment content type is an unknown variant. +#! - the attachment kind or scheme does not fit into a u32. +#! - the attachment kind is an unknown variant. #! #! Invocation: exec pub proc set_attachment exec.kernel_proc_offsets::output_note_set_attachment_offset - # => [offset, note_idx, attachment_content_type, attachment_type, ATTACHMENT] + # => [offset, note_idx, attachment_scheme, attachment_kind, ATTACHMENT] # pad the stack before the syscall padw padw swapdw - # => [offset, note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(8)] + # => [offset, note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(8)] syscall.exec_kernel_proc # => [pad(16)] @@ -188,23 +188,23 @@ end #! #! This overwrites any previously set attachment. #! -#! Inputs: [note_idx, attachment_type, ATTACHMENT] +#! Inputs: [note_idx, attachment_scheme, ATTACHMENT] #! Outputs: [] #! #! Where: #! - note_idx is the index of the note on which the attachment is set. -#! - attachment_type is the user-defined type of the attachment. +#! - attachment_scheme is the user-defined scheme of the attachment. #! - ATTACHMENT is the raw attachment to set. #! #! Panics if: #! - the procedure is called when the active account is not the native one. #! - the note index points to a non-existent output note. -#! - the attachment_type does not fit into a u32. +#! - the attachment_scheme does not fit into a u32. #! #! Invocation: exec pub proc set_word_attachment - push.ATTACHMENT_CONTENT_TYPE_WORD swap - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + push.ATTACHMENT_KIND_WORD movdn.2 + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] exec.set_attachment # => [] @@ -215,23 +215,23 @@ end #! #! This overwrites any previously set attachment. #! -#! Inputs: [note_idx, attachment_type, ATTACHMENT] +#! Inputs: [note_idx, attachment_scheme, ATTACHMENT] #! Outputs: [] #! #! Where: #! - note_idx is the index of the note on which the attachment is set. -#! - attachment_type is the user-defined type of the attachment. +#! - attachment_scheme is the user-defined scheme of the attachment. #! - ATTACHMENT is the commitment of the set of elements that form the note attachment. #! #! Panics if: #! - the procedure is called when the active account is not the native one. #! - the note index points to a non-existent output note. -#! - the attachment_type does not fit into a u32. +#! - the attachment_scheme does not fit into a u32. #! #! Invocation: exec pub proc set_array_attachment - push.ATTACHMENT_CONTENT_TYPE_ARRAY swap - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + push.ATTACHMENT_KIND_ARRAY movdn.2 + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] exec.set_attachment # => [] diff --git a/crates/miden-protocol/src/errors/mod.rs b/crates/miden-protocol/src/errors/mod.rs index 2134272f37..19236a647f 100644 --- a/crates/miden-protocol/src/errors/mod.rs +++ b/crates/miden-protocol/src/errors/mod.rs @@ -598,10 +598,10 @@ pub enum NoteError { NoteAttachmentArray::MAX_NUM_ELEMENTS )] NoteAttachmentArraySizeExceeded(usize), - #[error("unknown note attachment content type {0}")] - UnknownNoteAttachmentContentType(u8), - #[error("note attachment of type None must have untyped attachment type")] - NoneAttachmentMustHaveUntypedAttachmentType, + #[error("unknown note attachment kind {0}")] + UnknownNoteAttachmentKind(u8), + #[error("note attachment of kind None must have attachment scheme None")] + AttachmentKindNoneMustHaveAttachmentSchemeNone, #[error("{error_msg}")] Other { error_msg: Box, diff --git a/crates/miden-protocol/src/errors/tx_kernel.rs b/crates/miden-protocol/src/errors/tx_kernel.rs index 05846fb567..d267df0216 100644 --- a/crates/miden-protocol/src/errors/tx_kernel.rs +++ b/crates/miden-protocol/src/errors/tx_kernel.rs @@ -163,16 +163,16 @@ pub const ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT: MasmError = MasmError::from_stati /// Error Message: "the note's tag must fit into a u32 so the 32 most significant bits of the felt must be zero" pub const ERR_NOTE_TAG_MUST_BE_U32: MasmError = MasmError::from_static_str("the note's tag must fit into a u32 so the 32 most significant bits of the felt must be zero"); -/// Error Message: "attachment of content type none must be set to an empty word" -pub const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_BE_EMPTY_WORD: MasmError = MasmError::from_static_str("attachment of content type none must be set to an empty word"); -/// Error Message: "attachment type of content type none must be 0" -pub const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_HAVE_UNTYPED_ATTACHMENT_TYPE: MasmError = MasmError::from_static_str("attachment type of content type none must be 0"); +/// Error Message: "attachment kind None requires ATTACHMENT to be set to an empty word" +pub const ERR_OUTPUT_NOTE_ATTACHMENT_KIND_NONE_MUST_BE_EMPTY_WORD: MasmError = MasmError::from_static_str("attachment kind None requires ATTACHMENT to be set to an empty word"); +/// Error Message: "attachment kind none must have attachment scheme none" +pub const ERR_OUTPUT_NOTE_ATTACHMENT_KIND_NONE_MUST_HAVE_ATTACHMENT_SCHEME_NONE: MasmError = MasmError::from_static_str("attachment kind none must have attachment scheme none"); /// Error Message: "requested output note index should be less than the total number of created output notes" pub const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("requested output note index should be less than the total number of created output notes"); -/// Error Message: "attachment types must fit into u32s" -pub const ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_TYPES: MasmError = MasmError::from_static_str("attachment types must fit into u32s"); -/// Error Message: "attachment content type variant must be between 0 and 2" -pub const ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_CONTENT_TYPE: MasmError = MasmError::from_static_str("attachment content type variant must be between 0 and 2"); +/// Error Message: "attachment scheme and attachment kind must fit into u32s" +pub const ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_SCHEMES: MasmError = MasmError::from_static_str("attachment scheme and attachment kind must fit into u32s"); +/// Error Message: "attachment kind variant must be between 0 and 2" +pub const ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_KIND: MasmError = MasmError::from_static_str("attachment kind variant must be between 0 and 2"); /// Error Message: "existing accounts must have a non-zero nonce" pub const ERR_PROLOGUE_EXISTING_ACCOUNT_MUST_HAVE_NON_ZERO_NONCE: MasmError = MasmError::from_static_str("existing accounts must have a non-zero nonce"); diff --git a/crates/miden-protocol/src/note/attachment.rs b/crates/miden-protocol/src/note/attachment.rs index a7a03fc4d3..af15c29ec2 100644 --- a/crates/miden-protocol/src/note/attachment.rs +++ b/crates/miden-protocol/src/note/attachment.rs @@ -29,13 +29,13 @@ use crate::{Felt, Hasher, NoteError, Word}; /// [`NoteAttachmentContent::Array`] variant are available. See the type's docs for more /// details. /// -/// Next to the content, a note attachment can optionally specify a [`NoteAttachmentType`]. This +/// Next to the content, a note attachment can optionally specify a [`NoteAttachmentScheme`]. This /// allows a note attachment to describe itself. For example, a network account target attachment -/// can be identified by a standardized type. For cases when the attachment type is known from -/// content or typing is otherwise undesirable, [`NoteAttachmentType::untyped`] can be used. +/// can be identified by a standardized type. For cases when the attachment scheme is known from +/// content or typing is otherwise undesirable, [`NoteAttachmentScheme::none`] can be used. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct NoteAttachment { - attachment_type: NoteAttachmentType, + attachment_scheme: NoteAttachmentScheme, content: NoteAttachmentContent, } @@ -44,22 +44,28 @@ impl NoteAttachment { // -------------------------------------------------------------------------------------------- /// Creates a new [`NoteAttachment`] from a user-defined type and the provided content. + /// + /// # Errors + /// + /// Returns an error if: + /// - The attachment content is [`NoteAttachmentKind::None`] but the scheme is not + /// [`NoteAttachmentScheme::none`]. pub fn new( - attachment_type: NoteAttachmentType, + attachment_scheme: NoteAttachmentScheme, content: NoteAttachmentContent, ) -> Result { - if content.content_type().is_none() && !attachment_type.is_untyped() { - return Err(NoteError::NoneAttachmentMustHaveUntypedAttachmentType); + if content.attachment_kind().is_none() && !attachment_scheme.is_none() { + return Err(NoteError::AttachmentKindNoneMustHaveAttachmentSchemeNone); } - Ok(Self { attachment_type, content }) + Ok(Self { attachment_scheme, content }) } /// Creates a new note attachment with content [`NoteAttachmentContent::Word`] from the provided /// word. - pub fn new_word(attachment_type: NoteAttachmentType, word: Word) -> Self { + pub fn new_word(attachment_scheme: NoteAttachmentScheme, word: Word) -> Self { Self { - attachment_type, + attachment_scheme, content: NoteAttachmentContent::new_word(word), } } @@ -72,32 +78,24 @@ impl NoteAttachment { /// Returns an error if: /// - The maximum number of elements exceeds [`NoteAttachmentArray::MAX_NUM_ELEMENTS`]. pub fn new_array( - attachment_type: NoteAttachmentType, + attachment_scheme: NoteAttachmentScheme, elements: Vec, ) -> Result { - NoteAttachmentContent::new_array(elements).map(|content| Self { attachment_type, content }) - } - - /// Creates a new [`NoteAttachment`] from the provided content and using - /// [`NoteAttachmentType::untyped`]. - pub fn new_untyped(content: NoteAttachmentContent) -> Self { - Self { - attachment_type: NoteAttachmentType::untyped(), - content, - } + NoteAttachmentContent::new_array(elements) + .map(|content| Self { attachment_scheme, content }) } // ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the attachment type. - pub fn attachment_type(&self) -> NoteAttachmentType { - self.attachment_type + /// Returns the attachment scheme. + pub fn attachment_scheme(&self) -> NoteAttachmentScheme { + self.attachment_scheme } - /// Returns the attachment content type. - pub fn content_type(&self) -> NoteAttachmentContentType { - self.content.content_type() + /// Returns the attachment kind. + pub fn attachment_kind(&self) -> NoteAttachmentKind { + self.content.attachment_kind() } /// Returns a reference to the attachment content. @@ -108,17 +106,17 @@ impl NoteAttachment { impl Serializable for NoteAttachment { fn write_into(&self, target: &mut W) { - self.attachment_type().write_into(target); + self.attachment_scheme().write_into(target); self.content().write_into(target); } } impl Deserializable for NoteAttachment { fn read_from(source: &mut R) -> Result { - let attachment_type = NoteAttachmentType::read_from(source)?; + let attachment_scheme = NoteAttachmentScheme::read_from(source)?; let content = NoteAttachmentContent::read_from(source)?; - Self::new(attachment_type, content) + Self::new(attachment_scheme, content) .map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } @@ -174,12 +172,12 @@ impl NoteAttachmentContent { // ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the [`NoteAttachmentContentType`]. - pub fn content_type(&self) -> NoteAttachmentContentType { + /// Returns the [`NoteAttachmentKind`]. + pub fn attachment_kind(&self) -> NoteAttachmentKind { match self { - NoteAttachmentContent::None => NoteAttachmentContentType::None, - NoteAttachmentContent::Word(_) => NoteAttachmentContentType::Word, - NoteAttachmentContent::Array(_) => NoteAttachmentContentType::Array, + NoteAttachmentContent::None => NoteAttachmentKind::None, + NoteAttachmentContent::Word(_) => NoteAttachmentKind::Word, + NoteAttachmentContent::Array(_) => NoteAttachmentKind::Array, } } @@ -199,7 +197,7 @@ impl NoteAttachmentContent { impl Serializable for NoteAttachmentContent { fn write_into(&self, target: &mut W) { - self.content_type().write_into(target); + self.attachment_kind().write_into(target); match self { NoteAttachmentContent::None => (), @@ -216,15 +214,15 @@ impl Serializable for NoteAttachmentContent { impl Deserializable for NoteAttachmentContent { fn read_from(source: &mut R) -> Result { - let content_type = NoteAttachmentContentType::read_from(source)?; + let attachment_kind = NoteAttachmentKind::read_from(source)?; - match content_type { - NoteAttachmentContentType::None => Ok(NoteAttachmentContent::None), - NoteAttachmentContentType::Word => { + match attachment_kind { + NoteAttachmentKind::None => Ok(NoteAttachmentContent::None), + NoteAttachmentKind::Word => { let word = Word::read_from(source)?; Ok(NoteAttachmentContent::Word(word)) }, - NoteAttachmentContentType::Array => { + NoteAttachmentKind::Array => { let num_elements = u16::read_from(source)?; let elements = source.read_many(num_elements as usize)?; Self::new_array(elements) @@ -310,80 +308,81 @@ impl From for NoteAttachmentContent { } } -// NOTE ATTACHMENT TYPE +// NOTE ATTACHMENT SCHEME // ================================================================================================ /// The user-defined type of a [`NoteAttachment`]. /// -/// A note attachment type is an arbitrary 32-bit unsigned integer. +/// A note attachment scheme is an arbitrary 32-bit unsigned integer. /// -/// Value `0` is reserved to signal that an attachment is untyped. That is, no attempt should be -/// made to guess the type of the attachment. Whenever the type of attachment is not standardized or -/// interoperability is unimportant, this untyped value can be used. +/// Value `0` is reserved to signal that the scheme is none or absent. Whenever the kind of +/// attachment is not standardized or interoperability is unimportant, this none value can be +/// used. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NoteAttachmentType(u32); +pub struct NoteAttachmentScheme(u32); -impl NoteAttachmentType { +impl NoteAttachmentScheme { // CONSTANTS // -------------------------------------------------------------------------------------------- - /// The reserved value to signal an untyped note attachment. - const UNTYPED: u32 = 0; + /// The reserved value to signal an absent note attachment scheme. + const NONE: u32 = 0; // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new [`NoteAttachmentType`] from a `u32`. - pub const fn new(attachment_type: u32) -> Self { - Self(attachment_type) + /// Creates a new [`NoteAttachmentScheme`] from a `u32`. + pub const fn new(attachment_scheme: u32) -> Self { + Self(attachment_scheme) } - /// Returns the [`NoteAttachmentType`] that signals an untyped note attachment. - pub const fn untyped() -> Self { - Self(Self::UNTYPED) + /// Returns the [`NoteAttachmentScheme`] that signals the absence of an attachment scheme. + pub const fn none() -> Self { + Self(Self::NONE) } - /// Returns `true` if the attachment is untyped, `false` otherwise. - pub const fn is_untyped(&self) -> bool { - self.0 == Self::UNTYPED + /// Returns `true` if the attachment scheme is the reserved value that signals an absent scheme, + /// `false` otherwise. + pub const fn is_none(&self) -> bool { + self.0 == Self::NONE } // ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the note attachment type as a u32. + /// Returns the note attachment scheme as a u32. pub const fn as_u32(&self) -> u32 { self.0 } } -impl Default for NoteAttachmentType { - /// Returns [`NoteAttachmentType::untyped`]. +impl Default for NoteAttachmentScheme { + /// Returns [`NoteAttachmentScheme::none`]. fn default() -> Self { - Self::untyped() + Self::none() } } -impl core::fmt::Display for NoteAttachmentType { +impl core::fmt::Display for NoteAttachmentScheme { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_fmt(format_args!("{}", self.0)) } } -impl Serializable for NoteAttachmentType { +impl Serializable for NoteAttachmentScheme { fn write_into(&self, target: &mut W) { self.as_u32().write_into(target); } } -impl Deserializable for NoteAttachmentType { +impl Deserializable for NoteAttachmentScheme { fn read_from(source: &mut R) -> Result { - let attachment_type = u32::read_from(source)?; - Ok(Self::new(attachment_type)) + let attachment_scheme = u32::read_from(source)?; + Ok(Self::new(attachment_scheme)) } } -// NOTE ATTACHMENT CONTENT TYPE +// NOTE ATTACHMENT KIND // ================================================================================================ /// The type of [`NoteAttachmentContent`]. @@ -391,7 +390,7 @@ impl Deserializable for NoteAttachmentType { /// See its docs for more details on each type. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[repr(u8)] -pub enum NoteAttachmentContentType { +pub enum NoteAttachmentKind { /// Signals the absence of a note attachment. #[default] None = Self::NONE, @@ -403,7 +402,7 @@ pub enum NoteAttachmentContentType { Array = Self::ARRAY, } -impl NoteAttachmentContentType { +impl NoteAttachmentKind { // CONSTANTS // -------------------------------------------------------------------------------------------- @@ -414,28 +413,28 @@ impl NoteAttachmentContentType { // ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the content type as a u8. + /// Returns the attachment kind as a u8. pub const fn as_u8(&self) -> u8 { *self as u8 } - /// Returns `true` if the content type is `None`, `false` otherwise. + /// Returns `true` if the attachment kind is `None`, `false` otherwise. pub const fn is_none(&self) -> bool { matches!(self, Self::None) } - /// Returns `true` if the content type is `Word`, `false` otherwise. + /// Returns `true` if the attachment kind is `Word`, `false` otherwise. pub const fn is_word(&self) -> bool { matches!(self, Self::Word) } - /// Returns `true` if the content type is `Array`, `false` otherwise. + /// Returns `true` if the attachment kind is `Array`, `false` otherwise. pub const fn is_array(&self) -> bool { matches!(self, Self::Array) } } -impl TryFrom for NoteAttachmentContentType { +impl TryFrom for NoteAttachmentKind { type Error = NoteError; fn try_from(value: u8) -> Result { @@ -443,33 +442,33 @@ impl TryFrom for NoteAttachmentContentType { Self::NONE => Ok(Self::None), Self::WORD => Ok(Self::Word), Self::ARRAY => Ok(Self::Array), - _ => Err(NoteError::UnknownNoteAttachmentContentType(value)), + _ => Err(NoteError::UnknownNoteAttachmentKind(value)), } } } -impl core::fmt::Display for NoteAttachmentContentType { +impl core::fmt::Display for NoteAttachmentKind { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let output = match self { - NoteAttachmentContentType::None => "None", - NoteAttachmentContentType::Word => "Word", - NoteAttachmentContentType::Array => "Array", + NoteAttachmentKind::None => "None", + NoteAttachmentKind::Word => "Word", + NoteAttachmentKind::Array => "Array", }; f.write_str(output) } } -impl Serializable for NoteAttachmentContentType { +impl Serializable for NoteAttachmentKind { fn write_into(&self, target: &mut W) { self.as_u8().write_into(target); } } -impl Deserializable for NoteAttachmentContentType { +impl Deserializable for NoteAttachmentKind { fn read_from(source: &mut R) -> Result { - let content_type = u8::read_from(source)?; - Self::try_from(content_type) + let attachment_kind = u8::read_from(source)?; + Self::try_from(attachment_kind) .map_err(|err| DeserializationError::InvalidValue(err.to_string())) } } @@ -485,9 +484,9 @@ mod tests { #[rstest::rstest] #[case::attachment_none(NoteAttachment::default())] - #[case::attachment_word(NoteAttachment::new_word(NoteAttachmentType::new(1), Word::from([3, 4, 5, 6u32])))] + #[case::attachment_word(NoteAttachment::new_word(NoteAttachmentScheme::new(1), Word::from([3, 4, 5, 6u32])))] #[case::attachment_array(NoteAttachment::new_array( - NoteAttachmentType::new(u32::MAX), + NoteAttachmentScheme::new(u32::MAX), vec![Felt::new(5), Felt::new(6), Felt::new(7)], )?)] #[test] @@ -510,9 +509,9 @@ mod tests { } #[test] - fn note_attachment_content_type_fails_on_unknown_variant() -> anyhow::Result<()> { - let err = NoteAttachmentContentType::try_from(3u8).unwrap_err(); - assert_matches!(err, NoteError::UnknownNoteAttachmentContentType(3u8)); + fn note_attachment_kind_fails_on_unknown_variant() -> anyhow::Result<()> { + let err = NoteAttachmentKind::try_from(3u8).unwrap_err(); + assert_matches!(err, NoteError::UnknownNoteAttachmentKind(3u8)); Ok(()) } } diff --git a/crates/miden-protocol/src/note/metadata.rs b/crates/miden-protocol/src/note/metadata.rs index b41b74d527..e1c3714bb7 100644 --- a/crates/miden-protocol/src/note/metadata.rs +++ b/crates/miden-protocol/src/note/metadata.rs @@ -10,7 +10,7 @@ use super::{ Serializable, Word, }; -use crate::note::{NoteAttachment, NoteAttachmentContentType, NoteAttachmentType}; +use crate::note::{NoteAttachment, NoteAttachmentKind, NoteAttachmentScheme}; use crate::{Hasher, NoteError}; // NOTE METADATA @@ -36,7 +36,7 @@ use crate::{Hasher, NoteError}; /// 0th felt: [sender_id_suffix (56 bits) | 6 zero bits | note_type (2 bit)] /// 1st felt: [sender_id_prefix (64 bits)] /// 2nd felt: [32 zero bits | note_tag (32 bits)] -/// 3rd felt: [30 zero bits | attachment_content_type (2 bits) | attachment_type (32 bits)] +/// 3rd felt: [30 zero bits | attachment_kind (2 bits) | attachment_scheme (32 bits)] /// ``` /// /// The felt validity of each part of the layout is guaranteed: @@ -49,12 +49,11 @@ use crate::{Hasher, NoteError}; /// - 4th felt: The upper 30 bits are always zero. /// /// The value of the attachment word depends on the -/// [`NoteAttachmentContentType`](crate::note::NoteAttachmentContentType): -/// - [`NoteAttachmentContentType::None`](crate::note::NoteAttachmentContentType::None): Empty word. -/// - [`NoteAttachmentContentType::Word`](crate::note::NoteAttachmentContentType::Word): The raw -/// word itself. -/// - [`NoteAttachmentContentType::Array`](crate::note::NoteAttachmentContentType::Array): The -/// commitment to the elements. +/// [`NoteAttachmentKind`](crate::note::NoteAttachmentKind): +/// - [`NoteAttachmentKind::None`](crate::note::NoteAttachmentKind::None): Empty word. +/// - [`NoteAttachmentKind::Word`](crate::note::NoteAttachmentKind::Word): The raw word itself. +/// - [`NoteAttachmentKind::Array`](crate::note::NoteAttachmentKind::Array): The commitment to the +/// elements. #[derive(Clone, Debug, Eq, PartialEq)] pub struct NoteMetadata { /// The ID of the account which created the note. @@ -122,8 +121,8 @@ impl NoteMetadata { sender: self.sender, note_type: self.note_type, tag: self.tag, - attachment_content_type: self.attachment().content().content_type(), - attachment_type: self.attachment.attachment_type(), + attachment_kind: self.attachment().content().attachment_kind(), + attachment_scheme: self.attachment.attachment_scheme(), } } @@ -201,8 +200,8 @@ struct NoteMetadataHeader { sender: AccountId, note_type: NoteType, tag: NoteTag, - attachment_content_type: NoteAttachmentContentType, - attachment_type: NoteAttachmentType, + attachment_kind: NoteAttachmentKind, + attachment_scheme: NoteAttachmentScheme, } impl From for Word { @@ -213,7 +212,7 @@ impl From for Word { metadata[1] = header.sender.prefix().as_felt(); metadata[2] = Felt::from(header.tag); metadata[3] = - merge_attachment_type_info(header.attachment_content_type, header.attachment_type); + merge_attachment_kind_scheme(header.attachment_kind, header.attachment_scheme); metadata } @@ -229,7 +228,7 @@ impl TryFrom for NoteMetadataHeader { let tag = u32::try_from(word[2]).map(NoteTag::new).map_err(|_| { NoteError::other("failed to convert note tag from metadata header to u32") })?; - let (attachment_content_type, attachment_type) = unmerge_attachment_type_info(word[3])?; + let (attachment_kind, attachment_scheme) = unmerge_attachment_kind_scheme(word[3])?; let sender = AccountId::try_from([sender_prefix, sender_suffix]).map_err(|source| { NoteError::other_with_source("failed to decode account ID from metadata header", source) @@ -239,8 +238,8 @@ impl TryFrom for NoteMetadataHeader { sender, note_type, tag, - attachment_content_type, - attachment_type, + attachment_kind, + attachment_scheme, }) } } @@ -290,45 +289,41 @@ fn unmerge_sender_suffix_and_note_type(element: Felt) -> Result<(Felt, NoteType) Ok((sender_suffix, note_type)) } -/// Merges the [`NoteAttachmentType`] and [`NoteAttachmentContentType`] into a single [`Felt`]. +/// Merges the [`NoteAttachmentScheme`] and [`NoteAttachmentKind`] into a single [`Felt`]. /// /// The layout is as follows: /// /// ```text -/// [30 zero bits | attachment_content_type (2 bits) | attachment_type (32 bits)] +/// [30 zero bits | attachment_kind (2 bits) | attachment_scheme (32 bits)] /// ``` -fn merge_attachment_type_info( - attachment_content_type: NoteAttachmentContentType, - attachment_type: NoteAttachmentType, +fn merge_attachment_kind_scheme( + attachment_kind: NoteAttachmentKind, + attachment_scheme: NoteAttachmentScheme, ) -> Felt { - debug_assert!( - attachment_content_type.as_u8() < 4, - "attachment content type should fit into two bits" - ); - let mut merged = (attachment_content_type.as_u8() as u64) << 32; - let attachment_type = attachment_type.as_u32(); - merged |= attachment_type as u64; + debug_assert!(attachment_kind.as_u8() < 4, "attachment kind should fit into two bits"); + let mut merged = (attachment_kind.as_u8() as u64) << 32; + let attachment_scheme = attachment_scheme.as_u32(); + merged |= attachment_scheme as u64; Felt::try_from(merged).expect("the upper bit should be zero and the felt therefore valid") } -/// Unmerges the attachment content type and attachment type. -fn unmerge_attachment_type_info( +/// Unmerges the attachment kind and attachment scheme. +fn unmerge_attachment_kind_scheme( element: Felt, -) -> Result<(NoteAttachmentContentType, NoteAttachmentType), NoteError> { - let attachment_type = element.as_int() as u32; - let attachment_content_type = (element.as_int() >> 32) as u8; - - let attachment_type = NoteAttachmentType::new(attachment_type); - let attachment_content_type = NoteAttachmentContentType::try_from(attachment_content_type) - .map_err(|source| { - NoteError::other_with_source( - "failed to decode attachment content type from metadata header", - source, - ) - })?; +) -> Result<(NoteAttachmentKind, NoteAttachmentScheme), NoteError> { + let attachment_scheme = element.as_int() as u32; + let attachment_kind = (element.as_int() >> 32) as u8; + + let attachment_scheme = NoteAttachmentScheme::new(attachment_scheme); + let attachment_kind = NoteAttachmentKind::try_from(attachment_kind).map_err(|source| { + NoteError::other_with_source( + "failed to decode attachment kind from metadata header", + source, + ) + })?; - Ok((attachment_content_type, attachment_type)) + Ok((attachment_kind, attachment_scheme)) } // TESTS @@ -338,14 +333,14 @@ fn unmerge_attachment_type_info( mod tests { use super::*; - use crate::note::NoteAttachmentType; + use crate::note::NoteAttachmentScheme; use crate::testing::account_id::ACCOUNT_ID_MAX_ONES; #[rstest::rstest] #[case::attachment_none(NoteAttachment::default())] - #[case::attachment_raw(NoteAttachment::new_word(NoteAttachmentType::new(0), Word::from([3, 4, 5, 6u32])))] + #[case::attachment_raw(NoteAttachment::new_word(NoteAttachmentScheme::new(0), Word::from([3, 4, 5, 6u32])))] #[case::attachment_commitment(NoteAttachment::new_array( - NoteAttachmentType::new(u32::MAX), + NoteAttachmentScheme::new(u32::MAX), vec![Felt::new(5), Felt::new(6), Felt::new(7)], )?)] #[test] diff --git a/crates/miden-protocol/src/note/mod.rs b/crates/miden-protocol/src/note/mod.rs index 3c82e10e3d..7af4278953 100644 --- a/crates/miden-protocol/src/note/mod.rs +++ b/crates/miden-protocol/src/note/mod.rs @@ -25,8 +25,8 @@ pub use attachment::{ NoteAttachment, NoteAttachmentArray, NoteAttachmentContent, - NoteAttachmentContentType, - NoteAttachmentType, + NoteAttachmentKind, + NoteAttachmentScheme, }; mod execution_hint; diff --git a/crates/miden-protocol/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs index 3fba0621af..4fa48ae66d 100644 --- a/crates/miden-protocol/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -90,7 +90,7 @@ pub const KERNEL_PROCEDURES: [Word; 53] = [ // output_note_add_asset word!("0xaf22383e4390f4f15a429768f79aa445f8a535bb21b0807172b9ef2de063d9d1"), // output_note_set_attachment - word!("0xf832b327839619a98ee3992c9abf1cec0ede01c3a25019b6874bc27bc4614405"), + word!("0x800ab6457b20be22a721d61770ab493334004d2b5d01a6bbd245e49554c31a2c"), // tx_get_num_input_notes word!("0xfcc186d4b65c584f3126dda1460b01eef977efd76f9e36f972554af28e33c685"), // tx_get_input_notes_commitment diff --git a/crates/miden-standards/asm/standards/notes/mint.masm b/crates/miden-standards/asm/standards/notes/mint.masm index 9d11b9211a..7934306e33 100644 --- a/crates/miden-standards/asm/standards/notes/mint.masm +++ b/crates/miden-standards/asm/standards/notes/mint.masm @@ -14,8 +14,8 @@ const OUTPUT_NOTE_TYPE_PRIVATE=2 # Memory Addresses of MINT note inputs # The attachment is at the same memory address for both private and public inputs. -const ATTACHMENT_TYPE_ADDRESS=2 -const ATTACHMENT_CONTENT_TYPE_ADDRESS=3 +const ATTACHMENT_KIND_ADDRESS=2 +const ATTACHMENT_SCHEME_ADDRESS=3 const ATTACHMENT_ADDRESS=4 const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=16 @@ -39,16 +39,16 @@ const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 12 inputs for #! Private mode (12 inputs) - creates a private note: #! - tag: Note tag for the output note #! - amount: The amount to mint -#! - attachment_content_type: The content type of the attachment. -#! - attachment_type: The user-defined type of the attachment. +#! - attachment_scheme: The user-defined type of the attachment. +#! - attachment_kind: The attachment kind of the attachment. #! - ATTACHMENT: The attachment to be set. #! - RECIPIENT: The recipient digest (4 elements) #! #! Public mode (16+ inputs) - creates a public note with variable-length inputs: #! - tag: Note tag for the output note #! - amount: The amount to mint -#! - attachment_content_type: The content type of the attachment. -#! - attachment_type: The user-defined type of the attachment. +#! - attachment_scheme: The user-defined type of the attachment. +#! - attachment_kind: The attachment kind of the attachment. #! - ATTACHMENT: The attachment to be set. #! - SCRIPT_ROOT: Script root of the output note (4 elements) #! - SERIAL_NUM: Serial number of the output note (4 elements) @@ -126,10 +126,10 @@ pub proc main padw mem_loadw_be.ATTACHMENT_ADDRESS # => [ATTACHMENT, note_idx, pad(18))] - mem_load.ATTACHMENT_TYPE_ADDRESS - mem_load.ATTACHMENT_CONTENT_TYPE_ADDRESS + mem_load.ATTACHMENT_KIND_ADDRESS + mem_load.ATTACHMENT_SCHEME_ADDRESS movup.6 - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(18))] + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(18))] exec.output_note::set_attachment # => [pad(18))] diff --git a/crates/miden-standards/asm/standards/notes/swap.masm b/crates/miden-standards/asm/standards/notes/swap.masm index 81f7479447..8c95668cd1 100644 --- a/crates/miden-standards/asm/standards/notes/swap.masm +++ b/crates/miden-standards/asm/standards/notes/swap.masm @@ -9,8 +9,8 @@ const SWAP_NOTE_INPUTS_NUMBER=16 const PAYBACK_NOTE_TYPE_ADDRESS=0 const PAYBACK_NOTE_TAG_ADDRESS=1 -const ATTACHMENT_TYPE_ADDRESS=2 -const ATTACHMENT_CONTENT_TYPE_ADDRESS=3 +const ATTACHMENT_KIND_ADDRESS=2 +const ATTACHMENT_SCHEME_ADDRESS=3 const ATTACHMENT_ADDRESS=4 const REQUESTED_ASSET_ADDRESS=8 const PAYBACK_RECIPIENT_ADDRESS=12 @@ -35,8 +35,8 @@ const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset #! Note inputs are assumed to be as follows: #! - payback_note_type #! - payback_note_tag -#! - attachment_type -#! - attachment_content_type +#! - attachment_kind +#! - attachment_scheme #! - ATTACHMENT #! - REQUESTED_ASSET #! - PAYBACK_RECIPIENT @@ -47,8 +47,8 @@ const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset #! - account vault does not contain the requested asset. #! - adding a fungible asset would result in amount overflow, i.e., the total amount would be #! greater than 2^63. -#! - any of the attachment types does not fit into a u32. -#! - the attachment content type is an unknown variant. +#! - the attachment kind or scheme does not fit into a u32. +#! - the attachment kind is an unknown variant. pub proc main # dropping note args dropw @@ -98,10 +98,10 @@ pub proc main # => [note_idx, pad(11)] mem_loadw_be.ATTACHMENT_ADDRESS - mem_load.ATTACHMENT_TYPE_ADDRESS - mem_load.ATTACHMENT_CONTENT_TYPE_ADDRESS + mem_load.ATTACHMENT_KIND_ADDRESS + mem_load.ATTACHMENT_SCHEME_ADDRESS movup.6 - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] exec.output_note::set_attachment # => [pad(12)] @@ -129,4 +129,4 @@ pub proc main dropw end # => [] -end \ No newline at end of file +end diff --git a/crates/miden-standards/src/account/interface/component.rs b/crates/miden-standards/src/account/interface/component.rs index 14eb5e24ad..16bbe0ec91 100644 --- a/crates/miden-standards/src/account/interface/component.rs +++ b/crates/miden-standards/src/account/interface/component.rs @@ -295,17 +295,17 @@ impl AccountComponentInterface { body.push_str(&format!( " push.{ATTACHMENT} - push.{attachment_type} - push.{attachment_content_type} + push.{attachment_kind} + push.{attachment_scheme} movup.6 - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT, pad(16)] + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(16)] exec.::miden::protocol::output_note::set_attachment # => [pad(16)] ", ATTACHMENT = partial_note.metadata().to_attachment_word(), - attachment_type = partial_note.metadata().attachment().attachment_type().as_u32(), - attachment_content_type = - partial_note.metadata().attachment().content_type().as_u8(), + attachment_scheme = + partial_note.metadata().attachment().attachment_scheme().as_u32(), + attachment_kind = partial_note.metadata().attachment().attachment_kind().as_u8(), )); } diff --git a/crates/miden-standards/src/note/mint_inputs.rs b/crates/miden-standards/src/note/mint_inputs.rs index 20ee801757..eb52952956 100644 --- a/crates/miden-standards/src/note/mint_inputs.rs +++ b/crates/miden-standards/src/note/mint_inputs.rs @@ -41,7 +41,7 @@ impl MintNoteInputs { tag: Felt, ) -> Result { // Calculate total number of inputs that will be created: - // 16 fixed inputs (tag, amount, attachment_type, attachment_content_type, ATTACHMENT, + // 16 fixed inputs (tag, amount, attachment_kind, attachment_scheme, ATTACHMENT, // SCRIPT_ROOT, SERIAL_NUM) + variable recipient inputs length const FIXED_PUBLIC_INPUTS: usize = 16; let total_inputs = FIXED_PUBLIC_INPUTS + recipient.inputs().num_values() as usize; @@ -88,28 +88,23 @@ impl From for NoteInputs { tag, attachment, } => { - let attachment_type = Felt::from(attachment.attachment_type().as_u32()); - let attachment_content_type = Felt::from(attachment.content_type().as_u8()); + let attachment_scheme = Felt::from(attachment.attachment_scheme().as_u32()); + let attachment_kind = Felt::from(attachment.attachment_kind().as_u8()); let attachment = attachment.content().to_word(); let mut input_values = Vec::with_capacity(12); - input_values.extend_from_slice(&[ - tag, - amount, - attachment_type, - attachment_content_type, - ]); + input_values.extend_from_slice(&[tag, amount, attachment_kind, attachment_scheme]); input_values.extend_from_slice(attachment.as_elements()); input_values.extend_from_slice(recipient_digest.as_elements()); NoteInputs::new(input_values) .expect("number of inputs should not exceed max inputs") }, MintNoteInputs::Public { recipient, amount, tag, attachment } => { - let attachment_type = Felt::from(attachment.attachment_type().as_u32()); - let attachment_content_type = Felt::from(attachment.content_type().as_u8()); + let attachment_scheme = Felt::from(attachment.attachment_scheme().as_u32()); + let attachment_kind = Felt::from(attachment.attachment_kind().as_u8()); let attachment = attachment.content().to_word(); - let mut input_values = vec![tag, amount, attachment_type, attachment_content_type]; + let mut input_values = vec![tag, amount, attachment_kind, attachment_scheme]; input_values.extend_from_slice(attachment.as_elements()); input_values.extend_from_slice(recipient.script().root().as_elements()); input_values.extend_from_slice(recipient.serial_num().as_elements()); diff --git a/crates/miden-standards/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs index 6ef31e3fc5..0fa70b64ae 100644 --- a/crates/miden-standards/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -128,16 +128,16 @@ pub fn create_swap_note( let requested_asset_word: Word = requested_asset.into(); let payback_tag = NoteTag::with_account_target(sender); - let attachment_type = Felt::from(payback_note_attachment.attachment_type().as_u32()); - let attachment_content_type = Felt::from(payback_note_attachment.content_type().as_u8()); + let attachment_scheme = Felt::from(payback_note_attachment.attachment_scheme().as_u32()); + let attachment_kind = Felt::from(payback_note_attachment.attachment_kind().as_u8()); let attachment = payback_note_attachment.content().to_word(); let mut inputs = Vec::with_capacity(16); inputs.extend_from_slice(&[ payback_note_type.into(), payback_tag.into(), - attachment_type, - attachment_content_type, + attachment_scheme, + attachment_kind, ]); inputs.extend_from_slice(attachment.as_elements()); inputs.extend_from_slice(requested_asset_word.as_elements()); diff --git a/crates/miden-standards/src/note/network_account_target.rs b/crates/miden-standards/src/note/network_account_target.rs index 4c64073b78..28bb22944f 100644 --- a/crates/miden-standards/src/note/network_account_target.rs +++ b/crates/miden-standards/src/note/network_account_target.rs @@ -2,8 +2,8 @@ use miden_protocol::account::AccountId; use miden_protocol::note::{ NoteAttachment, NoteAttachmentContent, - NoteAttachmentContentType, - NoteAttachmentType, + NoteAttachmentKind, + NoteAttachmentScheme, NoteExecutionHint, }; use miden_protocol::{AccountIdError, NoteError, Word}; @@ -33,9 +33,9 @@ impl NetworkAccountTarget { // CONSTANTS // -------------------------------------------------------------------------------------------- - /// The standardized type of [`NetworkAccountTarget`] attachments. - pub const ATTACHMENT_TYPE: NoteAttachmentType = - WellKnownNoteAttachment::NetworkAccountTarget.attachment_type(); + /// The standardized scheme of [`NetworkAccountTarget`] attachments. + pub const ATTACHMENT_SCHEME: NoteAttachmentScheme = + WellKnownNoteAttachment::NetworkAccountTarget.attachment_scheme(); // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -80,7 +80,7 @@ impl From for NoteAttachment { word[1] = network_attachment.target_id.prefix().as_felt(); word[2] = network_attachment.exec_hint.into(); - NoteAttachment::new_word(NetworkAccountTarget::ATTACHMENT_TYPE, word) + NoteAttachment::new_word(NetworkAccountTarget::ATTACHMENT_SCHEME, word) } } @@ -88,9 +88,9 @@ impl TryFrom for NetworkAccountTarget { type Error = NetworkAccountTargetError; fn try_from(attachment: NoteAttachment) -> Result { - if attachment.attachment_type() != Self::ATTACHMENT_TYPE { - return Err(NetworkAccountTargetError::AttachmentTypeMismatch( - attachment.attachment_type(), + if attachment.attachment_scheme() != Self::ATTACHMENT_SCHEME { + return Err(NetworkAccountTargetError::AttachmentSchemeMismatch( + attachment.attachment_scheme(), )); } @@ -108,8 +108,8 @@ impl TryFrom for NetworkAccountTarget { NetworkAccountTarget::new(target_id, exec_hint) }, - _ => Err(NetworkAccountTargetError::AttachmentContentTypeMismatch( - attachment.content().content_type(), + _ => Err(NetworkAccountTargetError::AttachmentKindMismatch( + attachment.content().attachment_kind(), )), } } @@ -123,15 +123,15 @@ pub enum NetworkAccountTargetError { #[error("target account ID must be of type network account")] TargetNotNetwork(AccountId), #[error( - "attachment type {0} did not match expected type {expected}", - expected = NetworkAccountTarget::ATTACHMENT_TYPE + "attachment scheme {0} did not match expected type {expected}", + expected = NetworkAccountTarget::ATTACHMENT_SCHEME )] - AttachmentTypeMismatch(NoteAttachmentType), + AttachmentSchemeMismatch(NoteAttachmentScheme), #[error( - "attachment content type {0} did not match expected type {expected}", - expected = NoteAttachmentContentType::Word + "attachment kind {0} did not match expected type {expected}", + expected = NoteAttachmentKind::Word )] - AttachmentContentTypeMismatch(NoteAttachmentContentType), + AttachmentKindMismatch(NoteAttachmentKind), #[error("failed to decode target account ID")] DecodeTargetId(#[source] AccountIdError), #[error("failed to decode execution hint")] diff --git a/crates/miden-standards/src/note/well_known_note_attachment.rs b/crates/miden-standards/src/note/well_known_note_attachment.rs index 1f1577ba2a..90ca70a5b3 100644 --- a/crates/miden-standards/src/note/well_known_note_attachment.rs +++ b/crates/miden-standards/src/note/well_known_note_attachment.rs @@ -1,6 +1,6 @@ -use miden_protocol::note::NoteAttachmentType; +use miden_protocol::note::NoteAttachmentScheme; -/// The [`NoteAttachmentType`]s of well-known note attachmens. +/// The [`NoteAttachmentScheme`]s of well-known note attachmens. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] pub enum WellKnownNoteAttachment { @@ -9,10 +9,10 @@ pub enum WellKnownNoteAttachment { } impl WellKnownNoteAttachment { - /// Returns the [`NoteAttachmentType`] of the well-known attachment. - pub const fn attachment_type(&self) -> NoteAttachmentType { + /// Returns the [`NoteAttachmentScheme`] of the well-known attachment. + pub const fn attachment_scheme(&self) -> NoteAttachmentScheme { match self { - WellKnownNoteAttachment::NetworkAccountTarget => NoteAttachmentType::new(1u32), + WellKnownNoteAttachment::NetworkAccountTarget => NoteAttachmentScheme::new(1u32), } } } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index f63057b4a6..88ad6b922b 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -13,7 +13,7 @@ use miden_protocol::note::{ Note, NoteAssets, NoteAttachment, - NoteAttachmentType, + NoteAttachmentScheme, NoteExecutionHint, NoteInputs, NoteMetadata, @@ -261,7 +261,7 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { let output_tag_2 = NoteTag::with_account_target(local_account); let assets = NoteAssets::new(vec![input_asset_2])?; let attachment = NoteAttachment::new_array( - NoteAttachmentType::new(5), + NoteAttachmentScheme::new(5), [42, 43, 44, 45, 46u32].map(Felt::from).to_vec(), )?; let metadata = @@ -314,9 +314,9 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { # => [note_idx] push.{ATTACHMENT2} - push.{attachment_type2} + push.{attachment_scheme2} movup.5 - # => [note_idx, attachment_type, ATTACHMENT] + # => [note_idx, attachment_scheme, ATTACHMENT] exec.output_note::set_array_attachment # => [] @@ -341,7 +341,7 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { **output_note_2.assets().iter().take(1).collect::>().first().unwrap() ), ATTACHMENT2 = output_note_2.metadata().to_attachment_word(), - attachment_type2 = output_note_2.metadata().attachment().attachment_type().as_u32(), + attachment_scheme2 = output_note_2.metadata().attachment().attachment_scheme().as_u32(), ); let exec_output = &tx_context.execute_code(&code).await?; @@ -1024,10 +1024,10 @@ async fn test_set_none_attachment() -> anyhow::Result<()> { # => [note_idx] push.{ATTACHMENT} - push.{attachment_type} - push.{attachment_content_type} + push.{attachment_kind} + push.{attachment_scheme} movup.6 - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT] + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] exec.output_note::set_attachment # => [] @@ -1039,9 +1039,8 @@ async fn test_set_none_attachment() -> anyhow::Result<()> { note_type = output_note.metadata().note_type() as u8, tag = output_note.metadata().tag().as_u32(), ATTACHMENT = output_note.metadata().to_attachment_word(), - attachment_content_type = - output_note.metadata().attachment().content().content_type().as_u8(), - attachment_type = output_note.metadata().attachment().attachment_type().as_u32(), + attachment_kind = output_note.metadata().attachment().content().attachment_kind().as_u8(), + attachment_scheme = output_note.metadata().attachment().attachment_scheme().as_u32(), ); let tx_script = CodeBuilder::new().compile_tx_script(tx_script)?; @@ -1065,7 +1064,7 @@ async fn test_set_word_attachment() -> anyhow::Result<()> { let account = Account::mock(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, Auth::IncrNonce); let rng = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])); let attachment = - NoteAttachment::new_word(NoteAttachmentType::new(u32::MAX), Word::from([3, 4, 5, 6u32])); + NoteAttachment::new_word(NoteAttachmentScheme::new(u32::MAX), Word::from([3, 4, 5, 6u32])); let output_note = OutputNote::Full(NoteBuilder::new(account.id(), rng).attachment(attachment).build()?); @@ -1081,9 +1080,9 @@ async fn test_set_word_attachment() -> anyhow::Result<()> { # => [note_idx] push.{ATTACHMENT} - push.{attachment_type} + push.{attachment_scheme} movup.5 - # => [note_idx, attachment_type, ATTACHMENT] + # => [note_idx, attachment_scheme, ATTACHMENT] exec.output_note::set_word_attachment # => [] @@ -1094,7 +1093,7 @@ async fn test_set_word_attachment() -> anyhow::Result<()> { RECIPIENT = output_note.recipient().unwrap().digest(), note_type = output_note.metadata().note_type() as u8, tag = output_note.metadata().tag().as_u32(), - attachment_type = output_note.metadata().attachment().attachment_type().as_u32(), + attachment_scheme = output_note.metadata().attachment().attachment_scheme().as_u32(), ATTACHMENT = output_note.metadata().to_attachment_word(), ); @@ -1119,7 +1118,7 @@ async fn test_set_array_attachment() -> anyhow::Result<()> { let account = Account::mock(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, Auth::IncrNonce); let rng = RpoRandomCoin::new(Word::from([1, 2, 3, 4u32])); let elements = [3, 4, 5, 6, 7, 8, 9u32].map(Felt::from).to_vec(); - let attachment = NoteAttachment::new_array(NoteAttachmentType::new(42), elements.clone())?; + let attachment = NoteAttachment::new_array(NoteAttachmentScheme::new(42), elements.clone())?; let output_note = OutputNote::Full(NoteBuilder::new(account.id(), rng).attachment(attachment).build()?); @@ -1135,9 +1134,9 @@ async fn test_set_array_attachment() -> anyhow::Result<()> { # => [note_idx] push.{ATTACHMENT} - push.{attachment_type} + push.{attachment_scheme} movup.5 - # => [note_idx, attachment_type, ATTACHMENT] + # => [note_idx, attachment_scheme, ATTACHMENT] exec.output_note::set_array_attachment # => [] @@ -1148,7 +1147,7 @@ async fn test_set_array_attachment() -> anyhow::Result<()> { RECIPIENT = output_note.recipient().unwrap().digest(), note_type = output_note.metadata().note_type() as u8, tag = output_note.metadata().tag().as_u32(), - attachment_type = output_note.metadata().attachment().attachment_type().as_u32(), + attachment_scheme = output_note.metadata().attachment().attachment_scheme().as_u32(), ATTACHMENT = output_note.metadata().to_attachment_word(), ); diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index 8a50ed7b37..177f182db2 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -23,7 +23,7 @@ use miden_protocol::note::{ NoteAssets, NoteAttachment, NoteAttachmentContent, - NoteAttachmentType, + NoteAttachmentScheme, NoteHeader, NoteId, NoteInputs, @@ -206,9 +206,9 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { let tag3 = NoteTag::default(); let attachment2 = - NoteAttachment::new_word(NoteAttachmentType::new(28), Word::from([2, 3, 4, 5u32])); + NoteAttachment::new_word(NoteAttachmentScheme::new(28), Word::from([2, 3, 4, 5u32])); let attachment3 = NoteAttachment::new_array( - NoteAttachmentType::new(29), + NoteAttachmentScheme::new(29), [6, 7, 8, 9u32].map(Felt::from).to_vec(), )?; @@ -301,7 +301,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { # => [note_idx] push.{ATTACHMENT2} - push.{attachment_type2} + push.{attachment_scheme2} movup.5 exec.output_note::set_word_attachment # => [] @@ -314,7 +314,7 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { # => [note_idx = 2] push.{ATTACHMENT3} - push.{attachment_type3} + push.{attachment_scheme3} movup.5 exec.output_note::set_array_attachment # => [] @@ -329,9 +329,9 @@ async fn executed_transaction_output_notes() -> anyhow::Result<()> { NOTETYPE1 = note_type1 as u8, NOTETYPE2 = note_type2 as u8, NOTETYPE3 = note_type3 as u8, - attachment_type2 = attachment2.attachment_type().as_u32(), + attachment_scheme2 = attachment2.attachment_scheme().as_u32(), ATTACHMENT2 = attachment2.content().to_word(), - attachment_type3 = attachment3.attachment_type().as_u32(), + attachment_scheme3 = attachment3.attachment_scheme().as_u32(), ATTACHMENT3 = attachment3.content().to_word(), ); diff --git a/crates/miden-testing/src/utils.rs b/crates/miden-testing/src/utils.rs index d6b3468e0b..30aeb8fe76 100644 --- a/crates/miden-testing/src/utils.rs +++ b/crates/miden-testing/src/utils.rs @@ -238,16 +238,16 @@ fn note_script_that_creates_notes<'note>( out.push_str(&format!( " push.{ATTACHMENT} - push.{attachment_type} - push.{attachment_content_type} + push.{attachment_scheme} + push.{attachment_kind} dup.6 - # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT, note_idx] + # => [note_idx, attachment_kind, attachment_scheme, ATTACHMENT, note_idx] exec.output_note::set_attachment # => [note_idx] ", ATTACHMENT = note.metadata().to_attachment_word(), - attachment_type = note.metadata().attachment().attachment_type().as_u32(), - attachment_content_type = note.metadata().attachment().content().content_type().as_u8(), + attachment_scheme = note.metadata().attachment().attachment_scheme().as_u32(), + attachment_kind = note.metadata().attachment().content().attachment_kind().as_u8(), )); let assets_str = prepare_assets(note.assets()); diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index ca890b1362..f80a4c5a06 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -7,7 +7,7 @@ use miden_protocol::note::{ Note, NoteAssets, NoteAttachment, - NoteAttachmentType, + NoteAttachmentScheme, NoteInputs, NoteMetadata, NoteRecipient, @@ -38,7 +38,7 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { let tag = NoteTag::with_account_target(sender_basic_wallet_account.id()); let elements = [9, 8, 7, 6, 5u32].map(Felt::from).to_vec(); - let attachment = NoteAttachment::new_array(NoteAttachmentType::new(42), elements.clone())?; + let attachment = NoteAttachment::new_array(NoteAttachmentScheme::new(42), elements.clone())?; let metadata = NoteMetadata::new(sender_basic_wallet_account.id(), NoteType::Public, tag) .with_attachment(attachment.clone()); let assets = NoteAssets::new(vec![sent_asset]).unwrap(); @@ -98,7 +98,7 @@ async fn test_send_note_script_basic_fungible_faucet() -> anyhow::Result<()> { AccountInterface::from_account(&sender_basic_fungible_faucet_account); let tag = NoteTag::with_account_target(sender_basic_fungible_faucet_account.id()); - let attachment = NoteAttachment::new_word(NoteAttachmentType::new(100), Word::empty()); + let attachment = NoteAttachment::new_word(NoteAttachmentScheme::new(100), Word::empty()); let metadata = NoteMetadata::new(sender_basic_fungible_faucet_account.id(), NoteType::Public, tag) .with_attachment(attachment); diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index 94300ea96d..e6dca30443 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -241,7 +241,7 @@ pub enum TransactionKernelError { "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider" )] PublicNoteMissingDetails(NoteMetadata, Word), - #[error("attachment provided to set_attachment must be empty when content type is None")] + #[error("attachment provided to set_attachment must be empty when attachment kind is None")] NoteAttachmentNoneIsNotEmpty, #[error( "commitment of note attachment {actual} does not match attachment {provided} provided to set_attachment" diff --git a/crates/miden-tx/src/host/tx_event.rs b/crates/miden-tx/src/host/tx_event.rs index ab8ddaa419..760a6aff4e 100644 --- a/crates/miden-tx/src/host/tx_event.rs +++ b/crates/miden-tx/src/host/tx_event.rs @@ -7,8 +7,8 @@ use miden_protocol::note::{ NoteAttachment, NoteAttachmentArray, NoteAttachmentContent, - NoteAttachmentContentType, - NoteAttachmentType, + NoteAttachmentKind, + NoteAttachmentScheme, NoteId, NoteInputs, NoteMetadata, @@ -424,18 +424,18 @@ impl TransactionEvent { TransactionEventId::NoteBeforeSetAttachment => { // Expected stack state: [ - // event, attachment_content_type, attachment_type, + // event, attachment_scheme, attachment_kind, // note_ptr, note_ptr, ATTACHMENT // ] - let attachment_content_type = process.get_stack_item(1); - let attachment_type = process.get_stack_item(2); + let attachment_scheme = process.get_stack_item(1); + let attachment_kind = process.get_stack_item(2); let note_ptr = process.get_stack_item(3); let attachment = process.get_stack_word_be(5); let (note_idx, attachment) = extract_note_attachment( - attachment_content_type, - attachment_type, + attachment_scheme, + attachment_kind, attachment, note_ptr, process.advice_provider(), @@ -728,40 +728,38 @@ fn build_note_metadata( } fn extract_note_attachment( - attachment_content_type: Felt, - attachment_type: Felt, + attachment_scheme: Felt, + attachment_kind: Felt, attachment: Word, note_ptr: Felt, advice_provider: &AdviceProvider, ) -> Result<(usize, NoteAttachment), TransactionKernelError> { let note_idx = note_ptr_to_idx(note_ptr)?; - let attachment_content_type = u8::try_from(attachment_content_type) - .map_err(|_| { - TransactionKernelError::other("failed to convert attachment content type to u8") - }) - .and_then(|content_type| { - NoteAttachmentContentType::try_from(content_type).map_err(|source| { + let attachment_kind = u8::try_from(attachment_kind) + .map_err(|_| TransactionKernelError::other("failed to convert attachment kind to u8")) + .and_then(|attachment_kind| { + NoteAttachmentKind::try_from(attachment_kind).map_err(|source| { TransactionKernelError::other_with_source( - "failed to convert u8 to attachment content type", + "failed to convert u8 to attachment kind", source, ) }) })?; - let attachment_type = u32::try_from(attachment_type) - .map_err(|_| TransactionKernelError::other("failed to convert attachment type to u32")) - .map(NoteAttachmentType::new)?; + let attachment_scheme = u32::try_from(attachment_scheme) + .map_err(|_| TransactionKernelError::other("failed to convert attachment scheme to u32")) + .map(NoteAttachmentScheme::new)?; - let attachment_content = match attachment_content_type { - NoteAttachmentContentType::None => { + let attachment_content = match attachment_kind { + NoteAttachmentKind::None => { if !attachment.is_empty() { return Err(TransactionKernelError::NoteAttachmentNoneIsNotEmpty); } NoteAttachmentContent::None }, - NoteAttachmentContentType::Word => NoteAttachmentContent::Word(attachment), - NoteAttachmentContentType::Array => { + NoteAttachmentKind::Word => NoteAttachmentContent::Word(attachment), + NoteAttachmentKind::Array => { let elements = advice_provider.get_mapped_values(&attachment).ok_or_else(|| { TransactionKernelError::other( "elements of a note attachment commitment must be present in the advice provider", @@ -788,7 +786,7 @@ fn extract_note_attachment( }; let attachment = - NoteAttachment::new(attachment_type, attachment_content).map_err(|source| { + NoteAttachment::new(attachment_scheme, attachment_content).map_err(|source| { TransactionKernelError::other_with_source("failed to extract note attachment", source) })?; From 3a9d2c1c467772e86a1774d671046ca1335eea08 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Thu, 15 Jan 2026 19:35:24 -0500 Subject: [PATCH 103/114] chore: propagate NoteTag refactor to agglayer branch (#2287) --- .../asm/bridge/agglayer_faucet.masm | 14 +++++++------- .../miden-agglayer/asm/bridge/bridge_out.masm | 7 ++----- crates/miden-agglayer/src/lib.rs | 13 ++----------- .../miden-testing/tests/agglayer/bridge_in.rs | 15 +++------------ .../miden-testing/tests/agglayer/bridge_out.rs | 17 +++++------------ 5 files changed, 19 insertions(+), 47 deletions(-) diff --git a/crates/miden-agglayer/asm/bridge/agglayer_faucet.masm b/crates/miden-agglayer/asm/bridge/agglayer_faucet.masm index db22b900ad..4c12783065 100644 --- a/crates/miden-agglayer/asm/bridge/agglayer_faucet.masm +++ b/crates/miden-agglayer/asm/bridge/agglayer_faucet.masm @@ -35,7 +35,7 @@ const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 = 548 const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 = 552 # P2ID output note constants -const P2ID_SCRIPT_ROOT = [7588674509004260508, 4058706621878288170, 5607159951796201570, 5541281552524512743] +const P2ID_SCRIPT_ROOT = [13362761878458161062, 15090726097241769395, 444910447169617901, 3558201871398422326] const P2ID_NOTE_NUM_INPUTS = 2 const OUTPUT_NOTE_TYPE_PUBLIC = 1 const EXECUTION_HINT_ALWAYS = 1 @@ -160,19 +160,19 @@ proc build_p2id_output_note exec.note::build_recipient # => [RECIPIENT] - - push.EXECUTION_HINT_ALWAYS push.OUTPUT_NOTE_TYPE_PUBLIC push.OUTPUT_NOTE_AUX - # => [aux, note_type, execution_hint, RECIPIENT] + + push.OUTPUT_NOTE_TYPE_PUBLIC + # => [note_type, RECIPIENT] mem_load.OUTPUT_NOTE_TAG_MEM_ADDR - # => [tag, aux, execution_hint, RECIPIENT] + # => [tag, RECIPIENT] exec.get_raw_claim_amount - # => [AMOUNT[1], AMOUNT[0], tag, aux, note_type, execution_hint, RECIPIENT] + # => [AMOUNT[1], AMOUNT[0], tag, note_type, RECIPIENT] # TODO: implement scale down logic; stubbed out for now exec.asset_conversion::scale_u256_to_native_amount - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT] + # => [amount, tag, note_type, RECIPIENT] exec.faucets::distribute # => [pad(16)] diff --git a/crates/miden-agglayer/asm/bridge/bridge_out.masm b/crates/miden-agglayer/asm/bridge/bridge_out.masm index 3b8043e7c1..43c6885b35 100644 --- a/crates/miden-agglayer/asm/bridge/bridge_out.masm +++ b/crates/miden-agglayer/asm/bridge/bridge_out.masm @@ -10,8 +10,7 @@ use miden::agglayer::local_exit_tree const MMR_PTR=42 const LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") -const BURN_NOTE_ROOT = [6407337173854817345, 5626358912819151014, 703918618794810515, 17401169215223723177] -const EXECUTION_HINT_ALWAYS=1 +const BURN_NOTE_ROOT = [15615638671708113717, 1774623749760042586, 2028263167268363492, 12931944505143778072] const PUBLIC_NOTE=1 const AUX=0 const NUM_BURN_NOTE_INPUTS=0 @@ -75,11 +74,9 @@ proc create_burn_note exec.note::build_recipient # => [RECIPIENT] - push.EXECUTION_HINT_ALWAYS push.PUBLIC_NOTE - push.AUX loc_load.5 - # => [tag, aux, note_type, execution_hint, RECIPIENT] + # => [tag, note_type, RECIPIENT] call.output_note::create # => [note_idx] diff --git a/crates/miden-agglayer/src/lib.rs b/crates/miden-agglayer/src/lib.rs index 65765d1482..3753f6e663 100644 --- a/crates/miden-agglayer/src/lib.rs +++ b/crates/miden-agglayer/src/lib.rs @@ -24,7 +24,6 @@ use miden_protocol::crypto::rand::FeltRng; use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -465,23 +464,15 @@ pub fn create_claim_note(params: ClaimNoteParams<'_, R>) -> Result anyhow::Result<()> { metadata, ) = claim_note_test_inputs(amount_felt, user_account.id()); - let aux = Felt::new(0); - // Generate a serial number for the P2ID note let serial_num = builder.rng_mut().draw_word(); @@ -106,7 +103,7 @@ async fn test_bridge_in_claim_to_p2id() -> anyhow::Result<()> { metadata, claim_note_creator_account_id: user_account.id(), agglayer_faucet_account_id: agglayer_faucet.id(), - output_note_tag: NoteTag::from_account_id(user_account.id()), + output_note_tag: NoteTag::with_account_target(user_account.id()), p2id_serial_number: serial_num, destination_account_id: user_account.id(), rng: builder.rng_mut(), @@ -131,16 +128,10 @@ async fn test_bridge_in_claim_to_p2id() -> anyhow::Result<()> { // CREATE EXPECTED P2ID NOTE FOR VERIFICATION // -------------------------------------------------------------------------------------------- let mint_asset: Asset = FungibleAsset::new(agglayer_faucet.id(), amount_felt.into())?.into(); - let output_note_tag = NoteTag::from_account_id(user_account.id()); + let output_note_tag = NoteTag::with_account_target(user_account.id()); let expected_p2id_note = Note::new( NoteAssets::new(vec![mint_asset])?, - NoteMetadata::new( - agglayer_faucet.id(), - NoteType::Public, - output_note_tag, - NoteExecutionHint::always(), - aux, - )?, + NoteMetadata::new(agglayer_faucet.id(), NoteType::Public, output_note_tag), p2id_recipient, ); diff --git a/crates/miden-testing/tests/agglayer/bridge_out.rs b/crates/miden-testing/tests/agglayer/bridge_out.rs index e5be8f3814..dea33e04d4 100644 --- a/crates/miden-testing/tests/agglayer/bridge_out.rs +++ b/crates/miden-testing/tests/agglayer/bridge_out.rs @@ -15,7 +15,6 @@ use miden_protocol::asset::{Asset, FungibleAsset}; use miden_protocol::note::{ Note, NoteAssets, - NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -70,9 +69,7 @@ async fn test_bridge_out_consumes_b2agg_note() -> anyhow::Result<()> { let amount = Felt::new(100); let bridge_asset: Asset = FungibleAsset::new(faucet.id(), amount.into()).unwrap().into(); - let tag = NoteTag::for_local_use_case(0, 0).unwrap(); - let aux = Felt::new(0); - let note_execution_hint = NoteExecutionHint::always(); + let tag = NoteTag::new(0); let note_type = NoteType::Public; // Use Public note type for network transaction // Get the B2AGG note script @@ -93,8 +90,7 @@ async fn test_bridge_out_consumes_b2agg_note() -> anyhow::Result<()> { let inputs = NoteInputs::new(input_felts.clone())?; // Create the B2AGG note with assets from the faucet - let b2agg_note_metadata = - NoteMetadata::new(faucet.id(), note_type, tag, note_execution_hint, aux)?; + let b2agg_note_metadata = NoteMetadata::new(faucet.id(), note_type, tag); let b2agg_note_assets = NoteAssets::new(vec![bridge_asset])?; let serial_num = Word::from([1, 2, 3, 4u32]); let b2agg_note_script = NoteScript::new(b2agg_script); @@ -146,7 +142,7 @@ async fn test_bridge_out_consumes_b2agg_note() -> anyhow::Result<()> { assert_eq!( burn_note.metadata().tag(), - NoteTag::from_account_id(faucet.id()), + NoteTag::with_account_target(faucet.id()), "BURN note should have the correct tag" ); @@ -230,9 +226,7 @@ async fn test_b2agg_note_reclaim_scenario() -> anyhow::Result<()> { let amount = Felt::new(50); let bridge_asset: Asset = FungibleAsset::new(faucet.id(), amount.into()).unwrap().into(); - let tag = NoteTag::for_local_use_case(0, 0).unwrap(); - let aux = Felt::new(0); - let note_execution_hint = NoteExecutionHint::always(); + let tag = NoteTag::new(0); let note_type = NoteType::Public; // Get the B2AGG note script @@ -252,8 +246,7 @@ async fn test_b2agg_note_reclaim_scenario() -> anyhow::Result<()> { // Create the B2AGG note with the USER ACCOUNT as the sender // This is the key difference - the note sender will be the same as the consuming account - let b2agg_note_metadata = - NoteMetadata::new(user_account.id(), note_type, tag, note_execution_hint, aux)?; + let b2agg_note_metadata = NoteMetadata::new(user_account.id(), note_type, tag); let b2agg_note_assets = NoteAssets::new(vec![bridge_asset])?; let serial_num = Word::from([1, 2, 3, 4u32]); let b2agg_note_script = NoteScript::new(b2agg_script); From 9d6db9b0632c73545e31cf08edc04f052a34bbbf Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 16 Jan 2026 08:33:42 +0100 Subject: [PATCH 104/114] feat: add `NoteAttachment` docs (#2279) * feat: Add attachment section to note docs * chore: document advice map entry requirement for array attachments * chore: add set_attachment to protocol library docs * chore: add changelog * feat: make attachment array elements available for input notes * chore: improve readability * fix: remove attachment_kind param from `set_array_attachment` --- CHANGELOG.md | 2 +- .../asm/protocol/output_note.masm | 19 ++++++++++++---- .../src/transaction/kernel/advice_inputs.rs | 13 ++++++++++- docs/src/note.md | 22 +++++++++++++++++-- docs/src/protocol_library.md | 5 ++++- 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e27a4f5e54..c7a6acccad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260)). -- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260), [#2268](https://github.com/0xMiden/miden-base/pull/2268)). +- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260), [#2268](https://github.com/0xMiden/miden-base/pull/2268), [#2279](https://github.com/0xMiden/miden-base/pull/2279)). - Introduce standard `NetworkAccountTarget` attachment for use in network transactions which replaces `NoteTag::NetworkAccount` ([#2257](https://github.com/0xMiden/miden-base/pull/2257)). ### Changes diff --git a/crates/miden-protocol/asm/protocol/output_note.masm b/crates/miden-protocol/asm/protocol/output_note.masm index 45a9f13479..d8898bea68 100644 --- a/crates/miden-protocol/asm/protocol/output_note.masm +++ b/crates/miden-protocol/asm/protocol/output_note.masm @@ -150,7 +150,13 @@ end #! Sets the attachment of the note specified by the index. #! -#! Inputs: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] +#! If attachment_kind == Array, there must be an advice map entry for ATTACHMENT (see below). +#! +#! Inputs: +#! Operand Stack: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] +#! Advice map: { +#! ATTACHMENT?: [[ATTACHMENT_ELEMENTS]], +#! } #! Outputs: [] #! #! Where: @@ -158,6 +164,8 @@ end #! - attachment_scheme is the user-defined scheme of the attachment. #! - attachment_kind is the kind of the attachment content. #! - ATTACHMENT is the attachment to be set. +#! - ATTACHMENT_ELEMENTS are the elements for which ATTACHMENT is the sequential commitment (only +#! needed if attachment_kind == Array). #! #! Panics if: #! - the procedure is called when the active account is not the native one. @@ -182,8 +190,6 @@ pub proc set_attachment # => [] end - - #! Sets the attachment of the note specified by the note index to the provided word. #! #! This overwrites any previously set attachment. @@ -215,13 +221,18 @@ end #! #! This overwrites any previously set attachment. #! -#! Inputs: [note_idx, attachment_scheme, ATTACHMENT] +#! Inputs: +#! Operand Stack: [note_idx, attachment_scheme, ATTACHMENT] +#! Advice map: { +#! ATTACHMENT: [[ATTACHMENT_ELEMENTS]], +#! } #! Outputs: [] #! #! Where: #! - note_idx is the index of the note on which the attachment is set. #! - attachment_scheme is the user-defined scheme of the attachment. #! - ATTACHMENT is the commitment of the set of elements that form the note attachment. +#! - ATTACHMENT_ELEMENTS are the elements for which ATTACHMENT is the sequential commitment. #! #! Panics if: #! - the procedure is called when the active account is not the native one. diff --git a/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs index 067626a143..aec2c04229 100644 --- a/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs +++ b/crates/miden-protocol/src/transaction/kernel/advice_inputs.rs @@ -8,6 +8,7 @@ use crate::block::account_tree::AccountWitness; use crate::crypto::SequentialCommit; use crate::crypto::merkle::InnerNodeInfo; use crate::crypto::merkle::smt::SmtProof; +use crate::note::NoteAttachmentContent; use crate::transaction::{ AccountInputs, InputNote, @@ -349,9 +350,19 @@ impl TransactionAdviceInputs { let recipient = note.recipient(); let note_arg = tx_inputs.tx_args().get_note_args(note.id()).unwrap_or(&EMPTY_WORD); - // recipient inputs / assets commitments + // recipient inputs self.add_map_entry(recipient.inputs().commitment(), recipient.inputs().to_elements()); + // assets commitments self.add_map_entry(assets.commitment(), assets.to_padded_assets()); + // array attachments + if let NoteAttachmentContent::Array(array_attachment) = + note.metadata().attachment().content() + { + self.add_map_entry( + array_attachment.commitment(), + array_attachment.as_slice().to_vec(), + ); + } // note details / metadata note_data.extend(recipient.serial_num()); diff --git a/docs/src/note.md b/docs/src/note.md index 916b5dac03..b9ee08d714 100644 --- a/docs/src/note.md +++ b/docs/src/note.md @@ -61,10 +61,28 @@ The serial number has two main purposes. Firstly by adding some randomness to th ### Metadata :::note -Additional `Note` information. +Additional public `Note` information. ::: -Notes include metadata such as the sender’s account ID and a [tag](#note-discovery) that aids in discovery. Regardless of [storage mode](#note-storage-mode), these metadata fields remain public. +Every note includes metadata: +- the account ID of the sender, i.e. the creator of the note. +- its note type, i.e. private or public. +- the [note tag](#note-discovery) that aids in discovery of the note. +- an optional [note attachment](#attachment). + +Regardless of [storage mode](#note-storage-mode), these metadata fields are always public. + +### Attachment + +An attachment is a variable-size part of a note's metadata: +- It can either be absent (`None`), store a single `Word` or an `Array` of field elements. These are the three _kinds_ of attachments. +- The _scheme_ of an attachment is an optional, 32-bit user-defined value that can be used to detect the presence of certain standardized attachments. + +Example use cases for attachments are: +- Communicate the note details of a private note in encrypted form. This means the encrypted note is attached publicly to the otherwise private note. +- For [network transactions](./transaction.md#network-transaction), encode the ID of the network account that should + consume the note. This is a standardized attachment scheme in miden-standards called `NetworkAccountTarget`. +- Communicate the details of a _private_ note to the receiver so they can derive the note. For example, the payback note of a partially fillable swap note can be private and the receiver already knows a few details: It is a P2ID note, the serial number is derived from the SWAP note's serial number and the note inputs are the account ID of the receiver. The receiver only needs to now the exact amount that was filled to derive the full note for consumption. This amount can be encoded in the public attachment of the payback note, which allows this use case to work with private notes and still not require a side-channel. ## Note Lifecycle diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index 01bc92dee6..d9cef7f02e 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -103,10 +103,13 @@ Output note procedures can be used to fetch data on output notes created by the | Procedure | Description | Context | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | -| `create` | Creates a new output note and returns its index.

**Inputs:** `[tag, aux, note_type, execution_hint, RECIPIENT]`
**Outputs:** `[note_idx]` | Native & Account | +| `create` | Creates a new output note and returns its index.

**Inputs:** `[tag, note_type, RECIPIENT]`
**Outputs:** `[note_idx]` | Native & Account | | `get_assets_info` | Returns the information about assets in the output note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[ASSETS_COMMITMENT, num_assets]` | Any | | `get_assets` | Writes the assets of the output note with the specified index into memory starting at the specified address.

**Inputs:** `[dest_ptr, note_index]`
**Outputs:** `[num_assets, dest_ptr, note_index]` | Any | | `add_asset` | Adds the `ASSET` to the output note specified by the index.

**Inputs:** `[ASSET, note_idx]`
**Outputs:** `[]` | Native | +| `set_attachment` | Sets the attachment of the note specified by the index.

If attachment_kind == Array, there must be an advice map entry for ATTACHMENT.

**Inputs:**
`Operand Stack: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT]`
`Advice map: { ATTACHMENT?: [[ATTACHMENT_ELEMENTS]] }`
**Outputs:** `[]` | Native | +| `set_array_attachment` | Sets the attachment of the note specified by the note index to the provided ATTACHMENT which commits to an array of felts.

**Inputs:**
`Operand Stack: [note_idx, attachment_scheme, ATTACHMENT]`
`Advice map: { ATTACHMENT: [[ATTACHMENT_ELEMENTS]] }`
**Outputs:** `[]` | Native | +| `set_word_attachment` | Sets the attachment of the note specified by the note index to the provided word.

**Inputs:** `[note_idx, attachment_scheme, ATTACHMENT]`
**Outputs:** `[]` | | `get_recipient` | Returns the [recipient](note#note-recipient-restricting-consumption) of the output note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[RECIPIENT]` | Any | | `get_metadata` | Returns the [metadata](note#metadata) of the output note with the specified index.

**Inputs:** `[note_index]`
**Outputs:** `[METADATA]` | Any | From 2cbfeb10be787c949fda9658179a8be0cfbdc1a9 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Fri, 16 Jan 2026 03:13:41 -0500 Subject: [PATCH 105/114] Solidity `address` <> Miden `AccountId` helper functions (#2238) * feat: add Solidity<>Miden address type conversion functions * fix: formatting * refactor: rm unnecessary indirection * refactor: use crypto util functions * refactor: implement suggestions & refactor * refactor: update logic & comments to little endian * Update crates/miden-agglayer/src/utils.rs Co-authored-by: igamigo * refactor: improve EthAddress representation clarity and MASM alignment * refactor: simplify ethereum_address_to_account_id proc * fix: clippy * fix: lint doc check * refactor: use u32assert2 * refactor: simplify from_account_id() & u32 check * revert: undo drop addr4 in ethereum_address_to_account_id * Update crates/miden-agglayer/src/eth_address.rs Co-authored-by: Marti * refactor: rename to EthAddressFormat * refactor: rearrange EthAddressFormat * refactor: rename file to eth_address_format * fix: update script roots * refactor: update test & refactor * refactor: refactor .to_elements() method * refactor: rename to to_account_id * fix: lint check * refactor: rename to eth_address.rs & undo Felt::try_from() changes --------- Co-authored-by: igamigo Co-authored-by: Marti Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> --- Cargo.lock | 2 + .../miden-agglayer/asm/bridge/bridge_out.masm | 1 - .../asm/bridge/crypto_utils.masm | 1 - .../asm/bridge/eth_address.masm | 88 ++++++ crates/miden-agglayer/src/errors/agglayer.rs | 9 + crates/miden-agglayer/src/eth_address.rs | 243 ++++++++++++++++ crates/miden-agglayer/src/lib.rs | 94 +------ crates/miden-agglayer/src/utils.rs | 263 ++---------------- crates/miden-testing/Cargo.toml | 2 + .../tests/agglayer/bridge_out.rs | 13 +- crates/miden-testing/tests/agglayer/mod.rs | 1 + .../solidity_miden_address_conversion.rs | 174 ++++++++++++ 12 files changed, 549 insertions(+), 342 deletions(-) create mode 100644 crates/miden-agglayer/asm/bridge/eth_address.masm create mode 100644 crates/miden-agglayer/src/eth_address.rs create mode 100644 crates/miden-testing/tests/agglayer/solidity_miden_address_conversion.rs diff --git a/Cargo.lock b/Cargo.lock index 0ab6b7af3c..1ec7360d0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1753,11 +1753,13 @@ version = "0.13.0" dependencies = [ "anyhow", "assert_matches", + "hex", "itertools 0.14.0", "miden-agglayer", "miden-assembly", "miden-block-prover", "miden-core-lib", + "miden-crypto", "miden-processor", "miden-protocol", "miden-standards", diff --git a/crates/miden-agglayer/asm/bridge/bridge_out.masm b/crates/miden-agglayer/asm/bridge/bridge_out.masm index 43c6885b35..023677e511 100644 --- a/crates/miden-agglayer/asm/bridge/bridge_out.masm +++ b/crates/miden-agglayer/asm/bridge/bridge_out.masm @@ -12,7 +12,6 @@ const LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let") const BURN_NOTE_ROOT = [15615638671708113717, 1774623749760042586, 2028263167268363492, 12931944505143778072] const PUBLIC_NOTE=1 -const AUX=0 const NUM_BURN_NOTE_INPUTS=0 const BURN_ASSET_MEM_PTR=24 diff --git a/crates/miden-agglayer/asm/bridge/crypto_utils.masm b/crates/miden-agglayer/asm/bridge/crypto_utils.masm index f0020785d1..7796c1f94f 100644 --- a/crates/miden-agglayer/asm/bridge/crypto_utils.masm +++ b/crates/miden-agglayer/asm/bridge/crypto_utils.masm @@ -80,4 +80,3 @@ pub proc verify_claim_proof dropw dropw dropw dropw push.1 end - diff --git a/crates/miden-agglayer/asm/bridge/eth_address.masm b/crates/miden-agglayer/asm/bridge/eth_address.masm new file mode 100644 index 0000000000..57a8e9f298 --- /dev/null +++ b/crates/miden-agglayer/asm/bridge/eth_address.masm @@ -0,0 +1,88 @@ +use miden::core::crypto::hashes::keccak256 +use miden::core::word + +# CONSTANTS +# ================================================================================================= + +const U32_MAX=4294967295 +const TWO_POW_32=4294967296 + +const ERR_NOT_U32="address limb is not u32" +const ERR_ADDR4_NONZERO="most-significant 4 bytes (addr4) must be zero" +const ERR_FELT_OUT_OF_FIELD="combined u64 doesn't fit in field" + + +# ETHEREUM ADDRESS PROCEDURES +# ================================================================================================= + +#! Builds a single felt from two u32 limbs (little-endian limb order). +#! Conceptually, this is packing a 64-bit word (lo + (hi << 32)) into a field element. +#! This proc additionally verifies that the packed value did *not* reduce mod p by round-tripping +#! through u32split and comparing the limbs. +#! +#! Inputs: [lo, hi] +#! Outputs: [felt] +proc build_felt + # --- validate u32 limbs --- + u32assert2.err=ERR_NOT_U32 + # => [lo, hi] + + # keep copies for the overflow check + dup.1 dup.1 + # => [lo, hi, lo, hi] + + # felt = (hi * 2^32) + lo + swap + push.TWO_POW_32 mul + add + # => [felt, lo, hi] + + # ensure no reduction mod p happened: + # split felt back into (hi, lo) and compare to inputs + dup u32split + # => [hi2, lo2, felt, lo, hi] + + movup.4 assert_eq.err=ERR_FELT_OUT_OF_FIELD + # => [lo2, felt, lo] + + movup.2 assert_eq.err=ERR_FELT_OUT_OF_FIELD + # => [felt] +end + +#! Converts an Ethereum address format (address[5] type) back into an AccountId [prefix, suffix] type. +#! +#! The Ethereum address format is represented as 5 u32 limbs (20 bytes total) in *little-endian limb order*: +#! addr0 = bytes[16..19] (least-significant 4 bytes) +#! addr1 = bytes[12..15] +#! addr2 = bytes[ 8..11] +#! addr3 = bytes[ 4.. 7] +#! addr4 = bytes[ 0.. 3] (most-significant 4 bytes) +#! +#! The most-significant 4 bytes must be zero for a valid AccountId conversion (addr4 == 0). +#! The remaining 16 bytes are treated as two 8-byte words (conceptual u64 values): +#! prefix = (addr3 << 32) | addr2 # bytes[4..11] +#! suffix = (addr1 << 32) | addr0 # bytes[12..19] +#! +#! These 8-byte words are represented as field elements by packing two u32 limbs into a felt. +#! The packing is done via build_felt, which validates limbs are u32 and checks the packed value +#! did not reduce mod p (i.e. the word fits in the field). +#! +#! Inputs: [addr0, addr1, addr2, addr3, addr4] +#! Outputs: [prefix, suffix] +#! +#! Invocation: exec +pub proc to_account_id + # addr4 must be 0 (most-significant limb) + movup.4 + eq.0 assert.err=ERR_ADDR4_NONZERO + # => [addr0, addr1, addr2, addr3] + + exec.build_felt + # => [suffix, addr2, addr3] + + movdn.2 + # => [addr2, addr3, suffix] + + exec.build_felt + # => [prefix, suffix] +end diff --git a/crates/miden-agglayer/src/errors/agglayer.rs b/crates/miden-agglayer/src/errors/agglayer.rs index 8f70572329..efa9275dee 100644 --- a/crates/miden-agglayer/src/errors/agglayer.rs +++ b/crates/miden-agglayer/src/errors/agglayer.rs @@ -9,6 +9,9 @@ use miden_protocol::errors::MasmError; // AGGLAYER ERRORS // ================================================================================================ +/// Error Message: "most-significant 4 bytes (addr4) must be zero" +pub const ERR_ADDR4_NONZERO: MasmError = MasmError::from_static_str("most-significant 4 bytes (addr4) must be zero"); + /// Error Message: "B2AGG script requires exactly 1 note asset" pub const ERR_B2AGG_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("B2AGG script requires exactly 1 note asset"); /// Error Message: "B2AGG script expects exactly 6 note inputs" @@ -17,8 +20,14 @@ pub const ERR_B2AGG_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_s /// Error Message: "CLAIM's target account address and transaction address do not match" pub const ERR_CLAIM_TARGET_ACCT_MISMATCH: MasmError = MasmError::from_static_str("CLAIM's target account address and transaction address do not match"); +/// Error Message: "combined u64 doesn't fit in field" +pub const ERR_FELT_OUT_OF_FIELD: MasmError = MasmError::from_static_str("combined u64 doesn't fit in field"); + /// Error Message: "invalid claim proof" pub const ERR_INVALID_CLAIM_PROOF: MasmError = MasmError::from_static_str("invalid claim proof"); +/// Error Message: "address limb is not u32" +pub const ERR_NOT_U32: MasmError = MasmError::from_static_str("address limb is not u32"); + /// Error Message: "maximum scaling factor is 18" pub const ERR_SCALE_AMOUNT_EXCEEDED_LIMIT: MasmError = MasmError::from_static_str("maximum scaling factor is 18"); diff --git a/crates/miden-agglayer/src/eth_address.rs b/crates/miden-agglayer/src/eth_address.rs new file mode 100644 index 0000000000..f2a94ed6df --- /dev/null +++ b/crates/miden-agglayer/src/eth_address.rs @@ -0,0 +1,243 @@ +use alloc::format; +use alloc::string::{String, ToString}; +use core::fmt; + +use miden_core::FieldElement; +use miden_protocol::Felt; +use miden_protocol::account::AccountId; +use miden_protocol::utils::{HexParseError, bytes_to_hex_string, hex_to_bytes}; + +// ================================================================================================ +// ETHEREUM ADDRESS +// ================================================================================================ + +/// Represents an Ethereum address format (20 bytes). +/// +/// # Representations used in this module +/// +/// - Raw bytes: `[u8; 20]` in the conventional Ethereum big-endian byte order (`bytes[0]` is the +/// most-significant byte). +/// - MASM "address\[5\]" limbs: 5 x u32 limbs in *little-endian limb order*: +/// - addr0 = bytes[16..19] (least-significant 4 bytes) +/// - addr1 = bytes[12..15] +/// - addr2 = bytes[ 8..11] +/// - addr3 = bytes[ 4.. 7] +/// - addr4 = bytes[ 0.. 3] (most-significant 4 bytes) +/// - Embedded AccountId format: `0x00000000 || prefix(8) || suffix(8)`, where: +/// - prefix = (addr3 << 32) | addr2 = bytes[4..11] as a big-endian u64 +/// - suffix = (addr1 << 32) | addr0 = bytes[12..19] as a big-endian u64 +/// +/// Note: prefix/suffix are *conceptual* 64-bit words; when converting to [`Felt`], we must ensure +/// `Felt::new(u64)` does not reduce mod p (checked explicitly in `to_account_id`). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct EthAddressFormat([u8; 20]); + +impl EthAddressFormat { + // EXTERNAL API - For integrators (Gateway, claim managers, etc.) + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`EthAddressFormat`] from a 20-byte array. + pub const fn new(bytes: [u8; 20]) -> Self { + Self(bytes) + } + + /// Creates an [`EthAddressFormat`] from a hex string (with or without "0x" prefix). + /// + /// # Errors + /// + /// Returns an error if the hex string is invalid or the hex part is not exactly 40 characters. + pub fn from_hex(hex_str: &str) -> Result { + let hex_part = hex_str.strip_prefix("0x").unwrap_or(hex_str); + if hex_part.len() != 40 { + return Err(AddressConversionError::InvalidHexLength); + } + + let prefixed_hex = if hex_str.starts_with("0x") { + hex_str.to_string() + } else { + format!("0x{}", hex_str) + }; + + let bytes: [u8; 20] = hex_to_bytes(&prefixed_hex)?; + Ok(Self(bytes)) + } + + /// Creates an [`EthAddressFormat`] from an [`AccountId`]. + /// + /// **External API**: This function is used by integrators (Gateway, claim managers) to convert + /// Miden AccountIds into the Ethereum address format for constructing CLAIM notes or + /// interfacing when calling the Agglayer Bridge function bridgeAsset(). + /// + /// This conversion is infallible: an [`AccountId`] is two felts, and `as_int()` yields `u64` + /// words which we embed as `0x00000000 || prefix(8) || suffix(8)` (big-endian words). + /// + /// # Example + /// ```ignore + /// let destination_address = EthAddressFormat::from_account_id(destination_account_id).into_bytes(); + /// // then construct the CLAIM note with destination_address... + /// ``` + pub fn from_account_id(account_id: AccountId) -> Self { + let felts: [Felt; 2] = account_id.into(); + + let mut out = [0u8; 20]; + out[4..12].copy_from_slice(&felts[0].as_int().to_be_bytes()); + out[12..20].copy_from_slice(&felts[1].as_int().to_be_bytes()); + + Self(out) + } + + /// Returns the raw 20-byte array. + pub const fn as_bytes(&self) -> &[u8; 20] { + &self.0 + } + + /// Converts the address into a 20-byte array. + pub const fn into_bytes(self) -> [u8; 20] { + self.0 + } + + /// Converts the Ethereum address to a hex string (lowercase, 0x-prefixed). + pub fn to_hex(&self) -> String { + bytes_to_hex_string(self.0) + } + + // INTERNAL API - For CLAIM note processing + // -------------------------------------------------------------------------------------------- + + /// Converts the Ethereum address format into an array of 5 [`Felt`] values for MASM processing. + /// + /// **Internal API**: This function is used internally during CLAIM note processing to convert + /// the address format into the MASM `address[5]` representation expected by the + /// `to_account_id` procedure. + /// + /// The returned order matches the MASM `address\[5\]` convention (*little-endian limb order*): + /// - addr0 = bytes[16..19] (least-significant 4 bytes) + /// - addr1 = bytes[12..15] + /// - addr2 = bytes[ 8..11] + /// - addr3 = bytes[ 4.. 7] + /// - addr4 = bytes[ 0.. 3] (most-significant 4 bytes) + /// + /// Each limb is interpreted as a big-endian `u32` and stored in a [`Felt`]. + pub fn to_elements(&self) -> [Felt; 5] { + let mut result = [Felt::ZERO; 5]; + + // i=0 -> bytes[16..20], i=4 -> bytes[0..4] + for (felt, chunk) in result.iter_mut().zip(self.0.chunks(4).skip(1).rev()) { + let value = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + // u32 values always fit in Felt, so this conversion is safe + *felt = Felt::try_from(value as u64).expect("u32 value should always fit in Felt"); + } + + result + } + + /// Converts the Ethereum address format back to an [`AccountId`]. + /// + /// **Internal API**: This function is used internally during CLAIM note processing to extract + /// the original AccountId from the Ethereum address format. It mirrors the functionality of + /// the MASM `to_account_id` procedure. + /// + /// # Errors + /// + /// Returns an error if: + /// - the first 4 bytes are not zero (not in the embedded AccountId format), + /// - packing the 8-byte prefix/suffix into [`Felt`] would reduce mod p, + /// - or the resulting felts do not form a valid [`AccountId`]. + pub fn to_account_id(&self) -> Result { + let (prefix, suffix) = Self::bytes20_to_prefix_suffix(self.0)?; + + // Use `Felt::try_from(u64)` to avoid potential truncating conversion + let prefix_felt = + Felt::try_from(prefix).map_err(|_| AddressConversionError::FeltOutOfField)?; + + let suffix_felt = + Felt::try_from(suffix).map_err(|_| AddressConversionError::FeltOutOfField)?; + + AccountId::try_from([prefix_felt, suffix_felt]) + .map_err(|_| AddressConversionError::InvalidAccountId) + } + + // HELPER FUNCTIONS + // -------------------------------------------------------------------------------------------- + + /// Convert `[u8; 20]` -> `(prefix, suffix)` by extracting the last 16 bytes. + /// Requires the first 4 bytes be zero. + /// Returns prefix and suffix values that match the MASM little-endian limb implementation: + /// - prefix = bytes[4..12] as big-endian u64 = (addr3 << 32) | addr2 + /// - suffix = bytes[12..20] as big-endian u64 = (addr1 << 32) | addr0 + fn bytes20_to_prefix_suffix(bytes: [u8; 20]) -> Result<(u64, u64), AddressConversionError> { + if bytes[0..4] != [0, 0, 0, 0] { + return Err(AddressConversionError::NonZeroBytePrefix); + } + + let prefix = u64::from_be_bytes(bytes[4..12].try_into().unwrap()); + let suffix = u64::from_be_bytes(bytes[12..20].try_into().unwrap()); + + Ok((prefix, suffix)) + } +} + +impl fmt::Display for EthAddressFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.to_hex()) + } +} + +impl From<[u8; 20]> for EthAddressFormat { + fn from(bytes: [u8; 20]) -> Self { + Self(bytes) + } +} + +impl From for EthAddressFormat { + fn from(account_id: AccountId) -> Self { + EthAddressFormat::from_account_id(account_id) + } +} + +impl From for [u8; 20] { + fn from(addr: EthAddressFormat) -> Self { + addr.0 + } +} + +// ================================================================================================ +// ADDRESS CONVERSION ERROR +// ================================================================================================ + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AddressConversionError { + NonZeroWordPadding, + NonZeroBytePrefix, + InvalidHexLength, + InvalidHexChar(char), + HexParseError, + FeltOutOfField, + InvalidAccountId, +} + +impl fmt::Display for AddressConversionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AddressConversionError::NonZeroWordPadding => write!(f, "non-zero word padding"), + AddressConversionError::NonZeroBytePrefix => { + write!(f, "address has non-zero 4-byte prefix") + }, + AddressConversionError::InvalidHexLength => { + write!(f, "invalid hex length (expected 40 hex chars)") + }, + AddressConversionError::InvalidHexChar(c) => write!(f, "invalid hex character: {}", c), + AddressConversionError::HexParseError => write!(f, "hex parse error"), + AddressConversionError::FeltOutOfField => { + write!(f, "packed 64-bit word does not fit in the field") + }, + AddressConversionError::InvalidAccountId => write!(f, "invalid AccountId"), + } + } +} + +impl From for AddressConversionError { + fn from(_err: HexParseError) -> Self { + AddressConversionError::HexParseError + } +} diff --git a/crates/miden-agglayer/src/lib.rs b/crates/miden-agglayer/src/lib.rs index 3753f6e663..72d50ab7b8 100644 --- a/crates/miden-agglayer/src/lib.rs +++ b/crates/miden-agglayer/src/lib.rs @@ -36,9 +36,11 @@ use miden_standards::account::faucets::NetworkFungibleFaucet; use miden_utils_sync::LazyLock; pub mod errors; +pub mod eth_address; pub mod utils; -use utils::{bytes32_to_felts, ethereum_address_to_felts}; +pub use eth_address::EthAddressFormat; +use utils::bytes32_to_felts; // AGGLAYER NOTE SCRIPTS // ================================================================================================ @@ -422,7 +424,8 @@ pub fn create_claim_note(params: ClaimNoteParams<'_, R>) -> Result(params: ClaimNoteParams<'_, R>) -> Result [u8; 20] { - let mut address = [0u8; 20]; - - // Convert prefix and suffix to u64, then to bytes (big-endian) - let prefix_u64 = account_id.prefix().as_felt().as_int(); - let suffix_u64 = account_id.suffix().as_int(); - - let prefix_bytes = prefix_u64.to_be_bytes(); - let suffix_bytes = suffix_u64.to_be_bytes(); - - // Copy last 4 bytes from prefix (u32 portion) - address[0..4].copy_from_slice(&prefix_bytes[4..8]); - // Copy last 4 bytes from suffix (u32 portion) - address[4..8].copy_from_slice(&suffix_bytes[4..8]); - // Remaining 12 bytes stay as zeros - - address -} - -/// Converts a bytes20 EVM address into an AccountId. -/// -/// The conversion extracts the first 8 bytes as prefix (u32) and suffix (u32), -/// with the remaining bytes ignored (treated as zeros). -pub fn evm_address_to_account_id(address: &[u8; 20]) -> AccountId { - // Extract first 8 bytes and convert to u32 values - let mut prefix_bytes = [0u8; 8]; - let mut suffix_bytes = [0u8; 8]; - - // Copy first 4 bytes to prefix (pad with zeros) - prefix_bytes[4..8].copy_from_slice(&address[0..4]); - // Copy next 4 bytes to suffix (pad with zeros) - suffix_bytes[4..8].copy_from_slice(&address[4..8]); - - let prefix = u64::from_be_bytes(prefix_bytes); - let suffix = u64::from_be_bytes(suffix_bytes); - - // Create AccountId from the extracted values - // Note: This creates a basic account ID - in practice you might want to use - // a specific account type and storage mode - AccountId::new_unchecked([Felt::new(prefix), Felt::new(suffix)]) -} - -/// Converts an AccountId to a bytes20 address that will produce [prefix, suffix, 0, 0, 0] -/// when processed by ethereum_address_to_felts(). -/// -/// This function creates a 20-byte address where: -/// - Bytes 0-3: AccountId prefix as u32 (big-endian) -/// - Bytes 4-7: AccountId suffix as u32 (big-endian) -/// - Bytes 8-19: Zero padding -/// -/// When ethereum_address_to_felts() processes this address, it will extract: -/// - u32\[0\] from bytes 0-3: prefix -/// - u32\[1\] from bytes 4-7: suffix -/// - u32\[2\] from bytes 8-11: zeros -/// - u32\[3\] from bytes 12-15: zeros -/// - u32\[4\] from bytes 16-19: zeros -/// -/// This results in [prefix, suffix, 0, 0, 0] as desired. -pub fn account_id_to_destination_bytes(account_id: AccountId) -> [u8; 20] { - let mut address = [0u8; 20]; - - // Get prefix and suffix as u64 values, then convert to u32 - let prefix = account_id.prefix().as_felt().as_int() as u32; - let suffix = account_id.suffix().as_int() as u32; - - // Pack prefix into first 4 bytes (u32, big-endian) - address[0..4].copy_from_slice(&prefix.to_be_bytes()); - - // Pack suffix into next 4 bytes (u32, big-endian) - address[4..8].copy_from_slice(&suffix.to_be_bytes()); - - // Remaining 12 bytes stay as zeros - // This will result in [prefix, suffix, 0, 0, 0] when processed by ethereum_address_to_felts() - - address -} - // TESTING HELPERS // ================================================================================================ @@ -658,7 +577,8 @@ pub fn claim_note_test_inputs( let destination_network = Felt::new(2); // Convert AccountId to destination address bytes - let destination_address = account_id_to_destination_bytes(destination_account_id); + let destination_address = + EthAddressFormat::from_account_id(destination_account_id).into_bytes(); // Convert amount Felt to u256 array for agglayer let amount_u256 = [ diff --git a/crates/miden-agglayer/src/utils.rs b/crates/miden-agglayer/src/utils.rs index 3b8cd683c2..88850de58c 100644 --- a/crates/miden-agglayer/src/utils.rs +++ b/crates/miden-agglayer/src/utils.rs @@ -1,259 +1,28 @@ -use alloc::string::String; -use alloc::vec::Vec; - +use miden_core::FieldElement; use miden_protocol::Felt; -/// Convert 8 Felt values (u32 limbs in little-endian order) to U256 bytes in little-endian format. +// UTILITY FUNCTIONS +// ================================================================================================ + +/// Converts a bytes32 value (32 bytes) into an array of 8 Felt values. /// -/// The input limbs are expected to be in little-endian order (least significant limb first). -/// This function converts them to a 32-byte array in little-endian format for compatibility -/// with Ethereum/EVM which expects U256 values as 32 bytes in little-endian format. -/// This ensures compatibility when bridging assets between Miden and Ethereum-based chains. +/// Note: These utility functions will eventually be replaced with similar functions from miden-vm. +pub fn bytes32_to_felts(bytes32: &[u8; 32]) -> [Felt; 8] { + let mut result = [Felt::ZERO; 8]; + for (i, chunk) in bytes32.chunks(4).enumerate() { + let value = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + result[i] = Felt::from(value); + } + result +} + +/// Convert 8 Felt values (u32 limbs in little-endian order) to U256 bytes in little-endian format. pub fn felts_to_u256_bytes(limbs: [Felt; 8]) -> [u8; 32] { let mut bytes = [0u8; 32]; - for (i, limb) in limbs.iter().enumerate() { let u32_value = limb.as_int() as u32; let limb_bytes = u32_value.to_le_bytes(); bytes[i * 4..(i + 1) * 4].copy_from_slice(&limb_bytes); } - bytes } - -/// Converts an Ethereum address (20 bytes) into a vector of 5 Felt values. -/// -/// An Ethereum address is 20 bytes, which we split into 5 u32 values (4 bytes each). -/// The address bytes are distributed as follows: -/// - u32\[0\]: bytes 0-3 -/// - u32\[1\]: bytes 4-7 -/// - u32\[2\]: bytes 8-11 -/// - u32\[3\]: bytes 12-15 -/// - u32\[4\]: bytes 16-19 -/// -/// # Arguments -/// * `address` - A 20-byte Ethereum address -/// -/// # Returns -/// A vector of 5 Felt values representing the address -/// -/// # Panics -/// Panics if the address is not exactly 20 bytes -pub fn ethereum_address_to_felts(address: &[u8; 20]) -> Vec { - let mut result = Vec::with_capacity(5); - - // Convert each 4-byte chunk to a u32 (big-endian) - for i in 0..5 { - let start = i * 4; - let chunk = &address[start..start + 4]; - let value = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); - result.push(Felt::new(value as u64)); - } - - result -} - -/// Converts a vector of 5 Felt values back into a 20-byte Ethereum address. -/// -/// # Arguments -/// * `felts` - A vector of 5 Felt values representing an Ethereum address -/// -/// # Returns -/// A Result containing a 20-byte Ethereum address array, or an error string -/// -/// # Errors -/// Returns an error if the vector doesn't contain exactly 5 felts -pub fn felts_to_ethereum_address(felts: &[Felt]) -> Result<[u8; 20], String> { - if felts.len() != 5 { - return Err(alloc::format!("Expected 5 felts for Ethereum address, got {}", felts.len())); - } - - let mut address = [0u8; 20]; - - for (i, felt) in felts.iter().enumerate() { - let value = felt.as_int() as u32; - let bytes = value.to_be_bytes(); - let start = i * 4; - address[start..start + 4].copy_from_slice(&bytes); - } - - Ok(address) -} - -/// Converts an Ethereum address string (with or without "0x" prefix) into a vector of 5 Felt -/// values. -/// -/// # Arguments -/// * `address_str` - A hex string representing an Ethereum address (40 hex chars, optionally -/// prefixed with "0x") -/// -/// # Returns -/// A Result containing a vector of 5 Felt values representing the address, or an error string -/// -/// # Errors -/// Returns an error if: -/// - The string is not a valid hex string -/// - The string does not represent exactly 20 bytes (40 hex characters) -pub fn ethereum_address_string_to_felts(address_str: &str) -> Result, String> { - // Remove "0x" prefix if present - let hex_str = address_str.strip_prefix("0x").unwrap_or(address_str); - - // Check length (should be 40 hex characters for 20 bytes) - if hex_str.len() != 40 { - return Err(alloc::format!( - "Invalid Ethereum address length: expected 40 hex characters, got {}", - hex_str.len() - )); - } - - // Parse hex string to bytes - let mut address_bytes = [0u8; 20]; - for (i, chunk) in hex_str.as_bytes().chunks(2).enumerate() { - let hex_byte = core::str::from_utf8(chunk) - .map_err(|_| String::from("Invalid UTF-8 in address string"))?; - address_bytes[i] = u8::from_str_radix(hex_byte, 16) - .map_err(|_| alloc::format!("Invalid hex character in address: {}", hex_byte))?; - } - - Ok(ethereum_address_to_felts(&address_bytes)) -} - -/// Converts a bytes32 value (32 bytes) into a vector of 8 Felt values. -/// -/// A bytes32 value is 32 bytes, which we split into 8 u32 values (4 bytes each). -/// The bytes are distributed as follows: -/// - u32\[0\]: bytes 0-3 -/// - u32\[1\]: bytes 4-7 -/// - u32\[2\]: bytes 8-11 -/// - u32\[3\]: bytes 12-15 -/// - u32\[4\]: bytes 16-19 -/// - u32\[5\]: bytes 20-23 -/// - u32\[6\]: bytes 24-27 -/// - u32\[7\]: bytes 28-31 -/// -/// # Arguments -/// * `bytes32` - A 32-byte value (e.g., hash, root) -/// -/// # Returns -/// A vector of 8 Felt values representing the bytes32 value -/// -/// # Panics -/// Panics if the input is not exactly 32 bytes -pub fn bytes32_to_felts(bytes32: &[u8; 32]) -> Vec { - let mut result = Vec::with_capacity(8); - - // Convert each 4-byte chunk to a u32 (big-endian) - for i in 0..8 { - let start = i * 4; - let chunk = &bytes32[start..start + 4]; - let value = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); - result.push(Felt::new(value as u64)); - } - - result -} - -/// Converts a vector of 8 Felt values back into a 32-byte array. -/// -/// # Arguments -/// * `felts` - A vector of 8 Felt values representing a bytes32 value -/// -/// # Returns -/// A Result containing a 32-byte array, or an error string -/// -/// # Errors -/// Returns an error if the vector doesn't contain exactly 8 felts -pub fn felts_to_bytes32(felts: &[Felt]) -> Result<[u8; 32], String> { - if felts.len() != 8 { - return Err(alloc::format!("Expected 8 felts for bytes32, got {}", felts.len())); - } - - let mut bytes32 = [0u8; 32]; - - for (i, felt) in felts.iter().enumerate() { - let value = felt.as_int() as u32; - let bytes = value.to_be_bytes(); - let start = i * 4; - bytes32[start..start + 4].copy_from_slice(&bytes); - } - - Ok(bytes32) -} - -#[cfg(test)] -mod tests { - use alloc::vec; - - use super::*; - - #[test] - fn test_ethereum_address_round_trip() { - // Test that converting from string to felts and back gives the same result - let original_address = "0x1234567890abcdef1122334455667788990011aa"; - - // Convert string to felts - let felts = ethereum_address_string_to_felts(original_address).unwrap(); - - // Convert felts back to bytes - let recovered_bytes = felts_to_ethereum_address(&felts).unwrap(); - - // Convert original string to bytes for comparison - let original_hex = original_address.strip_prefix("0x").unwrap(); - let mut expected_bytes = [0u8; 20]; - for (i, chunk) in original_hex.as_bytes().chunks(2).enumerate() { - let hex_byte = core::str::from_utf8(chunk).unwrap(); - expected_bytes[i] = u8::from_str_radix(hex_byte, 16).unwrap(); - } - - // Assert they match - assert_eq!(recovered_bytes, expected_bytes); - } - - #[test] - fn test_ethereum_address_to_felts_basic() { - let address: [u8; 20] = [ - 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, - ]; - - let result = ethereum_address_to_felts(&address); - assert_eq!(result.len(), 5); - assert_eq!(result[0], Felt::new(0x12345678)); - assert_eq!(result[1], Felt::new(0x9abcdef0)); - } - - #[test] - fn test_felts_to_ethereum_address_invalid_length() { - let felts = vec![Felt::new(1), Felt::new(2)]; // Only 2 felts - let result = felts_to_ethereum_address(&felts); - assert!(result.is_err()); - } - - #[test] - fn test_ethereum_address_string_invalid_length() { - let address_str = "0x123456"; // Too short - let result = ethereum_address_string_to_felts(address_str); - assert!(result.is_err()); - } - - #[test] - fn test_bytes32_to_felts_basic() { - let bytes32: [u8; 32] = [ - 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, - 0x55, 0x66, 0x77, 0x88, - ]; - - let result = bytes32_to_felts(&bytes32); - assert_eq!(result.len(), 8); - assert_eq!(result[0], Felt::new(0x12345678)); - assert_eq!(result[1], Felt::new(0x9abcdef0)); - } - - #[test] - fn test_felts_to_bytes32_invalid_length() { - let felts = vec![Felt::new(1), Felt::new(2)]; // Only 2 felts - let result = felts_to_bytes32(&felts); - assert!(result.is_err()); - } -} diff --git a/crates/miden-testing/Cargo.toml b/crates/miden-testing/Cargo.toml index bbcbb06991..af565ab386 100644 --- a/crates/miden-testing/Cargo.toml +++ b/crates/miden-testing/Cargo.toml @@ -39,6 +39,8 @@ winterfell = { version = "0.13" } [dev-dependencies] anyhow = { features = ["backtrace", "std"], workspace = true } assert_matches = { workspace = true } +hex = { version = "0.4" } +miden-crypto = { workspace = true } miden-protocol = { features = ["std"], workspace = true } primitive-types = { workspace = true } rstest = { workspace = true } diff --git a/crates/miden-testing/tests/agglayer/bridge_out.rs b/crates/miden-testing/tests/agglayer/bridge_out.rs index dea33e04d4..ab832bb50a 100644 --- a/crates/miden-testing/tests/agglayer/bridge_out.rs +++ b/crates/miden-testing/tests/agglayer/bridge_out.rs @@ -1,7 +1,6 @@ extern crate alloc; -use miden_agglayer::utils::ethereum_address_string_to_felts; -use miden_agglayer::{b2agg_script, bridge_out_component}; +use miden_agglayer::{EthAddressFormat, b2agg_script, bridge_out_component}; use miden_protocol::account::{ Account, AccountId, @@ -80,8 +79,9 @@ async fn test_bridge_out_consumes_b2agg_note() -> anyhow::Result<()> { // destination_address: 20 bytes (Ethereum address) split into 5 u32 values let destination_network = Felt::new(1); // Example network ID let destination_address = "0x1234567890abcdef1122334455667788990011aa"; - let address_felts = - ethereum_address_string_to_felts(destination_address).expect("Valid Ethereum address"); + let eth_address = + EthAddressFormat::from_hex(destination_address).expect("Valid Ethereum address"); + let address_felts = eth_address.to_elements().to_vec(); // Combine network ID and address felts into note inputs (6 felts total) let mut input_felts = vec![destination_network]; @@ -235,8 +235,9 @@ async fn test_b2agg_note_reclaim_scenario() -> anyhow::Result<()> { // Create note inputs with destination network and address let destination_network = Felt::new(1); let destination_address = "0x1234567890abcdef1122334455667788990011aa"; - let address_felts = - ethereum_address_string_to_felts(destination_address).expect("Valid Ethereum address"); + let eth_address = + EthAddressFormat::from_hex(destination_address).expect("Valid Ethereum address"); + let address_felts = eth_address.to_elements().to_vec(); // Combine network ID and address felts into note inputs (6 felts total) let mut input_felts = vec![destination_network]; diff --git a/crates/miden-testing/tests/agglayer/mod.rs b/crates/miden-testing/tests/agglayer/mod.rs index b238560b86..2a6d344c67 100644 --- a/crates/miden-testing/tests/agglayer/mod.rs +++ b/crates/miden-testing/tests/agglayer/mod.rs @@ -1,3 +1,4 @@ pub mod asset_conversion; mod bridge_in; mod bridge_out; +mod solidity_miden_address_conversion; diff --git a/crates/miden-testing/tests/agglayer/solidity_miden_address_conversion.rs b/crates/miden-testing/tests/agglayer/solidity_miden_address_conversion.rs new file mode 100644 index 0000000000..2083a9dd36 --- /dev/null +++ b/crates/miden-testing/tests/agglayer/solidity_miden_address_conversion.rs @@ -0,0 +1,174 @@ +extern crate alloc; + +use alloc::sync::Arc; + +use miden_agglayer::{EthAddressFormat, agglayer_library}; +use miden_assembly::{Assembler, DefaultSourceManager}; +use miden_core_lib::CoreLibrary; +use miden_processor::fast::{ExecutionOutput, FastProcessor}; +use miden_processor::{AdviceInputs, DefaultHost, ExecutionError, Program, StackInputs}; +use miden_protocol::Felt; +use miden_protocol::account::AccountId; +use miden_protocol::address::NetworkId; +use miden_protocol::testing::account_id::{ + ACCOUNT_ID_PRIVATE_SENDER, + ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, + AccountIdBuilder, +}; +use miden_protocol::transaction::TransactionKernel; + +/// Execute a program with default host +async fn execute_program_with_default_host( + program: Program, +) -> Result { + let mut host = DefaultHost::default(); + + let test_lib = TransactionKernel::library(); + host.load_library(test_lib.mast_forest()).unwrap(); + + let std_lib = CoreLibrary::default(); + host.load_library(std_lib.mast_forest()).unwrap(); + + for (event_name, handler) in std_lib.handlers() { + host.register_handler(event_name, handler)?; + } + + let asset_conversion_lib = agglayer_library(); + host.load_library(asset_conversion_lib.mast_forest()).unwrap(); + + let stack_inputs = StackInputs::new(vec![]).unwrap(); + let advice_inputs = AdviceInputs::default(); + + let processor = FastProcessor::new_debug(stack_inputs.as_slice(), advice_inputs); + processor.execute(&program, &mut host).await +} + +#[test] +fn test_account_id_to_ethereum_roundtrip() { + let original_account_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap(); + let eth_address = EthAddressFormat::from_account_id(original_account_id); + let recovered_account_id = eth_address.to_account_id().unwrap(); + assert_eq!(original_account_id, recovered_account_id); +} + +#[test] +fn test_bech32_to_ethereum_roundtrip() { + let test_addresses = [ + "mtst1azcw08rget79fqp8ymr0zqkv5v5lj466", + "mtst1arxmxavamh7lqyp79mexktt4vgxv40mp", + "mtst1ar2phe0pa0ln75plsczxr8ryws4s8zyp", + ]; + + let evm_addresses = [ + "0x00000000b0e79c68cafc54802726c6f102cca300", + "0x00000000cdb3759dddfdf0103e2ef26b2d756200", + "0x00000000d41be5e1ebff3f503f8604619c647400", + ]; + + for (bech32, expected_evm) in test_addresses.iter().zip(evm_addresses.iter()) { + let (network_id, account_id) = AccountId::from_bech32(bech32).unwrap(); + + let eth = EthAddressFormat::from_account_id(account_id); + let recovered = eth.to_account_id().unwrap(); + let recovered_bech32 = recovered.to_bech32(network_id); + + assert_eq!(&account_id, &recovered); + assert_eq!(*expected_evm, eth.to_string()); + assert_eq!(*bech32, recovered_bech32); + } +} + +#[test] +fn test_random_bech32_to_ethereum_roundtrip() { + let mut rng = rand::rng(); + let network_id = NetworkId::Testnet; + + for _ in 0..3 { + let account_id = AccountIdBuilder::new().build_with_rng(&mut rng); + let bech32_address = account_id.to_bech32(network_id.clone()); + let eth_address = EthAddressFormat::from_account_id(account_id); + let recovered_account_id = eth_address.to_account_id().unwrap(); + let recovered_bech32 = recovered_account_id.to_bech32(network_id.clone()); + + assert_eq!(account_id, recovered_account_id); + assert_eq!(bech32_address, recovered_bech32); + } +} + +#[tokio::test] +async fn test_ethereum_address_to_account_id_in_masm() -> anyhow::Result<()> { + let test_account_ids = [ + AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER)?, + AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, + AccountIdBuilder::new().build_with_rng(&mut rand::rng()), + AccountIdBuilder::new().build_with_rng(&mut rand::rng()), + AccountIdBuilder::new().build_with_rng(&mut rand::rng()), + ]; + + for (idx, original_account_id) in test_account_ids.iter().enumerate() { + let eth_address = EthAddressFormat::from_account_id(*original_account_id); + + let address_felts = eth_address.to_elements().to_vec(); + let le: Vec = address_felts + .iter() + .map(|f| { + let val = f.as_int(); + assert!(val <= u32::MAX as u64, "felt value {} exceeds u32::MAX", val); + val as u32 + }) + .collect(); + + assert_eq!(le[4], 0, "test {}: expected msw limb (le[4]) to be zero", idx); + + let addr0 = le[0]; + let addr1 = le[1]; + let addr2 = le[2]; + let addr3 = le[3]; + let addr4 = le[4]; + + let account_id_felts: [Felt; 2] = (*original_account_id).into(); + let expected_prefix = account_id_felts[0].as_int(); + let expected_suffix = account_id_felts[1].as_int(); + + let script_code = format!( + r#" + use miden::core::sys + use miden::agglayer::eth_address + + begin + push.{}.{}.{}.{}.{} + exec.eth_address::to_account_id + exec.sys::truncate_stack + end + "#, + addr4, addr3, addr2, addr1, addr0 + ); + + let program = Assembler::new(Arc::new(DefaultSourceManager::default())) + .with_dynamic_library(CoreLibrary::default()) + .unwrap() + .with_dynamic_library(agglayer_library()) + .unwrap() + .assemble_program(&script_code) + .unwrap(); + + let exec_output = execute_program_with_default_host(program).await?; + + let actual_prefix = exec_output.stack[0].as_int(); + let actual_suffix = exec_output.stack[1].as_int(); + + assert_eq!(actual_prefix, expected_prefix, "test {}: prefix mismatch", idx); + assert_eq!(actual_suffix, expected_suffix, "test {}: suffix mismatch", idx); + + let reconstructed_account_id = + AccountId::try_from([Felt::new(actual_prefix), Felt::new(actual_suffix)])?; + + assert_eq!( + reconstructed_account_id, *original_account_id, + "test {}: accountId roundtrip failed", + idx + ); + } + + Ok(()) +} From aacee91072e2aa88b438e163b3e12a0b7e789eb3 Mon Sep 17 00:00:00 2001 From: Arthur Abeilice Date: Fri, 16 Jan 2026 14:23:53 -0300 Subject: [PATCH 106/114] feat: add reusable ownable module and refactor network fungible faucet to use it (#2228) --- CHANGELOG.md | 1 + .../faucets/network_fungible_faucet.masm | 2 + .../asm/standards/access/ownable.masm | 175 +++++++ .../standards/faucets/network_fungible.masm | 67 ++- .../src/account/faucets/network_fungible.rs | 2 +- .../miden-standards/src/errors/standards.rs | 6 +- crates/miden-testing/tests/scripts/faucet.rs | 486 +++++++++++++++++- 7 files changed, 706 insertions(+), 33 deletions(-) create mode 100644 crates/miden-standards/asm/standards/access/ownable.masm diff --git a/CHANGELOG.md b/CHANGELOG.md index c7a6acccad..b71f5185dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260), [#2268](https://github.com/0xMiden/miden-base/pull/2268), [#2279](https://github.com/0xMiden/miden-base/pull/2279)). - Introduce standard `NetworkAccountTarget` attachment for use in network transactions which replaces `NoteTag::NetworkAccount` ([#2257](https://github.com/0xMiden/miden-base/pull/2257)). +- Added `miden::standards::access::ownable` standard module for component ownership management, and integrated it into the `network_fungible` faucet (including new tests). ([#2228](https://github.com/0xMiden/miden-base/pull/2228)). ### Changes diff --git a/crates/miden-standards/asm/account_components/faucets/network_fungible_faucet.masm b/crates/miden-standards/asm/account_components/faucets/network_fungible_faucet.masm index 604239c7fd..7d350a4224 100644 --- a/crates/miden-standards/asm/account_components/faucets/network_fungible_faucet.masm +++ b/crates/miden-standards/asm/account_components/faucets/network_fungible_faucet.masm @@ -4,3 +4,5 @@ pub use ::miden::standards::faucets::network_fungible::distribute pub use ::miden::standards::faucets::network_fungible::burn +pub use ::miden::standards::faucets::network_fungible::transfer_ownership +pub use ::miden::standards::faucets::network_fungible::renounce_ownership diff --git a/crates/miden-standards/asm/standards/access/ownable.masm b/crates/miden-standards/asm/standards/access/ownable.masm new file mode 100644 index 0000000000..79702c1945 --- /dev/null +++ b/crates/miden-standards/asm/standards/access/ownable.masm @@ -0,0 +1,175 @@ +# miden::standards::access::ownable +# +# Provides ownership management functionality for account components. +# This template can be imported and used by any component that needs owner controls. + +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::protocol::native_account + +# CONSTANTS +# ================================================================================================ + +# The slot in this component's storage layout where the owner config is stored. +const OWNER_CONFIG_SLOT = word("miden::standards::access::ownable::owner_config") + +# ZERO_ADDRESS word (all zeros) used to represent no owner +# Format: [prefix=0, suffix=0, 0, 0] as stored in account storage +const ZERO_ADDRESS = [0, 0, 0, 0] + +# ERRORS +# ================================================================================================ + +const ERR_SENDER_NOT_OWNER = "note sender is not the owner" + +# INTERNAL PROCEDURES +# ================================================================================================ + +#! Returns the owner AccountId from storage. +#! +#! Inputs: [] +#! Outputs: [owner_prefix, owner_suffix] +#! +#! Where: +#! - owner_{prefix, suffix} are the prefix and suffix felts of the owner AccountId. +proc owner + push.OWNER_CONFIG_SLOT[0..2] exec.active_account::get_item + # => [owner_prefix, owner_suffix, 0, 0] + + # Storage format in memory: [0, 0, suffix, prefix] (word[0], word[1], word[2], word[3]) + # mem_loadw_be loads big-endian (reversed), so stack gets: [prefix, suffix, 0, 0] + # Stack: [owner_prefix (pos 0), owner_suffix (pos 1), 0 (pos 2), 0 (pos 3)] + # We want: [owner_prefix, owner_suffix] + # Move zeros to top using movup, then drop them + movup.2 + # => [0, owner_prefix, owner_suffix, 0] (moves element at pos 2 to pos 0) + + movup.3 + # => [0, 0, owner_prefix, owner_suffix] (moves element at pos 3 to pos 0) + + drop drop + # => [owner_prefix, owner_suffix] +end + +#! Checks if the given account ID is the owner of this component. +#! +#! Inputs: [account_id_prefix, account_id_suffix] +#! Outputs: [is_owner] +#! +#! Where: +#! - account_id_{prefix, suffix} are the prefix and suffix felts of the AccountId to check. +#! - is_owner is 1 if the account is the owner, 0 otherwise. +proc is_owner + + exec.owner + # => [owner_prefix, owner_suffix, account_id_prefix, account_id_suffix] + + exec.account_id::is_equal + # => [is_owner] + +end + +# PUBLIC INTERFACE +# ================================================================================================ + +#! Checks if the note sender is the owner and panics if not. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Panics if: +#! - the note sender is not the owner. +pub proc verify_owner + exec.active_note::get_sender + # => [sender_prefix, sender_suffix] + + exec.is_owner + # => [is_owner] + + assert.err=ERR_SENDER_NOT_OWNER + # => [] +end + +#! Returns the owner AccountId. +#! +#! Inputs: [pad(16)] +#! Outputs: [owner_prefix, owner_suffix, pad(14)] +#! +#! Where: +#! - owner_{prefix, suffix} are the prefix and suffix felts of the owner AccountId. +#! +#! Invocation: call +pub proc get_owner + exec.owner + # => [owner_prefix, owner_suffix, pad(14)] +end + +#! Transfers ownership to a new account. +#! +#! Can only be called by the current owner. +#! +#! Inputs: [new_owner_prefix, new_owner_suffix, pad(14)] +#! Outputs: [pad(16)] +#! +#! Where: +#! - new_owner_{prefix, suffix} are the prefix and suffix felts of the new owner AccountId. +#! +#! Panics if: +#! - the note sender is not the owner. +#! +#! Invocation: call +pub proc transfer_ownership + # Check that the caller is the owner + exec.verify_owner + # => [new_owner_prefix, new_owner_suffix, pad(14)] + + push.0 movdn.2 push.0 movdn.2 + # => [new_owner_prefix, new_owner_suffix, 0, 0, pad(14)] + + push.OWNER_CONFIG_SLOT[0..2] + # => [slot_prefix, slot_suffix, new_owner_prefix, new_owner_suffix, 0, 0, pad(14)] + + exec.native_account::set_item + # => [OLD_OWNER_WORD, pad(14)] + + # When the stack has 16 elements, dropw will shift in zeros from the right, + # resulting in [pad(16)]. So dropw is sufficient here. + dropw + # => [pad(16)] +end + +#! Renounces ownership, leaving the component without an owner. +#! +#! Can only be called by the current owner. +#! +#! Inputs: [pad(16)] +#! Outputs: [pad(16)] +#! +#! Panics if: +#! - the note sender is not the owner. +#! +#! Invocation: call +#! +#! Important Note! +#! This feature allows the owner to relinquish administrative privileges, a common pattern +#! after an initial stage with centralized administration is over. Once ownership is renounced, +#! the component becomes permanently ownerless and cannot be managed by any account. +pub proc renounce_ownership + exec.verify_owner + # => [pad(16)] + + # ---- Push ZERO_ADDRESS to storage ---- + push.ZERO_ADDRESS + # => [0, 0, 0, 0, pad(16)] + + push.OWNER_CONFIG_SLOT[0..2] + # => [slot_prefix, slot_suffix, 0, 0, 0, 0, pad(16)] + + exec.native_account::set_item + # => [OLD_OWNER_WORD, pad(16)] + + dropw + # => [pad(16)] +end + diff --git a/crates/miden-standards/asm/standards/faucets/network_fungible.masm b/crates/miden-standards/asm/standards/faucets/network_fungible.masm index 04cabea248..5f405db8fe 100644 --- a/crates/miden-standards/asm/standards/faucets/network_fungible.masm +++ b/crates/miden-standards/asm/standards/faucets/network_fungible.masm @@ -1,38 +1,52 @@ -use miden::protocol::active_account -use miden::protocol::account_id use miden::protocol::active_note use miden::standards::faucets -use miden::standards::faucets::basic_fungible +use miden::standards::access::ownable -# CONSTANTS +# PUBLIC INTERFACE # ================================================================================================ -# The slot in this component's storage layout where the owner config is stored. -const OWNER_CONFIG_SLOT=word("miden::standards::network_fungible_faucet::owner_config") +# OWNER MANAGEMENT +# ------------------------------------------------------------------------------------------------ -# ERRORS -const ERR_ONLY_OWNER_CAN_MINT="note sender is not the owner of the faucet who can mint assets" - -#! Checks if the note sender is the owner of this faucet. +#! Returns the owner AccountId. #! #! Inputs: [] -#! Outputs: [is_owner] +#! Outputs: [owner_prefix, owner_suffix, pad(14)] #! -#! Where: -#! - is_owner is 1 if the sender is the owner, 0 otherwise. -proc is_owner - push.OWNER_CONFIG_SLOT[0..2] exec.active_account::get_item - # => [owner_prefix, owner_suffix, 0, 0] +#! Invocation: call +pub use ownable::get_owner - exec.active_note::get_sender - # => [sender_prefix, sender_suffix, owner_prefix, owner_suffix, 0, 0] +#! Transfers ownership to a new account. +#! +#! Can only be called by the current owner. +#! +#! Inputs: [new_owner_prefix, new_owner_suffix, pad(14)] +#! Outputs: [pad(16)] +#! +#! Where: +#! - new_owner_{prefix, suffix} are the prefix and suffix felts of the new owner AccountId. +#! +#! Panics if: +#! - the note sender is not the owner. +#! +#! Invocation: call +pub use ownable::transfer_ownership - exec.account_id::is_equal - # => [are_equal, 0, 0] +#! Renounces ownership, leaving the component without an owner. +#! +#! Can only be called by the current owner. +#! +#! Inputs: [pad(16)] +#! Outputs: [pad(16)] +#! +#! Panics if: +#! - the note sender is not the owner. +#! +#! Invocation: call +pub use ownable::renounce_ownership - movdn.2 drop drop - # => [is_owner] -end +# ASSET DISTRIBUTION +# ------------------------------------------------------------------------------------------------ #! Distributes freshly minted fungible assets to the provided recipient. #! @@ -55,11 +69,8 @@ end #! #! Invocation: call pub proc distribute - exec.is_owner - # => [is_owner, amount, tag, note_type, RECIPIENT, pad(9)] - - assert.err=ERR_ONLY_OWNER_CAN_MINT - # => [amount, tag, note_type, RECIPIENT, pad(9)] + exec.ownable::verify_owner + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(7)] exec.faucets::distribute # => [note_idx, pad(15)] diff --git a/crates/miden-standards/src/account/faucets/network_fungible.rs b/crates/miden-standards/src/account/faucets/network_fungible.rs index 502e8582df..fb150d2d9d 100644 --- a/crates/miden-standards/src/account/faucets/network_fungible.rs +++ b/crates/miden-standards/src/account/faucets/network_fungible.rs @@ -37,7 +37,7 @@ procedure_digest!( ); static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::network_fungible_faucet::owner_config") + StorageSlotName::new("miden::standards::access::ownable::owner_config") .expect("storage slot name should be valid") }); diff --git a/crates/miden-standards/src/errors/standards.rs b/crates/miden-standards/src/errors/standards.rs index 85a861122f..007723901b 100644 --- a/crates/miden-standards/src/errors/standards.rs +++ b/crates/miden-standards/src/errors/standards.rs @@ -21,9 +21,6 @@ pub const ERR_MALFORMED_MULTISIG_CONFIG: MasmError = MasmError::from_static_str( /// Error Message: "MINT script expects exactly 12 inputs for private or 16+ inputs for public output notes" pub const ERR_MINT_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("MINT script expects exactly 12 inputs for private or 16+ inputs for public output notes"); -/// Error Message: "note sender is not the owner of the faucet who can mint assets" -pub const ERR_ONLY_OWNER_CAN_MINT: MasmError = MasmError::from_static_str("note sender is not the owner of the faucet who can mint assets"); - /// Error Message: "failed to reclaim P2IDE note because the reclaiming account is not the sender" pub const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER: MasmError = MasmError::from_static_str("failed to reclaim P2IDE note because the reclaiming account is not the sender"); /// Error Message: "P2IDE reclaim is disabled" @@ -40,6 +37,9 @@ pub const ERR_P2ID_TARGET_ACCT_MISMATCH: MasmError = MasmError::from_static_str( /// Error Message: "P2ID note expects exactly 2 note inputs" pub const ERR_P2ID_WRONG_NUMBER_OF_INPUTS: MasmError = MasmError::from_static_str("P2ID note expects exactly 2 note inputs"); +/// Error Message: "note sender is not the owner" +pub const ERR_SENDER_NOT_OWNER: MasmError = MasmError::from_static_str("note sender is not the owner"); + /// Error Message: "SWAP script requires exactly 1 note asset" pub const ERR_SWAP_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_str("SWAP script requires exactly 1 note asset"); /// Error Message: "SWAP script expects exactly 16 note inputs" diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 46db71e594..aaca772c55 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -33,7 +33,10 @@ use miden_standards::account::faucets::{ NetworkFungibleFaucet, }; use miden_standards::code_builder::CodeBuilder; -use miden_standards::errors::standards::ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED; +use miden_standards::errors::standards::{ + ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED, + ERR_SENDER_NOT_OWNER, +}; use miden_standards::note::{MintNoteInputs, WellKnownNote, create_burn_note, create_mint_note}; use miden_standards::testing::note::NoteBuilder; use miden_testing::{Auth, MockChain, assert_transaction_executor_error}; @@ -560,6 +563,487 @@ async fn network_faucet_mint() -> anyhow::Result<()> { Ok(()) } +// TESTS FOR NETWORK FAUCET OWNERSHIP +// ================================================================================================ + +/// Tests that the owner can mint assets on network faucet. +#[tokio::test] +async fn test_network_faucet_owner_can_mint() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + let owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let faucet = builder.add_existing_network_faucet("NET", 1000, owner_account_id, Some(50))?; + let target_account = builder.add_existing_wallet(Auth::IncrNonce)?; + let mock_chain = builder.build()?; + + let amount = Felt::new(75); + let mint_asset: Asset = FungibleAsset::new(faucet.id(), amount.into())?.into(); + + let output_note_tag = NoteTag::with_account_target(target_account.id()); + let p2id_note = create_p2id_note_exact( + faucet.id(), + target_account.id(), + vec![mint_asset], + NoteType::Private, + Word::default(), + )?; + let recipient = p2id_note.recipient().digest(); + + let mint_inputs = MintNoteInputs::new_private(recipient, amount, output_note_tag.into()); + + let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); + let mint_note = create_mint_note( + faucet.id(), + owner_account_id, + mint_inputs, + NoteAttachment::default(), + &mut rng, + )?; + + let tx_context = mock_chain.build_tx_context(faucet.id(), &[], &[mint_note])?.build()?; + let executed_transaction = tx_context.execute().await?; + + assert_eq!(executed_transaction.output_notes().num_notes(), 1); + + Ok(()) +} + +/// Tests that a non-owner cannot mint assets on network faucet. +#[tokio::test] +async fn test_network_faucet_non_owner_cannot_mint() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + let owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let non_owner_account_id = AccountId::dummy( + [2; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let faucet = builder.add_existing_network_faucet("NET", 1000, owner_account_id, Some(50))?; + let target_account = builder.add_existing_wallet(Auth::IncrNonce)?; + let mock_chain = builder.build()?; + + let amount = Felt::new(75); + let mint_asset: Asset = FungibleAsset::new(faucet.id(), amount.into())?.into(); + + let output_note_tag = NoteTag::with_account_target(target_account.id()); + let p2id_note = create_p2id_note_exact( + faucet.id(), + target_account.id(), + vec![mint_asset], + NoteType::Private, + Word::default(), + )?; + let recipient = p2id_note.recipient().digest(); + + let mint_inputs = MintNoteInputs::new_private(recipient, amount, output_note_tag.into()); + + // Create mint note from NON-OWNER + let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); + let mint_note = create_mint_note( + faucet.id(), + non_owner_account_id, + mint_inputs, + NoteAttachment::default(), + &mut rng, + )?; + + let tx_context = mock_chain.build_tx_context(faucet.id(), &[], &[mint_note])?.build()?; + let result = tx_context.execute().await; + + // The distribute function uses ERR_ONLY_OWNER, which is "note sender is not the owner" + let expected_error = ERR_SENDER_NOT_OWNER; + assert_transaction_executor_error!(result, expected_error); + + Ok(()) +} + +/// Tests that the owner is correctly stored and can be read from storage. +#[tokio::test] +async fn test_network_faucet_owner_storage() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + let owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let faucet = builder.add_existing_network_faucet("NET", 1000, owner_account_id, Some(50))?; + let _mock_chain = builder.build()?; + + // Verify owner is stored correctly + let stored_owner = faucet.storage().get_item(NetworkFungibleFaucet::owner_config_slot())?; + + // Storage format: [0, 0, suffix, prefix] + assert_eq!(stored_owner[3], owner_account_id.prefix().as_felt()); + assert_eq!(stored_owner[2], Felt::new(owner_account_id.suffix().as_int())); + assert_eq!(stored_owner[1], Felt::new(0)); + assert_eq!(stored_owner[0], Felt::new(0)); + + Ok(()) +} + +/// Tests that transfer_ownership updates the owner correctly. +#[tokio::test] +async fn test_network_faucet_transfer_ownership() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + // Setup: Create initial owner and new owner accounts + let initial_owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let new_owner_account_id = AccountId::dummy( + [2; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let faucet = + builder.add_existing_network_faucet("NET", 1000, initial_owner_account_id, Some(50))?; + let target_account = builder.add_existing_wallet(Auth::IncrNonce)?; + + let amount = Felt::new(75); + let mint_asset: Asset = FungibleAsset::new(faucet.id(), amount.into())?.into(); + + let output_note_tag = NoteTag::with_account_target(target_account.id()); + let p2id_note = create_p2id_note_exact( + faucet.id(), + target_account.id(), + vec![mint_asset], + NoteType::Private, + Word::default(), + )?; + let recipient = p2id_note.recipient().digest(); + + // Sanity Check: Prove that the initial owner can mint assets + let mint_inputs = MintNoteInputs::new_private(recipient, amount, output_note_tag.into()); + + let mut rng = RpoRandomCoin::new([Felt::from(42u32); 4].into()); + let mint_note = create_mint_note( + faucet.id(), + initial_owner_account_id, + mint_inputs.clone(), + NoteAttachment::default(), + &mut rng, + )?; + + // Action: Create transfer_ownership note script + let transfer_note_script_code = format!( + r#" + use miden::standards::faucets::network_fungible->network_faucet + + begin + repeat.14 push.0 end + push.{new_owner_suffix} + push.{new_owner_prefix} + call.network_faucet::transfer_ownership + dropw dropw dropw dropw + end + "#, + new_owner_prefix = new_owner_account_id.prefix().as_felt(), + new_owner_suffix = Felt::new(new_owner_account_id.suffix().as_int()), + ); + + let source_manager = Arc::new(DefaultSourceManager::default()); + let transfer_note_script = CodeBuilder::with_source_manager(source_manager.clone()) + .compile_note_script(transfer_note_script_code.clone())?; + + // Create the transfer note and add it to the builder so it exists on-chain + let mut rng = RpoRandomCoin::new([Felt::from(200u32); 4].into()); + let transfer_note = NoteBuilder::new(initial_owner_account_id, &mut rng) + .note_type(NoteType::Private) + .tag(NoteTag::default().into()) + .serial_number(Word::from([11, 22, 33, 44u32])) + .code(transfer_note_script_code.clone()) + .build()?; + + // Add the transfer note to the builder before building the chain + builder.add_output_note(OutputNote::Full(transfer_note.clone())); + let mut mock_chain = builder.build()?; + + // Prove the block to make the transfer note exist on-chain + mock_chain.prove_next_block()?; + + // Sanity Check: Execute mint transaction to verify initial owner can mint + let tx_context = mock_chain.build_tx_context(faucet.id(), &[], &[mint_note])?.build()?; + let executed_transaction = tx_context.execute().await?; + assert_eq!(executed_transaction.output_notes().num_notes(), 1); + + // Action: Execute transfer_ownership via note script + let tx_context = mock_chain + .build_tx_context(faucet.id(), &[transfer_note.id()], &[])? + .add_note_script(transfer_note_script.clone()) + .with_source_manager(source_manager.clone()) + .build()?; + let executed_transaction = tx_context.execute().await?; + + // Persistence: Apply the transaction to update the faucet state + mock_chain.add_pending_executed_transaction(&executed_transaction)?; + mock_chain.prove_next_block()?; + + // Apply the delta to the faucet account to reflect the ownership change + let mut updated_faucet = faucet.clone(); + updated_faucet.apply_delta(executed_transaction.account_delta())?; + + // Validation 1: Try to mint using the old owner - should fail + let mut rng = RpoRandomCoin::new([Felt::from(300u32); 4].into()); + let mint_note_old_owner = create_mint_note( + updated_faucet.id(), + initial_owner_account_id, + mint_inputs.clone(), + NoteAttachment::default(), + &mut rng, + )?; + + // Use the note as an unauthenticated note (full note object) - it will be created in this + // transaction + let tx_context = mock_chain + .build_tx_context(updated_faucet.id(), &[], &[mint_note_old_owner])? + .build()?; + let result = tx_context.execute().await; + + // The distribute function uses ERR_ONLY_OWNER, which is "note sender is not the owner" + let expected_error = ERR_SENDER_NOT_OWNER; + assert_transaction_executor_error!(result, expected_error); + + // Validation 2: Try to mint using the new owner - should succeed + let mut rng = RpoRandomCoin::new([Felt::from(400u32); 4].into()); + let mint_note_new_owner = create_mint_note( + updated_faucet.id(), + new_owner_account_id, + mint_inputs, + NoteAttachment::default(), + &mut rng, + )?; + + let tx_context = mock_chain + .build_tx_context(updated_faucet.id(), &[], &[mint_note_new_owner])? + .build()?; + let executed_transaction = tx_context.execute().await?; + + // Verify that minting succeeded + assert_eq!(executed_transaction.output_notes().num_notes(), 1); + + Ok(()) +} + +/// Tests that only the owner can transfer ownership. +#[tokio::test] +async fn test_network_faucet_only_owner_can_transfer() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + let owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let non_owner_account_id = AccountId::dummy( + [2; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let new_owner_account_id = AccountId::dummy( + [3; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let faucet = builder.add_existing_network_faucet("NET", 1000, owner_account_id, Some(50))?; + let mock_chain = builder.build()?; + + // Create transfer ownership note script + let transfer_note_script_code = format!( + r#" + use miden::standards::faucets::network_fungible->network_faucet + + begin + repeat.14 push.0 end + push.{new_owner_suffix} + push.{new_owner_prefix} + call.network_faucet::transfer_ownership + dropw dropw dropw dropw + end + "#, + new_owner_prefix = new_owner_account_id.prefix().as_felt(), + new_owner_suffix = Felt::new(new_owner_account_id.suffix().as_int()), + ); + + let source_manager = Arc::new(DefaultSourceManager::default()); + let transfer_note_script = CodeBuilder::with_source_manager(source_manager.clone()) + .compile_note_script(transfer_note_script_code.clone())?; + + // Create a note from NON-OWNER that tries to transfer ownership + let mut rng = RpoRandomCoin::new([Felt::from(100u32); 4].into()); + let transfer_note = NoteBuilder::new(non_owner_account_id, &mut rng) + .note_type(NoteType::Private) + .tag(NoteTag::default().into()) + .serial_number(Word::from([10, 20, 30, 40u32])) + .code(transfer_note_script_code.clone()) + .build()?; + + let tx_context = mock_chain + .build_tx_context(faucet.id(), &[], &[transfer_note])? + .add_note_script(transfer_note_script.clone()) + .with_source_manager(source_manager.clone()) + .build()?; + let result = tx_context.execute().await; + + // Verify that the transaction failed with ERR_ONLY_OWNER + let expected_error = ERR_SENDER_NOT_OWNER; + assert_transaction_executor_error!(result, expected_error); + + Ok(()) +} + +/// Tests that renounce_ownership clears the owner correctly. +#[tokio::test] +async fn test_network_faucet_renounce_ownership() -> anyhow::Result<()> { + let mut builder = MockChain::builder(); + + let owner_account_id = AccountId::dummy( + [1; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let new_owner_account_id = AccountId::dummy( + [2; 15], + AccountIdVersion::Version0, + AccountType::RegularAccountImmutableCode, + AccountStorageMode::Private, + ); + + let faucet = builder.add_existing_network_faucet("NET", 1000, owner_account_id, Some(50))?; + + // Check stored value before renouncing + let stored_owner_before = + faucet.storage().get_item(NetworkFungibleFaucet::owner_config_slot())?; + assert_eq!(stored_owner_before[3], owner_account_id.prefix().as_felt()); + assert_eq!(stored_owner_before[2], Felt::new(owner_account_id.suffix().as_int())); + + // Create renounce_ownership note script + let renounce_note_script_code = r#" + use miden::standards::faucets::network_fungible->network_faucet + + begin + repeat.16 push.0 end + call.network_faucet::renounce_ownership + dropw dropw dropw dropw + end + "#; + + let source_manager = Arc::new(DefaultSourceManager::default()); + let renounce_note_script = CodeBuilder::with_source_manager(source_manager.clone()) + .compile_note_script(renounce_note_script_code)?; + + // Create transfer note script (will be used after renounce) + let transfer_note_script_code = format!( + r#" + use miden::standards::faucets::network_fungible->network_faucet + + begin + repeat.14 push.0 end + push.{new_owner_suffix} + push.{new_owner_prefix} + call.network_faucet::transfer_ownership + dropw dropw dropw dropw + end + "#, + new_owner_prefix = new_owner_account_id.prefix().as_felt(), + new_owner_suffix = Felt::new(new_owner_account_id.suffix().as_int()), + ); + + let transfer_note_script = CodeBuilder::with_source_manager(source_manager.clone()) + .compile_note_script(transfer_note_script_code.clone())?; + + let mut rng = RpoRandomCoin::new([Felt::from(200u32); 4].into()); + let renounce_note = NoteBuilder::new(owner_account_id, &mut rng) + .note_type(NoteType::Private) + .tag(NoteTag::default().into()) + .serial_number(Word::from([11, 22, 33, 44u32])) + .code(renounce_note_script_code) + .build()?; + + let mut rng = RpoRandomCoin::new([Felt::from(300u32); 4].into()); + let transfer_note = NoteBuilder::new(owner_account_id, &mut rng) + .note_type(NoteType::Private) + .tag(NoteTag::default().into()) + .serial_number(Word::from([50, 60, 70, 80u32])) + .code(transfer_note_script_code.clone()) + .build()?; + + builder.add_output_note(OutputNote::Full(renounce_note.clone())); + builder.add_output_note(OutputNote::Full(transfer_note.clone())); + let mut mock_chain = builder.build()?; + mock_chain.prove_next_block()?; + + // Execute renounce_ownership + let tx_context = mock_chain + .build_tx_context(faucet.id(), &[renounce_note.id()], &[])? + .add_note_script(renounce_note_script.clone()) + .with_source_manager(source_manager.clone()) + .build()?; + let executed_transaction = tx_context.execute().await?; + + mock_chain.add_pending_executed_transaction(&executed_transaction)?; + mock_chain.prove_next_block()?; + + let mut updated_faucet = faucet.clone(); + updated_faucet.apply_delta(executed_transaction.account_delta())?; + + // Check stored value after renouncing - should be zero + let stored_owner_after = + updated_faucet.storage().get_item(NetworkFungibleFaucet::owner_config_slot())?; + assert_eq!(stored_owner_after[0], Felt::new(0)); + assert_eq!(stored_owner_after[1], Felt::new(0)); + assert_eq!(stored_owner_after[2], Felt::new(0)); + assert_eq!(stored_owner_after[3], Felt::new(0)); + + // Try to transfer ownership - should fail because there's no owner + // The transfer note was already added to the builder, so we need to prove another block + // to make it available on-chain after the renounce transaction + mock_chain.prove_next_block()?; + + let tx_context = mock_chain + .build_tx_context(updated_faucet.id(), &[transfer_note.id()], &[])? + .add_note_script(transfer_note_script.clone()) + .with_source_manager(source_manager.clone()) + .build()?; + let result = tx_context.execute().await; + + let expected_error = ERR_SENDER_NOT_OWNER; + assert_transaction_executor_error!(result, expected_error); + + Ok(()) +} + // TESTS FOR FAUCET PROCEDURE COMPATIBILITY // ================================================================================================ From 60baedaae850c94c5de529a74cc1c58476abdecf Mon Sep 17 00:00:00 2001 From: igamigo Date: Fri, 16 Jan 2026 14:46:18 -0300 Subject: [PATCH 107/114] feat: extend `InitStorageData` and allow passing native structs (#2230) --- CHANGELOG.md | 1 + .../src/account/component/metadata/mod.rs | 18 +- .../component/storage/init_storage_data.rs | 199 +++++++++-- .../src/account/component/storage/mod.rs | 2 +- .../src/account/component/storage/schema.rs | 331 ++++++++++-------- .../storage/toml/init_storage_data.rs | 213 ++++------- .../src/account/component/storage/toml/mod.rs | 3 +- .../component/storage/toml/serde_impls.rs | 39 ++- .../account/component/storage/toml/tests.rs | 142 +++++--- .../account/component/storage/value_name.rs | 42 ++- 10 files changed, 603 insertions(+), 387 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b71f5185dd..3369c0e5b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ - [BREAKING] Removed OLD_MAP_ROOT from being returned when calling [`native_account::set_map_item`](crates/miden-lib/asm/miden/native_account.masm) ([#2194](https://github.com/0xMiden/miden-base/pull/2194)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). - [BREAKING] Refactor note tags to be arbitrary `u32` values and drop previous validation ([#2219](https://github.com/0xMiden/miden-base/pull/2219)). +- [BREAKING] Refactored `InitStorageData` to support native types ([#2230](https://github.com/0xMiden/miden-base/pull/2230)). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-protocol/src/account/component/metadata/mod.rs b/crates/miden-protocol/src/account/component/metadata/mod.rs index 2f96c19274..db68b27b0a 100644 --- a/crates/miden-protocol/src/account/component/metadata/mod.rs +++ b/crates/miden-protocol/src/account/component/metadata/mod.rs @@ -27,10 +27,10 @@ use crate::AccountError; /// /// - The metadata's storage schema does not contain duplicate slot names. /// - The schema cannot contain protocol-reserved slot names. -/// - Each init-time value name uniquely identifies a single value. The expected init-time -/// requirements can be retrieved with [AccountComponentMetadata::schema_requirements()], which -/// returns a map from keys to [SchemaRequirement] (which indicates the expected value type and -/// optional defaults). +/// - Each init-time value name uniquely identifies a single value. The expected init-time metadata +/// can be retrieved with [AccountComponentMetadata::schema_requirements()], which returns a map +/// from keys to [SchemaRequirement] (which indicates the expected value type and optional +/// defaults). /// /// # Example /// @@ -48,6 +48,7 @@ use crate::AccountError; /// StorageValueName, /// ValueSlotSchema, /// WordSchema, +/// WordValue, /// }; /// use semver::Version; /// @@ -74,10 +75,9 @@ use crate::AccountError; /// ); /// /// // Init value keys are derived from slot name: `demo::test_value.foo`. -/// let init_storage_data = InitStorageData::new( -/// [(StorageValueName::from_slot_name(&slot_name).with_suffix("foo")?, "300".into())], -/// [], -/// ); +/// let value_name = StorageValueName::from_slot_name_with_suffix(&slot_name, "foo")?; +/// let mut init_storage_data = InitStorageData::default(); +/// init_storage_data.set_value(value_name, WordValue::Atomic("300".into()))?; /// /// let storage_slots = metadata.storage_schema().build_storage_slots(&init_storage_data)?; /// assert_eq!(storage_slots.len(), 1); @@ -123,7 +123,7 @@ impl AccountComponentMetadata { } } - /// Returns the init-time value requirements for this schema. + /// Returns the init-time values requirements for this schema. /// /// These values are used for initializing storage slot values or storage map entries. For a /// full example, refer to the docs for [AccountComponentMetadata]. diff --git a/crates/miden-protocol/src/account/component/storage/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs index bb2d5c0a49..ff870dd329 100644 --- a/crates/miden-protocol/src/account/component/storage/init_storage_data.rs +++ b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs @@ -1,18 +1,22 @@ use alloc::collections::BTreeMap; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; +use thiserror::Error; + use super::StorageValueName; +use crate::account::StorageSlotName; +use crate::{Felt, FieldElement, Word}; -/// A raw word value provided via [`InitStorageData`]. +/// A word value provided via [`InitStorageData`]. /// -/// This is used for defining specific values in relation to a component's schema, where each values -/// is supplied as either an atomic string (e.g. `"0x1234"`, `"16"`, `"BTC"`) or an array of 4 field -/// elements. +/// This is used for defining specific values in relation to a component's schema, where each value +/// is supplied as either a fully-typed word, an atomic string (e.g. `"0x1234"`, `"16"`, `"BTC"`), +/// or an array of 4 field elements. #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "std", serde(untagged))] pub enum WordValue { + /// A fully-typed word value. + FullyTyped(Word), /// Represents a single word value, given by a single string input. Atomic(String), /// Represents a word through four string-encoded field elements. @@ -31,6 +35,28 @@ impl From<&str> for WordValue { } } +impl From for WordValue { + fn from(value: Word) -> Self { + WordValue::FullyTyped(value) + } +} + +impl From for WordValue { + /// Converts a [`Felt`] to a [`WordValue`] as a Word in the form `[0, 0, 0, felt]`. + fn from(value: Felt) -> Self { + WordValue::FullyTyped(Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, value])) + } +} + +impl From<[Felt; 4]> for WordValue { + fn from(value: [Felt; 4]) -> Self { + WordValue::FullyTyped(Word::from(value)) + } +} + +// INIT STORAGE DATA +// ==================================================================================================== + /// Represents the data required to initialize storage entries when instantiating an /// [AccountComponent](crate::account::AccountComponent) from component metadata (either provided /// directly or extracted from a package). @@ -38,40 +64,153 @@ impl From<&str> for WordValue { /// An [`InitStorageData`] can be created from a TOML string when the `std` feature flag is set. #[derive(Clone, Debug, Default)] pub struct InitStorageData { - /// A mapping of init value names to their raw values. + /// A mapping of storage value names to their init values. value_entries: BTreeMap, - /// A mapping of storage map slot names to their raw key/value entries. - map_entries: BTreeMap>, + /// A mapping of storage map slot names to their init key/value entries. + map_entries: BTreeMap>, } impl InitStorageData { - /// Creates a new instance of [InitStorageData]. - /// - /// A [`BTreeMap`] is constructed from the passed iterator, so duplicate keys will cause - /// overridden values. - pub fn new( - entries: impl IntoIterator, - map_entries: impl IntoIterator)>, - ) -> Self { - InitStorageData { - value_entries: entries.into_iter().collect(), - map_entries: map_entries.into_iter().collect(), - } - } - /// Returns a reference to the underlying init values map. pub fn values(&self) -> &BTreeMap { &self.value_entries } - /// Returns a reference to the stored init value, or [`Option::None`] if the key is not - /// present. - pub fn get(&self, key: &StorageValueName) -> Option<&WordValue> { - self.value_entries.get(key) + /// Returns a reference to the underlying init map entries. + pub fn maps(&self) -> &BTreeMap> { + &self.map_entries + } + + /// Returns a reference to the stored init value for the given name. + pub fn value_entry(&self, name: &StorageValueName) -> Option<&WordValue> { + self.value_entries.get(name) + } + + /// Returns a reference to the stored init value for a full slot name. + pub fn slot_value_entry(&self, slot_name: &StorageSlotName) -> Option<&WordValue> { + let name = StorageValueName::from_slot_name(slot_name); + self.value_entries.get(&name) } /// Returns the map entries associated with the given storage map slot name, if any. - pub fn map_entries(&self, key: &StorageValueName) -> Option<&Vec<(WordValue, WordValue)>> { - self.map_entries.get(key) + pub fn map_entries(&self, slot_name: &StorageSlotName) -> Option<&Vec<(WordValue, WordValue)>> { + self.map_entries.get(slot_name) } + + /// Returns true if any init value entry targets the given slot name. + pub fn has_value_entries_for_slot(&self, slot_name: &StorageSlotName) -> bool { + self.value_entries.keys().any(|name| name.slot_name() == slot_name) + } + + /// Returns true if any init value entry targets a field of the given slot name. + pub fn has_field_entries_for_slot(&self, slot_name: &StorageSlotName) -> bool { + self.value_entries + .keys() + .any(|name| name.slot_name() == slot_name && name.field_name().is_some()) + } + + // MUTATORS + // -------------------------------------------------------------------------------------------- + + /// Inserts a value entry, returning an error on duplicate or conflicting keys. + /// + /// The value can be any type that implements `Into`, e.g.: + /// + /// - `Word`: a fully-typed word value + /// - `[Felt; 4]`: converted to a Word + /// - `Felt`: converted to `[0, 0, 0, felt]` + /// - `String` or `&str`: a parseable string value + /// - `WordValue`: a raw or fully-typed word value + pub fn insert_value( + &mut self, + name: StorageValueName, + value: impl Into, + ) -> Result<(), InitStorageDataError> { + if self.value_entries.contains_key(&name) { + return Err(InitStorageDataError::DuplicateKey(name.to_string())); + } + if self.map_entries.contains_key(name.slot_name()) { + return Err(InitStorageDataError::ConflictingEntries(name.slot_name().as_str().into())); + } + self.value_entries.insert(name, value.into()); + Ok(()) + } + + /// Sets a value entry, overriding any existing entry for the name. + /// + /// Returns an error if the [`StorageValueName`] has been used for a map slot. + pub fn set_value( + &mut self, + name: StorageValueName, + value: impl Into, + ) -> Result<(), InitStorageDataError> { + if self.map_entries.contains_key(name.slot_name()) { + return Err(InitStorageDataError::ConflictingEntries(name.slot_name().as_str().into())); + } + self.value_entries.insert(name, value.into()); + Ok(()) + } + + /// Inserts a single map entry, returning an error on duplicate or conflicting keys. + /// + /// See [`Self::insert_value`] for examples of supported types for `key` and `value`. + pub fn insert_map_entry( + &mut self, + slot_name: StorageSlotName, + key: impl Into, + value: impl Into, + ) -> Result<(), InitStorageDataError> { + if self.has_value_entries_for_slot(&slot_name) { + return Err(InitStorageDataError::ConflictingEntries(slot_name.as_str().into())); + } + + let key = key.into(); + if let Some(entries) = self.map_entries.get(&slot_name) + && entries.iter().any(|(existing_key, _)| existing_key == &key) + { + return Err(InitStorageDataError::DuplicateKey(format!( + "{}[{key:?}]", + slot_name.as_str() + ))); + } + + self.map_entries.entry(slot_name).or_default().push((key, value.into())); + Ok(()) + } + + /// Sets map entries for the slot, replacing any existing entries. + /// + /// Returns an error if there are conflicting value entries. + pub fn set_map_values( + &mut self, + slot_name: StorageSlotName, + entries: Vec<(WordValue, WordValue)>, + ) -> Result<(), InitStorageDataError> { + if self.has_value_entries_for_slot(&slot_name) { + return Err(InitStorageDataError::ConflictingEntries(slot_name.as_str().into())); + } + self.map_entries.insert(slot_name, entries); + Ok(()) + } + + /// Merges another [`InitStorageData`] into this one, overwriting value entries and appending + /// map entries. + pub fn merge_with(&mut self, other: InitStorageData) { + self.value_entries.extend(other.value_entries); + for (slot_name, entries) in other.map_entries { + self.map_entries.entry(slot_name).or_default().extend(entries); + } + } +} + +// ERRORS +// ==================================================================================================== + +/// Error returned when creating [`InitStorageData`] with invalid entries. +#[derive(Debug, Error, PartialEq, Eq)] +pub enum InitStorageDataError { + #[error("duplicate init key `{0}`")] + DuplicateKey(String), + #[error("conflicting init entries for `{0}`")] + ConflictingEntries(String), } diff --git a/crates/miden-protocol/src/account/component/storage/mod.rs b/crates/miden-protocol/src/account/component/storage/mod.rs index 2c28296c49..06b24bd768 100644 --- a/crates/miden-protocol/src/account/component/storage/mod.rs +++ b/crates/miden-protocol/src/account/component/storage/mod.rs @@ -8,7 +8,7 @@ mod type_registry; pub use type_registry::{SchemaRequirement, SchemaTypeError, SchemaTypeId}; mod init_storage_data; -pub use init_storage_data::{InitStorageData, WordValue}; +pub use init_storage_data::{InitStorageData, InitStorageDataError, WordValue}; #[cfg(feature = "std")] pub mod toml; diff --git a/crates/miden-protocol/src/account/component/storage/schema.rs b/crates/miden-protocol/src/account/component/storage/schema.rs index 9ae69f7b7a..e8054ac482 100644 --- a/crates/miden-protocol/src/account/component/storage/schema.rs +++ b/crates/miden-protocol/src/account/component/storage/schema.rs @@ -146,10 +146,10 @@ impl StorageSlotSchema { slot_name: &StorageSlotName, requirements: &mut BTreeMap, ) -> Result<(), AccountComponentTemplateError> { - let slot_prefix = StorageValueName::from_slot_name(slot_name); + let slot_name = StorageValueName::from_slot_name(slot_name); match self { StorageSlotSchema::Value(slot) => { - slot.collect_init_value_requirements(slot_prefix, requirements) + slot.collect_init_value_requirements(slot_name, requirements) }, StorageSlotSchema::Map(_) => Ok(()), } @@ -162,14 +162,13 @@ impl StorageSlotSchema { slot_name: &StorageSlotName, init_storage_data: &InitStorageData, ) -> Result { - let slot_prefix = StorageValueName::from_slot_name(slot_name); match self { StorageSlotSchema::Value(slot) => { - let word = slot.try_build_word(init_storage_data, slot_prefix)?; + let word = slot.try_build_word(init_storage_data, slot_name)?; Ok(StorageSlot::with_value(slot_name.clone(), word)) }, StorageSlotSchema::Map(slot) => { - let storage_map = slot.try_build_map(init_storage_data, slot_prefix)?; + let storage_map = slot.try_build_map(init_storage_data, slot_name)?; Ok(StorageSlot::with_map(slot_name.clone(), storage_map)) }, } @@ -268,7 +267,7 @@ impl WordSchema { fn collect_init_value_requirements( &self, - slot_prefix: StorageValueName, + value_name: StorageValueName, description: Option, requirements: &mut BTreeMap, ) -> Result<(), AccountComponentTemplateError> { @@ -284,7 +283,7 @@ impl WordSchema { if requirements .insert( - slot_prefix.clone(), + value_name.clone(), SchemaRequirement { description, r#type: r#type.clone(), @@ -293,14 +292,14 @@ impl WordSchema { ) .is_some() { - return Err(AccountComponentTemplateError::DuplicateInitValueName(slot_prefix)); + return Err(AccountComponentTemplateError::DuplicateInitValueName(value_name)); } Ok(()) }, WordSchema::Composite { value } => { for felt in value.iter() { - felt.collect_init_value_requirements(slot_prefix.clone(), requirements)?; + felt.collect_init_value_requirements(value_name.clone(), requirements)?; } Ok(()) }, @@ -336,50 +335,62 @@ impl WordSchema { Ok(()) } + /// Builds a [`Word`] from the provided initialization data according to this schema. + /// + /// For simple schemas, expects a direct slot value (not map or field entries). + /// For composite schemas, either parses a single value or builds the word from individual + /// felt entries. pub(crate) fn try_build_word( &self, init_storage_data: &InitStorageData, - value_prefix: StorageValueName, + slot_name: &StorageSlotName, ) -> Result { + let slot_prefix = StorageValueName::from_slot_name(slot_name); + let slot_value = init_storage_data.slot_value_entry(slot_name); + let has_fields = init_storage_data.has_field_entries_for_slot(slot_name); + + if init_storage_data.map_entries(slot_name).is_some() { + return Err(AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix, + "expected a value, got a map".into(), + )); + } + match self { WordSchema::Simple { r#type, default_value } => { - let value_name = value_prefix; - match init_storage_data.get(&value_name) { - Some(WordValue::Atomic(raw)) => SCHEMA_TYPE_REGISTRY - .try_parse_word(r#type, raw) - .map_err(AccountComponentTemplateError::StorageValueParsingError), - Some(WordValue::Elements(elements)) => { - let felts = elements - .iter() - .map(|element| { - SCHEMA_TYPE_REGISTRY - .try_parse_felt(&SchemaTypeId::native_felt(), element) - }) - .collect::, _>>() - .map_err(AccountComponentTemplateError::StorageValueParsingError)?; - let felts: [Felt; 4] = felts.try_into().expect("length is 4"); - let word = Word::from(felts); - SCHEMA_TYPE_REGISTRY - .validate_word_value(r#type, word) - .map_err(AccountComponentTemplateError::StorageValueParsingError)?; - Ok(word) - }, + if has_fields { + return Err(AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix, + "expected a value, got field entries".into(), + )); + } + match slot_value { + Some(value) => parse_storage_value_with_schema(self, value, &slot_prefix), None => { if *r#type == SchemaTypeId::void() { Ok(Word::empty()) } else { default_value.as_ref().copied().ok_or_else(|| { - AccountComponentTemplateError::InitValueNotProvided(value_name) + AccountComponentTemplateError::InitValueNotProvided(slot_prefix) }) } }, } }, WordSchema::Composite { value } => { + if let Some(value) = slot_value { + if has_fields { + return Err(AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix, + "expected a single value, got both value and field entries".into(), + )); + } + return parse_storage_value_with_schema(self, value, &slot_prefix); + } + let mut result = [Felt::ZERO; 4]; for (index, felt_schema) in value.iter().enumerate() { - result[index] = - felt_schema.try_build_felt(init_storage_data, value_prefix.clone())?; + result[index] = felt_schema.try_build_felt(init_storage_data, slot_name)?; } Ok(Word::from(result)) }, @@ -560,10 +571,9 @@ impl FeltSchema { "non-void felt elements must be named".into(), )); }; - let value_name = slot_prefix - .clone() - .with_suffix(name) - .map_err(|err| AccountComponentTemplateError::InvalidSchema(err.to_string()))?; + let value_name = + StorageValueName::from_slot_name_with_suffix(slot_prefix.slot_name(), name) + .map_err(|err| AccountComponentTemplateError::InvalidSchema(err.to_string()))?; let default_value = self .default_value @@ -593,31 +603,38 @@ impl FeltSchema { pub(crate) fn try_build_felt( &self, init_storage_data: &InitStorageData, - value_prefix: StorageValueName, + slot_name: &StorageSlotName, ) -> Result { - let value_name = - match self.name.as_deref() { - Some(name) => Some(value_prefix.with_suffix(name).map_err(|err| { - AccountComponentTemplateError::InvalidSchema(err.to_string()) - })?), - None => None, - }; - - if let Some(value_name) = value_name.clone() { - match init_storage_data.get(&value_name) { - Some(WordValue::Atomic(raw)) => { + let value_name = match self.name.as_deref() { + Some(name) => Some( + StorageValueName::from_slot_name_with_suffix(slot_name, name) + .map_err(|err| AccountComponentTemplateError::InvalidSchema(err.to_string()))?, + ), + None => None, + }; + + if let Some(value_name) = value_name.clone() + && let Some(raw_value) = init_storage_data.value_entry(&value_name) + { + match raw_value { + WordValue::Atomic(raw) => { let felt = SCHEMA_TYPE_REGISTRY .try_parse_felt(&self.r#type, raw) .map_err(AccountComponentTemplateError::StorageValueParsingError)?; return Ok(felt); }, - Some(WordValue::Elements(_)) => { + WordValue::Elements(_) => { return Err(AccountComponentTemplateError::InvalidInitStorageValue( value_name, "expected an atomic value, got a 4-element array".into(), )); }, - None => {}, + WordValue::FullyTyped(_) => { + return Err(AccountComponentTemplateError::InvalidInitStorageValue( + value_name, + "expected an atomic value, got a word".into(), + )); + }, } } @@ -718,22 +735,23 @@ impl ValueSlotSchema { fn collect_init_value_requirements( &self, - slot_prefix: StorageValueName, + value_name: StorageValueName, requirements: &mut BTreeMap, ) -> Result<(), AccountComponentTemplateError> { self.word.collect_init_value_requirements( - slot_prefix, + value_name, self.description.clone(), requirements, ) } + /// Builds a [Word] from the provided initialization data using the inner word schema. pub fn try_build_word( &self, init_storage_data: &InitStorageData, - value_prefix: StorageValueName, + slot_name: &StorageSlotName, ) -> Result { - self.word.try_build_word(init_storage_data, value_prefix) + self.word.try_build_word(init_storage_data, slot_name) } pub(crate) fn validate( @@ -788,49 +806,40 @@ impl MapSlotSchema { self.description.as_ref() } + /// Builds a [`StorageMap`] from the provided initialization data. + /// + /// Merges any default values with entries from the init data, validating that the data + /// contains map entries (not a direct value or field entries). pub fn try_build_map( &self, init_storage_data: &InitStorageData, - slot_prefix: StorageValueName, + slot_name: &StorageSlotName, ) -> Result { let mut entries = self.default_values.clone().unwrap_or_default(); + let slot_prefix = StorageValueName::from_slot_name(slot_name); - if init_storage_data.get(&slot_prefix).is_some() - && init_storage_data.map_entries(&slot_prefix).is_none() - { + if init_storage_data.slot_value_entry(slot_name).is_some() { return Err(AccountComponentTemplateError::InvalidInitStorageValue( slot_prefix, "expected a map, got a value".into(), )); } - - if let Some(init_entries) = init_storage_data.map_entries(&slot_prefix) { + if init_storage_data.has_field_entries_for_slot(slot_name) { + return Err(AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix, + "expected a map, got field entries".into(), + )); + } + if let Some(init_entries) = init_storage_data.map_entries(slot_name) { let mut parsed_entries = Vec::with_capacity(init_entries.len()); - for (index, (raw_key, raw_value)) in init_entries.iter().enumerate() { - let key_label = format!("map entry[{index}].key"); - let value_label = format!("map entry[{index}].value"); - - let key = parse_word_value_with_schema( - &self.key_schema, - raw_key, - &slot_prefix, - key_label.as_str(), - )?; - let value = parse_word_value_with_schema( - &self.value_schema, - raw_value, - &slot_prefix, - value_label.as_str(), - )?; + for (raw_key, raw_value) in init_entries.iter() { + let key = parse_storage_value_with_schema(&self.key_schema, raw_key, &slot_prefix)?; + let value = + parse_storage_value_with_schema(&self.value_schema, raw_value, &slot_prefix)?; parsed_entries.push((key, value)); } - // Reject duplicate keys in init-provided entries. - let _ = StorageMap::with_entries(parsed_entries.iter().copied()).map_err(|err| { - AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err)) - })?; - for (key, value) in parsed_entries.iter() { entries.insert(*key, *value); } @@ -863,68 +872,86 @@ impl MapSlotSchema { } } -pub(super) fn parse_word_value_with_schema( +pub(super) fn parse_storage_value_with_schema( schema: &WordSchema, raw_value: &WordValue, slot_prefix: &StorageValueName, - label: &str, ) -> Result { - match schema { - WordSchema::Simple { r#type, .. } => match raw_value { - WordValue::Atomic(value) => { - SCHEMA_TYPE_REGISTRY.try_parse_word(r#type, value).map_err(|err| { + let word = match (schema, raw_value) { + (_, WordValue::FullyTyped(word)) => *word, + (WordSchema::Simple { r#type, .. }, raw_value) => { + parse_simple_word_value(r#type, raw_value, slot_prefix)? + }, + (WordSchema::Composite { value }, WordValue::Elements(elements)) => { + parse_composite_elements(value, elements, slot_prefix)? + }, + (WordSchema::Composite { .. }, WordValue::Atomic(value)) => SCHEMA_TYPE_REGISTRY + .try_parse_word(&SchemaTypeId::native_word(), value) + .map_err(|err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("failed to parse value as `word`: {err}"), + ) + })?, + }; + + schema.validate_word_value(slot_prefix, "value", word)?; + Ok(word) +} + +fn parse_simple_word_value( + schema_type: &SchemaTypeId, + raw_value: &WordValue, + slot_prefix: &StorageValueName, +) -> Result { + match raw_value { + WordValue::Atomic(value) => { + SCHEMA_TYPE_REGISTRY.try_parse_word(schema_type, value).map_err(|err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("failed to parse value as `{}`: {err}", schema_type), + ) + }) + }, + WordValue::Elements(elements) => { + let felts: Vec = elements + .iter() + .map(|element| { + SCHEMA_TYPE_REGISTRY.try_parse_felt(&SchemaTypeId::native_felt(), element) + }) + .collect::>() + .map_err(|err| { AccountComponentTemplateError::InvalidInitStorageValue( slot_prefix.clone(), - format!("failed to parse {label} as `{}`: {err}", r#type), + format!("failed to parse value element as `felt`: {err}"), ) - }) - }, - WordValue::Elements(elements) => { - let felts: Vec = elements - .iter() - .map(|element| { - SCHEMA_TYPE_REGISTRY.try_parse_felt(&SchemaTypeId::native_felt(), element) - }) - .collect::>() - .map_err(|err| { - AccountComponentTemplateError::InvalidInitStorageValue( - slot_prefix.clone(), - format!("failed to parse {label} element as `felt`: {err}"), - ) - })?; - let felts: [Felt; 4] = felts.try_into().expect("length is 4"); - let word = Word::from(felts); - schema.validate_word_value(slot_prefix, label, word)?; - Ok(word) - }, + })?; + let felts: [Felt; 4] = felts.try_into().expect("length is 4"); + Ok(Word::from(felts)) }, - WordSchema::Composite { value } => match raw_value { - WordValue::Elements(elements) => { - let mut felts = [Felt::ZERO; 4]; - for index in 0..4 { - let felt_type = value[index].felt_type(); - felts[index] = SCHEMA_TYPE_REGISTRY - .try_parse_felt(&felt_type, &elements[index]) - .map_err(|err| { - AccountComponentTemplateError::InvalidInitStorageValue( - slot_prefix.clone(), - format!("failed to parse {label}[{index}] as `{felt_type}`: {err}"), - ) - })?; - } + WordValue::FullyTyped(word) => Ok(*word), + } +} - Ok(Word::from(felts)) - }, - WordValue::Atomic(value) => { - Err(AccountComponentTemplateError::InvalidInitStorageValue( - slot_prefix.clone(), - format!( - "{label} must be an array of 4 elements for a composite schema, got atomic `{value}`" - ), - )) - }, - }, +fn parse_composite_elements( + schema: &[FeltSchema; 4], + elements: &[String; 4], + slot_prefix: &StorageValueName, +) -> Result { + let mut felts = [Felt::ZERO; 4]; + for (index, felt_schema) in schema.iter().enumerate() { + let felt_type = felt_schema.felt_type(); + felts[index] = + SCHEMA_TYPE_REGISTRY + .try_parse_felt(&felt_type, &elements[index]) + .map_err(|err| { + AccountComponentTemplateError::InvalidInitStorageValue( + slot_prefix.clone(), + format!("failed to parse value[{index}] as `{felt_type}`: {err}"), + ) + })?; } + Ok(Word::from(felts)) } impl Serializable for MapSlotSchema { @@ -1028,24 +1055,27 @@ mod tests { #[test] fn value_slot_schema_accepts_typed_word_init_value() { let slot = ValueSlotSchema::new(None, WordSchema::new_simple(SchemaTypeId::native_word())); - let slot_prefix: StorageValueName = "demo::slot".parse().unwrap(); + let slot_name: StorageSlotName = "demo::slot".parse().unwrap(); let expected = Word::from([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]); - let init_data = - InitStorageData::new([(slot_prefix.clone(), expected.to_string().into())], []); + let mut init_data = InitStorageData::default(); + init_data + .set_value(StorageValueName::from_slot_name(&slot_name), expected) + .unwrap(); - let built = slot.try_build_word(&init_data, slot_prefix).unwrap(); + let built = slot.try_build_word(&init_data, &slot_name).unwrap(); assert_eq!(built, expected); } #[test] fn value_slot_schema_accepts_felt_typed_word_init_value() { let slot = ValueSlotSchema::new(None, WordSchema::new_simple(SchemaTypeId::u8())); - let slot_prefix: StorageValueName = "demo::u8_word".parse().unwrap(); + let slot_name: StorageSlotName = "demo::u8_word".parse().unwrap(); - let init_data = InitStorageData::new([(slot_prefix.clone(), "6".into())], []); + let mut init_data = InitStorageData::default(); + init_data.set_value(StorageValueName::from_slot_name(&slot_name), "6").unwrap(); - let built = slot.try_build_word(&init_data, slot_prefix).unwrap(); + let built = slot.try_build_word(&init_data, &slot_name).unwrap(); assert_eq!(built, Word::from([Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(6)])); } @@ -1058,10 +1088,14 @@ mod tests { FeltSchema::new_typed_with_default(SchemaTypeId::native_felt(), "d", Felt::new(4)), ]); let slot = ValueSlotSchema::new(None, word); + let slot_name: StorageSlotName = "demo::slot".parse().unwrap(); - let init_data = InitStorageData::new([("demo::slot.a".parse().unwrap(), "1".into())], []); + let mut init_data = InitStorageData::default(); + init_data + .set_value(StorageValueName::from_slot_name_with_suffix(&slot_name, "a").unwrap(), "1") + .unwrap(); - let built = slot.try_build_word(&init_data, "demo::slot".parse().unwrap()).unwrap(); + let built = slot.try_build_word(&init_data, &slot_name).unwrap(); assert_eq!(built, Word::from([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])); } @@ -1069,15 +1103,16 @@ mod tests { fn map_slot_schema_accepts_typed_map_init_value() { let word_schema = WordSchema::new_simple(SchemaTypeId::native_word()); let slot = MapSlotSchema::new(None, None, word_schema.clone(), word_schema); - let slot_prefix: StorageValueName = "demo::map".parse().unwrap(); + let slot_name: StorageSlotName = "demo::map".parse().unwrap(); let entries = vec![( WordValue::Elements(["1".into(), "0".into(), "0".into(), "0".into()]), WordValue::Elements(["10".into(), "11".into(), "12".into(), "13".into()]), )]; - let init_data = InitStorageData::new([], [(slot_prefix.clone(), entries.clone())]); + let mut init_data = InitStorageData::default(); + init_data.set_map_values(slot_name.clone(), entries.clone()).unwrap(); - let built = slot.try_build_map(&init_data, slot_prefix).unwrap(); + let built = slot.try_build_map(&init_data, &slot_name).unwrap(); let expected = StorageMap::with_entries([( Word::from([Felt::new(1), Felt::new(0), Felt::new(0), Felt::new(0)]), Word::from([Felt::new(10), Felt::new(11), Felt::new(12), Felt::new(13)]), @@ -1091,7 +1126,7 @@ mod tests { let word_schema = WordSchema::new_simple(SchemaTypeId::native_word()); let slot = MapSlotSchema::new(None, None, word_schema.clone(), word_schema); let built = slot - .try_build_map(&InitStorageData::default(), "demo::map".parse().unwrap()) + .try_build_map(&InitStorageData::default(), &"demo::map".parse().unwrap()) .unwrap(); assert_eq!(built, StorageMap::new()); } diff --git a/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs index 72a4a79044..cf681dc852 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/init_storage_data.rs @@ -1,148 +1,93 @@ -use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; -use alloc::vec::Vec; use serde::Deserialize; use thiserror::Error; -use super::super::{InitStorageData, StorageValueName, StorageValueNameError, WordValue}; +use super::super::{ + InitStorageData, + InitStorageDataError as CoreInitStorageDataError, + StorageValueName, + StorageValueNameError, + WordValue, +}; use super::RawMapEntrySchema; impl InitStorageData { /// Creates an instance of [`InitStorageData`] from a TOML string. /// - /// This method parses the provided TOML and flattens nested tables into - /// dot‑separated keys using [`StorageValueName`] as keys. + /// # Supported formats /// - /// Atomic values must be strings (e.g. `"0x1234"`, `"16"`, `"BTC"`). + /// ```toml + /// # Value entry (string) + /// "slot::name" = "0x1234" /// - /// Arrays are supported for: - /// - storage map slots: an array of inline tables of the form `{ key = , value = - /// }`, - /// - word values: a 4-element array of field elements. + /// # Value entry (4-element word) + /// "slot::name" = ["0", "0", "0", "100"] /// - /// # Errors + /// # Nested table (flattened to slot::name.field) + /// ["slot::name"] + /// field = "value" /// - /// - If the TOML string fails to parse - /// - If duplicate keys are found after parsing - /// - If empty tables are found in the string - /// - If the TOML string includes unsupported arrays + /// # Map entries + /// "slot::map" = [ + /// { key = "0x01", value = "0x10" }, + /// ] + /// ``` pub fn from_toml(toml_str: &str) -> Result { - // TOML documents are always parsed as a root table. let table: toml::Table = toml::from_str(toml_str)?; - let mut value_entries = BTreeMap::new(); - let mut map_entries = BTreeMap::new(); - // Start at the root (no prefix yet). - Self::flatten_parse_toml_value( - None, - toml::Value::Table(table), - &mut value_entries, - &mut map_entries, - )?; + let mut data = InitStorageData::default(); - Ok(InitStorageData::new(value_entries, map_entries)) - } + for (key, value) in table { + let name: StorageValueName = + key.parse().map_err(InitStorageDataError::InvalidStorageValueName)?; - /// Recursively flattens a TOML `Value` into a flat mapping. - /// - /// When recursing into nested tables, keys are combined using - /// [`StorageValueName::with_suffix`]. If an encountered table is empty (and not the top-level), - /// an error is returned. - fn flatten_parse_toml_value( - prefix: Option, - value: toml::Value, - value_entries: &mut BTreeMap, - map_entries: &mut BTreeMap>, - ) -> Result<(), InitStorageDataError> { - match value { - toml::Value::Table(table) => { - // If this is not the root and the table is empty, error - if let Some(prefix) = prefix.as_ref() - && table.is_empty() - { - return Err(InitStorageDataError::EmptyTable(prefix.to_string())); - } - for (key, val) in table { - let new_prefix = match prefix.as_ref() { - None => { - key.parse().map_err(InitStorageDataError::InvalidStorageValueName)? - }, - Some(prefix) => prefix - .clone() - .with_suffix(&key) - .map_err(InitStorageDataError::InvalidStorageValueName)?, - }; - Self::flatten_parse_toml_value( - Some(new_prefix), - val, - value_entries, - map_entries, - )?; - } - }, - toml::Value::Array(items) if items.is_empty() => { - let prefix = prefix.expect("arrays must have a key prefix"); - if value_entries.contains_key(&prefix) || map_entries.contains_key(&prefix) { - return Err(InitStorageDataError::DuplicateKey(prefix.to_string())); - } - map_entries.insert(prefix, Vec::new()); - }, - toml::Value::Array(items) => { - let prefix = prefix.expect("arrays must have a key prefix"); - // Arrays can be either: - // - map entries: an array of inline tables `{ key = ..., value = ... }` - // - a 4-element word value: an array of 4 field elements - if items.iter().all(|item| matches!(item, toml::Value::Table(_))) { - let entries = items.into_iter().map(parse_map_entry_value).collect::, - _, - >>( - )?; - if value_entries.contains_key(&prefix) || map_entries.contains_key(&prefix) { - return Err(InitStorageDataError::DuplicateKey(prefix.to_string())); + match value { + // ["slot::name"] + // field = "value" + toml::Value::Table(nested) => { + if nested.is_empty() { + return Err(InitStorageDataError::EmptyTable(name.to_string())); + } + if name.field_name().is_some() { + return Err(InitStorageDataError::ExcessiveNesting(name.to_string())); } - map_entries.insert(prefix, entries); - } else if items.len() == 4 - && items.iter().all(|item| matches!(item, toml::Value::String(_))) + for (field, field_value) in nested { + let field_name = + StorageValueName::from_slot_name_with_suffix(name.slot_name(), &field) + .map_err(InitStorageDataError::InvalidStorageValueName)?; + let word = WordValue::deserialize(field_value).map_err(|_| { + InitStorageDataError::InvalidValue(field_name.to_string()) + })?; + data.insert_value(field_name, word)?; + } + }, + // "slot::name" = [{ key = "...", value = "..." }, ...] + toml::Value::Array(items) + if items.iter().all(|v| matches!(v, toml::Value::Table(_))) => { - let elements: [String; 4] = items - .into_iter() - .map(|value| match value { - toml::Value::String(s) => Ok(s), - _ => Err(InitStorageDataError::ArraysNotSupported { - key: prefix.to_string(), - len: 4, - }), - }) - .collect::, _>>()? - .try_into() - .expect("length was checked above"); - if value_entries.contains_key(&prefix) || map_entries.contains_key(&prefix) { - return Err(InitStorageDataError::DuplicateKey(prefix.to_string())); + if name.field_name().is_some() { + return Err(InitStorageDataError::InvalidMapEntryKey(name.to_string())); } - value_entries.insert(prefix, WordValue::Elements(elements)); - } else { - return Err(InitStorageDataError::ArraysNotSupported { - key: prefix.to_string(), - len: items.len(), - }); - } - }, - toml_value => match toml_value { - toml::Value::String(s) => { - let prefix = prefix.expect("atomic values must have a key prefix"); - if value_entries.contains_key(&prefix) || map_entries.contains_key(&prefix) { - return Err(InitStorageDataError::DuplicateKey(prefix.to_string())); + for item in items { + // Try deserializing as map entry + let entry: RawMapEntrySchema = RawMapEntrySchema::deserialize(item) + .map_err(|e| { + InitStorageDataError::InvalidMapEntrySchema(e.to_string()) + })?; + + data.insert_map_entry(name.slot_name().clone(), entry.key, entry.value)?; } - value_entries.insert(prefix, WordValue::Atomic(s)); }, - _ => { - let prefix = prefix.expect("atomic values must have a key prefix"); - return Err(InitStorageDataError::NonStringAtomic(prefix.to_string())); + // "slot::name" = "value" or "slot::name" = ["a", "b", "c", "d"] + other => { + let word = WordValue::deserialize(other) + .map_err(|_| InitStorageDataError::InvalidValue(name.to_string()))?; + data.insert_value(name, word)?; }, - }, + } } - Ok(()) + + Ok(data) } } @@ -154,16 +99,19 @@ pub enum InitStorageDataError { #[error("empty table encountered for key `{0}`")] EmptyTable(String), - #[error("duplicate init key `{0}`")] - DuplicateKey(String), + #[error(transparent)] + InvalidData(#[from] CoreInitStorageDataError), + + #[error("invalid map entry key `{0}`: map entries must target a slot name")] + InvalidMapEntryKey(String), + + #[error("excessive nesting for key `{0}`: only one level of table nesting is allowed")] + ExcessiveNesting(String), #[error( - "invalid input for `{key}`: unsupported array value (length {len}); expected either a map entry list (array of inline tables with `key` and `value`) or a 4-element word array of strings" + "invalid input for `{0}`: expected a string, a 4-element string array, or a map entry list" )] - ArraysNotSupported { key: String, len: usize }, - - #[error("invalid input for `{0}`: init values must be strings")] - NonStringAtomic(String), + InvalidValue(String), #[error("invalid storage value name")] InvalidStorageValueName(#[source] StorageValueNameError), @@ -171,14 +119,3 @@ pub enum InitStorageDataError { #[error("invalid map entry: {0}")] InvalidMapEntrySchema(String), } - -/// Parses a `{ key, value }` table into a `(Word, Word)` pair, rejecting typed fields. -fn parse_map_entry_value( - item: toml::Value, -) -> Result<(WordValue, WordValue), InitStorageDataError> { - // Try to deserialize the user input as a map entry - let entry: RawMapEntrySchema = RawMapEntrySchema::deserialize(item) - .map_err(|err| InitStorageDataError::InvalidMapEntrySchema(err.to_string()))?; - - Ok((entry.key, entry.value)) -} diff --git a/crates/miden-protocol/src/account/component/storage/toml/mod.rs b/crates/miden-protocol/src/account/component/storage/toml/mod.rs index 1269e7db8b..a5e679959f 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/mod.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/mod.rs @@ -432,7 +432,7 @@ impl RawStorageSlotSchema { let mut map = BTreeMap::new(); let parse = |schema: &WordSchema, raw: &WordValue, label: &str| { - super::schema::parse_word_value_with_schema(schema, raw, slot_prefix, label).map_err( + super::schema::parse_storage_value_with_schema(schema, raw, slot_prefix).map_err( |err| { AccountComponentTemplateError::InvalidSchema(format!( "invalid map `{label}`: {err}" @@ -467,6 +467,7 @@ impl WordValue { label: &str, ) -> Result { let word = match self { + WordValue::FullyTyped(word) => *word, WordValue::Atomic(value) => SCHEMA_TYPE_REGISTRY .try_parse_word(schema_type, value) .map_err(AccountComponentTemplateError::StorageValueParsingError)?, diff --git a/crates/miden-protocol/src/account/component/storage/toml/serde_impls.rs b/crates/miden-protocol/src/account/component/storage/toml/serde_impls.rs index 3dfb29551f..3dc8f9033c 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/serde_impls.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/serde_impls.rs @@ -1,11 +1,11 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use serde::de::Error as _; use serde::ser::{Error as SerError, SerializeStruct}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::super::type_registry::SCHEMA_TYPE_REGISTRY; -use super::super::{FeltSchema, SchemaTypeId}; +use super::super::{FeltSchema, SchemaTypeId, WordValue}; // FELT SCHEMA SERIALIZATION // ================================================================================================ @@ -120,3 +120,38 @@ impl<'de> Deserialize<'de> for FeltSchema { }) } } + +// WORD VALUE SERIALIZATION +// ================================================================================================ + +impl Serialize for WordValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + WordValue::Atomic(value) => serializer.serialize_str(value), + WordValue::Elements(elements) => elements.serialize(serializer), + WordValue::FullyTyped(word) => serializer.serialize_str(&word.to_string()), + } + } +} + +impl<'de> Deserialize<'de> for WordValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(untagged)] + enum RawWordValue { + Atomic(String), + Elements([String; 4]), + } + + match RawWordValue::deserialize(deserializer)? { + RawWordValue::Atomic(value) => Ok(WordValue::Atomic(value)), + RawWordValue::Elements(elements) => Ok(WordValue::Elements(elements)), + } + } +} diff --git a/crates/miden-protocol/src/account/component/storage/toml/tests.rs b/crates/miden-protocol/src/account/component/storage/toml/tests.rs index 419992673e..627b783611 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/tests.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/tests.rs @@ -7,9 +7,11 @@ use crate::account::component::toml::init_storage_data::InitStorageDataError; use crate::account::component::{ AccountComponentMetadata, InitStorageData, + InitStorageDataError as CoreInitStorageDataError, SchemaTypeId, StorageSlotSchema, StorageValueName, + StorageValueNameError, WordSchema, WordValue, }; @@ -36,6 +38,43 @@ fn from_toml_str_with_nested_table_and_flattened() { let storage_inline = InitStorageData::from_toml(toml_inline).unwrap(); assert_eq!(storage_table.values(), storage_inline.values()); + assert_eq!(storage_table.maps(), storage_inline.maps()); +} + +#[test] +fn empty_table_is_rejected() { + let toml_str = r#" + ["demo::empty_table"] + + ["demo::valid_table"] + value = "42" + "#; + + assert_matches::assert_matches!( + InitStorageData::from_toml(toml_str), + Err(InitStorageDataError::EmptyTable(key)) if key == "demo::empty_table" + ); +} + +#[test] +fn invalid_storage_value_name_is_rejected() { + // Nested table fields are flattened to `slot.field` and thus must be valid field segments. + let toml_str = r#" + ["demo::valid_token_metadata"] + max_supply = "1000000000" + + "demo::another_valid_token_metadata.supply" = "1000000000" + + ["demo::invalid_token_metadata"] + "bad.field" = "42" + "#; + + assert_matches::assert_matches!( + InitStorageData::from_toml(toml_str), + Err(InitStorageDataError::InvalidStorageValueName( + StorageValueNameError::InvalidCharacter { part, character } + )) if part == "bad.field" && character == '.' + ); } #[test] @@ -48,7 +87,20 @@ fn from_toml_str_with_deeply_nested_tables_is_rejected() { assert_matches::assert_matches!( InitStorageData::from_toml(toml_str), - Err(InitStorageDataError::InvalidStorageValueName(_)) + Err(InitStorageDataError::InvalidValue(_)) + ); +} + +#[test] +fn from_toml_str_excessive_key_nesting_rejected() { + let toml_str = r#" + ["demo::token_metadata.nested"] + value = "42" + "#; + + assert_matches::assert_matches!( + InitStorageData::from_toml(toml_str), + Err(InitStorageDataError::ExcessiveNesting(_)) ); } @@ -59,22 +111,17 @@ fn from_toml_rejects_non_string_atomics() { "#; let result = InitStorageData::from_toml(toml_str); - assert_matches::assert_matches!(result.unwrap_err(), InitStorageDataError::NonStringAtomic(_)); + assert_matches::assert_matches!(result.unwrap_err(), InitStorageDataError::InvalidValue(_)); } #[test] fn test_error_on_array() { let toml_str = r#" - ["demo::token_metadata"] - v = ["1", "2", "3", "4", "5"] + "demo::token_metadata.v" = ["1", "2", "3", "4", "5"] "#; let err = InitStorageData::from_toml(toml_str).unwrap_err(); - assert_matches::assert_matches!( - &err, - InitStorageDataError::ArraysNotSupported { key, len } - if key == "demo::token_metadata.v" && *len == 5 - ); + assert_matches::assert_matches!(&err, InitStorageDataError::InvalidValue(key) if key == "demo::token_metadata.v"); } #[test] @@ -87,7 +134,7 @@ fn parse_map_entries_from_array() { "#; let storage = InitStorageData::from_toml(toml_str).expect("Failed to parse map entries"); - let map_name: StorageValueName = "demo::my_map".parse().unwrap(); + let map_name: StorageSlotName = "demo::my_map".parse().unwrap(); let entries = storage.map_entries(&map_name).expect("map entries missing"); assert_eq!(entries.len(), 2); @@ -101,12 +148,45 @@ fn parse_map_entries_from_array() { WordValue::Atomic(v) if v == "0x0000000000000000000000000000000000000000000000000000000000000010" ); - assert_matches::assert_matches!(&entries[1].1, WordValue::Elements(elements) if elements == &[ - "1".to_string(), - "2".to_string(), - "3".to_string(), - "4".to_string(), - ]); + assert_matches::assert_matches!( + &entries[1].1, + WordValue::Elements(elements) + if elements == &[ + "1".to_string(), + "2".to_string(), + "3".to_string(), + "4".to_string(), + ] + ); +} + +#[test] +fn map_entries_reject_field_key() { + let toml_str = r#" + "demo::my_map.entry" = [ + { key = "0x1", value = "0x2" } + ] + "#; + + assert_matches::assert_matches!( + InitStorageData::from_toml(toml_str), + Err(InitStorageDataError::InvalidMapEntryKey(_)) + ); +} + +#[test] +fn map_entries_reject_invalid_schema() { + // Missing required `value` field in the entry table should fail schema deserialization. + let toml_str = r#" + "demo::my_map" = [ + { key = "0x1" } + ] + "#; + + assert_matches::assert_matches!( + InitStorageData::from_toml(toml_str), + Err(InitStorageDataError::InvalidMapEntrySchema(_)) + ); } #[test] @@ -119,7 +199,7 @@ fn error_on_empty_subtable() { let result = InitStorageData::from_toml(toml_str); assert_matches::assert_matches!( result.unwrap_err(), - InitStorageDataError::EmptyTable(key) if key == "demo::token_metadata.max_supply" + InitStorageDataError::InvalidValue(key) if key == "demo::token_metadata.max_supply" ); } @@ -149,7 +229,8 @@ fn error_on_duplicate_keys_after_flattening() { let err = InitStorageData::from_toml(toml_str).unwrap_err(); assert_matches::assert_matches!( err, - InitStorageDataError::DuplicateKey(key) if key == "demo::token_metadata.max_supply" + InitStorageDataError::InvalidData(CoreInitStorageDataError::DuplicateKey(key)) + if key == "demo::token_metadata.max_supply" ); } @@ -200,29 +281,6 @@ fn metadata_from_toml_rejects_typed_fields_in_static_map_values() { ); } -#[test] -fn metadata_from_toml_rejects_short_composite_schema() { - let toml_str = r#" - name = "Test Component" - description = "Test description" - version = "0.1.0" - supported-types = [] - - [[storage.slots]] - name = "demo::short_composite" - type = [ - { type = "u8", name = "a" }, - { type = "void" }, - { type = "void" }, - ] - "#; - assert_matches::assert_matches!( - AccountComponentMetadata::from_toml(toml_str), - Err(AccountComponentTemplateError::InvalidSchema(msg)) - if msg.contains("array of 4 elements") - ); -} - #[test] fn metadata_from_toml_rejects_reserved_slot_names() { let reserved_slot = AccountStorage::faucet_sysdata_slot().as_str(); @@ -621,7 +679,7 @@ fn extensive_schema_metadata_and_init_toml_example() { "#; let init_with_overrides = InitStorageData::from_toml(init_toml_with_overrides).unwrap(); let parsed_entries = init_with_overrides - .map_entries(&"demo::typed_map_new".parse::().unwrap()) + .map_entries(&"demo::typed_map_new".parse::().unwrap()) .expect("demo::typed_map_new map entries missing"); assert_eq!(parsed_entries.len(), 2); let slots_with_maps = diff --git a/crates/miden-protocol/src/account/component/storage/value_name.rs b/crates/miden-protocol/src/account/component/storage/value_name.rs index bb27c3764a..302916d9de 100644 --- a/crates/miden-protocol/src/account/component/storage/value_name.rs +++ b/crates/miden-protocol/src/account/component/storage/value_name.rs @@ -14,6 +14,9 @@ use crate::errors::StorageSlotNameError; /// /// A storage value name is a string that identifies values supplied during component /// instantiation (via [`InitStorageData`](super::InitStorageData)). +/// +/// Each name is either a storage slot name, or a storage slot name with a suffixed identifier for +/// composite types (where the suffix identifies the inner type). #[derive(Clone, Debug)] #[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))] #[cfg_attr(feature = "std", serde(try_from = "String", into = "String"))] @@ -31,23 +34,30 @@ impl StorageValueName { } } - /// Adds a field-name suffix to a slot-name key, separated by a period, that identifies a - /// specific element (e.g., "basic_faucet::metadata.decimals") - pub fn with_suffix(self, suffix: &str) -> Result { - let mut key = self; + /// Creates a [`StorageValueName`] for the given storage slot and field suffix. + /// + /// A suffixed slot name is used to identify a specific field element's type in a schema + /// (e.g., `miden::contracts::fungible_faucets::token_metadata.max_supply` can specify the + /// `max_supply` element in the `token_metadata` storage slot) + pub fn from_slot_name_with_suffix( + slot_name: &StorageSlotName, + suffix: &str, + ) -> Result { + Self::validate_field_segment(suffix)?; + Ok(StorageValueName { + slot_name: slot_name.clone(), + element_field: Some(suffix.to_string()), + }) + } - // `StorageValueName` keys are either `slot` or `slot.field`. Appending to a key that is - // already suffixed is create an invalid name. - if key.element_field.is_some() { - return Err(StorageValueNameError::InvalidCharacter { - part: key.to_string(), - character: '.', - }); - } + /// Returns the storage slot name prefix of this value name. + pub fn slot_name(&self) -> &StorageSlotName { + &self.slot_name + } - Self::validate_field_segment(suffix)?; - key.element_field = Some(suffix.to_string()); - Ok(key) + /// Returns the optional field suffix of this value name. + pub fn field_name(&self) -> Option<&str> { + self.element_field.as_deref() } fn validate_field_segment(segment: &str) -> Result<(), StorageValueNameError> { @@ -191,7 +201,7 @@ impl Deserializable for StorageValueName { #[derive(Debug, Error)] pub enum StorageValueNameError { - #[error("key segment is empty")] + #[error("key suffix is empty")] EmptySuffix, #[error("key segment '{part}' contains invalid character '{character}'")] InvalidCharacter { part: String, character: char }, From e071df3b245ca34e82b2da7c873af78f7caddd6a Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 16 Jan 2026 19:23:25 +0100 Subject: [PATCH 108/114] chore: convert to `NetworkAccountTarget` from `&NoteAttachment` (#2289) --- crates/miden-standards/src/note/network_account_target.rs | 6 +++--- .../miden-testing/src/kernel_tests/tx/test_output_note.rs | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/miden-standards/src/note/network_account_target.rs b/crates/miden-standards/src/note/network_account_target.rs index 28bb22944f..b5f82e460f 100644 --- a/crates/miden-standards/src/note/network_account_target.rs +++ b/crates/miden-standards/src/note/network_account_target.rs @@ -84,10 +84,10 @@ impl From for NoteAttachment { } } -impl TryFrom for NetworkAccountTarget { +impl TryFrom<&NoteAttachment> for NetworkAccountTarget { type Error = NetworkAccountTargetError; - fn try_from(attachment: NoteAttachment) -> Result { + fn try_from(attachment: &NoteAttachment) -> Result { if attachment.attachment_scheme() != Self::ATTACHMENT_SCHEME { return Err(NetworkAccountTargetError::AttachmentSchemeMismatch( attachment.attachment_scheme(), @@ -157,7 +157,7 @@ mod tests { let network_account_target = NetworkAccountTarget::new(id, NoteExecutionHint::Always)?; assert_eq!( network_account_target, - NetworkAccountTarget::try_from(NoteAttachment::from(network_account_target))? + NetworkAccountTarget::try_from(&NoteAttachment::from(network_account_target))? ); Ok(()) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index 88ad6b922b..262301743c 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -1194,8 +1194,7 @@ async fn test_set_network_target_account_attachment() -> anyhow::Result<()> { assert_eq!(actual_note.assets().unwrap(), output_note.assets()); // Make sure we can deserialize the attachment back into its original type. - let actual_attachment = - NetworkAccountTarget::try_from(actual_note.metadata().attachment().clone())?; + let actual_attachment = NetworkAccountTarget::try_from(actual_note.metadata().attachment())?; assert_eq!(actual_attachment, attachment); Ok(()) From 27c2a99e137ce6f99dae0e71d5ed3c370f2ffc6d Mon Sep 17 00:00:00 2001 From: igamigo Date: Fri, 16 Jan 2026 15:46:46 -0300 Subject: [PATCH 109/114] feat: `StorageSchema::commitment` (#2244) --- CHANGELOG.md | 3 + .../src/account/component/metadata/mod.rs | 36 ++-- .../src/account/component/mod.rs | 4 +- .../component/storage/init_storage_data.rs | 21 +- .../src/account/component/storage/schema.rs | 182 ++++++++++++++---- .../src/account/component/storage/toml/mod.rs | 16 +- .../account/component/storage/toml/tests.rs | 160 ++++++++++++++- 7 files changed, 353 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3369c0e5b7..49064ae538 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ - [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Removed OLD_MAP_ROOT from being returned when calling [`native_account::set_map_item`](crates/miden-lib/asm/miden/native_account.masm) ([#2194](https://github.com/0xMiden/miden-base/pull/2194)). +- [BREAKING] Refactored account component templates into `StorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). +- [BREAKING] Refactored `InitStorageData` to support native types ([#2230](https://github.com/0xMiden/miden-base/pull/2230)). +- Added `StorageSchema::commitment()` ([#2244](https://github.com/0xMiden/miden-base/pull/2244)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). - [BREAKING] Refactor note tags to be arbitrary `u32` values and drop previous validation ([#2219](https://github.com/0xMiden/miden-base/pull/2219)). - [BREAKING] Refactored `InitStorageData` to support native types ([#2230](https://github.com/0xMiden/miden-base/pull/2230)). diff --git a/crates/miden-protocol/src/account/component/metadata/mod.rs b/crates/miden-protocol/src/account/component/metadata/mod.rs index db68b27b0a..1d8662dc13 100644 --- a/crates/miden-protocol/src/account/component/metadata/mod.rs +++ b/crates/miden-protocol/src/account/component/metadata/mod.rs @@ -7,7 +7,7 @@ use miden_mast_package::{Package, SectionId}; use miden_processor::DeserializationError; use semver::Version; -use super::{AccountStorageSchema, AccountType, SchemaRequirement, StorageValueName}; +use super::{AccountType, SchemaRequirement, StorageSchema, StorageValueName}; use crate::AccountError; // ACCOUNT COMPONENT METADATA @@ -40,10 +40,10 @@ use crate::AccountError; /// use miden_protocol::account::StorageSlotName; /// use miden_protocol::account::component::{ /// AccountComponentMetadata, -/// AccountStorageSchema, /// FeltSchema, /// InitStorageData, /// SchemaTypeId, +/// StorageSchema, /// StorageSlotSchema, /// StorageValueName, /// ValueSlotSchema, @@ -61,7 +61,7 @@ use crate::AccountError; /// FeltSchema::new_typed(SchemaTypeId::native_felt(), "foo"), /// ]); /// -/// let storage_schema = AccountStorageSchema::new([( +/// let storage_schema = StorageSchema::new([( /// slot_name.clone(), /// StorageSlotSchema::Value(ValueSlotSchema::new(Some("demo slot".into()), word)), /// )])?; @@ -102,7 +102,7 @@ pub struct AccountComponentMetadata { /// Storage schema defining the component's storage layout, defaults, and init-supplied values. #[cfg_attr(feature = "std", serde(rename = "storage"))] - storage_schema: AccountStorageSchema, + storage_schema: StorageSchema, } impl AccountComponentMetadata { @@ -112,7 +112,7 @@ impl AccountComponentMetadata { description: String, version: Version, targets: BTreeSet, - storage_schema: AccountStorageSchema, + storage_schema: StorageSchema, ) -> Self { Self { name, @@ -154,7 +154,7 @@ impl AccountComponentMetadata { } /// Returns the storage schema of the component. - pub fn storage_schema(&self) -> &AccountStorageSchema { + pub fn storage_schema(&self) -> &StorageSchema { &self.storage_schema } } @@ -200,14 +200,24 @@ impl Serializable for AccountComponentMetadata { impl Deserializable for AccountComponentMetadata { fn read_from(source: &mut R) -> Result { + let name = String::read_from(source)?; + let description = String::read_from(source)?; + if !description.is_ascii() { + return Err(DeserializationError::InvalidValue( + "description must contain only ASCII characters".to_string(), + )); + } + let version = semver::Version::from_str(&String::read_from(source)?) + .map_err(|err: semver::Error| DeserializationError::InvalidValue(err.to_string()))?; + let supported_types = BTreeSet::::read_from(source)?; + let storage_schema = StorageSchema::read_from(source)?; + Ok(Self { - name: String::read_from(source)?, - description: String::read_from(source)?, - version: semver::Version::from_str(&String::read_from(source)?).map_err( - |err: semver::Error| DeserializationError::InvalidValue(err.to_string()), - )?, - supported_types: BTreeSet::::read_from(source)?, - storage_schema: AccountStorageSchema::read_from(source)?, + name, + description, + version, + supported_types, + storage_schema, }) } } diff --git a/crates/miden-protocol/src/account/component/mod.rs b/crates/miden-protocol/src/account/component/mod.rs index 9f709b28d4..57e62bf284 100644 --- a/crates/miden-protocol/src/account/component/mod.rs +++ b/crates/miden-protocol/src/account/component/mod.rs @@ -271,7 +271,7 @@ mod tests { "A test component".to_string(), Version::new(1, 0, 0), BTreeSet::from_iter([AccountType::RegularAccountImmutableCode]), - AccountStorageSchema::default(), + StorageSchema::default(), ); let metadata_bytes = metadata.to_bytes(); @@ -329,7 +329,7 @@ mod tests { AccountType::RegularAccountImmutableCode, AccountType::RegularAccountUpdatableCode, ]), - AccountStorageSchema::default(), + StorageSchema::default(), ); // Test with empty init data - this tests the complete workflow: diff --git a/crates/miden-protocol/src/account/component/storage/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs index ff870dd329..a9df7e07e4 100644 --- a/crates/miden-protocol/src/account/component/storage/init_storage_data.rs +++ b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs @@ -23,6 +23,12 @@ pub enum WordValue { Elements([String; 4]), } +impl From for WordValue { + fn from(value: Word) -> Self { + WordValue::FullyTyped(value) + } +} + impl From for WordValue { fn from(value: String) -> Self { WordValue::Atomic(value) @@ -35,11 +41,8 @@ impl From<&str> for WordValue { } } -impl From for WordValue { - fn from(value: Word) -> Self { - WordValue::FullyTyped(value) - } -} +// CONVERSIONS +// ==================================================================================================== impl From for WordValue { /// Converts a [`Felt`] to a [`WordValue`] as a Word in the form `[0, 0, 0, felt]`. @@ -120,7 +123,7 @@ impl InitStorageData { /// - `[Felt; 4]`: converted to a Word /// - `Felt`: converted to `[0, 0, 0, felt]` /// - `String` or `&str`: a parseable string value - /// - `WordValue`: a raw or fully-typed word value + /// - `WordValue`: a word value (fully typed, atomic, or elements) pub fn insert_value( &mut self, name: StorageValueName, @@ -201,6 +204,12 @@ impl InitStorageData { self.map_entries.entry(slot_name).or_default().extend(entries); } } + + /// Merges another [`InitStorageData`] into this one, overwriting value entries and appending + /// map entries. + pub fn merge_from(&mut self, other: InitStorageData) { + self.merge_with(other); + } } // ERRORS diff --git a/crates/miden-protocol/src/account/component/storage/schema.rs b/crates/miden-protocol/src/account/component/storage/schema.rs index e8054ac482..710e70af94 100644 --- a/crates/miden-protocol/src/account/component/storage/schema.rs +++ b/crates/miden-protocol/src/account/component/storage/schema.rs @@ -10,20 +10,21 @@ use super::type_registry::{SCHEMA_TYPE_REGISTRY, SchemaRequirement, SchemaTypeId use super::{InitStorageData, StorageValueName, WordValue}; use crate::account::storage::is_reserved_slot_name; use crate::account::{StorageMap, StorageSlot, StorageSlotName}; +use crate::crypto::utils::bytes_to_elements_with_padding; use crate::errors::AccountComponentTemplateError; -use crate::{Felt, FieldElement, Word}; +use crate::{Felt, FieldElement, Hasher, Word}; // STORAGE SCHEMA // ================================================================================================ /// Describes the storage schema of an account component in terms of its named storage slots. #[derive(Debug, Clone, Default, PartialEq, Eq)] -pub struct AccountStorageSchema { +pub struct StorageSchema { slots: BTreeMap, } -impl AccountStorageSchema { - /// Creates a new [`AccountStorageSchema`]. +impl StorageSchema { + /// Creates a new [`StorageSchema`]. /// /// # Errors /// - If `fields` contains duplicate slot names. @@ -66,6 +67,16 @@ impl AccountStorageSchema { .collect() } + /// Returns a commitment to this storage schema definition. + /// + /// The commitment is computed over the serialized schema and does not include defaults. + pub fn commitment(&self) -> Word { + let mut bytes = Vec::new(); + self.write_into_with_optional_defaults(&mut bytes, false); + let elements = bytes_to_elements_with_padding(&bytes); + Hasher::hash_elements(&elements) + } + /// Returns init-value requirements for the entire schema. /// /// The returned map includes both required values (no `default_value`) and optional values @@ -80,6 +91,21 @@ impl AccountStorageSchema { Ok(requirements) } + /// Serializes the schema, optionally ignoring the default values (used for committing to a + /// schema definition). + fn write_into_with_optional_defaults( + &self, + target: &mut W, + include_defaults: bool, + ) { + target.write_u16(self.slots.len() as u16); + for (slot_name, schema) in self.slots.iter() { + target.write(slot_name); + schema.write_into_with_optional_defaults(target, include_defaults); + } + } + + /// Validates schema-level invariants across all slots. fn validate(&self) -> Result<(), AccountComponentTemplateError> { let mut init_values = BTreeMap::new(); @@ -88,7 +114,7 @@ impl AccountStorageSchema { return Err(AccountComponentTemplateError::ReservedSlotName(slot_name.clone())); } - schema.validate(slot_name)?; + schema.validate()?; schema.collect_init_value_requirements(slot_name, &mut init_values)?; } @@ -96,17 +122,13 @@ impl AccountStorageSchema { } } -impl Serializable for AccountStorageSchema { +impl Serializable for StorageSchema { fn write_into(&self, target: &mut W) { - target.write_u16(self.slots.len() as u16); - for (slot_name, schema) in self.slots.iter() { - target.write(slot_name); - target.write(schema); - } + self.write_into_with_optional_defaults(target, true); } } -impl Deserializable for AccountStorageSchema { +impl Deserializable for StorageSchema { fn read_from(source: &mut R) -> Result { let num_entries = source.read_u16()? as usize; let mut fields = BTreeMap::new(); @@ -122,12 +144,22 @@ impl Deserializable for AccountStorageSchema { } } - let schema = AccountStorageSchema::new(fields) + let schema = StorageSchema::new(fields) .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?; Ok(schema) } } +fn validate_description_ascii(description: &str) -> Result<(), AccountComponentTemplateError> { + if description.is_ascii() { + Ok(()) + } else { + Err(AccountComponentTemplateError::InvalidSchema( + "description must contain only ASCII characters".to_string(), + )) + } +} + // STORAGE SLOT SCHEMA // ================================================================================================ @@ -174,34 +206,42 @@ impl StorageSlotSchema { } } - pub(crate) fn validate( - &self, - slot_name: &StorageSlotName, - ) -> Result<(), AccountComponentTemplateError> { + /// Validates this slot schema's internal invariants. + pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> { match self { - StorageSlotSchema::Value(slot) => slot.validate(slot_name)?, + StorageSlotSchema::Value(slot) => slot.validate()?, StorageSlotSchema::Map(slot) => slot.validate()?, } Ok(()) } -} -impl Serializable for StorageSlotSchema { - fn write_into(&self, target: &mut W) { + /// Serializes the schema, optionally ignoring the default values (used for committing to a + /// schema definition). + fn write_into_with_optional_defaults( + &self, + target: &mut W, + include_defaults: bool, + ) { match self { StorageSlotSchema::Value(slot) => { target.write_u8(0u8); - slot.write_into(target); + slot.write_into_with_optional_defaults(target, include_defaults); }, StorageSlotSchema::Map(slot) => { target.write_u8(1u8); - slot.write_into(target); + slot.write_into_with_optional_defaults(target, include_defaults); }, } } } +impl Serializable for StorageSlotSchema { + fn write_into(&self, target: &mut W) { + self.write_into_with_optional_defaults(target, true); + } +} + impl Deserializable for StorageSlotSchema { fn read_from(source: &mut R) -> Result { let variant_tag = source.read_u8()?; @@ -306,7 +346,7 @@ impl WordSchema { } } - /// Validates that the defined word type exists and its inner felts (if any) are valid. + /// Validates the word schema type, defaults, and inner felts (if any). fn validate(&self) -> Result<(), AccountComponentTemplateError> { let type_exists = SCHEMA_TYPE_REGISTRY.contains_word_type(&self.word_type()); if !type_exists { @@ -429,24 +469,37 @@ impl WordSchema { }, } } -} -impl Serializable for WordSchema { - fn write_into(&self, target: &mut W) { + /// Serializes the schema, optionally ignoring the default values (used for committing to a + /// schema definition). + fn write_into_with_optional_defaults( + &self, + target: &mut W, + include_defaults: bool, + ) { match self { WordSchema::Simple { r#type, default_value } => { target.write_u8(0); target.write(r#type); + let default_value = if include_defaults { *default_value } else { None }; target.write(default_value); }, WordSchema::Composite { value } => { target.write_u8(1); - target.write(value); + for felt in value.iter() { + felt.write_into_with_optional_defaults(target, include_defaults); + } }, } } } +impl Serializable for WordSchema { + fn write_into(&self, target: &mut W) { + self.write_into_with_optional_defaults(target, true); + } +} + impl Deserializable for WordSchema { fn read_from(source: &mut R) -> Result { let tag = source.read_u8()?; @@ -655,8 +708,26 @@ impl FeltSchema { Err(AccountComponentTemplateError::InitValueNotProvided(value_name)) } - /// Validates that the defined felt type exists. + /// Serializes the schema, optionally ignoring the default values (used for committing to a + /// schema definition). + fn write_into_with_optional_defaults( + &self, + target: &mut W, + include_defaults: bool, + ) { + target.write(&self.name); + target.write(&self.description); + target.write(&self.r#type); + let default_value = if include_defaults { self.default_value } else { None }; + target.write(default_value); + } + + /// Validates the felt type, naming rules, and default value (if any). fn validate(&self) -> Result<(), AccountComponentTemplateError> { + if let Some(description) = self.description.as_deref() { + validate_description_ascii(description)?; + } + let type_exists = SCHEMA_TYPE_REGISTRY.contains_felt_type(&self.felt_type()); if !type_exists { return Err(AccountComponentTemplateError::InvalidType( @@ -696,10 +767,7 @@ impl FeltSchema { impl Serializable for FeltSchema { fn write_into(&self, target: &mut W) { - target.write(&self.name); - target.write(&self.description); - target.write(&self.r#type); - target.write(self.default_value); + self.write_into_with_optional_defaults(target, true); } } @@ -754,10 +822,22 @@ impl ValueSlotSchema { self.word.try_build_word(init_storage_data, slot_name) } - pub(crate) fn validate( + /// Serializes the schema, optionally ignoring the default values (used for committing to a + /// schema definition). + fn write_into_with_optional_defaults( &self, - _slot_name: &StorageSlotName, - ) -> Result<(), AccountComponentTemplateError> { + target: &mut W, + include_defaults: bool, + ) { + target.write(&self.description); + self.word.write_into_with_optional_defaults(target, include_defaults); + } + + /// Validates the slot's word schema. + pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> { + if let Some(description) = self.description.as_deref() { + validate_description_ascii(description)?; + } self.word.validate()?; Ok(()) } @@ -765,8 +845,7 @@ impl ValueSlotSchema { impl Serializable for ValueSlotSchema { fn write_into(&self, target: &mut W) { - target.write(&self.description); - target.write(&self.word); + self.write_into_with_optional_defaults(target, true); } } @@ -865,7 +944,29 @@ impl MapSlotSchema { self.default_values.clone() } + /// Serializes the schema, optionally ignoring the default values (used for committing to a + /// schema definition). + fn write_into_with_optional_defaults( + &self, + target: &mut W, + include_defaults: bool, + ) { + target.write(&self.description); + let default_values = if include_defaults { + self.default_values.clone() + } else { + None + }; + target.write(&default_values); + self.key_schema.write_into_with_optional_defaults(target, include_defaults); + self.value_schema.write_into_with_optional_defaults(target, include_defaults); + } + + /// Validates key/value word schemas for this map slot. fn validate(&self) -> Result<(), AccountComponentTemplateError> { + if let Some(description) = self.description.as_deref() { + validate_description_ascii(description)?; + } self.key_schema.validate()?; self.value_schema.validate()?; Ok(()) @@ -956,10 +1057,7 @@ fn parse_composite_elements( impl Serializable for MapSlotSchema { fn write_into(&self, target: &mut W) { - target.write(&self.description); - target.write(&self.default_values); - target.write(&self.key_schema); - target.write(&self.value_schema); + self.write_into_with_optional_defaults(target, true); } } diff --git a/crates/miden-protocol/src/account/component/storage/toml/mod.rs b/crates/miden-protocol/src/account/component/storage/toml/mod.rs index a5e679959f..3a9c137266 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/mod.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/mod.rs @@ -8,9 +8,9 @@ use serde::de::Error as _; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::super::{ - AccountStorageSchema, FeltSchema, MapSlotSchema, + StorageSchema, StorageSlotSchema, StorageValueName, ValueSlotSchema, @@ -55,6 +55,12 @@ impl AccountComponentMetadata { let raw: RawAccountComponentMetadata = toml::from_str(toml_string) .map_err(AccountComponentTemplateError::TomlDeserializationError)?; + if !raw.description.is_ascii() { + return Err(AccountComponentTemplateError::InvalidSchema( + "description must contain only ASCII characters".to_string(), + )); + } + let RawStorageSchema { slots } = raw.storage; let mut fields = Vec::with_capacity(slots.len()); @@ -62,7 +68,7 @@ impl AccountComponentMetadata { fields.push(slot.try_into_slot_schema()?); } - let storage_schema = AccountStorageSchema::new(fields)?; + let storage_schema = StorageSchema::new(fields)?; Ok(Self::new( raw.name, raw.description, @@ -130,7 +136,7 @@ struct RawMapType { // ACCOUNT STORAGE SCHEMA SERDE // ================================================================================================ -impl Serialize for AccountStorageSchema { +impl Serialize for StorageSchema { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -145,7 +151,7 @@ impl Serialize for AccountStorageSchema { } } -impl<'de> Deserialize<'de> for AccountStorageSchema { +impl<'de> Deserialize<'de> for StorageSchema { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -159,7 +165,7 @@ impl<'de> Deserialize<'de> for AccountStorageSchema { fields.push((slot_name, schema)); } - AccountStorageSchema::new(fields).map_err(D::Error::custom) + StorageSchema::new(fields).map_err(D::Error::custom) } } diff --git a/crates/miden-protocol/src/account/component/storage/toml/tests.rs b/crates/miden-protocol/src/account/component/storage/toml/tests.rs index 627b783611..dd6ead16cf 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/tests.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/tests.rs @@ -1,7 +1,8 @@ use alloc::string::ToString; use core::error::Error; -use miden_core::{Felt, FieldElement, Word}; +use miden_air::FieldElement; +use miden_core::{Felt, Word}; use crate::account::component::toml::init_storage_data::InitStorageDataError; use crate::account::component::{ @@ -259,6 +260,163 @@ fn metadata_from_toml_parses_named_storage_schema() { assert!(!requirements.contains_key(&"demo::my_map".parse::().unwrap())); } +#[test] +fn metadata_from_toml_rejects_non_ascii_component_description() { + let toml_str = r#" + name = "Test Component" + description = "Invalid \u00e9" + version = "0.1.0" + supported-types = [] + "#; + + assert_matches::assert_matches!( + AccountComponentMetadata::from_toml(toml_str), + Err(AccountComponentTemplateError::InvalidSchema(_)) + ); +} + +#[test] +fn metadata_from_toml_rejects_non_ascii_slot_description() { + let toml_str = r#" + name = "Test Component" + description = "Test description" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::test_value" + description = "Invalid \u00e9" + type = "word" + "#; + + assert_matches::assert_matches!( + AccountComponentMetadata::from_toml(toml_str), + Err(AccountComponentTemplateError::InvalidSchema(_)) + ); +} + +#[test] +fn metadata_schema_commitment_ignores_defaults_and_ordering() { + let toml_a = r#" + name = "Commitment Test" + description = "Schema commitments are equal regardless of defaults and ordering" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::first" + type = "word" + default-value = "0x1" + + [[storage.slots]] + name = "demo::map" + type = { key = "word", value = "word" } + default-values = [ + { key = "0x1", value = "0x10" }, + ] + + [[storage.slots]] + name = "demo::composed" + type = [ + { name = "a", type = "u8", description = "field a", default-value = "1" }, + { name = "b", description = "field b", default-value = "2" }, + { name = "c", type = "u16", description = "field c", default-value = "3" }, + { type = "void", description = "padding" }, + ] + "#; + + let toml_b = r#" + name = "Commitment Test" + description = "" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::map" + type = { key = "word", value = "word" } + default-values = [ + { key = "0x2", value = "0x20" }, + ] + + [[storage.slots]] + name = "demo::composed" + type = [ + { name = "a", type = "u8", description = "field a", default-value = "9" }, + { name = "b", description = "field b", default-value = "8" }, + { name = "c", type = "u16", description = "field c", default-value = "7" }, + { type = "void", description = "padding" }, + ] + + [[storage.slots]] + name = "demo::first" + type = "word" + default-value = "0x9" + "#; + + let metadata_a = AccountComponentMetadata::from_toml(toml_a).unwrap(); + let metadata_b = AccountComponentMetadata::from_toml(toml_b).unwrap(); + + assert_ne!(metadata_a.storage_schema(), metadata_b.storage_schema()); + assert_eq!( + metadata_a.storage_schema().commitment(), + metadata_b.storage_schema().commitment() + ); +} + +#[test] +fn metadata_schema_commitment_includes_descriptions() { + let toml_a = r#" + name = "Commitment Test" + description = "Component description" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::value" + description = "slot description a" + type = "word" + "#; + + let toml_bad_description = r#" + name = "Commitment Test" + description = "Component description" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::value" + description = "incorrect description" + type = "word" + "#; + + let toml_bad_name = r#" + name = "Commitment Test" + description = "Component description" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "demo::bad_value" + description = "slot description a" + type = "word" + "#; + + let metadata_a = AccountComponentMetadata::from_toml(toml_a).unwrap(); + let metadata_bad_description = + AccountComponentMetadata::from_toml(toml_bad_description).unwrap(); + let metadata_bad_slot_name = AccountComponentMetadata::from_toml(toml_bad_name).unwrap(); + + assert_ne!( + metadata_a.storage_schema().commitment(), + metadata_bad_description.storage_schema().commitment() + ); + + assert_ne!( + metadata_a.storage_schema().commitment(), + metadata_bad_slot_name.storage_schema().commitment() + ); +} + #[test] fn metadata_from_toml_rejects_typed_fields_in_static_map_values() { let toml_str = r#" From a413da7692f04b1b81e82a926561d4fe05c2c24d Mon Sep 17 00:00:00 2001 From: igamigo Date: Fri, 16 Jan 2026 16:43:03 -0300 Subject: [PATCH 110/114] feat: add `SchemaCommitment` component (#2253) --- CHANGELOG.md | 1 + .../src/account/component/metadata/mod.rs | 2 +- .../component/storage/init_storage_data.rs | 37 ++++ .../metadata/schema_commitment.masm | 5 + .../standards/metadata/storage_schema.masm | 19 ++ .../src/account/components/mod.rs | 17 ++ .../src/account/metadata/mod.rs | 185 ++++++++++++++++++ crates/miden-standards/src/account/mod.rs | 1 + 8 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 crates/miden-standards/asm/account_components/metadata/schema_commitment.masm create mode 100644 crates/miden-standards/asm/standards/metadata/storage_schema.masm create mode 100644 crates/miden-standards/src/account/metadata/mod.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 49064ae538..4fb9439885 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). - Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). - Add `AccountId::parse()` helper function to parse both hex and bech32 formats ([#2223](https://github.com/0xMiden/miden-base/pull/2223)). +- Added `AccountSchemaCommitment` component to expose account storage schema commitments ([#2253](https://github.com/0xMiden/miden-base/pull/2253)). - Add `read_foreign_account_inputs()`, `read_vault_asset_witnesses()`, and `read_storage_map_witness()` for `TransactionInputs` ([#2246](https://github.com/0xMiden/miden-base/pull/2246)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249)). - [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252)). diff --git a/crates/miden-protocol/src/account/component/metadata/mod.rs b/crates/miden-protocol/src/account/component/metadata/mod.rs index 1d8662dc13..9b84b4cf07 100644 --- a/crates/miden-protocol/src/account/component/metadata/mod.rs +++ b/crates/miden-protocol/src/account/component/metadata/mod.rs @@ -35,7 +35,7 @@ use crate::AccountError; /// # Example /// /// ``` -/// use std::collections::BTreeSet; +/// use std::collections::{BTreeMap, BTreeSet}; /// /// use miden_protocol::account::StorageSlotName; /// use miden_protocol::account::component::{ diff --git a/crates/miden-protocol/src/account/component/storage/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs index a9df7e07e4..999f552a3c 100644 --- a/crates/miden-protocol/src/account/component/storage/init_storage_data.rs +++ b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs @@ -74,6 +74,43 @@ pub struct InitStorageData { } impl InitStorageData { + /// Creates a new instance of [InitStorageData], validating that there are no conflicting + /// entries. + /// + /// # Errors + /// + /// Returns an error if: + /// - A slot has both value entries and map entries + /// - A slot has both a slot-level value and field values + pub fn new( + value_entries: BTreeMap, + map_entries: BTreeMap>, + ) -> Result { + // Check for conflicts between value entries and map entries + for slot_name in map_entries.keys() { + if value_entries.keys().any(|v| v.slot_name() == slot_name) { + return Err(InitStorageDataError::ConflictingEntries(slot_name.as_str().into())); + } + } + + // Check for conflicts between slot-level values and field values + for value_name in value_entries.keys() { + if value_name.field_name().is_none() { + // This is a slot-level value; check if there are field entries for this slot + let has_field_entries = value_entries.keys().any(|other| { + other.slot_name() == value_name.slot_name() && other.field_name().is_some() + }); + if has_field_entries { + return Err(InitStorageDataError::ConflictingEntries( + value_name.slot_name().as_str().into(), + )); + } + } + } + + Ok(InitStorageData { value_entries, map_entries }) + } + /// Returns a reference to the underlying init values map. pub fn values(&self) -> &BTreeMap { &self.value_entries diff --git a/crates/miden-standards/asm/account_components/metadata/schema_commitment.masm b/crates/miden-standards/asm/account_components/metadata/schema_commitment.masm new file mode 100644 index 0000000000..cc9b85230a --- /dev/null +++ b/crates/miden-standards/asm/account_components/metadata/schema_commitment.masm @@ -0,0 +1,5 @@ +# The MASM code of the Storage Commitment metadata Component. +# +# See the `AccountSchemaCommitment` Rust type's documentation for more details. + +pub use ::miden::standards::metadata::storage_schema::get_schema_commitment diff --git a/crates/miden-standards/asm/standards/metadata/storage_schema.masm b/crates/miden-standards/asm/standards/metadata/storage_schema.masm new file mode 100644 index 0000000000..fe61927c30 --- /dev/null +++ b/crates/miden-standards/asm/standards/metadata/storage_schema.masm @@ -0,0 +1,19 @@ +# This module defines the storage schema functionality for accounts. It exposes the storage +# slot at which an account stores a commitment to its storage schema, and provides a helper +# procedure to load that commitment from the active account's storage. + +use miden::protocol::active_account + +# CONSTANTS +# ================================================================================================= + +# The slot in this component's storage layout where the account storage schema commitment is stored +const SCHEMA_COMMITMENT_SLOT = word("miden::standards::metadata::storage_schema") + +pub proc get_schema_commitment + dropw + # => [pad(16)] + + push.SCHEMA_COMMITMENT_SLOT[0..2] exec.active_account::get_item + # => [SCHEMA_COMMITMENT, pad(16)] +end diff --git a/crates/miden-standards/src/account/components/mod.rs b/crates/miden-standards/src/account/components/mod.rs index de1edcb37d..b99c0a24ab 100644 --- a/crates/miden-standards/src/account/components/mod.rs +++ b/crates/miden-standards/src/account/components/mod.rs @@ -108,6 +108,18 @@ static NETWORK_FUNGIBLE_FAUCET_LIBRARY: LazyLock = LazyLock::new(|| { Library::read_from_bytes(bytes).expect("Shipped Network Fungible Faucet library is well-formed") }); +// METADATA LIBRARIES +// ================================================================================================ + +// Initialize the Storage Schema library only once. +static STORAGE_SCHEMA_LIBRARY: LazyLock = LazyLock::new(|| { + let bytes = include_bytes!(concat!( + env!("OUT_DIR"), + "/assets/account_components/metadata/schema_commitment.masl" + )); + Library::read_from_bytes(bytes).expect("Shipped Storage Schema library is well-formed") +}); + /// Returns the Basic Wallet Library. pub fn basic_wallet_library() -> Library { BASIC_WALLET_LIBRARY.clone() @@ -123,6 +135,11 @@ pub fn network_fungible_faucet_library() -> Library { NETWORK_FUNGIBLE_FAUCET_LIBRARY.clone() } +/// Returns the Storage Schema Library. +pub fn storage_schema_library() -> Library { + STORAGE_SCHEMA_LIBRARY.clone() +} + /// Returns the ECDSA K256 Keccak Library. pub fn ecdsa_k256_keccak_library() -> Library { ECDSA_K256_KECCAK_LIBRARY.clone() diff --git a/crates/miden-standards/src/account/metadata/mod.rs b/crates/miden-standards/src/account/metadata/mod.rs new file mode 100644 index 0000000000..8ab43e44ae --- /dev/null +++ b/crates/miden-standards/src/account/metadata/mod.rs @@ -0,0 +1,185 @@ +use alloc::collections::BTreeMap; + +use miden_protocol::Word; +use miden_protocol::account::component::StorageSchema; +use miden_protocol::account::{AccountComponent, StorageSlot, StorageSlotName}; +use miden_protocol::errors::AccountComponentTemplateError; +use miden_protocol::utils::sync::LazyLock; + +use crate::account::components::storage_schema_library; + +pub static SCHEMA_COMMITMENT_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::metadata::storage_schema") + .expect("storage slot name should be valid") +}); + +/// An [`AccountComponent`] exposing the account storage schema commitment. +/// +/// The [`AccountSchemaCommitment`] component can be constructed from a list of [`StorageSchema`], +/// from which a commitment is computed and then inserted into the [`SCHEMA_COMMITMENT_SLOT_NAME`] +/// slot. +/// +/// It reexports the `get_schema_commitment` procedure from +/// `miden::standards::metadata::storage_schema`. +/// +/// ## Storage Layout +/// +/// - [`Self::schema_commitment_slot`]: Storage schema commitment. +pub struct AccountSchemaCommitment { + schema_commitment: Word, +} + +impl AccountSchemaCommitment { + /// Creates a new [`AccountSchemaCommitment`] component from a list of storage schemas. + /// + /// The input schemas are merged into a single schema before the final commitment is computed. + /// + /// # Errors + /// + /// Returns an error if the schemas contain conflicting definitions for the same slot name. + pub fn new(schemas: &[StorageSchema]) -> Result { + Ok(Self { + schema_commitment: compute_schema_commitment(schemas)?, + }) + } + + /// Creates a new [`AccountSchemaCommitment`] component from a [`StorageSchema`]. + pub fn from_schema( + storage_schema: &StorageSchema, + ) -> Result { + Self::new(core::slice::from_ref(storage_schema)) + } + + /// Returns the [`StorageSlotName`] where the schema commitment is stored. + pub fn schema_commitment_slot() -> &'static StorageSlotName { + &SCHEMA_COMMITMENT_SLOT_NAME + } +} + +impl From for AccountComponent { + fn from(schema_commitment: AccountSchemaCommitment) -> Self { + AccountComponent::new( + storage_schema_library(), + vec![StorageSlot::with_value( + AccountSchemaCommitment::schema_commitment_slot().clone(), + schema_commitment.schema_commitment, + )], + ) + .expect( + "AccountSchemaCommitment component should satisfy the requirements of a valid account component", + ) + .with_supports_all_types() + } +} + +/// Computes the schema commitment. +/// +/// The account schema commitment is computed from the merged schema commitment. +/// If the passed list of schemas is empty, [`Word::empty()`] is returned. +fn compute_schema_commitment( + schemas: &[StorageSchema], +) -> Result { + if schemas.is_empty() { + return Ok(Word::empty()); + } + + let mut merged_slots = BTreeMap::new(); + for schema in schemas { + for (slot_name, slot_schema) in schema.iter() { + match merged_slots.get(slot_name) { + None => { + merged_slots.insert(slot_name.clone(), slot_schema.clone()); + }, + // Slot exists, check if the schema is the same before erroring + // TODO: If we wanted to not error, we would have to decide on a winning schema + // for the StorageSlotName + Some(existing) => { + if existing != slot_schema { + return Err(AccountComponentTemplateError::InvalidSchema(format!( + "conflicting definitions for storage slot `{slot_name}`", + ))); + } + }, + } + } + } + + let merged_schema = StorageSchema::new(merged_slots)?; + + Ok(merged_schema.commitment()) +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use miden_protocol::Word; + use miden_protocol::account::AccountBuilder; + use miden_protocol::account::component::AccountComponentMetadata; + + use super::AccountSchemaCommitment; + use crate::account::auth::NoAuth; + + #[test] + fn storage_schema_commitment_is_order_independent() { + let toml_a = r#" + name = "Component A" + description = "Component A schema" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "test::slot_a" + type = "word" + "#; + + let toml_b = r#" + name = "Component B" + description = "Component B schema" + version = "0.1.0" + supported-types = [] + + [[storage.slots]] + name = "test::slot_b" + description = "description is committed to" + type = "word" + "#; + + let metadata_a = AccountComponentMetadata::from_toml(toml_a).unwrap(); + let metadata_b = AccountComponentMetadata::from_toml(toml_b).unwrap(); + + let schema_a = metadata_a.storage_schema().clone(); + let schema_b = metadata_b.storage_schema().clone(); + + // Create one component for each of two different accounts, but switch orderings + let component_a = + AccountSchemaCommitment::new(&[schema_a.clone(), schema_b.clone()]).unwrap(); + let component_b = AccountSchemaCommitment::new(&[schema_b, schema_a]).unwrap(); + + let account_a = AccountBuilder::new([1u8; 32]) + .with_auth_component(NoAuth) + .with_component(component_a) + .build() + .unwrap(); + + let account_b = AccountBuilder::new([2u8; 32]) + .with_auth_component(NoAuth) + .with_component(component_b) + .build() + .unwrap(); + + let slot_name = AccountSchemaCommitment::schema_commitment_slot(); + let commitment_a = account_a.storage().get_item(slot_name).unwrap(); + let commitment_b = account_b.storage().get_item(slot_name).unwrap(); + + assert_eq!(commitment_a, commitment_b); + } + + #[test] + fn storage_schema_commitment_is_empty_for_no_schemas() { + let component = AccountSchemaCommitment::new(&[]).unwrap(); + + assert_eq!(component.schema_commitment, Word::empty()); + } +} diff --git a/crates/miden-standards/src/account/mod.rs b/crates/miden-standards/src/account/mod.rs index eea3a92141..af3d4ff69b 100644 --- a/crates/miden-standards/src/account/mod.rs +++ b/crates/miden-standards/src/account/mod.rs @@ -4,6 +4,7 @@ pub mod auth; pub mod components; pub mod faucets; pub mod interface; +pub mod metadata; pub mod wallets; /// Macro to simplify the creation of static procedure digest constants. From 042971145d631f860dcf078e1596a310ff1c09b6 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 16 Jan 2026 12:07:29 -0800 Subject: [PATCH 111/114] refactor: remove top-level error exports from miden-protocol --- CHANGELOG.md | 1 + crates/miden-agglayer/src/lib.rs | 2 +- .../src/account/account_id/mod.rs | 4 +-- .../src/account/account_id/seed.rs | 3 +- .../src/account/account_id/v0/mod.rs | 4 +-- crates/miden-protocol/src/account/auth.rs | 3 +- .../miden-protocol/src/account/builder/mod.rs | 3 +- crates/miden-protocol/src/account/code/mod.rs | 2 +- .../src/account/component/metadata/mod.rs | 2 +- .../src/account/component/mod.rs | 3 +- .../miden-protocol/src/account/delta/mod.rs | 6 ++-- .../src/account/delta/storage.rs | 3 +- crates/miden-protocol/src/account/header.rs | 3 +- crates/miden-protocol/src/account/mod.rs | 5 ++-- crates/miden-protocol/src/account/partial.rs | 3 +- .../src/account/storage/header.rs | 3 +- .../src/account/storage/map/mod.rs | 4 +-- .../miden-protocol/src/account/storage/mod.rs | 2 +- .../src/account/storage/partial.rs | 2 +- .../src/account/storage/slot/type.rs | 3 +- .../miden-protocol/src/address/address_id.rs | 3 +- .../miden-protocol/src/address/interface.rs | 2 +- crates/miden-protocol/src/address/mod.rs | 5 ++-- .../src/address/routing_parameters.rs | 3 +- crates/miden-protocol/src/address/type.rs | 3 +- crates/miden-protocol/src/asset/mod.rs | 3 +- .../src/asset/vault/asset_witness.rs | 2 +- crates/miden-protocol/src/asset/vault/mod.rs | 3 +- .../src/batch/input_output_note_tracker.rs | 4 +-- .../src/block/account_tree/witness.rs | 3 +- crates/miden-protocol/src/block/header.rs | 3 +- .../src/block/nullifier_tree/mod.rs | 3 +- crates/miden-protocol/src/lib.rs | 29 ------------------- crates/miden-protocol/src/note/attachment.rs | 3 +- .../miden-protocol/src/note/execution_hint.rs | 3 +- crates/miden-protocol/src/note/metadata.rs | 3 +- crates/miden-protocol/src/note/mod.rs | 3 +- crates/miden-protocol/src/note/note_type.rs | 3 +- crates/miden-protocol/src/note/script.rs | 3 +- crates/miden-protocol/src/testing/asset.rs | 2 +- .../src/testing/partial_blockchain.rs | 2 +- .../src/transaction/inputs/mod.rs | 4 +-- .../src/transaction/inputs/notes.rs | 2 +- .../src/transaction/kernel/mod.rs | 3 +- .../miden-protocol/src/transaction/outputs.rs | 6 ++-- .../src/transaction/partial_blockchain.rs | 5 ++-- .../src/transaction/proven_tx.rs | 13 +++------ .../src/account/auth/ecdsa_k256_keccak_acl.rs | 3 +- .../auth/ecdsa_k256_keccak_multisig.rs | 3 +- .../src/account/auth/rpo_falcon_512_acl.rs | 3 +- .../account/auth/rpo_falcon_512_multisig.rs | 3 +- .../src/account/faucets/mod.rs | 3 +- .../src/account/interface/test.rs | 3 +- .../src/account/wallets/mod.rs | 3 +- .../miden-standards/src/note/mint_inputs.rs | 3 +- crates/miden-standards/src/note/mod.rs | 3 +- .../src/note/network_account_target.rs | 3 +- crates/miden-standards/src/note/utils.rs | 3 +- crates/miden-standards/src/testing/note.rs | 3 +- .../src/kernel_tests/batch/proposed_batch.rs | 3 +- .../src/kernel_tests/block/header_errors.rs | 3 +- .../block/proposed_block_errors.rs | 3 +- .../src/kernel_tests/tx/test_asset_vault.rs | 3 +- .../src/mock_chain/chain_builder.rs | 3 +- crates/miden-testing/tests/scripts/swap.rs | 3 +- .../src/local_batch_prover.rs | 2 +- crates/miden-tx/src/errors/mod.rs | 9 +++--- 67 files changed, 127 insertions(+), 120 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fb9439885..c425397748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). - [BREAKING] Refactor note tags to be arbitrary `u32` values and drop previous validation ([#2219](https://github.com/0xMiden/miden-base/pull/2219)). - [BREAKING] Refactored `InitStorageData` to support native types ([#2230](https://github.com/0xMiden/miden-base/pull/2230)). +- [BREAKING] Removed top-level error exports from `miden-protocol` crate (the are still accessible under `miden_protocol::errors`). ## 0.12.4 (2025-11-26) diff --git a/crates/miden-agglayer/src/lib.rs b/crates/miden-agglayer/src/lib.rs index 72d50ab7b8..7020a384a4 100644 --- a/crates/miden-agglayer/src/lib.rs +++ b/crates/miden-agglayer/src/lib.rs @@ -8,7 +8,6 @@ use alloc::vec::Vec; use miden_assembly::Library; use miden_assembly::utils::Deserializable; use miden_core::{Felt, FieldElement, Program, Word}; -use miden_protocol::NoteError; use miden_protocol::account::{ Account, AccountBuilder, @@ -21,6 +20,7 @@ use miden_protocol::account::{ }; use miden_protocol::asset::TokenSymbol; use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::errors::NoteError; use miden_protocol::note::{ Note, NoteAssets, diff --git a/crates/miden-protocol/src/account/account_id/mod.rs b/crates/miden-protocol/src/account/account_id/mod.rs index 334dc25a54..03a575fbf8 100644 --- a/crates/miden-protocol/src/account/account_id/mod.rs +++ b/crates/miden-protocol/src/account/account_id/mod.rs @@ -23,9 +23,9 @@ use miden_core::utils::{ByteReader, Deserializable, Serializable}; use miden_crypto::utils::hex_to_bytes; use miden_processor::DeserializationError; +use crate::Word; use crate::address::NetworkId; -use crate::errors::AccountIdError; -use crate::{AccountError, Word}; +use crate::errors::{AccountError, AccountIdError}; /// The identifier of an [`Account`](crate::account::Account). /// diff --git a/crates/miden-protocol/src/account/account_id/seed.rs b/crates/miden-protocol/src/account/account_id/seed.rs index c472695dfb..8ad1be02a7 100644 --- a/crates/miden-protocol/src/account/account_id/seed.rs +++ b/crates/miden-protocol/src/account/account_id/seed.rs @@ -3,7 +3,8 @@ use alloc::vec::Vec; use crate::account::account_id::AccountIdVersion; use crate::account::account_id::v0::{compute_digest, validate_prefix}; use crate::account::{AccountStorageMode, AccountType}; -use crate::{AccountError, Felt, Word}; +use crate::errors::AccountError; +use crate::{Felt, Word}; /// Finds and returns a seed suitable for creating an account ID for the specified account type /// using the provided initial seed as a starting point. diff --git a/crates/miden-protocol/src/account/account_id/v0/mod.rs b/crates/miden-protocol/src/account/account_id/v0/mod.rs index c14095819d..34ad3ebbb9 100644 --- a/crates/miden-protocol/src/account/account_id/v0/mod.rs +++ b/crates/miden-protocol/src/account/account_id/v0/mod.rs @@ -19,9 +19,9 @@ use crate::account::account_id::account_type::{ use crate::account::account_id::storage_mode::{NETWORK, PRIVATE, PUBLIC}; use crate::account::{AccountIdVersion, AccountStorageMode, AccountType}; use crate::address::AddressType; -use crate::errors::{AccountIdError, Bech32Error}; +use crate::errors::{AccountError, AccountIdError, Bech32Error}; use crate::utils::{ByteReader, Deserializable, DeserializationError, Serializable}; -use crate::{AccountError, EMPTY_WORD, Felt, Hasher, Word}; +use crate::{EMPTY_WORD, Felt, Hasher, Word}; // ACCOUNT ID VERSION 0 // ================================================================================================ diff --git a/crates/miden-protocol/src/account/auth.rs b/crates/miden-protocol/src/account/auth.rs index a3fc47a577..4155d35463 100644 --- a/crates/miden-protocol/src/account/auth.rs +++ b/crates/miden-protocol/src/account/auth.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use rand::{CryptoRng, Rng}; use crate::crypto::dsa::{ecdsa_k256_keccak, falcon512_rpo}; +use crate::errors::AuthSchemeError; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -10,7 +11,7 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{AuthSchemeError, Felt, Hasher, Word}; +use crate::{Felt, Hasher, Word}; // AUTH SCHEME // ================================================================================================ diff --git a/crates/miden-protocol/src/account/builder/mod.rs b/crates/miden-protocol/src/account/builder/mod.rs index e02aac4152..6e4275b67c 100644 --- a/crates/miden-protocol/src/account/builder/mod.rs +++ b/crates/miden-protocol/src/account/builder/mod.rs @@ -15,7 +15,8 @@ use crate::account::{ AccountType, }; use crate::asset::AssetVault; -use crate::{AccountError, Felt, Word}; +use crate::errors::AccountError; +use crate::{Felt, Word}; /// A convenient builder for an [`Account`] allowing for safe construction of an account by /// combining multiple [`AccountComponent`]s. diff --git a/crates/miden-protocol/src/account/code/mod.rs b/crates/miden-protocol/src/account/code/mod.rs index 0d541b70b8..e079b0746c 100644 --- a/crates/miden-protocol/src/account/code/mod.rs +++ b/crates/miden-protocol/src/account/code/mod.rs @@ -410,9 +410,9 @@ mod tests { use miden_assembly::Assembler; use super::{AccountCode, Deserializable, Serializable}; - use crate::AccountError; use crate::account::code::build_procedure_commitment; use crate::account::{AccountComponent, AccountType}; + use crate::errors::AccountError; use crate::testing::account_code::CODE; use crate::testing::noop_auth_component::NoopAuthComponent; diff --git a/crates/miden-protocol/src/account/component/metadata/mod.rs b/crates/miden-protocol/src/account/component/metadata/mod.rs index 9b84b4cf07..a0f58ae9cd 100644 --- a/crates/miden-protocol/src/account/component/metadata/mod.rs +++ b/crates/miden-protocol/src/account/component/metadata/mod.rs @@ -8,7 +8,7 @@ use miden_processor::DeserializationError; use semver::Version; use super::{AccountType, SchemaRequirement, StorageSchema, StorageValueName}; -use crate::AccountError; +use crate::errors::AccountError; // ACCOUNT COMPONENT METADATA // ================================================================================================ diff --git a/crates/miden-protocol/src/account/component/mod.rs b/crates/miden-protocol/src/account/component/mod.rs index 57e62bf284..9245e93f11 100644 --- a/crates/miden-protocol/src/account/component/mod.rs +++ b/crates/miden-protocol/src/account/component/mod.rs @@ -14,7 +14,8 @@ pub use code::AccountComponentCode; use crate::account::{AccountType, StorageSlot}; use crate::assembly::Path; -use crate::{AccountError, MastForest, Word}; +use crate::errors::AccountError; +use crate::{MastForest, Word}; // ACCOUNT COMPONENT // ================================================================================================ diff --git a/crates/miden-protocol/src/account/delta/mod.rs b/crates/miden-protocol/src/account/delta/mod.rs index 178f3a8020..1b8d13d1f5 100644 --- a/crates/miden-protocol/src/account/delta/mod.rs +++ b/crates/miden-protocol/src/account/delta/mod.rs @@ -11,8 +11,9 @@ use crate::account::{ }; use crate::asset::AssetVault; use crate::crypto::SequentialCommit; +use crate::errors::{AccountDeltaError, AccountError}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; -use crate::{AccountDeltaError, AccountError, Felt, Word, ZERO}; +use crate::{Felt, Word, ZERO}; mod storage; pub use storage::{AccountStorageDelta, StorageMapDelta, StorageSlotDelta}; @@ -608,12 +609,13 @@ mod tests { NonFungibleAsset, NonFungibleAssetDetails, }; + use crate::errors::AccountDeltaError; use crate::testing::account_id::{ ACCOUNT_ID_PRIVATE_SENDER, ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, AccountIdBuilder, }; - use crate::{AccountDeltaError, ONE, Word, ZERO}; + use crate::{ONE, Word, ZERO}; #[test] fn account_delta_nonce_validation() { diff --git a/crates/miden-protocol/src/account/delta/storage.rs b/crates/miden-protocol/src/account/delta/storage.rs index c8f9a852de..bd600ac0e0 100644 --- a/crates/miden-protocol/src/account/delta/storage.rs +++ b/crates/miden-protocol/src/account/delta/storage.rs @@ -618,7 +618,8 @@ mod tests { use super::{AccountStorageDelta, Deserializable, Serializable}; use crate::account::{StorageMapDelta, StorageSlotDelta, StorageSlotName}; - use crate::{AccountDeltaError, ONE, Word}; + use crate::errors::AccountDeltaError; + use crate::{ONE, Word}; #[test] fn account_storage_delta_returns_err_on_slot_type_mismatch() { diff --git a/crates/miden-protocol/src/account/header.rs b/crates/miden-protocol/src/account/header.rs index 8a2a19f9cd..a725394a2f 100644 --- a/crates/miden-protocol/src/account/header.rs +++ b/crates/miden-protocol/src/account/header.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; use super::{Account, AccountId, Felt, PartialAccount, ZERO, hash_account}; +use crate::errors::AccountError; use crate::transaction::memory::{ ACCT_CODE_COMMITMENT_OFFSET, ACCT_DATA_MEM_SIZE, @@ -13,7 +14,7 @@ use crate::transaction::memory::{ MemoryOffset, }; use crate::utils::serde::{Deserializable, Serializable}; -use crate::{AccountError, WORD_SIZE, Word, WordError}; +use crate::{WORD_SIZE, Word, WordError}; // ACCOUNT HEADER // ================================================================================================ diff --git a/crates/miden-protocol/src/account/mod.rs b/crates/miden-protocol/src/account/mod.rs index 7f94221a46..c521c5e46f 100644 --- a/crates/miden-protocol/src/account/mod.rs +++ b/crates/miden-protocol/src/account/mod.rs @@ -2,6 +2,7 @@ use alloc::string::ToString; use alloc::vec::Vec; use crate::asset::{Asset, AssetVault}; +use crate::errors::AccountError; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -9,7 +10,7 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{AccountError, Felt, Hasher, Word, ZERO}; +use crate::{Felt, Hasher, Word, ZERO}; mod account_id; pub use account_id::{ @@ -592,7 +593,6 @@ mod tests { AccountStorageDelta, AccountVaultDelta, }; - use crate::AccountError; use crate::account::AccountStorageMode::Network; use crate::account::{ Account, @@ -608,6 +608,7 @@ mod tests { StorageSlotName, }; use crate::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; + use crate::errors::AccountError; use crate::testing::account_id::{ ACCOUNT_ID_PRIVATE_SENDER, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, diff --git a/crates/miden-protocol/src/account/partial.rs b/crates/miden-protocol/src/account/partial.rs index fd1161f07e..f7ae354dae 100644 --- a/crates/miden-protocol/src/account/partial.rs +++ b/crates/miden-protocol/src/account/partial.rs @@ -4,10 +4,11 @@ use miden_core::utils::{Deserializable, Serializable}; use miden_core::{Felt, ZERO}; use super::{Account, AccountCode, AccountId, PartialStorage}; +use crate::Word; use crate::account::{hash_account, validate_account_seed}; use crate::asset::PartialVault; +use crate::errors::AccountError; use crate::utils::serde::DeserializationError; -use crate::{AccountError, Word}; /// A partial representation of an account. /// diff --git a/crates/miden-protocol/src/account/storage/header.rs b/crates/miden-protocol/src/account/storage/header.rs index e6cb794ebf..359e6ff49e 100644 --- a/crates/miden-protocol/src/account/storage/header.rs +++ b/crates/miden-protocol/src/account/storage/header.rs @@ -7,6 +7,7 @@ use super::map::EMPTY_STORAGE_MAP_ROOT; use super::{AccountStorage, Felt, StorageSlotType, Word}; use crate::account::{StorageSlot, StorageSlotId, StorageSlotName}; use crate::crypto::SequentialCommit; +use crate::errors::AccountError; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -14,7 +15,7 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{AccountError, FieldElement, ZERO}; +use crate::{FieldElement, ZERO}; // ACCOUNT STORAGE HEADER // ================================================================================================ diff --git a/crates/miden-protocol/src/account/storage/map/mod.rs b/crates/miden-protocol/src/account/storage/map/mod.rs index 31bb7fa355..46d489a91c 100644 --- a/crates/miden-protocol/src/account/storage/map/mod.rs +++ b/crates/miden-protocol/src/account/storage/map/mod.rs @@ -4,11 +4,11 @@ use miden_core::EMPTY_WORD; use miden_crypto::merkle::EmptySubtreeRoots; use super::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, Word}; +use crate::Hasher; use crate::account::StorageMapDelta; use crate::crypto::merkle::InnerNodeInfo; use crate::crypto::merkle::smt::{LeafIndex, SMT_DEPTH, Smt, SmtLeaf}; -use crate::errors::StorageMapError; -use crate::{AccountError, Hasher}; +use crate::errors::{AccountError, StorageMapError}; mod partial; pub use partial::PartialStorageMap; diff --git a/crates/miden-protocol/src/account/storage/mod.rs b/crates/miden-protocol/src/account/storage/mod.rs index ddba04875f..a2e2f44c21 100644 --- a/crates/miden-protocol/src/account/storage/mod.rs +++ b/crates/miden-protocol/src/account/storage/mod.rs @@ -402,8 +402,8 @@ mod tests { use assert_matches::assert_matches; use super::{AccountStorage, Deserializable, Serializable}; - use crate::AccountError; use crate::account::{AccountStorageHeader, StorageSlot, StorageSlotHeader, StorageSlotName}; + use crate::errors::AccountError; #[test] fn test_serde_account_storage() -> anyhow::Result<()> { diff --git a/crates/miden-protocol/src/account/storage/partial.rs b/crates/miden-protocol/src/account/storage/partial.rs index d7e0a5b015..41e2500bac 100644 --- a/crates/miden-protocol/src/account/storage/partial.rs +++ b/crates/miden-protocol/src/account/storage/partial.rs @@ -6,8 +6,8 @@ use miden_crypto::merkle::InnerNodeInfo; use miden_crypto::merkle::smt::SmtLeaf; use super::{AccountStorage, AccountStorageHeader, StorageSlotContent}; -use crate::AccountError; use crate::account::PartialStorageMap; +use crate::errors::AccountError; /// A partial representation of an account storage, containing only a subset of the storage data. /// diff --git a/crates/miden-protocol/src/account/storage/slot/type.rs b/crates/miden-protocol/src/account/storage/slot/type.rs index 32da9c7e27..25bd5a3a60 100644 --- a/crates/miden-protocol/src/account/storage/slot/type.rs +++ b/crates/miden-protocol/src/account/storage/slot/type.rs @@ -3,6 +3,8 @@ use core::fmt::Display; use miden_core::{ONE, ZERO}; +use crate::Felt; +use crate::errors::AccountError; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -10,7 +12,6 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{AccountError, Felt}; // STORAGE SLOT TYPE // ================================================================================================ diff --git a/crates/miden-protocol/src/address/address_id.rs b/crates/miden-protocol/src/address/address_id.rs index 7b13ae73c2..e6cdbdddeb 100644 --- a/crates/miden-protocol/src/address/address_id.rs +++ b/crates/miden-protocol/src/address/address_id.rs @@ -4,10 +4,9 @@ use bech32::Bech32m; use bech32::primitives::decode::CheckedHrpstring; use miden_processor::DeserializationError; -use crate::AddressError; use crate::account::{AccountId, AccountStorageMode}; use crate::address::{AddressType, NetworkId}; -use crate::errors::Bech32Error; +use crate::errors::{AddressError, Bech32Error}; use crate::note::NoteTag; use crate::utils::serde::{ByteWriter, Deserializable, Serializable}; diff --git a/crates/miden-protocol/src/address/interface.rs b/crates/miden-protocol/src/address/interface.rs index 562feb2585..9884ebdc00 100644 --- a/crates/miden-protocol/src/address/interface.rs +++ b/crates/miden-protocol/src/address/interface.rs @@ -1,6 +1,6 @@ use core::fmt::{self, Display, Formatter}; -use crate::AddressError; +use crate::errors::AddressError; /// The account interface of an [`Address`](super::Address). /// diff --git a/crates/miden-protocol/src/address/mod.rs b/crates/miden-protocol/src/address/mod.rs index 93ef42b210..91c9438569 100644 --- a/crates/miden-protocol/src/address/mod.rs +++ b/crates/miden-protocol/src/address/mod.rs @@ -16,9 +16,9 @@ pub use interface::AddressInterface; use miden_processor::DeserializationError; pub use network_id::{CustomNetworkId, NetworkId}; -use crate::AddressError; use crate::account::AccountStorageMode; use crate::crypto::ies::SealingKey; +use crate::errors::AddressError; use crate::note::NoteTag; use crate::utils::serde::{ByteWriter, Deserializable, Serializable}; @@ -246,10 +246,9 @@ mod tests { use bech32::{Bech32, Bech32m, NoChecksum}; use super::*; - use crate::AccountIdError; use crate::account::{AccountId, AccountType}; use crate::address::CustomNetworkId; - use crate::errors::Bech32Error; + use crate::errors::{AccountIdError, Bech32Error}; use crate::testing::account_id::{ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, AccountIdBuilder}; /// Tests that an account ID address can be encoded and decoded. diff --git a/crates/miden-protocol/src/address/routing_parameters.rs b/crates/miden-protocol/src/address/routing_parameters.rs index 3d9d640ee0..0fb5ec6923 100644 --- a/crates/miden-protocol/src/address/routing_parameters.rs +++ b/crates/miden-protocol/src/address/routing_parameters.rs @@ -5,11 +5,10 @@ use alloc::vec::Vec; use bech32::primitives::decode::CheckedHrpstring; use bech32::{Bech32m, Hrp}; -use crate::AddressError; use crate::address::AddressInterface; use crate::crypto::dsa::{ecdsa_k256_keccak, eddsa_25519_sha512}; use crate::crypto::ies::SealingKey; -use crate::errors::Bech32Error; +use crate::errors::{AddressError, Bech32Error}; use crate::note::NoteTag; use crate::utils::serde::{ ByteReader, diff --git a/crates/miden-protocol/src/address/type.rs b/crates/miden-protocol/src/address/type.rs index 188755e28d..e3ee4f8fed 100644 --- a/crates/miden-protocol/src/address/type.rs +++ b/crates/miden-protocol/src/address/type.rs @@ -1,5 +1,4 @@ -use crate::AddressError; -use crate::errors::Bech32Error; +use crate::errors::{AddressError, Bech32Error}; /// The type of an [`Address`](super::Address) in Miden. /// diff --git a/crates/miden-protocol/src/asset/mod.rs b/crates/miden-protocol/src/asset/mod.rs index 3c3fc7d73c..4d14998289 100644 --- a/crates/miden-protocol/src/asset/mod.rs +++ b/crates/miden-protocol/src/asset/mod.rs @@ -1,4 +1,5 @@ use super::account::AccountType; +use super::errors::{AssetError, TokenSymbolError}; use super::utils::serde::{ ByteReader, ByteWriter, @@ -6,7 +7,7 @@ use super::utils::serde::{ DeserializationError, Serializable, }; -use super::{AssetError, Felt, Hasher, TokenSymbolError, Word, ZERO}; +use super::{Felt, Hasher, Word, ZERO}; use crate::account::AccountIdPrefix; mod fungible; diff --git a/crates/miden-protocol/src/asset/vault/asset_witness.rs b/crates/miden-protocol/src/asset/vault/asset_witness.rs index e894a56070..503b468d41 100644 --- a/crates/miden-protocol/src/asset/vault/asset_witness.rs +++ b/crates/miden-protocol/src/asset/vault/asset_witness.rs @@ -4,8 +4,8 @@ use miden_crypto::merkle::InnerNodeInfo; use miden_crypto::merkle::smt::{SmtLeaf, SmtProof}; use super::vault_key::AssetVaultKey; -use crate::AssetError; use crate::asset::Asset; +use crate::errors::AssetError; use crate::utils::serde::{Deserializable, DeserializationError, Serializable}; /// A witness of an asset in an [`AssetVault`](super::AssetVault). diff --git a/crates/miden-protocol/src/asset/vault/mod.rs b/crates/miden-protocol/src/asset/vault/mod.rs index bb3e1c046a..94bb7ab81a 100644 --- a/crates/miden-protocol/src/asset/vault/mod.rs +++ b/crates/miden-protocol/src/asset/vault/mod.rs @@ -14,9 +14,10 @@ use super::{ NonFungibleAsset, Serializable, }; +use crate::Word; use crate::account::{AccountId, AccountVaultDelta, NonFungibleDeltaAction}; use crate::crypto::merkle::smt::Smt; -use crate::{AssetVaultError, Word}; +use crate::errors::AssetVaultError; mod partial; pub use partial::PartialVault; diff --git a/crates/miden-protocol/src/batch/input_output_note_tracker.rs b/crates/miden-protocol/src/batch/input_output_note_tracker.rs index d18d3fe92e..296cf021e6 100644 --- a/crates/miden-protocol/src/batch/input_output_note_tracker.rs +++ b/crates/miden-protocol/src/batch/input_output_note_tracker.rs @@ -1,10 +1,11 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; +use crate::Word; use crate::batch::{BatchId, ProvenBatch}; use crate::block::{BlockHeader, BlockNumber}; use crate::crypto::merkle::MerkleError; -use crate::errors::ProposedBatchError; +use crate::errors::{ProposedBatchError, ProposedBlockError}; use crate::note::{NoteHeader, NoteId, NoteInclusionProof, Nullifier}; use crate::transaction::{ InputNoteCommitment, @@ -13,7 +14,6 @@ use crate::transaction::{ ProvenTransaction, TransactionId, }; -use crate::{ProposedBlockError, Word}; type BatchInputNotes = Vec; type BlockInputNotes = Vec; diff --git a/crates/miden-protocol/src/block/account_tree/witness.rs b/crates/miden-protocol/src/block/account_tree/witness.rs index b6d6e5083b..9c5c81d745 100644 --- a/crates/miden-protocol/src/block/account_tree/witness.rs +++ b/crates/miden-protocol/src/block/account_tree/witness.rs @@ -3,10 +3,11 @@ use alloc::string::ToString; use miden_crypto::merkle::smt::{LeafIndex, SMT_DEPTH, SmtLeaf, SmtProof, SmtProofError}; use miden_crypto::merkle::{InnerNodeInfo, SparseMerklePath}; +use crate::Word; use crate::account::AccountId; use crate::block::account_tree::{account_id_to_smt_key, smt_key_to_account_id}; +use crate::errors::AccountTreeError; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; -use crate::{AccountTreeError, Word}; // ACCOUNT WITNESS // ================================================================================================ diff --git a/crates/miden-protocol/src/block/header.rs b/crates/miden-protocol/src/block/header.rs index fec485c164..33b5f0f8dd 100644 --- a/crates/miden-protocol/src/block/header.rs +++ b/crates/miden-protocol/src/block/header.rs @@ -4,6 +4,7 @@ use alloc::vec::Vec; use crate::account::{AccountId, AccountType}; use crate::block::BlockNumber; use crate::crypto::dsa::ecdsa_k256_keccak::PublicKey; +use crate::errors::FeeError; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -11,7 +12,7 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{FeeError, Felt, Hasher, Word, ZERO}; +use crate::{Felt, Hasher, Word, ZERO}; // BLOCK HEADER // ================================================================================================ diff --git a/crates/miden-protocol/src/block/nullifier_tree/mod.rs b/crates/miden-protocol/src/block/nullifier_tree/mod.rs index 812e3906c2..18332812ea 100644 --- a/crates/miden-protocol/src/block/nullifier_tree/mod.rs +++ b/crates/miden-protocol/src/block/nullifier_tree/mod.rs @@ -329,10 +329,11 @@ mod tests { use assert_matches::assert_matches; use super::NullifierTree; + use crate::Word; use crate::block::BlockNumber; use crate::block::nullifier_tree::NullifierBlock; + use crate::errors::NullifierTreeError; use crate::note::Nullifier; - use crate::{NullifierTreeError, Word}; #[test] fn leaf_value_encode_decode() { diff --git a/crates/miden-protocol/src/lib.rs b/crates/miden-protocol/src/lib.rs index c84363f82a..0b4b8be503 100644 --- a/crates/miden-protocol/src/lib.rs +++ b/crates/miden-protocol/src/lib.rs @@ -25,35 +25,6 @@ mod constants; // ================================================================================================ pub use constants::*; -pub use errors::{ - AccountDeltaError, - AccountError, - AccountIdError, - AccountTreeError, - AddressError, - AssetError, - AssetVaultError, - AuthSchemeError, - BatchAccountUpdateError, - FeeError, - NetworkIdError, - NoteError, - NullifierTreeError, - PartialAssetVaultError, - PartialBlockchainError, - ProposedBatchError, - ProposedBlockError, - ProvenBatchError, - ProvenTransactionError, - StorageMapError, - StorageSlotNameError, - TokenSymbolError, - TransactionEventError, - TransactionInputError, - TransactionOutputError, - TransactionScriptError, - TransactionTraceParsingError, -}; pub use miden_core::mast::{MastForest, MastNodeId}; pub use miden_core::prettier::PrettyPrint; pub use miden_core::{EMPTY_WORD, Felt, FieldElement, ONE, StarkField, WORD_SIZE, ZERO}; diff --git a/crates/miden-protocol/src/note/attachment.rs b/crates/miden-protocol/src/note/attachment.rs index af15c29ec2..56a46e4b7d 100644 --- a/crates/miden-protocol/src/note/attachment.rs +++ b/crates/miden-protocol/src/note/attachment.rs @@ -2,8 +2,9 @@ use alloc::string::ToString; use alloc::vec::Vec; use crate::crypto::SequentialCommit; +use crate::errors::NoteError; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; -use crate::{Felt, Hasher, NoteError, Word}; +use crate::{Felt, Hasher, Word}; // NOTE ATTACHMENT // ================================================================================================ diff --git a/crates/miden-protocol/src/note/execution_hint.rs b/crates/miden-protocol/src/note/execution_hint.rs index 6dd14d05c8..10d11503cb 100644 --- a/crates/miden-protocol/src/note/execution_hint.rs +++ b/crates/miden-protocol/src/note/execution_hint.rs @@ -1,8 +1,9 @@ // NOTE EXECUTION HINT // ================================================================================================ +use crate::Felt; use crate::block::BlockNumber; -use crate::{Felt, NoteError}; +use crate::errors::NoteError; /// Specifies the conditions under which a note is ready to be consumed. /// These conditions are meant to be encoded in the note script as well. diff --git a/crates/miden-protocol/src/note/metadata.rs b/crates/miden-protocol/src/note/metadata.rs index e1c3714bb7..d1a58ceace 100644 --- a/crates/miden-protocol/src/note/metadata.rs +++ b/crates/miden-protocol/src/note/metadata.rs @@ -10,8 +10,9 @@ use super::{ Serializable, Word, }; +use crate::Hasher; +use crate::errors::NoteError; use crate::note::{NoteAttachment, NoteAttachmentKind, NoteAttachmentScheme}; -use crate::{Hasher, NoteError}; // NOTE METADATA // ================================================================================================ diff --git a/crates/miden-protocol/src/note/mod.rs b/crates/miden-protocol/src/note/mod.rs index 7af4278953..27aeda9a54 100644 --- a/crates/miden-protocol/src/note/mod.rs +++ b/crates/miden-protocol/src/note/mod.rs @@ -3,7 +3,8 @@ use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; use miden_processor::DeserializationError; use crate::account::AccountId; -use crate::{Felt, Hasher, NoteError, WORD_SIZE, ZERO}; +use crate::errors::NoteError; +use crate::{Felt, Hasher, WORD_SIZE, ZERO}; mod assets; pub use assets::NoteAssets; diff --git a/crates/miden-protocol/src/note/note_type.rs b/crates/miden-protocol/src/note/note_type.rs index 9a5871da0d..f426ea58ab 100644 --- a/crates/miden-protocol/src/note/note_type.rs +++ b/crates/miden-protocol/src/note/note_type.rs @@ -1,6 +1,8 @@ use core::fmt::Display; use core::str::FromStr; +use crate::Felt; +use crate::errors::NoteError; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -8,7 +10,6 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{Felt, NoteError}; // CONSTANTS // ================================================================================================ diff --git a/crates/miden-protocol/src/note/script.rs b/crates/miden-protocol/src/note/script.rs index 457d72cfb5..eb11c82e0b 100644 --- a/crates/miden-protocol/src/note/script.rs +++ b/crates/miden-protocol/src/note/script.rs @@ -6,6 +6,7 @@ use miden_processor::MastNodeExt; use super::Felt; use crate::assembly::mast::{MastForest, MastNodeId}; +use crate::errors::NoteError; use crate::utils::serde::{ ByteReader, ByteWriter, @@ -14,7 +15,7 @@ use crate::utils::serde::{ Serializable, }; use crate::vm::Program; -use crate::{NoteError, PrettyPrint, Word}; +use crate::{PrettyPrint, Word}; // NOTE SCRIPT // ================================================================================================ diff --git a/crates/miden-protocol/src/testing/asset.rs b/crates/miden-protocol/src/testing/asset.rs index 1f6964d2aa..b1b12223bd 100644 --- a/crates/miden-protocol/src/testing/asset.rs +++ b/crates/miden-protocol/src/testing/asset.rs @@ -1,9 +1,9 @@ use rand::Rng; use rand::distr::StandardUniform; -use crate::AssetError; use crate::account::{AccountId, AccountIdPrefix, AccountType}; use crate::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; +use crate::errors::AssetError; use crate::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, diff --git a/crates/miden-protocol/src/testing/partial_blockchain.rs b/crates/miden-protocol/src/testing/partial_blockchain.rs index 4497abbdf3..325c12566e 100644 --- a/crates/miden-protocol/src/testing/partial_blockchain.rs +++ b/crates/miden-protocol/src/testing/partial_blockchain.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; -use crate::PartialBlockchainError; use crate::block::{BlockHeader, BlockNumber, Blockchain}; +use crate::errors::PartialBlockchainError; use crate::transaction::PartialBlockchain; impl PartialBlockchain { diff --git a/crates/miden-protocol/src/transaction/inputs/mod.rs b/crates/miden-protocol/src/transaction/inputs/mod.rs index ef3d6b367d..a8122e0908 100644 --- a/crates/miden-protocol/src/transaction/inputs/mod.rs +++ b/crates/miden-protocol/src/transaction/inputs/mod.rs @@ -23,10 +23,10 @@ use crate::asset::{AssetVaultKey, AssetWitness, PartialVault}; use crate::block::account_tree::{AccountWitness, account_id_to_smt_index}; use crate::block::{BlockHeader, BlockNumber}; use crate::crypto::merkle::SparseMerklePath; -use crate::errors::TransactionInputsExtractionError; +use crate::errors::{TransactionInputError, TransactionInputsExtractionError}; use crate::note::{Note, NoteInclusionProof}; use crate::transaction::{TransactionAdviceInputs, TransactionArgs, TransactionScript}; -use crate::{Felt, TransactionInputError, Word}; +use crate::{Felt, Word}; #[cfg(test)] mod tests; diff --git a/crates/miden-protocol/src/transaction/inputs/notes.rs b/crates/miden-protocol/src/transaction/inputs/notes.rs index 72cad6931f..638ae56eba 100644 --- a/crates/miden-protocol/src/transaction/inputs/notes.rs +++ b/crates/miden-protocol/src/transaction/inputs/notes.rs @@ -373,7 +373,7 @@ mod input_notes_tests { use miden_core::Word; use super::InputNotes; - use crate::TransactionInputError; + use crate::errors::TransactionInputError; use crate::note::Note; use crate::transaction::InputNote; diff --git a/crates/miden-protocol/src/transaction/kernel/mod.rs b/crates/miden-protocol/src/transaction/kernel/mod.rs index d759c822e5..334376165a 100644 --- a/crates/miden-protocol/src/transaction/kernel/mod.rs +++ b/crates/miden-protocol/src/transaction/kernel/mod.rs @@ -12,12 +12,13 @@ use crate::assembly::{Assembler, DefaultSourceManager, KernelLibrary}; use crate::asset::FungibleAsset; use crate::block::BlockNumber; use crate::crypto::SequentialCommit; +use crate::errors::TransactionOutputError; use crate::protocol::ProtocolLib; use crate::transaction::{OutputNote, OutputNotes, TransactionInputs, TransactionOutputs}; use crate::utils::serde::Deserializable; use crate::utils::sync::LazyLock; use crate::vm::{AdviceInputs, Program, ProgramInfo, StackInputs, StackOutputs}; -use crate::{Felt, Hasher, TransactionOutputError, Word}; +use crate::{Felt, Hasher, Word}; mod procedures; diff --git a/crates/miden-protocol/src/transaction/outputs.rs b/crates/miden-protocol/src/transaction/outputs.rs index 4be4b2b9d7..179da30367 100644 --- a/crates/miden-protocol/src/transaction/outputs.rs +++ b/crates/miden-protocol/src/transaction/outputs.rs @@ -6,6 +6,7 @@ use core::fmt::Debug; use crate::account::AccountHeader; use crate::asset::FungibleAsset; use crate::block::BlockNumber; +use crate::errors::TransactionOutputError; use crate::note::{ Note, NoteAssets, @@ -23,7 +24,7 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{Felt, Hasher, MAX_OUTPUT_NOTES_PER_TX, TransactionOutputError, Word}; +use crate::{Felt, Hasher, MAX_OUTPUT_NOTES_PER_TX, Word}; // TRANSACTION OUTPUTS // ================================================================================================ @@ -348,9 +349,10 @@ mod output_notes_tests { use assert_matches::assert_matches; use super::OutputNotes; + use crate::Word; + use crate::errors::TransactionOutputError; use crate::note::Note; use crate::transaction::OutputNote; - use crate::{TransactionOutputError, Word}; #[test] fn test_duplicate_output_notes() -> anyhow::Result<()> { diff --git a/crates/miden-protocol/src/transaction/partial_blockchain.rs b/crates/miden-protocol/src/transaction/partial_blockchain.rs index 0d63027fe8..86dfcb409f 100644 --- a/crates/miden-protocol/src/transaction/partial_blockchain.rs +++ b/crates/miden-protocol/src/transaction/partial_blockchain.rs @@ -2,10 +2,10 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; use core::ops::RangeTo; -use crate::PartialBlockchainError; use crate::block::{BlockHeader, BlockNumber}; use crate::crypto::merkle::InnerNodeInfo; use crate::crypto::merkle::mmr::{MmrPeaks, PartialMmr}; +use crate::errors::PartialBlockchainError; use crate::utils::serde::{Deserializable, Serializable}; // PARTIAL BLOCKCHAIN @@ -282,12 +282,13 @@ mod tests { use rand_chacha::ChaCha20Rng; use super::PartialBlockchain; + use crate::Word; use crate::alloc::vec::Vec; use crate::block::{BlockHeader, BlockNumber, FeeParameters}; use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey; use crate::crypto::merkle::mmr::{Mmr, PartialMmr}; + use crate::errors::PartialBlockchainError; use crate::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; - use crate::{PartialBlockchainError, Word}; #[test] fn test_partial_blockchain_add() { diff --git a/crates/miden-protocol/src/transaction/proven_tx.rs b/crates/miden-protocol/src/transaction/proven_tx.rs index e0fde4d293..ab679b9ffc 100644 --- a/crates/miden-protocol/src/transaction/proven_tx.rs +++ b/crates/miden-protocol/src/transaction/proven_tx.rs @@ -7,6 +7,7 @@ use crate::account::Account; use crate::account::delta::AccountUpdateDetails; use crate::asset::FungibleAsset; use crate::block::BlockNumber; +use crate::errors::ProvenTransactionError; use crate::note::NoteHeader; use crate::transaction::{ AccountId, @@ -24,7 +25,7 @@ use crate::utils::serde::{ Serializable, }; use crate::vm::ExecutionProof; -use crate::{ACCOUNT_UPDATE_MAX_SIZE, ProvenTransactionError, Word}; +use crate::{ACCOUNT_UPDATE_MAX_SIZE, Word}; // PROVEN TRANSACTION // ================================================================================================ @@ -705,6 +706,7 @@ mod tests { }; use crate::asset::FungibleAsset; use crate::block::BlockNumber; + use crate::errors::ProvenTransactionError; use crate::testing::account_id::{ ACCOUNT_ID_PRIVATE_SENDER, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, @@ -713,14 +715,7 @@ mod tests { use crate::testing::noop_auth_component::NoopAuthComponent; use crate::transaction::{ProvenTransactionBuilder, TxAccountUpdate}; use crate::utils::Serializable; - use crate::{ - ACCOUNT_UPDATE_MAX_SIZE, - EMPTY_WORD, - LexicographicWord, - ONE, - ProvenTransactionError, - Word, - }; + use crate::{ACCOUNT_UPDATE_MAX_SIZE, EMPTY_WORD, LexicographicWord, ONE, Word}; fn check_if_sync() {} fn check_if_send() {} diff --git a/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs index 19f60867d9..276a85f1bf 100644 --- a/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_acl.rs @@ -1,5 +1,6 @@ use alloc::vec::Vec; +use miden_protocol::Word; use miden_protocol::account::auth::PublicKeyCommitment; use miden_protocol::account::{ AccountCode, @@ -8,8 +9,8 @@ use miden_protocol::account::{ StorageSlot, StorageSlotName, }; +use miden_protocol::errors::AccountError; use miden_protocol::utils::sync::LazyLock; -use miden_protocol::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_acl_library; diff --git a/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs index 1bb3f9163f..f29bf71732 100644 --- a/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak_multisig.rs @@ -1,10 +1,11 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; +use miden_protocol::Word; use miden_protocol::account::auth::PublicKeyCommitment; use miden_protocol::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; +use miden_protocol::errors::AccountError; use miden_protocol::utils::sync::LazyLock; -use miden_protocol::{AccountError, Word}; use crate::account::components::ecdsa_k256_keccak_multisig_library; diff --git a/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs index 16845d0db8..816e1f227d 100644 --- a/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs @@ -1,5 +1,6 @@ use alloc::vec::Vec; +use miden_protocol::Word; use miden_protocol::account::auth::PublicKeyCommitment; use miden_protocol::account::{ AccountCode, @@ -8,8 +9,8 @@ use miden_protocol::account::{ StorageSlot, StorageSlotName, }; +use miden_protocol::errors::AccountError; use miden_protocol::utils::sync::LazyLock; -use miden_protocol::{AccountError, Word}; use crate::account::components::rpo_falcon_512_acl_library; diff --git a/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs index d169d35586..6fc0c4e284 100644 --- a/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs @@ -1,10 +1,11 @@ use alloc::collections::BTreeSet; use alloc::vec::Vec; +use miden_protocol::Word; use miden_protocol::account::auth::PublicKeyCommitment; use miden_protocol::account::{AccountComponent, StorageMap, StorageSlot, StorageSlotName}; +use miden_protocol::errors::AccountError; use miden_protocol::utils::sync::LazyLock; -use miden_protocol::{AccountError, Word}; use crate::account::components::rpo_falcon_512_multisig_library; diff --git a/crates/miden-standards/src/account/faucets/mod.rs b/crates/miden-standards/src/account/faucets/mod.rs index 22a13dcb4f..9733cd53ab 100644 --- a/crates/miden-standards/src/account/faucets/mod.rs +++ b/crates/miden-standards/src/account/faucets/mod.rs @@ -1,8 +1,9 @@ use alloc::string::String; +use miden_protocol::Felt; use miden_protocol::account::{Account, AccountStorage, AccountType, StorageSlotName}; +use miden_protocol::errors::{AccountError, TokenSymbolError}; use miden_protocol::utils::sync::LazyLock; -use miden_protocol::{AccountError, Felt, TokenSymbolError}; use thiserror::Error; mod basic_fungible; diff --git a/crates/miden-standards/src/account/interface/test.rs b/crates/miden-standards/src/account/interface/test.rs index 002ae1822f..3675226458 100644 --- a/crates/miden-standards/src/account/interface/test.rs +++ b/crates/miden-standards/src/account/interface/test.rs @@ -3,6 +3,7 @@ use miden_protocol::account::auth::PublicKeyCommitment; use miden_protocol::account::{AccountBuilder, AccountComponent, AccountType}; use miden_protocol::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol}; use miden_protocol::crypto::rand::{FeltRng, RpoRandomCoin}; +use miden_protocol::errors::NoteError; use miden_protocol::note::{ Note, NoteAssets, @@ -17,7 +18,7 @@ use miden_protocol::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2, }; -use miden_protocol::{Felt, NoteError, Word}; +use miden_protocol::{Felt, Word}; use crate::AuthScheme; use crate::account::auth::{ diff --git a/crates/miden-standards/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs index fe412229a7..c8e66a3020 100644 --- a/crates/miden-standards/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -1,5 +1,6 @@ use alloc::string::String; +use miden_protocol::Word; use miden_protocol::account::{ Account, AccountBuilder, @@ -7,7 +8,7 @@ use miden_protocol::account::{ AccountStorageMode, AccountType, }; -use miden_protocol::{AccountError, Word}; +use miden_protocol::errors::AccountError; use thiserror::Error; use super::AuthScheme; diff --git a/crates/miden-standards/src/note/mint_inputs.rs b/crates/miden-standards/src/note/mint_inputs.rs index eb52952956..3fd62e3e67 100644 --- a/crates/miden-standards/src/note/mint_inputs.rs +++ b/crates/miden-standards/src/note/mint_inputs.rs @@ -1,7 +1,8 @@ use alloc::vec::Vec; +use miden_protocol::errors::NoteError; use miden_protocol::note::{NoteAttachment, NoteInputs, NoteRecipient}; -use miden_protocol::{Felt, MAX_INPUTS_PER_NOTE, NoteError, Word}; +use miden_protocol::{Felt, MAX_INPUTS_PER_NOTE, Word}; /// Represents the different input formats for MINT notes. /// - Private: Creates a private output note using a precomputed recipient digest (12 MINT note diff --git a/crates/miden-standards/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs index 0fa70b64ae..002de6e7d7 100644 --- a/crates/miden-standards/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -4,6 +4,7 @@ use miden_protocol::account::AccountId; use miden_protocol::asset::Asset; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::rand::FeltRng; +use miden_protocol::errors::NoteError; use miden_protocol::note::{ Note, NoteAssets, @@ -15,7 +16,7 @@ use miden_protocol::note::{ NoteTag, NoteType, }; -use miden_protocol::{Felt, NoteError, Word}; +use miden_protocol::{Felt, Word}; use utils::build_swap_tag; pub mod mint_inputs; diff --git a/crates/miden-standards/src/note/network_account_target.rs b/crates/miden-standards/src/note/network_account_target.rs index b5f82e460f..2ea446bafb 100644 --- a/crates/miden-standards/src/note/network_account_target.rs +++ b/crates/miden-standards/src/note/network_account_target.rs @@ -1,4 +1,6 @@ +use miden_protocol::Word; use miden_protocol::account::AccountId; +use miden_protocol::errors::{AccountIdError, NoteError}; use miden_protocol::note::{ NoteAttachment, NoteAttachmentContent, @@ -6,7 +8,6 @@ use miden_protocol::note::{ NoteAttachmentScheme, NoteExecutionHint, }; -use miden_protocol::{AccountIdError, NoteError, Word}; use crate::note::WellKnownNoteAttachment; diff --git a/crates/miden-standards/src/note/utils.rs b/crates/miden-standards/src/note/utils.rs index 1d9fb780ee..b111e68bce 100644 --- a/crates/miden-standards/src/note/utils.rs +++ b/crates/miden-standards/src/note/utils.rs @@ -1,8 +1,9 @@ use miden_protocol::account::AccountId; use miden_protocol::asset::Asset; use miden_protocol::block::BlockNumber; +use miden_protocol::errors::NoteError; use miden_protocol::note::{NoteInputs, NoteRecipient, NoteTag, NoteType}; -use miden_protocol::{Felt, NoteError, Word}; +use miden_protocol::{Felt, Word}; use super::well_known_note::WellKnownNote; diff --git a/crates/miden-standards/src/testing/note.rs b/crates/miden-standards/src/testing/note.rs index 7312ed0ad9..4e4460f432 100644 --- a/crates/miden-standards/src/testing/note.rs +++ b/crates/miden-standards/src/testing/note.rs @@ -6,6 +6,7 @@ use miden_protocol::account::AccountId; use miden_protocol::assembly::debuginfo::{SourceLanguage, SourceManagerSync, Uri}; use miden_protocol::assembly::{DefaultSourceManager, Library}; use miden_protocol::asset::Asset; +use miden_protocol::errors::NoteError; use miden_protocol::note::{ Note, NoteAssets, @@ -17,7 +18,7 @@ use miden_protocol::note::{ NoteType, }; use miden_protocol::testing::note::DEFAULT_NOTE_CODE; -use miden_protocol::{Felt, NoteError, Word}; +use miden_protocol::{Felt, Word}; use rand::Rng; use crate::code_builder::CodeBuilder; diff --git a/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs b/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs index 3f6b1f509f..2d44e187b9 100644 --- a/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs +++ b/crates/miden-testing/src/kernel_tests/batch/proposed_batch.rs @@ -3,14 +3,15 @@ use std::collections::BTreeMap; use anyhow::Context; use assert_matches::assert_matches; +use miden_protocol::Word; use miden_protocol::account::{Account, AccountId, AccountStorageMode}; use miden_protocol::batch::ProposedBatch; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::MerkleError; +use miden_protocol::errors::{BatchAccountUpdateError, ProposedBatchError}; use miden_protocol::note::{Note, NoteType}; use miden_protocol::testing::account_id::AccountIdBuilder; use miden_protocol::transaction::{InputNote, InputNoteCommitment, OutputNote, PartialBlockchain}; -use miden_protocol::{BatchAccountUpdateError, ProposedBatchError, Word}; use miden_standards::testing::account_component::MockAccountComponent; use miden_standards::testing::note::NoteBuilder; use rand::rngs::SmallRng; diff --git a/crates/miden-testing/src/kernel_tests/block/header_errors.rs b/crates/miden-testing/src/kernel_tests/block/header_errors.rs index a4a48262c1..e3d3864f3f 100644 --- a/crates/miden-testing/src/kernel_tests/block/header_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/header_errors.rs @@ -2,6 +2,7 @@ use alloc::vec::Vec; use anyhow::Context; use assert_matches::assert_matches; +use miden_protocol::Word; use miden_protocol::account::delta::AccountUpdateDetails; use miden_protocol::account::{ Account, @@ -14,10 +15,10 @@ use miden_protocol::account::{ use miden_protocol::asset::FungibleAsset; use miden_protocol::batch::ProvenBatch; use miden_protocol::block::{BlockInputs, BlockNumber, ProposedBlock}; +use miden_protocol::errors::{AccountTreeError, NullifierTreeError, ProposedBlockError}; use miden_protocol::note::NoteType; use miden_protocol::transaction::ProvenTransactionBuilder; use miden_protocol::vm::ExecutionProof; -use miden_protocol::{AccountTreeError, NullifierTreeError, ProposedBlockError, Word}; use miden_standards::testing::account_component::{IncrNonceAuthComponent, MockAccountComponent}; use miden_standards::testing::mock_account::MockAccountExt; use miden_tx::LocalTransactionProver; diff --git a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs index 4bb31ba9af..f7fea9e494 100644 --- a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs @@ -4,11 +4,12 @@ use std::vec::Vec; use assert_matches::assert_matches; use miden_processor::crypto::MerklePath; +use miden_protocol::MAX_BATCHES_PER_BLOCK; use miden_protocol::asset::FungibleAsset; use miden_protocol::block::{BlockInputs, BlockNumber, ProposedBlock}; use miden_protocol::crypto::merkle::SparseMerklePath; +use miden_protocol::errors::ProposedBlockError; use miden_protocol::note::{NoteAttachment, NoteInclusionProof, NoteType}; -use miden_protocol::{MAX_BATCHES_PER_BLOCK, ProposedBlockError}; use miden_standards::note::create_p2id_note; use miden_tx::LocalTransactionProver; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs index 497d083047..88c7d85737 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_asset_vault.rs @@ -1,6 +1,7 @@ use assert_matches::assert_matches; use miden_protocol::account::AccountId; use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; +use miden_protocol::errors::AssetVaultError; use miden_protocol::errors::tx_kernel::{ ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW, ERR_VAULT_FUNGIBLE_MAX_AMOUNT_EXCEEDED, @@ -15,7 +16,7 @@ use miden_protocol::testing::account_id::{ }; use miden_protocol::testing::constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}; use miden_protocol::transaction::memory; -use miden_protocol::{AssetVaultError, Felt, ONE, Word, ZERO}; +use miden_protocol::{Felt, ONE, Word, ZERO}; use crate::kernel_tests::tx::ExecutionOutputExt; use crate::{TransactionContextBuilder, assert_execution_error}; diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 4263e9ebed..19f014e18c 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -42,11 +42,12 @@ use miden_protocol::block::{ }; use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey; use miden_protocol::crypto::merkle::smt::Smt; +use miden_protocol::errors::NoteError; use miden_protocol::note::{Note, NoteAttachment, NoteDetails, NoteType}; use miden_protocol::testing::account_id::ACCOUNT_ID_NATIVE_ASSET_FAUCET; use miden_protocol::testing::random_signer::RandomBlockSigner; use miden_protocol::transaction::{OrderedTransactionHeaders, OutputNote, TransactionKernel}; -use miden_protocol::{Felt, MAX_OUTPUT_NOTES_PER_BATCH, NoteError, Word, ZERO}; +use miden_protocol::{Felt, MAX_OUTPUT_NOTES_PER_BATCH, Word, ZERO}; use miden_standards::account::faucets::{BasicFungibleFaucet, NetworkFungibleFaucet}; use miden_standards::account::wallets::BasicWallet; use miden_standards::note::{create_p2id_note, create_p2ide_note, create_swap_note}; diff --git a/crates/miden-testing/tests/scripts/swap.rs b/crates/miden-testing/tests/scripts/swap.rs index 701aec7a1a..748b9110c2 100644 --- a/crates/miden-testing/tests/scripts/swap.rs +++ b/crates/miden-testing/tests/scripts/swap.rs @@ -1,6 +1,7 @@ use anyhow::Context; use miden_protocol::account::{Account, AccountId, AccountStorageMode, AccountType}; use miden_protocol::asset::{Asset, FungibleAsset, NonFungibleAsset}; +use miden_protocol::errors::NoteError; use miden_protocol::note::{Note, NoteAssets, NoteDetails, NoteMetadata, NoteTag, NoteType}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, @@ -8,7 +9,7 @@ use miden_protocol::testing::account_id::{ AccountIdBuilder, }; use miden_protocol::transaction::OutputNote; -use miden_protocol::{Felt, NoteError, Word}; +use miden_protocol::{Felt, Word}; use miden_standards::code_builder::CodeBuilder; use miden_standards::note::utils; use miden_testing::{Auth, MockChain}; diff --git a/crates/miden-tx-batch-prover/src/local_batch_prover.rs b/crates/miden-tx-batch-prover/src/local_batch_prover.rs index 1c790a92ee..4e7ccfffc7 100644 --- a/crates/miden-tx-batch-prover/src/local_batch_prover.rs +++ b/crates/miden-tx-batch-prover/src/local_batch_prover.rs @@ -1,7 +1,7 @@ use alloc::boxed::Box; -use miden_protocol::ProvenBatchError; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; +use miden_protocol::errors::ProvenBatchError; use miden_tx::TransactionVerifier; // LOCAL BATCH PROVER diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index e6dca30443..0e0cc1dda2 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -10,19 +10,18 @@ use miden_protocol::assembly::diagnostics::reporting::PrintDiagnostic; use miden_protocol::asset::AssetVaultKey; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::smt::SmtProofError; -use miden_protocol::note::{NoteId, NoteMetadata}; -use miden_protocol::transaction::TransactionSummary; -use miden_protocol::{ +use miden_protocol::errors::{ AccountDeltaError, AccountError, AssetError, - Felt, NoteError, ProvenTransactionError, TransactionInputError, TransactionOutputError, - Word, }; +use miden_protocol::note::{NoteId, NoteMetadata}; +use miden_protocol::transaction::TransactionSummary; +use miden_protocol::{Felt, Word}; use miden_verifier::VerificationError; use thiserror::Error; From 1827299f21eec8397b96eb8831c6ed8a35fea392 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Fri, 16 Jan 2026 23:51:04 +0300 Subject: [PATCH 112/114] Post-migration follow up renamings (VM v0.20.1) (#2264) --- CHANGELOG.md | 1 + .../miden-agglayer/asm/bridge/bridge_out.masm | 3 +- .../asm/kernels/transaction/lib/account.masm | 9 +-- .../transaction/lib/account_delta.masm | 12 ++-- .../kernels/transaction/lib/asset_vault.masm | 14 +--- .../asm/kernels/transaction/lib/epilogue.masm | 3 +- .../asm/kernels/transaction/lib/note.masm | 4 +- .../asm/kernels/transaction/lib/prologue.masm | 36 +++++----- crates/miden-protocol/asm/protocol/note.masm | 14 ++-- crates/miden-protocol/src/account/auth.rs | 62 ++++++++-------- .../src/account/component/storage/toml/mod.rs | 2 +- .../src/transaction/kernel/procedures.rs | 28 ++++---- ...po_falcon_512.masm => falcon_512_rpo.masm} | 12 ++-- ...n_512_acl.masm => falcon_512_rpo_acl.masm} | 16 ++--- ...isig.masm => falcon_512_rpo_multisig.masm} | 16 ++--- .../asm/standards/auth/ecdsa_k256_keccak.masm | 3 +- ...{rpo_falcon512.masm => falcon512_rpo.masm} | 3 +- .../asm/standards/auth/mod.masm | 4 +- .../{rpo_falcon_512.rs => falcon_512_rpo.rs} | 22 +++--- ...alcon_512_acl.rs => falcon_512_rpo_acl.rs} | 64 ++++++++--------- ...multisig.rs => falcon_512_rpo_multisig.rs} | 64 ++++++++--------- .../miden-standards/src/account/auth/mod.rs | 12 ++-- .../src/account/components/mod.rs | 72 +++++++++---------- .../src/account/faucets/basic_fungible.rs | 24 +++---- .../src/account/interface/component.rs | 56 +++++++-------- .../src/account/interface/extension.rs | 18 ++--- .../src/account/interface/test.rs | 36 +++++----- .../src/account/wallets/mod.rs | 18 ++--- crates/miden-standards/src/auth_scheme.rs | 14 ++-- .../src/kernel_tests/tx/test_tx.rs | 22 +++--- crates/miden-testing/src/mock_chain/auth.rs | 24 +++---- crates/miden-testing/tests/auth/multisig.rs | 28 ++++---- .../tests/auth/rpo_falcon_acl.rs | 8 +-- crates/miden-testing/tests/wallet/mod.rs | 8 +-- docs/src/account/components.md | 12 ++-- docs/src/transaction.md | 4 +- 36 files changed, 373 insertions(+), 375 deletions(-) rename crates/miden-standards/asm/account_components/auth/{rpo_falcon_512.masm => falcon_512_rpo.masm} (79%) rename crates/miden-standards/asm/account_components/auth/{rpo_falcon_512_acl.masm => falcon_512_rpo_acl.masm} (90%) rename crates/miden-standards/asm/account_components/auth/{rpo_falcon_512_multisig.masm => falcon_512_rpo_multisig.masm} (96%) rename crates/miden-standards/asm/standards/auth/{rpo_falcon512.masm => falcon512_rpo.masm} (99%) rename crates/miden-standards/src/account/auth/{rpo_falcon_512.rs => falcon_512_rpo.rs} (73%) rename crates/miden-standards/src/account/auth/{rpo_falcon_512_acl.rs => falcon_512_rpo_acl.rs} (88%) rename crates/miden-standards/src/account/auth/{rpo_falcon_512_multisig.rs => falcon_512_rpo_multisig.rs} (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c425397748..acec386cb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ - [BREAKING] Refactored `InitStorageData` to support native types ([#2230](https://github.com/0xMiden/miden-base/pull/2230)). - Added `StorageSchema::commitment()` ([#2244](https://github.com/0xMiden/miden-base/pull/2244)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). +- [BREAKING] `RpoFalcon512` was renamed to `Falcon512Rpo` everywhere, including procedure and file names ([#2264](https://github.com/0xMiden/miden-base/pull/2264)). - [BREAKING] Refactor note tags to be arbitrary `u32` values and drop previous validation ([#2219](https://github.com/0xMiden/miden-base/pull/2219)). - [BREAKING] Refactored `InitStorageData` to support native types ([#2230](https://github.com/0xMiden/miden-base/pull/2230)). - [BREAKING] Removed top-level error exports from `miden-protocol` crate (the are still accessible under `miden_protocol::errors`). diff --git a/crates/miden-agglayer/asm/bridge/bridge_out.masm b/crates/miden-agglayer/asm/bridge/bridge_out.masm index 023677e511..3c53b62763 100644 --- a/crates/miden-agglayer/asm/bridge/bridge_out.masm +++ b/crates/miden-agglayer/asm/bridge/bridge_out.masm @@ -2,6 +2,7 @@ use miden::protocol::active_note use miden::protocol::note use miden::protocol::output_note use miden::core::crypto::hashes::keccak256 +use miden::core::crypto::hashes::rpo256 use miden::core::word use miden::agglayer::local_exit_tree @@ -31,7 +32,7 @@ proc compute_burn_note_serial_num exec.active_note::get_serial_number # => [B2AGG_SERIAL_NUM, ASSET] - hmerge + exec.rpo256::merge # => [SERIAL_NUM] end diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/account.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm index 68bf69d047..7c2458f0ba 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account.masm @@ -330,7 +330,8 @@ pub proc compute_commitment # stream account data and compute sequential hash. We perform two `mem_stream` operations # because the account data consists of exactly 4 words. - mem_stream hperm mem_stream hperm + mem_stream exec.rpo256::permute + mem_stream exec.rpo256::permute # => [RATE, RATE, PERM, account_data_ptr'] # extract account commitment @@ -923,7 +924,7 @@ pub proc validate_seed # perform first permutation of seed and code_commitment (from advice stack) # perm(seed, code_commitment) - hperm + exec.rpo256::permute # => [RATE, RATE, PERM, EMPTY_WORD] # clear rate elements @@ -934,7 +935,7 @@ pub proc validate_seed swapw exec.memory::get_account_storage_commitment swapw # => [EMPTY_WORD, STORAGE_COMMITMENT, PERM] - hperm + exec.rpo256::permute # => [RATE, RATE, CAP] # extract digest @@ -1886,7 +1887,7 @@ end #! Inputs: [KEY] #! Outputs: [HASHED_KEY] proc hash_map_key - hash + exec.rpo256::hash # => [HASHED_KEY] end diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm b/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm index 79ff5365ff..229103d606 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/account_delta.masm @@ -60,7 +60,7 @@ pub proc compute_commitment padw # => [EMPTY_WORD, ID_AND_NONCE, CAPACITY] - hperm + exec.rpo256::permute # => [RATE, RATE, PERM] # save the ID and nonce digest (the 2nd rate word) for a later check @@ -199,7 +199,7 @@ proc update_value_slot_delta swapw # => [CURRENT_VALUE, slot_id_prefix, slot_id_suffix, 0, domain, PERM] - hperm + exec.rpo256::permute # => [RATE, RATE, PERM] else # drop init value, current value and slot name @@ -273,7 +273,7 @@ proc update_map_slot_delta swapdw dropw dropw # => [NEW_VALUE, KEY, PERM] - hperm + exec.rpo256::permute # => [RATE, RATE, PERM] else # discard the key and init and new value words loaded from the map @@ -312,7 +312,7 @@ proc update_map_slot_delta push.DOMAIN_MAP loc_load.4 loc_load.0 loc_load.1 padw # => [EMPTY_WORD, [slot_id_prefix, slot_id_suffix, num_changed_entries, domain], PERM] - hperm + exec.rpo256::permute # => [RATE, RATE, PERM] end # => [RATE, RATE, PERM] @@ -377,7 +377,7 @@ proc update_fungible_asset_delta swapdw dropw dropw # => [[faucet_id_prefix, faucet_id_suffix, 0, delta_amount_abs], [0, 0, was_added, domain], PERM] - hperm + exec.rpo256::permute # => [RATE, RATE, PERM] else # discard values loaded from map: KEY, VALUE0 @@ -449,7 +449,7 @@ proc update_non_fungible_asset_delta swapdw dropw dropw # => [ASSET, [0, 0, was_added, domain], PERM] - hperm + exec.rpo256::permute # => [RATE, RATE, PERM] else # discard the two key and value words loaded from the map diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm b/crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm index 111224287f..2559bed3bc 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm @@ -26,14 +26,6 @@ const ERR_VAULT_REMOVE_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to re const ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND="failed to remove non-existent non-fungible asset from the vault" -# EVENTS -# ================================================================================================= - -# The event is from `stdlib`, visible through the prefix and is _not_ a `TransactionEvent`. -# Care must be taken it stays in sync with the `miden-stdlib` definition. Note that generally speaking -# we should not emit `"stdlib"` events, consider this "hijacking", tread carefully. -const SMT_PEEK_EVENT=event("miden::core::collections::smt::smt_peek") - # CONSTANTS # ================================================================================================= @@ -118,7 +110,7 @@ pub proc peek_balance # => [ASSET_KEY, ASSET_VAULT_ROOT] # lookup asset - emit.SMT_PEEK_EVENT + exec.smt::peek # OS => [ASSET_KEY, ASSET_VAULT_ROOT] # AS => [ASSET] @@ -211,7 +203,7 @@ pub proc add_fungible_asset mem_loadw_be swapw # => [ASSET_KEY, VAULT_ROOT, faucet_id_prefix, faucet_id_suffix, amount, vault_root_ptr] - emit.SMT_PEEK_EVENT adv_loadw + exec.smt::peek adv_loadw # => [CUR_VAULT_VALUE, VAULT_ROOT, faucet_id_prefix, faucet_id_suffix, amount, vault_root_ptr] swapw # => [VAULT_ROOT, CUR_VAULT_VALUE, faucet_id_prefix, faucet_id_suffix, amount, vault_root_ptr] @@ -544,7 +536,7 @@ proc peek_asset # => [ASSET_KEY, ASSET_VAULT_ROOT] # lookup asset - emit.SMT_PEEK_EVENT + exec.smt::peek # OS => [ASSET_KEY, ASSET_VAULT_ROOT] # AS => [ASSET] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm index ef969001b5..52916708b9 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm @@ -5,6 +5,7 @@ use $kernel::constants::NOTE_MEM_SIZE use $kernel::memory use $kernel::note +use miden::core::crypto::hashes::rpo256 use miden::core::word # ERRORS @@ -488,7 +489,7 @@ pub proc finalize_transaction adv.insert_hdword # => [ACCOUNT_DELTA_COMMITMENT, FINAL_ACCOUNT_COMMITMENT] - hmerge + exec.rpo256::merge # => [ACCOUNT_UPDATE_COMMITMENT] # ------ Build output stack ------ diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm index baf51d9a5f..581d6598d7 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm @@ -128,7 +128,7 @@ pub proc compute_output_note_assets_commitment # read assets from memory. # if this is the last permutation of the loop and we have an odd number of assets then we # implicitly pad the last word of the hasher rate with zeros by reading from empty memory. - mem_stream hperm + mem_stream exec.rpo256::permute # => [PERM, PERM, PERM, asset_data_ptr, asset_counter, num_asset_pairs, note_data_ptr] # check if we should loop again @@ -190,7 +190,7 @@ proc compute_output_note_id # => [ASSETS_COMMITMENT, RECIPIENT, EMPTY_WORD, note_data_ptr] # compute output note commitment (which is also note ID) and extract digest - hperm exec.rpo256::squeeze_digest + exec.rpo256::permute exec.rpo256::squeeze_digest # => [NOTE_ID, note_data_ptr] # save the output note commitment (note ID) to memory diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm index 2da165d876..bb0ce6e9d5 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/prologue.masm @@ -210,11 +210,11 @@ proc process_block_data # read block data and compute its sub commitment # see `Advice stack` above for details. padw padw padw - adv_pipe hperm - adv_pipe hperm - adv_pipe hperm - adv_pipe hperm - adv_pipe hperm + adv_pipe exec.rpo256::permute + adv_pipe exec.rpo256::permute + adv_pipe exec.rpo256::permute + adv_pipe exec.rpo256::permute + adv_pipe exec.rpo256::permute exec.rpo256::squeeze_digest # => [SUB_COMMITMENT, block_data_ptr', block_num] @@ -224,7 +224,7 @@ proc process_block_data # => [NOTE_ROOT, SUB_COMMITMENT, block_data_ptr', block_num] # merge the note root with the sub commitment to get the block commitment - hmerge + exec.rpo256::merge # => [BLOCK_COMMITMENT, block_data_ptr', block_num] # assert that the block commitment matches the commitment in global inputs @@ -416,8 +416,8 @@ proc process_account_data # read account details and compute its digest. See `Advice stack` above for details. padw padw padw - adv_pipe hperm - adv_pipe hperm + adv_pipe exec.rpo256::permute + adv_pipe exec.rpo256::permute exec.rpo256::squeeze_digest # => [ACCOUNT_COMMITMENT, acct_data_ptr'] @@ -543,7 +543,7 @@ proc authenticate_note # read data from advice and compute hash(BLOCK_SUB_COMMITMENT || NOTE_ROOT) padw padw padw - adv_pipe hperm + adv_pipe exec.rpo256::permute # => [PERM, COMPUTED_BLOCK_COMMITMENT, PERM, mem_ptr', BLOCK_COMMITMENT, NOTE_COMMITMENT] dropw @@ -605,8 +605,8 @@ proc process_input_note_details # read input note's data and compute its digest. See `Advice stack` above for details. padw padw padw - adv_pipe hperm - adv_pipe hperm + adv_pipe exec.rpo256::permute + adv_pipe exec.rpo256::permute exec.rpo256::squeeze_digest # => [NULLIFIER, note_data_ptr + 16] @@ -735,7 +735,7 @@ proc process_note_assets # loop and read assets from the advice provider while.true # read data and compute its digest. See `Advice stack` above for details. - adv_pipe hperm + adv_pipe exec.rpo256::permute # => [PERM, PERM, PERM, assets_ptr+8, note_ptr, counter, rounded_num_assets] # update counter @@ -821,15 +821,15 @@ end #! - NOTE_ID is the note's id, i.e. `hash(RECIPIENT || ASSET_COMMITMENT)`. proc compute_input_note_id # compute SERIAL_COMMITMENT: hash(SERIAL_NUMBER || EMPTY_WORD) - dup exec.memory::get_input_note_serial_num padw hmerge + dup exec.memory::get_input_note_serial_num padw exec.rpo256::merge # => [SERIAL_COMMITMENT, note_ptr] # compute MERGE_SCRIPT: hash(SERIAL_COMMITMENT || SCRIPT_ROOT) - dup.4 exec.memory::get_input_note_script_root hmerge + dup.4 exec.memory::get_input_note_script_root exec.rpo256::merge # => [MERGE_SCRIPT, note_ptr] # compute RECIPIENT: hash(MERGE_SCRIPT || INPUT_COMMITMENT) - dup.4 exec.memory::get_input_note_inputs_commitment hmerge + dup.4 exec.memory::get_input_note_inputs_commitment exec.rpo256::merge # => [RECIPIENT, note_ptr] # store the recipient in memory @@ -837,7 +837,7 @@ proc compute_input_note_id # => [RECIPIENT, note_ptr] # compute NOTE_ID: hash(RECIPIENT || ASSET_COMMITMENT) - movup.4 exec.memory::get_input_note_assets_commitment hmerge + movup.4 exec.memory::get_input_note_assets_commitment exec.rpo256::merge # => [NOTE_ID] end @@ -946,7 +946,7 @@ proc process_input_note # --------------------------------------------------------------------------------------------- # NOTE_COMMITMENT: `hash(NOTE_ID || NOTE_METADATA_COMMITMENT)` - swapw hmerge + swapw exec.rpo256::merge # => [NOTE_COMMITMENT, NULLIFIER, HASHER_CAPACITY] adv_push.1 @@ -964,7 +964,7 @@ proc process_input_note # => [EMPTY_WORD_OR_NOTE_COMMITMENT, NULLIFIER, HASHER_CAPACITY] # update the input note commitment - hperm + exec.rpo256::permute # => [PERM, PERM, PERM] end diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index c606b581a4..1fe0195e71 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -132,7 +132,7 @@ pub proc build_recipient # INPUTS_COMMITMENT, inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT] # compute the advice map key for num_inputs by hashing the inputs commitment - hash + exec.rpo256::hash # => [hash(INPUTS_COMMITMENT), num_inputs_start_ptr, num_inputs_end_ptr, # INPUTS_COMMITMENT, inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT] @@ -158,13 +158,13 @@ pub proc build_recipient movdnw.2 # => [SERIAL_NUM, SCRIPT_ROOT, INPUTS_COMMITMENT] - padw adv.insert_hdword hmerge + padw adv.insert_hdword exec.rpo256::merge # => [SERIAL_HASH, SCRIPT_ROOT, INPUTS_COMMITMENT] - swapw adv.insert_hdword hmerge + swapw adv.insert_hdword exec.rpo256::merge # => [SERIAL_SCRIPT_HASH, INPUTS_COMMITMENT] - swapw adv.insert_hdword hmerge + swapw adv.insert_hdword exec.rpo256::merge # => [RECIPIENT] end @@ -181,13 +181,13 @@ end #! #! Invocation: exec pub proc build_recipient_hash - padw hmerge + padw exec.rpo256::merge # => [SERIAL_NUM_HASH, SCRIPT_ROOT, INPUT_COMMITMENT] - swapw hmerge + swapw exec.rpo256::merge # => [MERGE_SCRIPT, INPUT_COMMITMENT] - swapw hmerge + swapw exec.rpo256::merge # [RECIPIENT] end diff --git a/crates/miden-protocol/src/account/auth.rs b/crates/miden-protocol/src/account/auth.rs index 4155d35463..22a747898b 100644 --- a/crates/miden-protocol/src/account/auth.rs +++ b/crates/miden-protocol/src/account/auth.rs @@ -17,7 +17,7 @@ use crate::{Felt, Hasher, Word}; // ================================================================================================ /// Identifier of signature schemes use for transaction authentication -const RPO_FALCON_512: u8 = 0; +const FALCON_512_RPO: u8 = 0; const ECDSA_K256_KECCAK: u8 = 1; /// Defines standard authentication schemes (i.e., signature schemes) available in the Miden @@ -26,12 +26,12 @@ const ECDSA_K256_KECCAK: u8 = 1; #[non_exhaustive] #[repr(u8)] pub enum AuthScheme { - /// A deterministic RPO Falcon512 signature scheme. + /// A deterministic Falcon512 signature scheme. /// /// This version differs from the reference Falcon512 implementation in its use of the RPO /// algebraic hash function in its hash-to-point algorithm to make signatures very efficient /// to verify inside Miden VM. - RpoFalcon512 = RPO_FALCON_512, + Falcon512Rpo = FALCON_512_RPO, /// ECDSA signature scheme over secp256k1 curve using Keccak to hash the messages when signing. EcdsaK256Keccak = ECDSA_K256_KECCAK, @@ -47,7 +47,7 @@ impl AuthScheme { impl core::fmt::Display for AuthScheme { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::RpoFalcon512 => f.write_str("RpoFalcon512"), + Self::Falcon512Rpo => f.write_str("Falcon512Rpo"), Self::EcdsaK256Keccak => f.write_str("EcdsaK256Keccak"), } } @@ -58,7 +58,7 @@ impl TryFrom for AuthScheme { fn try_from(value: u8) -> Result { match value { - RPO_FALCON_512 => Ok(Self::RpoFalcon512), + FALCON_512_RPO => Ok(Self::Falcon512Rpo), ECDSA_K256_KECCAK => Ok(Self::EcdsaK256Keccak), value => Err(AuthSchemeError::InvalidAuthSchemeIdentifier(value)), } @@ -79,7 +79,7 @@ impl Serializable for AuthScheme { impl Deserializable for AuthScheme { fn read_from(source: &mut R) -> Result { match source.read_u8()? { - RPO_FALCON_512 => Ok(Self::RpoFalcon512), + FALCON_512_RPO => Ok(Self::Falcon512Rpo), ECDSA_K256_KECCAK => Ok(Self::EcdsaK256Keccak), value => Err(DeserializationError::InvalidValue(format!( "auth scheme identifier `{value}` is not valid" @@ -96,20 +96,20 @@ impl Deserializable for AuthScheme { #[non_exhaustive] #[repr(u8)] pub enum AuthSecretKey { - RpoFalcon512(falcon512_rpo::SecretKey) = RPO_FALCON_512, + Falcon512Rpo(falcon512_rpo::SecretKey) = FALCON_512_RPO, EcdsaK256Keccak(ecdsa_k256_keccak::SecretKey) = ECDSA_K256_KECCAK, } impl AuthSecretKey { - /// Generates an RpoFalcon512 secret key from the OS-provided randomness. + /// Generates an Falcon512Rpo secret key from the OS-provided randomness. #[cfg(feature = "std")] pub fn new_falcon512_rpo() -> Self { - Self::RpoFalcon512(falcon512_rpo::SecretKey::new()) + Self::Falcon512Rpo(falcon512_rpo::SecretKey::new()) } - /// Generates an RpoFalcon512 secrete key using the provided random number generator. + /// Generates an Falcon512Rpo secrete key using the provided random number generator. pub fn new_falcon512_rpo_with_rng(rng: &mut R) -> Self { - Self::RpoFalcon512(falcon512_rpo::SecretKey::with_rng(rng)) + Self::Falcon512Rpo(falcon512_rpo::SecretKey::with_rng(rng)) } /// Generates an EcdsaK256Keccak secret key from the OS-provided randomness. @@ -126,7 +126,7 @@ impl AuthSecretKey { /// Returns the authentication scheme of this secret key. pub fn auth_scheme(&self) -> AuthScheme { match self { - AuthSecretKey::RpoFalcon512(_) => AuthScheme::RpoFalcon512, + AuthSecretKey::Falcon512Rpo(_) => AuthScheme::Falcon512Rpo, AuthSecretKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak, } } @@ -134,7 +134,7 @@ impl AuthSecretKey { /// Returns a public key associated with this secret key. pub fn public_key(&self) -> PublicKey { match self { - AuthSecretKey::RpoFalcon512(key) => PublicKey::RpoFalcon512(key.public_key()), + AuthSecretKey::Falcon512Rpo(key) => PublicKey::Falcon512Rpo(key.public_key()), AuthSecretKey::EcdsaK256Keccak(key) => PublicKey::EcdsaK256Keccak(key.public_key()), } } @@ -142,7 +142,7 @@ impl AuthSecretKey { /// Signs the provided message with this secret key. pub fn sign(&self, message: Word) -> Signature { match self { - AuthSecretKey::RpoFalcon512(key) => Signature::RpoFalcon512(key.sign(message)), + AuthSecretKey::Falcon512Rpo(key) => Signature::Falcon512Rpo(key.sign(message)), AuthSecretKey::EcdsaK256Keccak(key) => Signature::EcdsaK256Keccak(key.sign(message)), } } @@ -152,7 +152,7 @@ impl Serializable for AuthSecretKey { fn write_into(&self, target: &mut W) { self.auth_scheme().write_into(target); match self { - AuthSecretKey::RpoFalcon512(key) => key.write_into(target), + AuthSecretKey::Falcon512Rpo(key) => key.write_into(target), AuthSecretKey::EcdsaK256Keccak(key) => key.write_into(target), } } @@ -161,9 +161,9 @@ impl Serializable for AuthSecretKey { impl Deserializable for AuthSecretKey { fn read_from(source: &mut R) -> Result { match source.read::()? { - AuthScheme::RpoFalcon512 => { + AuthScheme::Falcon512Rpo => { let secret_key = falcon512_rpo::SecretKey::read_from(source)?; - Ok(AuthSecretKey::RpoFalcon512(secret_key)) + Ok(AuthSecretKey::Falcon512Rpo(secret_key)) }, AuthScheme::EcdsaK256Keccak => { let secret_key = ecdsa_k256_keccak::SecretKey::read_from(source)?; @@ -208,7 +208,7 @@ impl From for PublicKeyCommitment { #[derive(Clone, Debug)] #[non_exhaustive] pub enum PublicKey { - RpoFalcon512(falcon512_rpo::PublicKey), + Falcon512Rpo(falcon512_rpo::PublicKey), EcdsaK256Keccak(ecdsa_k256_keccak::PublicKey), } @@ -216,7 +216,7 @@ impl PublicKey { /// Returns the authentication scheme of this public key. pub fn auth_scheme(&self) -> AuthScheme { match self { - PublicKey::RpoFalcon512(_) => AuthScheme::RpoFalcon512, + PublicKey::Falcon512Rpo(_) => AuthScheme::Falcon512Rpo, PublicKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak, } } @@ -224,7 +224,7 @@ impl PublicKey { /// Returns a commitment to this public key. pub fn to_commitment(&self) -> PublicKeyCommitment { match self { - PublicKey::RpoFalcon512(key) => key.to_commitment().into(), + PublicKey::Falcon512Rpo(key) => key.to_commitment().into(), PublicKey::EcdsaK256Keccak(key) => key.to_commitment().into(), } } @@ -232,7 +232,7 @@ impl PublicKey { /// Verifies the provided signature against the provided message and this public key. pub fn verify(&self, message: Word, signature: Signature) -> bool { match (self, signature) { - (PublicKey::RpoFalcon512(key), Signature::RpoFalcon512(sig)) => { + (PublicKey::Falcon512Rpo(key), Signature::Falcon512Rpo(sig)) => { key.verify(message, &sig) }, (PublicKey::EcdsaK256Keccak(key), Signature::EcdsaK256Keccak(sig)) => { @@ -247,7 +247,7 @@ impl Serializable for PublicKey { fn write_into(&self, target: &mut W) { self.auth_scheme().write_into(target); match self { - PublicKey::RpoFalcon512(pub_key) => pub_key.write_into(target), + PublicKey::Falcon512Rpo(pub_key) => pub_key.write_into(target), PublicKey::EcdsaK256Keccak(pub_key) => pub_key.write_into(target), } } @@ -256,9 +256,9 @@ impl Serializable for PublicKey { impl Deserializable for PublicKey { fn read_from(source: &mut R) -> Result { match source.read::()? { - AuthScheme::RpoFalcon512 => { + AuthScheme::Falcon512Rpo => { let pub_key = falcon512_rpo::PublicKey::read_from(source)?; - Ok(PublicKey::RpoFalcon512(pub_key)) + Ok(PublicKey::Falcon512Rpo(pub_key)) }, AuthScheme::EcdsaK256Keccak => { let pub_key = ecdsa_k256_keccak::PublicKey::read_from(source)?; @@ -289,7 +289,7 @@ impl Deserializable for PublicKey { #[derive(Clone, Debug)] #[repr(u8)] pub enum Signature { - RpoFalcon512(falcon512_rpo::Signature) = RPO_FALCON_512, + Falcon512Rpo(falcon512_rpo::Signature) = FALCON_512_RPO, EcdsaK256Keccak(ecdsa_k256_keccak::Signature) = ECDSA_K256_KECCAK, } @@ -297,7 +297,7 @@ impl Signature { /// Returns the authentication scheme of this signature. pub fn auth_scheme(&self) -> AuthScheme { match self { - Signature::RpoFalcon512(_) => AuthScheme::RpoFalcon512, + Signature::Falcon512Rpo(_) => AuthScheme::Falcon512Rpo, Signature::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak, } } @@ -311,7 +311,7 @@ impl Signature { // TODO: the `expect()` should be changed to an error; but that will be a part of a bigger // refactoring let mut result = match self { - Signature::RpoFalcon512(sig) => prepare_falcon512_rpo_signature(sig), + Signature::Falcon512Rpo(sig) => prepare_falcon512_rpo_signature(sig), Signature::EcdsaK256Keccak(sig) => { let pk = ecdsa_k256_keccak::PublicKey::recover_from(msg, sig) .expect("inferring public key from signature and message should succeed"); @@ -328,7 +328,7 @@ impl Signature { impl From for Signature { fn from(signature: falcon512_rpo::Signature) -> Self { - Signature::RpoFalcon512(signature) + Signature::Falcon512Rpo(signature) } } @@ -336,7 +336,7 @@ impl Serializable for Signature { fn write_into(&self, target: &mut W) { self.auth_scheme().write_into(target); match self { - Signature::RpoFalcon512(signature) => signature.write_into(target), + Signature::Falcon512Rpo(signature) => signature.write_into(target), Signature::EcdsaK256Keccak(signature) => signature.write_into(target), } } @@ -345,9 +345,9 @@ impl Serializable for Signature { impl Deserializable for Signature { fn read_from(source: &mut R) -> Result { match source.read::()? { - AuthScheme::RpoFalcon512 => { + AuthScheme::Falcon512Rpo => { let signature = falcon512_rpo::Signature::read_from(source)?; - Ok(Signature::RpoFalcon512(signature)) + Ok(Signature::Falcon512Rpo(signature)) }, AuthScheme::EcdsaK256Keccak => { let signature = ecdsa_k256_keccak::Signature::read_from(source)?; diff --git a/crates/miden-protocol/src/account/component/storage/toml/mod.rs b/crates/miden-protocol/src/account/component/storage/toml/mod.rs index 3a9c137266..9d40674cb0 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/mod.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/mod.rs @@ -106,7 +106,7 @@ struct RawStorageSchema { /// Storage slot type descriptor. /// /// This field accepts either: -/// - a type identifier (e.g. `"word"`, `"u16"`, `"miden::standards::auth::rpo_falcon512::pub_key"`) +/// - a type identifier (e.g. `"word"`, `"u16"`, `"miden::standards::auth::falcon512_rpo::pub_key"`) /// for simple word slots, /// - an array of 4 [`FeltSchema`] descriptors for composite word slots, or /// - a table `{ key = ..., value = ... }` for map slots. diff --git a/crates/miden-protocol/src/transaction/kernel/procedures.rs b/crates/miden-protocol/src/transaction/kernel/procedures.rs index 4fa48ae66d..60c5fb4349 100644 --- a/crates/miden-protocol/src/transaction/kernel/procedures.rs +++ b/crates/miden-protocol/src/transaction/kernel/procedures.rs @@ -8,9 +8,9 @@ use crate::{Word, word}; /// Hashes of all dynamically executed kernel procedures. pub const KERNEL_PROCEDURES: [Word; 53] = [ // account_get_initial_commitment - word!("0x45c5a29a7420ecd4394c8cffe822f31781cb0a5e30aa2aa179d143df5710f23e"), + word!("0x1de52b747e823a098f3e146cf2e2b7c3f585a4424ec54c9022414d9ca2574375"), // account_compute_commitment - word!("0x5c228332186b23e8e6dcb22751150ef8e350657d2a0c7e7934b30dea6ecb8a7f"), + word!("0xdcb7c06bc7617d49bcda33d1753e327cc744b9f83e2019d61eca016b33c527a7"), // account_get_id word!("0xd76288f2e94b9e6a8f7eeee45c4ee0a23997d78496f6132e3f55681efea809c4"), // account_get_nonce @@ -30,19 +30,19 @@ pub const KERNEL_PROCEDURES: [Word; 53] = [ // account_set_item word!("0x29392c01f8953e4e4f6dd8eba69c53bd5f4ff7f54beeaab2e86d8ef7c8d982a0"), // account_get_map_item - word!("0x791d4be34dc2ea7b4574ba756e2d5103a24e4a961212a6d95168071157423c9c"), + word!("0x41e4f17b24281fbb05279fbba5ece3a5181055217be0d1d33cc44b71b6d19a23"), // account_get_initial_map_item - word!("0xfbc6fcd521aaed1018466126a795eaf820cfc48e6ef3761f21a4363ef259324b"), + word!("0x30f13f40cd4de71c1fae252218e27138e0b406a26e9c22cfb0219633fef9de23"), // account_set_map_item - word!("0xc4bccf37cd3961f402e46f34f1597092dd083c6d282fed94ec8b914ea2920363"), + word!("0x722b97307928cda6600f5ed17a5c55c8130200520ff076b08f0f77706f6e81ea"), // account_get_initial_vault_root word!("0x46297d9ac95afd60c7ef1a065e024ad49aa4c019f6b3924191905449b244d4ec"), // account_get_vault_root word!("0x42a2bfb8eac4fce9bbf75ea15215b00729faeeaf7fff784692948d3f618a9bb7"), // account_add_asset - word!("0xef3d652f73a6e3a88737cf08732537552b2423d5461880df50665264944c4891"), + word!("0x9a63b5385a8ac6404306852c13b1f7c2625449ce6afde6c14d27701e9e3fb550"), // account_remove_asset - word!("0x680c8d9df19de7dee201a658dcd7005d1f0c85c7d4729a6bc228ed0c2d48678f"), + word!("0x2ac6fa0e12e581a59edbf7ab0995033aa308dbf77ecaf9b9215ebcf27da5a942"), // account_get_balance word!("0x6a5eb788fd2beec7555874f978a4dd2f2c4f5d8088cd33e148c61450e4510fe1"), // account_get_initial_balance @@ -50,7 +50,7 @@ pub const KERNEL_PROCEDURES: [Word; 53] = [ // account_has_non_fungible_asset word!("0xffe57961158c8e5f8a3aaa773943ee208fac7ed4786a7c8b6fed04ba54f39111"), // account_compute_delta_commitment - word!("0xedd96b5ee329219e0dc2085b5249c02ce732500fca27719085aa99e1263f6113"), + word!("0x09767ee5e29aeca91a57f3af3871bbfb3037681e193444b3f7af878894c1aaa3"), // account_get_num_procedures word!("0x53b5ec38b7841948762c258010e6e07ad93963bcaac2d83813f8edb6710dc720"), // account_get_procedure_root @@ -60,13 +60,13 @@ pub const KERNEL_PROCEDURES: [Word; 53] = [ // account_has_procedure word!("0xb0b63fdd01af0bcb4aacb2412e934cdc7691308647152d416c7ae4fc909da076"), // faucet_mint_asset - word!("0xdb32b55a283114bb22d2f7fe867eb9ac745c49c45413cc4155d66df062c022ec"), + word!("0x3d533697caf8b2522507c33b98af7652f3eb2e4f5d29d64f3d147af07ed2c494"), // faucet_burn_asset - word!("0xa5b7f8c0261ee87a36f7b4d67f35d4369b6f139d0bb75aa24b8275b0f69dbb4e"), + word!("0xcad46a403b78da4082d3e256025fd427e19b3fbc261e062cd5b2f853617311a7"), // faucet_get_total_fungible_asset_issuance word!("0x0953a2f2ec88ad0b7008c3d71aca46ebfcbb58a8ffdf59390616497c6693e8ab"), // faucet_is_non_fungible_asset_issued - word!("0xd2131c592fd7565bbf5d555e73a022c4ec1b5bc70219724e26424ff662e02b1a"), + word!("0x9d9d5ec39551a1ab1c063a6dae9f3633195c89e8e31bbb6b217957f0ea8323c2"), // input_note_get_metadata word!("0x447b342e38855a9402cde0ea52ecb5e4c1fe542b535a5364cb5caa8e94c82442"), // input_note_get_assets_info @@ -84,7 +84,7 @@ pub const KERNEL_PROCEDURES: [Word; 53] = [ // output_note_get_metadata word!("0x3db8427f20eb70603b72aa574a986506eb7216312004aeaf8b2a7e55d0049a48"), // output_note_get_assets_info - word!("0xd2078081e19843b0c2500b9483f9888fa0a2c33a5155064aa97819696d7ae157"), + word!("0xbbf90ac2a7e16ee6d2336f7389d5b972bf0c1fa9938405945b85f948bf24fc4f"), // output_note_get_recipient word!("0x1ce137f0c5be72832970e6c818968a789f65b97db34515bfebb767705f28db67"), // output_note_add_asset @@ -98,7 +98,7 @@ pub const KERNEL_PROCEDURES: [Word; 53] = [ // tx_get_num_output_notes word!("0x2511fca9c078cd96e526fd488d1362cbfd597eb3db8452aedb00beffee9782b4"), // tx_get_output_notes_commitment - word!("0xbb94a5220ae82a2b45ab0eca2fc653d1a8f0967507a675430bd4caedc022334c"), + word!("0x8b9b29c837b5d0834f550d7f32703b35e2ff014b523dd581a09a0b94a925fcec"), // tx_get_block_commitment word!("0xe474b491a64d222397fcf83ee5db7b048061988e5e83ce99b91bae6fd75a3522"), // tx_get_block_number @@ -106,7 +106,7 @@ pub const KERNEL_PROCEDURES: [Word; 53] = [ // tx_get_block_timestamp word!("0x7903185b847517debb6c2072364e3e757b99ee623e97c2bd0a4661316c5c5418"), // tx_start_foreign_context - word!("0xfddca78a0611aa0badc95ef57d5d7485d076eab7d3a505347a26db13d40dc18e"), + word!("0x753c9c0afcbd36fc7b7ba1e8dc7b668c8aa6036ae8cf5c891587f10117669d76"), // tx_end_foreign_context word!("0xaa0018aa8da890b73511879487f65553753fb7df22de380dd84c11e6f77eec6f"), // tx_get_expiration_delta diff --git a/crates/miden-standards/asm/account_components/auth/rpo_falcon_512.masm b/crates/miden-standards/asm/account_components/auth/falcon_512_rpo.masm similarity index 79% rename from crates/miden-standards/asm/account_components/auth/rpo_falcon_512.masm rename to crates/miden-standards/asm/account_components/auth/falcon_512_rpo.masm index 7fd23fe2c2..bee640eab6 100644 --- a/crates/miden-standards/asm/account_components/auth/rpo_falcon_512.masm +++ b/crates/miden-standards/asm/account_components/auth/falcon_512_rpo.masm @@ -1,8 +1,8 @@ -# The MASM code of the RPO Falcon 512 authentication Account Component. +# The MASM code of the Falcon 512 RPO authentication Account Component. # -# See the `AuthRpoFalcon512` Rust type's documentation for more details. +# See the `AuthFalcon512Rpo` Rust type's documentation for more details. -use miden::standards::auth::rpo_falcon512 +use miden::standards::auth::falcon512_rpo use miden::protocol::active_account type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } @@ -11,7 +11,7 @@ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } # ================================================================================================= # The slot in this component's storage layout where the public key is stored. -const PUBLIC_KEY_SLOT = word("miden::standards::auth::rpo_falcon512::public_key") +const PUBLIC_KEY_SLOT = word("miden::standards::auth::falcon512_rpo::public_key") #! Authenticate a transaction using the Falcon signature scheme. #! @@ -28,7 +28,7 @@ const PUBLIC_KEY_SLOT = word("miden::standards::auth::rpo_falcon512::public_key" #! Outputs: [pad(16)] #! #! Invocation: call -pub proc auth_tx_rpo_falcon512(auth_args: BeWord) +pub proc auth_tx_falcon512_rpo(auth_args: BeWord) dropw # => [pad(16)] @@ -38,6 +38,6 @@ pub proc auth_tx_rpo_falcon512(auth_args: BeWord) push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] - exec.rpo_falcon512::authenticate_transaction + exec.falcon512_rpo::authenticate_transaction # => [pad(16)] end diff --git a/crates/miden-standards/asm/account_components/auth/rpo_falcon_512_acl.masm b/crates/miden-standards/asm/account_components/auth/falcon_512_rpo_acl.masm similarity index 90% rename from crates/miden-standards/asm/account_components/auth/rpo_falcon_512_acl.masm rename to crates/miden-standards/asm/account_components/auth/falcon_512_rpo_acl.masm index b183a45c4a..80081ab6c6 100644 --- a/crates/miden-standards/asm/account_components/auth/rpo_falcon_512_acl.masm +++ b/crates/miden-standards/asm/account_components/auth/falcon_512_rpo_acl.masm @@ -1,6 +1,6 @@ -# The MASM code of the RPO Falcon 512 authentication Account Component with ACL. +# The MASM code of the Falcon 512 RPO authentication Account Component with ACL. # -# See the `AuthRpoFalcon512Acl` Rust type's documentation for more details. +# See the `AuthFalcon512RpoAcl` Rust type's documentation for more details. use miden::protocol::active_account use miden::protocol::native_account @@ -13,13 +13,13 @@ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt } # ================================================================================================ # The slot in this component's storage layout where the public key is stored. -const PUBLIC_KEY_SLOT = word("miden::standards::auth::rpo_falcon512_acl::public_key") +const PUBLIC_KEY_SLOT = word("miden::standards::auth::falcon512_rpo_acl::public_key") # The slot where the authentication configuration is stored. -const AUTH_CONFIG_SLOT = word("miden::standards::auth::rpo_falcon512_acl::config") +const AUTH_CONFIG_SLOT = word("miden::standards::auth::falcon512_rpo_acl::config") # The slot where the map of auth trigger procedure roots is stored. -const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::rpo_falcon512_acl::trigger_procedure_roots") +const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::falcon512_rpo_acl::trigger_procedure_roots") #! Authenticate a transaction using the Falcon signature scheme based on procedure calls and note usage. #! @@ -28,7 +28,7 @@ const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::rpo_falcon512_ #! 2. If input notes were consumed and allow_unauthorized_input_notes is false #! 3. If output notes were created and allow_unauthorized_output_notes is false #! -#! If any of these conditions are true, standard RpoFalcon512 signature verification is performed. +#! If any of these conditions are true, standard Falcon512Rpo signature verification is performed. #! Otherwise, only the nonce is incremented. #! #! Inputs: [AUTH_ARGS, pad(12)] @@ -36,7 +36,7 @@ const AUTH_TRIGGER_PROCS_MAP_SLOT = word("miden::standards::auth::rpo_falcon512_ #! #! Invocation: call @locals(2) -pub proc auth_tx_rpo_falcon512_acl(auth_args: BeWord) +pub proc auth_tx_falcon512_rpo_acl(auth_args: BeWord) dropw # => [pad(16)] @@ -128,7 +128,7 @@ pub proc auth_tx_rpo_falcon512_acl(auth_args: BeWord) push.PUBLIC_KEY_SLOT[0..2] exec.active_account::get_item # => [PUB_KEY, pad(16)] - exec.::miden::standards::auth::rpo_falcon512::authenticate_transaction + exec.::miden::standards::auth::falcon512_rpo::authenticate_transaction else # ------ Check if initial account commitment differs from current commitment ------ diff --git a/crates/miden-standards/asm/account_components/auth/rpo_falcon_512_multisig.masm b/crates/miden-standards/asm/account_components/auth/falcon_512_rpo_multisig.masm similarity index 96% rename from crates/miden-standards/asm/account_components/auth/rpo_falcon_512_multisig.masm rename to crates/miden-standards/asm/account_components/auth/falcon_512_rpo_multisig.masm index db47387ddf..e3f3e2d6e7 100644 --- a/crates/miden-standards/asm/account_components/auth/rpo_falcon_512_multisig.masm +++ b/crates/miden-standards/asm/account_components/auth/falcon_512_rpo_multisig.masm @@ -1,6 +1,6 @@ -# The MASM code of the Multi-Signature RPO Falcon 512 Authentication Component. +# The MASM code of the Multi-Signature Falcon 512 RPO Authentication Component. # -# See the `AuthRpoFalcon512Multisig` Rust type's documentation for more details. +# See the `AuthFalcon512RpoMultisig` Rust type's documentation for more details. use miden::protocol::active_account use miden::protocol::native_account @@ -29,19 +29,19 @@ const AUTH_UNAUTHORIZED_EVENT = event("miden::auth::unauthorized") # number of approvers are stored as: # [default_threshold, num_approvers, 0, 0]. # The threshold is guaranteed to be less than or equal to num_approvers. -const THRESHOLD_CONFIG_SLOT = word("miden::standards::auth::rpo_falcon512_multisig::threshold_config") +const THRESHOLD_CONFIG_SLOT = word("miden::standards::auth::falcon512_rpo_multisig::threshold_config") # The slot in this component's storage layout where the public keys map is stored. # Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY -const APPROVER_PUBLIC_KEYS_SLOT = word("miden::standards::auth::rpo_falcon512_multisig::approver_public_keys") +const APPROVER_PUBLIC_KEYS_SLOT = word("miden::standards::auth::falcon512_rpo_multisig::approver_public_keys") # The slot in this component's storage layout where executed transactions are stored. # Map entries: transaction_message => [is_executed, 0, 0, 0] -const EXECUTED_TXS_SLOT = word("miden::standards::auth::rpo_falcon512_multisig::executed_transactions") +const EXECUTED_TXS_SLOT = word("miden::standards::auth::falcon512_rpo_multisig::executed_transactions") # The slot in this component's storage layout where procedure thresholds are stored. # Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0] -const PROC_THRESHOLD_ROOTS_SLOT = word("miden::standards::auth::rpo_falcon512_multisig::procedure_thresholds") +const PROC_THRESHOLD_ROOTS_SLOT = word("miden::standards::auth::falcon512_rpo_multisig::procedure_thresholds") # Executed Transaction Flag Constant const IS_EXECUTED_FLAG = [1, 0, 0, 0] @@ -356,7 +356,7 @@ end #! #! Invocation: call @locals(1) -pub proc auth_tx_rpo_falcon512_multisig(salt: BeWord) +pub proc auth_tx_falcon512_rpo_multisig(salt: BeWord) exec.native_account::incr_nonce drop # => [SALT] @@ -390,7 +390,7 @@ pub proc auth_tx_rpo_falcon512_multisig(salt: BeWord) push.APPROVER_PUBLIC_KEYS_SLOT[0..2] # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold] - exec.::miden::standards::auth::rpo_falcon512::verify_signatures + exec.::miden::standards::auth::falcon512_rpo::verify_signatures # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold] # ------ Checking threshold is >= num_verified_signatures ------ diff --git a/crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm b/crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm index 17fd8d9e7a..a0be086829 100644 --- a/crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm +++ b/crates/miden-standards/asm/standards/auth/ecdsa_k256_keccak.masm @@ -1,4 +1,5 @@ use miden::core::crypto::dsa::ecdsa_k256_keccak +use miden::core::crypto::hashes::rpo256 use miden::protocol::active_account use miden::protocol::native_account use miden::protocol::tx @@ -121,7 +122,7 @@ pub proc verify_signatures movup.4 movdn.8 swapw dupw movdnw.2 # => [MSG, OWNER_PUB_KEY, MSG, i-1] - hmerge + exec.rpo256::merge # => [SIG_KEY, MSG, i-1] adv.has_mapkey diff --git a/crates/miden-standards/asm/standards/auth/rpo_falcon512.masm b/crates/miden-standards/asm/standards/auth/falcon512_rpo.masm similarity index 99% rename from crates/miden-standards/asm/standards/auth/rpo_falcon512.masm rename to crates/miden-standards/asm/standards/auth/falcon512_rpo.masm index 4abb032699..7c9ccbf757 100644 --- a/crates/miden-standards/asm/standards/auth/rpo_falcon512.masm +++ b/crates/miden-standards/asm/standards/auth/falcon512_rpo.masm @@ -1,4 +1,5 @@ use miden::core::crypto::dsa::falcon512rpo +use miden::core::crypto::hashes::rpo256 use miden::protocol::active_account use miden::protocol::native_account use miden::protocol::tx @@ -121,7 +122,7 @@ pub proc verify_signatures movup.4 movdn.8 swapw dupw movdnw.2 # => [MSG, OWNER_PUB_KEY, MSG, i-1] - hmerge + exec.rpo256::merge # => [SIG_KEY, MSG, i-1] adv.has_mapkey diff --git a/crates/miden-standards/asm/standards/auth/mod.masm b/crates/miden-standards/asm/standards/auth/mod.masm index 1c0575afcd..ec213ee4b8 100644 --- a/crates/miden-standards/asm/standards/auth/mod.masm +++ b/crates/miden-standards/asm/standards/auth/mod.masm @@ -79,7 +79,7 @@ pub proc hash_tx_summary padw movdnw.2 # => [INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT, CAPACITY, SALT, OUTPUT_NOTES_COMMITMENT] - hperm + exec.rpo256::permute # => [RATE, RATE, PERM, SALT, OUTPUT_NOTES_COMMITMENT] # drop rate words @@ -89,7 +89,7 @@ pub proc hash_tx_summary movdnw.2 # => [SALT, OUTPUT_NOTES_COMMITMENT, PERM] - hperm + exec.rpo256::permute # => [RATE, RATE, PERM] exec.rpo256::squeeze_digest diff --git a/crates/miden-standards/src/account/auth/rpo_falcon_512.rs b/crates/miden-standards/src/account/auth/falcon_512_rpo.rs similarity index 73% rename from crates/miden-standards/src/account/auth/rpo_falcon_512.rs rename to crates/miden-standards/src/account/auth/falcon_512_rpo.rs index a65d9e0394..d3f37da6e6 100644 --- a/crates/miden-standards/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-standards/src/account/auth/falcon_512_rpo.rs @@ -2,17 +2,17 @@ use miden_protocol::account::auth::PublicKeyCommitment; use miden_protocol::account::{AccountComponent, StorageSlot, StorageSlotName}; use miden_protocol::utils::sync::LazyLock; -use crate::account::components::rpo_falcon_512_library; +use crate::account::components::falcon_512_rpo_library; static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512::public_key") + StorageSlotName::new("miden::standards::auth::falcon512_rpo::public_key") .expect("storage slot name should be valid") }); -/// An [`AccountComponent`] implementing the RpoFalcon512 signature scheme for authentication of +/// An [`AccountComponent`] implementing the Falcon512Rpo signature scheme for authentication of /// transactions. /// -/// It reexports the procedures from `miden::standards::auth::rpo_falcon512`. When linking against +/// It reexports the procedures from `miden::standards::auth::falcon512_rpo`. When linking against /// this component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must /// be available to the assembler which is the case when using [`CodeBuilder`][builder]. The /// procedures of this component are: @@ -28,12 +28,12 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// - [`Self::public_key_slot`]: Public key /// /// [builder]: crate::code_builder::CodeBuilder -pub struct AuthRpoFalcon512 { +pub struct AuthFalcon512Rpo { pub_key: PublicKeyCommitment, } -impl AuthRpoFalcon512 { - /// Creates a new [`AuthRpoFalcon512`] component with the given `public_key`. +impl AuthFalcon512Rpo { + /// Creates a new [`AuthFalcon512Rpo`] component with the given `public_key`. pub fn new(pub_key: PublicKeyCommitment) -> Self { Self { pub_key } } @@ -44,12 +44,12 @@ impl AuthRpoFalcon512 { } } -impl From for AccountComponent { - fn from(falcon: AuthRpoFalcon512) -> Self { +impl From for AccountComponent { + fn from(falcon: AuthFalcon512Rpo) -> Self { AccountComponent::new( - rpo_falcon_512_library(), + falcon_512_rpo_library(), vec![StorageSlot::with_value( - AuthRpoFalcon512::public_key_slot().clone(), + AuthFalcon512Rpo::public_key_slot().clone(), falcon.pub_key.into(), )], ) diff --git a/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs b/crates/miden-standards/src/account/auth/falcon_512_rpo_acl.rs similarity index 88% rename from crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs rename to crates/miden-standards/src/account/auth/falcon_512_rpo_acl.rs index 816e1f227d..aee82e064d 100644 --- a/crates/miden-standards/src/account/auth/rpo_falcon_512_acl.rs +++ b/crates/miden-standards/src/account/auth/falcon_512_rpo_acl.rs @@ -12,26 +12,26 @@ use miden_protocol::account::{ use miden_protocol::errors::AccountError; use miden_protocol::utils::sync::LazyLock; -use crate::account::components::rpo_falcon_512_acl_library; +use crate::account::components::falcon_512_rpo_acl_library; static PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512_acl::public_key") + StorageSlotName::new("miden::standards::auth::falcon512_rpo_acl::public_key") .expect("storage slot name should be valid") }); static CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512_acl::config") + StorageSlotName::new("miden::standards::auth::falcon512_rpo_acl::config") .expect("storage slot name should be valid") }); static TRIGGER_PROCEDURE_ROOT_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512_acl::trigger_procedure_roots") + StorageSlotName::new("miden::standards::auth::falcon512_rpo_acl::trigger_procedure_roots") .expect("storage slot name should be valid") }); -/// Configuration for [`AuthRpoFalcon512Acl`] component. +/// Configuration for [`AuthFalcon512RpoAcl`] component. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct AuthRpoFalcon512AclConfig { +pub struct AuthFalcon512RpoAclConfig { /// List of procedure roots that require authentication when called. pub auth_trigger_procedures: Vec, /// When `false`, creating output notes (sending notes to other accounts) requires @@ -42,7 +42,7 @@ pub struct AuthRpoFalcon512AclConfig { pub allow_unauthorized_input_notes: bool, } -impl AuthRpoFalcon512AclConfig { +impl AuthFalcon512RpoAclConfig { /// Creates a new configuration with no trigger procedures and both flags set to `false` (most /// restrictive). pub fn new() -> Self { @@ -72,14 +72,14 @@ impl AuthRpoFalcon512AclConfig { } } -impl Default for AuthRpoFalcon512AclConfig { +impl Default for AuthFalcon512RpoAclConfig { fn default() -> Self { Self::new() } } /// An [`AccountComponent`] implementing a procedure-based Access Control List (ACL) using the -/// RpoFalcon512 signature scheme for authentication of transactions. +/// Falcon512Rpo signature scheme for authentication of transactions. /// /// This component provides fine-grained authentication control based on three conditions: /// 1. **Procedure-based authentication**: Requires authentication when any of the specified trigger @@ -133,20 +133,20 @@ impl Default for AuthRpoFalcon512AclConfig { /// procedures for authentication. /// /// This component supports all account types. -pub struct AuthRpoFalcon512Acl { +pub struct AuthFalcon512RpoAcl { pub_key: PublicKeyCommitment, - config: AuthRpoFalcon512AclConfig, + config: AuthFalcon512RpoAclConfig, } -impl AuthRpoFalcon512Acl { - /// Creates a new [`AuthRpoFalcon512Acl`] component with the given `public_key` and +impl AuthFalcon512RpoAcl { + /// Creates a new [`AuthFalcon512RpoAcl`] component with the given `public_key` and /// configuration. /// /// # Panics /// Panics if more than [AccountCode::MAX_NUM_PROCEDURES] procedures are specified. pub fn new( pub_key: PublicKeyCommitment, - config: AuthRpoFalcon512AclConfig, + config: AuthFalcon512RpoAclConfig, ) -> Result { let max_procedures = AccountCode::MAX_NUM_PROCEDURES; if config.auth_trigger_procedures.len() > max_procedures { @@ -174,20 +174,20 @@ impl AuthRpoFalcon512Acl { } } -impl From for AccountComponent { - fn from(falcon: AuthRpoFalcon512Acl) -> Self { +impl From for AccountComponent { + fn from(falcon: AuthFalcon512RpoAcl) -> Self { let mut storage_slots = Vec::with_capacity(3); // Public key slot storage_slots.push(StorageSlot::with_value( - AuthRpoFalcon512Acl::public_key_slot().clone(), + AuthFalcon512RpoAcl::public_key_slot().clone(), falcon.pub_key.into(), )); // Config slot let num_procs = falcon.config.auth_trigger_procedures.len() as u32; storage_slots.push(StorageSlot::with_value( - AuthRpoFalcon512Acl::config_slot().clone(), + AuthFalcon512RpoAcl::config_slot().clone(), Word::from([ num_procs, u32::from(falcon.config.allow_unauthorized_output_notes), @@ -208,11 +208,11 @@ impl From for AccountComponent { // Safe to unwrap because we know that the map keys are unique. storage_slots.push(StorageSlot::with_map( - AuthRpoFalcon512Acl::trigger_procedure_roots_slot().clone(), + AuthFalcon512RpoAcl::trigger_procedure_roots_slot().clone(), StorageMap::with_entries(map_entries).unwrap(), )); - AccountComponent::new(rpo_falcon_512_acl_library(), storage_slots) + AccountComponent::new(falcon_512_rpo_acl_library(), storage_slots) .expect( "ACL auth component should satisfy the requirements of a valid account component", ) @@ -255,7 +255,7 @@ mod tests { let public_key = PublicKeyCommitment::from(Word::empty()); // Build the configuration - let mut acl_config = AuthRpoFalcon512AclConfig::new() + let mut acl_config = AuthFalcon512RpoAclConfig::new() .with_allow_unauthorized_output_notes(config.allow_unauthorized_output_notes) .with_allow_unauthorized_input_notes(config.allow_unauthorized_input_notes); @@ -269,7 +269,7 @@ mod tests { // Create component and account let component = - AuthRpoFalcon512Acl::new(public_key, acl_config).expect("component creation failed"); + AuthFalcon512RpoAcl::new(public_key, acl_config).expect("component creation failed"); let account = AccountBuilder::new([0; 32]) .with_auth_component(component) @@ -280,14 +280,14 @@ mod tests { // Check public key storage let public_key_slot = account .storage() - .get_item(AuthRpoFalcon512Acl::public_key_slot()) + .get_item(AuthFalcon512RpoAcl::public_key_slot()) .expect("public key storage slot access failed"); assert_eq!(public_key_slot, public_key.into()); // Check configuration storage let config_slot = account .storage() - .get_item(AuthRpoFalcon512Acl::config_slot()) + .get_item(AuthFalcon512RpoAcl::config_slot()) .expect("config storage slot access failed"); assert_eq!(config_slot, config.expected_config_slot); @@ -297,7 +297,7 @@ mod tests { let proc_root = account .storage() .get_map_item( - AuthRpoFalcon512Acl::trigger_procedure_roots_slot(), + AuthFalcon512RpoAcl::trigger_procedure_roots_slot(), Word::from([i as u32, 0, 0, 0]), ) .expect("storage map access failed"); @@ -307,7 +307,7 @@ mod tests { // When no procedures, the map should return empty for key [0,0,0,0] let proc_root = account .storage() - .get_map_item(AuthRpoFalcon512Acl::trigger_procedure_roots_slot(), Word::empty()) + .get_map_item(AuthFalcon512RpoAcl::trigger_procedure_roots_slot(), Word::empty()) .expect("storage map access failed"); assert_eq!(proc_root, Word::empty()); } @@ -315,7 +315,7 @@ mod tests { /// Test ACL component with no procedures and both authorization flags set to false #[test] - fn test_rpo_falcon_512_acl_no_procedures() { + fn test_falcon_512_rpo_acl_no_procedures() { test_acl_component(AclTestConfig { with_procedures: false, allow_unauthorized_output_notes: false, @@ -326,7 +326,7 @@ mod tests { /// Test ACL component with two procedures and both authorization flags set to false #[test] - fn test_rpo_falcon_512_acl_with_two_procedures() { + fn test_falcon_512_rpo_acl_with_two_procedures() { test_acl_component(AclTestConfig { with_procedures: true, allow_unauthorized_output_notes: false, @@ -337,7 +337,7 @@ mod tests { /// Test ACL component with no procedures and allow_unauthorized_output_notes set to true #[test] - fn test_rpo_falcon_512_acl_with_allow_unauthorized_output_notes() { + fn test_falcon_512_rpo_acl_with_allow_unauthorized_output_notes() { test_acl_component(AclTestConfig { with_procedures: false, allow_unauthorized_output_notes: true, @@ -348,7 +348,7 @@ mod tests { /// Test ACL component with two procedures and allow_unauthorized_output_notes set to true #[test] - fn test_rpo_falcon_512_acl_with_procedures_and_allow_unauthorized_output_notes() { + fn test_falcon_512_rpo_acl_with_procedures_and_allow_unauthorized_output_notes() { test_acl_component(AclTestConfig { with_procedures: true, allow_unauthorized_output_notes: true, @@ -359,7 +359,7 @@ mod tests { /// Test ACL component with no procedures and allow_unauthorized_input_notes set to true #[test] - fn test_rpo_falcon_512_acl_with_allow_unauthorized_input_notes() { + fn test_falcon_512_rpo_acl_with_allow_unauthorized_input_notes() { test_acl_component(AclTestConfig { with_procedures: false, allow_unauthorized_output_notes: false, @@ -370,7 +370,7 @@ mod tests { /// Test ACL component with two procedures and both authorization flags set to true #[test] - fn test_rpo_falcon_512_acl_with_both_allow_flags() { + fn test_falcon_512_rpo_acl_with_both_allow_flags() { test_acl_component(AclTestConfig { with_procedures: true, allow_unauthorized_output_notes: true, diff --git a/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs b/crates/miden-standards/src/account/auth/falcon_512_rpo_multisig.rs similarity index 85% rename from crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs rename to crates/miden-standards/src/account/auth/falcon_512_rpo_multisig.rs index 6fc0c4e284..0b038c20a1 100644 --- a/crates/miden-standards/src/account/auth/rpo_falcon_512_multisig.rs +++ b/crates/miden-standards/src/account/auth/falcon_512_rpo_multisig.rs @@ -7,40 +7,40 @@ use miden_protocol::account::{AccountComponent, StorageMap, StorageSlot, Storage use miden_protocol::errors::AccountError; use miden_protocol::utils::sync::LazyLock; -use crate::account::components::rpo_falcon_512_multisig_library; +use crate::account::components::falcon_512_rpo_multisig_library; static THRESHOLD_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512_multisig::threshold_config") + StorageSlotName::new("miden::standards::auth::falcon512_rpo_multisig::threshold_config") .expect("storage slot name should be valid") }); static APPROVER_PUBKEYS_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512_multisig::approver_public_keys") + StorageSlotName::new("miden::standards::auth::falcon512_rpo_multisig::approver_public_keys") .expect("storage slot name should be valid") }); static EXECUTED_TRANSACTIONS_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512_multisig::executed_transactions") + StorageSlotName::new("miden::standards::auth::falcon512_rpo_multisig::executed_transactions") .expect("storage slot name should be valid") }); static PROCEDURE_THRESHOLDS_SLOT_NAME: LazyLock = LazyLock::new(|| { - StorageSlotName::new("miden::standards::auth::rpo_falcon512_multisig::procedure_thresholds") + StorageSlotName::new("miden::standards::auth::falcon512_rpo_multisig::procedure_thresholds") .expect("storage slot name should be valid") }); // MULTISIG AUTHENTICATION COMPONENT // ================================================================================================ -/// Configuration for [`AuthRpoFalcon512Multisig`] component. +/// Configuration for [`AuthFalcon512RpoMultisig`] component. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct AuthRpoFalcon512MultisigConfig { +pub struct AuthFalcon512RpoMultisigConfig { approvers: Vec, default_threshold: u32, proc_thresholds: Vec<(Word, u32)>, } -impl AuthRpoFalcon512MultisigConfig { +impl AuthFalcon512RpoMultisigConfig { /// Creates a new configuration with the given approvers and a default threshold. /// /// The `default_threshold` must be at least 1 and at most the number of approvers. @@ -102,7 +102,7 @@ impl AuthRpoFalcon512MultisigConfig { } } -/// An [`AccountComponent`] implementing a multisig based on RpoFalcon512 signatures. +/// An [`AccountComponent`] implementing a multisig based on Falcon512Rpo signatures. /// /// It enforces a threshold of approver signatures for every transaction, with optional /// per-procedure thresholds overrides. Non-uniform thresholds (especially a threshold of one) @@ -119,13 +119,13 @@ impl AuthRpoFalcon512MultisigConfig { /// /// This component supports all account types. #[derive(Debug)] -pub struct AuthRpoFalcon512Multisig { - config: AuthRpoFalcon512MultisigConfig, +pub struct AuthFalcon512RpoMultisig { + config: AuthFalcon512RpoMultisigConfig, } -impl AuthRpoFalcon512Multisig { - /// Creates a new [`AuthRpoFalcon512Multisig`] component from the provided configuration. - pub fn new(config: AuthRpoFalcon512MultisigConfig) -> Result { +impl AuthFalcon512RpoMultisig { + /// Creates a new [`AuthFalcon512RpoMultisig`] component from the provided configuration. + pub fn new(config: AuthFalcon512RpoMultisigConfig) -> Result { Ok(Self { config }) } @@ -150,14 +150,14 @@ impl AuthRpoFalcon512Multisig { } } -impl From for AccountComponent { - fn from(multisig: AuthRpoFalcon512Multisig) -> Self { +impl From for AccountComponent { + fn from(multisig: AuthFalcon512RpoMultisig) -> Self { let mut storage_slots = Vec::with_capacity(3); // Threshold config slot (value: [threshold, num_approvers, 0, 0]) let num_approvers = multisig.config.approvers().len() as u32; storage_slots.push(StorageSlot::with_value( - AuthRpoFalcon512Multisig::threshold_config_slot().clone(), + AuthFalcon512RpoMultisig::threshold_config_slot().clone(), Word::from([multisig.config.default_threshold(), num_approvers, 0, 0]), )); @@ -171,14 +171,14 @@ impl From for AccountComponent { // Safe to unwrap because we know that the map keys are unique. storage_slots.push(StorageSlot::with_map( - AuthRpoFalcon512Multisig::approver_public_keys_slot().clone(), + AuthFalcon512RpoMultisig::approver_public_keys_slot().clone(), StorageMap::with_entries(map_entries).unwrap(), )); // Executed transactions slot (map) let executed_transactions = StorageMap::default(); storage_slots.push(StorageSlot::with_map( - AuthRpoFalcon512Multisig::executed_transactions_slot().clone(), + AuthFalcon512RpoMultisig::executed_transactions_slot().clone(), executed_transactions, )); @@ -192,11 +192,11 @@ impl From for AccountComponent { ) .unwrap(); storage_slots.push(StorageSlot::with_map( - AuthRpoFalcon512Multisig::procedure_thresholds_slot().clone(), + AuthFalcon512RpoMultisig::procedure_thresholds_slot().clone(), proc_threshold_roots, )); - AccountComponent::new(rpo_falcon_512_multisig_library(), storage_slots) + AccountComponent::new(falcon_512_rpo_multisig_library(), storage_slots) .expect("Multisig auth component should satisfy the requirements of a valid account component") .with_supports_all_types() } @@ -223,8 +223,8 @@ mod tests { let threshold = 2u32; // Create multisig component - let multisig_component = AuthRpoFalcon512Multisig::new( - AuthRpoFalcon512MultisigConfig::new(approvers.clone(), threshold) + let multisig_component = AuthFalcon512RpoMultisig::new( + AuthFalcon512RpoMultisigConfig::new(approvers.clone(), threshold) .expect("invalid multisig config"), ) .expect("multisig component creation failed"); @@ -239,7 +239,7 @@ mod tests { // Verify config slot: [threshold, num_approvers, 0, 0] let config_slot = account .storage() - .get_item(AuthRpoFalcon512Multisig::threshold_config_slot()) + .get_item(AuthFalcon512RpoMultisig::threshold_config_slot()) .expect("config storage slot access failed"); assert_eq!(config_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); @@ -248,7 +248,7 @@ mod tests { let stored_pub_key = account .storage() .get_map_item( - AuthRpoFalcon512Multisig::approver_public_keys_slot(), + AuthFalcon512RpoMultisig::approver_public_keys_slot(), Word::from([i as u32, 0, 0, 0]), ) .expect("approver public key storage map access failed"); @@ -263,8 +263,8 @@ mod tests { let approvers = vec![pub_key]; let threshold = 1u32; - let multisig_component = AuthRpoFalcon512Multisig::new( - AuthRpoFalcon512MultisigConfig::new(approvers.clone(), threshold) + let multisig_component = AuthFalcon512RpoMultisig::new( + AuthFalcon512RpoMultisigConfig::new(approvers.clone(), threshold) .expect("invalid multisig config"), ) .expect("multisig component creation failed"); @@ -278,14 +278,14 @@ mod tests { // Verify storage layout let config_slot = account .storage() - .get_item(AuthRpoFalcon512Multisig::threshold_config_slot()) + .get_item(AuthFalcon512RpoMultisig::threshold_config_slot()) .expect("config storage slot access failed"); assert_eq!(config_slot, Word::from([threshold, approvers.len() as u32, 0, 0])); let stored_pub_key = account .storage() .get_map_item( - AuthRpoFalcon512Multisig::approver_public_keys_slot(), + AuthFalcon512RpoMultisig::approver_public_keys_slot(), Word::from([0u32, 0, 0, 0]), ) .expect("approver pub keys storage map access failed"); @@ -299,11 +299,11 @@ mod tests { let approvers = vec![pub_key]; // Test threshold = 0 (should fail) - let result = AuthRpoFalcon512MultisigConfig::new(approvers.clone(), 0); + let result = AuthFalcon512RpoMultisigConfig::new(approvers.clone(), 0); assert!(result.unwrap_err().to_string().contains("threshold must be at least 1")); // Test threshold > number of approvers (should fail) - let result = AuthRpoFalcon512MultisigConfig::new(approvers, 2); + let result = AuthFalcon512RpoMultisigConfig::new(approvers, 2); assert!( result .unwrap_err() @@ -320,7 +320,7 @@ mod tests { // Test with duplicate approvers (should fail) let approvers = vec![pub_key_1, pub_key_2, pub_key_1]; - let result = AuthRpoFalcon512MultisigConfig::new(approvers, 2); + let result = AuthFalcon512RpoMultisigConfig::new(approvers, 2); assert!( result .unwrap_err() diff --git a/crates/miden-standards/src/account/auth/mod.rs b/crates/miden-standards/src/account/auth/mod.rs index 3eaedd20dc..5d239b6cfb 100644 --- a/crates/miden-standards/src/account/auth/mod.rs +++ b/crates/miden-standards/src/account/auth/mod.rs @@ -13,11 +13,11 @@ pub use ecdsa_k256_keccak_multisig::{ AuthEcdsaK256KeccakMultisigConfig, }; -mod rpo_falcon_512; -pub use rpo_falcon_512::AuthRpoFalcon512; +mod falcon_512_rpo; +pub use falcon_512_rpo::AuthFalcon512Rpo; -mod rpo_falcon_512_acl; -pub use rpo_falcon_512_acl::{AuthRpoFalcon512Acl, AuthRpoFalcon512AclConfig}; +mod falcon_512_rpo_acl; +pub use falcon_512_rpo_acl::{AuthFalcon512RpoAcl, AuthFalcon512RpoAclConfig}; -mod rpo_falcon_512_multisig; -pub use rpo_falcon_512_multisig::{AuthRpoFalcon512Multisig, AuthRpoFalcon512MultisigConfig}; +mod falcon_512_rpo_multisig; +pub use falcon_512_rpo_multisig::{AuthFalcon512RpoMultisig, AuthFalcon512RpoMultisigConfig}; diff --git a/crates/miden-standards/src/account/components/mod.rs b/crates/miden-standards/src/account/components/mod.rs index b99c0a24ab..3b0f7e2f3f 100644 --- a/crates/miden-standards/src/account/components/mod.rs +++ b/crates/miden-standards/src/account/components/mod.rs @@ -53,31 +53,31 @@ static ECDSA_K256_KECCAK_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| .expect("Shipped Multisig Ecdsa K256 Keccak library is well-formed") }); -// Initialize the Rpo Falcon 512 library only once. -static RPO_FALCON_512_LIBRARY: LazyLock = LazyLock::new(|| { +// Initialize the Falcon 512 RPO library only once. +static FALCON_512_RPO_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/auth/rpo_falcon_512.masl" + "/assets/account_components/auth/falcon_512_rpo.masl" )); - Library::read_from_bytes(bytes).expect("Shipped Rpo Falcon 512 library is well-formed") + Library::read_from_bytes(bytes).expect("Shipped Falcon 512 RPO library is well-formed") }); -// Initialize the Rpo Falcon 512 ACL library only once. -static RPO_FALCON_512_ACL_LIBRARY: LazyLock = LazyLock::new(|| { +// Initialize the Falcon 512 RPO ACL library only once. +static FALCON_512_RPO_ACL_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/auth/rpo_falcon_512_acl.masl" + "/assets/account_components/auth/falcon_512_rpo_acl.masl" )); - Library::read_from_bytes(bytes).expect("Shipped Rpo Falcon 512 ACL library is well-formed") + Library::read_from_bytes(bytes).expect("Shipped Falcon 512 RPO ACL library is well-formed") }); -// Initialize the Multisig Rpo Falcon 512 library only once. -static RPO_FALCON_512_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { +// Initialize the Multisig Falcon 512 RPO library only once. +static FALCON_512_RPO_MULTISIG_LIBRARY: LazyLock = LazyLock::new(|| { let bytes = include_bytes!(concat!( env!("OUT_DIR"), - "/assets/account_components/auth/rpo_falcon_512_multisig.masl" + "/assets/account_components/auth/falcon_512_rpo_multisig.masl" )); - Library::read_from_bytes(bytes).expect("Shipped Multisig Rpo Falcon 512 library is well-formed") + Library::read_from_bytes(bytes).expect("Shipped Multisig Falcon 512 RPO library is well-formed") }); // Initialize the NoAuth library only once. @@ -155,14 +155,14 @@ pub fn ecdsa_k256_keccak_multisig_library() -> Library { ECDSA_K256_KECCAK_MULTISIG_LIBRARY.clone() } -/// Returns the Rpo Falcon 512 Library. -pub fn rpo_falcon_512_library() -> Library { - RPO_FALCON_512_LIBRARY.clone() +/// Returns the Falcon 512 RPO Library. +pub fn falcon_512_rpo_library() -> Library { + FALCON_512_RPO_LIBRARY.clone() } -/// Returns the Rpo Falcon 512 ACL Library. -pub fn rpo_falcon_512_acl_library() -> Library { - RPO_FALCON_512_ACL_LIBRARY.clone() +/// Returns the Falcon 512 RPO ACL Library. +pub fn falcon_512_rpo_acl_library() -> Library { + FALCON_512_RPO_ACL_LIBRARY.clone() } /// Returns the NoAuth Library. @@ -170,9 +170,9 @@ pub fn no_auth_library() -> Library { NO_AUTH_LIBRARY.clone() } -/// Returns the RPO Falcon 512 Multisig Library. -pub fn rpo_falcon_512_multisig_library() -> Library { - RPO_FALCON_512_MULTISIG_LIBRARY.clone() +/// Returns the Falcon 512 RPO Multisig Library. +pub fn falcon_512_rpo_multisig_library() -> Library { + FALCON_512_RPO_MULTISIG_LIBRARY.clone() } // WELL KNOWN COMPONENTS @@ -186,9 +186,9 @@ pub enum WellKnownComponent { AuthEcdsaK256Keccak, AuthEcdsaK256KeccakAcl, AuthEcdsaK256KeccakMultisig, - AuthRpoFalcon512, - AuthRpoFalcon512Acl, - AuthRpoFalcon512Multisig, + AuthFalcon512Rpo, + AuthFalcon512RpoAcl, + AuthFalcon512RpoMultisig, AuthNoAuth, } @@ -202,9 +202,9 @@ impl WellKnownComponent { Self::AuthEcdsaK256Keccak => ECDSA_K256_KECCAK_LIBRARY.as_ref(), Self::AuthEcdsaK256KeccakAcl => ECDSA_K256_KECCAK_ACL_LIBRARY.as_ref(), Self::AuthEcdsaK256KeccakMultisig => ECDSA_K256_KECCAK_MULTISIG_LIBRARY.as_ref(), - Self::AuthRpoFalcon512 => RPO_FALCON_512_LIBRARY.as_ref(), - Self::AuthRpoFalcon512Acl => RPO_FALCON_512_ACL_LIBRARY.as_ref(), - Self::AuthRpoFalcon512Multisig => RPO_FALCON_512_MULTISIG_LIBRARY.as_ref(), + Self::AuthFalcon512Rpo => FALCON_512_RPO_LIBRARY.as_ref(), + Self::AuthFalcon512RpoAcl => FALCON_512_RPO_ACL_LIBRARY.as_ref(), + Self::AuthFalcon512RpoMultisig => FALCON_512_RPO_MULTISIG_LIBRARY.as_ref(), Self::AuthNoAuth => NO_AUTH_LIBRARY.as_ref(), }; @@ -256,14 +256,14 @@ impl WellKnownComponent { }, Self::AuthEcdsaK256KeccakMultisig => component_interface_vec .push(AccountComponentInterface::AuthEcdsaK256KeccakMultisig), - Self::AuthRpoFalcon512 => { - component_interface_vec.push(AccountComponentInterface::AuthRpoFalcon512) + Self::AuthFalcon512Rpo => { + component_interface_vec.push(AccountComponentInterface::AuthFalcon512Rpo) }, - Self::AuthRpoFalcon512Acl => { - component_interface_vec.push(AccountComponentInterface::AuthRpoFalcon512Acl) + Self::AuthFalcon512RpoAcl => { + component_interface_vec.push(AccountComponentInterface::AuthFalcon512RpoAcl) }, - Self::AuthRpoFalcon512Multisig => component_interface_vec - .push(AccountComponentInterface::AuthRpoFalcon512Multisig), + Self::AuthFalcon512RpoMultisig => component_interface_vec + .push(AccountComponentInterface::AuthFalcon512RpoMultisig), Self::AuthNoAuth => { component_interface_vec.push(AccountComponentInterface::AuthNoAuth) }, @@ -284,9 +284,9 @@ impl WellKnownComponent { Self::AuthEcdsaK256KeccakAcl.extract_component(procedures_set, component_interface_vec); Self::AuthEcdsaK256KeccakMultisig .extract_component(procedures_set, component_interface_vec); - Self::AuthRpoFalcon512.extract_component(procedures_set, component_interface_vec); - Self::AuthRpoFalcon512Acl.extract_component(procedures_set, component_interface_vec); - Self::AuthRpoFalcon512Multisig.extract_component(procedures_set, component_interface_vec); + Self::AuthFalcon512Rpo.extract_component(procedures_set, component_interface_vec); + Self::AuthFalcon512RpoAcl.extract_component(procedures_set, component_interface_vec); + Self::AuthFalcon512RpoMultisig.extract_component(procedures_set, component_interface_vec); Self::AuthNoAuth.extract_component(procedures_set, component_interface_vec); } } diff --git a/crates/miden-standards/src/account/faucets/basic_fungible.rs b/crates/miden-standards/src/account/faucets/basic_fungible.rs index 57c29385e8..b848b43431 100644 --- a/crates/miden-standards/src/account/faucets/basic_fungible.rs +++ b/crates/miden-standards/src/account/faucets/basic_fungible.rs @@ -16,8 +16,8 @@ use crate::account::AuthScheme; use crate::account::auth::{ AuthEcdsaK256KeccakAcl, AuthEcdsaK256KeccakAclConfig, - AuthRpoFalcon512Acl, - AuthRpoFalcon512AclConfig, + AuthFalcon512RpoAcl, + AuthFalcon512RpoAclConfig, }; use crate::account::components::basic_fungible_faucet_library; use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; @@ -254,9 +254,9 @@ pub fn create_basic_fungible_faucet( let distribute_proc_root = BasicFungibleFaucet::distribute_digest(); let auth_component: AccountComponent = match auth_scheme { - AuthScheme::RpoFalcon512 { pub_key } => AuthRpoFalcon512Acl::new( + AuthScheme::Falcon512Rpo { pub_key } => AuthFalcon512RpoAcl::new( pub_key, - AuthRpoFalcon512AclConfig::new() + AuthFalcon512RpoAclConfig::new() .with_auth_trigger_procedures(vec![distribute_proc_root]) .with_allow_unauthorized_input_notes(true), ) @@ -275,7 +275,7 @@ pub fn create_basic_fungible_faucet( "basic fungible faucets cannot be created with NoAuth authentication scheme".into(), )); }, - AuthScheme::RpoFalcon512Multisig { threshold: _, pub_keys: _ } => { + AuthScheme::Falcon512RpoMultisig { threshold: _, pub_keys: _ } => { return Err(FungibleFaucetError::UnsupportedAuthScheme( "basic fungible faucets do not support multisig authentication".into(), )); @@ -326,13 +326,13 @@ mod tests { TokenSymbol, create_basic_fungible_faucet, }; - use crate::account::auth::{AuthRpoFalcon512, AuthRpoFalcon512Acl}; + use crate::account::auth::{AuthFalcon512Rpo, AuthFalcon512RpoAcl}; use crate::account::wallets::BasicWallet; #[test] fn faucet_contract_creation() { let pub_key_word = Word::new([ONE; 4]); - let auth_scheme: AuthScheme = AuthScheme::RpoFalcon512 { pub_key: pub_key_word.into() }; + let auth_scheme: AuthScheme = AuthScheme::Falcon512Rpo { pub_key: pub_key_word.into() }; // we need to use an initial seed to create the wallet account let init_seed: [u8; 32] = [ @@ -369,7 +369,7 @@ mod tests { assert_eq!( faucet_account .storage() - .get_item(AuthRpoFalcon512Acl::public_key_slot()) + .get_item(AuthFalcon512RpoAcl::public_key_slot()) .unwrap(), pub_key_word ); @@ -380,7 +380,7 @@ mod tests { // With 1 trigger procedure (distribute), allow_unauthorized_output_notes=false, and // allow_unauthorized_input_notes=true, this should be [1, 0, 1, 0]. assert_eq!( - faucet_account.storage().get_item(AuthRpoFalcon512Acl::config_slot()).unwrap(), + faucet_account.storage().get_item(AuthFalcon512RpoAcl::config_slot()).unwrap(), [Felt::ONE, Felt::ZERO, Felt::ONE, Felt::ZERO].into() ); @@ -390,7 +390,7 @@ mod tests { faucet_account .storage() .get_map_item( - AuthRpoFalcon512Acl::trigger_procedure_roots_slot(), + AuthFalcon512RpoAcl::trigger_procedure_roots_slot(), [Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO].into() ) .unwrap(), @@ -429,7 +429,7 @@ mod tests { BasicFungibleFaucet::new(token_symbol, 10, Felt::new(100)) .expect("failed to create a fungible faucet component"), ) - .with_auth_component(AuthRpoFalcon512::new(mock_public_key)) + .with_auth_component(AuthFalcon512Rpo::new(mock_public_key)) .build_existing() .expect("failed to create wallet account"); @@ -442,7 +442,7 @@ mod tests { // invalid account: basic fungible faucet component is missing let invalid_faucet_account = AccountBuilder::new(mock_seed) .account_type(AccountType::FungibleFaucet) - .with_auth_component(AuthRpoFalcon512::new(mock_public_key)) + .with_auth_component(AuthFalcon512Rpo::new(mock_public_key)) // we need to add some other component so the builder doesn't fail .with_component(BasicWallet) .build_existing() diff --git a/crates/miden-standards/src/account/interface/component.rs b/crates/miden-standards/src/account/interface/component.rs index 16bbe0ec91..110e23d1b3 100644 --- a/crates/miden-standards/src/account/interface/component.rs +++ b/crates/miden-standards/src/account/interface/component.rs @@ -11,9 +11,9 @@ use crate::account::auth::{ AuthEcdsaK256Keccak, AuthEcdsaK256KeccakAcl, AuthEcdsaK256KeccakMultisig, - AuthRpoFalcon512, - AuthRpoFalcon512Acl, - AuthRpoFalcon512Multisig, + AuthFalcon512Rpo, + AuthFalcon512RpoAcl, + AuthFalcon512RpoMultisig, }; use crate::account::interface::AccountInterfaceError; @@ -41,14 +41,14 @@ pub enum AccountComponentInterface { /// [`AuthEcdsaK256KeccakMultisig`][crate::account::auth::AuthEcdsaK256KeccakMultisig] module. AuthEcdsaK256KeccakMultisig, /// Exposes procedures from the - /// [`AuthRpoFalcon512`][crate::account::auth::AuthRpoFalcon512] module. - AuthRpoFalcon512, + /// [`AuthFalcon512Rpo`][crate::account::auth::AuthFalcon512Rpo] module. + AuthFalcon512Rpo, /// Exposes procedures from the - /// [`AuthRpoFalcon512Acl`][crate::account::auth::AuthRpoFalcon512Acl] module. - AuthRpoFalcon512Acl, + /// [`AuthFalcon512RpoAcl`][crate::account::auth::AuthFalcon512RpoAcl] module. + AuthFalcon512RpoAcl, /// Exposes procedures from the - /// [`AuthRpoFalcon512Multisig`][crate::account::auth::AuthRpoFalcon512Multisig] module. - AuthRpoFalcon512Multisig, + /// [`AuthFalcon512RpoMultisig`][crate::account::auth::AuthFalcon512RpoMultisig] module. + AuthFalcon512RpoMultisig, /// Exposes procedures from the [`NoAuth`][crate::account::auth::NoAuth] module. /// /// This authentication scheme provides no cryptographic authentication and only increments @@ -81,10 +81,10 @@ impl AccountComponentInterface { AccountComponentInterface::AuthEcdsaK256KeccakMultisig => { "ECDSA K256 Keccak Multisig".to_string() }, - AccountComponentInterface::AuthRpoFalcon512 => "RPO Falcon512".to_string(), - AccountComponentInterface::AuthRpoFalcon512Acl => "RPO Falcon512 ACL".to_string(), - AccountComponentInterface::AuthRpoFalcon512Multisig => { - "RPO Falcon512 Multisig".to_string() + AccountComponentInterface::AuthFalcon512Rpo => "Falcon512 RPO".to_string(), + AccountComponentInterface::AuthFalcon512RpoAcl => "Falcon512 RPO ACL".to_string(), + AccountComponentInterface::AuthFalcon512RpoMultisig => { + "Falcon512 RPO Multisig".to_string() }, AccountComponentInterface::AuthNoAuth => "No Auth".to_string(), @@ -108,9 +108,9 @@ impl AccountComponentInterface { AccountComponentInterface::AuthEcdsaK256Keccak | AccountComponentInterface::AuthEcdsaK256KeccakAcl | AccountComponentInterface::AuthEcdsaK256KeccakMultisig - | AccountComponentInterface::AuthRpoFalcon512 - | AccountComponentInterface::AuthRpoFalcon512Acl - | AccountComponentInterface::AuthRpoFalcon512Multisig + | AccountComponentInterface::AuthFalcon512Rpo + | AccountComponentInterface::AuthFalcon512RpoAcl + | AccountComponentInterface::AuthFalcon512RpoMultisig | AccountComponentInterface::AuthNoAuth ) } @@ -143,29 +143,29 @@ impl AccountComponentInterface { AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(), )] }, - AccountComponentInterface::AuthRpoFalcon512 => { - vec![AuthScheme::RpoFalcon512 { + AccountComponentInterface::AuthFalcon512Rpo => { + vec![AuthScheme::Falcon512Rpo { pub_key: PublicKeyCommitment::from( storage - .get_item(AuthRpoFalcon512::public_key_slot()) - .expect("invalid slot name of the AuthRpoFalcon512 public key"), + .get_item(AuthFalcon512Rpo::public_key_slot()) + .expect("invalid slot name of the AuthFalcon512Rpo public key"), ), }] }, - AccountComponentInterface::AuthRpoFalcon512Acl => { - vec![AuthScheme::RpoFalcon512 { + AccountComponentInterface::AuthFalcon512RpoAcl => { + vec![AuthScheme::Falcon512Rpo { pub_key: PublicKeyCommitment::from( storage - .get_item(AuthRpoFalcon512Acl::public_key_slot()) - .expect("invalid slot name of the AuthRpoFalcon512Acl public key"), + .get_item(AuthFalcon512RpoAcl::public_key_slot()) + .expect("invalid slot name of the AuthFalcon512RpoAcl public key"), ), }] }, - AccountComponentInterface::AuthRpoFalcon512Multisig => { + AccountComponentInterface::AuthFalcon512RpoMultisig => { vec![extract_multisig_auth_scheme( storage, - AuthRpoFalcon512Multisig::threshold_config_slot(), - AuthRpoFalcon512Multisig::approver_public_keys_slot(), + AuthFalcon512RpoMultisig::threshold_config_slot(), + AuthFalcon512RpoMultisig::approver_public_keys_slot(), )] }, AccountComponentInterface::AuthNoAuth => vec![AuthScheme::NoAuth], @@ -354,5 +354,5 @@ fn extract_multisig_auth_scheme( } } - AuthScheme::RpoFalcon512Multisig { threshold, pub_keys } + AuthScheme::Falcon512RpoMultisig { threshold, pub_keys } } diff --git a/crates/miden-standards/src/account/interface/extension.rs b/crates/miden-standards/src/account/interface/extension.rs index d83cdd2e7f..e563408c33 100644 --- a/crates/miden-standards/src/account/interface/extension.rs +++ b/crates/miden-standards/src/account/interface/extension.rs @@ -16,11 +16,11 @@ use crate::account::components::{ ecdsa_k256_keccak_acl_library, ecdsa_k256_keccak_library, ecdsa_k256_keccak_multisig_library, + falcon_512_rpo_acl_library, + falcon_512_rpo_library, + falcon_512_rpo_multisig_library, network_fungible_faucet_library, no_auth_library, - rpo_falcon_512_acl_library, - rpo_falcon_512_library, - rpo_falcon_512_multisig_library, }; use crate::account::interface::{ AccountComponentInterface, @@ -116,17 +116,17 @@ impl AccountInterfaceExt for AccountInterface { ecdsa_k256_keccak_multisig_library().mast_forest().procedure_digests(), ); }, - AccountComponentInterface::AuthRpoFalcon512 => { + AccountComponentInterface::AuthFalcon512Rpo => { component_proc_digests - .extend(rpo_falcon_512_library().mast_forest().procedure_digests()); + .extend(falcon_512_rpo_library().mast_forest().procedure_digests()); }, - AccountComponentInterface::AuthRpoFalcon512Acl => { + AccountComponentInterface::AuthFalcon512RpoAcl => { component_proc_digests - .extend(rpo_falcon_512_acl_library().mast_forest().procedure_digests()); + .extend(falcon_512_rpo_acl_library().mast_forest().procedure_digests()); }, - AccountComponentInterface::AuthRpoFalcon512Multisig => { + AccountComponentInterface::AuthFalcon512RpoMultisig => { component_proc_digests.extend( - rpo_falcon_512_multisig_library().mast_forest().procedure_digests(), + falcon_512_rpo_multisig_library().mast_forest().procedure_digests(), ); }, AccountComponentInterface::AuthNoAuth => { diff --git a/crates/miden-standards/src/account/interface/test.rs b/crates/miden-standards/src/account/interface/test.rs index 3675226458..e6639b32f0 100644 --- a/crates/miden-standards/src/account/interface/test.rs +++ b/crates/miden-standards/src/account/interface/test.rs @@ -23,9 +23,9 @@ use miden_protocol::{Felt, Word}; use crate::AuthScheme; use crate::account::auth::{ AuthEcdsaK256Keccak, - AuthRpoFalcon512, - AuthRpoFalcon512Multisig, - AuthRpoFalcon512MultisigConfig, + AuthFalcon512Rpo, + AuthFalcon512RpoMultisig, + AuthFalcon512RpoMultisigConfig, NoAuth, }; use crate::account::faucets::BasicFungibleFaucet; @@ -626,10 +626,10 @@ fn test_custom_account_multiple_components_custom_notes() { // ================================================================================================ /// Helper function to create a mock auth component for testing -fn get_mock_auth_component() -> AuthRpoFalcon512 { +fn get_mock_auth_component() -> AuthFalcon512Rpo { let mock_word = Word::from([0, 1, 2, 3u32]); let mock_public_key = PublicKeyCommitment::from(mock_word); - AuthRpoFalcon512::new(mock_public_key) + AuthFalcon512Rpo::new(mock_public_key) } /// Helper function to create a mock Ecdsa auth component for testing @@ -673,7 +673,7 @@ fn test_get_auth_scheme_ecdsa_k256_keccak() { } #[test] -fn test_get_auth_scheme_rpo_falcon512() { +fn test_get_auth_scheme_falcon512_rpo() { let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let wallet_account = AccountBuilder::new(mock_seed) .with_auth_component(get_mock_auth_component()) @@ -683,22 +683,22 @@ fn test_get_auth_scheme_rpo_falcon512() { let wallet_account_interface = AccountInterface::from_account(&wallet_account); - // Find the RpoFalcon512 component interface + // Find the Falcon512Rpo component interface let rpo_falcon_component = wallet_account_interface .components() .iter() - .find(|component| matches!(component, AccountComponentInterface::AuthRpoFalcon512)) - .expect("should have RpoFalcon512 component"); + .find(|component| matches!(component, AccountComponentInterface::AuthFalcon512Rpo)) + .expect("should have Falcon512Rpo component"); // Test get_auth_schemes method let auth_schemes = rpo_falcon_component.get_auth_schemes(wallet_account.storage()); assert_eq!(auth_schemes.len(), 1); let auth_scheme = &auth_schemes[0]; match auth_scheme { - AuthScheme::RpoFalcon512 { pub_key } => { + AuthScheme::Falcon512Rpo { pub_key } => { assert_eq!(*pub_key, PublicKeyCommitment::from(Word::from([0, 1, 2, 3u32]))); }, - _ => panic!("Expected RpoFalcon512 auth scheme"), + _ => panic!("Expected Falcon512Rpo auth scheme"), } } @@ -761,11 +761,11 @@ fn test_account_interface_from_account_uses_get_auth_scheme() { assert_eq!(wallet_account_interface.auth().len(), 1); match &wallet_account_interface.auth()[0] { - AuthScheme::RpoFalcon512 { pub_key } => { + AuthScheme::Falcon512Rpo { pub_key } => { let expected_pub_key = PublicKeyCommitment::from(Word::from([0, 1, 2, 3u32])); assert_eq!(*pub_key, expected_pub_key); }, - _ => panic!("Expected RpoFalcon512 auth scheme"), + _ => panic!("Expected Falcon512Rpo auth scheme"), } // Test with NoAuth @@ -786,7 +786,7 @@ fn test_account_interface_from_account_uses_get_auth_scheme() { } } -/// Test AccountInterface.get_auth_scheme() method with RpoFalcon512 and NoAuth +/// Test AccountInterface.get_auth_scheme() method with Falcon512Rpo and NoAuth #[test] fn test_account_interface_get_auth_scheme() { let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); @@ -801,10 +801,10 @@ fn test_account_interface_get_auth_scheme() { // Test that auth() method provides the authentication schemes assert_eq!(wallet_account_interface.auth().len(), 1); match &wallet_account_interface.auth()[0] { - AuthScheme::RpoFalcon512 { pub_key } => { + AuthScheme::Falcon512Rpo { pub_key } => { assert_eq!(*pub_key, PublicKeyCommitment::from(Word::from([0, 1, 2, 3u32]))); }, - _ => panic!("Expected RpoFalcon512 auth scheme"), + _ => panic!("Expected Falcon512Rpo auth scheme"), } // Test AccountInterface.get_auth_scheme() method with NoAuth @@ -853,8 +853,8 @@ fn test_public_key_extraction_multisig_account() { let threshold = 2u32; // Create multisig component - let multisig_component = AuthRpoFalcon512Multisig::new( - AuthRpoFalcon512MultisigConfig::new(approvers.clone(), threshold).unwrap(), + let multisig_component = AuthFalcon512RpoMultisig::new( + AuthFalcon512RpoMultisigConfig::new(approvers.clone(), threshold).unwrap(), ) .expect("multisig component creation failed"); diff --git a/crates/miden-standards/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs index c8e66a3020..23e9204322 100644 --- a/crates/miden-standards/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -16,9 +16,9 @@ use crate::account::auth::{ AuthEcdsaK256Keccak, AuthEcdsaK256KeccakMultisig, AuthEcdsaK256KeccakMultisigConfig, - AuthRpoFalcon512, - AuthRpoFalcon512Multisig, - AuthRpoFalcon512MultisigConfig, + AuthFalcon512Rpo, + AuthFalcon512RpoMultisig, + AuthFalcon512RpoMultisigConfig, }; use crate::account::components::basic_wallet_library; use crate::procedure_digest; @@ -132,14 +132,14 @@ pub fn create_basic_wallet( .map_err(BasicWalletError::AccountError)? .into() }, - AuthScheme::RpoFalcon512 { pub_key } => AuthRpoFalcon512::new(pub_key).into(), - AuthScheme::RpoFalcon512Multisig { threshold, pub_keys } => { - let config = AuthRpoFalcon512MultisigConfig::new(pub_keys, threshold) + AuthScheme::Falcon512Rpo { pub_key } => AuthFalcon512Rpo::new(pub_key).into(), + AuthScheme::Falcon512RpoMultisig { threshold, pub_keys } => { + let config = AuthFalcon512RpoMultisigConfig::new(pub_keys, threshold) .and_then(|cfg| { cfg.with_proc_thresholds(vec![(BasicWallet::receive_asset_digest(), 1)]) }) .map_err(BasicWalletError::AccountError)?; - AuthRpoFalcon512Multisig::new(config) + AuthFalcon512RpoMultisig::new(config) .map_err(BasicWalletError::AccountError)? .into() }, @@ -183,7 +183,7 @@ mod tests { let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4])); let wallet = create_basic_wallet( [1; 32], - AuthScheme::RpoFalcon512 { pub_key }, + AuthScheme::Falcon512Rpo { pub_key }, AccountType::RegularAccountImmutableCode, AccountStorageMode::Public, ); @@ -198,7 +198,7 @@ mod tests { let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4])); let wallet = create_basic_wallet( [1; 32], - AuthScheme::RpoFalcon512 { pub_key }, + AuthScheme::Falcon512Rpo { pub_key }, AccountType::RegularAccountImmutableCode, AccountStorageMode::Public, ) diff --git a/crates/miden-standards/src/auth_scheme.rs b/crates/miden-standards/src/auth_scheme.rs index 32c924f98c..d8a5e7cf56 100644 --- a/crates/miden-standards/src/auth_scheme.rs +++ b/crates/miden-standards/src/auth_scheme.rs @@ -19,17 +19,17 @@ pub enum AuthScheme { threshold: u32, pub_keys: Vec, }, - /// A single-key authentication scheme which relies RPO Falcon512 signatures. + /// A single-key authentication scheme which relies Falcon512 RPO signatures. /// - /// RPO Falcon512 is a variant of the [Falcon](https://falcon-sign.info/) signature scheme. + /// Falcon512 RPO is a variant of the [Falcon](https://falcon-sign.info/) signature scheme. /// This variant differs from the standard in that instead of using SHAKE256 hash function in /// the hash-to-point algorithm we use RPO256. This makes the signature more efficient to /// verify in Miden VM. - RpoFalcon512 { pub_key: PublicKeyCommitment }, - /// A multi-signature authentication scheme using RPO Falcon512 signatures. + Falcon512Rpo { pub_key: PublicKeyCommitment }, + /// A multi-signature authentication scheme using Falcon512 RPO signatures. /// /// Requires a threshold number of signatures from the provided public keys. - RpoFalcon512Multisig { + Falcon512RpoMultisig { threshold: u32, pub_keys: Vec, }, @@ -46,8 +46,8 @@ impl AuthScheme { AuthScheme::NoAuth => Vec::new(), AuthScheme::EcdsaK256Keccak { pub_key } => vec![*pub_key], AuthScheme::EcdsaK256KeccakMultisig { pub_keys, .. } => pub_keys.clone(), - AuthScheme::RpoFalcon512 { pub_key } => vec![*pub_key], - AuthScheme::RpoFalcon512Multisig { pub_keys, .. } => pub_keys.clone(), + AuthScheme::Falcon512Rpo { pub_key } => vec![*pub_key], + AuthScheme::Falcon512RpoMultisig { pub_keys, .. } => pub_keys.clone(), AuthScheme::Unknown => Vec::new(), } } diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index 177f182db2..6ff0536b97 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -527,17 +527,17 @@ async fn tx_summary_commitment_is_signed_by_falcon_auth() -> anyhow::Result<()> let account_interface = AccountInterface::from_account(&account); let pub_key = match account_interface.auth().first().unwrap() { - AuthScheme::RpoFalcon512 { pub_key } => pub_key, - AuthScheme::NoAuth => panic!("Expected RpoFalcon512 auth scheme, got NoAuth"), - AuthScheme::RpoFalcon512Multisig { .. } => { - panic!("Expected RpoFalcon512 auth scheme, got RpoFalcon512Multisig") + AuthScheme::Falcon512Rpo { pub_key } => pub_key, + AuthScheme::NoAuth => panic!("Expected Falcon512Rpo auth scheme, got NoAuth"), + AuthScheme::Falcon512RpoMultisig { .. } => { + panic!("Expected Falcon512Rpo auth scheme, got Falcon512RpoMultisig") }, - AuthScheme::Unknown => panic!("Expected RpoFalcon512 auth scheme, got Unknown"), + AuthScheme::Unknown => panic!("Expected Falcon512Rpo auth scheme, got Unknown"), AuthScheme::EcdsaK256Keccak { .. } => { - panic!("Expected RpoFalcon512 auth scheme, got EcdsaK256Keccak") + panic!("Expected Falcon512Rpo auth scheme, got EcdsaK256Keccak") }, AuthScheme::EcdsaK256KeccakMultisig { .. } => { - panic!("Expected RpoFalcon512 auth scheme, got EcdsaK256KeccakMultisig") + panic!("Expected Falcon512Rpo auth scheme, got EcdsaK256KeccakMultisig") }, }; @@ -596,12 +596,12 @@ async fn tx_summary_commitment_is_signed_by_ecdsa_auth() -> anyhow::Result<()> { panic!("Expected EcdsaK256Keccak auth scheme, got EcdsaK256KeccakMultisig") }, AuthScheme::NoAuth => panic!("Expected EcdsaK256Keccak auth scheme, got NoAuth"), - AuthScheme::RpoFalcon512Multisig { .. } => { - panic!("Expected EcdsaK256Keccak auth scheme, got RpoFalcon512Multisig") + AuthScheme::Falcon512RpoMultisig { .. } => { + panic!("Expected EcdsaK256Keccak auth scheme, got Falcon512RpoMultisig") }, AuthScheme::Unknown => panic!("Expected EcdsaK256Keccak auth scheme, got Unknown"), - AuthScheme::RpoFalcon512 { .. } => { - panic!("Expected EcdsaK256Keccak auth scheme, got RpoFalcon512") + AuthScheme::Falcon512Rpo { .. } => { + panic!("Expected EcdsaK256Keccak auth scheme, got Falcon512Rpo") }, }; diff --git a/crates/miden-testing/src/mock_chain/auth.rs b/crates/miden-testing/src/mock_chain/auth.rs index 33b3cd57cc..c65dbfd5e6 100644 --- a/crates/miden-testing/src/mock_chain/auth.rs +++ b/crates/miden-testing/src/mock_chain/auth.rs @@ -12,11 +12,11 @@ use miden_standards::account::auth::{ AuthEcdsaK256KeccakAclConfig, AuthEcdsaK256KeccakMultisig, AuthEcdsaK256KeccakMultisigConfig, - AuthRpoFalcon512, - AuthRpoFalcon512Acl, - AuthRpoFalcon512AclConfig, - AuthRpoFalcon512Multisig, - AuthRpoFalcon512MultisigConfig, + AuthFalcon512Rpo, + AuthFalcon512RpoAcl, + AuthFalcon512RpoAclConfig, + AuthFalcon512RpoMultisig, + AuthFalcon512RpoMultisigConfig, }; use miden_standards::testing::account_component::{ ConditionalAuthComponent, @@ -30,7 +30,7 @@ use rand_chacha::ChaCha20Rng; #[derive(Debug, Clone)] pub enum Auth { /// Creates a secret key for the account and creates a [BasicAuthenticator] used to - /// authenticate the account with [AuthRpoFalcon512]. + /// authenticate the account with [AuthFalcon512Rpo]. BasicAuth, /// Creates a secret key for the account and creates a [BasicAuthenticator] used to @@ -61,7 +61,7 @@ pub enum Auth { }, /// Creates a secret key for the account, and creates a [BasicAuthenticator] used to - /// authenticate the account with [AuthRpoFalcon512Acl]. Authentication will only be + /// authenticate the account with [AuthFalcon512RpoAcl]. Authentication will only be /// triggered if any of the procedures specified in the list are called during execution. Acl { auth_trigger_procedures: Vec, @@ -94,7 +94,7 @@ impl Auth { let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); - let component = AuthRpoFalcon512::new(pub_key).into(); + let component = AuthFalcon512Rpo::new(pub_key).into(); let authenticator = BasicAuthenticator::new(&[sec_key]); (component, Some(authenticator)) @@ -126,10 +126,10 @@ impl Auth { let pub_keys: Vec<_> = approvers.iter().map(|word| PublicKeyCommitment::from(*word)).collect(); - let config = AuthRpoFalcon512MultisigConfig::new(pub_keys, *threshold) + let config = AuthFalcon512RpoMultisigConfig::new(pub_keys, *threshold) .and_then(|cfg| cfg.with_proc_thresholds(proc_threshold_map.clone())) .expect("invalid multisig config"); - let component = AuthRpoFalcon512Multisig::new(config) + let component = AuthFalcon512RpoMultisig::new(config) .expect("multisig component creation failed") .into(); @@ -144,9 +144,9 @@ impl Auth { let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); - let component = AuthRpoFalcon512Acl::new( + let component = AuthFalcon512RpoAcl::new( pub_key, - AuthRpoFalcon512AclConfig::new() + AuthFalcon512RpoAclConfig::new() .with_auth_trigger_procedures(auth_trigger_procedures.clone()) .with_allow_unauthorized_output_notes(*allow_unauthorized_output_notes) .with_allow_unauthorized_input_notes(*allow_unauthorized_input_notes), diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index 2c32f08211..e205cbfe3e 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -17,8 +17,8 @@ use miden_protocol::testing::account_id::{ use miden_protocol::transaction::OutputNote; use miden_protocol::vm::AdviceMap; use miden_protocol::{Felt, Hasher, Word}; -use miden_standards::account::auth::AuthRpoFalcon512Multisig; -use miden_standards::account::components::rpo_falcon_512_multisig_library; +use miden_standards::account::auth::AuthFalcon512RpoMultisig; +use miden_standards::account::components::falcon_512_rpo_multisig_library; use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_standards::account::wallets::BasicWallet; use miden_standards::code_builder::CodeBuilder; @@ -401,12 +401,12 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::rpo_falcon_512_multisig::update_signers_and_threshold + call.::falcon_512_rpo_multisig::update_signers_and_threshold end "; let tx_script = CodeBuilder::default() - .with_dynamically_linked_library(rpo_falcon_512_multisig_library())? + .with_dynamically_linked_library(falcon_512_rpo_multisig_library())? .compile_tx_script(tx_script_code)?; let advice_inputs = AdviceInputs { @@ -471,7 +471,7 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); let storage_item = updated_multisig_account .storage() - .get_map_item(AuthRpoFalcon512Multisig::approver_public_keys_slot(), storage_key) + .get_map_item(AuthFalcon512RpoMultisig::approver_public_keys_slot(), storage_key) .unwrap(); let expected_word: Word = expected_key.to_commitment().into(); @@ -482,7 +482,7 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // Verify the threshold was updated by checking the config storage slot let threshold_config_storage = updated_multisig_account .storage() - .get_item(AuthRpoFalcon512Multisig::threshold_config_slot())?; + .get_item(AuthFalcon512RpoMultisig::threshold_config_slot())?; assert_eq!( threshold_config_storage[0], @@ -644,9 +644,9 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { // Create transaction script let tx_script = CodeBuilder::default() - .with_dynamically_linked_library(rpo_falcon_512_multisig_library())? + .with_dynamically_linked_library(falcon_512_rpo_multisig_library())? .compile_tx_script( - "begin\n call.::rpo_falcon_512_multisig::update_signers_and_threshold\nend", + "begin\n call.::falcon_512_rpo_multisig::update_signers_and_threshold\nend", )?; let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; @@ -715,7 +715,7 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); let storage_item = updated_multisig_account .storage() - .get_map_item(AuthRpoFalcon512Multisig::approver_public_keys_slot(), storage_key)?; + .get_map_item(AuthFalcon512RpoMultisig::approver_public_keys_slot(), storage_key)?; let expected_word: Word = expected_key.to_commitment().into(); assert_eq!(storage_item, expected_word, "Public key {} doesn't match", i); } @@ -723,7 +723,7 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { // Verify threshold and num_approvers let threshold_config = updated_multisig_account .storage() - .get_item(AuthRpoFalcon512Multisig::threshold_config_slot())?; + .get_item(AuthFalcon512RpoMultisig::threshold_config_slot())?; assert_eq!(threshold_config[0], Felt::new(threshold), "Threshold not updated"); assert_eq!(threshold_config[1], Felt::new(num_of_approvers), "Num approvers not updated"); @@ -745,7 +745,7 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { [Felt::new(removed_idx), Felt::new(0), Felt::new(0), Felt::new(0)].into(); let removed_owner_slot = updated_multisig_account .storage() - .get_map_item(AuthRpoFalcon512Multisig::approver_public_keys_slot(), removed_owner_key) + .get_map_item(AuthFalcon512RpoMultisig::approver_public_keys_slot(), removed_owner_key) .unwrap(); assert_eq!( removed_owner_slot, @@ -761,7 +761,7 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { let storage_key = [Felt::new(i as u64), Felt::new(0), Felt::new(0), Felt::new(0)].into(); let storage_item = updated_multisig_account .storage() - .get_map_item(AuthRpoFalcon512Multisig::approver_public_keys_slot(), storage_key) + .get_map_item(AuthFalcon512RpoMultisig::approver_public_keys_slot(), storage_key) .unwrap(); if storage_item != Word::empty() { @@ -844,12 +844,12 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu // Create a transaction script that calls the update_signers procedure let tx_script_code = " begin - call.::rpo_falcon_512_multisig::update_signers_and_threshold + call.::falcon_512_rpo_multisig::update_signers_and_threshold end "; let tx_script = CodeBuilder::default() - .with_dynamically_linked_library(rpo_falcon_512_multisig_library())? + .with_dynamically_linked_library(falcon_512_rpo_multisig_library())? .compile_tx_script(tx_script_code)?; let advice_inputs = AdviceInputs { diff --git a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs index 91519a07bb..d6305848fb 100644 --- a/crates/miden-testing/tests/auth/rpo_falcon_acl.rs +++ b/crates/miden-testing/tests/auth/rpo_falcon_acl.rs @@ -14,7 +14,7 @@ use miden_protocol::note::Note; use miden_protocol::testing::storage::MOCK_VALUE_SLOT0; use miden_protocol::transaction::OutputNote; use miden_protocol::{Felt, FieldElement, Word}; -use miden_standards::account::auth::AuthRpoFalcon512Acl; +use miden_standards::account::auth::AuthFalcon512RpoAcl; use miden_standards::code_builder::CodeBuilder; use miden_standards::testing::account_component::MockAccountComponent; use miden_standards::testing::note::NoteBuilder; @@ -35,7 +35,7 @@ const TX_SCRIPT_NO_TRIGGER: &str = r#" // HELPER FUNCTIONS // ================================================================================================ -/// Sets up the basic components needed for RPO Falcon ACL tests. +/// Sets up the basic components needed for Falcon RPO ACL tests. /// Returns (account, mock_chain, note). fn setup_rpo_falcon_acl_test( allow_unauthorized_output_notes: bool, @@ -203,7 +203,7 @@ async fn test_rpo_falcon_acl_with_allow_unauthorized_output_notes() -> anyhow::R // Verify the storage layout includes both authorization flags let config_slot = account .storage() - .get_item(AuthRpoFalcon512Acl::config_slot()) + .get_item(AuthFalcon512RpoAcl::config_slot()) .expect("config storage slot access failed"); // Config Slot should be [num_trigger_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, @@ -243,7 +243,7 @@ async fn test_rpo_falcon_acl_with_disallow_unauthorized_input_notes() -> anyhow: // Verify the storage layout includes both flags let config_slot = account .storage() - .get_item(AuthRpoFalcon512Acl::config_slot()) + .get_item(AuthFalcon512RpoAcl::config_slot()) .expect("config storage slot access failed"); // Config Slot should be [num_trigger_procs, allow_unauthorized_output_notes, // allow_unauthorized_input_notes, 0] With 2 procedures, diff --git a/crates/miden-testing/tests/wallet/mod.rs b/crates/miden-testing/tests/wallet/mod.rs index 28ba42f95c..12386a14e3 100644 --- a/crates/miden-testing/tests/wallet/mod.rs +++ b/crates/miden-testing/tests/wallet/mod.rs @@ -9,7 +9,7 @@ use rand_chacha::rand_core::SeedableRng; #[test] fn wallet_creation() { use miden_protocol::account::{AccountCode, AccountStorageMode, AccountType}; - use miden_standards::account::auth::AuthRpoFalcon512; + use miden_standards::account::auth::AuthFalcon512Rpo; use miden_standards::account::wallets::BasicWallet; // we need a Falcon Public Key to create the wallet account @@ -18,7 +18,7 @@ fn wallet_creation() { let sec_key = AuthSecretKey::new_falcon512_rpo_with_rng(&mut rng); let pub_key = sec_key.public_key().to_commitment(); - let auth_scheme: AuthScheme = AuthScheme::RpoFalcon512 { pub_key }; + let auth_scheme: AuthScheme = AuthScheme::Falcon512Rpo { pub_key }; // we need to use an initial seed to create the wallet account let init_seed: [u8; 32] = [ @@ -32,7 +32,7 @@ fn wallet_creation() { let wallet = create_basic_wallet(init_seed, auth_scheme, account_type, storage_mode).unwrap(); let expected_code = AccountCode::from_components( - &[AuthRpoFalcon512::new(pub_key).into(), BasicWallet.into()], + &[AuthFalcon512Rpo::new(pub_key).into(), BasicWallet.into()], AccountType::RegularAccountUpdatableCode, ) .unwrap(); @@ -41,7 +41,7 @@ fn wallet_creation() { assert!(wallet.is_regular_account()); assert_eq!(wallet.code().commitment(), expected_code_commitment); assert_eq!( - wallet.storage().get_item(AuthRpoFalcon512::public_key_slot()).unwrap(), + wallet.storage().get_item(AuthFalcon512Rpo::public_key_slot()).unwrap(), Word::from(pub_key) ); } diff --git a/docs/src/account/components.md b/docs/src/account/components.md index ca88a272ac..300524086f 100644 --- a/docs/src/account/components.md +++ b/docs/src/account/components.md @@ -7,7 +7,7 @@ title: "Components" Account components are reusable units of functionality that define a part of an account's code and storage. Multiple account components can be merged together to form an account's final [code](./code) and [storage](./storage). -As an example, consider a typical wallet account, capable of holding a user's assets and requiring authentication whenever assets are added or removed. Such an account can be created by merging a `BasicWallet` component with an `RpoFalcon512` authentication component. The basic wallet does not need any storage, but contains the code to move assets in and out of the account vault. The authentication component holds a user's public key in storage and additionally contains the code to verify a signature against that public key. Together, these components form a fully functional wallet account. +As an example, consider a typical wallet account, capable of holding a user's assets and requiring authentication whenever assets are added or removed. Such an account can be created by merging a `BasicWallet` component with an `Falcon512Rpo` authentication component. The basic wallet does not need any storage, but contains the code to move assets in and out of the account vault. The authentication component holds a user's public key in storage and additionally contains the code to verify a signature against that public key. Together, these components form a fully functional wallet account. ## Account Component schemas @@ -55,7 +55,7 @@ type = [ [[storage.slots]] name = "demo::owner_public_key" description = "This is a typed value supplied at instantiation and interpreted as a Falcon public key" -type = "miden::standards::auth::rpo_falcon512::pub_key" +type = "miden::standards::auth::falcon512_rpo::pub_key" [[storage.slots]] name = "demo::protocol_version" @@ -113,7 +113,7 @@ shape of the `type` field. ##### Word types -Simple schemas accept `word` (default) and word-shaped types such as `miden::standards::auth::rpo_falcon512::pub_key` or `miden::standards::auth::ecdsa_k256_keccak::pub_key` (parsed from hexadecimal strings). +Simple schemas accept `word` (default) and word-shaped types such as `miden::standards::auth::falcon512_rpo::pub_key` or `miden::standards::auth::ecdsa_k256_keccak::pub_key` (parsed from hexadecimal strings). Simple schemas can also use any felt type (e.g. `u8`, `u16`, `u32`, `felt`, `miden::standards::fungible_faucets::metadata::token_symbol`, `void`). The value is parsed as a felt and stored as a word with the parsed felt in the last element and the remaining elements set to `0`. @@ -149,7 +149,7 @@ Single-slot entries are represented by `ValueSlotSchema` and occupy one slot (on - an array of 4 felt schema descriptors (composite slot schema). - `default-value` (optional): An overridable default for simple slots. If omitted, the slot is required at instantiation (unless `type = "void"`). -In our TOML example, the first entry defines a composite schema, while the second is an init-supplied value typed as `miden::standards::auth::rpo_falcon512::pub_key`. +In our TOML example, the first entry defines a composite schema, while the second is an init-supplied value typed as `miden::standards::auth::falcon512_rpo::pub_key`. ##### Storage map slots @@ -173,10 +173,10 @@ You can type maps at the slot level via `type.key` and `type.value` (each a `Wor ```toml [[storage.slots]] name = "demo::typed_map" -type = { key = "word", value = "miden::standards::auth::rpo_falcon512::pub_key" } +type = { key = "word", value = "miden::standards::auth::falcon512_rpo::pub_key" } ``` -This declares that all keys are `word` and all values are `miden::standards::auth::rpo_falcon512::pub_key`, regardless of whether the map contents come from `default-values = [...]` (static) or are supplied at instantiation via `InitStorageData`. +This declares that all keys are `word` and all values are `miden::standards::auth::falcon512_rpo::pub_key`, regardless of whether the map contents come from `default-values = [...]` (static) or are supplied at instantiation via `InitStorageData`. `type.key` / `type.value` are validated when building map entries from `InitStorageData` (and when validating `default-values`). diff --git a/docs/src/transaction.md b/docs/src/transaction.md index feffa9b98c..c4c9477cf5 100644 --- a/docs/src/transaction.md +++ b/docs/src/transaction.md @@ -66,7 +66,7 @@ To illustrate the `Transaction` protocol, we provide two examples for a basic `T Let's assume account A wants to create a P2ID note. P2ID notes are pay-to-ID notes that can only be consumed by a specified target account ID. Note creators can provide the target account ID using the [note inputs](note#inputs). -In this example, account A uses the basic wallet and the authentication component provided by `miden-standards`. The basic wallet component defines the methods `wallets::basic::create_note` and `wallets::basic::move_asset_to_note` to create notes with assets, and `wallets::basic::receive_asset` to receive assets. The authentication component exposes `auth::basic::auth_tx_rpo_falcon512` which allows for signing a transaction. Some account methods like `active_account::get_id` are always exposed. +In this example, account A uses the basic wallet and the authentication component provided by `miden-standards`. The basic wallet component defines the methods `wallets::basic::create_note` and `wallets::basic::move_asset_to_note` to create notes with assets, and `wallets::basic::receive_asset` to receive assets. The authentication component exposes `auth::basic::auth_tx_falcon512_rpo` which allows for signing a transaction. Some account methods like `active_account::get_id` are always exposed. The executor inputs to the Miden VM a `Transaction` script in which it places on the stack the data (tag, aux, note_type, execution_hint, RECIPIENT) of the note(s) that it wants to create using `wallets::basic::create_note` during the said `Transaction`. The [`NoteRecipient`](https://github.com/0xMiden/miden-base/blob/main/crates/miden-protocol/src/note/recipient.rs) is a value that describes under which condition a note can be consumed and is built using a `serial_number`, the `note_script` (in this case P2ID script) and the `note_inputs`. The Miden VM will execute the `Transaction` script and create the note(s). After having been created, the executor can use `wallets::basic::move_asset_to_note` to move assets from the account's vault to the notes vault. @@ -84,7 +84,7 @@ Then the P2ID note script is being executed. The script starts by reading the no If the check passes, the note script pushes the assets it holds into the account's vault. For every asset the note contains, the script calls the `wallets::basic::receive_asset` method exposed by the account's wallet component. The `wallets::basic::receive_asset` procedure calls `native_account::add_asset`, which cannot be called from the note itself. This allows accounts to control what functionality to expose, e.g. whether the account supports receiving assets or not, and the note cannot bypass that. -After the assets are stored in the account's vault, the transaction script is being executed. The script calls `auth::basic::auth_tx_rpo_falcon512` which is explicitly exposed in the account interface. The method is used to verify a provided signature against a public key stored in the account's storage and a commitment to this specific transaction. If the signature can be verified, the method increments the nonce. +After the assets are stored in the account's vault, the transaction script is being executed. The script calls `auth::basic::auth_tx_falcon512_rpo` which is explicitly exposed in the account interface. The method is used to verify a provided signature against a public key stored in the account's storage and a commitment to this specific transaction. If the signature can be verified, the method increments the nonce. The Epilogue finalizes the transaction by computing the final account hash, asserting the nonce increment and checking that no assets were created or destroyed in the transaction — that means the net sum of all assets must stay the same. From b7a383317d4b0531141c7b06a0cfd6106541ed5f Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 16 Jan 2026 17:16:53 -0800 Subject: [PATCH 113/114] chore: update changelog --- CHANGELOG.md | 26 ++- Cargo.lock | 478 +++++++++++++++++++-------------------------------- 2 files changed, 185 insertions(+), 319 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acec386cb6..12acf26f7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,20 @@ # Changelog -## 0.13.0 (TBD) +## 0.13.0 (2026-01-16) ### Features - [BREAKING] Refactored storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2160](https://github.com/0xMiden/miden-base/pull/2160), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)). - [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)). -- Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). - Add `AccountId::parse()` helper function to parse both hex and bech32 formats ([#2223](https://github.com/0xMiden/miden-base/pull/2223)). -- Added `AccountSchemaCommitment` component to expose account storage schema commitments ([#2253](https://github.com/0xMiden/miden-base/pull/2253)). - Add `read_foreign_account_inputs()`, `read_vault_asset_witnesses()`, and `read_storage_map_witness()` for `TransactionInputs` ([#2246](https://github.com/0xMiden/miden-base/pull/2246)). -- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249)). -- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252)). -- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260)). -- [BREAKING] Introduce `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260), [#2268](https://github.com/0xMiden/miden-base/pull/2268), [#2279](https://github.com/0xMiden/miden-base/pull/2279)). -- Introduce standard `NetworkAccountTarget` attachment for use in network transactions which replaces `NoteTag::NetworkAccount` ([#2257](https://github.com/0xMiden/miden-base/pull/2257)). +- [BREAKING] Introduced `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260), [#2268](https://github.com/0xMiden/miden-base/pull/2268), [#2279](https://github.com/0xMiden/miden-base/pull/2279)). +- Added `AccountSchemaCommitment` component to expose account storage schema commitments ([#2253](https://github.com/0xMiden/miden-base/pull/2253)). +- Introduced standard `NetworkAccountTarget` attachment for use in network transactions which replaces `NoteTag::NetworkAccount` ([#2257](https://github.com/0xMiden/miden-base/pull/2257)). - Added `miden::standards::access::ownable` standard module for component ownership management, and integrated it into the `network_fungible` faucet (including new tests). ([#2228](https://github.com/0xMiden/miden-base/pull/2228)). ### Changes -- No longer pad the note inputs on insertion into advice map ([#2232](https://github.com/0xMiden/miden-base/pull/2232)). - Added proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). @@ -29,24 +24,25 @@ - [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)). - [BREAKING] Added public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)). - [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)). -- Create `NullifierLeafValue` newtype wrapper ([#2136](https://github.com/0xMiden/miden-base/pull/2136)). +- Created `NullifierLeafValue` newtype wrapper ([#2136](https://github.com/0xMiden/miden-base/pull/2136)). - [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)). - Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)). - Added `TokenSymbol::from_static_str` const function for compile-time token symbol validation ([#2148](https://github.com/0xMiden/miden-base/pull/2148)). +- [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). -- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197), [#2255](https://github.com/0xMiden/miden-base/pull/2255)). -- [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). +- [BREAKING] Refactored `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197), [#2255](https://github.com/0xMiden/miden-base/pull/2255)). +- Added `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Removed OLD_MAP_ROOT from being returned when calling [`native_account::set_map_item`](crates/miden-lib/asm/miden/native_account.masm) ([#2194](https://github.com/0xMiden/miden-base/pull/2194)). - [BREAKING] Refactored account component templates into `StorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). -- [BREAKING] Refactored `InitStorageData` to support native types ([#2230](https://github.com/0xMiden/miden-base/pull/2230)). -- Added `StorageSchema::commitment()` ([#2244](https://github.com/0xMiden/miden-base/pull/2244)). - [BREAKING] Refactored account component templates into `AccountStorageSchema` ([#2193](https://github.com/0xMiden/miden-base/pull/2193)). -- [BREAKING] `RpoFalcon512` was renamed to `Falcon512Rpo` everywhere, including procedure and file names ([#2264](https://github.com/0xMiden/miden-base/pull/2264)). - [BREAKING] Refactor note tags to be arbitrary `u32` values and drop previous validation ([#2219](https://github.com/0xMiden/miden-base/pull/2219)). - [BREAKING] Refactored `InitStorageData` to support native types ([#2230](https://github.com/0xMiden/miden-base/pull/2230)). +- Refactored to no longer pad the note inputs on insertion into advice map ([#2232](https://github.com/0xMiden/miden-base/pull/2232)). +- Added `StorageSchema::commitment()` ([#2244](https://github.com/0xMiden/miden-base/pull/2244)). +- [BREAKING] `RpoFalcon512` was renamed to `Falcon512Rpo` everywhere, including procedure and file names ([#2264](https://github.com/0xMiden/miden-base/pull/2264)). - [BREAKING] Removed top-level error exports from `miden-protocol` crate (the are still accessible under `miden_protocol::errors`). ## 0.12.4 (2025-11-26) diff --git a/Cargo.lock b/Cargo.lock index 1ec7360d0a..f13f1556f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,28 +156,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "autocfg" version = "1.5.0" @@ -216,9 +194,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64ct" -version = "1.8.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bech32" @@ -285,15 +263,16 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "blake3" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "cpufeatures", ] [[package]] @@ -307,9 +286,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytemuck" @@ -323,12 +302,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "bytes" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" - [[package]] name = "cast" version = "0.3.0" @@ -337,9 +310,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.49" +version = "1.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" dependencies = [ "find-msvc-tools", "jobserver", @@ -417,18 +390,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstyle", "clap_lex", @@ -436,9 +409,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "color-eyre" @@ -481,9 +454,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "cpufeatures" @@ -632,7 +605,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -656,23 +629,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -806,7 +779,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -859,9 +832,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "findshlibs" @@ -910,9 +883,9 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "fs-err" -version = "3.2.0" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62d91fd049c123429b018c47887d3f75a265540dd3c30ba9cb7bae9197edb03a" +checksum = "baf68cef89750956493a66a10f512b9e58d9db21f2a573c079c0bdf1207a54a7" dependencies = [ "autocfg", ] @@ -961,7 +934,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -999,16 +972,17 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" dependencies = [ "cc", "cfg-if", "libc", "log", "rustversion", - "windows", + "windows-link", + "windows-result", ] [[package]] @@ -1024,9 +998,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -1135,9 +1109,9 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown", @@ -1222,15 +1196,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" dependencies = [ "jiff-static", "log", @@ -1241,13 +1215,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -1262,9 +1236,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -1331,9 +1305,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.178" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libm" @@ -1423,9 +1397,9 @@ dependencies = [ [[package]] name = "miden-air" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e663337017ed028dff8c18a0ce1db64aad0e850996e3214f137f98317533c2e1" +checksum = "3d819876b9e9b630e63152400e6df2a201668a9bdfd33d54d6806b9d7b992ff8" dependencies = [ "miden-core", "miden-utils-indexing", @@ -1436,9 +1410,9 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001249195c227624695529c82ebf51c390ec1c28e99a567549ce3a272a2aedf3" +checksum = "24c6a18e29c03141cf9044604390a00691c7342924ec865b4acfdd560ff41ede" dependencies = [ "env_logger", "log", @@ -1451,9 +1425,9 @@ dependencies = [ [[package]] name = "miden-assembly-syntax" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1963cfa667aa6a157c99982df340a7bd42b054652e6f33d5e3513217531eca73" +checksum = "7458ff670f5a514bf972aa84d6e1851a4c4e9afa351f53b71bdc2218b99254b6" dependencies = [ "aho-corasick", "env_logger", @@ -1483,9 +1457,9 @@ dependencies = [ [[package]] name = "miden-core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136debf5474190dc584df3252710dac07a0e45315740c9538a7fc0b72c596365" +checksum = "21a5c9c8c3d42ae8381ed49e47ff9ad2d2e345c4726761be36b7d4000ebb40ae" dependencies = [ "derive_more", "itertools 0.14.0", @@ -1505,9 +1479,9 @@ dependencies = [ [[package]] name = "miden-core-lib" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcec9fb9a256d2fae347162d9a94653a1790dd33b4af73ad29686475b63deb34" +checksum = "6556494ea5576803730fa15015bee6bd9d1a117450f22e7df0883421e7423674" dependencies = [ "env_logger", "fs-err", @@ -1541,7 +1515,7 @@ dependencies = [ "num-complex", "rand", "rand_chacha", - "rand_core 0.9.3", + "rand_core 0.9.5", "rand_hc", "rayon", "sha2", @@ -1561,14 +1535,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83479e7af490784c6f2d2e02cec5210fd6e5bc6ce3d4427734e36a773bca72d2" dependencies = [ "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "miden-debug-types" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dc25083822c3d582c42ad10aeee0138dec15a130f3017b05495bb91e31fde4a" +checksum = "19123e896f24b575e69921a79a39a0a4babeb98404a8601017feb13b75d653b3" dependencies = [ "memchr", "miden-crypto", @@ -1593,9 +1567,9 @@ dependencies = [ [[package]] name = "miden-mast-package" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da35f2fc1eacbfd0b6b995e888c2b778bd646acebf34dab27f9f7ed9b3effaa2" +checksum = "f0d6a322b91efa1bb71e224395ca1fb9ca00e2614f89427e35d8c42a903868a3" dependencies = [ "derive_more", "miden-assembly-syntax", @@ -1626,7 +1600,7 @@ dependencies = [ "supports-color", "supports-hyperlinks", "supports-unicode", - "syn 2.0.111", + "syn 2.0.114", "terminal_size", "textwrap", "thiserror", @@ -1642,14 +1616,14 @@ checksum = "86a905f3ea65634dd4d1041a4f0fd0a3e77aa4118341d265af1a94339182222f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "miden-processor" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb298dbdda739080497c18eace4d56c58f3e8d257676c9b2f407be441131ecd" +checksum = "4a659fac55de14647e2695f03d96b83ff94fe65fd31e74d81c225ec52af25acf" dependencies = [ "itertools 0.14.0", "miden-air", @@ -1711,14 +1685,14 @@ dependencies = [ "miden-protocol", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "miden-prover" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8506c8eb4d980134c0145887af50bd4631df4010eb23d6e454764cb1ee28836c" +checksum = "4e5df61f50f27886f6f777d6e0cdf785f7db87dd881799a84a801e7330c189c8" dependencies = [ "miden-air", "miden-debug-types", @@ -1801,9 +1775,9 @@ dependencies = [ [[package]] name = "miden-utils-core-derive" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0807840c07a4491a292153258cfae27914333e1a7240777a77c22d8ca3b55873" +checksum = "aa207ffd8b26a79d9b5b246a352812f0015c0bb8f75492ec089c5c8e6d5f9e2b" dependencies = [ "proc-macro2", "quote", @@ -1812,9 +1786,9 @@ dependencies = [ [[package]] name = "miden-utils-diagnostics" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b28b1b29e300b471b0f1cbc286997a1326c900814a73b0b28338d5926ce192c" +checksum = "6b2f55477d410542a5d8990ca04856adf5bef91bfa3b54ca3c03a5ff14a6e25c" dependencies = [ "miden-crypto", "miden-debug-types", @@ -1825,18 +1799,18 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bd0c1966de07d48a4ed0b2821466919c061f4866296be87afc56970a49716a" +checksum = "f39efae17e14ec8f8a1266cffd29eb7a08ac837143cd09223b1af361bbb55730" dependencies = [ "thiserror", ] [[package]] name = "miden-utils-sync" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fa7e37db2fbf2dee6ba6e411b3570ef48d52ec780b9c8125623f9ddca30da3" +checksum = "da7fa8f5fd27f122c83f55752f2a964bbfc2b713de419e9c152f7dcc05c194ec" dependencies = [ "lock_api", "loom", @@ -1845,9 +1819,9 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383c934eed92f89be4c1e3dbc97ccf37b48433a0b33727c92a5abbfa2d45f420" +checksum = "fbddac2e76486fb657929338323c68b9e7f40e33b8cfb593d0fb5bf637db046e" dependencies = [ "miden-air", "miden-core", @@ -1882,7 +1856,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] @@ -1952,7 +1926,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -2071,7 +2045,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -2162,9 +2136,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "portable-atomic-util" @@ -2234,9 +2208,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -2264,7 +2238,7 @@ checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -2278,9 +2252,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -2298,7 +2272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2308,7 +2282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2317,14 +2291,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] @@ -2344,7 +2318,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2353,7 +2327,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2464,15 +2438,15 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.111", + "syn 2.0.114", "unicode-ident", ] [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc_version" @@ -2507,9 +2481,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags 2.10.0", "errno", @@ -2524,12 +2498,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -2617,28 +2585,28 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "indexmap", "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -2797,9 +2765,9 @@ dependencies = [ [[package]] name = "supports-hyperlinks" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" +checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91" [[package]] name = "supports-unicode" @@ -2809,9 +2777,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "symbolic-common" -version = "12.17.0" +version = "12.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d8046c5674ab857104bc4559d505f4809b8060d57806e45d49737c97afeb60" +checksum = "520cf51c674f8b93d533f80832babe413214bb766b6d7cb74ee99ad2971f8467" dependencies = [ "debugid", "memmap2", @@ -2821,9 +2789,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.17.0" +version = "12.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1accb6e5c4b0f682de907623912e616b44be1c9e725775155546669dbff720ec" +checksum = "9f0de2ee0ffa2641e17ba715ad51d48b9259778176517979cb38b6aa86fa7425" dependencies = [ "rustc-demangle", "symbolic-common", @@ -2842,9 +2810,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -2859,14 +2827,14 @@ checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", - "rustix 1.1.2", + "rustix 1.1.3", "windows-sys 0.61.2", ] @@ -2926,7 +2894,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -2950,9 +2918,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "pin-project-lite", "tokio-macros", @@ -2966,14 +2934,14 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "tokio-stream" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -2982,12 +2950,10 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +checksum = "3f6d24790a10a7af737693a3e8f1d03faef7e6ca0cc99aae5066f533766de545" dependencies = [ - "async-stream", - "bytes", "futures-core", "tokio", "tokio-stream", @@ -2995,9 +2961,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "indexmap", "serde_core", @@ -3010,18 +2976,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.9" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", @@ -3031,24 +2997,24 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tracing" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3063,14 +3029,14 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -3261,18 +3227,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -3283,9 +3249,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3293,31 +3259,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -3354,112 +3320,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core", - "windows-link 0.1.3", - "windows-threading", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core", - "windows-link 0.1.3", -] - [[package]] name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -3486,7 +3359,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3520,15 +3393,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3680,7 +3544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d31a19dae58475d019850e25b0170e94b16d382fbf6afee9c0e80fdc935e73e" dependencies = [ "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -3743,9 +3607,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "x25519-dalek" @@ -3759,22 +3623,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -3782,3 +3646,9 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" From b995e161d811fca474b56285a8e313ccfcce8874 Mon Sep 17 00:00:00 2001 From: igamigo Date: Fri, 16 Jan 2026 22:44:22 -0300 Subject: [PATCH 114/114] feat: add `AccountComponentMetadata` to `AccountComponent` (#2269) --- CHANGELOG.md | 1 + .../miden-protocol/src/account/builder/mod.rs | 11 ++++++++++ .../src/account/component/mod.rs | 21 ++++++++++++++++++- .../src/account/metadata/mod.rs | 2 -- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12acf26f7a..d5aa4d6d49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - [BREAKING] Introduced `NoteAttachment` as part of `NoteMetadata` and remove `aux` and `execution_hint` ([#2249](https://github.com/0xMiden/miden-base/pull/2249), [#2252](https://github.com/0xMiden/miden-base/pull/2252), [#2260](https://github.com/0xMiden/miden-base/pull/2260), [#2268](https://github.com/0xMiden/miden-base/pull/2268), [#2279](https://github.com/0xMiden/miden-base/pull/2279)). - Added `AccountSchemaCommitment` component to expose account storage schema commitments ([#2253](https://github.com/0xMiden/miden-base/pull/2253)). - Introduced standard `NetworkAccountTarget` attachment for use in network transactions which replaces `NoteTag::NetworkAccount` ([#2257](https://github.com/0xMiden/miden-base/pull/2257)). +- Added an `AccountBuilder` extension trait to help build the schema commitment; added `AccountComponentMetadata` to `AccountComponent` ([#2269](https://github.com/0xMiden/miden-base/pull/2269)). - Added `miden::standards::access::ownable` standard module for component ownership management, and integrated it into the `network_fungible` faucet (including new tests). ([#2228](https://github.com/0xMiden/miden-base/pull/2228)). ### Changes diff --git a/crates/miden-protocol/src/account/builder/mod.rs b/crates/miden-protocol/src/account/builder/mod.rs index 6e4275b67c..478a2013d3 100644 --- a/crates/miden-protocol/src/account/builder/mod.rs +++ b/crates/miden-protocol/src/account/builder/mod.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use miden_core::FieldElement; +use crate::account::component::StorageSchema; use crate::account::{ Account, AccountCode, @@ -127,6 +128,16 @@ impl AccountBuilder { self } + /// Returns an iterator of storage schemas attached to the builder's components, if any. + /// + /// Components constructed without metadata will not contribute a schema. + pub fn storage_schemas(&self) -> impl Iterator + '_ { + self.auth_component + .iter() + .chain(self.components.iter()) + .filter_map(|component| component.storage_schema()) + } + /// Builds the common parts of testing and non-testing code. fn build_inner(&mut self) -> Result<(AssetVault, AccountCode, AccountStorage), AccountError> { #[cfg(any(feature = "testing", test))] diff --git a/crates/miden-protocol/src/account/component/mod.rs b/crates/miden-protocol/src/account/component/mod.rs index 9245e93f11..b77d2c2e0e 100644 --- a/crates/miden-protocol/src/account/component/mod.rs +++ b/crates/miden-protocol/src/account/component/mod.rs @@ -39,6 +39,7 @@ use crate::{MastForest, Word}; pub struct AccountComponent { pub(super) code: AccountComponentCode, pub(super) storage_slots: Vec, + pub(super) metadata: Option, pub(super) supported_types: BTreeSet, } @@ -71,6 +72,7 @@ impl AccountComponent { Ok(Self { code: code.into(), storage_slots, + metadata: None, supported_types: BTreeSet::new(), }) } @@ -146,7 +148,7 @@ impl AccountComponent { })?; Ok(AccountComponent::new(library.clone(), storage_slots)? - .with_supported_types(account_component_metadata.supported_types().clone())) + .with_metadata(account_component_metadata.clone())) } // ACCESSORS @@ -173,6 +175,16 @@ impl AccountComponent { self.storage_slots.as_slice() } + /// Returns the component metadata, if any. + pub fn metadata(&self) -> Option<&AccountComponentMetadata> { + self.metadata.as_ref() + } + + /// Returns the storage schema associated with this component, if any. + pub fn storage_schema(&self) -> Option<&StorageSchema> { + self.metadata.as_ref().map(AccountComponentMetadata::storage_schema) + } + /// Returns a reference to the supported [`AccountType`]s. pub fn supported_types(&self) -> &BTreeSet { &self.supported_types @@ -222,6 +234,13 @@ impl AccountComponent { self } + /// Attaches metadata to this component for downstream schema commitments and introspection. + pub fn with_metadata(mut self, metadata: AccountComponentMetadata) -> Self { + self.supported_types = metadata.supported_types().clone(); + self.metadata = Some(metadata); + self + } + /// Sets the [`AccountType`]s supported by this component to all account types. pub fn with_supports_all_types(mut self) -> Self { self.supported_types.extend([ diff --git a/crates/miden-standards/src/account/metadata/mod.rs b/crates/miden-standards/src/account/metadata/mod.rs index 8ab43e44ae..0d5e24a3b5 100644 --- a/crates/miden-standards/src/account/metadata/mod.rs +++ b/crates/miden-standards/src/account/metadata/mod.rs @@ -91,8 +91,6 @@ fn compute_schema_commitment( merged_slots.insert(slot_name.clone(), slot_schema.clone()); }, // Slot exists, check if the schema is the same before erroring - // TODO: If we wanted to not error, we would have to decide on a winning schema - // for the StorageSlotName Some(existing) => { if existing != slot_schema { return Err(AccountComponentTemplateError::InvalidSchema(format!(