Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
- Added `ProgramExecutor` hooks to support DAP and other custom transaction program executors ([#2574](https://github.com/0xMiden/protocol/pull/2574)).
- Added `create_fungible_key` for construction of fungible asset keys ([#2575](https://github.com/0xMiden/protocol/pull/2575)).
- Added metadata hash storage to AggLayer faucet and FPI retrieval during bridge-out leaf construction ([#2583](https://github.com/0xMiden/protocol/pull/2583)).
- Added `BurnPolicyConfig` for flexible burning policy execution ([#2664](https://github.com/0xMiden/protocol/pull/2664))

- Added `SwapNoteStorage` for typed serialization/deserialization of SWAP note storage ([#2585](https://github.com/0xMiden/protocol/pull/2585)).
- Added `InputNoteCommitment::from_parts()` for construction of input note commitments from a nullifier and optional note header ([#2588](https://github.com/0xMiden/protocol/pull/2588)).
- Added `bool` schema type to the type registry and updated ACL auth component to use it for boolean config fields ([#2591](https://github.com/0xMiden/protocol/pull/2591)).
Expand Down
12 changes: 7 additions & 5 deletions crates/miden-agglayer/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use miden_protocol::account::{
};
use miden_protocol::transaction::TransactionKernel;
use miden_standards::account::auth::NoAuth;
use miden_standards::account::mint_policies::OwnerControlled;
use miden_standards::account::burn_policies::BurnOwnerControlled;
use miden_standards::account::mint_policies::MintOwnerControlled;

// CONSTANTS
// ================================================================================================
Expand Down Expand Up @@ -249,9 +250,9 @@ fn generate_agglayer_constants(
let agglayer_component =
AccountComponent::new(content_library, vec![], dummy_metadata.clone()).unwrap();

// The faucet account includes Ownable2Step and OwnerControlled components
// alongside the agglayer faucet component, since network_fungible::mint_and_send
// requires these for access control.
// The faucet account includes Ownable2Step and OwnerControlled components for mint and burn
// policies alongside the agglayer faucet component, since
// network_fungible::mint_and_send requires these for access control.
let mut components: Vec<AccountComponent> =
vec![AccountComponent::from(NoAuth), agglayer_component];
if lib_name == "faucet" {
Expand All @@ -263,7 +264,8 @@ fn generate_agglayer_constants(
components.push(AccountComponent::from(
miden_standards::account::access::Ownable2Step::new(dummy_owner),
));
components.push(AccountComponent::from(OwnerControlled::owner_only()));
components.push(AccountComponent::from(MintOwnerControlled::owner_only()));
components.push(AccountComponent::from(BurnOwnerControlled::allow_all()));
}

// use `AccountCode` to merge codes of agglayer and authentication components
Expand Down
15 changes: 10 additions & 5 deletions crates/miden-agglayer/src/faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ use miden_protocol::account::{
use miden_protocol::asset::TokenSymbol;
use miden_protocol::errors::AccountIdError;
use miden_standards::account::access::Ownable2Step;
use miden_standards::account::burn_policies::BurnOwnerControlled;
use miden_standards::account::faucets::{FungibleFaucetError, TokenMetadata};
use miden_standards::account::mint_policies::OwnerControlled;
use miden_standards::account::mint_policies::MintOwnerControlled;
use miden_utils_sync::LazyLock;
use thiserror::Error;

Expand Down Expand Up @@ -89,7 +90,8 @@ static METADATA_HASH_HI_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(||
///
/// This component re-exports `network_fungible::mint_and_send`, which requires:
/// - [`Ownable2Step`]: Provides ownership data (bridge account ID as owner).
/// - [`miden_standards::account::mint_policies::OwnerControlled`]: Provides mint policy management.
/// - [`miden_standards::account::mint_policies::MintOwnerControlled`]: Provides mint policy
/// management.
///
/// These must be added as separate components when building the faucet account.
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -363,9 +365,12 @@ impl AggLayerFaucet {
&*METADATA_HASH_HI_SLOT_NAME,
TokenMetadata::metadata_slot(),
Ownable2Step::slot_name(),
OwnerControlled::active_policy_proc_root_slot(),
OwnerControlled::allowed_policy_proc_roots_slot(),
OwnerControlled::policy_authority_slot(),
MintOwnerControlled::active_policy_proc_root_slot(),
MintOwnerControlled::allowed_policy_proc_roots_slot(),
MintOwnerControlled::policy_authority_slot(),
BurnOwnerControlled::active_policy_proc_root_slot(),
BurnOwnerControlled::allowed_policy_proc_roots_slot(),
BurnOwnerControlled::policy_authority_slot(),
]
}
}
Expand Down
10 changes: 7 additions & 3 deletions crates/miden-agglayer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use miden_protocol::note::NoteScript;
use miden_protocol::vm::Program;
use miden_standards::account::access::Ownable2Step;
use miden_standards::account::auth::NoAuth;
use miden_standards::account::mint_policies::OwnerControlled;
use miden_standards::account::burn_policies::BurnOwnerControlled;
use miden_standards::account::mint_policies::MintOwnerControlled;
use miden_utils_sync::LazyLock;

pub mod b2agg_note;
Expand Down Expand Up @@ -207,8 +208,10 @@ pub fn create_existing_bridge_account(
/// The builder includes:
/// - The `AggLayerFaucet` component (conversion metadata + token metadata).
/// - The `Ownable2Step` component (bridge account ID as owner for mint authorization).
/// - The `OwnerControlled` component (mint policy management required by
/// - The `MintOwnerControlled` component (mint policy management required by
/// `network_fungible::mint_and_send`).
/// - The `BurnOwnerControlled` component (burn policy management required by
/// `basic_fungible::burn`).
#[allow(clippy::too_many_arguments)]
fn create_agglayer_faucet_builder(
seed: Word,
Expand Down Expand Up @@ -238,7 +241,8 @@ fn create_agglayer_faucet_builder(
.storage_mode(AccountStorageMode::Network)
.with_component(agglayer_component)
.with_component(Ownable2Step::new(bridge_account_id))
.with_component(OwnerControlled::owner_only())
.with_component(MintOwnerControlled::owner_only())
.with_component(BurnOwnerControlled::allow_all())
Comment on lines +244 to +245
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Judging by this comment (#2724 (comment)), this may have to be BurnOwnerControlled::owner_only(). Maybe @mmagician can double check.

}

/// Creates a new agglayer faucet account with the specified configuration.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The MASM code of the Burn Policy Auth Controlled Account Component.
#
# See the `BurnAuthControlled` Rust type's documentation for more details.

pub use ::miden::standards::burn_policies::allow_all
pub use ::miden::standards::burn_policies::policy_manager::set_burn_policy
pub use ::miden::standards::burn_policies::policy_manager::get_burn_policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# The MASM code of the Burn Policy Owner Controlled Account Component.
#
# See the `OwnerControlled` Rust type's documentation for more details.

# Re-export `allow_all` from `miden::standards::burn_policies` so the
# faucet can switch the active burn policy to allow-all via `set_burn_policy`.
# The auth-controlled component uses the same standards procedure.
pub use ::miden::standards::burn_policies::allow_all
pub use ::miden::standards::burn_policies::owner_controlled::owner_only
pub use ::miden::standards::burn_policies::policy_manager::set_burn_policy
pub use ::miden::standards::burn_policies::policy_manager::get_burn_policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# The MASM code of the Mint Policy Auth Controlled Account Component.
#
# See the `AuthControlled` Rust type's documentation for more details.
# See the `MintAuthControlled` Rust type's documentation for more details.

pub use ::miden::standards::mint_policies::auth_controlled::allow_all
pub use ::miden::standards::mint_policies::allow_all
pub use ::miden::standards::mint_policies::policy_manager::set_mint_policy
pub use ::miden::standards::mint_policies::policy_manager::get_mint_policy
14 changes: 14 additions & 0 deletions crates/miden-standards/asm/standards/burn_policies/mod.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generic burn policy procedures shared by policy manager flows.

# POLICY PROCEDURES
# ================================================================================================

#! Burn policy that accepts every burn request.
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE]
#! Outputs: []
#! Invocation: dynexec
pub proc allow_all
dropw dropw
# => []
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use miden::standards::access::ownable2step

# POLICY PROCEDURES
# ================================================================================================

#! Owner-only burn predicate.
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE]
#! Outputs: []
#!
#! Panics if:
#! - note sender is not owner.
#!
#! Invocation: dynexec
pub proc owner_only
exec.ownable2step::assert_sender_is_owner
# => [ASSET_KEY, ASSET_VALUE]

dropw dropw
# => []
end
207 changes: 207 additions & 0 deletions crates/miden-standards/asm/standards/burn_policies/policy_manager.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use miden::core::word
use miden::protocol::active_account
use miden::protocol::native_account
use miden::standards::access::ownable2step

# DEPENDENCY NOTE
# This manager supports two policy-authority modes:
# - 0: auth_controlled: no Ownable2Step dependency.
# - 1: owner_controlled: requires Ownable2Step component
# (`ownable2step::assert_sender_is_owner`) for `set_burn_policy`.

# CONSTANTS
# ================================================================================================

# Active policy root slot.
# Layout: [PROC_ROOT]
const ACTIVE_POLICY_PROC_ROOT_SLOT=word("miden::standards::burn_policy_manager::active_policy_proc_root")

# Allowlist map slot for policy roots.
# Map entries: [PROC_ROOT] -> [1, 0, 0, 0]
const ALLOWED_POLICY_PROC_ROOTS_SLOT=word("miden::standards::burn_policy_manager::allowed_policy_proc_roots")

# Policy authority slot.
# Layout: [policy_authority, 0, 0, 0]
# - POLICY_AUTHORITY = 0: policy authority rely on `auth_controlled`.
# - POLICY_AUTHORITY = 1: `set_burn_policy` requires Ownable2Step owner check.
const POLICY_AUTHORITY_SLOT=word("miden::standards::burn_policy_manager::policy_authority")
const POLICY_AUTHORITY_OWNER_CONTROLLED=1

# Local memory pointer used to pass a policy root to `dynexec`.
const BURN_POLICY_PROC_ROOT_PTR=0

# ERRORS
# ================================================================================================

const ERR_BURN_POLICY_ROOT_IS_ZERO="burn policy root is zero"
const ERR_BURN_POLICY_ROOT_NOT_IN_ACCOUNT="burn policy root is not a procedure of this account"
const ERR_BURN_POLICY_ROOT_NOT_ALLOWED="burn policy root is not allowed"

# INTERNAL PROCEDURES
# ================================================================================================

#! Reads active burn policy root from storage.
#!
#! Inputs: []
#! Outputs: [BURN_POLICY_ROOT]
#!
#! Invocation: exec
proc get_burn_policy_root
push.ACTIVE_POLICY_PROC_ROOT_SLOT[0..2] exec.active_account::get_item
# => [BURN_POLICY_ROOT]
end

#! Validates policy root before use.
#!
#! Inputs: [BURN_POLICY_ROOT]
#! Outputs: [BURN_POLICY_ROOT]
#!
#! Panics if:
#! - policy root is zero.
#! - policy root is not present in this account's procedures.
#!
#! Invocation: exec
proc assert_existing_policy_root
exec.word::testz
assertz.err=ERR_BURN_POLICY_ROOT_IS_ZERO
# => [BURN_POLICY_ROOT]

dupw exec.active_account::has_procedure
assert.err=ERR_BURN_POLICY_ROOT_NOT_IN_ACCOUNT
# => [BURN_POLICY_ROOT]
end

#! Validates that the policy root is one of the allowed policy roots configured for this account.
#!
#! Inputs: [BURN_POLICY_ROOT]
#! Outputs: [BURN_POLICY_ROOT]
#!
#! Panics if:
#! - policy root is not in the allowed policy roots map.
#!
#! Invocation: exec
proc assert_allowed_policy_root
dupw
push.ALLOWED_POLICY_PROC_ROOTS_SLOT[0..2]
exec.active_account::get_map_item
# => [ALLOWED_FLAG, BURN_POLICY_ROOT]

exec.word::eqz
assertz.err=ERR_BURN_POLICY_ROOT_NOT_ALLOWED
# => [BURN_POLICY_ROOT]
end

#! Reads policy authority mode.
#! - 0 = `auth_controlled`
#! - 1 = `owner_controlled`
#!
#! Inputs: []
#! Outputs: [policy_authority]
#!
#! Invocation: exec
proc get_policy_authority
push.POLICY_AUTHORITY_SLOT[0..2] exec.active_account::get_item
# => [policy_authority, 0, 0, 0]

movdn.3
# => [0, 0, 0, policy_authority]

drop drop drop
# => [policy_authority]
end

#! Authorizes policy update based on policy authority mode.
#!
#! `OwnerControlled` uses Ownable2Step directly rather than delegating
#! authorization to the active burn policy through dynexec. Delegating to the active
#! policy would allow a permissive policy (e.g. allow-all) to let any caller change
#! which burn policy root is active.
#!
#! Inputs: [NEW_POLICY_ROOT]
#! Outputs: [NEW_POLICY_ROOT]
#!
#! Panics if:
#! - POLICY_AUTHORITY = 1 and the sender is not owner.
#!
#! Invocation: exec
proc assert_can_set_burn_policy
exec.get_policy_authority
# => [policy_authority, NEW_POLICY_ROOT]

eq.POLICY_AUTHORITY_OWNER_CONTROLLED
if.true
exec.ownable2step::assert_sender_is_owner
# => [NEW_POLICY_ROOT]
end
end
Comment thread
PhilippGackstatter marked this conversation as resolved.

# PUBLIC INTERFACE
# ================================================================================================

#! Executes active burn policy for the provided asset by dynamic execution.
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE]
#! Outputs: []
#!
#! Panics if:
#! - burn policy root is invalid.
#! - active burn policy validation fails.
#!
#! Invocation: exec
@locals(4)
pub proc execute_burn_policy
exec.get_burn_policy_root
# => [BURN_POLICY_ROOT, ASSET_KEY, ASSET_VALUE]

exec.assert_existing_policy_root
# => [BURN_POLICY_ROOT, ASSET_KEY, ASSET_VALUE]

exec.assert_allowed_policy_root
# => [BURN_POLICY_ROOT, ASSET_KEY, ASSET_VALUE]

loc_storew_le.BURN_POLICY_PROC_ROOT_PTR dropw
# => [ASSET_KEY, ASSET_VALUE]

locaddr.BURN_POLICY_PROC_ROOT_PTR
# => [policy_root_ptr, ASSET_KEY, ASSET_VALUE]

dynexec
# => []
end

#! Returns active burn policy root.
#!
#! Inputs: [pad(16)]
#! Outputs: [BURN_POLICY_ROOT, pad(12)]
#!
#! Invocation: call
pub proc get_burn_policy
exec.get_burn_policy_root
# => [BURN_POLICY_ROOT, pad(12)]
end

#! Sets active burn policy root.
#!
#! Inputs: [NEW_POLICY_ROOT, pad(12)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - POLICY_AUTHORITY = 1 and the sender is not owner.
#! - NEW_POLICY_ROOT is zero.
#! - NEW_POLICY_ROOT is not a procedure of this account.
#! - NEW_POLICY_ROOT is not in the allowed roots map.
#!
#! Invocation: call
pub proc set_burn_policy
exec.assert_can_set_burn_policy
# => [NEW_POLICY_ROOT, pad(12)]

exec.assert_existing_policy_root
# => [NEW_POLICY_ROOT, pad(12)]

exec.assert_allowed_policy_root
# => [NEW_POLICY_ROOT, pad(12)]

push.ACTIVE_POLICY_PROC_ROOT_SLOT[0..2] exec.native_account::set_item dropw
# => [pad(16)]
end
Loading
Loading