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_aggregate_sig(crystallized_state: 'CrystallizedState', attestation: 'AttestationRecord', attestation_indices: Iterable[int], parent_hashes: Iterable[Hash32]) -> None: """ Validate ``aggregate_sig`` field. Raise ``ValidationError`` if it's invalid. """ pub_keys = [ crystallized_state.validators[validator_index].pubkey for committee_index, validator_index in enumerate(attestation_indices) if has_voted(attestation.attester_bitfield, committee_index) ] message = create_signing_message( attestation.slot, parent_hashes, attestation.shard_id, attestation.shard_block_hash, attestation.justified_slot, ) if not bls.verify(message, bls.aggregate_pubs(pub_keys), attestation.aggregate_sig): raise ValidationError("Attestation aggregate signature fails")
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 = b'hello' # Get votes: (committee_index, sig, public_key) votes = [(committee_index, bls.sign(message, privkeys[committee_index]), pubkeys[committee_index]) for committee_index in random_votes] # Verify sigs, committee_indices = verify_votes(message, votes) # 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_pubs(pubs) assert bls.verify(message, aggregated_pubs, sigs)
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 == b'\xff\xc0'
def validate_parent_block_proposer(crystallized_state: 'CrystallizedState', block: 'BaseBeaconBlock', parent_block: 'BaseBeaconBlock', cycle_length: int) -> None: if block.slot_number == 0: return block_committees_info = get_block_committees_info( parent_block, crystallized_state, cycle_length, ) try: attestation = block.attestations[0] except IndexError: raise ValidationError("block.attestations should not be an empty list") is_proposer_attestation = ( attestation.shard_id == block_committees_info.proposer_shard_id and attestation.slot == parent_block.slot_number and has_voted(attestation.attester_bitfield, block_committees_info.proposer_index_in_committee)) if not is_proposer_attestation: raise ValidationError( "Proposer of parent block should be one of the attesters in block.attestions[0]:\n" "\tExpected: proposer index in committee: %d, shard_id: %d, slot: %d\n" "\tFound: shard_id: %d, slot: %d, voted: %s" % ( block_committees_info.proposer_index_in_committee, block_committees_info.proposer_shard_id, parent_block.slot_number, attestation.shard_id, attestation.slot, has_voted( attestation.attester_bitfield, block_committees_info.proposer_index_in_committee, ), ))
def get_attestation_participants(state: 'BeaconState', slot: int, shard: int, participation_bitfield: bytes, epoch_length: int) -> Iterable[int]: """ 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 validate_bitfield(attestation: 'AttestationRecord', attestation_indices: Sequence[int]) -> None: """ Validate ``attester_bitfield`` field. Raise ``ValidationError`` if it's invalid. """ if len(attestation.attester_bitfield) != get_bitfield_length( len(attestation_indices)): raise ValidationError( "Attestation has incorrect bitfield length. Found: %s, Expected: %s" % (len(attestation.attester_bitfield), get_bitfield_length(len(attestation_indices)))) # check if end bits are zero last_bit = len(attestation_indices) if last_bit % 8 != 0: for i in range(8 - last_bit % 8): if has_voted(attestation.attester_bitfield, last_bit + i): raise ValidationError("Attestation has non-zero trailing bits")
def test_propose_block(fixture_sm_class, initial_chaindb, genesis_block, privkeys): chaindb = initial_chaindb # Propose a block block_1_shell = genesis_block.copy( parent_hash=genesis_block.hash, slot_number=genesis_block.slot_number + 1, ) sm = fixture_sm_class(chaindb, block_1_shell) # # The proposer of block_1 # block_committees_info = ( get_block_committees_info( block_1_shell, sm.crystallized_state, sm.config.CYCLE_LENGTH, ) ) private_key = privkeys[block_committees_info.proposer_index] block_proposal = BlockProposal( block=block_1_shell, shard_id=block_committees_info.proposer_shard_id, shard_block_hash=ZERO_HASH32, ) (block_1, post_crystallized_state, post_active_state, proposer_attestation) = ( sm.propose_block( crystallized_state=sm.crystallized_state, active_state=sm.active_state, block_proposal=block_proposal, chaindb=sm.chaindb, config=sm.config, private_key=private_key, ) ) expect_block_1 = block_1_shell.copy( active_state_root=post_active_state.hash, crystallized_state_root=post_crystallized_state.hash, ) assert block_1.hash == expect_block_1.hash # Persist block_1 sm.import_block(block_1) sm.chaindb.persist_block(block_1) # Validate the attestation block_2_shell = block_1.copy( parent_hash=block_1.hash, slot_number=block_1.slot_number + 1, attestations=(proposer_attestation, ), ) # Validate the parent block proposer validate_parent_block_proposer( sm.crystallized_state, block_2_shell, block_1, sm.config.CYCLE_LENGTH, ) assert has_voted( proposer_attestation.attester_bitfield, block_committees_info.proposer_index_in_committee ) # # The proposer of block_2 # block_committees_info = ( get_block_committees_info( block_2_shell, sm.crystallized_state, sm.config.CYCLE_LENGTH, ) ) private_key = privkeys[block_committees_info.proposer_index] block_proposal = BlockProposal( block=block_2_shell, shard_id=block_committees_info.proposer_shard_id, shard_block_hash=ZERO_HASH32, ) (block_2, post_crystallized_state, post_active_state, proposer_attestation) = ( sm.propose_block( crystallized_state=sm.crystallized_state, active_state=sm.active_state, block_proposal=block_proposal, chaindb=sm.chaindb, config=sm.config, private_key=private_key, ) ) expect_block_2 = block_2_shell.copy( active_state_root=post_active_state.hash, crystallized_state_root=post_crystallized_state.hash, ) assert block_2.hash == expect_block_2.hash
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_empty_bitfield(): attesters = list(range(10)) bitfield = get_empty_bitfield(len(attesters)) for attester in attesters: assert not has_voted(bitfield, attester)
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)