Skip to content

Exempt the issuing faucet from its own transfer allowlist/blocklist #3496

Description

@PhilippGackstatter

Summary

BasicBlocklist and BasicAllowlist evaluate their predicate against the native account of the transfer. When a faucet mints and sends its own asset, the faucet is the native account, so the faucet is filtered by its own list. This produces two mirrored footguns:

  • Blocklist: block_account on the faucet's own ID disables the faucet's minting. Nothing rejects it - not BlocklistConfigNote, not the MASM manager, not the low-level helper.
  • Allowlist: the faucet must allow_account its own ID before it can mint at all, and disallow_account on its own ID silently takes minting away again.

Neither behaviour is useful: a faucet filtering itself gains nothing that Pausable (freeze) or the mint policy (stop issuance) does not already provide, and the failure surfaces as a confusing panic in the minting path rather than at the point where the list entry is written.

Proposal: short-circuit the predicate in check_policy when the asset's issuing faucet is the native account, so the two lists only ever govern third-party transfers.

Why the faucet ends up filtering itself

The predicates read the native account, not the counterparty:

When a faucet mints into an output note it is itself the native account, the minted asset carries the callback flag, and the kernel invokes on_before_asset_added_to_note with no exemption for the issuer. The policy then looks the faucet's own ID up in its own list.

A self-block is recoverable (config notes carry no assets, so no transfer callback fires and an unblock_account note still executes), but it is a silent, self-inflicted outage in the meantime.

Why filtering the issuer is not useful

  • It does not freeze the token. A transfer between two user accounts never consults the faucet's own entry, so self-filtering leaves user-to-user transfers untouched.
  • Freezing all transfers is what Pausable is for. The pause check already runs in invoke_transfer_policy before the policy is dispatched.
  • Stopping issuance is what the mint policy is for. Disabling minting through the transfer list is an obscure side channel with a misleading error message.
  • The faucet is not a party the list can meaningfully constrain: it owns the list and can rewrite it at will, and it must authorize its own transaction anyway. Exempting it grants no capability it did not already have, so this is not a way for a blocked account to launder a transfer through the faucet.

Alternative considered: reject self-entries

Reject BlockAccount { account } == target in BlocklistConfigNote::new plus an assertion in
blocklist::block_account. Rejected as the primary fix because:

  • It needs guards in two places (Rust for fail-fast, MASM for the paths that bypass Rust) and gets the mirror case wrong: for the allowlist the dangerous action is disallow_account, and the faucet still has to remember to allowlist itself at deploy time.
  • It treats the symptom. The root cause is that the predicate is asked a question about an account it should never be filtering.

Rejecting self-entries could still be added on top of the short-circuit as a usability guard, since
such an entry would then be dead state, but it is not needed for correctness.

Metadata

Metadata

Assignees

Labels

rustIssues that affect or pull requests that update Rust codestandardsRelated to standard note scripts or account components

Type

Fields

Priority

Medium

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions