Skip to content

docs(tip-1154): keep a zone copy of configurable account commitments - #8814

Open
legion2002 wants to merge 6 commits into
tip/1154from
tip/1154-zone-copy
Open

legion2002 wants to merge 6 commits into
tip/1154from
tip/1154-zone-copy

Conversation

@legion2002

Copy link
Copy Markdown
Contributor

Stacked on #8721.

Switches TIP-1154 from reading every commitment from Tempo to a zone copy, as proposed in #8721 (comment). Retirement, the 0xef marker, EXTCODE* and pool checks read only zone state. Tempo is read only for 0x05 verification (at most 2 per tx, metered). Activations (registration and upgradeAccount) go into an append-only list on Tempo, and advanceTempo must apply them up to its anchor, the same way token enablements are chained.

Differs from the comment: the list carries registrations too, so the zone copy never waits for a first 0x05 tx, and it stores the head after each entry, so a zone created after T14 can catch up in several calls. If #8788 lands, the list carries migrations only.

Open cost: every activation writes the shared activationCount slot on Tempo.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T18:11:59.715960Z 7f9c162 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@tempoxyz-bot

tempoxyz-bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

👁️ Cyclops Security Review

✅ Reviewed 0f7d7b6 (latest) · Coverage: partial · ⚠️ 3 open findings · report
⚠️ Not checked: The changed input is a draft specification, and the referenced exithatch crate contains documentation rather than executable authorization logic. The new fallback could therefore be checked as a specified transition but not exercised throug
ℹ️ Not verifiable from the source alone (needs running code or deployed state): No exithatch STF implementation was examined. The forced-exit Tempo-read rule and its capacity accounting for the added per-exit account proof (README capacity rule) can't be checked in code. To verif

# Severity Finding Introduced Status
1 ⚪ Needs review Activation completeness does not protect inbox exits or RPC authentication 206ea0c 🟠 Open
2 🟢 Low Zone getConfigCommitment can return a superseded commitment indefinitely after updateConfig 7f9c162 🟠 Open
3 🟡 Medium Zone copy rules omit TIP-1107 registrations of derived accounts 2c55b76 🟠 Open
4 🟢 Low Forced-exit deferral contradicts the ordered, fully consumed inbox queue a5cfd58 ✅ Fixed in 0f7d7b6

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f9c1628f1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tips/tip-1154.md Outdated
These read only the copy and never Tempo:

- the TIP-1113 [retirement check](./tip-1113.md#retirement-check): a primitive signature from an address with a nonzero copy is rejected as a retired root
- authorization's code-hash check, `EXTCODESIZE`, `EXTCODEHASH`, `EXTCODECOPY`, calls to the account and ERC-1271, so Permit2 and other verifiers that check for code reject the retired original key

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Read Tempo during ERC-1271 signature verification

When an owner rotation is present at the anchor but the zone copy still contains the previous commitment, saying that ERC-1271 reads only the copy permits an implementation to validate a removed owner's Permit2/ERC-1271 signature indefinitely. This conflicts with the later requirement that every 0x05 verification, including verifyConfigurableAccount, reads Tempo; clarify that only code-marker dispatch uses the copy and that the ensuing ERC-1271 signature verification must use Tempo's anchored commitment.

Useful? React with 👍 / 👎.

Comment thread tips/tip-1154.md Outdated
- A primitive signature from an address with a nonzero commitment is rejected as a retired root.

### Account code
Each read is charged like a cold account access. Transaction authorization reads at most twice per transaction: for the sender and for the fee payer. TIP-20 `permit`, `verifyConfigurableAccount`, keychain and channel checks during execution each read and are metered the same way.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Meter the grant and outer signers, not the fee payer

For a transaction that creates and immediately uses an access-key grant, the two configurable signatures are the key_authorization parent and the outer configurable delegate, as specified in tips/tip-1111.md:93-95; a separate fee payer cannot use 0x05 at all (tips/tip-1107.md:340-342). Assigning the two reads to sender and fee payer leaves the grant signer without the required anchored account proof or incorrectly rejects this supported flow, so the rule should require one read per actual configurable signer, up to two.

Useful? React with 👍 / 👎.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md Outdated
Comment on lines +77 to +94

- `activationCount`: the number of entries
- `activationHeads[i]`: the running hash after `i` entries, where `activationHeads[0] = 0` and `activationHeads[i+1] = keccak256(activationHeads[i] ‖ account ‖ commitment)`

Each account appears at most once, since its commitment never returns to zero.

1. Every read of an account's commitment during zone execution returns Tempo's commitment for that address at the anchor. This includes transaction authorization, TIP-1113 retirement checks, `verifyConfigurableAccount`, `getConfigCommitment`, TIP-20 `permit`, keychain and channel checks.
2. The anchor is fixed for each transaction, as it is for TIP-403 reads. A transaction after `advanceTempo` in the same zone block sees the new anchor.
3. If Tempo state at the anchor can't be read, execution fails closed: the sequencer stops building rather than treating the commitment as zero.
On a zone, the `ZoneInbox` precompile keeps `processedActivationCount` and `processedActivationHead`, both starting at zero. `advanceTempo` takes, as input from the sequencer, the entries added to Tempo's list since the zone's processed position:

With this, the [TIP-1107 authorization](./tip-1107.md#authorization) rules and the [TIP-1113 retirement](./tip-1113.md#retirement-check) rules apply on zones without changes:
1. Fold the supplied entries onto `processedActivationHead`, giving head `h` after `n` entries.
2. Read `activationHeads[n]` from Tempo at the new anchor and require it to equal `h`.
3. Write each entry's commitment into that account's zone copy and set the account code marker as above.
4. Store `n` and `h` as the processed position.

`advanceTempoActivations(entries)` applies the next entries the same way at the current anchor, without advancing it. The sequencer uses it to catch up on a large backlog in several calls. A zone created after T14 starts with such a backlog: the list is global, so the zone's genesis anchor already includes every activation since T14, none of which the new zone has applied yet.

**Completeness.** A zone block MUST NOT contain a user transaction while `processedActivationCount` is below Tempo's `activationCount` at the anchor. A block that does is invalid. This also covers anchors advanced by `advanceTempoHeaders`, which applies no entries. So every activation included by the anchor is in the zone copy before any transaction uses that anchor.

**Gas.** Applying entries is a system operation outside the block gas limit and the inbox gas budget. Each account is applied at most once, and on Tempo each activation already pays for a commitment write, so Tempo's gas limit bounds the entries per Tempo block.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 (optional) Nothing caps how many activation entries one call applies, so a zone block's system work and proving cost have no limit. They sit outside the block gas limit and the inbox budget. The Gas paragraph bounds entries per Tempo block. But one advanceTempo can jump many Tempo blocks, and a post-T14 zone's genesis backlog covers all of them. Fix: set a justified per-call and per-zone-block cap on entries for advanceTempo and advanceTempoActivations, and say what happens past it (revert).

Why this was flagged

Line 94 makes applying entries a system operation outside the block gas limit and the inbox gas budget. It justifies that only by saying "Tempo's gas limit bounds the entries per Tempo block". advanceTempo (line 84) and advanceTempoActivations (line 90, interface line 167) accept an unbounded Activation[]. An anchor jump of many Tempo blocks, or the genesis catch-up at line 181, can put the whole backlog in one zone block. Each entry writes zone state and the code marker, and adds proof work. Line 90 says the sequencer "uses it to catch up ... in several calls", but no rule enforces that. The prover can then face a block too large to prove. AGENTS.md asks TIPs to justify every limit and to state range behavior. The base had no such unmetered system path.

Verification: Line 94 says "Applying entries is a system operation outside the block gas limit and the inbox gas budget". Its only justification is "Tempo's gas limit bounds the entries per Tempo block". That bound is per Tempo block. Nothing in the spec bounds it per zone block or per call. Line 90 says a zone created after T14 starts with a backlog of every activation since T14.

Comment thread tips/tip-1154.md Outdated
Comment on lines +143 to +170
## Interfaces

No new interfaces. Zones reuse TIP-1107's `getConfigCommitment` and `verifyConfigurableAccount`.
On Tempo, the configurable account precompile adds:

```solidity
/// Number of accounts whose commitment has gone from zero to nonzero.
function activationCount() external view returns (uint64);

/// Running hash of the activation list after `count` entries.
function activationHeads(uint64 count) external view returns (bytes32);
```

On zones, `ZoneInbox` adds:

```solidity
struct Activation {
address account;
bytes32 commitment;
}

function processedActivationCount() external view returns (uint64);
function processedActivationHead() external view returns (bytes32);

/// Apply activation entries at the current anchor without advancing it. Sequencer only.
function advanceTempoActivations(Activation[] calldata activations) external;
```

`advanceTempo` takes a trailing `Activation[] calldata activations` argument.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 (optional) Implementers of the new interface lack errors, events and storage layouts, which the repo's TIP rules require for precompiles. advanceTempoActivations and the new advanceTempo argument name no errors for a head mismatch, a non-sequencer caller or an incomplete catch-up. The new getters and the ZoneInbox fields have no NatSpec for parameters or returns and no storage slots. Fix: add full NatSpec with named errors for each of these interfaces, plus storage layouts for the Tempo list and the two ZoneInbox fields.

Why this was flagged

AGENTS.md (CLAUDE.md) requires precompile interfaces to have complete NatSpec for parameters, returns and errors, plus events and storage layouts. Lines 143-170 give one-line comments or none at all. Line 166 says only "Sequencer only" without naming the revert. Step 2 at line 86 says "require it to equal h" without naming the error. Without a defined storage layout for activationCount/activationHeads and processedActivationCount/processedActivationHead, the Solidity and Rust layouts that the PR rules require to match have no spec to follow. The storage proofs at line 118 also need fixed slot positions to verify against.

Verification: AGENTS.md line 20 says "Follow the template in tips/tip_template.md", and tips/tip_template.md:43 says "For precompiles, include complete NatSpec for parameters, return values, and errors." advanceTempoActivations says only "Sequencer only." It names no error for a non-sequencer caller or for a head mismatch. Nothing observably breaks in the document itself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The redesigned interface fixes part of this. The Tempo getters now have @ param/@ return, and the spec names MigrationHeadMismatch() and InvalidMigrationIndex(). Two gaps remain. First, the spec still gives no storage layout (slot positions) for migrationCount/migrationHeads in the configurable account precompile or for processedMigrationCount/processedMigrationHead in ZoneInbox. The advanceTempo storage proofs in Proving depend on those slots, and the Solidity and Rust layouts need them to match. Second, the ZoneInbox getters have no @ return tags, and the new Migration[] calldata migrations argument of advanceTempo has no NatSpec signature or @ param. Adding a storage layout section and full NatSpec for those items would close this.

Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md Outdated
### Reading the commitment
### Zone copy

Zone account leaves accept the optional [TIP-1107](./tip-1107.md#commitment-storage) fifth field, with the same encoding rules as Tempo: tag `0x00`, nonzero hash and no extra fields. An account whose copy is nonzero has the [account code marker](./tip-1107.md#account-code) (`0xef`) as its code, unless it already has other code on the zone, in which case it keeps that code and fails authorization as under TIP-1107.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 pre-existing, not blocking: A migrated account can keep EIP-7702 delegation or contract code on a zone. Its retired original key may then still move funds there, and its owners are locked out. Line 58 keeps "other code on the zone" when the copy becomes nonzero. Delegate code that checks the original key with ecrecover still runs on calls to the account. Fix: say what happens to zone code or delegation when an activation applies (for example, replace it with 0xef), or limit the line 205 invariant to transaction signatures. Pre-existing: the base text also kept local code.
A small fix can ride a push you are already making; otherwise a short reply is enough.

Why this was flagged

Bob delegates his EOA on a zone via EIP-7702 to a smart-wallet contract whose execute checks signatures by ecrecover against address(this). He then runs upgradeAccount on Tempo, which only checks Tempo state for code or delegation (tip-1113.md:73), so the migration succeeds. advanceTempo writes a nonzero copy but, per line 58, the account keeps its delegation. Anyone can relay a call to the delegate's execute with Bob's old-key signature, so the retired key still moves the zone funds. The owners can't remove the delegation: 0x05 authorization-list entries are invalid (tip-1107.md:343) and primitive entries are skipped for accounts with a commitment (tip-1113.md:121). The TIP-1107 code-hash check (tip-1107.md:169) also fails, so 0x05 authorization fails too. This breaks the invariant at line 205 and the retirement guarantee in the Abstract. The base text had the same keep-local-code rule, so this is pre-existing in the design that this diff rewrites.

Verification: Trigger: an account sets an EIP-7702 delegation on a zone and later migrates with upgradeAccount on Tempo. TIP-1113's migration precondition (tip-1113.md:73) is checked only against Tempo state. A delegate wallet whose execute checks ecrecover == address(this) therefore still accepts the retired key from any relayer. The base text had the same rule.

Comment thread tips/tip-1154.md Outdated
Comment on lines +90 to +92
`advanceTempoActivations(entries)` applies the next entries the same way at the current anchor, without advancing it. The sequencer uses it to catch up on a large backlog in several calls. A zone created after T14 starts with such a backlog: the list is global, so the zone's genesis anchor already includes every activation since T14, none of which the new zone has applied yet.

**Completeness.** A zone block MUST NOT contain a user transaction while `processedActivationCount` is below Tempo's `activationCount` at the anchor. A block that does is invalid. This also covers anchors advanced by `advanceTempoHeaders`, which applies no entries. So every activation included by the anchor is in the zone copy before any transaction uses that anchor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs review · unverified: Activation completeness does not protect inbox exits or RPC authentication

The proposed completeness control protects only user transactions, although retirement-sensitive consumers also run outside that boundary. The activation rules permit an authenticated partial prefix and a system-only block at an anchor containing unapplied migrations. RPC authentication and forced-exit authorization nevertheless treat a zero zone copy as sufficient to retain primitive-key authority.

Full finding

The proposed completeness control protects only user transactions, although retirement-sensitive consumers also run outside that boundary. The activation rules permit an authenticated partial prefix and a system-only block at an anchor containing unapplied migrations. RPC authentication and forced-exit authorization nevertheless treat a zero zone copy as sufficient to retain primitive-key authority. Concrete counterexample: account A migrates on Tempo before anchor 100; activationCount=1, processedActivationCount=0, processedActivationHead=0, and A's zone copy remains zero. Supplying no entries still authenticates against activationHeads[0]=0, and a block containing no user transactions satisfies the stated completeness rule. A holder of A's retired original key can therefore authenticate an RPC token during catch-up. Under the composed forced-exit rules, an otherwise valid encrypted request from that key can also execute through the system inbox and withdraw A's full balance to the signed recipient, without any ordinary user transaction. RPC caller identity protects private balance and nonce reads. This violates the promised retirement at an anchor containing the migration. Three executed Node state-model checks confirmed the authenticated empty prefix, stale-copy RPC acceptance, and a 1,000,000-unit modeled inbox withdrawal. These validate the prose counterexample, not deployed execution. Preconditions are possession of the original key and servicing RPC authentication or processing a forced exit while activation catch-up is incomplete; the withdrawal additionally requires the staged forced-exit design to be implemented. This is a proposed T14 specification defect, not a claim of current public-chain exploitation: neither public chainspec schedules T14, and the forced-exit crate is documentation-only. Require activation completeness before every retirement-sensitive authority decision, including inbox exits and RPC authentication. Catch-up system calls may remain available, but must not process such actions against an incomplete copy.

Valid? React with 👍 / 👎.

Comment thread tips/tip-1154.md Outdated
| Original-key retirement (TIP-1113), account code marker | Uses the local commitment | Uses the zone copy |
| Authorize with `0x05` (direct, access key, key authorization) | Checked against the local commitment | Checked against Tempo's commitment at the anchor (metered); the result is written into the zone copy |
| Initial registration, `upgradeAccount` | Writes the commitment and appends to the activation list | No local write; applied from the activation list by `advanceTempo` |
| `updateConfig` | Allowed | Rejected; the new commitment reaches the zone copy on the account's next `0x05` transaction there |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low (trigger unconfirmed) · unverified: Zone getConfigCommitment can return a superseded commitment indefinitely after updateConfig

Mechanism: TIP-1154 now makes zone getConfigCommitment return the zone copy (lines 67-71). The copy changes only through activation entries, which record zero-to-nonzero transitions only (lines 76-81), or through a 0x05 transaction-authorization write (lines 60-63, 105). An updateConfig rotation on Tempo therefore reaches a zone's getConfigCommitment only when the account next sends a 0x05 transaction on that zone (line 52).

Full finding

Mechanism: TIP-1154 now makes zone getConfigCommitment return the zone copy (lines 67-71). The copy changes only through activation entries, which record zero-to-nonzero transitions only (lines 76-81), or through a 0x05 transaction-authorization write (lines 60-63, 105). An updateConfig rotation on Tempo therefore reaches a zone's getConfigCommitment only when the account next sends a 0x05 transaction on that zone (line 52). That may never happen.

Trigger: An owner set {A,B} is rotated on Tempo to remove a compromised owner B. The zone anchor advances past the rotation. The account sends no 0x05 transaction on the zone. getConfigCommitment(account) on the zone keeps returning hash({A,B}).

Impact: TIP-1107 (lines 466-472) tells consumers to treat getConfigCommitment as the authoritative current commitment. A zone contract that authorizes by checking keccak(config) == getConfigCommitment(account) plus owner signatures keeps accepting the removed owner after the anchor includes the rotation. verifyConfigurableAccount and ERC-1271 stay correct, because they read Tempo. This contradicts the threat model (lines 189-194), which says a removed owner keeps zone authority only until the anchor includes the rotation. The previous TIP version read getConfigCommitment from Tempo at the anchor, so the regression comes from this change. Compatibility (line 176) mentions the lag only for wallets.

Preconditions: A zone contract or off-chain consumer relies on getConfigCommitment for current owners. Missing evidence: no deployed consumer is identified, because this is a spec-stage change.

Remediation: either make zone getConfigCommitment read Tempo at the anchor (metered, like verifyConfigurableAccount), or include updateConfig commitment changes in the activation list so the copy tracks every change. Alternatively, state normatively that zone getConfigCommitment is a liveness hint only, and update the threat model.

First reported at 7f9c162; recheck at this head: still present.

Valid? React with 👍 / 👎.

Comment thread tips/tip-1154.md Outdated

### Activation list

On Tempo, the configurable account precompile appends `(account, commitment)` to the activation list each time an account's commitment goes from zero to nonzero: TIP-1107 initial registration and TIP-1113 `upgradeAccount`. It stores:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the list includes registrations, every zone writes a commitment and code marker for every configurable account on Tempo, even accounts that never touch that zone. That work grows with Tempo's total number of configurable accounts, not with the zone's own usage, which undercuts L32's "only accounts that sign with 0x05 pay".

It also makes spam cheap. One registration costs about 250k gas on Tempo, once, and every zone must apply it before it can accept user transactions.

Suggest listing only migrations (upgradeAccount), since only they retire an original key. Registrations would reach a zone on the account's first 0x05 transaction there, as in the original proposal. That also shrinks the genesis backlog and the per-append Tempo storage cost.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed: the list carries migrations only now, so zones only apply upgradeAccounts

Comment thread tips/tip-1154.md
- A primitive signature from an address with a nonzero commitment is rejected as a retired root.

### Account code
Each read is charged like a cold account access. Transaction authorization reads once per configurable signer, at most twice per transaction: the outer signer and, when a transaction creates and uses an access-key grant at once, the `key_authorization` parent ([TIP-1111](./tip-1111.md#grant-and-use-in-one-transaction)). Fee payers can't sign with `0x05` ([TIP-1107](./tip-1107.md#supported-contexts)). ERC-1271 `isValidSignature`, `verifyConfigurableAccount` and keychain checks during execution each read and are metered the same way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reads during execution (ERC-1271 isValidSignature, verifyConfigurableAccount, keychain checks) are limited only by gas, at the price of a cold account access (2,600). Each one still adds a Tempo account proof of about 2 KB to the batch witness, and in the sequencer it may wait on a Tempo RPC round trip while the block is being built. So one transaction can force many proofs and sequential Tempo reads cheaply, and transaction authorization's limit of 2 reads doesn't apply to these.

Suggest pricing each read for its proof size and Tempo round trip, or capping Tempo reads per transaction and per block, and saying what happens past the cap (revert).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think this is mostly moot on zones: CREATE/CREATE2 are disabled, so there's no Permit2 or other contract that can loop ERC-1271, and keychain checks only run at tx auth (already capped at 2)

the remaining path is batching direct verifyConfigurableAccount / isValidSignature calls. we can close it with a short-circuit: by completeness, every migration at the anchor is already in the zone copy, so a zero copy means a zero commitment on tempo and the check can fail locally without a tempo read

with that, reads only hit real CAs, and repeated reads of the same account at the same anchor share one proof. the witness grows with distinct CAs used per anchor, each of which cost ≥500k gas to create on tempo, so fresh addresses can't be used to spam proofs. i can add the short-circuit to the TIP

Comment thread tips/tip-1154.md Outdated
Each read is charged like a cold account access. Transaction authorization reads once per configurable signer, at most twice per transaction: the outer signer and, when a transaction creates and uses an access-key grant at once, the `key_authorization` parent ([TIP-1111](./tip-1111.md#grant-and-use-in-one-transaction)). Fee payers can't sign with `0x05` ([TIP-1107](./tip-1107.md#supported-contexts)). ERC-1271 `isValidSignature`, `verifyConfigurableAccount` and keychain checks during execution each read and are metered the same way.

An account whose Tempo commitment at the anchor is nonzero has the [account code marker](./tip-1107.md#account-code) (`0xef`) as its code on the zone, read from Tempo like the commitment and never written to zone state. Authorization's code-hash check, `EXTCODESIZE`, `EXTCODEHASH`, `EXTCODECOPY`, calls to the account and ERC-1271 then behave as on Tempo, so Permit2 and other verifiers that check for code reject the retired original key. Zone accounts otherwise keep their local code; an account with other code or EIP-7702 delegation on the zone fails authorization as under TIP-1107.
If transaction authorization reads a nonzero commitment that differs from the zone copy, the transaction writes it into the copy and sets the account code marker. A derived account that is unregistered on Tempo writes nothing: it reaches the copy through the activation list once Tempo registers it. Verifications during execution never write.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A derived account that only uses zones never gets the code marker. It's unregistered on Tempo, so its zone copy stays zero, it has no 0xef code, and Permit2 never calls ERC-1271 for it on that zone. It also can't rotate owners until it sends a transaction on Tempo, since updateConfig reverts on zones. Worth stating as a limitation, or setting the marker on a derived account's first zone 0x05 transaction.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moot after rebasing on #8788, which removes derived accounts. every CA comes from a migration

Comment thread tips/tip-1154.md Outdated
Each read is charged like a cold account access. Transaction authorization reads once per configurable signer, at most twice per transaction: the outer signer and, when a transaction creates and uses an access-key grant at once, the `key_authorization` parent ([TIP-1111](./tip-1111.md#grant-and-use-in-one-transaction)). Fee payers can't sign with `0x05` ([TIP-1107](./tip-1107.md#supported-contexts)). ERC-1271 `isValidSignature`, `verifyConfigurableAccount` and keychain checks during execution each read and are metered the same way.

An account whose Tempo commitment at the anchor is nonzero has the [account code marker](./tip-1107.md#account-code) (`0xef`) as its code on the zone, read from Tempo like the commitment and never written to zone state. Authorization's code-hash check, `EXTCODESIZE`, `EXTCODEHASH`, `EXTCODECOPY`, calls to the account and ERC-1271 then behave as on Tempo, so Permit2 and other verifiers that check for code reject the retired original key. Zone accounts otherwise keep their local code; an account with other code or EIP-7702 delegation on the zone fails authorization as under TIP-1107.
If transaction authorization reads a nonzero commitment that differs from the zone copy, the transaction writes it into the copy and sets the account code marker. A derived account that is unregistered on Tempo writes nothing: it reaches the copy through the activation list once Tempo registers it. Verifications during execution never write.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the copy update persist if the transaction reverts? TIP-1107 registration persists through failed execution. If this write doesn't, an account whose transactions keep reverting keeps an old copy. Please state it either way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed: the TIP now says the write happens before calls run and persists through a revert

Comment thread tips/tip-1154.md Outdated
- Authorization tokens accept `0x05` signatures, checked against Tempo's commitment at the zone's current anchor.
- Tokens signed by a retired original key are rejected.
- Tokens signed by a retired original key are rejected, using the zone copy.
- Open sessions are rechecked when the anchor advances, and dropped when the commitment no longer authorizes them, as revoked keychain keys are today.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rechecking every open 0x05 session against Tempo each time the anchor advances lets anyone cause unpaid Tempo reads. Opening a session is free, so N sessions mean N unpaid reads per anchor. That contradicts the motivation (L32) and the threat model (L196).

Suggest rechecking only sessions whose account appears in that anchor's rotation or activation events, and capping sessions per account.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed: only accounts whose commitment changed in that anchor range get rechecked

Comment thread tips/tip-1154.md Outdated
When the anchor advances, the pool:

- drops pending primitive-key transactions from accounts the new activation entries retired, using the zone copy
- revalidates pending `0x05` transactions from accounts whose commitment changed at that anchor (`ConfigurableAccountConfigUpdated` and `AccountUpgraded` events on Tempo), with a fresh Tempo read

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revalidating on events alone can miss changes. TIP-1107 (L353) says events aren't enough, because some commitment changes persist through failed execution. Revalidate from the activation entries applied at this anchor plus a Tempo state diff instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with registration gone, both ConfigurableAccountConfigUpdated and AccountUpgraded roll back with their change, so events catch everything

Comment thread tips/tip-1154.md
Comment on lines +30 to +31
- An account's Tempo commitment never returns to zero once set ([TIP-1107](./tip-1107.md#commitment-storage), [TIP-1113](./tip-1113.md#ordering-and-permanence)). If it could, a nonzero zone copy could outlive the Tempo commitment it copies.
- Every commitment that goes from zero to nonzero on Tempo does so through a TIP-1113 migration, which emits `AccountUpgraded` and rolls back with its event on failure. There is no first-use registration ([TIP-1107](./tip-1107.md)). Any other zero-to-nonzero path would bypass the migration list and leave the original key live on zones.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: Zone copy rules omit TIP-1107 registrations of derived accounts

TIP-1154 now appends only upgradeAccount migrations to the migration list and writes the zone copy at authorization only when it is already nonzero. TIP-1107 in the same tree still has eventless first-use registration. A registered derived account therefore never gets a nonzero zone copy on any zone.

Valid? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first-use registration was removed in #8788, which landed on tanishk/configurable-account-tips. this stack (#8721 → #8814) still sits on the old TIP-1107, so we need to rebase it on top of tanishk/configurable-account-tips. after that, every activation goes through upgradeAccount and lands in the migration list

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 optional suggestion (a nit or a note on pre-existing code) was found and not posted.

Still open from earlier reviews (3):

  • Unresolved: 3 minor or pre-existing.

Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md
Comment on lines +30 to +31
- An account's Tempo commitment never returns to zero once set ([TIP-1107](./tip-1107.md#commitment-storage), [TIP-1113](./tip-1113.md#ordering-and-permanence)). If it could, a nonzero zone copy could outlive the Tempo commitment it copies.
- Every commitment that goes from zero to nonzero on Tempo does so through a TIP-1113 migration, which emits `AccountUpgraded` and rolls back with its event on failure. There is no first-use registration ([TIP-1107](./tip-1107.md)). Any other zero-to-nonzero path would bypass the migration list and leave the original key live on zones.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Accounts registered by first use on Tempo never get a zone copy. Their colliding original key stays live on every zone forever. The spec assumes registration is gone, but TIP-1107 still defines it. Line 31 says no first-use registration exists, yet tips/tip-1107.md:139 still writes a commitment with no upgradeAccount and no event. Fix: one of two changes. Make the migration list also carry TIP-1107 registrations. Or gate this TIP on the change that removes registration, and update TIP-1107 in the same change. Same pattern at 3 sites (tips/tip-1154.md:31, tips/tip-1154.md:126, tips/tip-1154.md:147).

Why this was flagged

TIP-1107 in this checkout keeps first-use registration. It writes the commitment during the account's first included transaction (tips/tip-1107.md:139), charges 250,000 gas (tips/tip-1107.md:320) and emits no event (tips/tip-1107.md:353). Under the new design only upgradeAccount appends to the migration list (tips/tip-1154.md:80). The copy is refreshed only when it is already nonzero (tips/tip-1154.md:113, 130). So a registered account's zone copy stays zero, and the zone installs no 0xef marker for it. The retirement check (line 119) therefore never fires on a zone. That leaves the salt-chooser's colliding EOA key (tips/tip-1107.md:103) live there, and Permit2 trusts ecrecover. This contradicts tips/tip-1107.md:107, "Registration covers a zone from then on". Line 126 also rejects zero-commitment 0x05 signatures that TIP-1107 accepts by derivation. Line 147's event-based pool revalidation misses registrations. The base read Tempo's commitment directly, so registration took effect on zones.

Verification: TIP-1107 still defines first-use registration at tips/tip-1107.md:46 and :139. Only upgradeAccount appends to the migration list (line 80). 0x05 authorization writes the copy only when Tempo "differs from a nonzero copy". So a registered account's zone copy stays zero forever. On every zone the colliding EOA key therefore stays live. This is a regression from the base text, which covered registration.

Comment thread tips/tip-1154.md Resolved
Comment thread tips/tip-1154.md Resolved

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last 3 reviews of this pull request each found new blocking issues, repeatedly in tips/tip-1154.md. Where they share a root cause, one fix may close them together.

Still open from earlier reviews (4):

  • 🔴 tips/tip-1154.md:31 — Accounts registered by first use on Tempo never get a zone copy. Their colliding original key stays live on every zone…
  • Also unresolved: 3 minor or pre-existing.

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

Comment thread tips/tip-1154.md Resolved

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last 4 reviews of this pull request each found new blocking issues, repeatedly in tips/tip-1154.md. Where they share a root cause, one fix may close them together.

Still open from earlier reviews (4):

  • 🔴 tips/tip-1154.md:31 — Accounts registered by first use on Tempo never get a zone copy. Their colliding original key stays live on every zone…
  • Also unresolved: 3 minor or pre-existing.

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

Comment thread tips/tip-1154.md Outdated
### Forced exits

When the anchor advances, the pool revalidates pending transactions whose signing accounts have a commitment that changed at that anchor. Only TIP-1107 owner updates and TIP-1113 migrations change zone authority. Both emit events from the configurable account precompile.
The forced-exit authorization (`crates/zones/exithatch`) accepts `0x05` signatures for the account under the same rules. It rejects a primitive signature from an address with a nonzero zone copy. For an address whose zone copy is zero, it also reads the address's commitment from Tempo at the anchor (one account proof) and rejects the primitive signature if that commitment is nonzero. Forced exits are inbox entries processed in queue order, so they can run while the zone is still applying a migration backlog. Without the Tempo read, a retired original key could force-exit during that window.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 A migrated account's 0x05-signed forced exit, processed while the zone is draining a migration backlog, is terminally rejected and the 0.1-token fee is lost. Line 160 applies the 0x05 rules "under the same rules". Line 128 then fails any zero-copy verification without reading Tempo. Its reason, "By the completeness rule", does not hold for forced exits, which line 160 says run mid-backlog. Fix: forced-exit 0x05 checks should read Tempo at the anchor when the zone copy is zero, as the primitive path now does, and line 243's invariant should allow that read.

Why this was flagged

Bob migrates with upgradeAccount and later queues a forced exit signed by his owners with 0x05. The zone was created after T14, or it is spreading a backlog over several calls (line 98), so Bob's zone copy is still zero when the exit is processed in queue order (line 160). The forced-exit check uses "the same rules" (line 160). Line 128 makes a zero copy fail without a Tempo read, and its claim that "a zero copy means Tempo's commitment at the anchor is also zero" assumes the completeness rule. Completeness (line 100) gates only user transactions, not inbox entries. Bob's primitive key is retired too, so the account has no valid way to sign a forced exit in this window. crates/zones/exithatch/README.md:117 makes an invalid signature a terminal Rejected(reason), and README:107/205 say the L1 fee is neither refunded nor charged back. Bob loses the fee and has to re-request. Line 160's Tempo fallback covers only primitive signatures, so it does not help here.

Verification: tips/tip-1154.md line 160 applies 0x05 rules "under the same rules" to forced exits, which "can run while the zone is still applying a migration backlog". Line 128 says: "If the account's zone copy is zero, verification fails without reading Tempo." The base text read Tempo. README.md line 117 makes this a terminal Rejected(reason), so the 0.1-token fee is lost.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest push brings this issue back at tips/tip-1154.md:160:

🔴 A migrated account's 0x05 forced exit during a migration backlog can still be rejected, and the 0.1-token fee is lost. The new zero-copy path does not settle which code hash it checks against. Line 160 verifies against Tempo's nonzero commitment "under the same rules". TIP-1107 step 1 (tip-1107.md:169) then requires code hash keccak256(0xef). The zone leaf has no marker, because the copy is still zero. Line 120 instead pairs the code-hash check with the zone copy. Fix: state that for zero-copy forced exits the code-hash check uses the zone copy (empty code required), or skip it, and that only the signature check uses Tempo's commitment.

Why this was flagged

Bob migrates on Tempo, so his commitment is nonzero. His zone copy is still zero, either because the zone was created after T14 or because a backlog is draining (line 98). His owners queue a 0x05-signed forced exit, and it is processed in inbox order. Line 160 now reads Tempo and verifies "against it". Line 126 applies the TIP-1107 rules "exactly as on Tempo" against that commitment. TIP-1107 step 1 (tips/tip-1107.md:169) says that with a nonzero commitment the leaf's codeHash MUST be keccak256(0xef) and rejects anything else. The zone leaf still has keccak256(""), because the marker is installed only when the copy becomes nonzero (line 108). An implementer who reads lines 126 and 160 together therefore rejects the exit. crates/zones/exithatch makes that a terminal rejection, and the fee is not refunded. Line 120 says the code-hash check reads only the copy, which contradicts this reading. So the fix this push makes for the earlier line-160 finding depends on an unstated rule.

Verification: Line 160 verifies a 0x05 signature against Tempo's nonzero commitment when the zone copy is zero, so tips/tip-1107.md:169 requires keccak256(0xef), but line 120 reads the zone copy, whose leaf still holds keccak256(""). Step 2.1 then rejects a valid owner quorum. The fee is then lost (crates/zones/exithatch/README.md:117 gives a terminal Rejected).

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest push brings back an issue flagged in an earlier review; replied in that comment's thread.

Still open from earlier reviews (5):

  • 🔴 tips/tip-1154.md:31 — Accounts registered by first use on Tempo never get a zone copy. Their colliding original key stays live on every zone…
  • Also unresolved: 3 minor or pre-existing, 1 blocking on lines changed since (possibly already fixed).

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

This branch has not been deployed

No deployments
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.

4 participants