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 @@ -4,6 +4,8 @@

### Features

- [BREAKING] `AuthMultisig` now bounds its fee payment via `fee::assert_fee_bound` to the native fee asset at at most twice the computed fee, closing the fee drain of [#3763](https://github.com/0xMiden/protocol/issues/3763); its code commitment changes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- [BREAKING] `AuthMultisig` now bounds its fee payment via `fee::assert_fee_bound` to the native fee asset at at most twice the computed fee, closing the fee drain of [#3763](https://github.com/0xMiden/protocol/issues/3763); its code commitment changes.

This is already mentioned in the following entry so no need to explicitly state it separately imo

- [BREAKING] The `AuthGuardedMultisig` and `AuthMultisigSmart` components now pay the transaction fee, bounded via `fee::assert_fee_bound` to the native fee asset at at most twice the computed fee; `tx_policy::assert_no_output_notes` takes the number of output notes the caller created itself ([#3786](https://github.com/0xMiden/protocol/pull/3786)).
- Added `active_note::get_storage_info` and `active_note::get_bounded_storage`, and switched the standard and agglayer note scripts with a bounded storage layout over to the latter ([#3563](https://github.com/0xMiden/protocol/pull/3563)).
- [BREAKING] AggLayer bridge and faucet accounts now map note repricing to an initial `FEE_MNGR` role instead of the built-in `ADMIN` role ([#3571](https://github.com/0xMiden/protocol/issues/3571)).
- [BREAKING] AggLayer bridge accounts now map emergency pause to an initial `PAUSER` role, while unpause remains restricted to `ADMIN` ([#3572](https://github.com/0xMiden/protocol/issues/3572)).
Expand Down
14 changes: 7 additions & 7 deletions bin/bench-transaction/bench-tx.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"core_rows": 81445,
"chiplets_rows": 11504,
"poseidon2_permutation_rows": 55392,
"range_rows": 20645,
"range_rows": 20537,
"chiplets_shape": {
"hasher_rows": 8656,
"bitwise_rows": 656,
Expand All @@ -39,7 +39,7 @@
"core_rows": 13682,
"chiplets_rows": 5872,
"poseidon2_permutation_rows": 19888,
"range_rows": 1969,
"range_rows": 1989,
"chiplets_shape": {
"hasher_rows": 4224,
"bitwise_rows": 848,
Expand All @@ -65,7 +65,7 @@
"core_rows": 84045,
"chiplets_rows": 13403,
"poseidon2_permutation_rows": 55648,
"range_rows": 20515,
"range_rows": 20265,
"chiplets_shape": {
"hasher_rows": 10072,
"bitwise_rows": 1032,
Expand All @@ -91,7 +91,7 @@
"core_rows": 16282,
"chiplets_rows": 7771,
"poseidon2_permutation_rows": 20144,
"range_rows": 1413,
"range_rows": 1427,
"chiplets_shape": {
"hasher_rows": 5640,
"bitwise_rows": 1224,
Expand All @@ -114,7 +114,7 @@
"core_rows": 79635,
"chiplets_rows": 10916,
"poseidon2_permutation_rows": 53280,
"range_rows": 20351,
"range_rows": 20457,
"chiplets_shape": {
"hasher_rows": 8240,
"bitwise_rows": 616,
Expand All @@ -137,7 +137,7 @@
"core_rows": 11872,
"chiplets_rows": 5284,
"poseidon2_permutation_rows": 17728,
"range_rows": 1237,
"range_rows": 1217,
"chiplets_shape": {
"hasher_rows": 3808,
"bitwise_rows": 808,
Expand Down Expand Up @@ -863,7 +863,7 @@
"core_rows": 17359,
"chiplets_rows": 7868,
"poseidon2_permutation_rows": 23776,
"range_rows": 1517,
"range_rows": 1529,
"chiplets_shape": {
"hasher_rows": 5824,
"bitwise_rows": 1160,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#
# See the `AuthGuardedMultisig` Rust type's documentation for more details.

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

pub use {update_signers_and_threshold} from miden::standards::auth::multisig
pub use {get_threshold_and_num_approvers} from miden::standards::auth::multisig
Expand All @@ -13,7 +13,16 @@ pub use {is_signer} from miden::standards::auth::multisig

pub use {update_guardian_public_key} from miden::standards::auth::guardian

#! Authenticate a transaction with multi-signature support and optional guardian verification.
#! Authenticate a transaction with multi-signature support and guardian verification, paying the
#! transaction fee in the process.
#!
#! The fee is paid before the transaction summary is created, bounded and in the native fee asset
#! (see multisig::pay_bounded_fee), so the fee note and the vault withdrawal funding it are covered
#! by the approver and guardian signatures.
#!
#! The guardian signature is verified in addition to the approvers' (see
#! guardian::verify_signature), except on the guardian key rotation path, which instead requires
#! that the transaction create no notes beyond the ones the fee payment creates.
#!
#! The guardian must not be an approver, otherwise a single signature would satisfy both the
#! multisig and the guardian check, so this procedure re-checks the invariant that
Expand All @@ -28,6 +37,8 @@ pub use {update_guardian_public_key} from miden::standards::auth::guardian
#! Operand stack: []
#!
#! Panics if:
#! - the auth args cannot be resolved, see `multisig::resolve_auth_args`.
#! - the fee payment fails, see `multisig::pay_bounded_fee`.
#! - insufficient number of valid approver or guardian signatures.
#! - the approval window ended at or before the transaction reference block, see
#! `multisig::auth_tx`.
Expand All @@ -39,15 +50,35 @@ pub proc auth_tx_guarded_multisig(auth_args: word)
exec.multisig::resolve_auth_args
# => [CONVERSION_INFO, block_number, SALT]

# this component does not pay the transaction fee yet, so the conversion info is unused
dropw
# => [block_number, SALT]
# Pay the transaction fee before the summary is created so that the TX_FEE note and the vault
# withdrawal funding it are covered by the approver and guardian signatures.
# ---------------------------------------------------------------------------------------------

exec.multisig::get_initial_threshold_and_num_approvers drop
# => [num_of_approvers, CONVERSION_INFO, block_number, SALT]

# one slot beyond the approvers, for the guardian signature.
add.1
# => [num_of_signers, CONVERSION_INFO, block_number, SALT]

exec.multisig::pay_bounded_fee
# => [num_own_output_notes, block_number, SALT]

# the rotation path excludes the notes the payment created from its no-output-notes check
movdn.5
# => [block_number, SALT, num_own_output_notes]

# Authenticate the transaction and record it for replay protection.
# ---------------------------------------------------------------------------------------------

exec.multisig::auth_tx
# => [TX_SUMMARY_COMMITMENT]
# => [TX_SUMMARY_COMMITMENT, num_own_output_notes]

dupw
# => [TX_SUMMARY_COMMITMENT, TX_SUMMARY_COMMITMENT]
# => [TX_SUMMARY_COMMITMENT, TX_SUMMARY_COMMITMENT, num_own_output_notes]

movup.8
# => [num_own_output_notes, TX_SUMMARY_COMMITMENT, TX_SUMMARY_COMMITMENT]

exec.guardian::verify_signature
# => [TX_SUMMARY_COMMITMENT]
Expand Down
17 changes: 7 additions & 10 deletions crates/miden-standards/asm/components/auth/multisig/multisig.masm
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
# See the `AuthMultisig` Rust type's documentation for more details.

use miden::standards::auth::multisig
use miden::standards::auth::signature
use miden::standards::fee

pub use {update_signers_and_threshold} from miden::standards::auth::multisig
pub use {get_threshold_and_num_approvers} from miden::standards::auth::multisig
Expand All @@ -16,10 +14,9 @@ pub use {is_signer} from miden::standards::auth::multisig
#! process.
#!
#! It resolves the AUTH_ARGS into the block the summary binds, the summary salt and the fee
#! conversion info, then pays the transaction fee by creating a public TX_FEE note. On chains with
#! a zero verification base fee no note is created. Because the fee is paid before the transaction
#! summary is created, the fee note and the vault withdrawal funding it are covered by the approver
#! signatures.
#! conversion info, then pays the transaction fee before the summary is created, bounded and in
#! the native fee asset (see multisig::pay_bounded_fee), so the fee note and the vault withdrawal
#! funding it are covered by the approver signatures.
#!
#! Inputs:
#! Operand stack: [AUTH_ARGS]
Expand All @@ -28,6 +25,8 @@ pub use {is_signer} from miden::standards::auth::multisig
#! Operand stack: []
#!
#! Panics if:
#! - the auth args cannot be resolved, see `multisig::resolve_auth_args`.
#! - the fee payment fails, see `multisig::pay_bounded_fee`.
#! - insufficient number of valid signatures (below threshold).
#! - the approval window ended at or before the transaction reference block, see
#! `multisig::auth_tx`.
Expand All @@ -45,10 +44,8 @@ pub proc auth_tx_multisig(auth_args: word)
exec.multisig::get_initial_threshold_and_num_approvers drop
# => [num_of_approvers, CONVERSION_INFO, block_number, SALT]

exec.signature::estimate_multisig_authentication_cycles
# => [num_extra_cycles, CONVERSION_INFO, block_number, SALT]

exec.fee::pay_fee drop
# the notes the payment created need no accounting here
exec.multisig::pay_bounded_fee drop
# => [block_number, SALT]

# Authenticate the transaction and record it for replay protection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ pub use {is_signer} from miden::standards::auth::multisig
pub use {set_procedure_policy} from miden::standards::auth::multisig_smart
pub use {update_signers_and_threshold} from miden::standards::auth::multisig_smart

#! Authenticate a transaction using multisig smart-policy rules.
#! Authenticate a transaction using multisig smart-policy rules, paying the transaction fee in the
#! process.
#!
#! The fee is paid before the transaction summary is created, bounded and in the native fee asset
#! (see multisig::pay_bounded_fee), so the fee note and the vault withdrawal funding it are covered
#! by the approver signatures. The notes the payment creates are excluded from the procedure
#! policies' note restrictions, so a policy forbidding output notes stays satisfiable on a
#! fee-charging chain.
#!
#! Inputs:
#! Operand stack: [AUTH_ARGS]
Expand All @@ -20,7 +27,10 @@ pub use {update_signers_and_threshold} from miden::standards::auth::multisig_sma
#! Operand stack: []
#!
#! Panics if:
#! - insufficient number of valid signatures (below threshold).
#! - the auth args cannot be resolved, see `multisig::resolve_auth_args`.
#! - the fee payment fails, see `multisig::pay_bounded_fee`.
#! - a called procedure's policy forbids the transaction's input or output notes, or the number of
#! valid signatures is below the threshold, see `multisig_smart::auth_tx`.
#! - the approval window ended at or before the transaction reference block, see
#! `multisig_smart::auth_tx`.
#!
Expand All @@ -30,9 +40,18 @@ pub proc auth_tx_multisig_smart(auth_args: word)
exec.multisig::resolve_auth_args
# => [CONVERSION_INFO, block_number, SALT]

# this component does not pay the transaction fee yet, so the conversion info is unused
dropw
# => [block_number, SALT]
# Pay the transaction fee before the summary is created so that the TX_FEE note and the vault
# withdrawal funding it are covered by the approver signatures.
# ---------------------------------------------------------------------------------------------

exec.multisig::get_initial_threshold_and_num_approvers drop
# => [num_of_approvers, CONVERSION_INFO, block_number, SALT]

exec.multisig::pay_bounded_fee
# => [num_own_output_notes, block_number, SALT]
Comment thread
mmagician marked this conversation as resolved.

# Authenticate the transaction and record it for replay protection.
# ---------------------------------------------------------------------------------------------

exec.multisig_smart::auth_tx
# => [TX_SUMMARY_COMMITMENT]
Expand Down
23 changes: 18 additions & 5 deletions crates/miden-standards/asm/standards/auth/guardian.masm
Original file line number Diff line number Diff line change
Expand Up @@ -135,27 +135,37 @@ end

#! Conditionally verifies a guardian signature.
#!
#! Inputs: [MSG]
#! Inputs: [num_own_output_notes, MSG]
#! Outputs: []
#!
#! Where:
#! - num_own_output_notes is the number of output notes the caller itself created, e.g. to pay the
#! transaction fee (see `tx_policy::assert_no_output_notes`). It is only consulted on the
#! guardian key rotation path.
#! - MSG is the message the guardian signs, i.e. the transaction summary commitment.
#!
#! Panics if:
#! - `update_guardian_public_key` is called together with another non-auth account procedure.
#! - `update_guardian_public_key` was called and the transaction consumes input notes or created
#! output notes the caller did not create itself.
#! - `update_guardian_public_key` was not called and a valid guardian signature is missing or
#! invalid.
#!
#! Invocation: exec
pub proc verify_signature(msg: word)
pub proc verify_signature(num_own_output_notes: u16, msg: word)
procref.update_guardian_public_key
# => [UPDATE_GUARDIAN_PUBLIC_KEY_ROOT, MSG]
# => [UPDATE_GUARDIAN_PUBLIC_KEY_ROOT, num_own_output_notes, MSG]

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

if.true
# Check the notes first: creating an output note requires calling `create_note`, which
# would also trip `assert_only_one_non_auth_procedure_called`. Running that check last lets
# the more specific input/output-note errors surface when notes are present.
exec.tx_policy::assert_no_input_notes
# => [num_own_output_notes, MSG]

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

Expand All @@ -165,6 +175,9 @@ pub proc verify_signature(msg: word)
dropw
# => []
else
drop
# => [MSG]

push.1
# => [1, MSG]

Expand Down
Loading
Loading