Skip to content

Transaction Permission Table - #432

Closed
seanyoung wants to merge 18 commits into
m1from
sean/tx-perms
Closed

seanyoung wants to merge 18 commits into
m1from
sean/tx-perms

Conversation

@seanyoung

@seanyoung seanyoung commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Transaction Permission Table

Adds an on-chain, governance-managed rule table that decides whether a transaction
is allowed to run, enforced by the VM behind a new feature flag (off by default).

How it works

Each rule matches a sender (a specific account, or all accounts) and a
transaction kind — deploy (0), upgrade (1), script (2), other (3) —
and carries an allow/deny verdict. Rules are evaluated in order, first match
wins, and no match means allow, so an empty table changes nothing. Rules match
the primary transaction sender (fee-payer, multi-agent, and multisig included).

Enforcement happens at two points, because no single point can classify all
four kinds:

  • Validation (validate_signed_transaction): scripts and "other"
    transactions. A denial discards with the new status code
    TRANSACTION_NOT_PERMITTED (claims reserved slot 46) — no gas charged, and
    it applies uniformly to mempool admission, execution, and simulation.
  • Code-publish funnel (resolve_pending_code_publish_and_finish_user_session):
    deploy vs. upgrade, where upgrade means any target module already exists.
    This catches every publish path, including publishes from scripts, multisig,
    resource accounts, and object code deployment. A denial fails the transaction
    with the new kept status code MODULE_PUBLISHING_NOT_PERMITTED (reserved
    slot 1136), so denied publishes still pay gas and are not free compute.

A script that publishes code must pass both the script rules and the
deploy/upgrade rules.

Self-bricking guard

Hardcoded in the VM, not expressible in the table: approved governance scripts,
framework-reserved senders (0x1–0xa), the core-resources account, and
0x1::aptos_governance entry functions are never denied. A deny-all table
therefore cannot lock out the governance path needed to fix it (covered by a
dedicated e2e test).

Changes

  • Framework: new 0x1::transaction_permissions module — vector<Rule>
    under @aptos_framework capped at 256 entries (the scan runs unmetered in
    validation); admin entry functions gated by the framework signer
    (insert_rule_for_sender, insert_rule_for_all, remove_rule,
    initialize_extension); get_rules / check / is_allowed views; events
    on every mutation; initialized from genesis for new chains.
  • Feature flag: TRANSACTION_PERMISSIONS = 226 (fork range), wired in
    features.move, aptos_features.rs, and the release builder. Default off.
  • VM: OnChainConfig mirror of the resource (BCS layout pinned by a unit
    test) and the two enforcement checks above.
  • Migration: movement-migration/tx-permissions/ — core-resources scripts
    for initialize, rule edits, and feature enable/disable, plus an operator
    runbook (rollout order, listing via the view API, emergency disable, and the
    governance-proposal path once core-resources is decommissioned).

Note: the admin entry points are split into _for_sender / _for_all
variants because the SDK transaction-builder generator cannot encode
Option<address> arguments; a public insert_rule(Option<address>, ...)
remains for governance scripts.

Testing

  • 13 Move unit tests in the new module (first-match ordering, wildcard
    matching, remove-shift, signer gating, bounds and cap) — all 679 framework
    Move tests pass.
  • 6 e2e tests (e2e-move-tests/src/tests/transaction_permissions.rs): feature
    gating off/on, wildcard and per-sender rules, script/entry/deploy/upgrade
    denial with the expected status codes, and deny-all recovery through the
    exempt framework account.
  • 4 unit tests on the Rust config mirror, including a BCS layout pin against
    the Move struct.
  • All six migration scripts compile-verified against the framework.

Rollout

  1. Framework upgrade carrying the new module and flag (plus node binaries with
    the enforcement code).
  2. initialize_transaction_permissions.move (one-time, idempotent).
  3. Populate initial rules (optional).
  4. enable_transaction_permissions.move (enables flag 226 + epoch change).

disable_transaction_permissions.move is the emergency off switch; it leaves
the table intact.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

fEst1ck and others added 18 commits April 2, 2026 03:17
Add proposal to update on-chain execution config
fix: enforce strict bytecode verifier bounds to prevent validator DoS
chore(sync): merge public m1 and rework verifier strict bounds
- new 0x1::transaction_permissions module: ordered allow/deny rule
  table keyed by sender (specific or all accounts) and transaction
  kind (deploy/upgrade/script/other); first match wins, no match
  allows, capped at 256 rules since validation scans unmetered
- admin insert/remove entry functions gated by the framework signer,
  get_rules/check/is_allowed views, module events on every mutation
- initialized at genesis; initialize_extension entry for chains
  upgraded past genesis
- TRANSACTION_PERMISSIONS feature flag (226), default off; VM-side
  enforcement lands separately
- regenerated docs and SDK builder (the builder also picks up the
  earlier delegation-pool partial-voting entry function)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- doc catch-up for earlier delegation-pool partial-voting,
  staking-contract, and token-objects changes; no source changes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- OnChainConfig mirror of 0x1::transaction_permissions with
  first-match-wins verdict lookup; layout pinned by a BCS test
- check A at validation: scripts and other transactions, denied with
  new discard code TRANSACTION_NOT_PERMITTED (claims reserved 46);
  applies to mempool, execution, and simulation with no gas charged
- check B at the code-publish funnel: deploy (no target module
  exists) vs upgrade, matched on the transaction sender, denied with
  new kept code MODULE_PUBLISHING_NOT_PERMITTED (claims reserved
  1136) so denied publishes still pay gas; catches publishes from
  scripts, multisig, and object code deployment
- self-bricking guard hardcoded in both checks: approved governance
  scripts, framework-reserved and core-resources senders, and
  0x1::aptos_governance entry functions are never denied
- e2e tests: feature gating, first-match ordering, wildcard and
  per-sender rules, script/entry/deploy/upgrade denial, deny-all
  recovery through the exempt framework account

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- core-resources scripts: initialize table, insert rule (per-sender
  and wildcard), remove rule, enable/disable feature 226
- operator runbook: rollout order, rule semantics, listing via the
  view API, first-match ordering guidance, emergency disable, and
  the governance-proposal path after core-resources decommissioning
- all six scripts compile against the framework

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rule gains an optional module pattern (address, optional module
  name); rules without one behave as before, rules with one apply
  per module: published modules for deploy/upgrade kinds, loaded
  modules for script/other kinds
- module access is enforced by a storage decorator around the user
  payload session, so entry points, transitive calls, resource-type
  loads, closures, and lazy loading are all covered; a mid-execution
  denial is kept and charged (new code MODULE_ACCESS_DENIED, claims
  reserved 1137), while an entry function's own module is pre-checked
  at validation and discarded free
- publish checks now classify and judge each bundle module
  separately; module patterns enable per-module publish allowlists
- special addresses (0x0..0xf) are always accessible and cannot be
  targeted by a pattern; system sessions and the publish process
  keep raw storage
- new entry functions insert_module_rule_for_{sender,all}, view
  check_module_access, migration scripts and runbook section;
  5 new e2e tests, 6 new Move unit tests, 3 new Rust config tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@seanyoung seanyoung closed this Oct 7, 2026
@seanyoung
seanyoung deleted the sean/tx-perms branch October 7, 2026 08:34
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.

5 participants