def test_bitfield_single_votes(): attesters = list(range(10)) bitfield = get_empty_bitfield(len(attesters)) assert set_voted(bitfield, 0) == (True, False, False, False, False, False, False, False, False, False) assert set_voted(bitfield, 1) == (False, True, False, False, False, False, False, False, False, False) assert set_voted(bitfield, 2) == (False, False, True, False, False, False, False, False, False, False) assert set_voted(bitfield, 4) == (False, False, False, False, True, False, False, False, False, False) assert set_voted(bitfield, 5) == (False, False, False, False, False, True, False, False, False, False) assert set_voted(bitfield, 6) == (False, False, False, False, False, False, True, False, False, False) assert set_voted(bitfield, 7) == (False, False, False, False, False, False, False, True, False, False) assert set_voted(bitfield, 8) == (False, False, False, False, False, False, False, False, True, False) assert set_voted(bitfield, 9) == (False, False, False, False, False, False, False, False, False, True) for voter in attesters: bitfield = set_voted((False, ) * 16, voter) for attester in attesters: if attester == voter: assert has_voted(bitfield, attester) else: assert not has_voted(bitfield, attester)
def test_get_aggregate_from_valid_committee_attestations( sample_attestation_params, privkeys, genesis_state, config): committee_size = 16 empty_bitfield = get_empty_bitfield(committee_size) base_attestation = Attestation.create(**sample_attestation_params) attestations = [] expected_bitfield = empty_bitfield for i in range(4, 16, 2): attestations.append( base_attestation.mset( "aggregation_bits", set_voted(empty_bitfield, i), "signature", sign_transaction( object=base_attestation.data, privkey=privkeys[i], state=genesis_state, slot=genesis_state.slot, signature_domain=SignatureDomain.DOMAIN_BEACON_ATTESTER, slots_per_epoch=config.SLOTS_PER_EPOCH, ), )) expected_bitfield = set_voted(expected_bitfield, i) aggregate_attestation = get_aggregate_from_valid_committee_attestations( attestations) assert aggregate_attestation.aggregation_bits == expected_bitfield
def test_validate_bitfield_padding_zero(committee_size): bitfield = get_empty_bitfield(committee_size) for index in range(committee_size): bitfield = set_voted(bitfield, index) if committee_size % 8 != 0: bitfield = set_voted(bitfield, committee_size) with pytest.raises(ValidationError): validate_bitfield(bitfield, committee_size) else: validate_bitfield(bitfield, committee_size)
def test_bitfield_some_votes(): attesters = list(range(10)) voters = [0, 4, 5, 9] # b'\x01\x00' # b'\x10\x00' # b'\x20\x00' # b'\x00\x02' bitfield = get_empty_bitfield(len(attesters)) for voter in voters: bitfield = set_voted(bitfield, voter) assert bitfield == ( True, False, False, False, True, True, False, False, False, True, ) for attester in attesters: if attester in voters: assert has_voted(bitfield, attester) else: assert not has_voted(bitfield, attester)
def test_get_unslashed_attesting_indices(genesis_state, config): state = genesis_state.copy( slot=compute_start_slot_of_epoch(3, config.SLOTS_PER_EPOCH)) target_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_shard = (state.start_shard + 3) % config.SHARD_COUNT some_committee = get_crosslink_committee(state, target_epoch, target_shard, CommitteeConfig(config)) data = AttestationData(target=Checkpoint(epoch=target_epoch), crosslink=Crosslink(shard=target_shard)) some_subset_count = random.randrange(1, len(some_committee) // 2) some_subset = random.sample(some_committee, some_subset_count) bitfield = get_empty_bitfield(len(some_committee)) for i, index in enumerate(some_committee): if index in some_subset: if random.choice([True, False]): state = state.update_validator_with_fn( index, lambda v, *_: v.copy(slashed=True)) bitfield = set_voted(bitfield, i) some_subset = tuple( filter(lambda index: not state.validators[index].slashed, some_subset)) indices = get_unslashed_attesting_indices( state, (PendingAttestation(data=data, aggregation_bits=bitfield), ), CommitteeConfig(config), ) assert set(indices) == set(some_subset) assert len(indices) == len(some_subset)
def _find_collision(state, config, validator_index, epoch, block_producer): """ Given a target epoch, make the attestation expected for the validator w/ the given ``validator_index``. """ for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if slot >= state.slot: # do not make attestations in the future return {} if validator_index in committee: # TODO(ralexstokes) refactor w/ tools/builder block = block_producer(slot) root = block.message.hash_tree_root attestation_data = AttestationData.create( slot=slot, index=committee_index, target=Checkpoint.create(epoch=epoch, root=root), beacon_block_root=root, ) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for i in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, i) return { index: (slot, (aggregation_bits, attestation_data)) for index in committee } else: raise Exception("should have found a duplicate validator")
def _mk_attestation_inputs_in_epoch(epoch, block_producer, state, config): for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if not committee: # empty committee this slot continue if slot >= state.slot: # do not make attestations in the future break block = block_producer(slot) root = block.message.hash_tree_root attestation_data = AttestationData.create( slot=slot, index=committee_index, target=Checkpoint.create(epoch=epoch, root=root), beacon_block_root=root, ) committee_size = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_size) for index in range(committee_size): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, (attestation_data.slot, (aggregation_bits, attestation_data)), )
def get_aggregation_bitfield(attestation_participants, target_committee_size): bitfield = get_empty_bitfield(target_committee_size) bitfield = pipe( bitfield, *(set_voted(index=committee_index) for committee_index in attestation_participants)) return bitfield
def test_get_attesting_indices(genesis_state, config): state = genesis_state.set( "slot", compute_start_slot_at_epoch(3, config.SLOTS_PER_EPOCH)) target_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_slot = compute_start_slot_at_epoch(target_epoch, config.SLOTS_PER_EPOCH) committee_index = 0 some_committee = get_beacon_committee(state, target_slot, committee_index, config) data = AttestationData.create( slot=target_slot, index=committee_index, target=Checkpoint.create(epoch=target_epoch), ) some_subset_count = random.randrange(1, len(some_committee) // 2) some_subset = random.sample(some_committee, some_subset_count) bitfield = get_empty_bitfield(len(some_committee)) for i, index in enumerate(some_committee): if index in some_subset: bitfield = set_voted(bitfield, i) indices = get_attesting_indices(state, data, bitfield, config) assert set(indices) == set(some_subset) assert len(indices) == len(some_subset)
def test_get_attesting_indices(genesis_state, config): state = genesis_state.copy( slot=get_epoch_start_slot(3, config.SLOTS_PER_EPOCH)) target_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_shard = (state.start_shard + 3) % config.SHARD_COUNT some_committee = get_crosslink_committee( state, target_epoch, target_shard, CommitteeConfig(config), ) data = AttestationData( target_epoch=target_epoch, crosslink=Crosslink(shard=target_shard, ), ) some_subset_count = random.randint(1, len(some_committee) // 2) some_subset = random.sample(some_committee, some_subset_count) bitfield = get_empty_bitfield(len(some_committee)) for i, index in enumerate(some_committee): if index in some_subset: bitfield = set_voted(bitfield, i) indices = get_attesting_indices( state, data, bitfield, CommitteeConfig(config), ) assert set(indices) == set(some_subset) assert len(indices) == len(some_subset)
def _mk_attestation_inputs_in_epoch(epoch, block_producer, state, config): for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config ): if not committee: # empty committee this slot continue block = block_producer(slot) root = block.signing_root attestation_data = AttestationData( slot=slot, index=committee_index, target=Checkpoint(epoch=epoch, root=root), beacon_block_root=root, ) committee_size = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_size) for index in range(committee_size): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, (attestation_data.slot, (aggregation_bits, attestation_data)), )
def _convert_to_bitfield(bits): data = bits.to_bytes(1, "little") length = bits.bit_length() bitfield = get_empty_bitfield(length) for index in range(length): value = (data[index // 8] >> index % 8) % 2 if value: bitfield = set_voted(bitfield, index) return (bitfield + (False, ) * (4 - length))[0:4]
def test_get_inclusion_infos( monkeypatch, n, n_validators_state, config, slots_per_epoch, target_committee_size, shard_count, attestation_1_inclusion_slot, attestation_1_data_slot, attestation_2_inclusion_slot, attestation_2_data_slot, expected_inclusion_slot, expected_inclusion_distance, sample_attestation_data_params, sample_pending_attestation_record_params): participating_validator_index = 1 committee = (1, 2, 3) shard = 1 from eth2.beacon import committee_helpers def mock_get_crosslink_committees_at_slot(state, slot, committee_config, registry_change=False): return (( committee, shard, ), ) monkeypatch.setattr(committee_helpers, 'get_crosslink_committees_at_slot', mock_get_crosslink_committees_at_slot) aggregation_bitfield = get_empty_bitfield(target_committee_size) aggregation_bitfield = set_voted( aggregation_bitfield, committee.index(participating_validator_index)) previous_epoch_attestations = [ PendingAttestationRecord( **sample_pending_attestation_record_params).copy( data=AttestationData(**sample_attestation_data_params).copy( slot=attestation_1_data_slot, shard=shard, ), aggregation_bitfield=aggregation_bitfield, slot_included=attestation_1_inclusion_slot, ), PendingAttestationRecord( **sample_pending_attestation_record_params).copy( data=AttestationData(**sample_attestation_data_params).copy( slot=attestation_2_data_slot, shard=shard, ), aggregation_bitfield=aggregation_bitfield, slot_included=attestation_2_inclusion_slot, ), ] result = get_inclusion_infos( state=n_validators_state, attestations=previous_epoch_attestations, committee_config=CommitteeConfig(config), ) assert result[ participating_validator_index].inclusion_slot == expected_inclusion_slot assert result[ participating_validator_index].inclusion_distance == expected_inclusion_distance
def test_bitfield_all_votes(): attesters = list(range(10)) bitfield = get_empty_bitfield(len(attesters)) for attester in attesters: bitfield = set_voted(bitfield, attester) for attester in attesters: assert has_voted(bitfield, attester) assert bitfield == (True, ) * len(attesters)
def test_or_bitfields(): bitfield_1 = get_empty_bitfield(2) bitfield_1 = set_voted(bitfield_1, 0) assert get_vote_count(bitfield_1) == 1 # same size as bitfield_1 bitfield_2 = get_empty_bitfield(2) bitfield_2 = set_voted(bitfield_2, 1) assert get_vote_count(bitfield_2) == 1 bitfield = or_bitfields([bitfield_1, bitfield_2]) assert get_vote_count(bitfield) == 2 # different size from bitfield_1 bitfield_3 = get_empty_bitfield(100) bitfield_3 = set_voted(bitfield_3, 99) assert get_vote_count(bitfield_3) == 1 with pytest.raises(ValueError): or_bitfields([bitfield_1, bitfield_3])
def _mk_attestation_inputs_in_epoch(epoch, state, config): active_validators_indices = get_active_validator_indices(state.validators, epoch) epoch_committee_count = get_committee_count( len(active_validators_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) epoch_start_shard = get_start_shard( state, epoch, CommitteeConfig(config), ) for shard_offset in random.sample(range(epoch_committee_count), epoch_committee_count): shard = Shard((epoch_start_shard + shard_offset) % config.SHARD_COUNT) committee = get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), ) if not committee: # empty crosslink committee this epoch continue attestation_data = AttestationData( target=Checkpoint( epoch=epoch, ), crosslink=Crosslink( shard=shard, ), ) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for index in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, ( get_attestation_data_slot( state, attestation_data, config, ), ( aggregation_bits, attestation_data, ), ), )
def test_bitfield_single_votes(): attesters = list(range(10)) bitfield = get_empty_bitfield(len(attesters)) assert set_voted(bitfield, 0) == b'\x01\x00' assert set_voted(bitfield, 1) == b'\x02\x00' assert set_voted(bitfield, 2) == b'\x04\x00' assert set_voted(bitfield, 4) == b'\x10\x00' assert set_voted(bitfield, 5) == b'\x20\x00' assert set_voted(bitfield, 6) == b'\x40\x00' assert set_voted(bitfield, 7) == b'\x80\x00' assert set_voted(bitfield, 8) == b'\x00\x01' assert set_voted(bitfield, 9) == b'\x00\x02' for voter in attesters: bitfield = set_voted(b'\x00\x00', voter) for attester in attesters: if attester == voter: assert has_voted(bitfield, attester) else: assert not has_voted(bitfield, attester)
def test_has_voted_random(votes_count): bit_count = 1000 bitfield = get_empty_bitfield(bit_count) random_votes = random.sample(range(bit_count), votes_count) for index in random_votes: bitfield = set_voted(bitfield, index) assert get_vote_count(bitfield) == votes_count for index in range(bit_count): if index in random_votes: assert has_voted(bitfield, index) else: assert not has_voted(bitfield, index)
def test_bitfield_some_votes(): attesters = list(range(10)) voters = [0, 4, 5, 9] bitfield = get_empty_bitfield(len(attesters)) for voter in voters: bitfield = set_voted(bitfield, voter) assert bitfield == b'\x8c\x40' for attester in attesters: if attester in voters: assert has_voted(bitfield, attester) else: assert not has_voted(bitfield, attester)
def test_or_bitfields_random(votes): bitfields = [] bit_count = 100 for vote in votes: bitfield = get_empty_bitfield(bit_count) for index in vote: bitfield = set_voted(bitfield, index) bitfields.append(bitfield) bitfield = or_bitfields(bitfields) for index in range(bit_count): if has_voted(bitfield, index): assert any(has_voted(b, index) for b in bitfields)
def _mk_attestation_for_block_with_committee(block, committee, shard, config): committee_count = len(committee) aggregation_bitfield = bitfield.get_empty_bitfield(committee_count) for index in range(committee_count): aggregation_bitfield = bitfield.set_voted(aggregation_bitfield, index) attestation = Attestation( aggregation_bitfield=aggregation_bitfield, data=AttestationData( beacon_block_root=block.signing_root, target_epoch=slot_to_epoch(block.slot, config.SLOTS_PER_EPOCH), crosslink=Crosslink(shard=shard, ), ), ) return attestation
def aggregate_votes( bitfield: Bitfield, sigs: Sequence[BLSSignature], voting_sigs: Sequence[BLSSignature], attesting_indices: Sequence[ValidatorIndex] ) -> Tuple[Bitfield, BLSSignature]: """ Aggregate the votes. """ # Update the bitfield and append the signatures sigs = tuple(sigs) + tuple(voting_sigs) bitfield = pipe( bitfield, *(set_voted(index=committee_index) for committee_index in attesting_indices)) return bitfield, bls.aggregate_signatures(sigs)
def _find_collision(state, config, index, epoch): """ Given a target epoch, make the attestation expected for the validator w/ the given index. """ active_validators = get_active_validator_indices(state.validators, epoch) committees_per_slot = get_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) // config.SLOTS_PER_EPOCH epoch_start_slot = compute_start_slot_of_epoch( epoch, config.SLOTS_PER_EPOCH, ) epoch_start_shard = get_start_shard(state, epoch, CommitteeConfig(config)) for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH): offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH) slot_start_shard = (epoch_start_shard + offset) % config.SHARD_COUNT for i in range(committees_per_slot): shard = Shard((slot_start_shard + i) % config.SHARD_COUNT) committee = get_crosslink_committee(state, epoch, shard, CommitteeConfig(config)) if index in committee: # TODO(ralexstokes) refactor w/ tools/builder attestation_data = AttestationData( target=Checkpoint( epoch=epoch, ), crosslink=Crosslink( shard=shard, ), ) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for i in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, i) return { index: ( slot, (aggregation_bits, attestation_data) ) for index in committee } else: raise Exception("should have found a duplicate validator")
def mk_pending_attestation_from_committee( committee_size: int, target_epoch: Epoch = default_epoch, target_root: Root = ZERO_ROOT, slot: Slot = default_slot, committee_index: CommitteeIndex = default_committee_index, ) -> PendingAttestation: bitfield = get_empty_bitfield(committee_size) for i in range(committee_size): bitfield = set_voted(bitfield, i) return _mk_pending_attestation( bitfield=bitfield, target_root=target_root, target_epoch=target_epoch, slot=slot, committee_index=committee_index, )
def _mk_attestation_for_block_with_committee(block, committee, committee_index, config): committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for index in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, index) attestation = Attestation.create( aggregation_bits=aggregation_bits, data=AttestationData.create( slot=block.slot, index=committee_index, beacon_block_root=block.message.hash_tree_root, target=Checkpoint.create(epoch=compute_epoch_at_slot( block.slot, config.SLOTS_PER_EPOCH)), ), ) return attestation
def test_generate_aggregate_pubkeys(activated_genesis_validators, sample_slashable_attestation_params, data): max_value_for_list = len(activated_genesis_validators) - 1 (validator_indices, some_index) = _list_and_index(data, elements=st.integers( min_value=0, max_value=max_value_for_list, )) key = "validator_indices" sample_slashable_attestation_params[key] = validator_indices custody_bitfield = get_empty_bitfield(len(validator_indices)) for index in range(some_index): custody_bitfield = set_voted(custody_bitfield, index) key = "custody_bitfield" sample_slashable_attestation_params[key] = custody_bitfield slashable_attestation = SlashableAttestation( **sample_slashable_attestation_params) custody_bit_0_indices, custody_bit_1_indices = slashable_attestation.custody_bit_indices assert len( set(custody_bit_0_indices).intersection( set(custody_bit_1_indices))) == 0 keys = generate_aggregate_pubkeys_from_indices( activated_genesis_validators, *slashable_attestation.custody_bit_indices, ) assert len(keys) == 2 (poc_0_key, poc_1_key) = keys poc_0_keys = get_pubkey_for_indices(activated_genesis_validators, custody_bit_0_indices) poc_1_keys = get_pubkey_for_indices(activated_genesis_validators, custody_bit_1_indices) assert bls.aggregate_pubkeys(poc_0_keys) == poc_0_key assert bls.aggregate_pubkeys(poc_1_keys) == poc_1_key
def mk_pending_attestation_from_committee( parent: Crosslink, committee_size: int, shard: Shard, target_epoch: Epoch = default_epoch, target_root: Hash32 = ZERO_HASH32, data_root: Hash32 = ZERO_HASH32) -> PendingAttestation: bitfield = get_empty_bitfield(committee_size) for i in range(committee_size): bitfield = set_voted(bitfield, i) return _mk_pending_attestation( bitfield=bitfield, target_root=target_root, target_epoch=target_epoch, shard=shard, start_epoch=parent.end_epoch, parent_root=parent.root, data_root=data_root, )
def test_bitfield_some_votes(): attesters = list(range(10)) voters = [ 0, # b'\x01\x00' 4, # b'\x10\x00' 5, # b'\x20\x00' 9, # b'\x00\x02' ] bitfield = get_empty_bitfield(len(attesters)) for voter in voters: bitfield = set_voted(bitfield, voter) assert bitfield == b'\x31\x02' for attester in attesters: if attester in voters: assert has_voted(bitfield, attester) else: assert not has_voted(bitfield, attester)
def test_get_unslashed_attesting_indices(genesis_state, config): state = genesis_state.set( "slot", compute_start_slot_at_epoch(3, config.SLOTS_PER_EPOCH) ) target_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_slot = compute_start_slot_at_epoch(target_epoch, config.SLOTS_PER_EPOCH) committee_index = 0 some_committee = get_beacon_committee( state, target_slot, committee_index, CommitteeConfig(config) ) data = AttestationData.create( slot=state.slot, index=committee_index, target=Checkpoint.create(epoch=target_epoch), ) some_subset_count = random.randrange(1, len(some_committee) // 2) some_subset = random.sample(some_committee, some_subset_count) bitfield = get_empty_bitfield(len(some_committee)) for i, index in enumerate(some_committee): if index in some_subset: if random.choice([True, False]): state = state.transform(["validators", index, "slashed"], True) bitfield = set_voted(bitfield, i) some_subset = tuple( filter(lambda index: not state.validators[index].slashed, some_subset) ) indices = get_unslashed_attesting_indices( state, (PendingAttestation.create(data=data, aggregation_bits=bitfield),), CommitteeConfig(config), ) assert set(indices) == set(some_subset) assert len(indices) == len(some_subset)
def _find_collision(state, config, validator_index, epoch): """ Given a target epoch, make the attestation expected for the validator w/ the given ``validator_index``. """ for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if validator_index in committee: # TODO(ralexstokes) refactor w/ tools/builder attestation_data = AttestationData(slot=slot, index=committee_index, target=Checkpoint(epoch=epoch)) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for i in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, i) return { index: (slot, (aggregation_bits, attestation_data)) for index in committee } else: raise Exception("should have found a duplicate validator")