def _get_checkpoints(cls, db: DatabaseAPI) -> Tuple[Hash32, ...]: concatenated_checkpoints = db.get( SchemaV1.make_checkpoint_headers_key()) if concatenated_checkpoints is None: return () else: return tuple( Hash32(concatenated_checkpoints[index:index + 32]) for index in range(0, len(concatenated_checkpoints), 32))
def _persist_checkpoint_header( cls, db: DatabaseAPI, header: BlockHeaderAPI, score: int ) -> None: db.set( header.hash, rlp.encode(header), ) # Add new checkpoint header previous_checkpoints = cls._get_checkpoints(db) new_checkpoints = previous_checkpoints + (header.hash,) db.set( SchemaV1.make_checkpoint_headers_key(), b''.join(new_checkpoints), ) previous_score = score - header.difficulty cls._set_hash_scores_to_db(db, header, previous_score) cls._set_as_canonical_chain_head(db, header, GENESIS_PARENT_HASH) _, gaps = cls._update_header_chain_gaps(db, header) # check if the parent block number exists, and is not a match for checkpoint.parent_hash parent_block_num = BlockNumber(header.block_number - 1) try: parent_hash = cls._get_canonical_block_hash(db, parent_block_num) except HeaderNotFound: # no parent to check pass else: # User is asserting that the checkpoint must be canonical, so if the parent doesn't # match, then the parent must not be canonical, and should be de-canonicalized. if parent_hash != header.parent_hash: # does the correct header exist in the database? try: true_parent = cls._get_block_header_by_hash(db, header.parent_hash) except HeaderNotFound: # True parent unavailable, just delete the now non-canonical one cls._decanonicalize_single(db, parent_block_num, gaps) else: # True parent should have already been canonicalized during # _set_as_canonical_chain_head() raise ValidationError( f"Why was a non-matching parent header {parent_hash!r} left as canonical " f"after _set_as_canonical_chain_head() and {true_parent} is available?" ) cls._decanonicalize_descendant_orphans(db, header, new_checkpoints)