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, 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 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 _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"), )
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 create_block_proposal( slot: Slot, parent_root: Root, randao_reveal: BLSSignature, eth1_data: Eth1Data, attestations: Sequence[Attestation], state: BeaconState, state_machine: BaseBeaconStateMachine, ) -> BeaconBlock: config = state_machine.config state_at_slot, _ = state_machine.apply_state_transition(state, future_slot=slot) proposer_index = get_beacon_proposer_index(state_at_slot, config) block_body = BeaconBlockBody.create(randao_reveal=randao_reveal, eth1_data=eth1_data, attestations=attestations) proposal = BeaconBlock.create( slot=slot, parent_root=parent_root, body=block_body, proposer_index=proposer_index, ) block_with_empty_signature = SignedBeaconBlock.create( message=proposal, signature=EMPTY_SIGNATURE) post_state, block_with_state_root = state_machine.apply_state_transition( state, block_with_empty_signature, check_proposer_signature=False) return block_with_state_root.message
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.create(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.create(body=BeaconBlockBody.create(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, config, ): eth1_data_votes = tuple(mapcat(_expand_eth1_votes, original_votes)) state = BeaconState.create(**sample_beacon_state_params).set( "eth1_data_votes", eth1_data_votes) block_body = BeaconBlockBody.create( **sample_beacon_block_body_params).mset( "eth1_data", Eth1Data.create(block_hash=block_data)) block = BeaconBlock.create(**sample_beacon_block_params).set( "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 tuple(updated_votes) == expanded_expected_votes
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
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 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 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
async def _get_block_proposal(self, request: web.Request) -> web.Response: slot = Slot(int(request.query["slot"])) randao_reveal = BLSSignature( decode_hex(request.query["randao_reveal"]).ljust(96, b"\x00")) block = BeaconBlock.create( slot=slot, body=BeaconBlockBody.create(randao_reveal=randao_reveal)) return web.json_response(to_formatted_dict(block))
def _mk_minimum_viable_signed_beacon_blocks(slots, state_machine_provider, state, block): for slot in slots: message = BeaconBlock.create(slot=slot, parent_root=block.message.hash_tree_root) block = SignedBeaconBlock.create(message=message) state, block = state_machine_provider(slot).import_block(block, state) yield block
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)
def test_update_attestations(sample_attestation_params, sample_beacon_block_params): block = BeaconBlock.create(**sample_beacon_block_params) attestations = block.body.attestations attestations = list(attestations) attestations.append(Attestation.create(**sample_attestation_params)) body2 = block.body.set("attestations", attestations) block2 = block.set("body", body2) assert len(block2.body.attestations) == 1
def test_higher_slot_fork_choice_scoring(sample_beacon_block_params, slot): block = BeaconBlock.create(**sample_beacon_block_params).set("slot", slot) expected_score = HigherSlotScore(slot) scoring = HigherSlotScoring() score = scoring.score(block) assert score == expected_score
def test_higher_slot_fork_choice_scoring(slot): block = BeaconBlock.create(slot=slot) expected_score = HigherSlotScore(slot) scoring = HigherSlotScoring() score = scoring.score(block) assert score == expected_score
def _mk_block(block_params, slot, parent, block_offset): return BeaconBlock.create(**block_params).mset( "slot", slot, "parent_root", parent.signing_root, # mix in something unique "state_root", block_offset.to_bytes(32, byteorder="big"), )
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 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_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), )
def _mk_block(block_params, slot, parent, block_offset): block = BeaconBlock.create(**block_params).mset( "slot", slot, "parent_root", parent.message.hash_tree_root, # mix in something unique "state_root", block_offset.to_bytes(32, byteorder="big"), ) return SignedBeaconBlock.create(message=block)
def test_from_genesis(base_db, genesis_block, genesis_state, fixture_sm_class, config): klass = BeaconChain.configure( __name__="TestChain", sm_configuration=((0, fixture_sm_class),), chain_id=5566 ) assert type(genesis_block) == SerenityBeaconBlock block = BeaconBlock.convert_block(genesis_block) assert type(block) == BeaconBlock with pytest.raises(BlockClassError): klass.from_genesis(base_db, genesis_state, block, config)
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 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 _mk_minimum_viable_signed_beacon_blocks(slots, state_machine_provider, state, block, config): for slot in slots: future_state, _ = state_machine_provider(slot).apply_state_transition( state, future_slot=block.slot + 1) proposer_index = get_beacon_proposer_index(future_state, config) message = BeaconBlock.create( slot=slot, parent_root=block.message.hash_tree_root, proposer_index=proposer_index, ) block = SignedBeaconBlock.create(message=message) state, block = state_machine_provider(slot).apply_state_transition( state, block) yield block
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_block_slot( sample_beacon_state_params, sample_beacon_block_params, state_slot, block_slot, expected, ): state = BeaconState.create(**sample_beacon_state_params).set( "slot", state_slot) block = BeaconBlock.create(**sample_beacon_block_params).set( "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": 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))