def process_middleware(mock_networking) -> tuple: ################# # MUST NOT RAISE! ################# try: from tests.utils.middleware import MockRestMiddleware except ImportError: # It's okay to not crash here despite not having the tests package available. logger = Logger("CLI-Middleware-Optional-Handler") logger.info('--mock-networking flag is unavailable without dev install.') if mock_networking: middleware = MockRestMiddleware() else: from nucypher.network.middleware import RestMiddleware middleware = RestMiddleware() return 'middleware', middleware
def enacted_blockchain_policy(idle_blockchain_policy, blockchain_ursulas): # Alice has a policy in mind and knows of enough qualified Ursulas; she crafts an offer for them. # value and expiration were set when creating idle_blockchain_policy already # cannot set them again # deposit = NON_PAYMENT(b"0000000") # contract_end_datetime = maya.now() + datetime.timedelta(days=5) network_middleware = MockRestMiddleware() idle_blockchain_policy.make_arrangements( network_middleware, handpicked_ursulas=list(blockchain_ursulas)) idle_blockchain_policy.enact( network_middleware ) # REST call happens here, as does population of TreasureMap. return idle_blockchain_policy
def test_alice_sets_treasure_map(enacted_federated_policy): """ Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and ...... TODO """ enacted_federated_policy.publish_treasure_map( network_middleware=MockRestMiddleware()) treasure_map_id = enacted_federated_policy.treasure_map.public_id() found = 0 for node in enacted_federated_policy.bob.matching_nodes_among( enacted_federated_policy.alice.known_nodes): with node.datastore.describe(DatastoreTreasureMap, treasure_map_id) as treasure_map_on_node: assert FederatedTreasureMap.from_bytes( treasure_map_on_node.treasure_map ) == enacted_federated_policy.treasure_map found += 1 assert found
def test_vladimir_illegal_interface_key_does_not_propagate(blockchain_ursulas): """ Although Ursulas propagate each other's interface information, as demonstrated above, they do not propagate interface information for Vladimir. Specifically, if Vladimir tries to perform the most obvious imitation attack - propagating his own wallet address along with Ursula's information - the validity check will catch it and Ursula will refuse to propagate it and also record Vladimir's details. """ ursulas = list(blockchain_ursulas) ursula_whom_vladimir_will_imitate, other_ursula = ursulas[0], ursulas[1] # Vladimir sees Ursula on the network and tries to use her public information. vladimir = Vladimir.from_target_ursula(ursula_whom_vladimir_will_imitate) # This Ursula is totally legit... ursula_whom_vladimir_will_imitate.verify_node(MockRestMiddleware(), accept_federated_only=True) learning_callers = [] crosstown_traffic.decorator = crosstownTaskListDecoratorFactory(learning_callers) vladimir.network_middleware.propagate_shitty_interface_id(other_ursula, bytes(vladimir)) # So far, Ursula hasn't noticed any Vladimirs. assert other_ursula.suspicious_activities_witnessed['vladimirs'] == [] # ...but now, Ursula will now try to learn about Vladimir on a different thread. # We only passed one node (Vladimir)... learn_about_vladimir = learning_callers.pop() # ...so there was only one learning caller in the queue (now none since we popped it just now). assert len(learning_callers) == 0 # OK, so cool, let's see what happens when Ursula tries to learn about Vlad. learn_about_vladimir() # And indeed, Ursula noticed the situation. # She didn't record Vladimir's address. assert vladimir not in other_ursula.known_nodes # But she *did* record the actual Ursula's address. assert ursula_whom_vladimir_will_imitate in other_ursula.known_nodes # Furthermore, she properly marked Vladimir as suspicious. assert vladimir in other_ursula.suspicious_activities_witnessed['vladimirs']
def test_alice_sets_treasure_map_decentralized(enacted_blockchain_policy): """ Same as test_alice_sets_treasure_map except with a blockchain policy. """ enacted_blockchain_policy.publish_treasure_map( network_middleware=MockRestMiddleware()) treasure_map_hrac = enacted_blockchain_policy.treasure_map._hrac[:16].hex() found = 0 for node in enacted_blockchain_policy.bob.matching_nodes_among( enacted_blockchain_policy.alice.known_nodes): with node.datastore.describe( DatastoreTreasureMap, treasure_map_hrac) as treasure_map_on_node: assert DecentralizedTreasureMap.from_bytes( treasure_map_on_node.treasure_map ) == enacted_blockchain_policy.treasure_map found += 1 assert found
def test_character_card(character_class): character = character_class(federated_only=True, start_learning_now=False, network_middleware=MockRestMiddleware()) character_card = character.get_card() same_card = Card.from_character(character) assert character_card == same_card with pytest.raises(TypeError): # only cards can be compared to other cards _ = character_card == same_card.verifying_key # Bob's Keys assert character_card.verifying_key == character.public_keys(SigningPower) assert character_card.encrypting_key == character.public_keys( DecryptingPower) # Card Serialization # bytes card_bytes = bytes(character_card) assert Card.from_bytes(card_bytes) == character_card == same_card # hex hex_bob = character_card.to_hex() assert Card.from_hex(hex_bob) == character_card == same_card # base64 base64_bob = character_card.to_base64() assert Card.from_base64(base64_bob) == character_card == same_card # qr code echo character_card.to_qr_code() # TODO: Examine system output here? # nicknames original_checksum = character_card.id nickname = 'Wilson' character_card.set_nickname(nickname) restored = Card.from_bytes(bytes(character_card)) restored_checksum = restored.id assert restored.nickname == nickname assert original_checksum == restored_checksum == same_card.id
def test_vladimir_illegal_interface_key_does_not_propagate(blockchain_ursulas): """ Although Ursulas propagate each other's interface information, as demonstrated above, they do not propagate interface information for Vladimir. Specifically, if Vladimir tries to perform the most obvious imitation attack - propagating his own wallet address along with Ursula's information - the validity check will catch it and Ursula will refuse to propagate it and also record Vladimir's details. """ ursulas = list(blockchain_ursulas) ursula_whom_vladimir_will_imitate, other_ursula = ursulas[0], ursulas[1] # Vladimir sees Ursula on the network and tries to use her public information. vladimir = Vladimir.from_target_ursula(ursula_whom_vladimir_will_imitate) # This Ursula is totally legit... ursula_whom_vladimir_will_imitate.verify_node(MockRestMiddleware()) vladimir.network_middleware.propagate_shitty_interface_id( other_ursula, bytes(vladimir)) # So far, Ursula hasn't noticed any Vladimirs. assert other_ursula.suspicious_activities_witnessed['vladimirs'] == [] # ...but now, Ursula will now try to learn about Vladimir on a different thread. other_ursula.block_until_specific_nodes_are_known( [vladimir.checksum_address]) vladimir_as_learned = other_ursula.known_nodes[vladimir.checksum_address] # OK, so cool, let's see what happens when Ursula tries to learn with Vlad as the teacher. other_ursula._current_teacher_node = vladimir_as_learned result = other_ursula.learn_from_teacher_node() # Indeed, Ursula noticed that something was up. vladimir in other_ursula.suspicious_activities_witnessed['vladimirs'] # She marked him as Invalid... vladimir in other_ursula.known_nodes._marked[vladimir.InvalidNode] # ...and booted him from known_nodes vladimir not in other_ursula.known_nodes
def test_new_federated_ursula_announces_herself(ursula_federated_test_config): ursula_in_a_house, ursula_with_a_mouse = make_federated_ursulas( ursula_config=ursula_federated_test_config, quantity=2, know_each_other=False, network_middleware=MockRestMiddleware()) # Neither Ursula knows about the other. assert ursula_in_a_house.known_nodes == ursula_with_a_mouse.known_nodes ursula_in_a_house.remember_node(ursula_with_a_mouse) # OK, now, ursula_in_a_house knows about ursula_with_a_mouse, but not vice-versa. assert ursula_with_a_mouse in ursula_in_a_house.known_nodes assert ursula_in_a_house not in ursula_with_a_mouse.known_nodes # But as ursula_in_a_house learns, she'll announce herself to ursula_with_a_mouse. ursula_in_a_house.learn_from_teacher_node() assert ursula_with_a_mouse in ursula_in_a_house.known_nodes assert ursula_in_a_house in ursula_with_a_mouse.known_nodes
def test_node_posts_future_version(federated_ursulas): ursula = list(federated_ursulas)[0] middleware = MockRestMiddleware() warnings = [] def warning_trapper(event): if event['log_level'] == LogLevel.warn: warnings.append(event) globalLogPublisher.addObserver(warning_trapper) crazy_node = b"invalid-node" middleware.get_nodes_via_rest(node=ursula, announce_nodes=(crazy_node, )) assert len(warnings) == 1 future_node = list(federated_ursulas)[1] future_node.TEACHER_VERSION = future_node.TEACHER_VERSION + 10 future_node_bytes = bytes(future_node) middleware.get_nodes_via_rest(node=ursula, announce_nodes=(future_node_bytes, )) assert len(warnings) == 2
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_invalid_workers_tolerance(testerchain, test_registry, blockchain_ursulas, agency, idle_staker, token_economics, ursula_decentralized_test_config ): # # Setup # lonely_blockchain_learner, blockchain_teacher, unsigned, *the_others = list(blockchain_ursulas) _, staking_agent, _ = agency warnings = [] def warning_trapper(event): if event['log_level'] == LogLevel.warn: warnings.append(event) # We start with an "idle_staker" (i.e., no tokens in StakingEscrow) assert 0 == staking_agent.owned_tokens(idle_staker.checksum_address) # Now let's create an active worker for this staker. # First, stake something (e.g. the bare minimum) amount = token_economics.minimum_allowed_locked periods = token_economics.minimum_locked_periods # Mock Powerup consumption (Staker) testerchain.transacting_power = TransactingPower(account=idle_staker.checksum_address) idle_staker.initialize_stake(amount=amount, lock_periods=periods) # Stake starts next period (or else signature validation will fail) testerchain.time_travel(periods=1) idle_staker.stake_tracker.refresh() # We create an active worker node for this staker worker = make_ursula_for_staker(staker=idle_staker, worker_address=testerchain.unassigned_accounts[-1], ursula_config=ursula_decentralized_test_config, blockchain=testerchain, commit_to_next_period=True, ursulas_to_learn_about=None) # Since we made a commitment, we need to advance one period testerchain.time_travel(periods=1) # The worker is valid and can be verified (even with the force option) worker.verify_node(force=True, network_middleware=MockRestMiddleware(), certificate_filepath="quietorl") # In particular, we know that it's bonded to a staker who is really staking. assert worker._worker_is_bonded_to_staker(registry=test_registry) assert worker._staker_is_really_staking(registry=test_registry) # OK. Now we learn about this worker. lonely_blockchain_learner.remember_node(worker) # The worker already committed one period before. Let's commit to the remaining 29. for i in range(29): worker.commit_to_next_period() testerchain.time_travel(periods=1) # The stake period has ended, and the staker wants her tokens back ("when lambo?"). # She withdraws up to the last penny (well, last nunit, actually). # Mock Powerup consumption (Staker) testerchain.transacting_power = TransactingPower(account=idle_staker.checksum_address) idle_staker.mint() testerchain.time_travel(periods=1) i_want_it_all = staking_agent.owned_tokens(idle_staker.checksum_address) idle_staker.withdraw(i_want_it_all) # OK...so...the staker is not staking anymore ... assert 0 == staking_agent.owned_tokens(idle_staker.checksum_address) # ... but the worker node still is "verified" (since we're not forcing on-chain verification) worker.verify_node(network_middleware=MockRestMiddleware(), certificate_filepath="quietorl") # If we force, on-chain verification, the worker is of course not verified with pytest.raises(worker.NotStaking): worker.verify_node(force=True, network_middleware=MockRestMiddleware(), certificate_filepath="quietorl") # Let's learn from this invalid node lonely_blockchain_learner._current_teacher_node = worker globalLogPublisher.addObserver(warning_trapper) lonely_blockchain_learner.learn_from_teacher_node() # lonely_blockchain_learner.remember_node(worker) # The same problem occurs if we directly try to remember this node globalLogPublisher.removeObserver(warning_trapper) # TODO: What should we really check here? (#1075) assert len(warnings) == 1 warning = warnings[-1]['log_format'] assert str(worker) in warning assert "no active stakes" in warning # TODO: Cleanup logging templates assert worker not in lonely_blockchain_learner.known_nodes
def test_alices_powers_are_persistent(federated_ursulas, tmpdir): # Create a non-learning AliceConfiguration alice_config = AliceConfiguration( config_root=os.path.join(tmpdir, 'nucypher-custom-alice-config'), network_middleware=MockRestMiddleware(), domain=TEMPORARY_DOMAIN, start_learning_now=False, federated_only=True, save_metadata=False, reload_metadata=False ) # Generate keys and write them the disk alice_config.initialize(password=INSECURE_DEVELOPMENT_PASSWORD) # Unlock Alice's keyring alice_config.keyring.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) # Produce an Alice alice = alice_config() # or alice_config.produce() # Save Alice's node configuration file to disk for later use alice_config_file = alice_config.to_configuration_file() # Let's save Alice's public keys too to check they are correctly restored later alices_verifying_key = alice.public_keys(SigningPower) alices_receiving_key = alice.public_keys(DecryptingPower) # Next, let's fix a label for all the policies we will create later. label = b"this_is_the_path_to_which_access_is_being_granted" # Even before creating the policies, we can know what will be its public key. # This can be used by Enrico (i.e., a Data Source) to encrypt messages # before Alice grants access to Bobs. policy_pubkey = alice.get_policy_encrypting_key_from_label(label) # Now, let's create a policy for some Bob. m, n = 3, 4 policy_end_datetime = maya.now() + datetime.timedelta(days=5) bob = Bob(federated_only=True, start_learning_now=False, network_middleware=MockRestMiddleware()) bob_policy = alice.grant(bob, label, m=m, n=n, expiration=policy_end_datetime) assert policy_pubkey == bob_policy.public_key # ... and Alice and her configuration disappear. alice.disenchant() del alice del alice_config ################################### # Some time passes. # # ... # # (jmyles plays the Song of Time) # # ... # # Alice appears again. # ################################### # A new Alice is restored from the configuration file new_alice_config = AliceConfiguration.from_configuration_file( filepath=alice_config_file, network_middleware=MockRestMiddleware(), known_nodes=federated_ursulas, start_learning_now=False, ) # Alice unlocks her restored keyring from disk new_alice_config.attach_keyring() new_alice_config.keyring.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) new_alice = new_alice_config() # First, we check that her public keys are correctly restored assert alices_verifying_key == new_alice.public_keys(SigningPower) assert alices_receiving_key == new_alice.public_keys(DecryptingPower) # Bob's eldest brother, Roberto, appears too roberto = Bob(federated_only=True, start_learning_now=False, network_middleware=MockRestMiddleware()) # Alice creates a new policy for Roberto. Note how all the parameters # except for the label (i.e., recipient, m, n, policy_end) are different # from previous policy m, n = 2, 5 policy_end_datetime = maya.now() + datetime.timedelta(days=3) roberto_policy = new_alice.grant(roberto, label, m=m, n=n, expiration=policy_end_datetime) # Both policies must share the same public key (i.e., the policy public key) assert policy_pubkey == roberto_policy.public_key new_alice.disenchant()
def test_bob_joins_policy_and_retrieves(federated_alice, federated_ursulas, certificates_tempdir, ): # Let's partition Ursulas in two parts a_couple_of_ursulas = list(federated_ursulas)[:2] rest_of_ursulas = list(federated_ursulas)[2:] # Bob becomes bob = Bob(federated_only=True, domain=TEMPORARY_DOMAIN, start_learning_now=True, network_middleware=MockRestMiddleware(), abort_on_learning_error=True, known_nodes=a_couple_of_ursulas, ) # Bob has only connected to - at most - 2 nodes. assert sum(node.verified_node for node in bob.known_nodes) <= 2 # Alice creates a policy granting access to Bob # Just for fun, let's assume she distributes KFrags among Ursulas unknown to Bob n = NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK - 2 label = b'label://' + os.urandom(32) contract_end_datetime = maya.now() + datetime.timedelta(days=5) policy = federated_alice.grant(bob=bob, label=label, m=3, n=n, expiration=contract_end_datetime, handpicked_ursulas=set(rest_of_ursulas), ) assert bob == policy.bob assert label == policy.label try: # Now, Bob joins the policy bob.join_policy(label=label, alice_verifying_key=federated_alice.stamp, block=True) except policy.treasure_map.NowhereToBeFound: maps = [] for ursula in federated_ursulas: for map in ursula.treasure_maps.values(): maps.append(map) if policy.treasure_map in maps: # This is a nice place to put a breakpoint to examine Bob's failure to join a policy. bob.join_policy(label=label, alice_verifying_key=federated_alice.stamp, block=True) pytest.fail(f"Bob didn't find map {policy.treasure_map} even though it was available. Come on, Bob.") else: pytest.fail(f"It seems that Alice didn't publish {policy.treasure_map}. Come on, Alice.") # In the end, Bob should know all the Ursulas assert len(bob.known_nodes) == len(federated_ursulas) # Enrico becomes enrico = Enrico(policy_encrypting_key=policy.public_key) plaintext = b"What's your approach? Mississippis or what?" message_kit, _signature = enrico.encrypt_message(plaintext) alices_verifying_key = federated_alice.stamp.as_umbral_pubkey() # Bob takes the message_kit and retrieves the message within delivered_cleartexts = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, retain_cfrags=True) assert plaintext == delivered_cleartexts[0] # Bob tries to retrieve again, but without using the cached CFrags, it fails. with pytest.raises(TypeError): delivered_cleartexts = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label) cleartexts_delivered_a_second_time = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, use_attached_cfrags=True) # Indeed, they're the same cleartexts. assert delivered_cleartexts == cleartexts_delivered_a_second_time # Let's try retrieve again, but Alice revoked the policy. failed_revocations = federated_alice.revoke(policy) assert len(failed_revocations) == 0 # One thing to note here is that Bob *can* still retrieve with the cached CFrags, even though this Policy has been revoked. #892 _cleartexts = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, use_precedent_work_orders=True, ) assert _cleartexts == delivered_cleartexts # TODO: 892 # OK, but we imagine that the message_kit is fresh here. message_kit.capsule.clear_cfrags() with pytest.raises(Ursula.NotEnoughUrsulas): _cleartexts = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, ) bob.disenchant()
def test_bob_joins_policy_and_retrieves( federated_alice, federated_ursulas, certificates_tempdir, ): # Let's partition Ursulas in two parts a_couple_of_ursulas = list(federated_ursulas)[:2] rest_of_ursulas = list(federated_ursulas)[2:] # Bob becomes bob = Bob( federated_only=True, domains={TEMPORARY_DOMAIN}, start_learning_now=True, network_middleware=MockRestMiddleware(), abort_on_learning_error=True, known_nodes=a_couple_of_ursulas, ) # Bob only knows a couple of Ursulas initially assert len(bob.known_nodes) == 2 # Alice creates a policy granting access to Bob # Just for fun, let's assume she distributes KFrags among Ursulas unknown to Bob n = NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK - 2 label = b'label://' + os.urandom(32) contract_end_datetime = maya.now() + datetime.timedelta(days=5) policy = federated_alice.grant( bob=bob, label=label, m=3, n=n, expiration=contract_end_datetime, handpicked_ursulas=set(rest_of_ursulas), ) assert bob == policy.bob assert label == policy.label # Now, Bob joins the policy bob.join_policy(label=label, alice_verifying_key=federated_alice.stamp, block=True) # In the end, Bob should know all the Ursulas assert len(bob.known_nodes) == len(federated_ursulas) # Enrico becomes enrico = Enrico(policy_encrypting_key=policy.public_key) plaintext = b"What's your approach? Mississippis or what?" message_kit, _signature = enrico.encrypt_message(plaintext) alices_verifying_key = federated_alice.stamp.as_umbral_pubkey() # Bob takes the message_kit and retrieves the message within delivered_cleartexts = bob.retrieve( message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, retain_cfrags=True) assert plaintext == delivered_cleartexts[0] # Bob tries to retrieve again, but without using the cached CFrags, it fails. with pytest.raises(TypeError): delivered_cleartexts = bob.retrieve( message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label) cleartexts_delivered_a_second_time = bob.retrieve( message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, use_attached_cfrags=True) # Indeed, they're the same cleartexts. assert delivered_cleartexts == cleartexts_delivered_a_second_time # Let's try retrieve again, but Alice revoked the policy. failed_revocations = federated_alice.revoke(policy) assert len(failed_revocations) == 0 # One thing to note here is that Bob *can* still retrieve with the cached CFrags, even though this Policy has been revoked. #892 _cleartexts = bob.retrieve( message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, use_precedent_work_orders=True, ) assert _cleartexts == delivered_cleartexts # TODO: 892 # OK, but we imagine that the message_kit is fresh here. message_kit.capsule.clear_cfrags() with pytest.raises(Ursula.NotEnoughUrsulas): _cleartexts = bob.retrieve( message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, )
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_bob_retrieves(federated_alice, federated_ursulas, certificates_tempdir): # Let's partition Ursulas in two parts a_couple_of_ursulas = list(federated_ursulas)[:2] rest_of_ursulas = list(federated_ursulas)[2:] # Bob becomes bob = Bob( federated_only=True, domain=TEMPORARY_DOMAIN, start_learning_now=True, network_middleware=MockRestMiddleware(), abort_on_learning_error=True, known_nodes=a_couple_of_ursulas, ) # Bob has only connected to - at most - 2 nodes. assert sum(node.verified_node for node in bob.known_nodes) <= 2 # Alice creates a policy granting access to Bob # Just for fun, let's assume she distributes KFrags among Ursulas unknown to Bob shares = NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK - 2 label = b'label://' + os.urandom(32) contract_end_datetime = maya.now() + datetime.timedelta(days=5) policy = federated_alice.grant( bob=bob, label=label, threshold=3, shares=shares, expiration=contract_end_datetime, ursulas=set(rest_of_ursulas), ) assert label == policy.label # Enrico becomes enrico = Enrico(policy_encrypting_key=policy.public_key) plaintext = b"What's your approach? Mississippis or what?" message_kit = enrico.encrypt_message(plaintext) alices_verifying_key = federated_alice.stamp.as_umbral_pubkey() # Bob takes the message_kit and retrieves the message within delivered_cleartexts = bob.retrieve_and_decrypt( [message_kit], alice_verifying_key=alices_verifying_key, encrypted_treasure_map=policy.treasure_map) assert plaintext == delivered_cleartexts[0] cleartexts_delivered_a_second_time = bob.retrieve_and_decrypt( [message_kit], alice_verifying_key=alices_verifying_key, encrypted_treasure_map=policy.treasure_map) # Indeed, they're the same cleartexts. assert delivered_cleartexts == cleartexts_delivered_a_second_time # Let's try retrieve again, but Alice revoked the policy. receipt, failed_revocations = federated_alice.revoke(policy) assert len(failed_revocations) == 0 # One thing to note here is that Bob *can* still retrieve with the cached CFrags, # even though this Policy has been revoked. #892 _cleartexts = bob.retrieve_and_decrypt( [message_kit], alice_verifying_key=alices_verifying_key, encrypted_treasure_map=policy.treasure_map) assert _cleartexts == delivered_cleartexts # TODO: 892 bob.disenchant()
def test_retrieve_cfrags(blockchain_porter, blockchain_porter_web_controller, random_blockchain_policy, blockchain_bob, blockchain_alice): # Send bad data to assert error return response = blockchain_porter_web_controller.post('/retrieve_cfrags', data=json.dumps( {'bad': 'input'})) assert response.status_code == 400 # Setup network_middleware = MockRestMiddleware() # enact new random policy since idle_blockchain_policy/enacted_blockchain_policy already modified in previous tests enacted_policy = random_blockchain_policy.enact( network_middleware=network_middleware) original_message = b"Those who say it can't be done are usually interrupted by others doing it." # - James Baldwin retrieve_cfrags_params, message_kit = retrieval_request_setup( enacted_policy, blockchain_bob, blockchain_alice, original_message=original_message, encode_for_rest=True) # # Success # response = blockchain_porter_web_controller.post( '/retrieve_cfrags', data=json.dumps(retrieve_cfrags_params)) assert response.status_code == 200 response_data = json.loads(response.data) retrieval_results = response_data['result']['retrieval_results'] assert retrieval_results # expected results - can only compare length of results, ursulas are randomized to obtain cfrags retrieve_args = retrieval_params_decode_from_rest(retrieve_cfrags_params) expected_results = blockchain_porter.retrieve_cfrags(**retrieve_args) assert len(retrieval_results) == len(expected_results) # check that the re-encryption performed was valid treasure_map = retrieve_args['treasure_map'] policy_message_kit = PolicyMessageKit.from_message_kit( message_kit=message_kit, policy_encrypting_key=enacted_policy.public_key, threshold=treasure_map.threshold) assert len(retrieval_results) == 1 field = RetrievalResultSchema() cfrags = field.load(retrieval_results[0])['cfrags'] verified_cfrags = {} for ursula, cfrag in cfrags.items(): # need to obtain verified cfrags (verified cfrags are not deserializable, only non-verified cfrags) verified_cfrag = cfrag.verify( capsule=policy_message_kit.message_kit.capsule, verifying_pk=blockchain_alice.stamp.as_umbral_pubkey(), delegating_pk=enacted_policy.public_key, receiving_pk=blockchain_bob.public_keys(DecryptingPower)) verified_cfrags[ursula] = verified_cfrag retrieval_result_object = RetrievalResult(cfrags=verified_cfrags) policy_message_kit = policy_message_kit.with_result( retrieval_result_object) assert policy_message_kit.is_decryptable_by_receiver() cleartext = blockchain_bob._crypto_power.power_ups( DecryptingPower).keypair.decrypt_message_kit(policy_message_kit) assert cleartext == original_message # # Try using multiple retrieval kits # multiple_retrieval_kits_params = dict(retrieve_cfrags_params) enrico = Enrico(policy_encrypting_key=enacted_policy.public_key) retrieval_kit_1 = RetrievalKit.from_message_kit( enrico.encrypt_message(b"Those who say it can't be done")) retrieval_kit_2 = RetrievalKit.from_message_kit( enrico.encrypt_message(b"are usually interrupted by others doing it.")) retrieval_kit_field = RetrievalKitField() # use multiple retrieval kits and serialize for json multiple_retrieval_kits_params['retrieval_kits'] = [ retrieval_kit_field._serialize(value=retrieval_kit_1, attr=None, obj=None), retrieval_kit_field._serialize(value=retrieval_kit_2, attr=None, obj=None) ] response = blockchain_porter_web_controller.post( '/retrieve_cfrags', data=json.dumps(multiple_retrieval_kits_params)) assert response.status_code == 200 response_data = json.loads(response.data) retrieval_results = response_data['result']['retrieval_results'] assert retrieval_results assert len(retrieval_results) == 2 # # Try same retrieval (with multiple retrieval kits) using query parameters # url_retrieve_params = dict( multiple_retrieval_kits_params) # use multiple kit params from above # adjust parameter for url query parameter list format url_retrieve_params['retrieval_kits'] = ",".join( url_retrieve_params['retrieval_kits']) # adjust for list response = blockchain_porter_web_controller.post( f'/retrieve_cfrags' f'?{urlencode(url_retrieve_params)}') assert response.status_code == 200 response_data = json.loads(response.data) retrieval_results = response_data['result']['retrieval_results'] assert retrieval_results assert len(retrieval_results) == 2 # # Failure # failure_retrieve_cfrags_params = dict(retrieve_cfrags_params) # use invalid treasure map bytes failure_retrieve_cfrags_params['treasure_map'] = b64encode( os.urandom(32)).decode() response = blockchain_porter_web_controller.post( '/retrieve_cfrags', data=json.dumps(failure_retrieve_cfrags_params)) assert response.status_code == 400 # invalid treasure map provided