Skip to content

spec: make non-upgradeability of the enforcing logic normative - #6

Merged
XiaoHai67890 merged 2 commits into
mainfrom
spec/upgradeability-normative
Aug 3, 2026
Merged

XiaoHai67890 merged 2 commits into
mainfrom
spec/upgradeability-normative

Conversation

@everest-an

Copy link
Copy Markdown
Contributor

Raised by @blockbird on the discussion thread after an independent recompute of the Sepolia deployment (script commit-pinned, storage-proof based, block header rebuilt and cross-checked against a second endpoint).

The gap

The linearity guarantee is a property of the deployed code, not of the recorded data. A registry behind an upgrade authority reproduces every published test vector while still admitting a rollback — so "passes the vectors" and "provides the guarantee this ERC claims" are distinct statements.

That makes non-upgradeability a precondition of the non-forgeability property, not a deployment preference. As written, the spec was weaker than the code implementing it: the reference deployment already has both EIP-1967 slots empty and exposes no owner, admin, initializer or proxy, but nothing in the document required that.

Changes

  • Specification — new ### Immutability of the enforcing logic, placed immediately after the sequence == currentSequence + 1 rule it protects. States that a conforming registry MUST NOT be deployed behind an upgrade mechanism able to replace the logic enforcing sequence linearity, state root chaining, or signature validation, and MUST expose no upgrade authority over that logic. Extensions and off-chain tooling MAY still be upgraded independently.
  • Security Considerations — the existing Upgradeability note now points at the normative requirement and says what a verifier should actually check (EIP-1967 slots, absence of owner/admin/initializer/proxy) instead of advising. Restated here deliberately, per the ERC-8312 precedent @blockbird cites: a requirement stated only in Security Considerations gets read as informative.

No behavioural change to the reference implementation — it already satisfies this.

Before merge

erc/erc-8350.md is a byte-copy of upstream ERCS/erc-8350.md (see erc/SUBMISSION.md sync discipline), so the same edit needs to land on ethereum/ERCs#1910 or the copies diverge.

Not self-merging — please review the wording, particularly whether "extensions and off-chain tooling MAY be upgraded independently" is the right carve-out or too permissive.

@everest-an

Copy link
Copy Markdown
Contributor Author

Pushed a second normative section to this branch: Referencing a transition (eb48d21).

Different subject from the upgradeability change, but same category of problem — something the reference implementation gets right that the spec never actually required.

Why now: the same gap was hit twice in a week by two independent implementers on t/29098. @JimmyShi22 first shipped AgentVerifierUpdated(bytes32 transitionId) for ERC-8274, then added spaceId after discussion; @babyblueviper1's composition example carried registry only once it was raised. Two people landing on the same omission independently says the spec was underspecified, not that either of them misread it.

The gap: spaceId is deliberately chain-free — one identifier per agent, not per chain — so two chains may hold different histories under the same spaceId, and (spaceId, transitionId) does not determine which one is meant.

The part worth reviewing carefully, because both @babyblueviper1 and I initially got it wrong: we described registry as a locating annotation, on the grounds that it is not an input to transitionId. True of the struct hash — but the signing domain sets verifyingContract = registry address, so the signed digest cannot be reconstructed without it, and the authorizer's signature cannot be checked at all. A reference omitting the registry is not merely ambiguous, it is unverifiable.

That distinction has a practical consequence: on the "annotation" reading, someone trimming a payload later would drop the field and only discover the breakage when a third party tried to verify. Hence normative MUST rather than SHOULD.

chainId is allowed to stay implicit when the reference is emitted on the same chain as the registry, so the common case costs one field rather than two.

Still not self-merging — this branch now carries two normative additions, both of which deserve wording review.

@babyblueviper1

Copy link
Copy Markdown
Contributor

Confirming from our side, since our example is cited above: we hit this exact gap independently and just fixed it (babyblueviper1/invinoveritas@5bf1c84) after everest-an caught the same "locating annotation" mischaracterization in our own worked example on t/29098#17 — same wording error, same root cause (registry isn't a transitionId input, but it IS the EIP-712 verifyingContract, so the signed digest can't be reconstructed without it).

Two independent implementations landing on the identical mistake, then the identical fix, from two different directions (spec review here, worked example there) is a reasonably strong signal this belongs in the normative text rather than staying an implementation detail either of us could have gotten away with getting wrong.

Everest added 2 commits August 3, 2026 20:07
Raised by @blockbird on ethereum-magicians.org/t/29098 after an independent
recompute of the Sepolia deployment.

The linearity guarantee is a property of the deployed code, not of the recorded
data. A registry behind an upgrade authority reproduces every published test
vector while still admitting a rollback, so "passes the vectors" and "provides
the guarantee this ERC claims" are distinct statements. That makes
non-upgradeability a precondition of the non-forgeability property rather than a
deployment preference, and the spec was therefore weaker than the code
implementing it -- the reference deployment already has both EIP-1967 slots empty
and no owner, admin, initializer or proxy.

- Specification: new "Immutability of the enforcing logic" immediately after the
  sequence rule it protects, stating MUST NOT deploy behind an upgrade mechanism
  able to replace the enforcing logic, and MUST expose no upgrade authority over
  it. Extensions and off-chain tooling MAY still be upgraded.
- Security Considerations: the existing Upgradeability note now points at the
  normative requirement and adds what a verifier should actually check, rather
  than advising. Restated here per the ERC-8312 precedent, on the view that a
  requirement stated only in Security Considerations reads as informative.

Note for sync: erc/erc-8350.md is a byte-copy of upstream ERCS/erc-8350.md, so
this needs the same edit on ethereum/ERCs#1910 before the copies diverge.
Raised in practice twice within a week on t/29098: JimmyShi22 (ERC-8274) first
shipped AgentVerifierUpdated(bytes32 transitionId), then added spaceId; and
babyblueviper1's composition example carried registry only after it was pointed
out. Both landed on the same gap independently, which is a sign the spec was
underspecified rather than that either implementer misread it.

The gap: spaceId is deliberately chain-free (one identifier per agent, not per
chain), so two chains may hold different histories under the same spaceId and
(spaceId, transitionId) does not determine which one is meant.

Stated normatively here, and — the part both of us initially got wrong — as a
*verification* requirement rather than a resolution convenience. The signing
domain sets verifyingContract = registry address, so without the registry the
signed digest cannot be reconstructed and the authorizer's signature cannot be
checked at all. Describing registry as a "locating annotation" understates it:
a reference that omits it is not independently verifiable, and anyone trimming
payloads on the annotation reading would break verification without noticing.

Also notes chainId MAY be implicit when the reference is emitted on the same
chain as the registry, so the common case costs one field, not two.
@XiaoHai67890
XiaoHai67890 force-pushed the spec/upgradeability-normative branch from eb48d21 to ad8d8d4 Compare August 3, 2026 12:09
@XiaoHai67890
XiaoHai67890 merged commit 0050cfb into main Aug 3, 2026
6 checks passed
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.

3 participants