From ce0ea05e14ed463f29de26fe30f827fbf2081524 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Fri, 7 Aug 2026 17:20:08 +0300 Subject: [PATCH 1/3] feat(standards): read Authority procedure roles without requiring a RoleSymbol --- CHANGELOG.md | 2 + .../src/account/access/authority.rs | 125 +++++++++++++++++- .../miden-standards/src/account/access/mod.rs | 2 +- 3 files changed, 122 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eeeb3e4cd..783a492b60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Features +- Added `Authority::read_procedure_roles`, which reads the per-procedure role map even when a role felt carries no `RoleSymbol` ([#TBD](https://github.com/0xMiden/protocol)). + ### Changes - Documented the RBAC freeze-only actor pattern on `Authority` and added test coverage pinning that a `FREEZER` can trip the emergency switch but can never unfreeze the account ([#3520](https://github.com/0xMiden/protocol/pull/3520)). diff --git a/crates/miden-standards/src/account/access/authority.rs b/crates/miden-standards/src/account/access/authority.rs index ae55608a8e..2481fd92fe 100644 --- a/crates/miden-standards/src/account/access/authority.rs +++ b/crates/miden-standards/src/account/access/authority.rs @@ -71,6 +71,41 @@ const OWNER_CONTROLLED: u8 = 1; /// Authority value written to the storage slot for [`Authority::RbacControlled`]. const RBAC_CONTROLLED: u8 = 2; +// PROCEDURE ROLE +// ================================================================================================ + +/// The role required by an authority-gated procedure, as read from the procedure-roles map. +/// +/// A role is identified on-chain by a field element: `rbac::assert_sender_has_role` matches that +/// felt against the RBAC membership map without ever decoding it. A [`RoleSymbol`] is the printable +/// name the felt decodes to, which exists only for canonically encoded values, so +/// [`to_symbol`][Self::to_symbol] returns `None` for a role that carries no name. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ProcedureRole(Felt); + +impl ProcedureRole { + /// Returns the role identified by `felt`. + pub fn new(felt: Felt) -> Self { + Self(felt) + } + + /// Returns the felt identifying the role, the form on-chain authorization matches on. + pub fn as_felt(&self) -> Felt { + self.0 + } + + /// Returns the role's symbol, or `None` if its felt is not a valid [`RoleSymbol`] encoding. + pub fn to_symbol(&self) -> Option { + RoleSymbol::try_from(self.0).ok() + } +} + +impl From<&RoleSymbol> for ProcedureRole { + fn from(role: &RoleSymbol) -> Self { + Self(role.as_element()) + } +} + // AUTHORITY // ================================================================================================ @@ -253,6 +288,22 @@ impl Authority { Ok(word[1] != Felt::ZERO) } + /// Reads the per-procedure role map without requiring every role to carry a decodable symbol. + /// + /// # Errors + /// + /// Returns an error if: + /// - the procedure-roles storage slot is missing or is not a map. + /// - a role's value word carries data in its reserved felts. + pub fn read_procedure_roles( + storage: &AccountStorage, + ) -> Result, AuthorityError> { + Ok(Self::read_role_felts_from_storage(storage)? + .into_iter() + .map(|(proc_root, role_felt)| (proc_root, ProcedureRole::new(role_felt))) + .collect()) + } + /// Returns the [`AccountComponentMetadata`] for this configuration. pub fn component_metadata(&self) -> AccountComponentMetadata { let mut slots = vec![( @@ -323,10 +374,25 @@ impl Authority { Ok(word) } - /// Reconstructs the per-procedure role map from the procedure-roles storage slot. + /// Reconstructs the per-procedure role map from the procedure-roles storage slot, requiring + /// every role felt to decode into a [`RoleSymbol`]. fn read_roles_from_storage( storage: &AccountStorage, ) -> Result, AuthorityError> { + Self::read_role_felts_from_storage(storage)? + .into_iter() + .map(|(proc_root, role_felt)| { + let role = + RoleSymbol::try_from(role_felt).map_err(AuthorityError::InvalidRoleSymbol)?; + Ok((proc_root, role)) + }) + .collect() + } + + /// Reads the procedure-roles storage slot into the raw role felt of every entry. + fn read_role_felts_from_storage( + storage: &AccountStorage, + ) -> Result, AuthorityError> { let slot = storage .slots() .iter() @@ -337,18 +403,16 @@ impl Authority { return Err(AuthorityError::MissingProcedureRolesSlot); }; - let mut roles = BTreeMap::new(); + let mut role_felts = BTreeMap::new(); for (key, value) in map.entries() { // Enforce the canonical encoding on read: the reserved felts must be zero. if value[1..4].iter().any(|v| *v != Felt::ZERO) { return Err(AuthorityError::NonCanonicalConfig); } - let proc_root = AccountProcedureRoot::from_raw(key.as_word()); - let role = RoleSymbol::try_from(value[0]).map_err(AuthorityError::InvalidRoleSymbol)?; - roles.insert(proc_root, role); + role_felts.insert(AccountProcedureRoot::from_raw(key.as_word()), value[0]); } - Ok(roles) + Ok(role_felts) } } @@ -507,5 +571,54 @@ mod tests { Authority::try_from_storage(&storage), Err(AuthorityError::NonCanonicalConfig) ); + assert_matches!( + Authority::read_procedure_roles(&storage), + Err(AuthorityError::NonCanonicalConfig) + ); + } + + /// On-chain a role is just a felt, so an account built outside `RbacControlled` can hold one + /// that decodes to no symbol. + #[test] + fn role_felt_without_a_symbol_is_read_without_one() { + // 27 is the smallest non-zero felt whose base-27 length digit is 0, so it encodes no + // symbol. + let unnamed_role_felt = Felt::from(27u8); + let expected_root = AccountProcedureRoot::from_raw(Word::from(ROLE_KEY_WORD)); + let storage = rbac_storage_with_role_value(Word::new([ + unnamed_role_felt, + Felt::ZERO, + Felt::ZERO, + Felt::ZERO, + ])); + + // Reconstructing the typed value still fails: `RbacControlled` holds `RoleSymbol`s. + assert_matches!( + Authority::try_from_storage(&storage), + Err(AuthorityError::InvalidRoleSymbol(_)) + ); + + // Reading the map succeeds and preserves the felt on-chain authorization matches on. + let roles = Authority::read_procedure_roles(&storage).unwrap(); + let role = roles.get(&expected_root).expect("the entry should be read"); + assert_eq!(role.as_felt(), unnamed_role_felt); + assert_eq!(role.to_symbol(), None); + } + + /// A canonical role felt is read with its symbol, which is the `RoleSymbol` that wrote it. + #[test] + fn role_felt_with_a_symbol_is_read_with_it() { + let role = RoleSymbol::new("ADMIN").unwrap(); + let expected_root = AccountProcedureRoot::from_raw(Word::from(ROLE_KEY_WORD)); + let storage = rbac_storage_with_role_value(Word::new([ + role.as_element(), + Felt::ZERO, + Felt::ZERO, + Felt::ZERO, + ])); + + let roles = Authority::read_procedure_roles(&storage).unwrap(); + assert_eq!(roles.get(&expected_root), Some(&ProcedureRole::from(&role))); + assert_eq!(roles[&expected_root].to_symbol(), Some(role)); } } diff --git a/crates/miden-standards/src/account/access/mod.rs b/crates/miden-standards/src/account/access/mod.rs index 14b13b67f1..e340804ad2 100644 --- a/crates/miden-standards/src/account/access/mod.rs +++ b/crates/miden-standards/src/account/access/mod.rs @@ -90,7 +90,7 @@ impl IntoIterator for AccessControl { } } -pub use authority::{Authority, AuthorityError}; +pub use authority::{Authority, AuthorityError, ProcedureRole}; pub use ownable2step::{Ownable2Step, Ownable2StepError}; pub use pausable::{Pausable, PausableManager, PausableStorage}; pub use rbac::{RoleBasedAccessControl, RoleBasedAccessControlError, RoleConfig}; From 14354f7c67fe3a852b0f1a84344a67d4a6b79d6b Mon Sep 17 00:00:00 2001 From: onurinanc Date: Fri, 7 Aug 2026 17:24:05 +0300 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 783a492b60..3e1796492f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- Added `Authority::read_procedure_roles`, which reads the per-procedure role map even when a role felt carries no `RoleSymbol` ([#TBD](https://github.com/0xMiden/protocol)). +- Added `Authority::read_procedure_roles`, which reads the per-procedure role map even when a role felt carries no `RoleSymbol` ([#3524](https://github.com/0xMiden/protocol/pull/3524)). ### Changes From 5a455af2bf68ed4f3218d341bd3ea44b2f4a2967 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 10 Aug 2026 12:10:34 +0300 Subject: [PATCH 3/3] add canonical encoding in RBAC --- CHANGELOG.md | 4 +- bin/bench-transaction/bench-tx.json | 658 +++++++++--------- crates/miden-agglayer/src/costs/table.rs | 16 +- .../asm/standards/access/mod.masm | 1 + .../asm/standards/access/rbac.masm | 31 +- .../asm/standards/access/role_symbol.masm | 147 ++++ .../src/account/access/authority.rs | 125 +--- .../miden-standards/src/account/access/mod.rs | 2 +- .../src/account/access/rbac.rs | 5 + .../miden-standards/src/note/costs/table.rs | 46 +- .../miden-testing/tests/scripts/rbac/mod.rs | 88 +++ 11 files changed, 621 insertions(+), 502 deletions(-) create mode 100644 crates/miden-standards/asm/standards/access/role_symbol.masm diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ca7004413..c734ae436d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,6 @@ ### Features -- Added `Authority::read_procedure_roles`, which reads the per-procedure role map even when a role felt carries no `RoleSymbol` ([#3524](https://github.com/0xMiden/protocol/pull/3524)). - ### Changes - [BREAKING] Changed asset callbacks into validation-only interfaces that return no asset value; the transaction kernel retains and uses the original value, preventing callbacks from modifying it. The kernel commitment changes ([#3505](https://github.com/0xMiden/protocol/issues/3505), [#3513](https://github.com/0xMiden/protocol/pull/3513)). @@ -15,6 +13,8 @@ ### Fixes +- [BREAKING] Added canonical enforcement for `RoleSymbol` encodings in the `RBAC` entrypoints ([#3524](https://github.com/0xMiden/protocol/pull/3524)). + ## v0.16.0 (2026-08-06) ### Features diff --git a/bin/bench-transaction/bench-tx.json b/bin/bench-transaction/bench-tx.json index e527c79111..3d176f2831 100644 --- a/bin/bench-transaction/bench-tx.json +++ b/bin/bench-transaction/bench-tx.json @@ -1,9 +1,9 @@ { "consume single P2ID note with Falcon signing": { "prologue": 3754, - "notes_processing": 2184, + "notes_processing": 2200, "note_execution": { - "0x5cbede6b9f04c8219271e3221e97adaa749f01afe9888a9924d52307ccb22b1c": 2142 + "0xfd18316dcbad03e049fb245c180bf9186e56d5275d5baddd3737836568e0a78f": 2158 }, "tx_script_processing": 42, "epilogue": { @@ -11,11 +11,11 @@ "auth_procedure": 72646 }, "trace": { - "core_rows": 79785, - "chiplets_rows": 11248, - "range_rows": 20327, + "core_rows": 79801, + "chiplets_rows": 11256, + "range_rows": 20531, "chiplets_shape": { - "hasher_rows": 8216, + "hasher_rows": 8224, "bitwise_rows": 584, "memory_rows": 2385, "kernel_rom_rows": 62, @@ -25,9 +25,9 @@ }, "consume single P2ID note with ECDSA signing": { "prologue": 3754, - "notes_processing": 2184, + "notes_processing": 2200, "note_execution": { - "0x747a592f85aaf459c7a36387a89ef937b2d88941e2b535fe49b20ae9fb1e71a4": 2142 + "0xe8db31535b900812ba8dab74054b7c883b0cc703d5a6a0dc853bddc9fdfce75a": 2158 }, "tx_script_processing": 42, "epilogue": { @@ -35,11 +35,11 @@ "auth_procedure": 5008 }, "trace": { - "core_rows": 12147, - "chiplets_rows": 5505, - "range_rows": 1541, + "core_rows": 12163, + "chiplets_rows": 5513, + "range_rows": 1561, "chiplets_shape": { - "hasher_rows": 3864, + "hasher_rows": 3872, "bitwise_rows": 840, "memory_rows": 738, "kernel_rom_rows": 62, @@ -49,10 +49,10 @@ }, "consume two P2ID notes with Falcon signing": { "prologue": 5025, - "notes_processing": 4550, + "notes_processing": 4582, "note_execution": { - "0x0ff983e6b5e8a0a04e7375b87d8e811ad51de36406f871e1d9d4e219f0f8abfb": 2357, - "0x60bba08165a03bfcf1febebf16679a0661b4d2672d9341545034f86c5c03a295": 2142 + "0x55856c817efd90a535e6245b8b9e39dc27e2dac5dd466ae5eb11257eef40c15a": 2158, + "0x8381b9aecd5eb2f220cfda02977f18c5d92b28926313900dc5e021bb89c18a2d": 2373 }, "tx_script_processing": 42, "epilogue": { @@ -60,11 +60,11 @@ "auth_procedure": 72610 }, "trace": { - "core_rows": 83350, - "chiplets_rows": 13291, - "range_rows": 20321, + "core_rows": 83382, + "chiplets_rows": 13307, + "range_rows": 20497, "chiplets_shape": { - "hasher_rows": 9768, + "hasher_rows": 9784, "bitwise_rows": 928, "memory_rows": 2532, "kernel_rom_rows": 62, @@ -74,10 +74,10 @@ }, "consume two P2ID notes with ECDSA signing": { "prologue": 5025, - "notes_processing": 4550, + "notes_processing": 4582, "note_execution": { - "0x0ff983e6b5e8a0a04e7375b87d8e811ad51de36406f871e1d9d4e219f0f8abfb": 2357, - "0x60bba08165a03bfcf1febebf16679a0661b4d2672d9341545034f86c5c03a295": 2142 + "0x55856c817efd90a535e6245b8b9e39dc27e2dac5dd466ae5eb11257eef40c15a": 2158, + "0x8381b9aecd5eb2f220cfda02977f18c5d92b28926313900dc5e021bb89c18a2d": 2373 }, "tx_script_processing": 42, "epilogue": { @@ -85,11 +85,11 @@ "auth_procedure": 4972 }, "trace": { - "core_rows": 15712, - "chiplets_rows": 7548, - "range_rows": 1477, + "core_rows": 15744, + "chiplets_rows": 7564, + "range_rows": 1495, "chiplets_shape": { - "hasher_rows": 5416, + "hasher_rows": 5432, "bitwise_rows": 1184, "memory_rows": 885, "kernel_rom_rows": 62, @@ -101,15 +101,15 @@ "prologue": 1881, "notes_processing": 35, "note_execution": {}, - "tx_script_processing": 1888, + "tx_script_processing": 1910, "epilogue": { "total": 75295, "auth_procedure": 73253 }, "trace": { - "core_rows": 79143, + "core_rows": 79165, "chiplets_rows": 10910, - "range_rows": 20199, + "range_rows": 20299, "chiplets_shape": { "hasher_rows": 8000, "bitwise_rows": 544, @@ -123,15 +123,15 @@ "prologue": 1881, "notes_processing": 35, "note_execution": {}, - "tx_script_processing": 1888, + "tx_script_processing": 1910, "epilogue": { "total": 7657, "auth_procedure": 5615 }, "trace": { - "core_rows": 11505, + "core_rows": 11527, "chiplets_rows": 5167, - "range_rows": 1347, + "range_rows": 1321, "chiplets_shape": { "hasher_rows": 3648, "bitwise_rows": 800, @@ -143,21 +143,21 @@ }, "consume CLAIM note (L1 to Miden)": { "prologue": 3957, - "notes_processing": 28614, + "notes_processing": 28630, "note_execution": { - "0x7ba705db8a8a71392d468e19b6d1abf206ca3358bba4dc2bf3dd92b5187856dc": 28572 + "0xd1113dedd28530bbfdd2e791aab2e5d77e801eb64f9e980b0949e4d9f1c00813": 28588 }, "tx_script_processing": 42, "epilogue": { - "total": 16671, - "auth_procedure": 11750 + "total": 16683, + "auth_procedure": 11762 }, "trace": { - "core_rows": 49328, - "chiplets_rows": 19484, - "range_rows": 3391, + "core_rows": 49356, + "chiplets_rows": 19508, + "range_rows": 3443, "chiplets_shape": { - "hasher_rows": 12624, + "hasher_rows": 12648, "bitwise_rows": 2752, "memory_rows": 4045, "kernel_rom_rows": 62, @@ -167,21 +167,21 @@ }, "consume CLAIM note (L2 to Miden)": { "prologue": 3957, - "notes_processing": 38776, + "notes_processing": 38792, "note_execution": { - "0x927c8823d42f4ca75bfe60aedf56d3c2dc30ed4ee9db3657a63ade6bdaecc1e0": 38734 + "0xd9f2ccf0f2312612eec27110733d927fdd4cc0c72f39c0b0667cfd775ec93eb0": 38750 }, "tx_script_processing": 42, "epilogue": { - "total": 16671, - "auth_procedure": 11750 + "total": 16683, + "auth_procedure": 11762 }, "trace": { - "core_rows": 59490, - "chiplets_rows": 22234, - "range_rows": 3589, + "core_rows": 59518, + "chiplets_rows": 22258, + "range_rows": 3599, "chiplets_shape": { - "hasher_rows": 14176, + "hasher_rows": 14200, "bitwise_rows": 3008, "memory_rows": 4987, "kernel_rom_rows": 62, @@ -191,21 +191,21 @@ }, "consume B2AGG note (bridge-out)": { "prologue": 4881, - "notes_processing": 118011, + "notes_processing": 118067, "note_execution": { - "0x2ca2d2a9fbfd77c6817c923b1d797f92e3f31c3aa9b828b2ad869e5de4e3dd9f": 117969 + "0xbac81ba61cf684e582edc195a69cc81811884bf835bb93799337656b83b7f394": 118025 }, "tx_script_processing": 42, "epilogue": { - "total": 26438, - "auth_procedure": 11835 + "total": 26450, + "auth_procedure": 11847 }, "trace": { - "core_rows": 149416, - "chiplets_rows": 70533, - "range_rows": 4665, + "core_rows": 149484, + "chiplets_rows": 70573, + "range_rows": 4655, "chiplets_shape": { - "hasher_rows": 56440, + "hasher_rows": 56480, "bitwise_rows": 3528, "memory_rows": 10502, "kernel_rom_rows": 62, @@ -215,21 +215,21 @@ }, "consume B2AGG note (bridge-out, 2^31 leaves)": { "prologue": 4881, - "notes_processing": 116304, + "notes_processing": 116360, "note_execution": { - "0x2ca2d2a9fbfd77c6817c923b1d797f92e3f31c3aa9b828b2ad869e5de4e3dd9f": 116262 + "0xbac81ba61cf684e582edc195a69cc81811884bf835bb93799337656b83b7f394": 116318 }, "tx_script_processing": 42, "epilogue": { - "total": 26150, - "auth_procedure": 11835 + "total": 26162, + "auth_procedure": 11847 }, "trace": { - "core_rows": 147421, - "chiplets_rows": 69546, - "range_rows": 4729, + "core_rows": 147489, + "chiplets_rows": 69586, + "range_rows": 4705, "chiplets_shape": { - "hasher_rows": 55584, + "hasher_rows": 55624, "bitwise_rows": 3528, "memory_rows": 10371, "kernel_rom_rows": 62, @@ -239,21 +239,21 @@ }, "consume B2AGG note (bridge-out, 2^31-1 leaves)": { "prologue": 4881, - "notes_processing": 61655, + "notes_processing": 61711, "note_execution": { - "0x2ca2d2a9fbfd77c6817c923b1d797f92e3f31c3aa9b828b2ad869e5de4e3dd9f": 61613 + "0xbac81ba61cf684e582edc195a69cc81811884bf835bb93799337656b83b7f394": 61669 }, "tx_script_processing": 42, "epilogue": { - "total": 17510, - "auth_procedure": 11835 + "total": 17522, + "auth_procedure": 11847 }, "trace": { - "core_rows": 84132, - "chiplets_rows": 39448, - "range_rows": 3563, + "core_rows": 84200, + "chiplets_rows": 39488, + "range_rows": 3559, "chiplets_shape": { - "hasher_rows": 29416, + "hasher_rows": 29456, "bitwise_rows": 3528, "memory_rows": 6441, "kernel_rom_rows": 62, @@ -263,21 +263,21 @@ }, "consume P2ID note (network account)": { "prologue": 3719, - "notes_processing": 2184, + "notes_processing": 2200, "note_execution": { - "0x9ffdc9ed78028f7eda93f7c44287cb1f719d69069d2d8669025fc760c7da567a": 2142 + "0x2fc9fa5f7c6db2669cdeaf6af782e1cd2e24a8026cdd4faf95b74453c0f939f8": 2158 }, "tx_script_processing": 42, "epilogue": { - "total": 12698, - "auth_procedure": 9521 + "total": 12732, + "auth_procedure": 9555 }, "trace": { - "core_rows": 18687, - "chiplets_rows": 8300, - "range_rows": 1459, + "core_rows": 18737, + "chiplets_rows": 8316, + "range_rows": 1485, "chiplets_shape": { - "hasher_rows": 6312, + "hasher_rows": 6328, "bitwise_rows": 824, "memory_rows": 1101, "kernel_rom_rows": 62, @@ -287,21 +287,21 @@ }, "consume P2ID note (16 assets, network account)": { "prologue": 12314, - "notes_processing": 30173, + "notes_processing": 30279, "note_execution": { - "0x27098731a1d1d6d12551117a4e3c77fa946491a977105e8d33f70a367fbceda7": 30131 + "0x0568c989f8b4f74472a76cef4fac6f51c17fa69c38e5041dd73944c72df91bc1": 30237 }, "tx_script_processing": 42, "epilogue": { - "total": 15177, - "auth_procedure": 9840 + "total": 15211, + "auth_procedure": 9874 }, "trace": { - "core_rows": 57750, - "chiplets_rows": 33266, - "range_rows": 3631, + "core_rows": 57890, + "chiplets_rows": 33282, + "range_rows": 3583, "chiplets_shape": { - "hasher_rows": 24768, + "hasher_rows": 24784, "bitwise_rows": 5504, "memory_rows": 2931, "kernel_rom_rows": 62, @@ -311,21 +311,21 @@ }, "consume P2IDE note (claim, network account)": { "prologue": 3719, - "notes_processing": 2309, + "notes_processing": 2325, "note_execution": { - "0x05847ac68b2b527d6932ebc07325c6e4346cacba2c8eca17334cabaed180fe50": 2267 + "0x943339b21513253f8df4b3afcf5903347f84ba03df23ec1518d1d164a3863eba": 2283 }, "tx_script_processing": 42, "epilogue": { - "total": 12698, - "auth_procedure": 9521 + "total": 12732, + "auth_procedure": 9555 }, "trace": { - "core_rows": 18812, - "chiplets_rows": 8336, - "range_rows": 1495, + "core_rows": 18862, + "chiplets_rows": 8352, + "range_rows": 1459, "chiplets_shape": { - "hasher_rows": 6344, + "hasher_rows": 6360, "bitwise_rows": 824, "memory_rows": 1105, "kernel_rom_rows": 62, @@ -335,21 +335,21 @@ }, "consume P2IDE note (claim, 16 assets, network account)": { "prologue": 12314, - "notes_processing": 30298, + "notes_processing": 30404, "note_execution": { - "0x3cb8d89cd690317eadc9777eb377d0bc75e44c5f3b76b4484a3928c9fc0bb16f": 30256 + "0x991129c34019d8612646f8209d36a40b5d0ec46ac487e10f9a39e168e8371129": 30362 }, "tx_script_processing": 42, "epilogue": { - "total": 15177, - "auth_procedure": 9840 + "total": 15211, + "auth_procedure": 9874 }, "trace": { - "core_rows": 57875, - "chiplets_rows": 33302, + "core_rows": 58015, + "chiplets_rows": 33318, "range_rows": 3617, "chiplets_shape": { - "hasher_rows": 24800, + "hasher_rows": 24816, "bitwise_rows": 5504, "memory_rows": 2935, "kernel_rom_rows": 62, @@ -359,21 +359,21 @@ }, "consume P2IDE note (reclaim, network account)": { "prologue": 3822, - "notes_processing": 2361, + "notes_processing": 2377, "note_execution": { - "0xbb2f39f6cb6f735050be9911032bebb082c4477c01920a05bdf81449a2a2531f": 2319 + "0x7f2daf1d1282ddbfd5feea9246fc7c90d45edfbb321dda3246b4b141380098df": 2335 }, "tx_script_processing": 42, "epilogue": { - "total": 12698, - "auth_procedure": 9521 + "total": 12732, + "auth_procedure": 9555 }, "trace": { - "core_rows": 18967, - "chiplets_rows": 8367, - "range_rows": 1539, + "core_rows": 19017, + "chiplets_rows": 8383, + "range_rows": 1487, "chiplets_shape": { - "hasher_rows": 6368, + "hasher_rows": 6384, "bitwise_rows": 824, "memory_rows": 1112, "kernel_rom_rows": 62, @@ -383,21 +383,21 @@ }, "consume SWAP note (public payback, network account)": { "prologue": 3719, - "notes_processing": 4578, + "notes_processing": 4622, "note_execution": { - "0xd642acc2180f1ea6f6fbd1bce45b076d517ed595c4f3c3bd9bce22ec66e9e5c7": 4536 + "0x5ac2969fe0ea6bc33421d9df0d015da7761323c0f0f4233a450178b8e6e777ec": 4580 }, "tx_script_processing": 42, "epilogue": { - "total": 14389, - "auth_procedure": 10061 + "total": 14423, + "auth_procedure": 10095 }, "trace": { - "core_rows": 22772, - "chiplets_rows": 10390, - "range_rows": 1759, + "core_rows": 22850, + "chiplets_rows": 10414, + "range_rows": 1803, "chiplets_shape": { - "hasher_rows": 7880, + "hasher_rows": 7904, "bitwise_rows": 1192, "memory_rows": 1255, "kernel_rom_rows": 62, @@ -407,21 +407,21 @@ }, "consume SWAP note (private payback, network account)": { "prologue": 3719, - "notes_processing": 4064, + "notes_processing": 4102, "note_execution": { - "0x6d44d87b75165cdc5794a3dbc5cdba77277e5241ccd2e332e9d78b47f58a29af": 4022 + "0x2a8487a87546bf9fbc85b0cf0d215adfb2cc8a0c559852b7fdb39ffdfe2408a4": 4060 }, "tx_script_processing": 42, "epilogue": { - "total": 14389, - "auth_procedure": 10061 + "total": 14423, + "auth_procedure": 10095 }, "trace": { - "core_rows": 22258, - "chiplets_rows": 10269, - "range_rows": 1737, + "core_rows": 22330, + "chiplets_rows": 10293, + "range_rows": 1755, "chiplets_shape": { - "hasher_rows": 7776, + "hasher_rows": 7800, "bitwise_rows": 1192, "memory_rows": 1238, "kernel_rom_rows": 62, @@ -431,21 +431,21 @@ }, "consume PSWAP note (full fill, network account)": { "prologue": 3719, - "notes_processing": 7100, + "notes_processing": 7156, "note_execution": { - "0x8258ed08b1106ffafca1fa325d7588834fea990bd9e6c4600b871ef6e2708192": 7058 + "0xd4e6cc16194c18f021ad0d9333e548a059e412a2629e8ef948a26b67cd36e831": 7114 }, "tx_script_processing": 42, "epilogue": { - "total": 14397, - "auth_procedure": 10061 + "total": 14431, + "auth_procedure": 10095 }, "trace": { - "core_rows": 25302, - "chiplets_rows": 11078, - "range_rows": 1751, + "core_rows": 25392, + "chiplets_rows": 11110, + "range_rows": 1793, "chiplets_shape": { - "hasher_rows": 8320, + "hasher_rows": 8352, "bitwise_rows": 1264, "memory_rows": 1431, "kernel_rom_rows": 62, @@ -455,21 +455,21 @@ }, "consume PSWAP note (partial fill, network account)": { "prologue": 3719, - "notes_processing": 9662, + "notes_processing": 9770, "note_execution": { - "0x8258ed08b1106ffafca1fa325d7588834fea990bd9e6c4600b871ef6e2708192": 9620 + "0xd4e6cc16194c18f021ad0d9333e548a059e412a2629e8ef948a26b67cd36e831": 9728 }, "tx_script_processing": 42, "epilogue": { - "total": 15811, - "auth_procedure": 10284 + "total": 15845, + "auth_procedure": 10318 }, "trace": { - "core_rows": 29278, - "chiplets_rows": 12802, - "range_rows": 2001, + "core_rows": 29420, + "chiplets_rows": 12850, + "range_rows": 1965, "chiplets_shape": { - "hasher_rows": 9504, + "hasher_rows": 9552, "bitwise_rows": 1640, "memory_rows": 1595, "kernel_rom_rows": 62, @@ -479,23 +479,23 @@ }, "consume MINT note (fungible faucet, network account)": { "prologue": 5447, - "notes_processing": 7585, + "notes_processing": 7589, "note_execution": { - "0x7cb085b49226958234c26ea8f79155940e3a985b932c3ce66b76e9fd1420e456": 7543 + "0xd1e75321768c16605adc33ce53d38d3b85f3b8b86b30d77e4e027473b57ebcc1": 7547 }, "tx_script_processing": 42, "epilogue": { - "total": 19188, - "auth_procedure": 9816 + "total": 19222, + "auth_procedure": 9850 }, "trace": { - "core_rows": 32306, - "chiplets_rows": 12689, - "range_rows": 2797, + "core_rows": 32344, + "chiplets_rows": 12709, + "range_rows": 2815, "chiplets_shape": { - "hasher_rows": 9408, + "hasher_rows": 9432, "bitwise_rows": 1144, - "memory_rows": 2074, + "memory_rows": 2070, "kernel_rom_rows": 62, "ace_rows": 0 } @@ -503,23 +503,23 @@ }, "consume MINT note (non-fungible faucet, network account)": { "prologue": 5496, - "notes_processing": 10133, + "notes_processing": 10137, "note_execution": { - "0xa488b4dd26ce659555153165a2b5dd830383a226748abc325f19cc39e914ad8c": 10091 + "0x1c5bf423b277651ccd476d2481db0495facbcb4910f14bbce86371c40b78156b": 10095 }, "tx_script_processing": 42, "epilogue": { - "total": 19481, - "auth_procedure": 9825 + "total": 19515, + "auth_procedure": 9859 }, "trace": { - "core_rows": 35196, - "chiplets_rows": 13817, + "core_rows": 35234, + "chiplets_rows": 13829, "range_rows": 2943, "chiplets_shape": { - "hasher_rows": 10416, + "hasher_rows": 10432, "bitwise_rows": 1144, - "memory_rows": 2194, + "memory_rows": 2190, "kernel_rom_rows": 62, "ace_rows": 0 } @@ -527,21 +527,21 @@ }, "consume BURN note (network account)": { "prologue": 6046, - "notes_processing": 4758, + "notes_processing": 4768, "note_execution": { - "0x652872e4aa6a0681c4fa88e107ca15726c52b521e30ab84049a8eaf3e4422026": 4716 + "0x90898e9cd22c094ab2ccdda7833c8f3f82d972051bb75e245cca8e220093e523": 4726 }, "tx_script_processing": 42, "epilogue": { - "total": 17930, - "auth_procedure": 9595 + "total": 17964, + "auth_procedure": 9629 }, "trace": { - "core_rows": 28820, - "chiplets_rows": 11433, - "range_rows": 2609, + "core_rows": 28864, + "chiplets_rows": 11457, + "range_rows": 2581, "chiplets_shape": { - "hasher_rows": 8624, + "hasher_rows": 8648, "bitwise_rows": 856, "memory_rows": 1890, "kernel_rom_rows": 62, @@ -551,23 +551,23 @@ }, "consume FAUCET_POLICY_CONFIG note (network account)": { "prologue": 5400, - "notes_processing": 5407, + "notes_processing": 5378, "note_execution": { - "0xc7d1d6d6dc6a738a3115fb8aec12c2fb9767356f2c67fd08a7163d7aadc1e7e0": 5365 + "0x52d411d2bdc92e4d12d2fd5093d148be6afe26e6ed3b3e2e736b9696235b187e": 5336 }, "tx_script_processing": 42, "epilogue": { - "total": 17783, - "auth_procedure": 9586 + "total": 17817, + "auth_procedure": 9620 }, "trace": { - "core_rows": 28676, - "chiplets_rows": 10392, - "range_rows": 2577, + "core_rows": 28681, + "chiplets_rows": 10415, + "range_rows": 2581, "chiplets_shape": { - "hasher_rows": 7976, + "hasher_rows": 8000, "bitwise_rows": 560, - "memory_rows": 1793, + "memory_rows": 1792, "kernel_rom_rows": 62, "ace_rows": 0 } @@ -575,21 +575,21 @@ }, "consume FAUCET_METADATA_CONFIG note (network account)": { "prologue": 4824, - "notes_processing": 6294, + "notes_processing": 6316, "note_execution": { - "0x8ea187c948675fe48a1ef5dcf25edeb636f501de68daffcad465c87fc418d30b": 6252 + "0x5185368b3cf185a3afa71202f8027cb96d8497076cb08d7b7e69f1a1c6fcf9f3": 6274 }, "tx_script_processing": 42, "epilogue": { - "total": 16438, - "auth_procedure": 9505 + "total": 16472, + "auth_procedure": 9539 }, "trace": { - "core_rows": 27642, - "chiplets_rows": 10162, - "range_rows": 2403, + "core_rows": 27698, + "chiplets_rows": 10186, + "range_rows": 2405, "chiplets_shape": { - "hasher_rows": 7696, + "hasher_rows": 7720, "bitwise_rows": 568, "memory_rows": 1835, "kernel_rom_rows": 62, @@ -599,21 +599,21 @@ }, "consume MIN_BURN_AMOUNT_CONFIG note (network account)": { "prologue": 5457, - "notes_processing": 2514, + "notes_processing": 2536, "note_execution": { - "0xd1e581d8109fee2c5b6e8c4d5600506cdf6128e7d40bb27236f6b383e8208898": 2472 + "0xab3ba7fcce2c7f0168e42a4d5def6b0c697a0e8d71ca0a6f601f0eadf6212308": 2494 }, "tx_script_processing": 42, "epilogue": { - "total": 17930, - "auth_procedure": 9595 + "total": 17964, + "auth_procedure": 9629 }, "trace": { - "core_rows": 25987, - "chiplets_rows": 9641, - "range_rows": 2471, + "core_rows": 26043, + "chiplets_rows": 9665, + "range_rows": 2477, "chiplets_shape": { - "hasher_rows": 7304, + "hasher_rows": 7328, "bitwise_rows": 560, "memory_rows": 1714, "kernel_rom_rows": 62, @@ -623,21 +623,21 @@ }, "consume ALLOWLIST_CONFIG note (network account)": { "prologue": 5457, - "notes_processing": 3088, + "notes_processing": 3110, "note_execution": { - "0x96371aba3cc701dee73e96fbbc51f87d716641a06ccc6e455168e7a4ae443e94": 3046 + "0xa3180834f27f30f7f39a1f9381c01bb750cec10e02feed4d5f5cbd8a3acf2ed2": 3068 }, "tx_script_processing": 42, "epilogue": { - "total": 18106, - "auth_procedure": 9595 + "total": 18140, + "auth_procedure": 9629 }, "trace": { - "core_rows": 26737, - "chiplets_rows": 10269, - "range_rows": 2499, + "core_rows": 26793, + "chiplets_rows": 10301, + "range_rows": 2461, "chiplets_shape": { - "hasher_rows": 7856, + "hasher_rows": 7888, "bitwise_rows": 560, "memory_rows": 1790, "kernel_rom_rows": 62, @@ -647,21 +647,21 @@ }, "consume BLOCKLIST_CONFIG note (network account)": { "prologue": 5457, - "notes_processing": 3088, + "notes_processing": 3110, "note_execution": { - "0xe13a4c39807ed4265ac1abb9de8509a3f9a23bfca58de66624560c04c164ccc4": 3046 + "0x160faafbcec1348cc284ef663ee8490b09fe7332cab4485ba5a7bd97ad04e4df": 3068 }, "tx_script_processing": 42, "epilogue": { - "total": 18106, - "auth_procedure": 9595 + "total": 18140, + "auth_procedure": 9629 }, "trace": { - "core_rows": 26737, - "chiplets_rows": 10269, - "range_rows": 2481, + "core_rows": 26793, + "chiplets_rows": 10301, + "range_rows": 2441, "chiplets_shape": { - "hasher_rows": 7856, + "hasher_rows": 7888, "bitwise_rows": 560, "memory_rows": 1790, "kernel_rom_rows": 62, @@ -671,21 +671,21 @@ }, "consume PAUSE_CONFIG note (network account)": { "prologue": 3327, - "notes_processing": 2529, + "notes_processing": 2551, "note_execution": { - "0x76891cc44cdb15d05251ecfe69b337f1281360a49b8b302640565382d9307143": 2487 + "0xebb018d8bcaf491bcb03cd88236753c98f3b71b8ba606fb471352b884d8c0c1e": 2509 }, "tx_script_processing": 42, "epilogue": { - "total": 12745, - "auth_procedure": 9280 + "total": 12779, + "auth_procedure": 9314 }, "trace": { - "core_rows": 18687, - "chiplets_rows": 7453, - "range_rows": 1529, + "core_rows": 18743, + "chiplets_rows": 7485, + "range_rows": 1511, "chiplets_shape": { - "hasher_rows": 5680, + "hasher_rows": 5712, "bitwise_rows": 560, "memory_rows": 1150, "kernel_rom_rows": 62, @@ -695,21 +695,21 @@ }, "consume OWNER_CONFIG note (network account)": { "prologue": 3186, - "notes_processing": 2558, + "notes_processing": 2580, "note_execution": { - "0xff8dc68047d1df36aa4b3b1130779084c9216aac8f9c8dfcdfbd1f82cbe8ec40": 2516 + "0x9f882e6a06d58d867c57b456e39bc2ebe4a93392dc9bfc262d5bbefc55146aee": 2538 }, "tx_script_processing": 42, "epilogue": { - "total": 12451, - "auth_procedure": 9262 + "total": 12485, + "auth_procedure": 9296 }, "trace": { - "core_rows": 18281, - "chiplets_rows": 7341, - "range_rows": 1529, + "core_rows": 18337, + "chiplets_rows": 7373, + "range_rows": 1527, "chiplets_shape": { - "hasher_rows": 5584, + "hasher_rows": 5616, "bitwise_rows": 576, "memory_rows": 1118, "kernel_rom_rows": 62, @@ -719,21 +719,21 @@ }, "consume RBAC_CONFIG note (network account)": { "prologue": 3365, - "notes_processing": 5422, + "notes_processing": 5619, "note_execution": { - "0xa36b3c9088c716fd7295a35f01a53acab54c7ea5d85feefbecb1f9f4b8699307": 5380 + "0xdbfded8af71d1235e164dc8a04861244a363bc04df10772d5010a9cafbbf4d87": 5577 }, "tx_script_processing": 42, "epilogue": { - "total": 13272, - "auth_procedure": 9289 + "total": 13306, + "auth_procedure": 9323 }, "trace": { - "core_rows": 22145, - "chiplets_rows": 9797, - "range_rows": 1709, + "core_rows": 22376, + "chiplets_rows": 9869, + "range_rows": 1753, "chiplets_shape": { - "hasher_rows": 7720, + "hasher_rows": 7792, "bitwise_rows": 576, "memory_rows": 1438, "kernel_rom_rows": 62, @@ -743,21 +743,21 @@ }, "consume NETWORK_ACCOUNT_CONFIG note (network account)": { "prologue": 3270, - "notes_processing": 3061, + "notes_processing": 3083, "note_execution": { - "0x963ba76870db04bbab4fd2fa4ed3939aa38dd709634296b681beac6723728d1e": 3019 + "0xecbb7b076577526e725415c52c1e77cba5d4a59afd6b8d3b4569da055bdc7344": 3041 }, "tx_script_processing": 42, "epilogue": { - "total": 12764, - "auth_procedure": 9271 + "total": 12798, + "auth_procedure": 9305 }, "trace": { - "core_rows": 19181, - "chiplets_rows": 7999, - "range_rows": 1595, + "core_rows": 19237, + "chiplets_rows": 8023, + "range_rows": 1557, "chiplets_shape": { - "hasher_rows": 6176, + "hasher_rows": 6200, "bitwise_rows": 560, "memory_rows": 1200, "kernel_rom_rows": 62, @@ -767,21 +767,21 @@ }, "consume CONSTANT_FEE_POLICY_CONFIG note (network account)": { "prologue": 3317, - "notes_processing": 3782, + "notes_processing": 3804, "note_execution": { - "0xba847903cc257dbbe09852d5269201716661c41ad25494c8252ff15b423cc687": 3740 + "0x0f572100bd3d38b4acfe7395f9989917763a99bc67a122cf9c13d7a9b99fb5c1": 3762 }, "tx_script_processing": 42, "epilogue": { - "total": 12911, - "auth_procedure": 9280 + "total": 12945, + "auth_procedure": 9314 }, "trace": { - "core_rows": 20096, - "chiplets_rows": 8283, - "range_rows": 1579, + "core_rows": 20152, + "chiplets_rows": 8307, + "range_rows": 1543, "chiplets_shape": { - "hasher_rows": 6384, + "hasher_rows": 6408, "bitwise_rows": 560, "memory_rows": 1276, "kernel_rom_rows": 62, @@ -791,22 +791,22 @@ }, "consume FEE_SPONSORSHIP note with feature note (network account)": { "prologue": 4775, - "notes_processing": 1191, + "notes_processing": 1201, "note_execution": { - "0x0ac353668246cdbf750e7e8a12eba871f95984a19b9f0998b5c9809e7e27cea4": 666, + "0x0878e4f039a63ef94a6aea1b5170fa5a155acd70ed72f17d9e9054f86f7254e1": 676, "0x30e133565785ae090e4920c05bb4adac8004adba6ba51e887396e0082197e1e7": 474 }, "tx_script_processing": 42, "epilogue": { - "total": 15455, - "auth_procedure": 12422 + "total": 15507, + "auth_procedure": 12474 }, "trace": { - "core_rows": 21507, - "chiplets_rows": 9347, - "range_rows": 1593, + "core_rows": 21569, + "chiplets_rows": 9379, + "range_rows": 1597, "chiplets_shape": { - "hasher_rows": 7176, + "hasher_rows": 7208, "bitwise_rows": 888, "memory_rows": 1220, "kernel_rom_rows": 62, @@ -816,21 +816,21 @@ }, "consume FEE_SPONSORSHIP note (reclaim)": { "prologue": 3605, - "notes_processing": 2943, + "notes_processing": 2959, "note_execution": { - "0x10ecf93babe366a86b031e4bc407ec2e05f08b8be402539782e54f239aca3222": 2901 + "0xf98abceec1f290ee4de60d305d4d4631e83ef11732a89c99868e31bb8524c9ea": 2917 }, "tx_script_processing": 42, "epilogue": { - "total": 10461, - "auth_procedure": 8243 + "total": 10483, + "auth_procedure": 8265 }, "trace": { - "core_rows": 17095, - "chiplets_rows": 7725, - "range_rows": 1583, + "core_rows": 17133, + "chiplets_rows": 7733, + "range_rows": 1607, "chiplets_shape": { - "hasher_rows": 5648, + "hasher_rows": 5656, "bitwise_rows": 1144, "memory_rows": 870, "kernel_rom_rows": 62, @@ -840,21 +840,21 @@ }, "consume CLAIM note (L1 to Miden, with fee payment)": { "prologue": 3957, - "notes_processing": 28614, + "notes_processing": 28630, "note_execution": { - "0x7ba705db8a8a71392d468e19b6d1abf206ca3358bba4dc2bf3dd92b5187856dc": 28572 + "0xd1113dedd28530bbfdd2e791aab2e5d77e801eb64f9e980b0949e4d9f1c00813": 28588 }, "tx_script_processing": 42, "epilogue": { - "total": 20799, - "auth_procedure": 14496 + "total": 20833, + "auth_procedure": 14530 }, "trace": { - "core_rows": 53456, - "chiplets_rows": 21604, - "range_rows": 3533, + "core_rows": 53506, + "chiplets_rows": 21628, + "range_rows": 3565, "chiplets_shape": { - "hasher_rows": 14256, + "hasher_rows": 14280, "bitwise_rows": 3088, "memory_rows": 4197, "kernel_rom_rows": 62, @@ -864,21 +864,21 @@ }, "consume CLAIM note (L2 to Miden, with fee payment)": { "prologue": 3957, - "notes_processing": 38776, + "notes_processing": 38792, "note_execution": { - "0x927c8823d42f4ca75bfe60aedf56d3c2dc30ed4ee9db3657a63ade6bdaecc1e0": 38734 + "0xd9f2ccf0f2312612eec27110733d927fdd4cc0c72f39c0b0667cfd775ec93eb0": 38750 }, "tx_script_processing": 42, "epilogue": { - "total": 20799, - "auth_procedure": 14496 + "total": 20833, + "auth_procedure": 14530 }, "trace": { - "core_rows": 63618, - "chiplets_rows": 24354, - "range_rows": 3725, + "core_rows": 63668, + "chiplets_rows": 24378, + "range_rows": 3797, "chiplets_shape": { - "hasher_rows": 15808, + "hasher_rows": 15832, "bitwise_rows": 3344, "memory_rows": 5139, "kernel_rom_rows": 62, @@ -888,21 +888,21 @@ }, "consume B2AGG note (bridge-out, with fee payment)": { "prologue": 4881, - "notes_processing": 118011, + "notes_processing": 118067, "note_execution": { - "0x2ca2d2a9fbfd77c6817c923b1d797f92e3f31c3aa9b828b2ad869e5de4e3dd9f": 117969 + "0xbac81ba61cf684e582edc195a69cc81811884bf835bb93799337656b83b7f394": 118025 }, "tx_script_processing": 42, "epilogue": { - "total": 30566, - "auth_procedure": 14581 + "total": 30600, + "auth_procedure": 14615 }, "trace": { - "core_rows": 153544, - "chiplets_rows": 72653, - "range_rows": 4767, + "core_rows": 153634, + "chiplets_rows": 72685, + "range_rows": 4791, "chiplets_shape": { - "hasher_rows": 58072, + "hasher_rows": 58104, "bitwise_rows": 3864, "memory_rows": 10654, "kernel_rom_rows": 62, @@ -912,21 +912,21 @@ }, "consume B2AGG note (bridge-out, 2^31-1 leaves, with fee payment)": { "prologue": 4881, - "notes_processing": 61655, + "notes_processing": 61711, "note_execution": { - "0x2ca2d2a9fbfd77c6817c923b1d797f92e3f31c3aa9b828b2ad869e5de4e3dd9f": 61613 + "0xbac81ba61cf684e582edc195a69cc81811884bf835bb93799337656b83b7f394": 61669 }, "tx_script_processing": 42, "epilogue": { - "total": 21638, - "auth_procedure": 14581 + "total": 21672, + "auth_procedure": 14615 }, "trace": { - "core_rows": 88260, - "chiplets_rows": 41568, - "range_rows": 3713, + "core_rows": 88350, + "chiplets_rows": 41600, + "range_rows": 3731, "chiplets_shape": { - "hasher_rows": 31048, + "hasher_rows": 31080, "bitwise_rows": 3864, "memory_rows": 6593, "kernel_rom_rows": 62, @@ -936,21 +936,21 @@ }, "consume CONFIG_AGG_BRIDGE note (with fee payment)": { "prologue": 4265, - "notes_processing": 13724, + "notes_processing": 14224, "note_execution": { - "0x51eb958cc6ce003f866bed3f618c3a530dcb515bf791949db7707b662797a8a7": 13682 + "0xc7d78ac1c12ee2c73ace591198df2f7f2a0df35a6c28512e46e8c9f215eba0d5": 14182 }, "tx_script_processing": 42, "epilogue": { - "total": 16153, - "auth_procedure": 9424 + "total": 16187, + "auth_procedure": 9458 }, "trace": { - "core_rows": 34228, - "chiplets_rows": 14978, - "range_rows": 2415, + "core_rows": 34762, + "chiplets_rows": 15106, + "range_rows": 2489, "chiplets_shape": { - "hasher_rows": 11928, + "hasher_rows": 12056, "bitwise_rows": 616, "memory_rows": 2371, "kernel_rom_rows": 62, @@ -960,21 +960,21 @@ }, "consume DEREGISTER_AGG_FAUCET note (with fee payment)": { "prologue": 4324, - "notes_processing": 12969, + "notes_processing": 13227, "note_execution": { - "0xdc8ae96d95b6c8bba383e0829a1de57827736349bf2a65d29b98a0b8ce8de44f": 12927 + "0xd49c5d9494f5d5a0bdd6caef10d9a27b5339b64d7419763f064f58b88ee36cee": 13185 }, "tx_script_processing": 42, "epilogue": { - "total": 16153, - "auth_procedure": 9424 + "total": 16187, + "auth_procedure": 9458 }, "trace": { - "core_rows": 33532, - "chiplets_rows": 14628, - "range_rows": 2313, + "core_rows": 33824, + "chiplets_rows": 14700, + "range_rows": 2417, "chiplets_shape": { - "hasher_rows": 11712, + "hasher_rows": 11784, "bitwise_rows": 608, "memory_rows": 2245, "kernel_rom_rows": 62, @@ -984,21 +984,21 @@ }, "consume UPDATE_GER note (with fee payment)": { "prologue": 4265, - "notes_processing": 4504, + "notes_processing": 4775, "note_execution": { - "0x83a2de49840c9bf985e0b919a89e3ce2e632036932a345ad4faf40bccf37b61f": 4462 + "0xb7aa0dc73094d2587c0edb6f344291937ee6e4b85b8b1c711e3891124f995f64": 4733 }, "tx_script_processing": 42, "epilogue": { - "total": 15353, - "auth_procedure": 9424 + "total": 15387, + "auth_procedure": 9458 }, "trace": { - "core_rows": 24208, - "chiplets_rows": 9841, - "range_rows": 2053, + "core_rows": 24513, + "chiplets_rows": 9913, + "range_rows": 2125, "chiplets_shape": { - "hasher_rows": 7584, + "hasher_rows": 7656, "bitwise_rows": 560, "memory_rows": 1634, "kernel_rom_rows": 62, @@ -1008,21 +1008,21 @@ }, "consume REMOVE_GER note (with fee payment)": { "prologue": 4324, - "notes_processing": 5568, + "notes_processing": 5826, "note_execution": { - "0xffa3eac6e83fdd664e9dca686e96d17b331a77437f1a5cbb3521b261fab833b9": 5526 + "0x949c716588b8e9d4077fb95beb108285152e125cb68a0fcf4c2c5e1208f53aca": 5784 }, "tx_script_processing": 42, "epilogue": { - "total": 15389, - "auth_procedure": 9424 + "total": 15423, + "auth_procedure": 9458 }, "trace": { - "core_rows": 25367, - "chiplets_rows": 10161, - "range_rows": 2077, + "core_rows": 25659, + "chiplets_rows": 10233, + "range_rows": 2171, "chiplets_shape": { - "hasher_rows": 7816, + "hasher_rows": 7888, "bitwise_rows": 560, "memory_rows": 1722, "kernel_rom_rows": 62, diff --git a/crates/miden-agglayer/src/costs/table.rs b/crates/miden-agglayer/src/costs/table.rs index 2b76b74377..84d868d58d 100644 --- a/crates/miden-agglayer/src/costs/table.rs +++ b/crates/miden-agglayer/src/costs/table.rs @@ -2,20 +2,20 @@ // Values are maxima across the benchmarked paths; see `miden_standards::note::costs` for the // caveats on what they do and do not cover. -/// Cycles of consuming a CLAIM note: L1 origin 53412, L2 origin 63574 (maximum). -pub const CLAIM_CONSUMPTION_CYCLES: u32 = 63574; +/// Cycles of consuming a CLAIM note: L1 origin 53462, L2 origin 63624 (maximum). +pub const CLAIM_CONSUMPTION_CYCLES: u32 = 63624; -/// Cycles of consuming a B2AGG note: empty frontier 153500 (maximum), 2^31-1 leaves 88216. -pub const B2AGG_CONSUMPTION_CYCLES: u32 = 153500; +/// Cycles of consuming a B2AGG note: empty frontier 153590 (maximum), 2^31-1 leaves 88306. +pub const B2AGG_CONSUMPTION_CYCLES: u32 = 153590; /// Cycles of consuming a CONFIG_AGG_BRIDGE note (single benchmarked path). -pub const CONFIG_AGG_BRIDGE_CONSUMPTION_CYCLES: u32 = 34184; +pub const CONFIG_AGG_BRIDGE_CONSUMPTION_CYCLES: u32 = 34718; /// Cycles of consuming a DEREGISTER_AGG_FAUCET note (single benchmarked path). -pub const DEREGISTER_AGG_FAUCET_CONSUMPTION_CYCLES: u32 = 33488; +pub const DEREGISTER_AGG_FAUCET_CONSUMPTION_CYCLES: u32 = 33780; /// Cycles of consuming an UPDATE_GER note (single benchmarked path). -pub const UPDATE_GER_CONSUMPTION_CYCLES: u32 = 24164; +pub const UPDATE_GER_CONSUMPTION_CYCLES: u32 = 24469; /// Cycles of consuming a REMOVE_GER note (single benchmarked path). -pub const REMOVE_GER_CONSUMPTION_CYCLES: u32 = 25323; +pub const REMOVE_GER_CONSUMPTION_CYCLES: u32 = 25615; diff --git a/crates/miden-standards/asm/standards/access/mod.masm b/crates/miden-standards/asm/standards/access/mod.masm index 5402f77d26..d27d4c5364 100644 --- a/crates/miden-standards/asm/standards/access/mod.masm +++ b/crates/miden-standards/asm/standards/access/mod.masm @@ -2,3 +2,4 @@ pub mod authority pub mod ownable2step pub mod pausable pub mod rbac +pub mod role_symbol diff --git a/crates/miden-standards/asm/standards/access/rbac.masm b/crates/miden-standards/asm/standards/access/rbac.masm index 67db3c8cac..b9dade526c 100644 --- a/crates/miden-standards/asm/standards/access/rbac.masm +++ b/crates/miden-standards/asm/standards/access/rbac.masm @@ -41,6 +41,7 @@ use miden::protocol::account_id use miden::protocol::active_account use miden::protocol::active_note use miden::protocol::native_account +use miden::standards::access::role_symbol # CONSTANTS # ================================================================================================= @@ -68,7 +69,6 @@ const ADMIN_ROLE = 1836707 const ERR_SENDER_LACKS_ROLE = "note sender does not hold the required role" const ERR_SENDER_NOT_ROLE_ADMIN = "note sender does not hold the role's admin role" const ERR_ACCOUNT_NOT_IN_ROLE = "account does not hold the role" -const ERR_ROLE_SYMBOL_ZERO = "role symbol is zero" const ERR_MEMBER_COUNT_OVERFLOW = "role member count overflowed u32" # PUBLIC INTERFACE @@ -156,14 +156,18 @@ end #! management by the default `ADMIN` role. #! #! Panics if: -#! - role_symbol is zero. +#! - role_symbol is not a valid role symbol encoding. +#! - admin_role_symbol is non-zero and is not a valid role symbol encoding. #! - the note sender is not authorized to administer the role (see #! `assert_sender_is_role_admin`). #! #! Invocation: call @account_procedure pub proc set_role_admin - dup exec.assert_role_symbol_non_zero + dup exec.role_symbol::validate_encoding + # => [role_symbol, new_admin_role_symbol, pad(14)] + + dup.1 exec.role_symbol::validate_admin_role_symbol # => [role_symbol, new_admin_role_symbol, pad(14)] dup exec.assert_sender_is_role_admin @@ -200,7 +204,7 @@ end #! Invocation: call @account_procedure pub proc grant_role - dup exec.assert_role_symbol_non_zero + dup exec.role_symbol::validate_encoding # => [role_symbol, account_suffix, account_prefix, pad(13)] dup exec.assert_sender_is_role_admin @@ -240,7 +244,7 @@ end #! Invocation: call @account_procedure pub proc revoke_role - dup exec.assert_role_symbol_non_zero + dup exec.role_symbol::validate_encoding # => [role_symbol, account_suffix, account_prefix, pad(13)] dup exec.assert_sender_is_role_admin @@ -268,7 +272,7 @@ end #! Invocation: call @account_procedure pub proc renounce_role - dup exec.assert_role_symbol_non_zero + dup exec.role_symbol::validate_encoding # => [role_symbol, pad(15)] exec.active_note::get_sender @@ -301,7 +305,7 @@ end #! #! Invocation: exec pub proc assert_sender_has_role - dup exec.assert_role_symbol_non_zero + dup exec.role_symbol::validate_encoding # => [role_symbol] exec.is_sender_in_role @@ -328,19 +332,6 @@ end # HELPER PROCEDURES # ================================================================================================= -#! Asserts that a role symbol is non-zero. -#! -#! Inputs: [role_symbol] -#! Outputs: [] -#! -#! Panics if: -#! - role_symbol is zero. -#! -#! Invocation: exec -proc assert_role_symbol_non_zero - eq.0 assertz.err=ERR_ROLE_SYMBOL_ZERO - # => [] -end #! Returns the config for a role. #! diff --git a/crates/miden-standards/asm/standards/access/role_symbol.masm b/crates/miden-standards/asm/standards/access/role_symbol.masm new file mode 100644 index 0000000000..63149d351f --- /dev/null +++ b/crates/miden-standards/asm/standards/access/role_symbol.masm @@ -0,0 +1,147 @@ +# miden::standards::access::role_symbol +# +# Encoding of the role symbols used by role-based access control. +# +# A role symbol names a role in 1 to `MAX_LENGTH` characters of a 27-character alphabet (`A`-`Z` +# and `_`), packed into a single felt as `length + ALPHABET_LEN * characters`. +# +# On-chain a role is only ever that felt: `rbac` matches it against its membership map without +# decoding it, so nothing about the encoding enforces itself. This module holds a felt to the +# encoding, which keeps every role an account authorizes decodable by readers of that account. +# +# Keep the constants below in sync with `RoleSymbol` on the Rust side. + +# CONSTANTS +# ================================================================================================= + +# Size of the alphabet a role symbol is packed in (`A`-`Z` and `_`), i.e. the base of the encoding. +const ALPHABET_LEN = 27 + +# Maximum number of characters a role symbol encodes. +const MAX_LENGTH = 12 + +# 2^32 reduced modulo `ALPHABET_LEN`. Folding a felt's high limb in by this factor yields the +# remainder from u32 divisions alone, avoiding a 64-bit one. +const POW_2_32_MOD_ALPHABET_LEN = 22 + +# ERRORS +# ================================================================================================= + +const ERR_ROLE_SYMBOL_ZERO = "role symbol is zero" +const ERR_INVALID_ROLE_SYMBOL = "role symbol is not a valid encoding" + +# PUBLIC INTERFACE +# ================================================================================================= + +#! Asserts that a felt is a valid role symbol encoding. +#! +#! The encoded value carries its own length: the value modulo `ALPHABET_LEN`. Every character digit +#! above it is below `ALPHABET_LEN` by construction, so beyond the length being in range exactly one +#! condition remains: the value must stay below `ALPHABET_LEN^(length + 1)`, or it carries data past +#! the characters its length announces. +#! +#! Inputs: [role_symbol] +#! Outputs: [] +#! +#! Panics if: +#! - role_symbol is zero. +#! - role_symbol is not a valid role symbol encoding. +#! +#! Invocation: exec +pub proc validate_encoding + dup eq.0 assertz.err=ERR_ROLE_SYMBOL_ZERO + # => [role_symbol] + + dup exec.encoded_length + # => [length, role_symbol] + + # The announced length must select between one and `MAX_LENGTH` characters. + dup neq.0 dup.1 u32lte.MAX_LENGTH and + assert.err=ERR_INVALID_ROLE_SYMBOL + # => [length, role_symbol] + + add.1 exec.alphabet_pow + # => [upper_bound, role_symbol] + + # The value must fit in the characters its length announces. + lt assert.err=ERR_INVALID_ROLE_SYMBOL + # => [] +end + +#! Asserts that a delegated admin role symbol is a valid encoding, accepting zero. +#! +#! A zero symbol carries no delegation, which the `rbac` component reads as management by its +#! built-in `ADMIN` role. +#! +#! Inputs: [admin_role_symbol] +#! Outputs: [] +#! +#! Panics if: +#! - admin_role_symbol is non-zero and is not a valid role symbol encoding. +#! +#! Invocation: exec +pub proc validate_admin_role_symbol + dup eq.0 + # => [is_unset, admin_role_symbol] + + if.true + drop + # => [] + else + exec.validate_encoding + # => [] + end +end + +# HELPER PROCEDURES +# ================================================================================================= + +#! Returns the number of characters a role symbol encodes, i.e. its value modulo `ALPHABET_LEN`. +#! +#! The felt is folded limb by limb, so the remainder comes from u32 divisions alone. +#! +#! Inputs: [role_symbol] +#! Outputs: [length] +#! +#! Invocation: exec +proc encoded_length + u32split + # => [low_limb, high_limb] + + u32divmod.ALPHABET_LEN swap drop + # => [low_remainder, high_limb] + + swap u32divmod.ALPHABET_LEN swap drop + # => [high_remainder, low_remainder] + + mul.POW_2_32_MOD_ALPHABET_LEN add + # => [folded_remainder] + + u32divmod.ALPHABET_LEN swap drop + # => [length] +end + +#! Returns `ALPHABET_LEN` raised to the power of `exponent`. +#! +#! Inputs: [exponent] +#! Outputs: [power] +#! +#! Invocation: exec +proc alphabet_pow + push.1 swap + # => [exponent, power] + + dup neq.0 + # => [is_running, exponent, power] + + while.true + sub.1 swap mul.ALPHABET_LEN swap + # => [exponent, power] + + dup neq.0 + # => [is_running, exponent, power] + end + + drop + # => [power] +end diff --git a/crates/miden-standards/src/account/access/authority.rs b/crates/miden-standards/src/account/access/authority.rs index 2481fd92fe..ae55608a8e 100644 --- a/crates/miden-standards/src/account/access/authority.rs +++ b/crates/miden-standards/src/account/access/authority.rs @@ -71,41 +71,6 @@ const OWNER_CONTROLLED: u8 = 1; /// Authority value written to the storage slot for [`Authority::RbacControlled`]. const RBAC_CONTROLLED: u8 = 2; -// PROCEDURE ROLE -// ================================================================================================ - -/// The role required by an authority-gated procedure, as read from the procedure-roles map. -/// -/// A role is identified on-chain by a field element: `rbac::assert_sender_has_role` matches that -/// felt against the RBAC membership map without ever decoding it. A [`RoleSymbol`] is the printable -/// name the felt decodes to, which exists only for canonically encoded values, so -/// [`to_symbol`][Self::to_symbol] returns `None` for a role that carries no name. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ProcedureRole(Felt); - -impl ProcedureRole { - /// Returns the role identified by `felt`. - pub fn new(felt: Felt) -> Self { - Self(felt) - } - - /// Returns the felt identifying the role, the form on-chain authorization matches on. - pub fn as_felt(&self) -> Felt { - self.0 - } - - /// Returns the role's symbol, or `None` if its felt is not a valid [`RoleSymbol`] encoding. - pub fn to_symbol(&self) -> Option { - RoleSymbol::try_from(self.0).ok() - } -} - -impl From<&RoleSymbol> for ProcedureRole { - fn from(role: &RoleSymbol) -> Self { - Self(role.as_element()) - } -} - // AUTHORITY // ================================================================================================ @@ -288,22 +253,6 @@ impl Authority { Ok(word[1] != Felt::ZERO) } - /// Reads the per-procedure role map without requiring every role to carry a decodable symbol. - /// - /// # Errors - /// - /// Returns an error if: - /// - the procedure-roles storage slot is missing or is not a map. - /// - a role's value word carries data in its reserved felts. - pub fn read_procedure_roles( - storage: &AccountStorage, - ) -> Result, AuthorityError> { - Ok(Self::read_role_felts_from_storage(storage)? - .into_iter() - .map(|(proc_root, role_felt)| (proc_root, ProcedureRole::new(role_felt))) - .collect()) - } - /// Returns the [`AccountComponentMetadata`] for this configuration. pub fn component_metadata(&self) -> AccountComponentMetadata { let mut slots = vec![( @@ -374,25 +323,10 @@ impl Authority { Ok(word) } - /// Reconstructs the per-procedure role map from the procedure-roles storage slot, requiring - /// every role felt to decode into a [`RoleSymbol`]. + /// Reconstructs the per-procedure role map from the procedure-roles storage slot. fn read_roles_from_storage( storage: &AccountStorage, ) -> Result, AuthorityError> { - Self::read_role_felts_from_storage(storage)? - .into_iter() - .map(|(proc_root, role_felt)| { - let role = - RoleSymbol::try_from(role_felt).map_err(AuthorityError::InvalidRoleSymbol)?; - Ok((proc_root, role)) - }) - .collect() - } - - /// Reads the procedure-roles storage slot into the raw role felt of every entry. - fn read_role_felts_from_storage( - storage: &AccountStorage, - ) -> Result, AuthorityError> { let slot = storage .slots() .iter() @@ -403,16 +337,18 @@ impl Authority { return Err(AuthorityError::MissingProcedureRolesSlot); }; - let mut role_felts = BTreeMap::new(); + let mut roles = BTreeMap::new(); for (key, value) in map.entries() { // Enforce the canonical encoding on read: the reserved felts must be zero. if value[1..4].iter().any(|v| *v != Felt::ZERO) { return Err(AuthorityError::NonCanonicalConfig); } - role_felts.insert(AccountProcedureRoot::from_raw(key.as_word()), value[0]); + let proc_root = AccountProcedureRoot::from_raw(key.as_word()); + let role = RoleSymbol::try_from(value[0]).map_err(AuthorityError::InvalidRoleSymbol)?; + roles.insert(proc_root, role); } - Ok(role_felts) + Ok(roles) } } @@ -571,54 +507,5 @@ mod tests { Authority::try_from_storage(&storage), Err(AuthorityError::NonCanonicalConfig) ); - assert_matches!( - Authority::read_procedure_roles(&storage), - Err(AuthorityError::NonCanonicalConfig) - ); - } - - /// On-chain a role is just a felt, so an account built outside `RbacControlled` can hold one - /// that decodes to no symbol. - #[test] - fn role_felt_without_a_symbol_is_read_without_one() { - // 27 is the smallest non-zero felt whose base-27 length digit is 0, so it encodes no - // symbol. - let unnamed_role_felt = Felt::from(27u8); - let expected_root = AccountProcedureRoot::from_raw(Word::from(ROLE_KEY_WORD)); - let storage = rbac_storage_with_role_value(Word::new([ - unnamed_role_felt, - Felt::ZERO, - Felt::ZERO, - Felt::ZERO, - ])); - - // Reconstructing the typed value still fails: `RbacControlled` holds `RoleSymbol`s. - assert_matches!( - Authority::try_from_storage(&storage), - Err(AuthorityError::InvalidRoleSymbol(_)) - ); - - // Reading the map succeeds and preserves the felt on-chain authorization matches on. - let roles = Authority::read_procedure_roles(&storage).unwrap(); - let role = roles.get(&expected_root).expect("the entry should be read"); - assert_eq!(role.as_felt(), unnamed_role_felt); - assert_eq!(role.to_symbol(), None); - } - - /// A canonical role felt is read with its symbol, which is the `RoleSymbol` that wrote it. - #[test] - fn role_felt_with_a_symbol_is_read_with_it() { - let role = RoleSymbol::new("ADMIN").unwrap(); - let expected_root = AccountProcedureRoot::from_raw(Word::from(ROLE_KEY_WORD)); - let storage = rbac_storage_with_role_value(Word::new([ - role.as_element(), - Felt::ZERO, - Felt::ZERO, - Felt::ZERO, - ])); - - let roles = Authority::read_procedure_roles(&storage).unwrap(); - assert_eq!(roles.get(&expected_root), Some(&ProcedureRole::from(&role))); - assert_eq!(roles[&expected_root].to_symbol(), Some(role)); } } diff --git a/crates/miden-standards/src/account/access/mod.rs b/crates/miden-standards/src/account/access/mod.rs index e340804ad2..14b13b67f1 100644 --- a/crates/miden-standards/src/account/access/mod.rs +++ b/crates/miden-standards/src/account/access/mod.rs @@ -90,7 +90,7 @@ impl IntoIterator for AccessControl { } } -pub use authority::{Authority, AuthorityError, ProcedureRole}; +pub use authority::{Authority, AuthorityError}; pub use ownable2step::{Ownable2Step, Ownable2StepError}; pub use pausable::{Pausable, PausableManager, PausableStorage}; pub use rbac::{RoleBasedAccessControl, RoleBasedAccessControlError, RoleConfig}; diff --git a/crates/miden-standards/src/account/access/rbac.rs b/crates/miden-standards/src/account/access/rbac.rs index aa3c996845..297129da57 100644 --- a/crates/miden-standards/src/account/access/rbac.rs +++ b/crates/miden-standards/src/account/access/rbac.rs @@ -193,6 +193,11 @@ impl RoleConfig { /// `MINTER`, `MINTER_ADMIN`, `PAUSER`. The zero field element is reserved and cannot be /// used as a role symbol; attempting to do so panics with `ERR_ROLE_SYMBOL_ZERO`. /// +/// On-chain a role is only ever the encoded field element, so the component's entrypoints hold it +/// to this same encoding and panic with `ERR_INVALID_ROLE_SYMBOL` otherwise. A role the +/// account authorizes therefore always decodes back into a [`RoleSymbol`], which keeps on-chain +/// authorization and off-chain readers (such as `Authority::try_from_storage`) in agreement. +/// /// ## Usage /// /// Guarding a procedure in MASM so that only members of `MINTER` can call it: diff --git a/crates/miden-standards/src/note/costs/table.rs b/crates/miden-standards/src/note/costs/table.rs index d0dc8056ba..cb6f86be4e 100644 --- a/crates/miden-standards/src/note/costs/table.rs +++ b/crates/miden-standards/src/note/costs/table.rs @@ -2,54 +2,54 @@ // Values are maxima across the benchmarked paths; see `miden_standards::note::costs` for the // caveats on what they do and do not cover. -/// Cycles of consuming a P2ID note: 1 asset 18643, 16 assets 57706 (maximum). -pub const P2ID_CONSUMPTION_CYCLES: u32 = 57706; +/// Cycles of consuming a P2ID note: 1 asset 18693, 16 assets 57846 (maximum). +pub const P2ID_CONSUMPTION_CYCLES: u32 = 57846; -/// Cycles of consuming a P2IDE note: claim 18768, claim with 16 assets 57831 (maximum), reclaim -/// 18923. -pub const P2IDE_CONSUMPTION_CYCLES: u32 = 57831; +/// Cycles of consuming a P2IDE note: claim 18818, claim with 16 assets 57971 (maximum), reclaim +/// 18973. +pub const P2IDE_CONSUMPTION_CYCLES: u32 = 57971; -/// Cycles of consuming a SWAP note: public payback 22728 (maximum), private payback 22214. -pub const SWAP_CONSUMPTION_CYCLES: u32 = 22728; +/// Cycles of consuming a SWAP note: public payback 22806 (maximum), private payback 22286. +pub const SWAP_CONSUMPTION_CYCLES: u32 = 22806; -/// Cycles of consuming a PSWAP note: full fill 25258, partial fill 29234 (maximum). -pub const PSWAP_CONSUMPTION_CYCLES: u32 = 29234; +/// Cycles of consuming a PSWAP note: full fill 25348, partial fill 29376 (maximum). +pub const PSWAP_CONSUMPTION_CYCLES: u32 = 29376; -/// Cycles of consuming a MINT note: fungible faucet 32262, non-fungible faucet 35152 (maximum). -pub const MINT_CONSUMPTION_CYCLES: u32 = 35152; +/// Cycles of consuming a MINT note: fungible faucet 32300, non-fungible faucet 35190 (maximum). +pub const MINT_CONSUMPTION_CYCLES: u32 = 35190; /// Cycles of consuming a BURN note (single benchmarked path). -pub const BURN_CONSUMPTION_CYCLES: u32 = 28776; +pub const BURN_CONSUMPTION_CYCLES: u32 = 28820; /// Cycles of consuming a CONSTANT_FEE_POLICY_CONFIG note (single benchmarked path). -pub const CONSTANT_FEE_POLICY_CONFIG_CONSUMPTION_CYCLES: u32 = 20052; +pub const CONSTANT_FEE_POLICY_CONFIG_CONSUMPTION_CYCLES: u32 = 20108; /// Cycles of consuming a FAUCET_POLICY_CONFIG note (single benchmarked path). -pub const FAUCET_POLICY_CONFIG_CONSUMPTION_CYCLES: u32 = 28632; +pub const FAUCET_POLICY_CONFIG_CONSUMPTION_CYCLES: u32 = 28637; /// Cycles of consuming a FAUCET_METADATA_CONFIG note (single benchmarked path). -pub const FAUCET_METADATA_CONFIG_CONSUMPTION_CYCLES: u32 = 27598; +pub const FAUCET_METADATA_CONFIG_CONSUMPTION_CYCLES: u32 = 27654; /// Cycles of consuming a MIN_BURN_AMOUNT_CONFIG note (single benchmarked path). -pub const MIN_BURN_AMOUNT_CONFIG_CONSUMPTION_CYCLES: u32 = 25943; +pub const MIN_BURN_AMOUNT_CONFIG_CONSUMPTION_CYCLES: u32 = 25999; /// Cycles of consuming an ALLOWLIST_CONFIG note (single benchmarked path). -pub const ALLOWLIST_CONFIG_CONSUMPTION_CYCLES: u32 = 26693; +pub const ALLOWLIST_CONFIG_CONSUMPTION_CYCLES: u32 = 26749; /// Cycles of consuming a BLOCKLIST_CONFIG note (single benchmarked path). -pub const BLOCKLIST_CONFIG_CONSUMPTION_CYCLES: u32 = 26693; +pub const BLOCKLIST_CONFIG_CONSUMPTION_CYCLES: u32 = 26749; /// Cycles of consuming a PAUSE_CONFIG note (single benchmarked path). -pub const PAUSE_CONFIG_CONSUMPTION_CYCLES: u32 = 18643; +pub const PAUSE_CONFIG_CONSUMPTION_CYCLES: u32 = 18699; /// Cycles of consuming an OWNER_CONFIG note (single benchmarked path). -pub const OWNER_CONFIG_CONSUMPTION_CYCLES: u32 = 18237; +pub const OWNER_CONFIG_CONSUMPTION_CYCLES: u32 = 18293; /// Cycles of consuming an RBAC_CONFIG note (single benchmarked path). -pub const RBAC_CONFIG_CONSUMPTION_CYCLES: u32 = 22101; +pub const RBAC_CONFIG_CONSUMPTION_CYCLES: u32 = 22332; /// Cycles of consuming a NETWORK_ACCOUNT_CONFIG note (single benchmarked path). -pub const NETWORK_ACCOUNT_CONFIG_CONSUMPTION_CYCLES: u32 = 19137; +pub const NETWORK_ACCOUNT_CONFIG_CONSUMPTION_CYCLES: u32 = 19193; /// Cycles of consuming a FEE_SPONSORSHIP note (single benchmarked path). -pub const FEE_SPONSORSHIP_CONSUMPTION_CYCLES: u32 = 21463; +pub const FEE_SPONSORSHIP_CONSUMPTION_CYCLES: u32 = 21525; diff --git a/crates/miden-testing/tests/scripts/rbac/mod.rs b/crates/miden-testing/tests/scripts/rbac/mod.rs index abcf3ce920..8090b5f3ca 100644 --- a/crates/miden-testing/tests/scripts/rbac/mod.rs +++ b/crates/miden-testing/tests/scripts/rbac/mod.rs @@ -19,6 +19,7 @@ use miden_protocol::{Felt, Word}; use miden_standards::account::access::{AccessControl, RoleBasedAccessControl, RoleConfig}; use miden_standards::errors::standards::{ ERR_ACCOUNT_NOT_IN_ROLE, + ERR_INVALID_ROLE_SYMBOL, ERR_ROLE_SYMBOL_ZERO, ERR_SENDER_NOT_ROLE_ADMIN, }; @@ -329,6 +330,26 @@ fn set_role_admin_raw_script(role: Felt, admin_role: Felt) -> String { ) } +fn grant_role_raw_script(role: Felt, account_id: AccountId) -> String { + format!( + r#" + use miden::standards::access::rbac + + @note_script + pub proc main + repeat.13 push.0 end + push.{account_prefix} + push.{account_suffix} + push.{role} + call.rbac::grant_role + dropw dropw dropw dropw + end + "#, + account_prefix = account_id.prefix().as_felt(), + account_suffix = account_id.suffix(), + ) +} + // TESTS // ================================================================================================ @@ -695,6 +716,73 @@ async fn test_rbac_non_admin_cannot_set_role_admin() -> anyhow::Result<()> { Ok(()) } +/// On-chain a role is only ever a felt, so the entrypoints enforce the encoding the Rust +/// `RoleSymbol` accepts: without that, a role could be granted and authorized on-chain under a felt +/// that no reader can decode back into a symbol. +/// +/// `27` announces a length of zero (it is the first multiple of the alphabet size), and +/// `MAX_ENCODED_VALUE + 1` runs past the longest encodable symbol; neither names a role. +#[rstest] +#[case::announces_no_characters(Felt::new(27).unwrap())] +#[case::past_the_longest_symbol(Felt::new(4052555153018976253).unwrap())] +#[tokio::test] +async fn test_rbac_grant_role_rejects_non_canonical_role_symbol( + #[case] role_symbol: Felt, +) -> anyhow::Result<()> { + let admin = test_account_id(130); + let member = test_account_id(131); + + let (account, mock_chain) = create_rbac_chain(admin)?; + + let note = build_note(admin, grant_role_raw_script(role_symbol, member))?; + let tx = mock_chain.build_transaction(account).unauthenticated_input_note(note).build()?; + let result = tx.execute().await; + assert_transaction_executor_error!(result, ERR_INVALID_ROLE_SYMBOL); + + Ok(()) +} + +/// The shortest and the longest encodable symbols stay grantable: the encoding check must not +/// narrow the role space the `RoleSymbol` type allows. +#[rstest] +#[case::shortest("A")] +#[case::longest("____________")] +#[tokio::test] +async fn test_rbac_grant_role_accepts_the_encoding_extremes( + #[case] symbol: &str, +) -> anyhow::Result<()> { + let admin = test_account_id(132); + let member = test_account_id(133); + + let edge_role = role(symbol); + let (account, mock_chain) = create_rbac_chain(admin)?; + + let note = build_note(admin, grant_role_script(&edge_role, member))?; + let updated = execute_note_and_apply(&mock_chain, &account, ¬e).await?; + assert!(is_role_member(&updated, &edge_role, member)?); + + Ok(()) +} + +/// A delegated admin is a role like any other, so `set_role_admin` holds its symbol to the same +/// encoding. Zero stays accepted: it clears the delegation. +#[tokio::test] +async fn test_rbac_set_role_admin_rejects_non_canonical_admin_role_symbol() -> anyhow::Result<()> { + let admin = test_account_id(134); + + let minter = role("MINTER"); + + let (account, mock_chain) = create_rbac_chain(admin)?; + + let note = + build_note(admin, set_role_admin_raw_script(Felt::from(&minter), Felt::new(27).unwrap()))?; + let tx = mock_chain.build_transaction(account).unauthenticated_input_note(note).build()?; + let result = tx.execute().await; + assert_transaction_executor_error!(result, ERR_INVALID_ROLE_SYMBOL); + + Ok(()) +} + #[tokio::test] async fn test_rbac_set_role_admin_rejects_zero_role_symbol() -> anyhow::Result<()> { let admin = test_account_id(92);