def test_run_lone_federated_default_development_ursula(click_runner): deploy_port = select_test_port() args = ( 'ursula', 'run', # Stat Ursula Command '--debug', # Display log output; Do not attach console '--federated-only', # Operating Mode '--rest-port', deploy_port, # Network Port '--dev', # Run in development mode (ephemeral node) '--dry-run', # Disable twisted reactor in subprocess '--lonely' # Do not load seednodes ) result = yield threads.deferToThread(click_runner.invoke, nucypher_cli, args, catch_exceptions=False, input=INSECURE_DEVELOPMENT_PASSWORD + '\n') time.sleep(Learner._SHORT_LEARNING_DELAY) assert result.exit_code == 0, result.output assert "Running" in result.output assert "127.0.0.1:{}".format(deploy_port) in result.output reserved_ports = (UrsulaConfiguration.DEFAULT_REST_PORT, UrsulaConfiguration.DEFAULT_DEVELOPMENT_REST_PORT) assert deploy_port not in reserved_ports
def test_ursula_init(click_runner, custom_filepath, agency_local_registry, manual_staker, manual_worker, testerchain): deploy_port = select_test_port() init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--worker-address', manual_worker, '--config-root', str(custom_filepath.absolute()), '--provider', TEST_PROVIDER_URI, '--registry-filepath', str(agency_local_registry.filepath.absolute()), '--rest-host', MOCK_IP_ADDRESS, '--rest-port', deploy_port) result = click_runner.invoke(nucypher_cli, init_args, input=FAKE_PASSWORD_CONFIRMED, catch_exceptions=False) assert result.exit_code == 0 # Files and Directories assert custom_filepath.is_dir(), 'Configuration file does not exist' assert (custom_filepath / 'keystore').is_dir(), 'KEYSTORE does not exist' assert (custom_filepath / 'known_nodes').is_dir(), 'known_nodes directory does not exist' custom_config_filepath = custom_filepath / UrsulaConfiguration.generate_filename( ) assert custom_config_filepath.is_file( ), 'Configuration file does not exist' with open(custom_config_filepath, 'r') as config_file: raw_config_data = config_file.read() config_data = json.loads(raw_config_data) assert config_data['provider_uri'] == TEST_PROVIDER_URI assert config_data['worker_address'] == manual_worker assert TEMPORARY_DOMAIN == config_data['domain']
def test_blockchain_porter_cli_run_simple(click_runner, blockchain_ursulas, testerchain, agency_local_registry, blockchain_teacher_uri): porter_run_command = ('porter', 'run', '--dry-run', '--network', TEMPORARY_DOMAIN, '--eth-provider', TEST_ETH_PROVIDER_URI, '--registry-filepath', agency_local_registry.filepath, '--teacher', blockchain_teacher_uri) result = click_runner.invoke(nucypher_cli, porter_run_command, catch_exceptions=False) assert result.exit_code == 0 output = result.output assert f"Network: {TEMPORARY_DOMAIN}" in output assert f"ETH Provider URI: {TEST_ETH_PROVIDER_URI}" in output assert PORTER_RUN_MESSAGE.format(http_scheme="http", http_port=Porter.DEFAULT_PORT) in output # Non-default port non_default_port = select_test_port() porter_run_command = ('porter', 'run', '--dry-run', '--network', TEMPORARY_DOMAIN, '--eth-provider', TEST_ETH_PROVIDER_URI, '--registry-filepath', agency_local_registry.filepath, '--http-port', non_default_port, '--teacher', blockchain_teacher_uri) result = click_runner.invoke(nucypher_cli, porter_run_command, catch_exceptions=False) assert result.exit_code == 0 output = result.output assert f"Network: {TEMPORARY_DOMAIN}" in output assert f"ETH Provider URI: {TEST_ETH_PROVIDER_URI}" in output assert PORTER_RUN_MESSAGE.format(http_scheme="http", http_port=non_default_port) in output
def test_federated_porter_cli_run_simple(click_runner, federated_ursulas, federated_teacher_uri): porter_run_command = ('porter', 'run', '--dry-run', '--federated-only', '--teacher', federated_teacher_uri) result = click_runner.invoke(nucypher_cli, porter_run_command, catch_exceptions=False) assert result.exit_code == 0 output = result.output assert f"Network: {TEMPORARY_DOMAIN}" in output assert PORTER_RUN_MESSAGE.format(http_scheme="http", http_port=Porter.DEFAULT_PORT) in output # Non-default port non_default_port = select_test_port() porter_run_command = ('porter', 'run', '--dry-run', '--federated-only', '--http-port', non_default_port, '--teacher', federated_teacher_uri) result = click_runner.invoke(nucypher_cli, porter_run_command, catch_exceptions=False) assert result.exit_code == 0 output = result.output assert f"Network: {TEMPORARY_DOMAIN}" in output assert PORTER_RUN_MESSAGE.format(http_scheme="http", http_port=non_default_port) in output
def test_initialize_custom_configuration_root(custom_filepath, click_runner): deploy_port = select_test_port() # Use a custom local filepath for configuration init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--federated-only', '--config-root', custom_filepath, '--rest-host', MOCK_IP_ADDRESS, '--rest-port', deploy_port) result = click_runner.invoke(nucypher_cli, init_args, input=FAKE_PASSWORD_CONFIRMED, catch_exceptions=False) assert result.exit_code == 0 # CLI Output assert str(MOCK_CUSTOM_INSTALLATION_PATH) in result.output, "Configuration not in system temporary directory" assert "nucypher ursula run" in result.output, 'Help message is missing suggested command' assert 'IPv4' not in result.output # Files and Directories assert os.path.isdir(custom_filepath), 'Configuration file does not exist' assert os.path.isdir(os.path.join(custom_filepath, 'keyring')), 'Keyring does not exist' assert os.path.isdir(os.path.join(custom_filepath, 'known_nodes')), 'known_nodes directory does not exist' custom_config_filepath = os.path.join(custom_filepath, UrsulaConfiguration.generate_filename()) assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist' # Auth assert 'Enter NuCypher keyring password' in result.output, 'WARNING: User was not prompted for password' assert 'Repeat for confirmation:' in result.output, 'User was not prompted to confirm password'
def test_refund(click_runner, testerchain, agency_local_registry, token_economics): bidder = testerchain.client.accounts[2] worker_address = testerchain.unassigned_accounts[-1] # # WorkLock Staker-Worker # worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry) # Bidder is now STAKER. Bond a worker. staker = Staker(is_me=True, checksum_address=bidder, registry=agency_local_registry) receipt = staker.bond_worker(worker_address=worker_address) assert receipt['status'] == 1 worker = Ursula(is_me=True, registry=agency_local_registry, checksum_address=bidder, worker_address=worker_address, rest_host=MOCK_IP_ADDRESS, rest_port=select_test_port(), db_filepath=tempfile.mkdtemp()) # Ensure there is work to do remaining_work = worklock_agent.get_remaining_work(checksum_address=bidder) assert remaining_work > 0 # Unlock transacting_power = worker._crypto_power.power_ups(TransactingPower) transacting_power.activate(password=INSECURE_DEVELOPMENT_PASSWORD) # Do some work for i in range(3): txhash = worker.commit_to_next_period() testerchain.wait_for_receipt(txhash) assert receipt['status'] == 1 testerchain.time_travel(periods=1) command = ('refund', '--participant-address', bidder, '--registry-filepath', agency_local_registry.filepath, '--provider', TEST_PROVIDER_URI, '--network', TEMPORARY_DOMAIN, '--force') user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n' result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Less work to do... new_remaining_work = worklock_agent.get_remaining_work( checksum_address=bidder) assert new_remaining_work < remaining_work
def test_federated_ursula_learns_via_cli(click_runner, federated_ursulas): # Establish a running Teacher Ursula teacher = list(federated_ursulas)[0] teacher_uri = teacher.seed_node_metadata(as_teacher_uri=True) deploy_port = select_test_port() def run_ursula(): i = start_pytest_ursula_services(ursula=teacher) args = ( 'ursula', 'run', '--debug', # Display log output; Do not attach console '--federated-only', # Operating Mode '--rest-port', deploy_port, # Network Port '--teacher', teacher_uri, '--dev', # Run in development mode (ephemeral node) '--dry-run' # Disable twisted reactor ) return threads.deferToThread(click_runner.invoke, nucypher_cli, args, catch_exceptions=False, input=INSECURE_DEVELOPMENT_PASSWORD + '\n') # Run the Callbacks d = run_ursula() yield d result = d.result assert result.exit_code == 0 assert "Starting services" in result.output assert f"127.0.0.1:{deploy_port}" in result.output reserved_ports = (UrsulaConfiguration.DEFAULT_REST_PORT, UrsulaConfiguration.DEFAULT_DEVELOPMENT_REST_PORT) assert deploy_port not in reserved_ports # Check that CLI Ursula reports that it remembers the teacher and saves the TLS certificate assert teacher.checksum_address in result.output assert f"Saved TLS certificate for {teacher.nickname}" in result.output federated_ursulas.clear()
def test_initialize_custom_configuration_root(click_runner, custom_filepath: Path): deploy_port = select_test_port() # Use a custom local filepath for configuration init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--federated-only', '--config-root', str(custom_filepath.absolute()), '--rest-host', MOCK_IP_ADDRESS, '--rest-port', deploy_port) result = click_runner.invoke(nucypher_cli, init_args, input=FAKE_PASSWORD_CONFIRMED, catch_exceptions=False) assert result.exit_code == 0 # CLI Output assert str( MOCK_CUSTOM_INSTALLATION_PATH ) in result.output, "Configuration not in system temporary directory" assert "nucypher ursula run" in result.output, 'Help message is missing suggested command' assert 'IPv4' not in result.output # Files and Directories assert custom_filepath.is_dir(), 'Configuration file does not exist' assert (custom_filepath / 'keystore').is_dir(), 'KEYSTORE does not exist' # TODO: Only using in-memory node storage for now # assert (custom_filepath / 'known_nodes').is_dir(), 'known_nodes directory does not exist' assert not (custom_filepath / 'known_nodes').is_dir(), 'known_nodes directory does not exist' custom_config_filepath = custom_filepath / UrsulaConfiguration.generate_filename( ) assert custom_config_filepath.is_file( ), 'Configuration file does not exist' # Auth assert COLLECT_NUCYPHER_PASSWORD in result.output, 'WARNING: User was not prompted for password' assert 'Repeat for confirmation:' in result.output, 'User was not prompted to confirm password'
def test_collect_rewards_integration( click_runner, testerchain, agency_local_registry, stakeholder_configuration_file_location, blockchain_alice, blockchain_bob, random_policy_label, manual_staker, manual_worker, token_economics, mock_transacting_power_activation, policy_value, policy_rate): half_stake_time = token_economics.minimum_locked_periods // 2 # Test setup logger = Logger("Test-CLI") # Enter the Teacher's Logger, and current_period = 0 # State the initial period for incrementing staker_address = manual_staker worker_address = manual_worker staker = Staker(is_me=True, checksum_address=staker_address, registry=agency_local_registry) staker.refresh_stakes() # The staker is staking. assert staker.is_staking assert staker.stakes assert staker.worker_address == worker_address ursula_port = select_test_port() ursula = Ursula(is_me=True, checksum_address=staker_address, worker_address=worker_address, registry=agency_local_registry, rest_host='127.0.0.1', rest_port=ursula_port, network_middleware=MockRestMiddleware(), db_filepath=tempfile.mkdtemp(), domain=TEMPORARY_DOMAIN) MOCK_KNOWN_URSULAS_CACHE[ursula_port] = ursula assert ursula.worker_address == worker_address assert ursula.checksum_address == staker_address mock_transacting_power_activation(account=worker_address, password=INSECURE_DEVELOPMENT_PASSWORD) # Make a commitment for half the first stake duration for _ in range(half_stake_time): logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() testerchain.time_travel(periods=1) current_period += 1 # Alice creates a policy and grants Bob access blockchain_alice.selection_buffer = 1 M, N = 1, 1 days = 3 now = testerchain.w3.eth.getBlock('latest').timestamp expiration = maya.MayaDT(now).add(days=days - 1) blockchain_policy = blockchain_alice.grant(bob=blockchain_bob, label=random_policy_label, m=M, n=N, value=policy_value, expiration=expiration, handpicked_ursulas={ursula}) # Ensure that the handpicked Ursula was selected for the policy arrangement = list(blockchain_policy._accepted_arrangements)[0] assert arrangement.ursula == ursula # Bob learns about the new staker and joins the policy blockchain_bob.start_learning_loop() blockchain_bob.remember_node(node=ursula) blockchain_bob.join_policy(random_policy_label, bytes(blockchain_alice.stamp)) # Enrico Encrypts (of course) enrico = Enrico(policy_encrypting_key=blockchain_policy.public_key, network_middleware=MockRestMiddleware()) verifying_key = blockchain_alice.stamp.as_umbral_pubkey() for index in range(half_stake_time - 5): logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() # Encrypt random_data = os.urandom(random.randrange(20, 100)) ciphertext, signature = enrico.encrypt_message(plaintext=random_data) # Decrypt cleartexts = blockchain_bob.retrieve(ciphertext, enrico=enrico, alice_verifying_key=verifying_key, label=random_policy_label) assert random_data == cleartexts[0] # Ursula Staying online and the clock advancing testerchain.time_travel(periods=1) current_period += 1 # Finish the passage of time for the first Stake for _ in range(5): # plus the extended periods from stake division logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() testerchain.time_travel(periods=1) current_period += 1 # # WHERES THE MONEY URSULA?? - Collecting Rewards # # The address the client wants Ursula to send rewards to burner_wallet = testerchain.w3.eth.account.create( INSECURE_DEVELOPMENT_PASSWORD) # The rewards wallet is initially empty, because it is freshly created assert testerchain.client.get_balance(burner_wallet.address) == 0 # Rewards will be unlocked after the # final committed period has passed (+1). logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") testerchain.time_travel(periods=1) current_period += 1 logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") # At least half of the tokens are unlocked (restaking was enabled for some prior periods) assert staker.locked_tokens() >= token_economics.minimum_allowed_locked # Since we are mocking the blockchain connection, manually consume the transacting power of the Staker. mock_transacting_power_activation(account=staker_address, password=INSECURE_DEVELOPMENT_PASSWORD) # Collect Policy Fee collection_args = ('stake', 'collect-reward', '--config-file', stakeholder_configuration_file_location, '--policy-fee', '--no-staking-reward', '--staking-address', staker_address, '--withdraw-address', burner_wallet.address) result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # Policy Fee collected_policy_fee = testerchain.client.get_balance( burner_wallet.address) expected_collection = policy_rate * 30 assert collected_policy_fee == expected_collection # Finish the passage of time... once and for all # Extended periods from stake division for _ in range(9): ursula.commit_to_next_period() current_period += 1 logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") testerchain.time_travel(periods=1) # # Collect Staking Reward # balance_before_collecting = staker.token_agent.get_balance( address=staker_address) collection_args = ('stake', 'collect-reward', '--config-file', stakeholder_configuration_file_location, '--no-policy-fee', '--staking-reward', '--staking-address', staker_address, '--force') result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # The staker has withdrawn her staking rewards assert staker.token_agent.get_balance( address=staker_address) > balance_before_collecting
def test_ursula_and_local_keystore_signer_integration( click_runner, tmp_path, staking_providers, application_economics, mocker, mock_funded_account_password_keystore, testerchain): config_root_path = tmp_path ursula_config_path = config_root_path / UrsulaConfiguration.generate_filename( ) worker_account, password, mock_keystore_path = mock_funded_account_password_keystore # # Operator Steps # # Good signer... mock_signer_uri = f'keystore:{mock_keystore_path}' pre_config_signer = KeystoreSigner.from_signer_uri(uri=mock_signer_uri, testnet=True) assert worker_account.address in pre_config_signer.accounts deploy_port = select_test_port() init_args = ( 'ursula', 'init', '--network', TEMPORARY_DOMAIN, '--payment-network', TEMPORARY_DOMAIN, '--operator-address', worker_account.address, '--config-root', str(config_root_path.absolute()), '--eth-provider', TEST_ETH_PROVIDER_URI, '--payment-provider', TEST_POLYGON_PROVIDER_URI, '--rest-host', MOCK_IP_ADDRESS, '--rest-port', deploy_port, # The bit we are testing for here '--signer', mock_signer_uri) cli_env = { NUCYPHER_ENVVAR_KEYSTORE_PASSWORD: password, NUCYPHER_ENVVAR_OPERATOR_ETH_PASSWORD: password, } result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=cli_env) assert result.exit_code == 0, result.stdout # Inspect the configuration file for the signer URI with open(ursula_config_path, 'r') as config_file: raw_config_data = config_file.read() config_data = json.loads(raw_config_data) assert config_data['signer_uri'] == mock_signer_uri,\ "Keystore URI was not correctly included in configuration file" # Recreate a configuration with the signer URI preserved ursula_config = UrsulaConfiguration.from_configuration_file( ursula_config_path) assert ursula_config.signer_uri == mock_signer_uri # Mock decryption of web3 client keystore mocker.patch.object(Account, 'decrypt', return_value=worker_account.key) ursula_config.keystore.unlock(password=password) # Produce an Ursula with a Keystore signer correctly derived from the signer URI, and don't do anything else! ursula = ursula_config.produce() ursula.signer.unlock_account(account=worker_account.address, password=password) try: # Verify the keystore path is still preserved assert isinstance(ursula.signer, KeystoreSigner) assert isinstance(ursula.signer.path, Path), "Use Path" assert ursula.signer.path.absolute() == mock_keystore_path.absolute() # Show that we can produce the exact same signer as pre-config... assert pre_config_signer.path == ursula.signer.path # ...and that transactions are signed by the keystore signer txhash = ursula.confirm_address() receipt = testerchain.wait_for_receipt(txhash) transaction_data = testerchain.client.w3.eth.getTransaction( receipt['transactionHash']) assert transaction_data['from'] == worker_account.address finally: ursula.stop()
def test_collect_rewards_integration(click_runner, testerchain, agency_local_registry, stakeholder_configuration_file_location, blockchain_alice, blockchain_bob, random_policy_label, beneficiary, preallocation_escrow_agent, mock_allocation_registry, manual_worker, token_economics, mock_transacting_power_activation, stake_value, policy_value, policy_rate): # Disable re-staking restake_args = ('stake', 'restake', '--disable', '--config-file', stakeholder_configuration_file_location, '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, restake_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 half_stake_time = token_economics.minimum_locked_periods // 2 # Test setup logger = Logger("Test-CLI") # Enter the Teacher's Logger, and current_period = 0 # State the initial period for incrementing staker_address = preallocation_escrow_agent.principal_contract.address worker_address = manual_worker # The staker is staking. stakes = StakeList(registry=agency_local_registry, checksum_address=staker_address) stakes.refresh() assert stakes staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=agency_local_registry) assert worker_address == staking_agent.get_worker_from_staker(staker_address=staker_address) ursula_port = select_test_port() ursula = Ursula(is_me=True, checksum_address=staker_address, worker_address=worker_address, registry=agency_local_registry, rest_host='127.0.0.1', rest_port=ursula_port, start_working_now=False, network_middleware=MockRestMiddleware()) MOCK_KNOWN_URSULAS_CACHE[ursula_port] = ursula assert ursula.worker_address == worker_address assert ursula.checksum_address == staker_address mock_transacting_power_activation(account=worker_address, password=INSECURE_DEVELOPMENT_PASSWORD) # Make a commitment for half the first stake duration for _ in range(half_stake_time): logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() testerchain.time_travel(periods=1) current_period += 1 # Alice creates a policy and grants Bob access blockchain_alice.selection_buffer = 1 M, N = 1, 1 days = 3 now = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp expiration = maya.MayaDT(now).add(days=days-1) blockchain_policy = blockchain_alice.grant(bob=blockchain_bob, label=random_policy_label, m=M, n=N, value=policy_value, expiration=expiration, handpicked_ursulas={ursula}) # Ensure that the handpicked Ursula was selected for the policy arrangement = list(blockchain_policy._accepted_arrangements)[0] assert arrangement.ursula == ursula # Bob learns about the new staker and joins the policy blockchain_bob.start_learning_loop() blockchain_bob.remember_node(node=ursula) blockchain_bob.join_policy(random_policy_label, bytes(blockchain_alice.stamp)) # Enrico Encrypts (of course) enrico = Enrico(policy_encrypting_key=blockchain_policy.public_key, network_middleware=MockRestMiddleware()) verifying_key = blockchain_alice.stamp.as_umbral_pubkey() for index in range(half_stake_time - 5): logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() # Encrypt random_data = os.urandom(random.randrange(20, 100)) message_kit, signature = enrico.encrypt_message(message=random_data) # Decrypt cleartexts = blockchain_bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=verifying_key, label=random_policy_label) assert random_data == cleartexts[0] # Ursula Staying online and the clock advancing testerchain.time_travel(periods=1) current_period += 1 # Finish the passage of time for _ in range(5 - 1): # minus 1 because the first period was already committed to in test_ursula_run logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() current_period += 1 testerchain.time_travel(periods=1) # # WHERES THE MONEY URSULA?? - Collecting Rewards # balance = testerchain.client.get_balance(beneficiary) # Rewards will be unlocked after the # final committed period has passed (+1). logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") testerchain.time_travel(periods=1) current_period += 1 logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") # Since we are mocking the blockchain connection, manually consume the transacting power of the Beneficiary. mock_transacting_power_activation(account=beneficiary, password=INSECURE_DEVELOPMENT_PASSWORD) # Collect Policy Fee collection_args = ('stake', 'collect-reward', '--config-file', stakeholder_configuration_file_location, '--policy-fee', '--no-staking-reward', '--withdraw-address', beneficiary, '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # Policy Fee collected_policy_fee = testerchain.client.get_balance(beneficiary) assert collected_policy_fee > balance # # Collect Staking Reward # token_agent = ContractAgency.get_agent(agent_class=NucypherTokenAgent, registry=agency_local_registry) balance_before_collecting = token_agent.get_balance(address=staker_address) collection_args = ('stake', 'collect-reward', '--config-file', stakeholder_configuration_file_location, '--no-policy-fee', '--staking-reward', '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # The beneficiary has withdrawn her staking rewards, which are now in the staking contract assert token_agent.get_balance(address=staker_address) >= balance_before_collecting
def test_collect_rewards_integration( click_runner, testerchain, agency_local_registry, stakeholder_configuration_file_location, blockchain_alice, blockchain_bob, random_policy_label, manual_staker, manual_worker, token_economics, policy_value): half_stake_time = 2 * token_economics.minimum_locked_periods # Test setup logger = Logger("Test-CLI") # Enter the Teacher's Logger, and current_period = 0 # State the initial period for incrementing staker_address = manual_staker worker_address = manual_worker staker = Staker(domain=TEMPORARY_DOMAIN, checksum_address=staker_address, registry=agency_local_registry) staker.refresh_stakes() # The staker is staking. assert staker.is_staking assert staker.stakes assert staker.worker_address == worker_address ursula_port = select_test_port() ursula = Ursula(is_me=True, checksum_address=staker_address, signer=Web3Signer(testerchain.client), worker_address=worker_address, registry=agency_local_registry, rest_host=LOOPBACK_ADDRESS, rest_port=ursula_port, provider_uri=TEST_PROVIDER_URI, network_middleware=MockRestMiddleware(), db_filepath=tempfile.mkdtemp(), domain=TEMPORARY_DOMAIN) MOCK_KNOWN_URSULAS_CACHE[ursula_port] = ursula assert ursula.worker_address == worker_address assert ursula.checksum_address == staker_address # Make a commitment for half the first stake duration testerchain.time_travel(periods=1) for _ in range(half_stake_time): logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() testerchain.time_travel(periods=1) current_period += 1 # Alice creates a policy and grants Bob access blockchain_alice.selection_buffer = 1 threshold, shares = 1, 1 duration_in_periods = 3 days = (duration_in_periods - 1) * (token_economics.hours_per_period // 24) now = testerchain.w3.eth.getBlock('latest').timestamp expiration = maya.MayaDT(now).add(days=days) blockchain_policy = blockchain_alice.grant(bob=blockchain_bob, label=random_policy_label, threshold=threshold, shares=shares, value=policy_value, expiration=expiration, ursulas={ursula}) # Ensure that the handpicked Ursula was selected for the policy treasure_map = blockchain_bob._decrypt_treasure_map( blockchain_policy.treasure_map, blockchain_policy.publisher_verifying_key) assert ursula.canonical_address in treasure_map.destinations # Bob learns about the new staker and joins the policy blockchain_bob.start_learning_loop() blockchain_bob.remember_node(node=ursula) # Enrico Encrypts (of course) enrico = Enrico(policy_encrypting_key=blockchain_policy.public_key, network_middleware=MockRestMiddleware()) verifying_key = blockchain_alice.stamp.as_umbral_pubkey() for index in range(half_stake_time - 5): logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() # Encrypt random_data = os.urandom(random.randrange(20, 100)) message_kit = enrico.encrypt_message(plaintext=random_data) # Decrypt cleartexts = blockchain_bob.retrieve_and_decrypt( [message_kit], alice_verifying_key=verifying_key, encrypted_treasure_map=blockchain_policy.treasure_map) assert random_data == cleartexts[0] # Ursula Staying online and the clock advancing testerchain.time_travel(periods=1) current_period += 1 # Finish the passage of time for the first Stake for _ in range(5): # plus the extended periods from stake division logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.commit_to_next_period() testerchain.time_travel(periods=1) current_period += 1 # # WHERES THE MONEY URSULA?? - Collecting Rewards # # The address the client wants Ursula to send rewards to burner_wallet = testerchain.w3.eth.account.create( INSECURE_DEVELOPMENT_PASSWORD) # The rewards wallet is initially empty, because it is freshly created assert testerchain.client.get_balance(burner_wallet.address) == 0 # Rewards will be unlocked after the # final committed period has passed (+1). logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") testerchain.time_travel(periods=1) current_period += 1 logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") # At least half of the tokens are unlocked (restaking was enabled for some prior periods) assert staker.locked_tokens() >= token_economics.minimum_allowed_locked # Collect Policy Fee collection_args = ('stake', 'rewards', 'withdraw', '--config-file', str(stakeholder_configuration_file_location.absolute()), '--fees', '--no-tokens', '--staking-address', staker_address, '--withdraw-address', burner_wallet.address) result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # Policy Fee collected_policy_fee = testerchain.client.get_balance( burner_wallet.address) expected_collection = policy_value assert collected_policy_fee == expected_collection # Finish the passage of time... once and for all # Extended periods from stake division for _ in range(9): ursula.commit_to_next_period() current_period += 1 logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") testerchain.time_travel(periods=1) # # Collect Staking Reward # balance_before_collecting = staker.token_agent.get_balance( address=staker_address) collection_args = ('stake', 'rewards', 'withdraw', '--config-file', str(stakeholder_configuration_file_location.absolute()), '--no-fees', '--tokens', '--staking-address', staker_address, '--force') result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # The staker has withdrawn her staking rewards assert staker.token_agent.get_balance( address=staker_address) > balance_before_collecting
def test_ursula_init_with_local_keystore_signer( click_runner, temp_dir_path, mocker, mock_testerchain, mock_account_password_keystore): custom_filepath = temp_dir_path custom_config_filepath = temp_dir_path / UrsulaConfiguration.generate_filename( ) worker_account, password, mock_keystore_path = mock_account_password_keystore mock_signer_uri = f'keystore://{mock_keystore_path}' # Good signer... pre_config_signer = KeystoreSigner.from_signer_uri(uri=mock_signer_uri, testnet=True) deploy_port = select_test_port() init_args = ( 'ursula', 'init', # Layer 1 '--network', TEMPORARY_DOMAIN, '--eth-provider', mock_testerchain.eth_provider_uri, # Layer 2 '--payment-network', TEMPORARY_DOMAIN, '--payment-provider', mock_testerchain.eth_provider_uri, '--rest-host', MOCK_IP_ADDRESS, '--rest-port', deploy_port, '--operator-address', worker_account.address, '--config-root', str(custom_filepath.absolute()), # The bit we are testing here '--signer', mock_signer_uri) cli_env = { NUCYPHER_ENVVAR_KEYSTORE_PASSWORD: password, NUCYPHER_ENVVAR_OPERATOR_ETH_PASSWORD: password, } result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=cli_env) assert result.exit_code == 0, result.stdout # Inspect the configuration file for the signer URI with open(custom_config_filepath, 'r') as config_file: raw_config_data = config_file.read() config_data = json.loads(raw_config_data) assert config_data['signer_uri'] == mock_signer_uri,\ "Keystore URI was not correctly included in configuration file" # Recreate a configuration with the signer URI preserved ursula_config = UrsulaConfiguration.from_configuration_file( custom_config_filepath, config_root=custom_filepath) assert ursula_config.signer_uri == mock_signer_uri # Mock decryption of web3 client keystore mocker.patch.object(Account, 'decrypt', return_value=worker_account.key) ursula_config.keystore.unlock(password=password) # Produce an ursula with a Keystore signer correctly derived from the signer URI, and don't do anything else! ursula = ursula_config.produce() ursula.signer.unlock_account(account=worker_account.address, password=password) # Verify the keystore path is still preserved assert isinstance(ursula.signer, KeystoreSigner) assert isinstance(ursula.signer.path, Path), "Use pathlib.Path" assert ursula.signer.path.absolute() == mock_keystore_path.absolute() # Show that we can produce the exact same signer as pre-config... assert pre_config_signer.path == ursula.signer.path ursula.stop()
def test_ursula_and_local_keystore_signer_integration( click_runner, tmp_path, manual_staker, stake_value, token_economics, mocker, mock_funded_account_password_keystore, testerchain): config_root_path = tmp_path ursula_config_path = config_root_path / UrsulaConfiguration.generate_filename( ) stakeholder_config_path = config_root_path / StakeHolderConfiguration.generate_filename( ) worker_account, password, mock_keystore_path = mock_funded_account_password_keystore # # Stakeholder Steps # init_args = ('stake', 'init-stakeholder', '--config-root', config_root_path, '--provider', TEST_PROVIDER_URI, '--network', TEMPORARY_DOMAIN) click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False) stake_args = ('stake', 'create', '--config-file', stakeholder_config_path, '--staking-address', manual_staker, '--value', stake_value.to_tokens(), '--lock-periods', token_economics.minimum_locked_periods, '--force') # TODO: Is This test is writing to the default system directory and ignoring updates to the passed filepath? user_input = f'0\n{password}\nY\n' click_runner.invoke(nucypher_cli, stake_args, input=user_input, catch_exceptions=False) init_args = ('stake', 'bond-worker', '--config-file', stakeholder_config_path, '--staking-address', manual_staker, '--worker-address', worker_account.address, '--force') user_input = password click_runner.invoke(nucypher_cli, init_args, input=user_input, catch_exceptions=False) # # Worker Steps # # Good signer... mock_signer_uri = f'keystore:{mock_keystore_path}' pre_config_signer = KeystoreSigner.from_signer_uri(uri=mock_signer_uri, testnet=True) assert worker_account.address in pre_config_signer.accounts deploy_port = select_test_port() init_args = ( 'ursula', 'init', '--network', TEMPORARY_DOMAIN, '--worker-address', worker_account.address, '--config-root', config_root_path, '--provider', TEST_PROVIDER_URI, '--rest-host', MOCK_IP_ADDRESS, '--rest-port', deploy_port, # The bit we are testing for here '--signer', mock_signer_uri) cli_env = { NUCYPHER_ENVVAR_KEYRING_PASSWORD: password, NUCYPHER_ENVVAR_WORKER_ETH_PASSWORD: password, } result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=cli_env) assert result.exit_code == 0, result.stdout # Inspect the configuration file for the signer URI with open(ursula_config_path, 'r') as config_file: raw_config_data = config_file.read() config_data = json.loads(raw_config_data) assert config_data['signer_uri'] == mock_signer_uri,\ "Keystore URI was not correctly included in configuration file" # Recreate a configuration with the signer URI preserved ursula_config = UrsulaConfiguration.from_configuration_file( ursula_config_path) assert ursula_config.signer_uri == mock_signer_uri # Mock decryption of web3 client keyring mocker.patch.object(Account, 'decrypt', return_value=worker_account.privateKey) ursula_config.attach_keyring(checksum_address=worker_account.address) ursula_config.keyring.unlock(password=password) # Produce an Ursula with a Keystore signer correctly derived from the signer URI, and don't do anything else! mocker.patch.object(StakeList, 'refresh', autospec=True) ursula = ursula_config.produce(client_password=password, commit_now=False, block_until_ready=False) try: # Verify the keystore path is still preserved assert isinstance(ursula.signer, KeystoreSigner) assert isinstance(ursula.signer.path, str), "Use str" assert ursula.signer.path == str(mock_keystore_path) # Show that we can produce the exact same signer as pre-config... assert pre_config_signer.path == ursula.signer.path # ...and that transactions are signed by the keystore signer txhash = ursula.commit_to_next_period() receipt = testerchain.wait_for_receipt(txhash) transaction_data = testerchain.client.w3.eth.getTransaction( receipt['transactionHash']) assert transaction_data['from'] == worker_account.address finally: ursula.stop()
def test_ursula_init_with_local_keystore_signer(click_runner, tmp_path, mocker, mock_testerchain, mock_account_password_keystore, test_registry_source_manager): custom_filepath = tmp_path custom_config_filepath = tmp_path / UrsulaConfiguration.generate_filename() worker_account, password, mock_keystore_path = mock_account_password_keystore mock_signer_uri = f'keystore:{mock_keystore_path}' # Good signer... pre_config_signer = KeystoreSigner.from_signer_uri(uri=mock_signer_uri) deploy_port = select_test_port() init_args = ( 'ursula', 'init', '--network', TEMPORARY_DOMAIN, '--worker-address', worker_account.address, '--config-root', custom_filepath, '--provider', mock_testerchain.provider_uri, '--rest-host', MOCK_IP_ADDRESS, '--rest-port', deploy_port, # The bit we are testing here '--signer', mock_signer_uri) cli_env = { NUCYPHER_ENVVAR_KEYRING_PASSWORD: password, NUCYPHER_ENVVAR_WORKER_ETH_PASSWORD: password, } result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=cli_env) assert result.exit_code == 0, result.stdout # Inspect the configuration file for the signer URI with open(custom_config_filepath, 'r') as config_file: raw_config_data = config_file.read() config_data = json.loads(raw_config_data) assert config_data['signer_uri'] == mock_signer_uri,\ "Keystore URI was not correctly included in configuration file" # Recreate a configuration with the signer URI preserved ursula_config = UrsulaConfiguration.from_configuration_file( custom_config_filepath) assert ursula_config.signer_uri == mock_signer_uri # Mock decryption of web3 client keyring mocker.patch.object(Account, 'decrypt', return_value=worker_account.privateKey) ursula_config.attach_keyring(checksum_address=worker_account.address) ursula_config.keyring.unlock(password=password) # Produce an ursula with a Keystore signer correctly derived from the signer URI, and dont do anything else! mocker.patch.object(StakeList, 'refresh', autospec=True) ursula = ursula_config.produce(client_password=password, block_until_ready=False) # Verify the keystore path is still preserved assert isinstance(ursula.signer, KeystoreSigner) assert isinstance(ursula.signer.path, str), "Use str" assert ursula.signer.path == str(mock_keystore_path) # Show that we can produce the exact same signer as pre-config... assert pre_config_signer.path == ursula.signer.path ursula.stop()
# you will not perform any re-encryptions, and you will not get paid. # It might be (but might not be) useful for determining whether you have # the proper dependencies and configuration to run an actual mining node. from click.testing import CliRunner from nucypher.cli.main import nucypher_cli from nucypher.exceptions import DevelopmentInstallationRequired from nucypher.utilities.networking import LOOPBACK_ADDRESS try: from tests.utils.ursula import select_test_port except ImportError: raise DevelopmentInstallationRequired(importable_name='tests.utils.ursula.select_test_port') click_runner = CliRunner() DEMO_NODE_PORT = select_test_port() DEMO_FLEET_STARTING_PORT = 11500 args = ['ursula', 'run', '--debug', '--federated-only', '--teacher', f'https://{LOOPBACK_ADDRESS}:{DEMO_FLEET_STARTING_PORT}', '--rest-port', DEMO_NODE_PORT, '--dev' ] nucypher_cli.main(args=args, prog_name="nucypher-cli")