0xMiden / 0xMiden/protocol

Unconsumed Administrative Action Notes Retain Authority Indefinitely

Aberta
#3,560 5 comentários 0 reações 0 responsáveis Ver no GitHub
standards
Linguagem predominante
Rust
Estrelas
132
Forks
167
Merge médio
1d 23h
PRs com merge (30d)
110

Descrição

The standards library provides four administrative notes: [`pause_action`](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/standards/notes/pause_action.masm#L55-L83), [`owner_action`](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/standards/notes/owner_action.masm#L60-L98), [`rbac_action`](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/standards/notes/rbac_action.masm#L66-L114), and [`faucet_policy_action`](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/standards/notes/faucet_policy_action.masm#L67-L116). An owner or role holder creates one of these notes to request a privileged action, such as pausing a faucet or granting a role, and the account carries the action out when the note is consumed.

Nothing limits when that happens. No script reads the block number, no counter marks a note as outdated, and the protocol offers no way to withdraw a note once it exists. A note therefore stays valid indefinitely, for as long as its sender remains authorized, until somebody consumes it. The missing control is available in the same directory, since [`p2ide`](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/standards/notes/p2ide.masm#L139-L143) reads the current block number and refuses to run before a chosen height. Several of the actions also overwrite state without checking what is already there. [`set_mint_policy`](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/standards/faucets/policies/policy_manager.masm#L328-L340) confirms that the new policy appears on an approved list but never looks at the policy currently in force, and that list is fixed when the account is [created](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/src/account/policies/manager.rs#L330-L361) and thereafter only ever [read](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/standards/faucets/policies/policy_manager.masm#L108-L117), never written, so a policy allowed once remains allowed for the life of the account.

Who can consume such a note depends on how the account authenticates. If it requires a signature, only the key holder can submit the transaction, and the risk is that an operator's own tooling runs an outdated instruction. However, all four note types are documented as [public and intended for network execution](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/src/note/pause_action.rs#L94-L95), and each [fixes the public note type](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/src/note/pause_action.rs#L222-L224) when converted into a protocol note, so an operator cannot issue them privately to limit who sees them. The network authentication component checks [no signature](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/components/auth/network_account/network_account.masm#L45-L57). It confirms only that the transaction script, if any, and every input note script appear on approved lists, which are [never changed after the account is created](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/src/account/auth/network_account/auth_network_account.rs#L57-L61). Any deployment that manages a network faucet through these notes must add those note scripts to that list, and once it does, anyone may submit the transaction that consumes an outstanding note and therefore chooses the moment it takes effect. No shipped constructor adds them today, as [`create_network_fungible_faucet`](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/src/account/faucets/fungible/mod.rs#L639-L649) approves only the mint and burn note scripts. The component's own guidance nonetheless invites the change, advising that a script may be allowlisted when its effect is [safe for every possible input](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/src/account/auth/network_account/auth_network_account.rs#L43-L52): the four administrative scripts discard their note arguments and read their action from note storage fixed at creation, so they satisfy that stated test while remaining unsafe for the timing reasons described here.

Notes also run in the [order the submitter chooses](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-protocol/src/transaction/inputs/notes.rs#L53-L68), and a later note [sees whatever an earlier note in the same transaction wrote](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-protocol/asm/kernels/transaction/bin/main.masm#L100-L123). Where any party may execute a transaction against the managed account, the following outcomes are reachable:

- Supply drain. A leftover note that restores a permissive mint policy can be placed ahead of an attacker's own mint note in one transaction. The policy change and the minting happen together, so the supply is drained before any observer can react.
- Ownership takeover. If a planned ownership handover is called off, cancelling the nomination does not remove the outstanding note, because the nomination was never written. That note and the nominee's own acceptance note can later be consumed together, making the nominee the owner.
- Restored access after removal. A leftover note that grants a role restores it after that role was deliberately revoked. An administrator cannot pre-position a revoke note as a defense, because revoking a role from an account that does not hold it fails.
- Emergency pause defeated. A leftover unpause note clears the pause flag without checking whether the account is paused. Placing a leftover pause note after it restores the flag, so the pause state afterwards appears unchanged.
- Permanent loss of control. A leftover note that renounces ownership sets the owner to zero. No party can restore an owner afterwards, and every owner-gated action fails permanently.

Freezing the account does not help. A frozen account [rejects](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/crates/miden-standards/asm/standards/access/authority.masm#L75-L80) `pause_action` and `faucet_policy_action` notes rather than consuming them, so those notes are not used up and remain available afterwards. The other two are unaffected entirely, because `ownable2step` and `rbac` do not consult the authority component, so `owner_action` and `rbac_action` notes still succeed while the account is frozen. Removing the sender's ownership or role invalidates that sender's outstanding notes which depend on the authority removed, but changing the sender account's signing keys does not, because authorization is tied to the account identifier, which never changes. The only dependable option is for the account to consume each unwanted note itself, which requires knowing the note exists and acting first. The project has already described this risk for a different feature: the [transaction documentation](https://github.com/0xMiden/protocol/blob/8411bf093bde25285708faac152b6d7269009617/docs/src/transaction.md?plain=1#L135) warns that a prover may anchor to an old block "to bypass cross-account authorization such as roles or allowlists" and asks the affected account to defend itself with an expiration delta. No comparable control exists for a note that changes ownership, roles, pause state, or mint policy.

Consider storing a block height in each administrative note and checking it against the current block number before acting, following the pattern already used by `p2ide`, so that a note cannot be held indefinitely. Consider checking the expected state before overwriting it, so that a policy setter refuses a change from a policy the note's author did not anticipate; the `pausable` module already defines `assert_paused` for this purpose and nothing calls it. If a broader control is preferred, consider a per-account counter that every administrative note must match, so that increasing it invalidates all outstanding notes at once. Consider also allowing the approved-policy list to be reduced, so that a policy retired after a launch phase can be removed rather than remaining permanently available, and adding tests for delayed and out-of-order consumption, which the current suites for all four notes omit.

Part of: https://github.com/0xMiden/protocol/issues/3551

Guia de contribuição

Abrir o guia de contribuição

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.