def compute_shuffled_index(index: int, index_count: int, seed: Hash32, shuffle_round_count: int) -> int: """ Return `p(index)` in a pseudorandom permutation `p` of `0...index_count-1` with ``seed`` as entropy. Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ if index >= index_count: raise ValidationError( f"The given `index` ({index}) should be less than `index_count` ({index_count}" ) if index_count > MAX_INDEX_COUNT: raise ValidationError( f"The given `index_count` ({index_count}) should be equal to or less than " f"`MAX_INDEX_COUNT` ({MAX_INDEX_COUNT}") new_index = index for current_round in range(shuffle_round_count): pivot = int.from_bytes( hash_eth2(seed + current_round.to_bytes(1, 'little'))[0:8], 'little', ) % index_count flip = (pivot + index_count - new_index) % index_count position = max(new_index, flip) source = hash_eth2(seed + current_round.to_bytes(1, 'little') + (position // 256).to_bytes(4, 'little')) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 new_index = flip if bit else new_index return new_index
def _get_seed( state: "BeaconState", epoch: Epoch, domain_type: DomainType, randao_provider: RandaoProvider, epoch_provider: Callable[[Epoch], Hash32], committee_config: CommitteeConfig, ) -> Hash32: randao_mix = randao_provider( state, Epoch(epoch + committee_config.EPOCHS_PER_HISTORICAL_VECTOR - committee_config.MIN_SEED_LOOKAHEAD - 1), committee_config.EPOCHS_PER_HISTORICAL_VECTOR, ) epoch_as_bytes = epoch_provider(epoch) return hash_eth2(domain_type + epoch_as_bytes + randao_mix)
def _find_proposer_in_committee( validators: Sequence[Validator], committee: Sequence[ValidatorIndex], epoch: Epoch, seed: Hash32, max_effective_balance: Gwei, ) -> ValidatorIndex: base = int(epoch) i = 0 committee_len = len(committee) while True: candidate_index = committee[(base + i) % committee_len] random_byte = hash_eth2(seed + (i // 32).to_bytes(8, "little"))[i % 32] effective_balance = validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= max_effective_balance * random_byte: return candidate_index i += 1
def _main(): config_type = Minimal config = _load_config(config_type) override_lengths(config) key_set = load_yaml_at(KEY_DIR / KEY_SET_FILE) pubkeys = () privkeys = () withdrawal_credentials = () keymap = {} for key_pair in key_set: pubkey = decode_hex(key_pair["pubkey"]) privkey = int.from_bytes(decode_hex(key_pair["privkey"]), "big") withdrawal_credential = ( config.BLS_WITHDRAWAL_PREFIX.to_bytes(1, byteorder="big") + hash_eth2(pubkey)[1:]) pubkeys += (pubkey, ) privkeys += (privkey, ) withdrawal_credentials += (withdrawal_credential, ) keymap[pubkey] = privkey deposits, _ = create_mock_deposits_and_root(pubkeys, keymap, config, withdrawal_credentials) eth1_block_hash = b"\x42" * 32 # NOTE: this timestamp is a placeholder eth1_timestamp = 10000 state = initialize_beacon_state_from_eth1( eth1_block_hash=eth1_block_hash, eth1_timestamp=eth1_timestamp, deposits=deposits, config=config, ) genesis_time = int(time.time()) print(f"creating genesis at time {genesis_time}") genesis_state = state.copy(genesis_time=genesis_time) print(genesis_state.hash_tree_root.hex()) genesis_file_path = RESOURCE_DIR / GENESIS_FILE output_file = open(genesis_file_path, "wb") output_file.write(ssz.encode(genesis_state)) output_file.close() print(f"genesis is saved in {genesis_file_path}")
def test_get_seed( genesis_state, committee_config, slots_per_epoch, min_seed_lookahead, activation_exit_delay, epochs_per_historical_vector, ): def mock_get_randao_mix(state, epoch, epochs_per_historical_vector): return hash_eth2( state.hash_tree_root + epoch.to_bytes(32, byteorder="little") + epochs_per_historical_vector.to_bytes(32, byteorder="little")) def mock_get_active_index_root(state, epoch, epochs_per_historical_vector): return hash_eth2( state.hash_tree_root + epoch.to_bytes(32, byteorder="little") + slots_per_epoch.to_bytes(32, byteorder="little") + epochs_per_historical_vector.to_bytes(32, byteorder="little")) state = genesis_state epoch = 1 state = state.copy(slot=compute_start_slot_of_epoch( epoch, committee_config.SLOTS_PER_EPOCH)) epoch_as_bytes = epoch.to_bytes(32, "little") seed = _get_seed( state=state, epoch=epoch, randao_provider=mock_get_randao_mix, active_index_root_provider=mock_get_active_index_root, epoch_provider=lambda *_: epoch_as_bytes, committee_config=committee_config, ) assert seed == hash_eth2( mock_get_randao_mix( state=state, epoch=(epoch + epochs_per_historical_vector - min_seed_lookahead - 1), epochs_per_historical_vector=epochs_per_historical_vector, ) + mock_get_active_index_root( state=state, epoch=epoch, epochs_per_historical_vector=epochs_per_historical_vector, ) + epoch_as_bytes)
def create_keypair_and_mock_withdraw_credentials( config: Eth2Config, key_set: Sequence[Dict[str, Any]] ) -> Tuple[Tuple[BLSPubkey, ...], Tuple[int, ...], Tuple[Hash32, ...]]: pubkeys: Tuple[BLSPubkey, ...] = () privkeys: Tuple[int, ...] = () withdrawal_credentials: Tuple[Hash32, ...] = () for key_pair in key_set: pubkey = BLSPubkey(decode_hex(key_pair["pubkey"])) privkey = int.from_bytes(decode_hex(key_pair["privkey"]), "big") withdrawal_credential = Hash32( config.BLS_WITHDRAWAL_PREFIX.to_bytes(1, byteorder="big") + hash_eth2(pubkey)[1:]) pubkeys += (pubkey, ) privkeys += (privkey, ) withdrawal_credentials += (withdrawal_credential, ) return (pubkeys, privkeys, withdrawal_credentials)
def verify_merkle_proof(root: Hash32, item: Union[bytes, bytearray], item_index: int, proof: MerkleProof) -> bool: """ Verify a Merkle proof against a root hash. """ leaf = hash_eth2(item) branch_indices = get_branch_indices(item_index, len(proof)) node_orderers = [ identity if branch_index % 2 == 0 else reversed for branch_index in branch_indices ] proof_root = reduce( lambda n1, n2_and_order: _calc_parent_hash(*n2_and_order[1] ([n1, n2_and_order[0]])), zip(proof, node_orderers), leaf, ) return proof_root == root
def _get_seed(state: 'BeaconState', epoch: Epoch, randao_provider: RandaoProvider, active_index_root_provider: ActiveIndexRootProvider, epoch_provider: Callable[[Epoch], Hash32], committee_config: CommitteeConfig) -> Hash32: randao_mix = randao_provider( state, Epoch(epoch + committee_config.EPOCHS_PER_HISTORICAL_VECTOR - committee_config.MIN_SEED_LOOKAHEAD - 1), committee_config.EPOCHS_PER_HISTORICAL_VECTOR, ) active_index_root = active_index_root_provider( state, epoch, committee_config.EPOCHS_PER_HISTORICAL_VECTOR, ) epoch_as_bytes = epoch_provider(epoch) return hash_eth2(randao_mix + active_index_root + epoch_as_bytes)
def test_get_seed( genesis_state, config, slots_per_epoch, min_seed_lookahead, max_seed_lookahead, epochs_per_historical_vector, ): def mock_get_randao_mix(state, epoch, epochs_per_historical_vector): return hash_eth2( state.hash_tree_root + epoch.to_bytes(32, byteorder="little") + epochs_per_historical_vector.to_bytes(32, byteorder="little") ) state = genesis_state epoch = 1 state = state.set( "slot", compute_start_slot_at_epoch(epoch, config.SLOTS_PER_EPOCH) ) epoch_as_bytes = epoch.to_bytes(32, "little") domain_type = signature_domain_to_domain_type( SignatureDomain.DOMAIN_BEACON_PROPOSER ) seed = _get_seed( state=state, epoch=epoch, domain_type=domain_type, randao_provider=mock_get_randao_mix, epoch_provider=lambda *_: epoch_as_bytes, config=config, ) assert seed == hash_eth2( domain_type + epoch_as_bytes + mock_get_randao_mix( state=state, epoch=(epoch + epochs_per_historical_vector - min_seed_lookahead - 1), epochs_per_historical_vector=epochs_per_historical_vector, ) )
def get_beacon_proposer_index(state: BeaconState, config: Eth2Config) -> ValidatorIndex: """ Return the current beacon proposer index. """ current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) domain_type = signature_domain_to_domain_type( SignatureDomain.DOMAIN_BEACON_PROPOSER) seed = hash_eth2( get_seed(state, current_epoch, domain_type, config) + state.slot.to_bytes(8, "little")) indices = get_active_validator_indices(state.validators, current_epoch) return compute_proposer_index( state.validators, indices, seed, config.MAX_EFFECTIVE_BALANCE, config.SHUFFLE_ROUND_COUNT, )
def create_keypair_and_mock_withdraw_credentials( config: Eth2Config, key_set: Sequence[Dict[str, Any]] ) -> Tuple[Tuple[BLSPubkey, ...], Tuple[int, ...], Tuple[Hash32, ...]]: """ NOTE: this function mixes the parsing of keying material with the generation of derived values. Prefer other functions in this module that do the derivation directly. """ pubkeys: Tuple[BLSPubkey, ...] = () privkeys: Tuple[int, ...] = () withdrawal_credentials: Tuple[Hash32, ...] = () for key_pair in key_set: pubkey = BLSPubkey(decode_hex(key_pair["pubkey"])) privkey = int.from_bytes(decode_hex(key_pair["privkey"]), "big") withdrawal_credential = Hash32(config.BLS_WITHDRAWAL_PREFIX + hash_eth2(pubkey)[1:]) pubkeys += (pubkey, ) privkeys += (privkey, ) withdrawal_credentials += (withdrawal_credential, ) return (pubkeys, privkeys, withdrawal_credentials)
def _main(): config_type = Minimal config = _load_config(config_type) override_lengths(config) key_set = load_yaml_at(ROOT_DIR / KEY_SET_FILE) pubkeys = () privkeys = () withdrawal_credentials = () keymap = {} for key_pair in key_set: pubkey = key_pair["pubkey"].to_bytes(48, byteorder="big") privkey = key_pair["privkey"] withdrawal_credential = ( config.BLS_WITHDRAWAL_PREFIX.to_bytes(1, byteorder="big") + hash_eth2(pubkey)[1:]) pubkeys += (pubkey, ) privkeys += (privkey, ) withdrawal_credentials += (withdrawal_credential, ) keymap[pubkey] = privkey deposits, _ = create_mock_deposits_and_root(pubkeys, keymap, config, withdrawal_credentials) eth1_block_hash = b"\x42" * 32 # NOTE: this timestamp is a placeholder eth1_timestamp = 10000 state = initialize_beacon_state_from_eth1( eth1_block_hash=eth1_block_hash, eth1_timestamp=eth1_timestamp, deposits=deposits, config=config, ) genesis_time = int(time.time()) print(f"creating genesis at time {genesis_time}") genesis_state = state.copy(genesis_time=genesis_time) print(genesis_state.hash_tree_root.hex())
def generate_seed(state: 'BeaconState', epoch: Epoch, committee_config: CommitteeConfig) -> Hash32: """ Generate a seed for the given ``epoch``. """ randao_mix = get_randao_mix( state=state, epoch=Epoch(epoch - committee_config.MIN_SEED_LOOKAHEAD), slots_per_epoch=committee_config.SLOTS_PER_EPOCH, latest_randao_mixes_length=committee_config.LATEST_RANDAO_MIXES_LENGTH, ) active_index_root = get_active_index_root( state=state, epoch=epoch, slots_per_epoch=committee_config.SLOTS_PER_EPOCH, activation_exit_delay=committee_config.ACTIVATION_EXIT_DELAY, latest_active_index_roots_length=committee_config. LATEST_ACTIVE_INDEX_ROOTS_LENGTH, ) epoch_as_bytes = epoch.to_bytes(32, byteorder="little") return hash_eth2(randao_mix + active_index_root + epoch_as_bytes)
def create_mock_genesis_validator_deposits_and_root( num_validators: int, config: Eth2Config, pubkeys: Sequence[BLSPubkey], keymap: Dict[BLSPubkey, int]) -> Tuple[Tuple[Deposit, ...], Hash32]: # Mock data withdrawal_credentials = Hash32(b'\x22' * 32) fork = Fork( previous_version=config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), current_version=config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), epoch=config.GENESIS_EPOCH, ) deposit_data_array = tuple() # type: Tuple[DepositData, ...] deposit_data_leaves = tuple() # type: Tuple[Hash32, ...] for i in range(num_validators): deposit_data = create_mock_deposit_data( config=config, pubkeys=pubkeys, keymap=keymap, validator_index=ValidatorIndex(i), withdrawal_credentials=withdrawal_credentials, fork=fork, ) item = hash_eth2(ssz.encode(deposit_data)) deposit_data_leaves += (item, ) deposit_data_array += (deposit_data, ) tree = calc_merkle_tree_from_leaves(deposit_data_leaves) root = get_merkle_root(deposit_data_leaves) genesis_validator_deposits = tuple( Deposit( proof=get_merkle_proof(tree, item_index=i), index=i, deposit_data=deposit_data_array[i], ) for i in range(num_validators)) return genesis_validator_deposits, root
def create_mock_deposit(config, sample_beacon_state_params, keymap, pubkeys, withdrawal_credentials, validator_index): state = BeaconState(**sample_beacon_state_params).copy( slot=1, validator_registry=(), ) fork = Fork( previous_version=config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), current_version=config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), epoch=config.GENESIS_EPOCH, ) deposit_data = create_mock_deposit_data( config=config, pubkeys=pubkeys, keymap=keymap, validator_index=validator_index, withdrawal_credentials=withdrawal_credentials, fork=fork, ) item = hash_eth2(ssz.encode(deposit_data)) test_deposit_data_leaves = (item, ) tree = calc_merkle_tree_from_leaves(test_deposit_data_leaves) root = get_merkle_root(test_deposit_data_leaves) proof = list(get_merkle_proof(tree, item_index=validator_index)) state = state.copy(latest_eth1_data=state.latest_eth1_data.copy( deposit_root=root, ), ) deposit = Deposit( proof=proof, index=validator_index, deposit_data=deposit_data, ) return state, deposit
def _find_proposer_in_committee( validators: Sequence[Validator], committee: Sequence[ValidatorIndex], epoch: Epoch, seed: Hash32, max_effective_balance: Gwei, ) -> ValidatorIndex: """ Loop through the validators in the committee one by one. A validator with higher balance would be chosen as the proposer more likely. It is expected to end in just 1 or 2 rounds. More than `MAX_ROUNDS` rounds is rare and could consider as a bug. Detail: The committee passed in here should consist 'active' validators. An active validator has a balance of at least 17 Ether and at most 32 Ether. This function choose a number between 0 and 1, which is represented by `random_byte / MAX_RANDOM_BYTE`. The probability of a validator chosen as a proposer is `effective_balance/max_effective_balance`. The worst/easiest possible scenario for the loop to reach more rounds is when every validator has 17 Ether and has the 17/32 probability of being chosen. This requires 1 out of (17/32)^100 chance to reach 100 rounds. """ base = int(epoch) committee_len = len(committee) i = 0 while i < MAX_ROUNDS: candidate_index = committee[(base + i) % committee_len] random_byte = hash_eth2(seed + (i // 32).to_bytes(8, "little"))[i % 32] effective_balance = validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= max_effective_balance * random_byte: return candidate_index i += 1 else: raise ImprobableToReach( f"Search for a proposer failed after {MAX_ROUNDS} rounds.")
def mock_get_randao_mix(state, epoch, epochs_per_historical_vector): return hash_eth2( state.hash_tree_root + epoch.to_bytes(32, byteorder="little") + epochs_per_historical_vector.to_bytes(32, byteorder="little") )
def test_chaindb_persist_block_and_unknown_parent(chaindb, block, fork_choice_scoring, seed): n_block = block.transform(("message", "parent_root"), hash_eth2(seed)) with pytest.raises(ParentNotFound): chaindb.persist_block(n_block, n_block.__class__, fork_choice_scoring)
def calc_merkle_tree(items: Sequence[Union[bytes, bytearray]]) -> MerkleTree: """ Calculate the Merkle tree corresponding to a list of items. """ leaves = tuple(hash_eth2(item) for item in items) return calc_merkle_tree_from_leaves(leaves)
def generate_privkey_from_index(index: int) -> int: return (int.from_bytes(hash_eth2(index.to_bytes(32, "little")), "little") % BLS12_381_CURVE_ORDER)
def test_hash(): output = hash_eth2(b'helloworld') assert len(output) == 32
from eth2._utils.bls import bls from eth2._utils.hash import hash_eth2 from eth2.beacon.chains.base import BaseBeaconChain from eth2.beacon.chains.testnet import TestnetChain from eth2.beacon.state_machines.forks.serenity.blocks import SerenityBeaconBlock from eth2.beacon.state_machines.forks.xiao_long_bao.configs import XIAO_LONG_BAO_CONFIG from eth2.beacon.tools.builder.initializer import create_mock_genesis from eth2.beacon.tools.misc.ssz_vector import override_lengths from eth2.beacon.typing import Timestamp from eth2.configs import Eth2GenesisConfig NUM_VALIDATORS = 8 privkeys = tuple( int.from_bytes(hash_eth2(str(i).encode("utf-8"))[:4], "big") for i in range(NUM_VALIDATORS) ) index_to_pubkey = {} keymap = {} # pub -> priv for i, k in enumerate(privkeys): pubkey = bls.privtopub(k) index_to_pubkey[i] = pubkey keymap[pubkey] = k override_lengths(XIAO_LONG_BAO_CONFIG) genesis_state, genesis_block = create_mock_genesis( config=XIAO_LONG_BAO_CONFIG, pubkeys=tuple(keymap.keys()), keymap=keymap,
def mk_key_pair_from_seed_index(seed_index: int) -> Tuple[BLSPubkey, int]: privkey = int.from_bytes( hash_eth2(str(seed_index).encode("utf-8"))[:4], "big") pubkey = bls.privtopub(privkey) return (pubkey, privkey)
from eth2._utils.hash import hash_eth2 from eth2._utils.merkle.normal import ( calc_merkle_tree, get_merkle_proof, get_merkle_root, get_merkle_root_from_items, get_root, verify_merkle_proof, ) @pytest.mark.parametrize( "leaves,tree", [ ((b"single leaf", ), ((hash_eth2(b"single leaf"), ), )), ( (b"left", b"right"), ( (hash_eth2(hash_eth2(b"left") + hash_eth2(b"right")), ), (hash_eth2(b"left"), hash_eth2(b"right")), ), ), ( (b"1", b"2", b"3", b"4"), ( (hash_eth2( hash_eth2(hash_eth2(b"1") + hash_eth2(b"2")) + hash_eth2(hash_eth2(b"3") + hash_eth2(b"4"))), ), ( hash_eth2(hash_eth2(b"1") + hash_eth2(b"2")),
def _calc_parent_hash(left_node: Hash32, right_node: Hash32) -> Hash32: """ Calculate the parent hash of a node and its sibling. """ return hash_eth2(left_node + right_node)
def _get_privkey_for(self, index): # Want privkey an intger slightly less than the curve order privkey = int.from_bytes(hash_eth2(index.to_bytes(32, 'little')), 'little') % 2**254 self.all_privkeys_by_index[index] = privkey return privkey
def mk_withdrawal_credentials_from(prefix: bytes, public_key: BLSPubkey) -> Hash32: return Hash32(prefix + hash_eth2(public_key)[1:])
def test_hash_is_keccak256(): assert hash_eth2(b'foo') == keccak(b'foo')
def test_chaindb_persist_block_and_unknown_parent( chaindb, block, fork_choice_scoring, seed ): n_block = block.copy(parent_root=hash_eth2(seed)) with pytest.raises(ParentNotFound): chaindb.persist_block(n_block, n_block.__class__, fork_choice_scoring)
def test_chaindb_persist_block_and_unknown_parent(chaindb, block, seed): n_block = block.copy(previous_block_root=hash_eth2(seed)) with pytest.raises(ParentNotFound): chaindb.persist_block(n_block, n_block.__class__)