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 @@ -5,6 +5,8 @@
### Changes

- [BREAKING] Renamed `ProvenBatch::new` to `new_unchecked` ([#2687](https://github.com/0xMiden/miden-base/issues/2687)).
- [BREAKING] Renamed the multisig PSM auth component, procedures, storage paths, and related APIs from `psm` to `guardian`.
- [BREAKING] Renamed the guarded multisig component-facing APIs from `multisig_guardian` / `AuthMultisigGuardian` to `guarded_multisig` / `AuthGuardedMultisig`, while retaining the `guardian` auth namespace and guardian-specific procedures.

## 0.14.0 (2026-03-23)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# The MASM code of the Multi-Signature Authentication Component with Private State Manager.
# The MASM code of the Multi-Signature Authentication component integrated with a state guardian.
#
# See the `AuthMultisigPsm` Rust type's documentation for more details.
# See the `AuthGuardedMultisig` Rust type's documentation for more details.

use miden::standards::auth::multisig
use miden::standards::auth::psm
use miden::standards::auth::guardian

pub use multisig::update_signers_and_threshold
pub use multisig::get_threshold_and_num_approvers
pub use multisig::set_procedure_threshold
pub use multisig::get_signer_at
pub use multisig::is_signer

pub use psm::update_psm_public_key
pub use guardian::update_guardian_public_key

#! Authenticate a transaction with multi-signature support and optional PSM verification.
#! Authenticate a transaction with multi-signature support and optional guardian verification.
#!
#! Inputs:
#! Operand stack: [SALT]
Expand All @@ -22,14 +22,14 @@ pub use psm::update_psm_public_key
#!
#! Invocation: call
@auth_script
pub proc auth_tx_multisig_psm(salt: word)
pub proc auth_tx_guarded_multisig(salt: word)
exec.multisig::auth_tx
# => [TX_SUMMARY_COMMITMENT]

dupw
# => [TX_SUMMARY_COMMITMENT, TX_SUMMARY_COMMITMENT]

exec.psm::verify_signature
exec.guardian::verify_signature
# => [TX_SUMMARY_COMMITMENT]

exec.multisig::assert_new_tx
Expand Down
162 changes: 162 additions & 0 deletions crates/miden-standards/asm/standards/auth/guardian.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# State Guardian account component.
# This component is composed into account auth flows especially for multisig and adds
# an extra signature check by a dedicated guardian signer.
#
# A state guardian can help coordinate state availability for private accounts.

use miden::protocol::auth::AUTH_UNAUTHORIZED_EVENT
use miden::protocol::native_account
use miden::standards::auth::tx_policy
use miden::standards::auth::signature

# IMPORTANT SECURITY NOTES
# --------------------------------------------------------------------------------
# - By default, exactly one valid guardian signature is required.
# - If `update_guardian_public_key` is the only non-auth account procedure called in the current
# transaction, `verify_signature` skips the guardian signature check so key rotation can proceed
# without the old guardian signer.
# - `update_guardian_public_key` rotates the guardian public key and corresponding
# scheme id using the fixed map key `GUARDIAN_MAP_KEY`.


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

# Storage Slots
#
# This authentication component uses named storage slots.
# - GUARDIAN_PUBLIC_KEYS_SLOT (map):
# GUARDIAN_MAP_KEY => GUARDIAN_PUBLIC_KEY
# where: GUARDIAN_MAP_KEY = [0, 0, 0, 0]
#
# - GUARDIAN_SCHEME_ID_SLOT (map):
# GUARDIAN_MAP_KEY => [scheme_id, 0, 0, 0]
# where: GUARDIAN_MAP_KEY = [0, 0, 0, 0]

# The slot in this component's storage layout where the guardian public key map is stored.
# Map entries: [GUARDIAN_MAP_KEY] => [GUARDIAN_PUBLIC_KEY]
const GUARDIAN_PUBLIC_KEYS_SLOT = word("miden::standards::auth::guardian::pub_key")

# The slot in this component's storage layout where the scheme id for the corresponding guardian
# public key map is stored.
# Map entries: [GUARDIAN_MAP_KEY] => [scheme_id, 0, 0, 0]
const GUARDIAN_SCHEME_ID_SLOT = word("miden::standards::auth::guardian::scheme")

# Single-entry storage map key where guardian signer data is stored.
const GUARDIAN_MAP_KEY = [0, 0, 0, 0]

# ERRORS
# -------------------------------------------------------------------------------------------------
const ERR_INVALID_GUARDIAN_SIGNATURE = "invalid guardian signature"

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

#! Updates the guardian public key.
#!
#! Inputs: [new_guardian_scheme_id, NEW_GUARDIAN_PUBLIC_KEY]
#! Outputs: []
#!
#! Notes:
#! - This procedure only updates the guardian public key and corresponding scheme id.
#! - `verify_signature` skips guardian verification only when this is the only non-auth account
#! procedure called in the transaction.
#!
#! Invocation: call
@locals(1)
pub proc update_guardian_public_key(new_guardian_scheme_id: felt, new_guardian_public_key: word)
# Validate supported signature scheme before committing it to storage.
dup exec.signature::assert_supported_scheme
# => [new_guardian_scheme_id, NEW_GUARDIAN_PUBLIC_KEY]

loc_store.0
# => [NEW_GUARDIAN_PUBLIC_KEY]

push.GUARDIAN_MAP_KEY
# => [GUARDIAN_MAP_KEY, NEW_GUARDIAN_PUBLIC_KEY]

push.GUARDIAN_PUBLIC_KEYS_SLOT[0..2]
# => [guardian_pubkeys_slot_prefix, guardian_pubkeys_slot_suffix,
# GUARDIAN_MAP_KEY, NEW_GUARDIAN_PUBLIC_KEY]

exec.native_account::set_map_item
# => [OLD_GUARDIAN_PUBLIC_KEY]

dropw
# => []

# Store new scheme id as [scheme_id, 0, 0, 0] in the single-entry map.
loc_load.0
# => [scheme_id]

push.0.0.0 movup.3
# => [NEW_GUARDIAN_SCHEME_ID_WORD]

push.GUARDIAN_MAP_KEY
# => [GUARDIAN_MAP_KEY, NEW_GUARDIAN_SCHEME_ID_WORD]

push.GUARDIAN_SCHEME_ID_SLOT[0..2]
# => [guardian_scheme_slot_prefix, guardian_scheme_slot_suffix,
# GUARDIAN_MAP_KEY, NEW_GUARDIAN_SCHEME_ID_WORD]

exec.native_account::set_map_item
# => [OLD_GUARDIAN_SCHEME_ID_WORD]

dropw
# => []
end

#! Conditionally verifies a guardian signature.
#!
#! Inputs: [MSG]
#! Outputs: []
#!
#! Panics if:
#! - `update_guardian_public_key` is called together with another non-auth account procedure.
#! - `update_guardian_public_key` was not called and a valid guardian signature is missing or
#! invalid.
#!
#! Invocation: exec
pub proc verify_signature(msg: word)
procref.update_guardian_public_key
# => [UPDATE_GUARDIAN_PUBLIC_KEY_ROOT, MSG]

exec.native_account::was_procedure_called
# => [was_update_guardian_public_key_called, MSG]

if.true
exec.tx_policy::assert_only_one_non_auth_procedure_called
# => [MSG]

exec.tx_policy::assert_no_input_or_output_notes
# => [MSG]

dropw
# => []
else
push.1
# => [1, MSG]

push.GUARDIAN_PUBLIC_KEYS_SLOT[0..2]
# => [guardian_pubkeys_slot_prefix, guardian_pubkeys_slot_suffix, 1, MSG]

push.GUARDIAN_SCHEME_ID_SLOT[0..2]
# => [guardian_scheme_slot_prefix, guardian_scheme_slot_suffix,
# guardian_pubkeys_slot_prefix, guardian_pubkeys_slot_suffix, 1, MSG]

exec.signature::verify_signatures
# => [num_verified_signatures, MSG]

neq.1
# => [is_not_exactly_one, MSG]

if.true
emit.AUTH_UNAUTHORIZED_EVENT
push.0 assert.err=ERR_INVALID_GUARDIAN_SIGNATURE
end
# => [MSG]

dropw
# => []
end
end
2 changes: 1 addition & 1 deletion crates/miden-standards/asm/standards/auth/multisig.masm
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,6 @@ pub proc auth_tx(salt: word)
end

# TX_SUMMARY_COMMITMENT is returned so wrappers can run optional checks
# (e.g. PSM) before replay-protection finalization.
# (e.g. guardian verification) before replay-protection finalization.
# => [TX_SUMMARY_COMMITMENT]
end
158 changes: 0 additions & 158 deletions crates/miden-standards/asm/standards/auth/psm.masm

This file was deleted.

Loading