def import_block( self, block: BaseBeaconBlock, perform_validation: bool = True ) -> Tuple[BaseBeaconBlock, Tuple[BaseBeaconBlock, ...], Tuple[ BaseBeaconBlock, ...]]: """ Import a complete block and returns a 3-tuple - the imported block - a tuple of blocks which are now part of the canonical chain. - a tuple of blocks which were canonical and now are no longer canonical. """ self.logger.debug( "attempting import of block with slot %s and signing_root %s", block.slot, humanize_hash(block.signing_root), ) try: parent_block = self.get_block_by_root(block.parent_root) except BlockNotFound: raise ValidationError( "Attempt to import block #{}. Cannot import block {} before importing " "its parent block at {}".format( block.slot, humanize_hash(block.signing_root), humanize_hash(block.parent_root), )) state_machine = self.get_state_machine(at_slot=parent_block.slot) state_class = state_machine.get_state_class() state = self.chaindb.get_state_by_root(parent_block.state_root, state_class) state, imported_block = state_machine.import_block( block, state, check_proposer_signature=perform_validation) # Validate the imported block. if perform_validation: validate_imported_block_unchanged(imported_block, block) # TODO: Now it just persists all state. Should design how to clean up the old state. self.chaindb.persist_state(state) fork_choice_scoring = state_machine.get_fork_choice_scoring() (new_canonical_blocks, old_canonical_blocks) = self.chaindb.persist_block( imported_block, imported_block.__class__, fork_choice_scoring) self.logger.debug( "successfully imported block at slot %s with signing root %s", imported_block.slot, humanize_hash(imported_block.signing_root), ) return imported_block, new_canonical_blocks, old_canonical_blocks
def import_block( self, block: BaseBeaconBlock, perform_validation: bool = True ) -> Tuple[BaseBeaconBlock, Tuple[BaseBeaconBlock, ...], Tuple[ BaseBeaconBlock, ...]]: """ Import a complete block and returns a 3-tuple - the imported block - a tuple of blocks which are now part of the canonical chain. - a tuple of blocks which were canonical and now are no longer canonical. """ try: parent_block = self.get_block_by_root(block.parent_root) except BlockNotFound: raise ValidationError( "Attempt to import block #{}. Cannot import block {} before importing " "its parent block at {}".format(block.slot, block.signing_root, block.parent_root)) head_state_slot = self.chaindb.get_head_state_slot() if head_state_slot >= block.slot: # Importing a block older than the head state. Hence head state can not be used to # perform state transition. prev_state_slot = parent_block.slot else: prev_state_slot = head_state_slot state_machine = self.get_state_machine(prev_state_slot) state = self.get_state_by_slot(prev_state_slot) state, imported_block = state_machine.import_block( block, state, check_proposer_signature=perform_validation) # Validate the imported block. if perform_validation: validate_imported_block_unchanged(imported_block, block) # TODO: Now it just persists all state. Should design how to clean up the old state. self.chaindb.persist_state(state) fork_choice_scoring = state_machine.get_fork_choice_scoring() (new_canonical_blocks, old_canonical_blocks) = self.chaindb.persist_block( imported_block, imported_block.__class__, fork_choice_scoring) self.logger.debug( "IMPORTED_BLOCK: slot %s | signed root %s", imported_block.slot, encode_hex(imported_block.signing_root), ) return imported_block, new_canonical_blocks, old_canonical_blocks
def import_block( self, block: BaseBeaconBlock, perform_validation: bool = True ) -> Tuple[BaseBeaconBlock, Tuple[BaseBeaconBlock, ...], Tuple[ BaseBeaconBlock, ...]]: """ Import a complete block and returns a 3-tuple - the imported block - a tuple of blocks which are now part of the canonical chain. - a tuple of blocks which were canonical and now are no longer canonical. """ try: parent_block = self.get_block_by_root(block.previous_block_root) except BlockNotFound: raise ValidationError( "Attempt to import block #{}. Cannot import block {} before importing " "its parent block at {}".format( block.slot, block.signing_root, block.previous_block_root, )) base_block_for_import = self.create_block_from_parent( parent_block, FromBlockParams(), ) state, imported_block = self.get_state_machine( base_block_for_import).import_block(block) # Validate the imported block. if perform_validation: validate_imported_block_unchanged(imported_block, block) # TODO: Now it just persists all state. Should design how to clean up the old state. self.chaindb.persist_state(state) ( new_canonical_blocks, old_canonical_blocks, ) = self.chaindb.persist_block(imported_block, imported_block.__class__) self.logger.debug( 'IMPORTED_BLOCK: slot %s | signed root %s', imported_block.slot, encode_hex(imported_block.signing_root), ) return imported_block, new_canonical_blocks, old_canonical_blocks
def FuzzerRunOne(input_data: bytes) -> typing.Optional[bytes]: test_case = ssz.decode(input_data, BlockTestCase) try: post = st_instance.apply_state_transition( state=test_case.pre, block=test_case.block, check_proposer_signature=True, ) if VALIDATE_STATE_ROOT: # NOTE trinity performs state root validation at a higher level # so we perform it here if needed. # See https://github.com/ethereum/trinity/issues/1340 # https://github.com/ethereum/trinity/blob/0d53ef2cf57458d11c5a5b0e5546b026f2fce3f9/eth2/beacon/chains/base.py#L413-L415 # TODO when updating spec versions, ensure this matches the relevant # Trinity code post_block = test_case.block.copy(state_root=post.hash_tree_root) # Raises a ValidationError if state_roots are different validate_imported_block_unchanged(test_case.block, post_block) except ValidationError as e: return None # NOTE - signature verification should do nothing with bls disabled return ssz.encode(post)
def _import_block( self, block: BaseSignedBeaconBlock, perform_validation: bool = True) -> BaseSignedBeaconBlock: try: # NOTE: would need to introduce a "root to slot" look up here for polymorphism parent_block = self._chain_db.get_block_by_root( block.parent_root, BeaconBlock) except BlockNotFound: raise ParentNotFoundError( f"attempt to import block {block} but missing parent block") # NOTE: check if block is in the canonical chain # First, see if we have a block already at that slot... existing_block = self._get_block_by_slot(block.slot, block.__class__) if existing_block: if existing_block != block: # NOTE: we want to keep the block but avoid heavy state transition for now... # Rationale: this block may simply be a slashable block. It could also be on # a fork. Choose to defer the state materialization until we re-org via fork choice. self._chain_db.persist_block(block) raise SlashableBlockError( block, f"attempt to import {block} but canonical chain" " already has a block at this slot", ) else: # NOTE: block already imported! return block else: head = self.get_canonical_head() extension_of_head = block.parent_root == head.hash_tree_root if not extension_of_head: # NOTE: this arm implies we received a block for a slot _ahead_ of our head # on the canonical chain... # NOTE: block validity _should_ reject a block before it gets to this layer # but we will double-check in the event that invariant is violated or does not hold # NOTE: we elect to the block in the event of a # re-org later, but do no further processing. self._chain_db.persist_block(block) raise SlashableBlockError( block, f"attempt to import {block} but canonical chain is not as far ahead", ) state_machine = self.get_state_machine(block.slot) pre_state = self._chain_db.get_state_by_root(parent_block.state_root, state_machine.state_class, state_machine.config) state, imported_block = state_machine.apply_state_transition( pre_state, block, check_proposer_signature=perform_validation) if perform_validation: validate_imported_block_unchanged(imported_block, block) # NOTE: if we have a valid block/state, then record in the database. self._chain_db.persist_block(block) self._chain_db.persist_state(state, state_machine.config) self._reconcile_justification_and_finality(state) return imported_block
def import_block( self, block: BaseBeaconBlock, perform_validation: bool = True ) -> Tuple[BaseBeaconBlock, Tuple[BaseBeaconBlock, ...], Tuple[ BaseBeaconBlock, ...]]: """ Import a complete block and returns a 3-tuple - the imported block - a tuple of blocks which are now part of the canonical chain. - a tuple of blocks which were canonical and now are no longer canonical. """ try: parent_block = self.get_block_by_root(block.parent_root) except BlockNotFound: raise ValidationError( "Attempt to import block #{}. Cannot import block {} before importing " "its parent block at {}".format(block.slot, block.signing_root, block.parent_root)) try: canonical_root_at_slot = self.get_canonical_block_root( parent_block.slot) except BlockNotFound: # No corresponding block at this slot which means parent block is not # on canonical chain. is_new_canonical_block = False else: is_on_canonical_chain = parent_block.signing_root == canonical_root_at_slot is_newer_than_head_state_slot = ( self.chaindb.get_head_state_slot() < block.slot) is_new_canonical_block = (is_on_canonical_chain and is_newer_than_head_state_slot) if is_new_canonical_block: state_machine = self.get_state_machine() state = self.get_head_state() else: state_machine = self.get_state_machine(at_slot=parent_block.slot) state = self.get_state_by_slot(parent_block.slot) state, imported_block = state_machine.import_block( block, state, check_proposer_signature=perform_validation) # Validate the imported block. if perform_validation: validate_imported_block_unchanged(imported_block, block) # TODO: Now it just persists all state. Should design how to clean up the old state. self.chaindb.persist_state(state) fork_choice_scoring = state_machine.get_fork_choice_scoring() (new_canonical_blocks, old_canonical_blocks) = self.chaindb.persist_block( imported_block, imported_block.__class__, fork_choice_scoring) self.logger.debug( "IMPORTED_BLOCK: slot %s | signed root %s", imported_block.slot, encode_hex(imported_block.signing_root), ) return imported_block, new_canonical_blocks, old_canonical_blocks
def import_block( self, block: BaseSignedBeaconBlock, perform_validation: bool = True ) -> Tuple[ BaseSignedBeaconBlock, Tuple[BaseSignedBeaconBlock, ...], Tuple[BaseSignedBeaconBlock, ...], ]: """ Import a complete block and returns a 3-tuple - the imported block - a tuple of blocks which are now part of the canonical chain. - a tuple of blocks which were canonical and now are no longer canonical. """ self.logger.debug("attempting import of block %s", block) try: parent_block = self.get_block_by_root(block.parent_root) except BlockNotFound: raise ParentNotFoundError( f"attempt to import block {block} but missing parent block" ) try: existing_block = self.get_canonical_block_by_slot(block.slot) if existing_block != block: # NOTE: we want to keep the block but avoid heavy state transition for now... # Rationale: this block may simply be a slashable block. It could also be on # a fork. Choose to defer the state materialization until we re-org via fork choice. self.chaindb.write_signed_block(block) raise SlashableBlockError( block, f"attempt to import {block} but canonical chain" " already has a block at this slot", ) else: # NOTE: block already imported! return block, (), () except BlockNotFound: # NOTE: block has not been imported for ``block.slot`` pass state_machine = self.get_state_machine(at_slot=parent_block.slot) state_class = state_machine.state_class state = self.chaindb.get_state_by_root( parent_block.message.state_root, state_class ) state, imported_block = state_machine.apply_state_transition( state, block, check_proposer_signature=perform_validation ) # Validate the imported block. if perform_validation: validate_imported_block_unchanged(imported_block, block) fork_choice_scoring = state_machine.get_fork_choice_scoring() ( new_canonical_blocks, old_canonical_blocks, ) = self._persist_validated_state_and_block( state, imported_block, fork_choice_scoring ) self.logger.debug("successfully imported block %s", imported_block) return imported_block, new_canonical_blocks, old_canonical_blocks