Skip to content

feat(standards): add the pass-through auth component - #3733

Draft
mmagician wants to merge 8 commits into
mmagician-claude/pass-through-tx-scriptfrom
mmagician-claude/pass-through-auth
Draft

feat(standards): add the pass-through auth component#3733
mmagician wants to merge 8 commits into
mmagician-claude/pass-through-tx-scriptfrom
mmagician-claude/pass-through-auth

Conversation

@mmagician

Copy link
Copy Markdown
Collaborator

Stacked on #3713 - review that first; this diff is only the auth component and what follows from it.

Closes the second half of #3709, "skipping fee note creation".

What it does

AuthPassThrough asserts the account's commitment is the one the transaction started with, never increments the nonce, and creates no TX_FEE note. That is what makes a pass-through account immutable: nothing can alter it, so transactions against it never conflict and a batch builder can append one to every batch it builds concurrently.

With NoAuth neither property held. Any third party could run a transaction against the account and change its state, breaking the concurrency guarantee independently of the script; and on a fee-charging chain the auth procedure funded a TX_FEE note out of the account's vault, which is both a state change and impossible for an account the script has just emptied.

What follows from it

  • assert_vault_unchanged is removed. The auth procedure's commitment check subsumes it - it runs after the tx script and also covers storage - so the script's own call became redundant. The script's docs say plainly that the guarantee is a property of the account composition, which the script cannot verify.
  • The action component is renamed PassThroughSweep (miden::standards::pass_through::sweep), since it is down to a single procedure and other pass-through actions need somewhere to land.
  • Both components are registered with StandardAccountComponent and AccountComponentInterface. Without the auth variant, AccountInterface::new finds no auth component and panics - AccountInterface::from_account would have been a panic on valid input.

Worth reviewing

  • The account authenticates nothing. Anyone can execute a transaction against it and choose where the assets passing through go. Assets are only safe in transit if the input note's own script constrains its destination, or if they were already unrestricted - TX_FEE notes are the latter, which is why fee forwarding is sound while routing a P2ID note through such an account would not be.
  • Fee-lessness is a property of the procedure, not the transaction. The assert requires a zero net vault delta, not the absence of withdrawals, so another script could still route assets an input note deposited into a fee note of its own.
  • Such an account cannot be created by a transaction - the nonce is never incremented and the kernel rejects a zero nonce - so it has to be provisioned out of band. Any asset it holds is then permanently unspendable. Pinned by pass_through_auth_cannot_create_an_account.
  • Fee-less transactions need a batch builder that accepts them. Nothing in the protocol prices them; that is the node-side work in 0xMiden/node#2501.

Testing

Three tests in crates/miden-testing/tests/auth/pass_through.rs: a transaction leaving a deposit in the account is rejected, an account-creating transaction fails on the nonce, and on a chain with a non-zero verification_base_fee the only output is the P2ID note with the account unchanged. Plus an interface test pinning the component registration that would otherwise panic. The existing script tests move over to AuthPassThrough. make lint and make doc clean; miden-standards and miden-testing green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB

claude added 8 commits August 25, 2026 16:25
`AuthPassThrough` asserts the account's commitment is the one the transaction
started with, never increments the nonce, and pays no transaction fee. That is
what makes a pass-through account immutable: nothing can alter it, so
transactions against it never conflict and batches can be built concurrently.
With `NoAuth` any third party could mutate the account, and on a fee-charging
chain it funded a TX_FEE note out of the account's vault - the "skipping fee
note creation" half of #3709.

With the invariant enforced by the auth procedure, the script's own
`assert_vault_unchanged` call becomes redundant and is removed, leaving the
action component with a single procedure; it is renamed `PassThroughSweep`
(`miden::standards::pass_through::sweep`) so the other actions a pass-through
script might call have somewhere to land.

Registers both components with `StandardAccountComponent` and
`AccountComponentInterface`. Without the auth variant `AccountInterface::new`
finds no auth component and panics on any pass-through account.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB
The script's MASM contract promised a vault-unchanged guarantee it delegated to
the auth procedure and cannot verify. Says so, and spells out the two silent
failure modes on an account composed differently.

Also corrects the fee claim: a FEE_SPONSORSHIP note could in principle fund a
fee note, so the accurate statement is that this procedure creates none.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB
The auth procedure asserts a zero net vault delta, not the absence of
withdrawals, so "an account that cannot change its state cannot fund a fee
note" does not follow: another script could route assets an input note
deposited into a TX_FEE note of its own and still pass the assert. Scopes the
claim to what the procedure and the canonical script actually guarantee.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB
Third copy of the claim the previous commit corrected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB
Rebase fallout: the sweep component is down to one procedure, so its export
test shrinks with it, and the root-stability check needs the renamed component
plus the new auth one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB
`sweep_asset_to_note_root` resolves a hand-written path through a `LazyLock`
that panics on a mismatch, and nothing forced it, so a typo would have surfaced
in a downstream caller rather than in CI. Folds the check into the interface
test that already builds a pass-through account.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB
The MASM doc lost the paragraph the Rust one still carried and was left with a
dangling fragment on the requires list; the component doc had a sentence
clippy read as a list continuation, and one link still pointed at the old
component name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB
Adds an `Auth::PassThrough` testing variant and a test asserting an
account-creating transaction fails with `ERR_EPILOGUE_NONCE_CANNOT_BE_0`, so
the documented limitation is covered rather than only asserted in prose.

Also drops a stale sentence claiming a pre-existing balance is swept out along
with the deposits; `sweep_asset_to_note` asserts against that and fails the
transaction instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyebszMeyUrYBjsJBDLVwB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants