def test_block_is_not_genesis(): genesis_block = SignedBeaconBlock.create().transform( ("message", "parent_root"), GENESIS_PARENT_ROOT) another_block = SignedBeaconBlock.from_parent(genesis_block, FromBlockParams()) assert genesis_block.is_genesis assert not another_block.is_genesis
async def test_hosts_can_gossip_blocks(host_factory): host_a_blocks = set() host_a, host_a_listen_maddr = host_factory("a", host_a_blocks) host_b_blocks = set() host_b, host_b_listen_maddr = host_factory("b", host_b_blocks) with trio.move_on_after(2 * 60): async with _run_host(host_a, host_a_listen_maddr): async with _run_host(host_b, host_b_listen_maddr): await host_b.add_peer_from_maddr(host_a_listen_maddr) await host_a.subscribe_gossip_channels() await host_b.subscribe_gossip_channels() # NOTE: subscription fails to register if we do not sleep here... # Need to debug inside `libp2p`... await trio.sleep(1) block = SignedBeaconBlock.create(signature=b"\xcc" * 96) await host_a.broadcast_block(block) block_source = host_b.stream_block_gossip() gossiped_block = await block_source.__anext__() assert gossiped_block == block # NOTE: the following is racy... # Need to debug inside `libp2p`... await host_a.unsubscribe_gossip_channels() await trio.sleep(1) await host_b.unsubscribe_gossip_channels()
def test_get_state_root_at_slot( sample_beacon_state_params, current_slot, target_slot, success, slots_per_epoch, slots_per_historical_root, ): blocks, _, state_roots = generate_mock_latest_historical_roots( SignedBeaconBlock.create(), current_slot, slots_per_epoch, slots_per_historical_root, ) state = BeaconState.create(**sample_beacon_state_params).mset( "slot", current_slot, "state_roots", state_roots ) blocks[current_slot].message.state_root = state.hash_tree_root if success: state_root = get_state_root_at_slot( state, target_slot, slots_per_historical_root ) assert state_root == blocks[target_slot].message.state_root else: with pytest.raises(ValidationError): get_state_root_at_slot(state, target_slot, slots_per_historical_root)
def _find_present_ancestor_state( self, block_root: Root ) -> Tuple[BeaconState, Tuple[SignedBeaconBlock, ...]]: """ Find the first state we have persisted that is an ancestor of ``target_block``. """ try: block = self._chain_db.get_block_by_root(block_root, BeaconBlock) blocks: Tuple[SignedBeaconBlock, ...] = () # NOTE: re: bounds here; worst case, we return the genesis state. for slot in range(block.slot, GENESIS_SLOT - 1, -1): try: state_machine = self.get_state_machine(Slot(slot)) state = self._chain_db.get_state_by_root( block.state_root, state_machine.state_class, state_machine.config, ) return (state, blocks) except StateNotFound: signature = self._chain_db.get_block_signature_by_root( block.hash_tree_root) blocks += (SignedBeaconBlock.create(message=block, signature=signature), ) block = self._chain_db.get_block_by_root( block.parent_root, BeaconBlock) except BlockNotFound: raise Exception( "invariant violated: querying a block that has not been persisted" ) # NOTE: `mypy` complains without this although execution should never get here... return (None, ())
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 _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
def _get_block_by_root(self, root: Root) -> Optional[SignedBeaconBlock]: try: block = self._chain.db.get_block_by_root(root, BeaconBlock) signature = self._chain.db.get_block_signature_by_root( block.hash_tree_root) return SignedBeaconBlock.create(message=block, signature=signature) except BlockNotFound: return None
def get_pseudo_chain(length, genesis_block): """ Get a pseudo chain, only slot and parent_root are valid. """ block = genesis_block yield genesis_block for slot in range(1, length * 3): block = SignedBeaconBlock.from_parent(block, FromBlockParams(slot=slot)) yield block
def _get_block_by_slot( self, slot: Slot, block_class: Type[SignedBeaconBlock] ) -> Optional[SignedBeaconBlock]: # check in db first, implying a finalized chain block = self._chain_db.get_block_by_slot(slot, block_class.block_class) if block: signature = self._chain_db.get_block_signature_by_root(block.hash_tree_root) return SignedBeaconBlock.create(message=block, signature=signature) else: # check in the canonical chain according to fork choice # NOTE: likely want a more efficient way to determine block by slot... for block in self._fork_choice.get_canonical_chain(): if block.slot == slot: signature = self._chain_db.get_block_signature_by_root( block.hash_tree_root ) return SignedBeaconBlock.create(message=block, signature=signature) else: return None
async def test_json_rpc_http_server(aiohttp_raw_server, aiohttp_client, event_bus, base_db, ipc_path): manager = DBManager(base_db) with manager.run(ipc_path): # Set chaindb override_lengths(SERENITY_CONFIG) db = DBClient.connect(ipc_path) genesis_config = SERENITY_CONFIG chaindb = AsyncBeaconChainDB(db, genesis_config) fork_choice_scoring = HigherSlotScoring() genesis_state, genesis_block = create_mock_genesis( pubkeys=(), config=SERENITY_CONFIG, keymap=dict(), genesis_block_class=BeaconBlock, genesis_time=0, ) chaindb.persist_state(genesis_state) chaindb.persist_block( SignedBeaconBlock.create(message=genesis_block), SignedBeaconBlock, fork_choice_scoring, ) try: rpc = RPCServer(initialize_beacon_modules(chaindb, event_bus), chaindb, event_bus) raw_server = await aiohttp_raw_server( RPCHandler.handle(rpc.execute)) client = await aiohttp_client(raw_server) request_id = 1 request_data = { "jsonrpc": "2.0", "method": "beacon_head", "params": [], "id": request_id, } response = await client.post("/", json=request_data) response_data = await response.json() assert response_data["id"] == request_id result = response_data["result"] assert result["slot"] == 0 assert decode_hex( result["block_root"]) == genesis_block.hash_tree_root assert decode_hex( result["state_root"]) == genesis_state.hash_tree_root except KeyboardInterrupt: pass finally: await raw_server.close() db.close()
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), )) assert type(genesis_block) == SerenitySignedBeaconBlock block = SignedBeaconBlock.create(message=genesis_block.message) assert type(block) == SignedBeaconBlock chain = klass.from_genesis(base_db, genesis_state) assert chain
def _attach_signature(duty: Duty, operation: Operation, signature: BLSSignature) -> SignedOperation: if duty.duty_type == DutyType.Attestation: attestation = cast(Attestation, operation) return attestation.set("signature", signature) elif duty.duty_type == DutyType.BlockProposal: block_proposal = cast(BeaconBlock, operation) return SignedBeaconBlock.create(message=block_proposal, signature=signature) else: raise NotImplementedError(f"unrecognized duty type in duty {duty}")
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) == SerenitySignedBeaconBlock block = SignedBeaconBlock.create(message=genesis_block.message) assert type(block) == SignedBeaconBlock with pytest.raises(BlockClassError): klass.from_genesis(base_db, genesis_state, block, config)
def _build_branch_across_slots(number_of_slots, chain, config): head = chain.get_canonical_head() state = chain.db.get_state_by_root(head.state_root, BeaconState) head = SignedBeaconBlock.create(message=head) return _mk_minimum_viable_signed_beacon_blocks( range(head.slot + 1, head.slot + 1 + number_of_slots), lambda slot: chain.get_state_machine(slot), state, head, config, )
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
async def test_hosts_can_do_req_resp(host_factory): host_a_blocks = set() host_a, host_a_listen_maddr = host_factory("c", host_a_blocks) host_b_blocks = set( [SignedBeaconBlock.create(message=BeaconBlock.create(slot=0))]) host_b, host_b_listen_maddr = host_factory("d", host_b_blocks) with trio.move_on_after(2 * 60): async with _run_host(host_a, host_a_listen_maddr): async with _run_host(host_b, host_b_listen_maddr): await host_b.add_peer_from_maddr(host_a_listen_maddr) assert len(host_a.get_network().connections) == 1 assert len(host_b.get_network().connections) == 1 peer_b = host_b.get_id() status_b = await host_a.exchange_status( peer_b, host_a._status_provider()) assert status_b some_blocks = set() async for block in host_a.get_blocks_by_range(peer_b, 0, 20): some_blocks.add(block) assert len(some_blocks) == 1 no_blocks = set() async for block in host_a.get_blocks_by_range(peer_b, 30, 3): no_blocks.add(block) assert len(no_blocks) == 0 a_block = some_blocks.pop() the_same_block = await host_a.get_blocks_by_root( peer_b, a_block.message.hash_tree_root).__anext__() assert a_block == the_same_block b_seq_number = await host_a.send_ping(peer_b) assert b_seq_number == ord("d") b_metadata = await host_a.get_metadata(peer_b) assert b_metadata.seq_number == b_seq_number await host_a.send_goodbye_to(peer_b, GoodbyeReason.shutting_down) assert len(host_a.get_network().connections) == 0 assert len(host_b.get_network().connections) == 0
def test_chain2_at_genesis(base_db, genesis_state, genesis_block, config): genesis_block = genesis_block.message chain_db = BeaconChainDB.from_genesis(base_db, genesis_state, SignedBeaconBlock, config) block_at_genesis = chain_db.get_block_by_slot(GENESIS_SLOT, BeaconBlock) assert block_at_genesis == genesis_block block_at_genesis = chain_db.get_block_by_root(genesis_block.hash_tree_root, BeaconBlock) assert block_at_genesis == genesis_block genesis_signature = chain_db.get_block_signature_by_root( genesis_block.hash_tree_root) assert genesis_signature == EMPTY_SIGNATURE state_at_genesis = chain_db.get_state_by_slot(GENESIS_SLOT, BeaconState) assert state_at_genesis == genesis_state state_at_genesis = chain_db.get_state_by_root(genesis_state.hash_tree_root, BeaconState) assert state_at_genesis == genesis_state finalized_head = chain_db.get_finalized_head(BeaconBlock) assert finalized_head == genesis_block some_future_slot = Slot(22) assert not chain_db.get_block_by_slot(some_future_slot, BeaconBlock) assert not chain_db.get_state_by_slot(some_future_slot, BeaconState) block_at_future_slot = SignedBeaconBlock.create(message=BeaconBlock.create( slot=some_future_slot)) chain_db.persist_block(block_at_future_slot) future_block = chain_db.get_block_by_root( block_at_future_slot.message.hash_tree_root, BeaconBlock) assert block_at_future_slot.message == future_block # NOTE: only finalized blocks are stored by slot in the DB # non-finalized but canonical blocks are determined by fork choice, separately from the DB assert not chain_db.get_block_by_slot(some_future_slot, BeaconBlock) # assume the fork choice did finalize this block... chain_db.mark_canonical_block(block_at_future_slot.message) assert chain_db.get_block_by_slot(some_future_slot, BeaconBlock) == future_block
def create_block_proposal( slot: Slot, parent_root: Root, randao_reveal: BLSSignature, eth1_data: Eth1Data, state: BeaconState, state_machine: BaseBeaconStateMachine, ) -> BeaconBlock: proposal = BeaconBlock.create( slot=slot, parent_root=parent_root, body=BeaconBlockBody.create(randao_reveal=randao_reveal, eth1_data=eth1_data), ) signed_block = SignedBeaconBlock.create(message=proposal, signature=EMPTY_SIGNATURE) post_state, signed_block = state_machine.import_block( signed_block, state, check_proposer_signature=False ) return signed_block.message
def test_chain2_full(base_db, genesis_state, config): chain_db = BeaconChainDB.from_genesis(base_db, genesis_state, SignedBeaconBlock, config) state = genesis_state states = [genesis_state] blocks = {} num_slots = 500 skip_slots = [5, 64, 100, 300, 301, 302, 401] finalized_slots = [8, 24, 32, 72, 152, 160, 328, 336, 344, 352, 400] # create a new state at each slot using ``_mini_stf()`` and persist it for _ in range(1, num_slots): if state.slot not in skip_slots: new_block = BeaconBlock.create(slot=state.slot, state_root=state.hash_tree_root) blocks[state.slot] = new_block chain_db.persist_block(SignedBeaconBlock.create(message=new_block)) else: new_block = None state = _mini_stf(state, new_block, config) chain_db.persist_state(state, config) states.append(state) # test that each state created above equals the state stored at its root for state in states: # finalize a slot two epochs after processing it # this is here to test the reconstruction of ``state.randao_mixes`` maybe_finalized_slot = state.slot - config.SLOTS_PER_EPOCH * 2 if maybe_finalized_slot in finalized_slots: chain_db.mark_finalized_head(blocks[maybe_finalized_slot]) retrieved_state = chain_db._read_state(state.hash_tree_root, BeaconState, config) assert retrieved_state == state for slot in range(0, num_slots): if slot in blocks and slot <= finalized_slots[-1]: assert chain_db.get_block_by_slot(Slot(slot), BeaconBlock) == blocks[slot] else: assert chain_db.get_block_by_slot(Slot(slot), BeaconBlock) is None
def test_validate_proposer_signature( slots_per_epoch, max_committees_per_slot, proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, target_committee_size, max_effective_balance, config, ): state = BeaconState.create(**sample_beacon_state_params).mset( "validators", tuple( create_mock_validator(proposer_pubkey, config) for _ in range(10)), "balances", (max_effective_balance, ) * 10, ) block = BeaconBlock.create(**sample_beacon_block_params) proposed_block = SignedBeaconBlock.create( message=block, signature=bls.sign( message_hash=block.hash_tree_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 sign_block(state: BeaconState, block: BeaconBlock, private_key: int, slots_per_epoch: int) -> SignedBeaconBlock: signature = get_block_signature(state, block, private_key, slots_per_epoch) return SignedBeaconBlock.create(message=block, signature=signature)