feat(eip8130): single k1 path, EIP-2 low-s, DEFAULT_EOA_REVOKED flag#3605
Open
chunter-cb wants to merge 5 commits into
Open
feat(eip8130): single k1 path, EIP-2 low-s, DEFAULT_EOA_REVOKED flag#3605chunter-cb wants to merge 5 commits into
chunter-cb wants to merge 5 commits into
Conversation
Standardise on a single non-malleable recovery for every transaction- authorizing surface: - `dispatch::ecrecover` (the `ECRECOVER_AUTHENTICATOR` sentinel) now rejects malleable upper-half `s` (EIP-2) instead of canonicalizing and accepting it, so the recovered signer commits to one signature encoding. Must stay in lockstep with the deployed `AccountConfiguration` reference. - Split a shadow-only `ActorAuthorizer::implicit_eoa_owner` helper. The empty-`sender` path in the verifier recovers the signer exactly once (checked, low-s) and authorizes the implicit owner directly, instead of synthesizing an `address(0) || sig` blob and re-recovering through dispatch. The wire `address(0)` form still recovers, since there the account is wire-supplied and the signature must be bound to it.
Collaborator
🟡 Heimdall Review Status
|
Track the EIP-8130 / contract rework (ethereum/EIPs#11815, base/eip-8130#18) that unifies the secp256k1 authentication path and removes the revoked authenticator sentinel. - Rename ECRECOVER_AUTHENTICATOR -> K1_AUTHENTICATOR (still address(1)): the single secp256k1 identity for both the implicit default EOA and any explicit k1 actor. `address(0)` is now purely the empty "no actor configured" sentinel and is never a valid authenticator selector (rejected as non-canonical). - Delete REVOKED_AUTHENTICATOR (type(uint160).max). Revocation folds into a new DEFAULT_EOA_REVOKED (0x01) bit in a `flags` byte appended to the packed AccountState slot (bits 184..192). - Replace the address(0) implicit-EOA route with a single `authorize_k1` resolver mirroring `_authenticateK1`: a live default EOA (recovered == account and the flag unset) is an unrestricted owner from one account-state read; otherwise the signer must carry an explicit k1 actor_config entry. The empty-`sender` verifier path feeds this resolver after its single recovery. - Drop the now-dead Revoked / ImplicitEoa* error variants. - txpool: collapse the structural authenticator floor to reject only address(0) (< K1_AUTHENTICATOR); drop the obsolete REVOKED-sentinel rejection tests.
Adopts the inline self-key model (base/eip-8130#20, ethereum/EIPs#11816): the account's own secp256k1 ("self") scope/policyType/expiry now live in the packed account-state slot alongside DEFAULT_EOA_REVOKED, so the self key — full owner or scoped — resolves in a single SLOAD. The actor_config(self) slot is reserved for a mutually-exclusive non-k1 self authenticator. - AccountState: decode inline default_eoa_{scope,policy_type,expiry}. - authorize_k1: resolve the self path from the inline config; a set DEFAULT_EOA_REVOKED flag rejects with the new DefaultEoaRevoked error (revoked, or a non-k1 self is live) rather than falling through to a bound lookup; honor inline expiry and gate. - get_policy: mirror the contract's (policy_type, target, commitment) shape with inline-self resolution.
authorize_k1 granted owner access based on a caller-supplied B256 recovered signer; as a pub entrypoint that trusted its caller, it was a risky surface on a security-critical path (the only caller, verify_sender, recovers correctly, but nothing enforced that). Introduce RecoveredActorId, a newtype whose private address is only producible via a recovery constructor (recover_k1 for the K1_AUTHENTICATOR wire form; recover_eoa_sender for the empty-sender EOA path). authorize_k1 now consumes the token, lifting the "must have recovered first" precondition into the type system without re-recovering. The k1 ecrecover is consolidated into RecoveredActorId::recover_k1 (dispatch delegates to it), so the dispatch path and the token cannot drift.
Contributor
Review SummaryClean PR. The Reviewed all 12 changed files. No new issues found. Verified:
|
Contributor
✅ base-std fork tests: all 616 passedbase/base is fully in sync with the base-std spec.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The EIP-8130 secp256k1 authentication model for
base-execution-eip8130, matching the spec + contract reference in ethereum/EIPs#11815 and base/eip-8130#18.secp256k1 authentication
K1_AUTHENTICATOR(address(1)) is the single secp256k1 identity: the implicit default EOA and every explicitly-registered k1 actor authenticate through it.address(0)is the empty / "no actor configured" sentinel and is not an authenticator selector — it is rejected as non-canonical by dispatch, and structurally below the txpool'sK1_AUTHENTICATORfloor.dispatch::ecrecoverenforces EIP-2 low-s(andv ∈ {27,28}), so every authorizing surface that feeds the transaction id commits to one signature encoding. This is byte-parity with the deployedAccountConfiguration._recoverSigner.Implicit default EOA and revocation
The implicit default EOA is live when its
actor_configslot is empty and theDEFAULT_EOA_REVOKEDflag is unset. The flag is0x01in aflagsbyte in the packedAccountStateslot (bits184..192); there is no separate revoked-authenticator sentinel.authorize_k1mirrors_authenticateK1after recovery:recovered == account && !DEFAULT_EOA_REVOKED→ unrestricted owner, from one account-state read.actor_configentry (a scoped / re-enabled self key, or any other k1 actor).This relies on the contract invariant an explicit self-actor entry implies the flag is set, so a live self never has a config to shadow.
Recover the EOA sender once
The empty-
senderverifier path recovers the signer a single time (checked, low-s) and resolves the implicit owner directly viaauthorize_k1, rather than synthesizing anaddress(0) || sigblob and re-recovering through dispatch.Layer-by-layer
base-common-consensus:K1_AUTHENTICATOR,DEFAULT_EOA_REVOKEDconstants.base-execution-eip8130:AccountState.flags+default_eoa_revoked(); flag-awareis_actor;authorize_k1; dispatch k1 routing +address(0)reject.base-execution-txpool: structural authenticator floor rejectsaddress(0)(< K1_AUTHENTICATOR).Parity note
The native low-
senforcement and the k1 / flag model stay byte-parity with the deployedAccountConfiguration(base/eip-8130#18). A divergence re-pins the canonical CREATE2 address, caught by the registry drift test.