def test_validate_serenity_proposer_signature( proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, sample_shard_committee_params, beacon_chain_shard_number, epoch_length, max_deposit): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator_record(proposer_pubkey) for _ in range(10)), validator_balances=(max_deposit * GWEI_PER_ETH, ) * 10, shard_committees_at_slots=get_sample_shard_committees_at_slots( num_slot=128, num_shard_committee_per_slot=10, sample_shard_committee_params=sample_shard_committee_params, ), ) default_block = BeaconBlock(**sample_beacon_block_params) empty_signature_block_root = default_block.block_without_signature_root proposal_root = ProposalSignedData( state.slot, beacon_chain_shard_number, empty_signature_block_root, ).root proposed_block = BeaconBlock(**sample_beacon_block_params).copy( signature=bls.sign( message=proposal_root, privkey=proposer_privkey, domain=SignatureDomain.DOMAIN_PROPOSAL, ), ) if is_valid_signature: validate_serenity_proposer_signature( state, proposed_block, beacon_chain_shard_number, epoch_length, ) else: with pytest.raises(ValidationError): validate_serenity_proposer_signature( state, proposed_block, beacon_chain_shard_number, epoch_length, )
def test_process_eth1_data(original_votes, block_data, expected_votes, sample_beacon_state_params, sample_beacon_block_params, sample_beacon_block_body_params, config): eth1_data_votes = tuple(mapcat( _expand_eth1_votes, original_votes, )) state = BeaconState(**sample_beacon_state_params).copy( eth1_data_votes=eth1_data_votes, ) block_body = BeaconBlockBody(**sample_beacon_block_body_params).copy( eth1_data=Eth1Data( block_hash=block_data, ), ) block = BeaconBlock(**sample_beacon_block_params).copy( body=block_body, ) updated_state = process_eth1_data(state, block, config) updated_votes = updated_state.eth1_data_votes expanded_expected_votes = tuple(mapcat( _expand_eth1_votes, expected_votes, )) assert updated_votes == expanded_expected_votes
async def test_get_blocks_from_canonical_chain_by_slot( monkeypatch, db_block_slots, slot_of_requested_blocks, expected_block_slots ): node = NodeFactory() # Mock up block database mock_slot_to_block_db = { slot: BeaconBlock( slot=slot, parent_root=ZERO_HASH32, state_root=ZERO_HASH32, signature=EMPTY_SIGNATURE, body=BeaconBlockBody(), ) for slot in db_block_slots } def get_canonical_block_by_slot(slot): if slot in mock_slot_to_block_db: return mock_slot_to_block_db[slot] else: raise BlockNotFound monkeypatch.setattr( node.chain, "get_canonical_block_by_slot", get_canonical_block_by_slot ) result_blocks = node._get_blocks_from_canonical_chain_by_slot( slot_of_requested_blocks=slot_of_requested_blocks ) expected_blocks = [mock_slot_to_block_db[slot] for slot in expected_block_slots] assert len(result_blocks) == len(expected_blocks) assert set(result_blocks) == set(expected_blocks)
def run_with( cls, inputs: Tuple[BeaconState, OperationOrBlockHeader], config: Optional[Eth2Config], ) -> BeaconState: state, operation = inputs # NOTE: we do not have an easy way to evaluate a single operation on the state # So, we wrap it in a beacon block. The following statement lets us rely on # the config given in a particular handler class while working w/in the # update API provided by `py-ssz`. # NOTE: we ignore the type here, otherwise need to spell out each of the keyword # arguments individually... save some work and just build them dynamically block = BeaconBlock( body=BeaconBlockBody(**{f"{cls.name}s": ( operation, )}) # type: ignore ) try: return cls.processor(state, block, config) except ValidationError as e: # if already a ValidationError, re-raise raise e except Exception as e: # check if the exception is expected... for exception in cls.expected_exceptions: if isinstance(e, exception): raise ValidationError(e) # else raise (and fail the pytest test case ...) raise e
def _mk_block(block_params, slot, parent, block_offset): return BeaconBlock(**block_params).copy( slot=slot, parent_root=parent.signing_root, # mix in something unique state_root=block_offset.to_bytes(32, byteorder="big"), )
def test_ensure_update_eth1_vote_if_exists(genesis_state, config, vote_offsets): # one less than a majority is the majority divided by 2 threshold = config.SLOTS_PER_ETH1_VOTING_PERIOD // 2 data_votes = tuple( concat( ( Eth1Data( block_hash=(i).to_bytes(32, "little"), ), ) * (threshold + offset) for i, offset in enumerate(vote_offsets) ) ) state = genesis_state for vote in data_votes: state = process_eth1_data(state, BeaconBlock( body=BeaconBlockBody( eth1_data=vote, ) ), config) if not vote_offsets: assert state.eth1_data == genesis_state.eth1_data # we should update the 'latest' entry if we have a majority for offset in vote_offsets: if offset <= 0: assert genesis_state.eth1_data == state.eth1_data else: assert state.eth1_data == data_votes[0]
def test_process_eth1_data(original_votes, block_data, expected_votes, sample_beacon_state_params, sample_beacon_block_params, sample_beacon_block_body_params): eth1_data_votes = tuple( Eth1DataVote(data, vote_count) for data, vote_count in original_votes ) state = BeaconState(**sample_beacon_state_params).copy( eth1_data_votes=eth1_data_votes, ) block_body = BeaconBlockBody(**sample_beacon_block_body_params).copy( eth1_data=block_data, ) block = BeaconBlock(**sample_beacon_block_params).copy( body=block_body, ) updated_state = process_eth1_data(state, block) updated_votes = tuple( (vote.eth1_data, vote.vote_count) for vote in updated_state.eth1_data_votes ) assert updated_votes == expected_votes
def test_higher_slot_fork_choice_scoring(sample_beacon_block_params, slot): block = BeaconBlock(**sample_beacon_block_params).copy(slot=slot, ) expected_score = slot score = higher_slot_scoring(block) assert score == expected_score
def test_update_attestations(sample_attestation_params, sample_beacon_block_params): block = BeaconBlock(**sample_beacon_block_params) attestations = block.body.attestations attestations = list(attestations) attestations.append(Attestation(**sample_attestation_params)) body2 = block.body.copy(attestations=attestations) block2 = block.copy(body=body2) assert len(block2.body.attestations) == 1
def test_validate_proposer_signature( slots_per_epoch, shard_count, proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, beacon_chain_shard_number, genesis_epoch, target_committee_size, max_deposit_amount, config): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator_record(proposer_pubkey, config) for _ in range(10)), validator_balances=(max_deposit_amount, ) * 10, ) default_block = BeaconBlock(**sample_beacon_block_params) empty_signature_block_root = default_block.block_without_signature_root proposal_signed_root = Proposal( state.slot, beacon_chain_shard_number, empty_signature_block_root, ).signed_root proposed_block = BeaconBlock(**sample_beacon_block_params).copy( signature=bls.sign( message_hash=proposal_signed_root, privkey=proposer_privkey, domain=SignatureDomain.DOMAIN_PROPOSAL, ), ) if is_valid_signature: validate_proposer_signature( state, proposed_block, beacon_chain_shard_number, CommitteeConfig(config), ) else: with pytest.raises(ValidationError): validate_proposer_signature( state, proposed_block, beacon_chain_shard_number, CommitteeConfig(config), )
def test_validate_block_slot(sample_beacon_state_params, sample_beacon_block_params, state_slot, block_slot, expected): state = BeaconState(**sample_beacon_state_params).copy(slot=state_slot, ) block = BeaconBlock(**sample_beacon_block_params).copy(slot=block_slot, ) if isinstance(expected, Exception): with pytest.raises(ValidationError): validate_block_slot(state, block) else: validate_block_slot(state, block)
def create_test_block(parent=None, **kwargs): defaults = { "slot": SERENITY_CONFIG.GENESIS_SLOT, "previous_block_root": ZERO_HASH32, "state_root": ZERO_HASH32, # note: not the actual genesis state root "signature": EMPTY_SIGNATURE, "body": BeaconBlockBody.create_empty_body() } if parent is not None: kwargs["previous_block_root"] = parent.signing_root kwargs["slot"] = parent.slot + 1 return BeaconBlock(**merge(defaults, kwargs))
def test_operation_fixture(config, test_case): config = config._replace(MAX_TRANSFERS=1, ) post_state = test_case.pre block = BeaconBlock().copy(body=BeaconBlockBody( **{test_case.handler + 's': ( test_case.operation, )} # TODO: it looks awful )) _, operation_processing = handler_to_processing_call_map[test_case.handler] if test_case.is_valid: post_state = operation_processing(post_state, block, config) validate_state(test_case.post, post_state) else: with pytest.raises((ValidationError, IndexError, SignatureError)): operation_processing(post_state, block, config)
def test_validate_proposer_signature( slots_per_epoch, shard_count, proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, target_committee_size, max_effective_balance, config): state = BeaconState(**sample_beacon_state_params).copy( validators=tuple( create_mock_validator(proposer_pubkey, config) for _ in range(10) ), balances=(max_effective_balance,) * 10, ) block = BeaconBlock(**sample_beacon_block_params) header = block.header proposed_block = block.copy( signature=bls.sign( message_hash=header.signing_root, privkey=proposer_privkey, domain=get_domain( state, SignatureDomain.DOMAIN_BEACON_PROPOSER, slots_per_epoch, ), ), ) if is_valid_signature: validate_proposer_signature( state, proposed_block, CommitteeConfig(config), ) else: with pytest.raises(ValidationError): validate_proposer_signature( state, proposed_block, CommitteeConfig(config), )
def create_test_block(parent=None, **kwargs): defaults = { "slot": 0, "parent_root": ZERO_HASH32, "state_root": ZERO_HASH32, # note: not the actual genesis state root "randao_reveal": EMPTY_SIGNATURE, "eth1_data": Eth1Data.create_empty_data(), "signature": EMPTY_SIGNATURE, "body": BeaconBlockBody.create_empty_body() } if parent is not None: kwargs["parent_root"] = parent.root kwargs["slot"] = parent.slot + 1 return BeaconBlock(**merge(defaults, kwargs))
def create_test_block(parent=None, **kwargs): defaults = { "slot": 0, "parent_root": ZERO_HASH32, "state_root": ZERO_HASH32, # note: not the actual genesis state root "randao_reveal": ZERO_HASH32, "candidate_pow_receipt_root": ZERO_HASH32, "signature": (0, 0), "body": empty_body() } if parent is not None: kwargs["parent_root"] = parent.root kwargs["slot"] = parent.slot + 1 return BeaconBlock(**merge(defaults, kwargs))
async def test_get_blocks_from_fork_chain_by_root( monkeypatch, fork_chain_block_slots, slot_of_requested_blocks, expected_block_slots ): node = NodeFactory() mock_block = BeaconBlock( slot=0, parent_root=ZERO_HASH32, state_root=ZERO_HASH32, signature=EMPTY_SIGNATURE, body=BeaconBlockBody(), ) # Mock up fork chain block database fork_chain_blocks = [] for slot in fork_chain_block_slots: if len(fork_chain_blocks) == 0: fork_chain_blocks.append(mock_block.copy(slot=slot)) else: fork_chain_blocks.append( mock_block.copy( slot=slot, parent_root=fork_chain_blocks[-1].signing_root ) ) mock_root_to_block_db = {block.signing_root: block for block in fork_chain_blocks} def get_block_by_root(root): if root in mock_root_to_block_db: return mock_root_to_block_db[root] else: raise BlockNotFound monkeypatch.setattr(node.chain, "get_block_by_root", get_block_by_root) requested_blocks = node._get_blocks_from_fork_chain_by_root( start_slot=slot_of_requested_blocks[0], peer_head_block=fork_chain_blocks[-1], slot_of_requested_blocks=slot_of_requested_blocks, ) expected_blocks = [ block for block in fork_chain_blocks if block.slot in expected_block_slots ] assert len(requested_blocks) == len(expected_blocks) assert set(requested_blocks) == set(expected_blocks)
async def test_send_single_block(request, event_loop): alice, msg_buffer = await get_command_setup(request, event_loop) request_id = 5 block = BeaconBlock( slot=1, parent_root=ZERO_HASH32, state_root=ZERO_HASH32, signature=EMPTY_SIGNATURE, body=BeaconBlockBody(), ) alice.sub_proto.send_blocks((block, ), request_id=request_id) message = await msg_buffer.msg_queue.get() assert isinstance(message.command, BeaconBlocks) assert message.payload == { "request_id": request_id, "encoded_blocks": (ssz.encode(block), ), }
def test_validate_proposer_signature( slots_per_epoch, shard_count, proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, target_committee_size, max_deposit_amount, config): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator(proposer_pubkey, config) for _ in range(10)), validator_balances=(max_deposit_amount, ) * 10, ) block = BeaconBlock(**sample_beacon_block_params) header = block.header proposed_block = block.copy(signature=bls.sign( message_hash=header.signing_root, privkey=proposer_privkey, domain=get_domain( Fork( config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), config.GENESIS_EPOCH, ), slot_to_epoch(state.slot, slots_per_epoch), SignatureDomain.DOMAIN_BEACON_BLOCK, ), ), ) if is_valid_signature: validate_proposer_signature( state, proposed_block, CommitteeConfig(config), ) else: with pytest.raises(ValidationError): validate_proposer_signature( state, proposed_block, CommitteeConfig(config), )
async def test_send_multiple_blocks(request, event_loop): alice, msg_buffer = await get_command_setup(request, event_loop) request_id = 5 blocks = tuple( BeaconBlock( slot=slot, parent_root=ZERO_HASH32, state_root=ZERO_HASH32, signature=EMPTY_SIGNATURE, body=BeaconBlockBody(), ) for slot in range(3)) alice.sub_proto.send_blocks(blocks, request_id=request_id) message = await msg_buffer.msg_queue.get() assert isinstance(message.command, BeaconBlocks) assert message.payload == { "request_id": request_id, "encoded_blocks": tuple(ssz.encode(block) for block in blocks), }
async def test_send_single_block(request, event_loop): alice, msg_buffer = await get_command_setup(request, event_loop) request_id = 5 block = BeaconBlock( slot=1, parent_root=ZERO_HASH32, state_root=ZERO_HASH32, randao_reveal=ZERO_HASH32, eth1_data=Eth1Data.create_empty_data(), signature=EMPTY_SIGNATURE, body=BeaconBlockBody.create_empty_body(), ) alice.sub_proto.send_blocks((block, ), request_id=request_id) message = await msg_buffer.msg_queue.get() assert isinstance(message.command, BeaconBlocks) assert message.payload == { "request_id": request_id, "blocks": (block, ), }
async def test_request_recent_beacon_blocks(monkeypatch): async with ConnectionPairFactory() as (alice, bob): # Mock up block database head_block = BeaconBlock( slot=0, parent_root=ZERO_HASH32, state_root=ZERO_HASH32, signature=EMPTY_SIGNATURE, body=BeaconBlockBody(), ) blocks = [head_block.copy(slot=slot) for slot in range(5)] mock_root_to_block_db = {block.hash_tree_root: block for block in blocks} def get_block_by_hash_tree_root(root): validate_word(root) if root in mock_root_to_block_db: return mock_root_to_block_db[root] else: raise BlockNotFound monkeypatch.setattr( bob.chain, "get_block_by_hash_tree_root", get_block_by_hash_tree_root ) requesting_block_roots = [ blocks[0].hash_tree_root, b"\x12" * 32, # Unknown block root blocks[1].hash_tree_root, b"\x23" * 32, # Unknown block root blocks[3].hash_tree_root, ] requested_blocks = await alice.request_recent_beacon_blocks( peer_id=bob.peer_id, block_roots=requesting_block_roots ) expected_blocks = [blocks[0], blocks[1], blocks[3]] assert len(requested_blocks) == len(expected_blocks) assert set(requested_blocks) == set(expected_blocks)
def test_block_is_not_genesis(sample_beacon_block_params): genesis_block = BeaconBlock(**sample_beacon_block_params) another_block = BeaconBlock.from_parent(genesis_block, FromBlockParams()) assert genesis_block.is_genesis assert not another_block.is_genesis
def test_defaults(sample_beacon_block_params): block = BeaconBlock(**sample_beacon_block_params) assert block.slot == sample_beacon_block_params['slot'] assert block.is_genesis
async def test_request_beacon_blocks_by_range_invalid_request(monkeypatch): async with ConnectionPairFactory() as (alice, bob): head_slot = 1 request_head_block_root = b"\x56" * 32 head_block = BeaconBlock( slot=head_slot, parent_root=ZERO_HASH32, state_root=ZERO_HASH32, signature=EMPTY_SIGNATURE, body=BeaconBlockBody(), ) # TEST: Can not request blocks with `start_slot` greater than head block slot start_slot = 2 def get_block_by_root(root): return head_block monkeypatch.setattr(bob.chain, "get_block_by_root", get_block_by_root) count = 1 step = 1 with pytest.raises(RequestFailure): await alice.request_beacon_blocks_by_range( peer_id=bob.peer_id, head_block_root=request_head_block_root, start_slot=start_slot, count=count, step=step, ) # TEST: Can not request fork chain blocks with `start_slot` # lower than peer's latest finalized slot start_slot = head_slot state_machine = bob.chain.get_state_machine() old_state = bob.chain.get_head_state() new_checkpoint = old_state.finalized_checkpoint.copy( epoch=old_state.finalized_checkpoint.epoch + 1 ) def get_canonical_block_by_slot(slot): raise BlockNotFound monkeypatch.setattr( bob.chain, "get_canonical_block_by_slot", get_canonical_block_by_slot ) def get_state_machine(at_slot=None): class MockStateMachine: state = old_state.copy(finalized_checkpoint=new_checkpoint) config = state_machine.config return MockStateMachine() def get_head_state(): return old_state.copy(finalized_checkpoint=new_checkpoint) monkeypatch.setattr(bob.chain, "get_state_machine", get_state_machine) monkeypatch.setattr(bob.chain, "get_head_state", get_head_state) with pytest.raises(RequestFailure): await alice.request_beacon_blocks_by_range( peer_id=bob.peer_id, head_block_root=request_head_block_root, start_slot=start_slot, count=count, step=step, )
def test_defaults(sample_beacon_block_params): block = BeaconBlock(**sample_beacon_block_params) assert block.slot == sample_beacon_block_params['slot'] assert len(block.body.custody_challenges) == 0