Skip to content

[design] Unify DVT crypto layer (AAStarValidator) with SuperPaymaster economic layer — stake-gated, operator-bound registration #321

Description

@jhfnetboy

Context — two DVT contract systems that don't talk to each other

Today the DVT has two independent, disconnected on-chain authorization systems:

AAStarValidator (YetAnotherAA-Validator) — crypto layer DVTValidator + Registry + GTokenStaking (SuperPaymaster) — economic layer
Identity key nodeId (bytes32) + BLS G1 pubkey operator address (EOA) + ROLE_DVT + GToken stake
Stores registeredKeys[nodeId]→pubkey, isRegistered[nodeId]no operator, no stake isValidator[address], GToken lock, role
Register registerPublicKey(nodeId, pubkey)no stake gate (deployed AAStarBLSAlgorithm is onlyOwner) addValidator(address)checks Registry.hasRole(ROLE_DVT) + locked stake ≥ minStake
Verify verifyAggregateSignature(nodeIds, sig, msgPoint) — checks isRegistered only, no stake/liveness _requireActiveValidator — real-time role+stake liveness on every consensus call
Job who can co-sign + verify the aggregate BLS signature who staked + slashing + liveness

The problem is not overlap/override — it's a break. The signing identity
(nodeId+pubkey in AAStarValidator) and the economic identity (address+stake in
SuperPaymaster) are never bound to each other. Consequences:

  • A BLS pubkey can be registered in AAStarValidator with zero stake — the signing
    key has no skin in the game.
  • An operator can stake in SuperPaymaster with no effect on who can actually co-sign.
  • Slashing can't punish a misbehaving signing node: DVTValidator doesn't know which
    nodeId belongs to which operator address.
  • The production signing/verification path (dvt nodes → registerPublicKey
    verifyAggregateSignature) is not connected to the economic layer at all.

Business goal

  1. Single stake source of truth — GToken staked under ROLE_DVT in the SuperPaymaster
    Registry/GTokenStaking is the only place stake is managed. Every other contract
    reads it, never re-implements it.
  2. Bind signing ↔ stake — a nodeId/pubkey is tied to a staked operator, so
    co-signing rights require economic backing and a misbehaving signer can be slashed
    through its operator.
  3. Permissionless self-registration — an operator can stake + register their own DVT
    node without owner approval, with an owner toggle so we can bootstrap in a
    permissioned (onlyOwner) mode first and flip to permissionless when ready.
  4. Keep the hot verification path cheap — no per-signature cross-contract reads.

Technical plan (proposed — to be challenged/finalized)

Bind by operator address; defer stake to SuperPaymaster; keep pubkey local.

YetAnotherAA-Validator — AAStarValidator

  • Add mapping(bytes32 => address) public nodeOperator; — the operator that owns a nodeId.
  • registerPublicKey(bytes32 nodeId, bytes pubkey) called by the operator
    (msg.sender):
    • record nodeOperator[nodeId] = msg.sender.
    • stake gate (when enabled): require
      Registry.hasRole(ROLE_DVT, msg.sender) and
      GTokenStaking.roleLocks(msg.sender, ROLE_DVT).amount ≥ Registry.getRoleConfig(ROLE_DVT).minStake.
    • toggle: bool public requireStake (owner-settable). false → bootstrap
      (onlyOwner registers, as today). true → permissionless-but-staked. This is the
      ①stake-check + ②toggle asked for.
  • Store the SuperPaymaster Registry address (owner-settable), read-only.
  • (Optional, later) verifyAggregateSignature adds a real-time liveness read per
    participating operator — off by default to keep gas/coupling low; discuss.

SuperPaymaster — Registry / GTokenStaking / DVTValidator

  • Confirm/keep the read interfaces the crypto layer needs: Registry.hasRole,
    Registry.getRoleConfig(ROLE_DVT).minStake, Registry.GTOKEN_STAKING(),
    GTokenStaking.roleLocks(user, roleId). (All appear to exist as of DVTValidator 0.6.0.)
  • Slashing linkage: to slash a misbehaving signer, DVTValidator needs
    nodeId → operator. Options: (a) AAStarValidator emits NodeBound(nodeId, operator)
    and off-chain relays to a slash proposal; (b) a small view/callback. Discuss which.
  • ROLE_DVT = keccak256("DVT") — align the roleId constant across both repos.

Roles / responsibilities after unification

  • SuperPaymaster = economic/governance single source of truth: GToken stake,
    ROLE_DVT, slashing, liveness.
  • AAStarValidator = cryptographic layer: BLS pubkey registry + aggregate
    verification; registration reads SuperPaymaster for the stake gate and binds
    nodeId → staked operator.
  • Link = operator address, stored per nodeId.

Why not fully consolidate into SuperPaymaster

verifyAggregateSignature is the hot path (called per UserOp). Keeping pubkeys local
avoids a cross-contract read per signature (gas + tight coupling). We read the economic
layer once at registration, not per verification.

Post-implementation flow

operator EOA
 └─(1) SuperPaymaster: lock GToken → registerRole(ROLE_DVT)          [stake: single entry]
 └─(2) AAStarValidator.registerPublicKey(nodeId, pubkey) as operator
        → nodeOperator[nodeId]=operator
        → if requireStake: assert Registry.hasRole(ROLE_DVT,op) && lock≥minStake   [①②③]
 node co-signs UserOps  →  AAStarValidator.verifyAggregateSignature(nodeIds,…)
        → (optional) assert each nodeId's operator still active                     [liveness]
 misbehavior  →  DVTValidator: slash operator via nodeId→operator binding           [slashing]
 discovery    →  gossip roster {nodeId, apiEndpoint}; coordinator maps nodeId→operator to
                 check active/slashed state before including in a quorum

Open questions (for the challenge / finalization)

  1. Slashing linkage — how does DVTValidator resolve nodeId → operator? Event-relay
    vs on-chain view vs storing the binding in SuperPaymaster instead.
  2. Real-time liveness at verify — worth the per-verify gas, or off-chain pruning
    only (as DVTValidator already assumes)?
  3. Deployed-contract reconciliation — repo AAStarValidator.sol (permissionless
    source) vs deployed AAStarBLSAlgorithm v0.20.0 (onlyOwner): which is canonical,
    and does this change target a redeploy?
  4. roleId + minStake governance — confirm ROLE_DVT constant + who tunes minStake
    (Registry.configureRole, owner/governance).
  5. One operator, many nodes? — does an operator run multiple nodeIds under one stake,
    or one stake per nodeId? Affects the binding cardinality.

Cross-repo: AAStarCommunity/YetAnotherAA-Validator (AAStarValidator changes) +
AAStarCommunity/SuperPaymaster (interface confirm + slashing linkage). This issue is
filed to both. Related: YetAnotherAA-Validator#157 (N-of-M), #50 (hardening).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions