# Secret Single Leader Election (SSLE) in Eth2 ### Parameters | Name | Value | | - | - | | `SHUFFLE_INDICES` | `[i for i in range(16)] + [(16 + i % 16) * 2**(i // 16) for i in range(112)]` | | `SHUFFLE_SIZE` | `len(SHUFFLE_INDICES)` | | `MAX_SHUFFLE_POOL_SIZE` | `2048` | | `MAX_SHUFFLE_REGISTRATIONS` | `8` | | `DOMAIN_SHUFFLE_REGISTRATION` | `???` | ### Type aliases * alias `G1Point` to `Bytes48` * alias `BLSPubkey` to `G1Point` (removing the alias to `Bytes48`) * alias `ShuffleCommitment` to `G1Point` * alias `ShuffleProof` to `???` ### New containers ##### `ShufflePair` ```python class ShufflePair(Container): base: G1Point # blinding base key: G1Point # equal to `base * shuffle_secret` for some `shuffle_secret` ``` ##### `ShuffleSecretSeed` ```python class ShuffleSecretSeed(Container): index: ValidatorIndex commitment: ShuffleCommitment # shuffle permutation commitment ``` ##### `ShuffleRegistration` ```python class ShuffleRegistration(Container): slot: Slot # slot at which validator was eligible for the shuffle pool key: G1Point # equal to `bls.G1 * shuffle_secret` for some `shuffle_secret` ``` ##### `ShuffleReveal` ```python class ShuffleReveal(Container): commitment: ShuffleCommitment # shuffle permutation commitment pairs: Vector[ShufflePair, SHUFFLE_SIZE] # shuffled pairs proof: ShuffleProof # zero-knowledge shuffle proof ``` ##### `SignedShuffleRegistration` ```python class SignedShuffleRegistration(Container): message: ShuffleRegistration signature: BLSSignature ``` ### New container properties ##### `BeaconBlockBody` ```py shuffle_reveal: ShuffleReveal shuffle_registrations: List[SignedShuffleRegistration, MAX_SHUFFLE_REGISTRATIONS] ``` ##### `BeaconState` ```py shuffle_pool: List[ShufflePair, MAX_SHUFFLE_POOL_SIZE] ``` ##### `Validator` ```py in_shuffle_pool: bool ``` ### Process shuffle registrations Add the line `for_ops(body.shuffle_registrations, process_shuffle_registration)` in `process_operations`. ```python def get_shuffle_pool_registrant_index(state: BeaconState, slot: Slot) -> ValidatorIndex: # Generalize `get_beacon_proposer_index` to slots other than the current one def process_shuffle_registration(state: BeaconState, signed_registration: SignedShuffleRegistration) -> None: registration = signed_registration.message # Validators have `MAX_SHUFFLE_POOL_SIZE // 2` slots to submit their shuffle registration assert state.slot - MAX_SHUFFLE_POOL_SIZE // 2 < registration.slot <= state.slot # Verify the registrant is not already in the shuffle pool registrant_index = get_shuffle_pool_registrant_index(state, registration.slot) registrant = state.validators[registrant_index] assert registrant.in_shuffle_pool == False # Verify the shuffle registration assert registration.key != bls.Z1 domain = get_domain(state, DOMAIN_SHUFFLE_REGISTRATION, compute_epoch_at_slot(registration.slot)) signing_root = compute_signing_root(registration, domain) assert bls.Verify(registrant.pubkey, signing_root, signed_registration.signature) # Add the registrant shuffle pair to the shuffle pool state.shuffle_pool.append(ShufflePair(base=bls.G1, key=registration.key)) registrant.in_shuffle_pool = True ``` ### Process shuffle reveal ```python def process_shuffle_reveal(state: BeaconState, block: BeaconBlock) -> None: # Fetch and remove the proposer shuffle pair (zeroth shuffle pair) from the shuffle pool proposer_shuffle_pair = state.shuffle_pool.pop(0) state.validators[block.proposer_index].in_shuffle_pool = False # Compute shuffle secret from proposer index and shuffle commitment shuffle_secret = bytes_to_uint256(hash_tree_root(ShuffleSecretSeed( index=block.proposer_index, commitment=block.body.shuffle_reveal.commitment, ))) # Verify the proposer shuffle pair and secret are consistent assert bls.multiply(proposer_shuffle_pair.base, secret) == proposer_shuffle_pair.key # Verify the shuffle proof target_shuffle_pairs = [state.shuffle_pool[i] for i in SHUFFLE_INDICES if i < len(state.shuffle_pool)] dummy_shuffle_pairs = [ShufflePair(base=bls.G1, key=bls.Z1)]*(SHUFFLE_SIZE - len(target_shuffle_pairs)) assert is_valid_shuffle_proof( non_shuffled_pairs=target_shuffle_pairs + dummy_shuffle_pairs, shuffle_reveal=block.body.shuffle_reveal, ) # Record the shuffled pairs to the shuffle pool dummy_shuffle_pairs_counter = 0 for i in range(SHUFFLE_SIZE): if block.body.shuffle_reveal.pairs[i].key == bls.Z1: dummy_shuffle_pairs_counter += 1 else: shuffle_index = SHUFFLE_INDICES[i - dummy_shuffle_pairs_counter] state.shuffle_pool[shuffle_index] = block.body.shuffle_reveal.pairs[i] ``` ### Beacon proposer selection In `process_block_header` replace the line ```python assert block.proposer_index == get_beacon_proposer_index(state) ``` by ```python if len(state.shuffle_pool) < MAX_SHUFFLE_POOL_SIZE // 2: # If the shuffle pool is too small the propooser is not secret assert block.proposer_index == get_beacon_proposer_index(state) # Verify the shuffle reveal is zero assert block.body.shuffle_reveal == ShuffleReveal() else: process_shuffle_reveal(state, block) ```