def test_key_store_can_import_private_key(tmp_path, sample_bls_key_pairs): public_key, private_key = tuple(sample_bls_key_pairs.items())[0] encoded_private_key = private_key.to_bytes(32, "little").hex() key_store = KeyStore(sample_bls_key_pairs) key_store.import_private_key(encoded_private_key) assert key_store.private_key_for(public_key) == private_key
def test_key_store_can_persist_key_files(tmp_path, sample_bls_key_pairs, has_key_pairs): some_password = b"password" public_key, private_key = tuple(sample_bls_key_pairs.items())[0] private_key_bytes = private_key.to_bytes(32, "little") encoded_private_key = private_key_bytes.hex() if has_key_pairs: key_pairs = sample_bls_key_pairs else: key_pairs = {} key_store = KeyStore( key_pairs=key_pairs, key_store_dir=tmp_path, password_provider=lambda _public_key: some_password, ) with key_store.persistence(): assert not tuple(key_store._key_store_dir.iterdir()) key_store.import_private_key(encoded_private_key) key_files = tuple(key_store._key_store_dir.iterdir()) assert len(key_files) == 1 key_file = key_files[0] with open(key_file) as key_file_handle: key_file_json = json.load(key_file_handle) assert decode_hex(key_file_json["public_key"]) == public_key assert private_key_bytes == eth_keyfile.decode_keyfile_json( key_file_json, some_password)
async def _import_key(logger: logging.Logger, config: Config, arguments: argparse.Namespace) -> None: logger.info("importing private key...") try: key_store = KeyStore( key_store_dir=config.key_store_dir, password_provider=terminal_password_provider, ) with key_store.persistence(should_load_existing_key_pairs=False): key_store.import_private_key(arguments.private_key) except Exception: logger.exception("error importing key")
async def _main( logger: logging.Logger, config: Config, arguments: argparse.Namespace ) -> None: key_store = KeyStore.from_config(config) clock = Clock.from_config(config) beacon_node = BeaconNode.from_config(config) # with key_store.persistence(): async with beacon_node: client = Client(key_store, clock, beacon_node) async with background_trio_service(client): await _wait_for_interrupts() logger.info("received interrupt; shutting down...")
async def test_beacon_node_and_validator_client_can_talk( autojump_clock, clock, beacon_node, eth2_config, chain_config, seconds_per_epoch, sample_bls_key_pairs, client_id, # NOTE: temporarily disable BLS while it standardizes no_op_bls, ): starting_head_slot = beacon_node._chain.get_canonical_head().slot assert starting_head_slot == GENESIS_SLOT async with trio.open_nursery() as nursery: await nursery.start(beacon_node.run) api_client = BeaconNodeClient( chain_config.genesis_time, f"http://{local_host_name}:{beacon_node.validator_api_port}", eth2_config.SECONDS_PER_SLOT, ) async with api_client: # sanity check assert api_client.client_version == client_id key_store = KeyStore(sample_bls_key_pairs) validator = ValidatorClient(key_store, clock, api_client) with trio.move_on_after(seconds_per_epoch): async with background_trio_service(validator): await trio.sleep(seconds_per_epoch * 2) nursery.cancel_scope.cancel() sent_operations_for_broadcast = api_client._broadcast_operations received_operations_for_broadcast = ( beacon_node._validator_api_server.context._broadcast_operations) # temporary until we update to the new API # NOTE: with new API, remove assertion and deletion assert validator._duty_store._store[((1 << 64) - 1, 0)] del validator._duty_store._store[((1 << 64) - 1, 0)] # NOTE: this is the easiest condition to pass while suggesting this is working # As the other parts of the project shore up, we should do stricter testing to ensure # the operations we expect (and that they exist...) get across the gap from # validator to beacon node assert received_operations_for_broadcast.issubset( sent_operations_for_broadcast) assert beacon_node._chain.get_canonical_head().slot > starting_head_slot
def test_key_store_can_persist_key_files(tmp_path, sample_bls_key_pair): config = Config( key_store_constructor=lambda config: KeyStore.from_config(config), root_data_dir=tmp_path, ) some_password = b"password" public_key, private_key = sample_bls_key_pair private_key_bytes = private_key.to_bytes(length=32, byteorder="big") encoded_private_key = private_key_bytes.hex() with config.key_store as key_store: assert not tuple(key_store._location.iterdir()) key_store.import_private_key(encoded_private_key, some_password) key_files = tuple(key_store._location.iterdir()) assert len(key_files) == 1 key_file = key_files[0] with open(key_file) as key_file_handle: key_file_json = json.load(key_file_handle) assert decode_hex(key_file_json["public_key"]) == public_key assert private_key_bytes == eth_keyfile.decode_keyfile_json( key_file_json, some_password)
async def test_client_works( autojump_clock, sample_bls_key_pairs, seconds_per_slot, slots_per_epoch ): """ This test constructs a ``Client`` with enough known inputs to compute an expected set of signatures after running for a given amount of time. The test fails if the expected signatures are not observed as outputs of the client. """ slots_per_epoch = 4 # NOTE: start 2 epochs ahead of genesis to emulate the client # waiting to the time it can start polling the beacon node # and the getting duties in the first epoch in the epoch prior to genesis total_epochs_to_run = 4 epochs_before_genesis_to_start = 2 epochs_after_genesis_to_end = total_epochs_to_run - epochs_before_genesis_to_start # Set genesis so that we aren't aligned with a slot, which could hide some # bugs we will otherwise see... non_aligned_time = trio.current_time() + seconds_per_slot / 3 seconds_per_epoch = seconds_per_slot * slots_per_epoch genesis_time = int( non_aligned_time + epochs_before_genesis_to_start * seconds_per_epoch ) public_key = tuple(sample_bls_key_pairs.keys())[0] key_store = KeyStore(sample_bls_key_pairs) beacon_node = MockBeaconNode( slots_per_epoch, seconds_per_slot, duty_fetcher=_mk_duty_fetcher(public_key, slots_per_epoch, seconds_per_slot), ) clock = Clock( seconds_per_slot, genesis_time, slots_per_epoch, seconds_per_epoch, trio.current_time, ) client = Client(key_store, clock, beacon_node) try: async with background_trio_service(client): await trio.sleep(total_epochs_to_run * seconds_per_epoch) except DaemonTaskExit: # NOTE: there is a race condition in ``async_service`` that will # trigger ``DaemonTaskExit`` when the test would otherwise pass. # See: https://github.com/ethereum/async-service/issues/54 pass fulfilled_duties = tuple( filter( lambda duty: duty.tick_for_execution.epoch < epochs_after_genesis_to_end, beacon_node.given_duties, ) ) assert len(beacon_node.published_signatures) == len(fulfilled_duties) randao_provider = mk_randao_provider(key_store.private_key_for) for duty in fulfilled_duties: if duty.duty_type == DutyType.Attestation: operation = await beacon_node.fetch_attestation( duty.validator_public_key, duty.tick_for_execution.slot, duty.committee_index, ) else: randao_reveal = randao_provider( duty.validator_public_key, duty.tick_for_execution.epoch ) operation = await beacon_node.fetch_block_proposal( duty.tick_for_execution.slot, randao_reveal ) observed_signature = beacon_node.published_signatures[duty] expected_signature = sign(duty, operation, key_store.private_key_for) assert observed_signature == expected_signature