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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [BREAKING] Added cycle counts to notes returned by `NoteConsumptionInfo` and removed public fields from related types ([#2772](https://github.com/0xMiden/miden-base/issues/2772)).
- [BREAKING] Removed unused `payback_attachment` from `SwapNoteStorage` and `attachment` from `MintNoteStorage` ([#2789](https://github.com/0xMiden/protocol/pull/2789)).
- Automatically enable `concurrent` feature in `miden-tx` for `std` context ([#2791](https://github.com/0xMiden/protocol/pull/2791)).
- Added `Pausable` standard component with `pause`, `unpause`, `is_paused` procedures and `on_before_asset_added_to_account`, `on_before_asset_added_to_note` callbacks ([#2793](https://github.com/0xMiden/protocol/pull/2793)).
- Added `TransactionScript::from_package()` method to create `TransactionScript` from `miden-mast-package::Package` ([#2779](https://github.com/0xMiden/protocol/pull/2779)).

### Fixes
Expand Down
10 changes: 10 additions & 0 deletions crates/miden-standards/asm/account_components/utils/pausable.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# The MASM code of the Pausable account component.
#
# NOTE: This is a temporary no-auth variant of the component for testing purposes.
# It is intended to be replaced by dedicated owner and role-based access control wrappers.

pub use ::miden::standards::utils::pausable::is_paused
pub use ::miden::standards::utils::pausable::pause
pub use ::miden::standards::utils::pausable::unpause
pub use ::miden::standards::utils::pausable::on_before_asset_added_to_account
pub use ::miden::standards::utils::pausable::on_before_asset_added_to_note
192 changes: 192 additions & 0 deletions crates/miden-standards/asm/standards/utils/pausable.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# miden::standards::utils::pausable
#
# Minimal pause flag storage and helpers. Pause/unpause procedures do not perform authorization.
# Compose with ownable2step or role-based access control in a higher layer.
#
# Asset callbacks enforce "not paused" when the issuing faucet has callbacks enabled on the asset.

use miden::core::word
use miden::protocol::active_account
use miden::protocol::native_account

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

# The slot where the paused flag is stored as a single word.
# Unpaused: [0, 0, 0, 0]. Paused: [1, 0, 0, 0].
const IS_PAUSED_SLOT = word("miden::standards::utils::pausable::is_paused")

const PAUSED_WORD = [1, 0, 0, 0]

const UNPAUSED_WORD = [0, 0, 0, 0]

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

const ERR_PAUSABLE_ENFORCED_PAUSE = "the contract is paused"

const ERR_PAUSABLE_EXPECTED_PAUSE = "the contract is not paused"

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

#! Returns whether the account is currently paused.
#!
#! Reads [`IS_PAUSED_SLOT`] on the active account and returns `1` if the stored word is non-zero
#! (paused) or `0` if it is the zero word (unpaused).
#!
#! Inputs: [pad(16)]
#! Outputs: [is_paused, pad(15)]
#!
#! Invocation: call
pub proc is_paused
push.IS_PAUSED_SLOT[0..2]
exec.active_account::get_item
# => [is_paused, 0, 0, 0, pad(16)]

exec.word::eqz not
# => [is_paused, pad(16)]

swap drop
# => [is_paused, pad(15)]
end

#! Sets the paused flag. Fails if already paused.
#!
#! This procedure does not verify the caller. Wrap with access control in the account component
#! composition if only privileged accounts should pause.
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! Invocation: call
pub proc pause
exec.assert_not_paused
# => [pad(16)]

push.PAUSED_WORD
# => [1, 0, 0, 0, pad(16)]

push.IS_PAUSED_SLOT[0..2]
# => [slot_suffix, slot_prefix, 1, 0, 0, 0, pad(16)]

exec.native_account::set_item
# => [OLD_WORD, pad(16)]

dropw
# => [pad(16)]
end

#! Clears the paused flag. Fails if not paused.
#!
#! This procedure does not verify the caller.
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! Invocation: call
pub proc unpause
exec.assert_paused
# => [pad(16)]

push.UNPAUSED_WORD
# => [0, 0, 0, 0, pad(16)]

push.IS_PAUSED_SLOT[0..2]
# => [slot_suffix, slot_prefix, 0, 0, 0, 0, pad(16)]

exec.native_account::set_item
# => [OLD_WORD, pad(16)]

dropw
# => [pad(16)]
end

#! Requires the contract to be unpaused (storage word is the zero word).
#!
#! Reads [`IS_PAUSED_SLOT`] on the active account and applies [`word::eqz`]. If the stored word
#! is non-zero (paused), panics with [`ERR_PAUSABLE_ENFORCED_PAUSE`].
#!
#! Use from other modules or transaction scripts to guard logic that must not run while paused.
#! In asset callback foreign context, the active account is the issuing faucet.
#!
#! Inputs: []
#! Outputs: []
#!
#! Panics if:
#! - the paused-state word is not the zero word.
#!
#! Invocation: exec
pub proc assert_not_paused
push.IS_PAUSED_SLOT[0..2]
exec.active_account::get_item
# => [is_paused, 0, 0, 0]

exec.word::eqz
# => [is_unpaused]

assert.err=ERR_PAUSABLE_ENFORCED_PAUSE
# => []
end

#! Requires the contract to be paused (storage word is not the zero word).
#!
#! Reads [`IS_PAUSED_SLOT`] on the active account, then [`word::eqz`] and inverts. If the
#! stored word is zero (unpaused), panics with [`ERR_PAUSABLE_EXPECTED_PAUSE`].
#!
#! Typical use: guard `unpause` so clearing the flag only happens from a paused state.
#!
#! Inputs: []
#! Outputs: []
#!
#! Panics if:
#! - the paused-state word is the zero word.
#!
#! Invocation: exec
pub proc assert_paused
push.IS_PAUSED_SLOT[0..2]
exec.active_account::get_item
# => [is_paused, 0, 0, 0]

exec.word::eqz not
# => [is_paused]

assert.err=ERR_PAUSABLE_EXPECTED_PAUSE
# => []
end

# CALLBACKS
# ================================================================================================

#! Callback when a callbacks-enabled asset is added to an account vault.
#!
#! Panics if this faucet account is paused (reads `IS_PAUSED_SLOT` on the active account,
#! which is the issuing faucet in the callback foreign context).
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE, pad(8)]
#! Outputs: [ASSET_VALUE, pad(12)]
#!
#! Invocation: call
pub proc on_before_asset_added_to_account
exec.assert_not_paused
# => [ASSET_KEY, ASSET_VALUE, pad(8)]

dropw
# => [ASSET_VALUE, pad(12)]
end

#! Callback when a callbacks-enabled asset is added to an output note.
#!
#! Panics if this faucet account is paused.
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE, note_idx, pad(7)]
#! Outputs: [ASSET_VALUE, pad(12)]
#!
#! Invocation: call
pub proc on_before_asset_added_to_note
exec.assert_not_paused
# => [ASSET_KEY, ASSET_VALUE, note_idx, pad(7)]

dropw
# => [ASSET_VALUE, note_idx, pad(7)]
end
12 changes: 12 additions & 0 deletions crates/miden-standards/src/account/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ static NETWORK_FUNGIBLE_FAUCET_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
Library::read_from_bytes(bytes).expect("Shipped Network Fungible Faucet library is well-formed")
});

// Initialize the Pausable library only once.
static PAUSABLE_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let bytes =
include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/utils/pausable.masl"));
Library::read_from_bytes(bytes).expect("Shipped Pausable library is well-formed")
});

// Initialize the Fungible Token Metadata library only once.
static FUNGIBLE_TOKEN_METADATA_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let bytes = include_bytes!(concat!(
Expand Down Expand Up @@ -169,6 +176,11 @@ pub fn network_fungible_faucet_library() -> Library {
NETWORK_FUNGIBLE_FAUCET_LIBRARY.clone()
}

/// Returns the Pausable component library.
pub fn pausable_library() -> Library {
PAUSABLE_LIBRARY.clone()
}

/// Returns the Fungible Token Metadata Library.
pub fn fungible_token_metadata_library() -> Library {
FUNGIBLE_TOKEN_METADATA_LIBRARY.clone()
Expand Down
1 change: 1 addition & 0 deletions crates/miden-standards/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod faucets;
pub mod interface;
pub mod metadata;
pub mod mint_policies;
pub mod pausable;
pub mod policy_manager;
pub mod wallets;

Expand Down
Loading
Loading