def test_get_x_coordinate(message, domain): x_coordinate = _get_x_coordinate(message, domain) domain_in_bytes = domain.to_bytes(8, 'big') assert x_coordinate == FQ2([ big_endian_to_int(hash_eth2(message + domain_in_bytes + b'\x01')), big_endian_to_int(hash_eth2(message + domain_in_bytes + b'\x02')), ])
def _get_x_coordinate(message: bytes, domain: int) -> FQ2: domain_in_bytes = domain.to_bytes(8, 'big') # Initial candidate x coordinate x_re = big_endian_to_int(hash_eth2(message + domain_in_bytes + b'\x01')) x_im = big_endian_to_int(hash_eth2(message + domain_in_bytes + b'\x02')) x_coordinate = FQ2([x_re, x_im]) # x_re + x_im * i return x_coordinate
def verify_merkle_branch(leaf: Hash32, proof: Sequence[Hash32], depth: int, index: int, root: Hash32) -> bool: """ Verify that the given ``leaf`` is on the merkle branch ``proof``. """ value = leaf for i in range(depth): if index // (2**i) % 2: value = hash_eth2(proof[i] + value) else: value = hash_eth2(value + proof[i]) return value == root
def verify_merkle_proof(root: Hash32, leaf: Hash32, index: int, proof: MerkleProof) -> bool: """ Verify that the given ``item`` is on the merkle branch ``proof`` starting with the given ``root``. """ assert len(proof) == TreeDepth value = leaf for i in range(TreeDepth): if index // (2**i) % 2: value = hash_eth2(proof[i] + value) else: value = hash_eth2(value + proof[i]) return value == root
def test_update_latest_index_roots(genesis_state, config, state_slot, epoch_length, latest_index_roots_length): state = genesis_state.copy( slot=state_slot, ) result_state = _update_latest_index_roots(state, config) # TODO: chanege to hash_tree_root index_root = hash_eth2( b''.join( [ index.to_bytes(32, 'big') for index in get_active_validator_indices( state.validator_registry, # TODO: change to `per-epoch` version state.slot, ) ] ) ) assert result_state.latest_index_roots[ state.next_epoch(epoch_length) % latest_index_roots_length ] == index_root
def generate_seed(state: 'BeaconState', epoch: EpochNumber, epoch_length: int, seed_lookahead: int, entry_exit_delay: int, latest_index_roots_length: int, latest_randao_mixes_length: int) -> Hash32: """ Generate a seed for the given ``epoch``. """ randao_mix = get_randao_mix( state=state, epoch=EpochNumber(epoch - seed_lookahead), epoch_length=epoch_length, latest_randao_mixes_length=latest_randao_mixes_length, ) active_index_root = get_active_index_root( state=state, epoch=epoch, epoch_length=epoch_length, entry_exit_delay=entry_exit_delay, latest_index_roots_length=latest_index_roots_length, ) epoch_as_bytes = epoch.to_bytes(32, byteorder="little") return hash_eth2(randao_mix + active_index_root + epoch_as_bytes)
def process_randao(state: BeaconState, block: BaseBeaconBlock, config: Eth2Config) -> BeaconState: proposer_index = get_beacon_proposer_index( state=state, slot=state.slot, committee_config=CommitteeConfig(config), ) proposer = state.validator_registry[proposer_index] epoch = state.current_epoch(config.SLOTS_PER_EPOCH) validate_randao_reveal( randao_reveal=block.randao_reveal, proposer_index=proposer_index, proposer_pubkey=proposer.pubkey, epoch=epoch, fork=state.fork, ) randao_mix_index = epoch % config.LATEST_RANDAO_MIXES_LENGTH new_randao_mix = bitwise_xor( get_randao_mix( state=state, epoch=epoch, slots_per_epoch=config.SLOTS_PER_EPOCH, latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH, ), hash_eth2(block.randao_reveal), ) return state.copy(latest_randao_mixes=update_tuple_item( state.latest_randao_mixes, randao_mix_index, new_randao_mix, ), )
def validate_deposit_proof(state: BeaconState, deposit: Deposit, deposit_contract_tree_depth: int) -> None: """ Validate if deposit branch proof is valid. """ # Should equal 8 bytes for deposit_data.amount + # 8 bytes for deposit_data.timestamp + # 176 bytes for deposit_data.deposit_input # It should match the deposit_data in the eth1.0 deposit contract serialized_deposit_data = ssz.encode(deposit.deposit_data) is_valid_proof = verify_merkle_branch( leaf=hash_eth2(serialized_deposit_data), proof=deposit.proof, depth=deposit_contract_tree_depth, index=deposit.index, root=state.latest_eth1_data.deposit_root, ) if not is_valid_proof: raise ValidationError( f"deposit.proof ({deposit.proof}) is invalid against " f"leaf={hash_eth2(serialized_deposit_data)}, " f"deposit_contract_tree_depth={deposit_contract_tree_depth}, " f"deposit.index={deposit.index} " f"state.latest_eth1_data.deposit_root={state.latest_eth1_data.deposit_root.hex()}" )
def mock_get_active_index_root(state, epoch, slots_per_epoch, activation_exit_delay, latest_active_index_roots_length): return hash_eth2( state.root + epoch.to_bytes(32, byteorder='little') + slots_per_epoch.to_bytes(32, byteorder='little') + latest_active_index_roots_length.to_bytes(32, byteorder='little'))
def test_get_previous_epoch_matching_head_attestations( random, sample_state, genesis_epoch, slots_per_epoch, slots_per_historical_root, sample_attestation_data_params, sample_attestation_params): previous_epoch = 9 current_epoch = previous_epoch + 1 current_slot = get_epoch_start_slot(current_epoch + 1, slots_per_epoch) - 1 latest_block_roots = [ hash_eth2(b'block_root' + i.to_bytes(1, 'little')) for i in range(slots_per_historical_root) ] num_previous_epoch_attestation = random.sample(range(slots_per_epoch), 1)[0] previous_epoch_attestion_slots = random.sample( range( get_epoch_start_slot(previous_epoch, slots_per_epoch), get_epoch_start_slot(current_epoch, slots_per_epoch), ), num_previous_epoch_attestation, ) num_previous_epoch_head_attestation = random.sample( range(num_previous_epoch_attestation), 1)[0] previous_epoch_head_attestion_slots = random.sample( previous_epoch_attestion_slots, num_previous_epoch_head_attestation, ) previous_epoch_not_head_attestion_slots = set( previous_epoch_attestion_slots).difference( set(previous_epoch_head_attestion_slots)) previous_epoch_head_attestations = [] for slot in previous_epoch_head_attestion_slots: previous_epoch_head_attestations.append( Attestation(**sample_attestation_params).copy( data=AttestationData(**sample_attestation_data_params).copy( slot=slot, beacon_block_root=latest_block_roots[ slot % slots_per_historical_root], ), )) previous_epoch_not_head_attestations = [] for slot in previous_epoch_not_head_attestion_slots: previous_epoch_not_head_attestations.append( Attestation(**sample_attestation_params).copy(data=AttestationData( **sample_attestation_data_params).copy(slot=slot, ), )) state = sample_state.copy( slot=current_slot, latest_block_roots=latest_block_roots, previous_epoch_attestations=(previous_epoch_head_attestations + previous_epoch_not_head_attestations), ) result = get_previous_epoch_matching_head_attestations( state, slots_per_epoch, genesis_epoch, slots_per_historical_root, ) assert set(previous_epoch_head_attestations) == set(result)
def test_update_latest_active_index_roots(genesis_state, committee_config, state_slot, slots_per_epoch, latest_active_index_roots_length, activation_exit_delay): state = genesis_state.copy( slot=state_slot, ) result_state = _update_latest_active_index_roots(state, committee_config) # TODO: chanege to hash_tree_root index_root = hash_eth2( b''.join( [ index.to_bytes(32, 'little') for index in get_active_validator_indices( state.validator_registry, # TODO: change to `per-epoch` version slot_to_epoch(state.slot, slots_per_epoch), ) ] ) ) target_epoch = state.next_epoch(slots_per_epoch) + activation_exit_delay assert result_state.latest_active_index_roots[ target_epoch % latest_active_index_roots_length ] == index_root
def _update_latest_index_roots(state: BeaconState, committee_config: CommitteeConfig) -> BeaconState: """ Return the BeaconState with updated `latest_index_roots`. """ next_epoch = state.next_epoch(committee_config.EPOCH_LENGTH) # TODO: chanege to hash_tree_root active_validator_indices = get_active_validator_indices( state.validator_registry, EpochNumber(next_epoch + committee_config.ENTRY_EXIT_DELAY), ) index_root = hash_eth2( b''.join( [ index.to_bytes(32, 'big') for index in active_validator_indices ] ) ) latest_index_roots = update_tuple_item( state.latest_index_roots, ( (next_epoch + committee_config.ENTRY_EXIT_DELAY) % committee_config.LATEST_INDEX_ROOTS_LENGTH ), index_root, ) return state.copy( latest_index_roots=latest_index_roots, )
def mock_get_active_index_root(state, epoch, epoch_length, entry_exit_delay, latest_index_roots_length): return hash_eth2( state.root + epoch.to_bytes(32, byteorder='little') + epoch_length.to_bytes(32, byteorder='little') + latest_index_roots_length.to_bytes(32, byteorder='little'))
def mock_get_randao_mix(state, slot, latest_randao_mixes_length): return hash_eth2( state.root + abs(slot).to_bytes(32, byteorder='big') + latest_randao_mixes_length.to_bytes(32, byteorder='big') )
def mock_get_randao_mix(state, epoch, slots_per_epoch, latest_randao_mixes_length): return hash_eth2( state.root + epoch.to_bytes(32, byteorder='little') + latest_randao_mixes_length.to_bytes(32, byteorder='little') )
def shuffle(values: Sequence[TItem], seed: Hash32) -> Iterable[TItem]: """ Return the shuffled ``values`` with ``seed`` as entropy. Mainly for shuffling active validators in-protocol. Spec: https://github.com/ethereum/eth2.0-specs/blob/70cef14a08de70e7bd0455d75cf380eb69694bfb/specs/core/0_beacon-chain.md#helper-functions # noqa: E501 """ values_count = len(values) # The range of the RNG places an upper-bound on the size of the list that # may be shuffled. It is a logic error to supply an oversized list. if values_count >= RAND_MAX: raise ValueError( "values_count (%s) should less than RAND_MAX (%s)." % (values_count, RAND_MAX) ) output = [x for x in values] source = seed index = 0 while index < values_count - 1: # Re-hash the `source` to obtain a new pattern of bytes. source = hash_eth2(source) # Iterate through the `source` bytes in 3-byte chunks. for position in range(0, 32 - (32 % RAND_BYTES), RAND_BYTES): # Determine the number of indices remaining in `values` and exit # once the last index is reached. remaining = values_count - index if remaining == 1: break # Read 3-bytes of `source` as a 24-bit big-endian integer. sample_from_source = int.from_bytes( source[position:position + RAND_BYTES], 'big' ) # Sample values greater than or equal to `sample_max` will cause # modulo bias when mapped into the `remaining` range. sample_max = RAND_MAX - RAND_MAX % remaining # Perform a swap if the consumed entropy will not cause modulo bias. if sample_from_source < sample_max: # Select a replacement index for the current index. replacement_position = (sample_from_source % remaining) + index # Swap the current index with the replacement index. (output[index], output[replacement_position]) = ( output[replacement_position], output[index] ) index += 1 else: # The sample causes modulo bias. A new sample should be read. pass return output
def hash_to_G2(message: bytes, domain: int) -> Tuple[FQ2, FQ2, FQ2]: domain_in_bytes = domain.to_bytes(8, 'big') # Initial candidate x coordinate x_re = big_endian_to_int(hash_eth2(domain_in_bytes + b'\x01' + message)) x_im = big_endian_to_int(hash_eth2(domain_in_bytes + b'\x02' + message)) x_coordinate = FQ2([x_re, x_im]) # x_re + x_im * i # Test candidate y coordinates until a one is found while 1: y_coordinate_squared = x_coordinate**3 + FQ2( [4, 4]) # The curve is y^2 = x^3 + 4(i + 1) y_coordinate = modular_squareroot(y_coordinate_squared) if y_coordinate is not None: # Check if quadratic residue found break x_coordinate += FQ2([1, 0]) # Add 1 and try again return multiply((x_coordinate, y_coordinate, FQ2([1, 0])), G2_cofactor)
def mock_get_active_index_root(state, slot, epoch_length, latest_index_roots_length): return hash_eth2( state.root + abs(slot).to_bytes(32, byteorder='big') + epoch_length.to_bytes(32, byteorder='big') + latest_index_roots_length.to_bytes(32, byteorder='big') )
def shuffle(values: Sequence[TItem], seed: Hash32, shuffle_round_count: int = 90) -> Iterable[TItem]: """ Return shuffled indices in a pseudorandom permutation `0...list_size-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. """ list_size = len(values) if list_size > MAX_LIST_SIZE: raise ValidationError( f"The `list_size` ({list_size}) should be equal to or less than " f"`MAX_LIST_SIZE` ({MAX_LIST_SIZE}") indices = list(range(list_size)) for round in range(shuffle_round_count): hash_bytes = b''.join([ hash_eth2(seed + round.to_bytes(1, 'little') + i.to_bytes(4, 'little')) for i in range((list_size + 255) // 256) ]) pivot = int.from_bytes( hash_eth2(seed + round.to_bytes(1, 'little'))[:8], 'little', ) % list_size for i in range(list_size): flip = (pivot - indices[i]) % list_size hash_position = indices[i] if indices[i] > flip else flip byte = hash_bytes[hash_position // 8] mask = POWER_OF_TWO_NUMBERS[hash_position % 8] if byte & mask: indices[i] = flip else: # not swap pass for i in indices: yield values[i]
def test_get_merkle_root(): hash_0 = b"0" * 32 leaves = (hash_0, ) root = get_merkle_root(leaves) assert root == hash_0 hash_1 = b"1" * 32 leaves = (hash_0, hash_1) root = get_merkle_root(leaves) assert root == hash_eth2(hash_0 + hash_1)
def test_merkle_root_and_proofs(items, expected_root): tree = calc_merkle_tree(items) assert get_root(tree) == expected_root for index in range(len(items)): item = items[index] proof = get_merkle_proof(tree, index) assert verify_merkle_proof(expected_root, hash_eth2(item), index, proof) assert not verify_merkle_proof(b"\x32" * 32, hash_eth2(item), index, proof) assert not verify_merkle_proof(expected_root, hash_eth2(b"\x32" * 32), index, proof) if len(items) > 1: assert not verify_merkle_proof(expected_root, hash_eth2(item), (index + 1) % len(items), proof) for replaced_index in range(len(proof)): altered_proof = proof[:replaced_index] + ( b"\x32" * 32, ) + proof[replaced_index + 1:] assert not verify_merkle_proof(expected_root, hash_eth2(item), index, altered_proof)
def test_generate_seed(monkeypatch, genesis_state, slots_per_epoch, min_seed_lookahead, activation_exit_delay, latest_active_index_roots_length, latest_randao_mixes_length): from eth2.beacon import helpers def mock_get_randao_mix(state, epoch, slots_per_epoch, latest_randao_mixes_length): return hash_eth2( state.root + epoch.to_bytes(32, byteorder='little') + latest_randao_mixes_length.to_bytes(32, byteorder='little')) def mock_get_active_index_root(state, epoch, slots_per_epoch, activation_exit_delay, latest_active_index_roots_length): return hash_eth2( state.root + epoch.to_bytes(32, byteorder='little') + slots_per_epoch.to_bytes(32, byteorder='little') + latest_active_index_roots_length.to_bytes(32, byteorder='little')) monkeypatch.setattr(helpers, 'get_randao_mix', mock_get_randao_mix) monkeypatch.setattr(helpers, 'get_active_index_root', mock_get_active_index_root) state = genesis_state epoch = 1 epoch_as_bytes = epoch.to_bytes(32, 'little') seed = generate_seed( state=state, epoch=epoch, slots_per_epoch=slots_per_epoch, min_seed_lookahead=min_seed_lookahead, activation_exit_delay=activation_exit_delay, latest_active_index_roots_length=latest_active_index_roots_length, latest_randao_mixes_length=latest_randao_mixes_length, ) assert seed == hash_eth2( mock_get_randao_mix( state=state, epoch=(epoch - min_seed_lookahead), slots_per_epoch=slots_per_epoch, latest_randao_mixes_length=latest_randao_mixes_length, ) + mock_get_active_index_root( state=state, epoch=epoch, slots_per_epoch=slots_per_epoch, activation_exit_delay=activation_exit_delay, latest_active_index_roots_length=latest_active_index_roots_length, ) + epoch_as_bytes)
def get_permuted_index(index: int, list_size: int, seed: Hash32, shuffle_round_count: int) -> int: """ Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-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 >= list_size: raise ValidationError( f"The given `index` ({index}) should be less than `list_size` ({list_size}" ) if list_size > MAX_LIST_SIZE: raise ValidationError( f"The given `list_size` ({list_size}) should be equal to or less than " f"`MAX_LIST_SIZE` ({MAX_LIST_SIZE}") new_index = index for round in range(shuffle_round_count): pivot = int.from_bytes( hash_eth2(seed + round.to_bytes(1, 'little'))[0:8], 'little', ) % list_size flip = (pivot - new_index) % list_size hash_pos = max(new_index, flip) h = hash_eth2(seed + round.to_bytes(1, 'little') + (hash_pos // 256).to_bytes(4, 'little')) byte = h[(hash_pos % 256) // 8] bit = (byte >> (hash_pos % 8)) % 2 new_index = flip if bit else new_index return new_index
def test_generate_seed(monkeypatch, genesis_state, epoch_length, seed_lookahead, entry_exit_delay, latest_index_roots_length, latest_randao_mixes_length): from eth2.beacon import helpers def mock_get_randao_mix(state, epoch, epoch_length, latest_randao_mixes_length): return hash_eth2( state.root + abs(epoch).to_bytes(32, byteorder='big') + latest_randao_mixes_length.to_bytes(32, byteorder='big')) def mock_get_active_index_root(state, epoch, epoch_length, entry_exit_delay, latest_index_roots_length): return hash_eth2( state.root + abs(epoch).to_bytes(32, byteorder='big') + epoch_length.to_bytes(32, byteorder='big') + latest_index_roots_length.to_bytes(32, byteorder='big')) monkeypatch.setattr(helpers, 'get_randao_mix', mock_get_randao_mix) monkeypatch.setattr(helpers, 'get_active_index_root', mock_get_active_index_root) state = genesis_state epoch = 1 seed = generate_seed( state=state, epoch=epoch, epoch_length=epoch_length, seed_lookahead=seed_lookahead, entry_exit_delay=entry_exit_delay, latest_index_roots_length=latest_index_roots_length, latest_randao_mixes_length=latest_randao_mixes_length, ) assert seed == hash_eth2( mock_get_randao_mix( state=state, epoch=(epoch - seed_lookahead), epoch_length=epoch_length, latest_randao_mixes_length=latest_randao_mixes_length, ) + mock_get_active_index_root( state=state, epoch=epoch, epoch_length=epoch_length, entry_exit_delay=entry_exit_delay, latest_index_roots_length=latest_index_roots_length, ))
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 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 generate_seed(state: 'BeaconState', slot: SlotNumber, epoch_length: int, seed_lookahead: int, latest_index_roots_length: int, latest_randao_mixes_length: int) -> Hash32: """ Generate a seed for the given ``slot``. TODO: it's slot version, will be changed to epoch version. """ randao_mix = get_randao_mix( state, SlotNumber(slot - seed_lookahead), latest_randao_mixes_length=latest_randao_mixes_length, ) active_index_root = get_active_index_root( state, slot, epoch_length=epoch_length, latest_index_roots_length=latest_index_roots_length, ) return hash_eth2(randao_mix + active_index_root)
def validate_serenity_attestation_aggregate_signature( state: BeaconState, attestation: Attestation, epoch_length: int) -> None: """ Validate ``aggregate_signature`` field of ``attestation``. Raise ``ValidationError`` if it's invalid. Note: This is the phase 0 version of `aggregate_signature`` validation. All proof of custody bits are assumed to be 0 within the signed data. This will change to reflect real proof of custody bits in the Phase 1. """ participant_indices = get_attestation_participants( state=state, slot=attestation.data.slot, shard=attestation.data.shard, participation_bitfield=attestation.participation_bitfield, epoch_length=epoch_length, ) pubkeys = tuple(state.validator_registry[validator_index].pubkey for validator_index in participant_indices) group_public_key = bls.aggregate_pubkeys(pubkeys) # TODO: change to tree hashing when we have SSZ # TODO: Replace with AttestationAndCustodyBit data structure message = hash_eth2(rlp.encode(attestation.data) + (0).to_bytes(1, "big")) is_valid_signature = bls.verify( message=message, pubkey=group_public_key, signature=attestation.aggregate_signature, domain=get_domain( fork_data=state.fork_data, slot=attestation.data.slot, domain_type=SignatureDomain.DOMAIN_ATTESTATION, ), ) if not is_valid_signature: raise ValidationError( "Attestation ``aggregate_signature`` is invalid.")
def _update_latest_index_roots(state: BeaconState, config: BeaconConfig) -> BeaconState: """ Return the BeaconState with updated `latest_index_roots`. """ next_epoch = state.next_epoch(config.EPOCH_LENGTH) # TODO: chanege to hash_tree_root active_validator_indices = get_active_validator_indices( state.validator_registry, # TODO: change to `per-epoch` version state.slot, ) index_root = hash_eth2(b''.join( [index.to_bytes(32, 'big') for index in active_validator_indices])) latest_index_roots = update_tuple_item( state.latest_index_roots, next_epoch % config.LATEST_INDEX_ROOTS_LENGTH, index_root, ) return state.copy(latest_index_roots=latest_index_roots, )
def create_mock_signed_attestation(state, shard_committee, voting_committee_indices, attestation_data): message = hash_eth2( rlp.encode(attestation_data) + (0).to_bytes(1, "big") ) # participants sign message signatures = [ bls.sign( message, privkeys[shard_committee.committee[committee_index]], domain=get_domain( fork_data=state.fork_data, slot=attestation_data.slot, domain_type=SignatureDomain.DOMAIN_ATTESTATION, ) ) for committee_index in voting_committee_indices ] # aggregate signatures and construct participant bitfield participation_bitfield, aggregate_signature = aggregate_votes( bitfield=get_empty_bitfield(len(shard_committee.committee)), sigs=(), voting_sigs=signatures, voting_committee_indices=voting_committee_indices, ) # create attestation from attestation_data, particpipant_bitfield, and signature return Attestation( data=attestation_data, participation_bitfield=participation_bitfield, custody_bitfield=b'', aggregate_signature=aggregate_signature, )