Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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']
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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,
        )
Ejemplo n.º 15
0
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
Ejemplo n.º 17
0
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