def _read_state_randao_mixes(self, state_root: Root, EPOCHS_PER_HISTORICAL_VECTOR: int, SLOTS_PER_EPOCH: int) -> Iterable[Root]: """ Reconstructs the ``randao_mixes`` at a given state root. """ state_slot = self._read_state_slot(state_root) state_epoch = compute_epoch_at_slot(state_slot, SLOTS_PER_EPOCH) finalized_slot = self.get_finalized_head(BeaconBlock).slot non_finalized_state_roots = dict( enumerate( self.get_state_parents(state_root, state_slot - finalized_slot), finalized_slot, )) # create a list of epochs that corresponds to each mix in ``state.randao_mixes`` epochs = [ Epoch(n) for n in range(state_epoch - EPOCHS_PER_HISTORICAL_VECTOR + 1, state_epoch + 1) ] offset = EPOCHS_PER_HISTORICAL_VECTOR - epochs[ 0] % EPOCHS_PER_HISTORICAL_VECTOR epochs = epochs[offset:] + epochs[:offset] genesis_root = self._read_state_root_at_slot(Slot(0)) genesis_randao_mix = Root( Hash32(self.db[SchemaV1.state_root_to_randao_mix(genesis_root)])) for epoch in epochs: if epoch < 0: yield genesis_randao_mix elif epoch == state_epoch: # yield the randao mix at the particular slot key = SchemaV1.state_root_to_randao_mix(state_root) yield Root(Hash32(self.db[key])) else: # yield the randao mix at the last slot in the epoch slot = Slot((epoch + 1) * SLOTS_PER_EPOCH - 1) if slot in non_finalized_state_roots: root = non_finalized_state_roots[slot] else: root = self._read_state_root_at_slot(slot) key = SchemaV1.state_root_to_randao_mix(root) yield Root(Hash32(self.db[key]))
def get_state_by_slot(self, slot: Slot, state_class: Type[BeaconState], config: Eth2Config) -> Optional[BeaconState]: key = SchemaV1.slot_to_state_root(slot) try: root = Root(Hash32(self.db[key])) except KeyError: return None return self.get_state_by_root(root, state_class, config)
def get_block_by_slot( self, slot: Slot, block_class: Type[BaseBeaconBlock]) -> Optional[BaseBeaconBlock]: key = SchemaV1.slot_to_block_root(slot) try: root = Root(Hash32(self.db[key])) except KeyError: return None return self.get_block_by_root(root, block_class)
def _bytes_to_roots(roots_bytes: bytes) -> Iterable[Root]: bytes_len = len(roots_bytes) if bytes_len % 32 != 0: raise ValueError("bytes not divisible by 32") for i in range(0, int(bytes_len / 32)): start = i * 32 end = start + 32 yield Root(Hash32(roots_bytes[start:end]))
def _get_genesis_data(self) -> Tuple[Timestamp, Root]: key = SchemaV1.genesis_data() try: data = self.db[key] except KeyError: return default_timestamp, default_root genesis_time = ssz.decode(data[:8], ssz.sedes.uint64) genesis_validators_root = ssz.decode(data[8:], ssz.sedes.bytes32) return Timestamp(genesis_time), Root(genesis_validators_root)
def _write_state_block_header(self, state_root: Root, block_header: BeaconBlockHeader) -> None: block_root = Root(block_header.hash_tree_root) state_root_to_latest_block_header_root = SchemaV1.state_root_to_latest_block_header_root( state_root) self.db[state_root_to_latest_block_header_root] = block_root block_root_to_block_header = SchemaV1.block_root_to_block_header( block_root) self.db[block_root_to_block_header] = ssz.encode(block_header)
def _read_state_balances(self, state_root: Root, VALIDATOR_REGISTRY_LIMIT: int) -> Tuple[Gwei]: state_root_to_balances_root = SchemaV1.state_root_to_balances_root( state_root) balances_root = Root(Hash32(self.db[state_root_to_balances_root])) balances_root_to_balances = SchemaV1.balances_root_to_balances( balances_root) return ssz.decode( self.db[balances_root_to_balances], ssz.List(ssz.uint64, VALIDATOR_REGISTRY_LIMIT), )
def _read_state_validators(self, state_root: Root) -> Iterable[Validator]: state_root_to_validators_root = SchemaV1.state_root_to_validators_root( state_root) validators_root = Root(Hash32(self.db[state_root_to_validators_root])) validators_root_to_roots_of_validators = SchemaV1.validators_root_to_roots_of_validators( validators_root) roots = _bytes_to_roots( self.db[validators_root_to_roots_of_validators]) for root in roots: yield ssz.decode(self.db[bytes(root)], Validator)
def _read_state_block_roots( self, state_root: Root, SLOTS_PER_HISTORICAL_ROOT: int) -> Iterable[Root]: """ Reconstructs ``state.block_roots`` at a given state root. """ for root in self._read_state_state_roots(state_root, SLOTS_PER_HISTORICAL_ROOT): if root == default_root: yield default_root else: key = SchemaV1.state_root_to_latest_block_header_root(root) yield Root(Hash32(self.db[key]))
def sign(duty: Duty, operation: Operation, private_key_provider: PrivateKeyProvider) -> BLSSignature: privkey = private_key_provider(duty.validator_public_key) # TODO use correct ``domain`` value # NOTE currently only uses part of the domain value # need to get fork from the state and compute the full domain value locally # NOTE: hardcoded for testing, based on generating the minimal set of validators genesis_validators_root = Root( Hash32( bytes.fromhex( "83431ec7fcf92cfc44947fc0418e831c25e1d0806590231c439830db7ad54fda" ))) domain = compute_domain(duty.signature_domain, genesis_validators_root=genesis_validators_root) signing_root = compute_signing_root(operation, domain) return bls.sign(privkey, signing_root)
def test_get_matching_head_attestations(genesis_state, config): some_epoch = GENESIS_EPOCH + 20 some_slot = ( compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH) + config.SLOTS_PER_EPOCH // 4 ) some_target_root = Root(Hash32(b"\x33" * 32)) target_attestations = tuple( ( PendingAttestation.create( data=AttestationData.create( slot=some_slot - 1, index=0, beacon_block_root=some_target_root, target=Checkpoint.create( epoch=some_epoch - 1, root=some_target_root ), ) ) for _ in range(3) ) ) current_epoch_attestations = target_attestations + tuple( ( PendingAttestation.create( data=AttestationData.create( beacon_block_root=b"\x44" * 32, target=Checkpoint.create(epoch=some_epoch - 1), ) ) for _ in range(3) ) ) state = genesis_state.mset( "slot", some_slot, "block_roots", tuple(some_target_root for _ in range(config.SLOTS_PER_HISTORICAL_ROOT)), "current_epoch_attestations", current_epoch_attestations, ) attestations = get_matching_head_attestations(state, some_epoch, config) assert attestations == target_attestations
def _randao_provider_of_epoch_signature( public_key: BLSPubkey, epoch: Epoch ) -> BLSSignature: privkey = private_key_provider(public_key) # NOTE: hardcoded for testing, based on generating the minimal set of validators genesis_validators_root = Root( Hash32( bytes.fromhex( "83431ec7fcf92cfc44947fc0418e831c25e1d0806590231c439830db7ad54fda" ) ) ) domain = compute_domain( SignatureDomain.DOMAIN_RANDAO, genesis_validators_root=genesis_validators_root, ) signing_root = compute_signing_root(SerializableUint64(epoch), domain) return bls.sign(privkey, signing_root)
def _get_justified_head( cls, db: DatabaseAPI, block_class: Type[BaseBeaconBlock] ) -> BaseBeaconBlock: justified_head_root = cls._get_justified_head_root(db) return cls._get_block_by_root(db, Root(justified_head_root), block_class)
def _get_canonical_head( cls, db: DatabaseAPI, block_class: Type[BaseBeaconBlock] ) -> BaseBeaconBlock: canonical_head_root = cls._get_canonical_head_root(db) return cls._get_block_by_root(db, Root(canonical_head_root), block_class)
def _mini_stf(state: BeaconState, block: Optional[BeaconBlock], config: Eth2Config) -> BeaconState: """ A simplified state transition for testing state storage. - updates ``state_roots`` with the previous slot's state root - updates ``block_roots`` with the previous slot's block root - updates ``randao_mixes`` with an arbitrary mix at the current epoch - creates a new ``latest_block_header`` and adds it to the state - fills in the rest of the attributes with arbitrary values """ current_slot = state.slot + 1 current_epoch = current_slot // config.SLOTS_PER_EPOCH if block: latest_block_header = block.header else: latest_block_header = state.latest_block_header # state changes that depend on the previous state for retrieval randao_mix = Root(Hash32(current_slot.to_bytes(32, byteorder="little"))) state = (state.transform( ("state_roots", state.slot % config.SLOTS_PER_HISTORICAL_ROOT), state.hash_tree_root, ).transform( ("block_roots", state.slot % config.SLOTS_PER_HISTORICAL_ROOT), state.latest_block_header.hash_tree_root, ).transform( ("randao_mixes", current_epoch % config.EPOCHS_PER_HISTORICAL_VECTOR), randao_mix, ).mset("slot", current_slot, "latest_block_header", latest_block_header)) # state changes that do not depend on the previous state for retrieval new_validators = [ Validator.create(pubkey=BLSPubkey(n.to_bytes(48, byteorder="little"))) for n in range(current_slot, current_slot + 20) ] new_eth1_data_votes = [ Eth1Data.create( deposit_root=Root(Hash32(n.to_bytes(32, byteorder="little")))) for n in range(current_slot, current_slot + 7) ] new_previous_epoch_attestations = [ PendingAttestation.create(proposer_index=ValidatorIndex(n)) for n in range(current_slot, current_slot + 5) ] new_current_epoch_attestations = [ PendingAttestation.create(proposer_index=ValidatorIndex(n)) for n in range(current_slot + 5, current_slot + 10) ] state = state.mset( "validators", new_validators, "balances", (32, ) * len(new_validators), "eth1_data_votes", new_eth1_data_votes, "eth1_data", new_eth1_data_votes[0], "previous_epoch_attestations", new_previous_epoch_attestations, "current_epoch_attestations", new_current_epoch_attestations, "previous_justified_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 42)), "current_justified_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 43)), "finalized_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 44)), ) return state
def get_justified_head( self, block_class: Type[BaseBeaconBlock]) -> BaseBeaconBlock: justified_head_root_key = SchemaV1.justified_head_root() justified_head_root = Root(Hash32(self.db[justified_head_root_key])) return self.get_block_by_root(justified_head_root, block_class)
def _read_state_parent_state_root(self, state_root: Root) -> Root: key = SchemaV1.state_root_to_parent_state_root(state_root) return Root(Hash32(self.db[key]))
def get_finalized_head( self, block_class: Type[BaseBeaconBlock]) -> BaseBeaconBlock: finalized_head_root_key = SchemaV1.finalized_head_root() finalized_head_root = Root(Hash32(self.db[finalized_head_root_key])) return self.get_block_by_root(finalized_head_root, block_class)
from eth.constants import ZERO_HASH32 from eth_typing import BLSPubkey, BLSSignature from eth2.beacon.typing import Epoch, Root, Timestamp EMPTY_SIGNATURE = BLSSignature(b"\x00" * 96) EMPTY_PUBKEY = BLSPubkey(b"\x00" * 48) GWEI_PER_ETH = 10**9 FAR_FUTURE_EPOCH = Epoch(2**64 - 1) ZERO_ROOT = Root(ZERO_HASH32) GENESIS_PARENT_ROOT = ZERO_ROOT ZERO_TIMESTAMP = Timestamp(0) MAX_INDEX_COUNT = 2**40 MAX_RANDOM_BYTE = 2**8 - 1 BASE_REWARDS_PER_EPOCH = 4 DEPOSIT_CONTRACT_TREE_DEPTH = 2**5 SECONDS_PER_DAY = 86400 JUSTIFICATION_BITS_LENGTH = 4
def _read_state_root_at_slot(self, slot: Slot) -> Root: key = SchemaV1.slot_to_state_root(slot) return Root(Hash32(self.db[key]))
def _read_state_block_header(self, state_root: Root) -> BeaconBlockHeader: block_root = Root( Hash32(self.db[SchemaV1.state_root_to_latest_block_header_root( state_root)])) key = SchemaV1.block_root_to_block_header(block_root) return ssz.decode(self.db[key], BeaconBlockHeader)
) def sign_transaction(*, message_hash: Hash32, privkey: int, state: BeaconState, slot: Slot, signature_domain: SignatureDomain, slots_per_epoch: int) -> BLSSignature: domain = get_domain( state, signature_domain, slots_per_epoch, message_epoch=compute_epoch_at_slot(slot, slots_per_epoch), ) return bls.sign(message_hash=message_hash, privkey=privkey, domain=domain) SAMPLE_HASH_1 = Root(Hash32(b"\x11" * 32)) SAMPLE_HASH_2 = Hash32(b"\x22" * 32) def create_block_header_with_signature( state: BeaconState, body_root: Hash32, privkey: int, slots_per_epoch: int, parent_root: Root = SAMPLE_HASH_1, state_root: Hash32 = SAMPLE_HASH_2, ) -> SignedBeaconBlockHeader: block_header = BeaconBlockHeader.create( slot=state.slot, parent_root=parent_root, state_root=state_root,
def get_canonical_head( self, block_class: Type[BaseBeaconBlock]) -> BaseBeaconBlock: canonical_head_root_key = SchemaV1.canonical_head_root() canonical_head_root = Root(Hash32(self.db[canonical_head_root_key])) return self.get_block_by_root(canonical_head_root, block_class)