Try   HackMD

The Case for Execution Layer Initiated Consolidations

There are two options for initiating validator consolidations:

  1. Implement a new Beacon Chain operation
  2. Trigger consolidations via the Execution Layer

Before we get into the reasons why I think we should make consolidations EL initiated, I want to first address the main argument against making them EL initiated: complexity.

At this moment the prevailing sentiment is that making validator consolidations a beacon chain operation is the simplest path forward. I'm not so sure this is true. Let's compare them:

Beacon Chain Operation

The consensus layer devs will follow the same playbook as BLS_TO_EXECUTION_CHANGE. They will roughly need the following:

  1. A new gossip topic
  2. New gossip validation rules for spam prevention & rate limiting
  3. API changes and tooling to submit SignedConsolidations & corresponding changes to submit via CLI

None of this is particularly difficult.

EL Initiated Operation

The execution layer devs will follow the same playbook as EIP-7002. They will roughly need the following:

  1. Generalize the 7002 exit contract.
  2. A second instance of this contract for consolidations
  3. Add a consolidations field tacked onto the end of the block (just like deposits in 6110 or exits in 7002, both of which are implemented in Pectra)

I'll expand on each of these.

Generalize the 7002 Contract

Just ctrl+F replace the word exit with operation in all pseudo-code. Also remove references to the exact size of the input data so that:

def trigger_exit(Bytes48: validator_pubkey):

in the pseudocode becomes:

def trigger_operation(BytesN: input_data)

If it wasn't already clear, generalizing the contract is only a conceptual change. The 7002 contract in its most basic form is simply a queue with some rate limiting & fee calculation applied.

Launch A Second Instance of the 7002 Contract for Consolidations

To accommodate a different operation besides exits (like consolidations), the actual code changes we need to make to the 7002 exit contract are trivial. Just modify:

Thus the data type would thus change from:

rlp_encoded_exit = RLP([
    source_address,
    validator_pubkey,
    amount,
])

to

rlp_encoded_consolidation = RLP([
    source_address,
    source_pubkey,
    destination_pubkey,
])

Add A Consolidations Field to the Execution Block

Simply change the block structure to:

block_body_rlp = RLP([
    field_0,
    ...,
    # Latest block body field before exits
    field_n,

    [exit_0, ..., exit_k],
+   [consolidation_0, ..., consolidation_k]
])

That's it! Surely we can manage that right? If these are the only necessary changes to the contract then we can reuse much of the auditing work. Given that the EL devs are implementing this pattern twice already in Pectra, shouldn't it be trivial to add a third instance? Is this not at least comparable to the complexity of what the consensus layer devs would have to do?

Why EL Initiated?

Several reasons:

  1. Every staking entity we've spoken with (Lido, RocketPool, EigenLayer) has expressed their preference for EL initiated consolidations.
  2. Lido & Eigenlayer require changes to their existing contracts before the fork if we make consolidations a beacon chain operation. Lido has expressed that this extra work will delay them in their actual consolidation of validators.
  3. EL initiated consolidations require less trust for decentralized staking pools (no oracles).
  4. EL initiated consolidations allow the consolidation of validators with different withdrawal credentials.
  5. We've designed staking around the philosophy that the withdrawal address owns the validator & EL initiated consolidations are more consistent with that.