AuthGuardedMultisig and AuthMultisigSmart resolve the fee conversion info from the auth args and then discard it, so accounts using them pay no transaction fee on a fee-charging chain.
Both components contain, verbatim:
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
Expected: they pay the fee, as the other auth components do.
Impact: guarded and smart multisig accounts transact for free while singlesig and plain multisig accounts pay. It fails open, not loudly — the fee moved out of the kernel epilogue into the auth procedure, and neither the batch nor the block kernel validates that a transaction paid one (grep -ri fee crates/miden-tx-batch/src crates/miden-block-prover/src → no matches). So there is no error anywhere; the fee note is simply never created.
Four of six auth components pay:
| component |
exec.fee::pay_fee |
singlesig |
1 |
multisig |
1 |
no_auth |
1 |
network_account |
1 |
guarded_multisig |
0 |
multisig_smart |
0 |
Repro: execute any transaction from a guarded- or smart-multisig account on a chain with a non-zero verification_base_fee. The transaction succeeds and produces no TX_FEE output note. On MockChain::builder().verification_base_fee(500), executed_transaction.output_notes().num_notes() is 0 where the equivalent plain-multisig transaction yields 1.
Origin
The comment was introduced in #3731, which built multisig::resolve_auth_args and moved the fee-paying multisig component off fee::load_conversion_info. The plumbing is in place and the payment step was deferred — the conversion info is already decoded and correct at the point where it is dropped.
Fix: mirror asm/components/auth/multisig/multisig.masm — feed the resolved conversion info and a cycle estimate into fee::pay_fee before the summary is created, so the fee note and the vault withdrawal funding it are covered by the signatures. For the guarded component the estimate needs one extra signer, since the guardian signature is verified in addition to the approvers'.
Note this changes both components' procedure roots, and therefore the code commitment of every account using them.
AuthGuardedMultisigandAuthMultisigSmartresolve the fee conversion info from the auth args and then discard it, so accounts using them pay no transaction fee on a fee-charging chain.Both components contain, verbatim:
Expected: they pay the fee, as the other auth components do.
Impact: guarded and smart multisig accounts transact for free while singlesig and plain multisig accounts pay. It fails open, not loudly — the fee moved out of the kernel epilogue into the auth procedure, and neither the batch nor the block kernel validates that a transaction paid one (
grep -ri fee crates/miden-tx-batch/src crates/miden-block-prover/src→ no matches). So there is no error anywhere; the fee note is simply never created.Four of six auth components pay:
exec.fee::pay_feesinglesigmultisigno_authnetwork_accountguarded_multisigmultisig_smartRepro: execute any transaction from a guarded- or smart-multisig account on a chain with a non-zero
verification_base_fee. The transaction succeeds and produces noTX_FEEoutput note. OnMockChain::builder().verification_base_fee(500),executed_transaction.output_notes().num_notes()is0where the equivalent plain-multisig transaction yields1.Origin
The comment was introduced in #3731, which built
multisig::resolve_auth_argsand moved the fee-paying multisig component offfee::load_conversion_info. The plumbing is in place and the payment step was deferred — the conversion info is already decoded and correct at the point where it is dropped.Fix: mirror
asm/components/auth/multisig/multisig.masm— feed the resolved conversion info and a cycle estimate intofee::pay_feebefore the summary is created, so the fee note and the vault withdrawal funding it are covered by the signatures. For the guarded component the estimate needs one extra signer, since the guardian signature is verified in addition to the approvers'.Note this changes both components' procedure roots, and therefore the code commitment of every account using them.