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_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_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 _validate_custody_bitfield_from_aggregation_bitfield( committee_size: int, aggregation_bitfield: Bitfield, custody_bitfield: Bitfield) -> None: """ Ensure that every unset bit in the ``aggregation_bitfield`` is also unset in the ``custody_bitfield`` to ensure a canonical representation of information between the two sources of data. Raise ``ValidationError`` if there is a mismatch. """ for i in range(committee_size): if not bitfield.has_voted(aggregation_bitfield, i): if bitfield.has_voted(custody_bitfield, i): raise ValidationError( "Invalid attestation bitfields:\n" f"\tExpected index {i} to not have custody data because " "they did not participate in this attestation.")
def test_bitfield_single_votes(): attesters = list(range(10)) bitfield = get_empty_bitfield(len(attesters)) assert set_voted(bitfield, 0) == b'\x80\x00' assert set_voted(bitfield, 1) == b'\x40\x00' assert set_voted(bitfield, 2) == b'\x20\x00' assert set_voted(bitfield, 7) == b'\x01\x00' assert set_voted(bitfield, 8) == b'\x00\x80' assert set_voted(bitfield, 9) == b'\x00\x40' 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_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 custody_bit_indices(self) -> Tuple[Tuple[ValidatorIndex, ...], Tuple[ValidatorIndex, ...]]: custody_bit_0_indices = () # type: Tuple[ValidatorIndex, ...] custody_bit_1_indices = () # type: Tuple[ValidatorIndex, ...] for i, validator_index in enumerate(self.validator_indices): if not has_voted(self.custody_bitfield, i): custody_bit_0_indices += (validator_index,) else: custody_bit_1_indices += (validator_index,) return (custody_bit_0_indices, custody_bit_1_indices)
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 validate_bitfield(bitfield: bytes, committee_size: int) -> None: """ Verify ``bitfield`` against the ``committee_size``. """ if len(bitfield) != get_bitfield_length(committee_size): raise ValidationError( f"len(bitfield) ({len(bitfield)}) != " f"get_bitfield_length(committee_size) ({get_bitfield_length(committee_size)}), " f"where committee_size={committee_size}") for i in range(committee_size, len(bitfield) * 8): if has_voted(bitfield, i): raise ValidationError(f"bit ({i}) should be zero")
def get_attesting_indices( state: BeaconState, attestation_data: AttestationData, bitfield: Bitfield, config: CommitteeConfig, ) -> Set[ValidatorIndex]: """ Return the attesting indices corresponding to ``attestation_data`` and ``bitfield``. """ committee = get_beacon_committee(state, attestation_data.slot, attestation_data.index, config) return set(index for i, index in enumerate(committee) if has_voted(bitfield, i))
def get_attesting_indices( state: BeaconState, attestation_data: AttestationData, bitfield: Bitfield, config: CommitteeConfig, ) -> Set[ValidatorIndex]: """ Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``. """ committee = get_crosslink_committee(state, attestation_data.target.epoch, attestation_data.crosslink.shard, config) return set(index for i, index in enumerate(committee) if has_voted(bitfield, i))
def test_aggregate_votes(votes_count, random, privkeys, pubkeys): bit_count = 10 pre_bitfield = get_empty_bitfield(bit_count) pre_sigs = () domain = compute_domain(SignatureDomain.DOMAIN_ATTESTATION) random_votes = random.sample(range(bit_count), votes_count) message_hash = b"\x12" * 32 # Get votes: (committee_index, sig, public_key) votes = [ ( committee_index, bls.sign(message_hash, privkeys[committee_index], domain), pubkeys[committee_index], ) for committee_index in random_votes ] # Verify sigs, committee_indices = verify_votes(message_hash, votes, domain) # Aggregate the votes bitfield, sigs = aggregate_votes( bitfield=pre_bitfield, sigs=pre_sigs, voting_sigs=sigs, attesting_indices=committee_indices, ) try: _, _, pubs = zip(*votes) except ValueError: pubs = () voted_index = [ committee_index for committee_index in random_votes if has_voted(bitfield, committee_index) ] assert len(voted_index) == len(votes) aggregated_pubs = bls.aggregate_pubkeys(pubs) if votes_count == 0 and bls.backend in (ChiaBackend, MilagroBackend): with pytest.raises(ValidationError): bls.validate(message_hash, aggregated_pubs, sigs, domain) else: bls.validate(message_hash, aggregated_pubs, sigs, domain)
def get_members_from_bitfield(committee: Sequence[ValidatorIndex], bitfield: Bitfield) -> Iterable[ValidatorIndex]: """ Return all indices in ``committee`` if they "voted" according to the ``bitfield``. Raise ``ValidationError`` if the ``bitfield`` does not conform to some basic checks around length and zero-padding based on the ``committee`` length. """ validate_bitfield(bitfield, len(committee)) # Extract committee members if the corresponding bit is set in the bitfield for bitfield_index, validator_index in enumerate(committee): if has_voted(bitfield, bitfield_index): yield validator_index
def get_attestation_participants(state: 'BeaconState', attestation_data: 'AttestationData', aggregation_bitfield: Bitfield, genesis_epoch: EpochNumber, epoch_length: int, target_committee_size: int, shard_count: int) -> Iterable[ValidatorIndex]: """ Return the participant indices at for the ``attestation_data`` and ``aggregation_bitfield``. """ # Find the committee in the list with the desired shard crosslink_committees = get_crosslink_committees_at_slot( state=state, slot=attestation_data.slot, genesis_epoch=genesis_epoch, epoch_length=epoch_length, target_committee_size=target_committee_size, shard_count=shard_count, ) if attestation_data.shard not in set( [shard for _, shard in crosslink_committees]): raise ValidationError( "attestation_data.shard ({}) is not in crosslink_committees". format(attestation_data.shard, )) try: # Filter by shard committee = tuple( _get_committee_for_shard(crosslink_committees, attestation_data.shard)) except IndexError: raise ValidationError( "committee for shard={} should not be empty.".format( attestation_data.shard, )) committee_size = len(committee) if len(aggregation_bitfield) != get_bitfield_length(committee_size): raise ValidationError( f"Invalid bitfield length," f"\texpected: {get_bitfield_length(committee_size)}, found: {len(aggregation_bitfield)}" ) # Find the participating attesters in the committee for bitfield_index, validator_index in enumerate(committee): if has_voted(aggregation_bitfield, bitfield_index): yield validator_index
def test_aggregate_votes(votes_count, random, privkeys, pubkeys): bit_count = 10 pre_bitfield = get_empty_bitfield(bit_count) pre_sigs = () domain = 0 random_votes = random.sample(range(bit_count), votes_count) message_hash = b'\x12' * 32 # Get votes: (committee_index, sig, public_key) votes = [( committee_index, bls.sign(message_hash, privkeys[committee_index], domain), pubkeys[committee_index], ) for committee_index in random_votes] # Verify sigs, committee_indices = verify_votes(message_hash, votes, domain) # Aggregate the votes bitfield, sigs = aggregate_votes(bitfield=pre_bitfield, sigs=pre_sigs, voting_sigs=sigs, attesting_indices=committee_indices) try: _, _, pubs = zip(*votes) except ValueError: pubs = () voted_index = [ committee_index for committee_index in random_votes if has_voted(bitfield, committee_index) ] assert len(voted_index) == len(votes) aggregated_pubs = bls.aggregate_pubkeys(pubs) if votes_count != 0: bls.validate(message_hash, aggregated_pubs, sigs, domain) else: # EMPTY_SIGNATURE is considered invalid with pytest.raises(ValueError): bls.validate(message_hash, aggregated_pubs, sigs, domain)
def test_aggregate_votes(votes_count, random, privkeys, pubkeys): bit_count = 10 pre_bitfield = get_empty_bitfield(bit_count) pre_sigs = () random_votes = random.sample(range(bit_count), votes_count) message_hash = b"\x12" * 32 # Get votes: (committee_index, sig, public_key) votes = [( committee_index, bls.sign(privkeys[committee_index], message_hash), pubkeys[committee_index], ) for committee_index in random_votes] # Verify sigs, committee_indices = verify_votes(message_hash, votes) # Aggregate the votes bitfield, sigs = aggregate_votes( bitfield=pre_bitfield, sigs=pre_sigs, voting_sigs=sigs, attesting_indices=committee_indices, ) try: _, _, pubs = zip(*votes) except ValueError: pubs = () voted_index = [ committee_index for committee_index in random_votes if has_voted(bitfield, committee_index) ] assert len(voted_index) == len(votes) if votes_count == 0: with pytest.raises(ValidationError): bls.validate(message_hash, sigs, *pubs) else: bls.validate(message_hash, sigs, *pubs)
def get_attestation_participants( state: 'BeaconState', slot: SlotNumber, shard: ShardNumber, participation_bitfield: Bitfield, epoch_length: int) -> Iterable[ValidatorIndex]: """ Return the participants' indices at the ``slot`` of shard ``shard`` from ``participation_bitfield``. """ # Find the relevant committee # Filter by slot shard_committees_at_slot = get_shard_committees_at_slot( state, slot, epoch_length, ) # Filter by shard shard_committees = tuple([ shard_committee for shard_committee in shard_committees_at_slot if shard_committee.shard == shard ]) try: shard_committee = shard_committees[0] except IndexError: raise ValidationError("shard_committees should not be empty.") if len(participation_bitfield) != get_bitfield_length( len(shard_committee.committee)): raise ValidationError( 'Invalid bitfield length,' "\texpected: %s, found: %s" % ( get_bitfield_length(len(shard_committee.committee)), len(participation_bitfield), )) # Find the participating attesters in the committee for bitfield_index, validator_index in enumerate( shard_committee.committee): if has_voted(participation_bitfield, bitfield_index): yield validator_index
def test_aggregate_votes(votes_count, random, privkeys, pubkeys): bit_count = 10 pre_bitfield = get_empty_bitfield(bit_count) pre_sigs = () domain = 0 random_votes = random.sample(range(bit_count), votes_count) message = b'hello' # Get votes: (committee_index, sig, public_key) votes = [( committee_index, bls.sign(message, privkeys[committee_index], domain), pubkeys[committee_index], ) for committee_index in random_votes] # Verify sigs, committee_indices = verify_votes(message, votes, domain) # Aggregate the votes bitfield, sigs = aggregate_votes( bitfield=pre_bitfield, sigs=pre_sigs, voting_sigs=sigs, voting_committee_indices=committee_indices) try: _, _, pubs = zip(*votes) except ValueError: pubs = () voted_index = [ committee_index for committee_index in random_votes if has_voted(bitfield, committee_index) ] assert len(voted_index) == len(votes) aggregated_pubs = bls.aggregate_pubkeys(pubs) assert bls.verify(message, aggregated_pubs, sigs, domain)
def get_attestation_participants( state: 'BeaconState', attestation_data: 'AttestationData', bitfield: Bitfield, committee_config: CommitteeConfig) -> Iterable[ValidatorIndex]: """ Return the participant indices at for the ``attestation_data`` and ``bitfield``. """ # Find the committee in the list with the desired shard crosslink_committees = get_crosslink_committees_at_slot( state=state, slot=attestation_data.slot, committee_config=committee_config, ) if attestation_data.shard not in set( [shard for _, shard in crosslink_committees]): raise ValidationError( "attestation_data.shard ({}) is not in crosslink_committees". format(attestation_data.shard, )) try: # Filter by shard committee = tuple( _get_committee_for_shard(crosslink_committees, attestation_data.shard)) except IndexError: raise ValidationError( "committee for shard={} should not be empty.".format( attestation_data.shard, )) validate_bitfield(bitfield, len(committee)) # Find the participating attesters in the committee for bitfield_index, validator_index in enumerate(committee): if has_voted(bitfield, bitfield_index): yield validator_index
def test_bitfield_multiple_votes(): bitfield = get_empty_bitfield(1) bitfield = set_voted(bitfield, 0) bitfield = set_voted(bitfield, 0) assert has_voted(bitfield, 0)
def test_set_vote_and_has_vote(bit_count): bitfield = get_empty_bitfield(bit_count) index = random.choice(range(bit_count)) bitfield = set_voted(bitfield, index) assert has_voted(bitfield, index)
def test_empty_bitfield(): attesters = list(range(10)) bitfield = get_empty_bitfield(len(attesters)) for attester in attesters: assert not has_voted(bitfield, attester)