feat(standards): pay the transaction fee in guarded and smart multisig auth - #3798
Open
mmagician wants to merge 12 commits into
Open
feat(standards): pay the transaction fee in guarded and smart multisig auth#3798mmagician wants to merge 12 commits into
mmagician wants to merge 12 commits into
Conversation
mmagician
commented
Sep 3, 2026
Comment on lines
+81
to
+83
| # one slot beyond the approvers, for the guardian signature. It is unconditional because the | ||
| # rotation path verifies no guardian signature but scans every account procedure instead, which | ||
| # the slot also covers. |
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| # one slot beyond the approvers, for the guardian signature. It is unconditional because the | |
| # rotation path verifies no guardian signature but scans every account procedure instead, which | |
| # the slot also covers. | |
| # one slot beyond the approvers, for the guardian signature. |
unnecessary impl. detail.
We should guard against such bloat in the future, add / modify the MASM skills (separate PR to do this)
Comment on lines
+93
to
+94
| # settle the sponsorship obligation first, in pay_fee's order; the bound below guards the | ||
| # host-supplied rate, which the sponsorship amounts do not depend on |
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| # settle the sponsorship obligation first, in pay_fee's order; the bound below guards the | |
| # host-supplied rate, which the sponsorship amounts do not depend on | |
| # settle the sponsorship obligation first |
Comment on lines
+57
to
+59
| pub proc auth_tx_multisig_smart(auth_args: word) | ||
| # read the output-note count before the fee payment so the notes it creates can be counted | ||
| exec.tx::get_num_output_notes movdn.4 |
Collaborator
Author
There was a problem hiding this comment.
apply the same doc changes as suggested in the other file
Comment on lines
+69
to
+104
| exec.multisig::get_initial_threshold_and_num_approvers drop | ||
| # => [num_of_approvers, CONVERSION_INFO, block_number, SALT, num_output_notes_before_fee] | ||
|
|
||
| exec.signature::estimate_multisig_authentication_cycles | ||
| # => [num_extra_cycles, CONVERSION_INFO, block_number, SALT, num_output_notes_before_fee] | ||
|
|
||
| exec.fee::estimate_fee | ||
| # => [fee_amount, CONVERSION_INFO, block_number, SALT, num_output_notes_before_fee] | ||
|
|
||
| # settle the sponsorship obligation first, in pay_fee's order; the bound below guards the | ||
| # host-supplied rate, which the sponsorship amounts do not depend on | ||
| exec.tx::get_fee_asset_id exec.fees::create_network_note_sponsorships drop | ||
| # => [fee_amount, CONVERSION_INFO, block_number, SALT, num_output_notes_before_fee] | ||
|
|
||
| dup movdn.5 | ||
| # => [fee_amount, CONVERSION_INFO, fee_amount, block_number, SALT, num_output_notes_before_fee] | ||
|
|
||
| exec.fee::resolve_payment_info | ||
| # => [payment_faucet_id_suffix, payment_faucet_id_prefix, payment_amount, fee_amount, | ||
| # block_number, SALT, num_output_notes_before_fee] | ||
|
|
||
| push.FEE_BOUND_DEN push.FEE_BOUND_NUM | ||
| # => [bound_num, bound_den, payment_faucet_id_suffix, payment_faucet_id_prefix, payment_amount, | ||
| # fee_amount, block_number, SALT, num_output_notes_before_fee] | ||
|
|
||
| exec.fee::assert_fee_bound | ||
| # => [payment_faucet_id_suffix, payment_faucet_id_prefix, payment_amount, block_number, SALT, | ||
| # num_output_notes_before_fee] | ||
|
|
||
| exec.fee::pay_estimated_fee | ||
| # => [block_number, SALT, num_output_notes_before_fee] | ||
|
|
||
| # the notes the fee payment created: the TX_FEE note and one FEE_SPONSORSHIP note per network | ||
| # output note. The procedure policies' note restrictions exclude them. | ||
| exec.tx::get_num_output_notes movup.6 sub | ||
| # => [num_own_output_notes, block_number, SALT] |
Collaborator
Author
There was a problem hiding this comment.
isn't this logic duplicated in crates/miden-standards/asm/components/auth/guarded_multisig/guarded_multisig.masm? If we can re-use it, then let's pull it out to a common helper procedure
mmagician
force-pushed
the
mmagician-claude/port-multisig-pays-fee
branch
from
September 3, 2026 11:02
db6959d to
cf7038d
Compare
mmagician
force-pushed
the
mmagician-claude/port-multisig-pays-fee
branch
from
September 3, 2026 13:46
cf7038d to
6f27e78
Compare
mmagician
force-pushed
the
mmagician-claude/port-multisig-pays-fee
branch
from
September 3, 2026 14:32
6f27e78 to
0611809
Compare
…g auth Port of #3786 to next. Both components run estimate_fee, the sponsorship payment, resolve_payment_info, assert_fee_bound (2/1, native fee asset) and pay_estimated_fee between multisig::resolve_auth_args and the summary, and pass the number of notes the payment created to guardian::verify_signature and multisig_smart::auth_tx, whose note restrictions exclude them. tx_policy::assert_no_output_notes reads the verified count itself. Adaptations to next: the conversion info comes from resolve_auth_args (MultisigAuthArgs) rather than load_conversion_info, so the auth args no longer double as the summary salt; multisig_smart::auth_tx takes (num_own_output_notes, block_number, SALT); the fee asset is read via tx::get_fee_asset_id; the sponsorship wrapper dropped in the fee-split port is spelled out at the call sites; pay_fee reads the fee asset once via an estimate_fee_for_asset helper. Tests build MultisigAuthArgs, share the multisig fixture and signing helpers, and drop the salt-binding assertions. Cost tables and bench-tx.json regenerated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
…tisig auth Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
…sig auth Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
…g auth Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
…ig components Pull the fee-paying block of the guarded and smart multisig auth procedures into multisig::pay_bounded_fee, which takes the number of signers and the conversion info and returns the number of notes the payment created. The fee bound constants and the drain rationale live there once; the components keep only the signer count that differs. Written against fee::estimate_fee taking the fee asset ID, so the fee asset is read once for the estimate and the sponsorship notes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
The estimate records every network note's sponsorship price in a local table, which the sponsorship payment reads back instead of pricing the notes through FPI a second time, mirroring fee::pay_fee. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
AuthMultisig now pays its fee through multisig::pay_bounded_fee, so the host-supplied conversion rate can move at most twice the computed fee out of the vault, in the native fee asset, closing the drain of #3763. The bound's rationale is reworded to cover every multisig component. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
Composing pay_fee from resolve_payment_info and pay_estimated_fee cost every plain fee payer 121 auth cycles and pushed the two-P2ID ECDSA consumption into the next padded-trace bracket. Only the bounded multisig payment composes the two procedures; pay_fee keeps its inline tail. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
The sponsorship price table now holds one fee asset value word per output note, so the local table grows to fees::SPONSORSHIP_PRICE_TABLE_NUM_ELEMENTS as in fee::pay_fee. The pointer plumbing is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
mmagician
force-pushed
the
mmagician-claude/port-multisig-pays-fee
branch
from
September 3, 2026 15:10
0611809 to
78fe2df
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Port of #3786 to
next.On
nextthe guarded and smart multisig components decode their auth args but drop the conversion info and pay no fee. Both paths can authenticate below the account's spending quorum (guardian key rotation, or a procedure with a reduced per-procedure threshold), and the conversion rate is host-supplied, so paying the fee unbounded would let such an authorization move arbitrary value out of the vault as a fee note. This wires in the bounded flow from #3797 instead.Notes
Differs from the rc version in following
next's auth-args scheme: the conversion info comes fromresolve_auth_argsrather thanload_conversion_info, so the auth args no longer double as the summary salt, and the summary is bound to the block number as onnext.AuthMultisigcomponent is bounded too, through the samemultisig::pay_bounded_feehelper the guarded and smart components share, so this closes AuthMultisig: unbounded fee conversion lets approvers drain the vault via the fee note #3763 onnext(the rc counterpart is feat(standards): bound the fee payment of the plain multisig component #3802). The MAST roots of all three multisig components change, so accounts built on them get new code commitments.