# Copyright (c) 2018-2023 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. # Helpers and functions pertaining to managing the validator set when (NimMajor, NimMinor) < (1, 4): {.push raises: [Defect].} else: {.push raises: [].} import ./datatypes/[phase0, altair, bellatrix], ./helpers export helpers const SEED_SIZE = sizeof(Eth2Digest) ROUND_SIZE = 1 POSITION_WINDOW_SIZE = 4 PIVOT_VIEW_SIZE = SEED_SIZE + ROUND_SIZE TOTAL_SIZE = PIVOT_VIEW_SIZE + POSITION_WINDOW_SIZE # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/phase0/beacon-chain.md#compute_shuffled_index # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/phase0/beacon-chain.md#compute_committee # Port of https://github.com/protolambda/zrnt/blob/master/eth2/beacon/shuffle.go # Shuffles or unshuffles, depending on the `dir` (true for shuffling, false for unshuffling func shuffle_list*(input: var seq[ValidatorIndex], seed: Eth2Digest) = let list_size = input.lenu64 if list_size <= 1: return var buf {.noinit.}: array[TOTAL_SIZE, byte] # Seed is always the first 32 bytes of the hash input, we never have to change # this part of the buffer. buf[0..<32] = seed.data # The original code includes a direction flag, but only the reverse direction # is used in eth2, so we simplify it here for r in 0'u8..= MAX_EFFECTIVE_BALANCE * random_byte: return Opt.some(candidate_index) i += 1 # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/phase0/beacon-chain.md#get_beacon_proposer_index func get_beacon_proposer_index*( state: ForkyBeaconState, cache: var StateCache, slot: Slot): Opt[ValidatorIndex] = let epoch = get_current_epoch(state) if slot.epoch() != epoch: # compute_proposer_index depends on `effective_balance`, therefore the # beacon proposer index can only be computed for the "current" epoch: # https://github.com/ethereum/consensus-specs/pull/772#issuecomment-475574357 return Opt.none(ValidatorIndex) cache.beacon_proposer_indices.withValue(slot, proposer) do: return proposer[] do: # Return the beacon proposer index at the current slot. var buffer: array[32 + 8, byte] buffer[0..31] = get_seed(state, epoch, DOMAIN_BEACON_PROPOSER).data # There's exactly one beacon proposer per slot - the same validator may # however propose several times in the same epoch (however unlikely) let # active validator indices are kept in cache but sorting them takes # quite a while indices = get_active_validator_indices(state, epoch) var res: Opt[ValidatorIndex] for epoch_slot in epoch.slots(): buffer[32..39] = uint_to_bytes(epoch_slot.asUInt64) let seed = eth2digest(buffer) let pi = compute_proposer_index(state, indices, seed) if epoch_slot == slot: res = pi cache.beacon_proposer_indices[epoch_slot] = pi return res # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/phase0/beacon-chain.md#get_beacon_proposer_index func get_beacon_proposer_index*(state: ForkyBeaconState, cache: var StateCache): Opt[ValidatorIndex] = get_beacon_proposer_index(state, cache, state.slot) func get_beacon_proposer_index*(state: ForkedHashedBeaconState, cache: var StateCache, slot: Slot): Opt[ValidatorIndex] = withState(state): get_beacon_proposer_index(forkyState.data, cache, slot) # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/phase0/validator.md#aggregation-selection func is_aggregator*(committee_len: uint64, slot_signature: ValidatorSig): bool = let modulo = max(1'u64, committee_len div TARGET_AGGREGATORS_PER_COMMITTEE) bytes_to_uint64(eth2digest( slot_signature.toRaw()).data.toOpenArray(0, 7)) mod modulo == 0