Skip to content

Blocks may carry two AttestationData for one slot, which no leanVM proof can express #1209

Description

@dimka90

Summary

The spec permits a block whose proof cannot be built. A block may carry several distinct AttestationData for the same slot, but a leanVM aggregate carries at most one message per epoch, and the epoch is the slot. The proposer then fails to produce its block proof and the slot is lost.

Two of three clients on a mixed devnet are losing slots to this today.

Where the spec stands

The block rules constrain the number of distinct data and forbid repeats, but say nothing about two different data sharing a slot:

  • spec/forks/lstar/config.py:58MAX_ATTESTATIONS_DATA = 8
  • spec/forks/lstar/state_transition.py:218-221 — rejects a block whose distinct data exceed the cap (TOO_MANY_ATTESTATION_DATA)
  • spec/forks/lstar/fork_choice.py:619 — rejects a block that repeats an AttestationData (DUPLICATE_ATTESTATION_DATA)
  • spec/forks/lstar/block_production.py:129 — candidates are ordered by target slot, with the cap applied at line 148; nothing restricts two candidates that share data.slot

Validators disagreeing within a slot is ordinary: some have seen the slot's block and some have not, so they vote for different heads, or different targets, at the same slot. Each such vote is a distinct, valid AttestationData at one slot, and the spec invites a proposer to include them all.

Where the proof system stands

leanVM 022ec377:

  • crates/rec_aggregation/src/aggregation.rs check_signer_set requires strictly increasing epochs across XMSS claim groups, so one message per epoch
  • plan_coverage keeps "the epoch-to-message function every contributor must agree on" and keys XMSS claims as (epoch, key), with the message derived from the epoch
  • a second, different message at one epoch returns AggregationError::ConflictingMessages

The proposer's own signature occupies the block's own slot, so an attestation for that slot collides with it too.

Observed

Three-client devnet (gean, ethlambda, lantern), 14 validators, 4 committees, all on leanVM 022ec377, 8 hours:

client slots lost to this
lantern 186 (leanvm: merge_many_type_1 failed: two claims at one epoch carry different messages)
ethlambda 41 (failed to merge single-message aggregates into a multi-message aggregate: prover failure: two claims at one epoch carry different messages)
gean 0 — it keeps one AttestationData per slot, a rule it added itself

Traced example: two aggregates for slot 86 with targets 82 and 83 arrived, both went into the block for slot 87, and sealing failed. Reported as lambdaclass/ethlambda#617 and bitminetech/lantern#196.

What should the rule be?

The spec needs to settle what a block may carry, so every client filters identically. Today it is silent, so each client decides for itself: gean keeps one AttestationData per slot, ethlambda and lantern do not, and they lose slots for it.

The shape we implemented in gean, offered as a starting point rather than a conclusion:

  • a block carries at most one AttestationData per slot, and none at the block's own slot
  • build_block (block_production.py) selects accordingly
  • block validation says whether a block breaking the rule is invalid, or valid but unprovable

Note that filtering has a cost of its own: the dropped votes are honest, and they are exactly the votes cast when validators disagree, so justification slows at the moment the network most needs it. If that cost is unacceptable, the alternative is on the leanVM side, below.

Longer term

The restriction is stricter than XMSS requires. The signature rule is one message per key per epoch; leanVM enforces one message per epoch across all keys. SPHINCS in the same file keys claims on (key, message) ("one key signing two messages is two claims") while XMSS derives the message from the epoch. Whether that can be relaxed is a question for leanVM, raised separately; if it is relaxed, this spec rule can be dropped again.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions