コード例 #1
0
 def was_this_evidence_evaluated(self, evidence) -> bool:
     data_hash = sha256_digest(evidence.task.capsule, evidence.task.cfrag)
     return self.contract.functions.evaluatedCFrags(data_hash).call()
コード例 #2
0
ファイル: test_adjudicator.py プロジェクト: y3v63n/nucypher
def test_evaluate_cfrag(testerchain, escrow, adjudicator, token_economics,
                        blockchain_ursulas, mock_ursula_reencrypts):
    ursula = list(blockchain_ursulas)[0]

    creator, non_staker, investigator, *everyone_else = testerchain.client.accounts
    evaluation_log = adjudicator.events.CFragEvaluated.createFilter(
        fromBlock='latest')
    verdict_log = adjudicator.events.IncorrectCFragVerdict.createFilter(
        fromBlock='latest')

    worker_stake = 1000
    worker_penalty_history = 0
    investigator_balance = 0
    number_of_evaluations = 0

    def compute_penalty_and_reward(stake: int,
                                   penalty_history: int) -> Tuple[int, int]:
        penalty_ = token_economics.base_penalty
        penalty_ += token_economics.penalty_history_coefficient * penalty_history
        penalty_ = min(penalty_,
                       stake // token_economics.percentage_penalty_coefficient)
        reward_ = penalty_ // token_economics.reward_coefficient
        return penalty_, reward_

    # Prepare one staker
    staker = ursula.checksum_address
    tx = escrow.functions.setStakerInfo(staker, worker_stake,
                                        ursula.worker_address).transact()
    testerchain.wait_for_receipt(tx)

    assert ursula._stamp_has_valid_signature_by_worker()

    # Prepare evaluation data
    evidence = mock_ursula_reencrypts(ursula)
    capsule = evidence.task.capsule
    cfrag = evidence.task.cfrag
    assert cfrag.verify_correctness(capsule)

    evidence_data = evidence.precompute_values()
    assert len(evidence_data) == 20 * 32 + 32 + 20 + 5

    data_hash = sha256_digest(capsule, cfrag)
    # This capsule and cFrag are not yet evaluated
    assert not adjudicator.functions.evaluatedCFrags(data_hash).call()

    args = list(evidence.evaluation_arguments())

    # Challenge using good data
    assert worker_stake == escrow.functions.getAllTokens(staker).call()

    tx = adjudicator.functions.evaluateCFrag(*args).transact(
        {'from': investigator})
    testerchain.wait_for_receipt(tx)
    number_of_evaluations += 1
    # Hash of the data is saved and staker was not slashed
    assert adjudicator.functions.evaluatedCFrags(data_hash).call()
    assert worker_stake == escrow.functions.getAllTokens(staker).call()
    assert investigator_balance == escrow.functions.rewardInfo(
        investigator).call()

    events = evaluation_log.get_all_entries()
    assert number_of_evaluations == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert investigator == event_args['investigator']
    assert event_args['correctness']
    assert 0 == len(verdict_log.get_all_entries())

    ###############################
    # Test: Don't evaluate staker with data that already was checked
    ###############################
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*args).transact()
        testerchain.wait_for_receipt(tx)

    ###############################
    # Test: Ursula produces incorrect proof:
    ###############################
    evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True)
    capsule = evidence.task.capsule
    cfrag = evidence.task.cfrag
    assert not cfrag.verify_correctness(capsule)

    args = list(evidence.evaluation_arguments())

    data_hash = sha256_digest(capsule, cfrag)
    assert not adjudicator.functions.evaluatedCFrags(data_hash).call()

    tx = adjudicator.functions.evaluateCFrag(*args).transact(
        {'from': investigator})
    testerchain.wait_for_receipt(tx)
    number_of_evaluations += 1

    # Hash of the data is saved and staker was slashed
    assert adjudicator.functions.evaluatedCFrags(data_hash).call()

    penalty, reward = compute_penalty_and_reward(worker_stake,
                                                 worker_penalty_history)
    worker_stake -= penalty
    investigator_balance += reward
    worker_penalty_history += 1

    assert worker_stake == escrow.functions.getAllTokens(staker).call()
    assert investigator_balance == escrow.functions.rewardInfo(
        investigator).call()

    events = evaluation_log.get_all_entries()
    assert number_of_evaluations == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert investigator == event_args['investigator']
    assert not event_args['correctness']
    events = verdict_log.get_all_entries()
    assert number_of_evaluations - 1 == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert ursula.worker_address == event_args['worker']
    assert staker == event_args['staker']

    ###############################
    # Test: Bob produces wrong precomputed data
    ###############################

    evidence = mock_ursula_reencrypts(ursula)
    capsule = evidence.task.capsule
    cfrag = evidence.task.cfrag
    assert cfrag.verify_correctness(capsule)

    # Bob produces a random point and gets the bytes of coords x and y
    random_point_bytes = Point.gen_rand().to_bytes(is_compressed=False)[1:]
    # He uses this garbage instead of correct precomputation of z*E
    evidence_data = bytearray(evidence_data)
    evidence_data[32:32 + 64] = random_point_bytes
    evidence_data = bytes(evidence_data)

    args = list(evidence.evaluation_arguments())
    args[-1] = evidence_data

    data_hash = sha256_digest(capsule, cfrag)
    assert not adjudicator.functions.evaluatedCFrags(data_hash).call()

    # Evaluation must fail since Bob precomputed wrong values
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*args).transact(
            {'from': investigator})
        testerchain.wait_for_receipt(tx)

    ###############################
    # Test: Second violation. Penalty is increased
    ###############################

    evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True)
    capsule = evidence.task.capsule
    cfrag = evidence.task.cfrag
    assert not cfrag.verify_correctness(capsule)

    args = list(evidence.evaluation_arguments())

    data_hash = sha256_digest(capsule, cfrag)
    assert not adjudicator.functions.evaluatedCFrags(data_hash).call()

    worker_stake = escrow.functions.getAllTokens(staker).call()
    investigator_balance = escrow.functions.rewardInfo(investigator).call()

    assert not adjudicator.functions.evaluatedCFrags(data_hash).call()
    tx = adjudicator.functions.evaluateCFrag(*args).transact(
        {'from': investigator})
    testerchain.wait_for_receipt(tx)
    number_of_evaluations += 1

    assert adjudicator.functions.evaluatedCFrags(data_hash).call()

    previous_penalty = penalty
    penalty, reward = compute_penalty_and_reward(worker_stake,
                                                 worker_penalty_history)
    # Penalty was increased because it's the second violation
    assert penalty == previous_penalty + token_economics.penalty_history_coefficient
    worker_stake -= penalty
    investigator_balance += reward
    worker_penalty_history += 1

    assert worker_stake == escrow.functions.getAllTokens(staker).call()
    assert investigator_balance == escrow.functions.rewardInfo(
        investigator).call()

    events = evaluation_log.get_all_entries()
    assert number_of_evaluations == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert investigator == event_args['investigator']
    assert not event_args['correctness']
    events = verdict_log.get_all_entries()
    assert number_of_evaluations - 1 == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert ursula.worker_address == event_args['worker']
    assert staker == event_args['staker']

    ###############################
    # Test: Third violation. Penalty reaches the maximum allowed
    ###############################

    evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True)
    capsule = evidence.task.capsule
    cfrag = evidence.task.cfrag
    assert not cfrag.verify_correctness(capsule)

    args = list(evidence.evaluation_arguments())

    data_hash = sha256_digest(capsule, cfrag)
    assert not adjudicator.functions.evaluatedCFrags(data_hash).call()

    worker_stake = escrow.functions.getAllTokens(staker).call()
    investigator_balance = escrow.functions.rewardInfo(investigator).call()

    tx = adjudicator.functions.evaluateCFrag(*args).transact(
        {'from': investigator})
    testerchain.wait_for_receipt(tx)
    number_of_evaluations += 1

    assert adjudicator.functions.evaluatedCFrags(data_hash).call()

    penalty, reward = compute_penalty_and_reward(worker_stake,
                                                 worker_penalty_history)
    # Penalty has reached maximum available percentage of value
    assert penalty == worker_stake // token_economics.percentage_penalty_coefficient
    worker_stake -= penalty
    investigator_balance += reward
    worker_penalty_history += 1

    assert worker_stake == escrow.functions.getAllTokens(staker).call()
    assert investigator_balance == escrow.functions.rewardInfo(
        investigator).call()

    events = evaluation_log.get_all_entries()
    assert number_of_evaluations == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert investigator == event_args['investigator']
    assert not event_args['correctness']
    events = verdict_log.get_all_entries()
    assert number_of_evaluations - 1 == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert ursula.worker_address == event_args['worker']
    assert staker == event_args['staker']

    #################
    # Test: Invalid evaluations
    ##############

    evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True)
    capsule = evidence.task.capsule
    cfrag = evidence.task.cfrag
    assert not cfrag.verify_correctness(capsule)

    args = list(evidence.evaluation_arguments())

    data_hash = sha256_digest(capsule, cfrag)
    assert not adjudicator.functions.evaluatedCFrags(data_hash).call()

    # Can't evaluate staker using broken signatures
    wrong_args = list(args)
    wrong_args[2] = evidence.task.cfrag_signature[1:]
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    wrong_args = list(args)
    wrong_args[3] = evidence.task.signature[1:]
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    wrong_args = list(args)
    wrong_args[7] = wrong_args[7][1:]
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # Can't evaluate staker using wrong keys
    wrong_args = list(args)
    wrong_args[5] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(
        is_compressed=False)[1:]
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    wrong_args = list(args)
    wrong_args[6] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(
        is_compressed=False)[1:]
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # Can't use signature for another data
    wrong_args = list(args)
    wrong_args[1] = os.urandom(len(bytes(cfrag)))
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # Can't evaluate nonexistent staker
    signed_stamp = testerchain.client.sign_message(account=non_staker,
                                                   message=bytes(ursula.stamp))

    wrong_args = list(args)
    wrong_args[7] = signed_stamp
    with pytest.raises(TransactionFailed):
        tx = adjudicator.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # Can't evaluate staker without tokens
    tx = escrow.functions.setStakerInfo(non_staker, 0,
                                        ursula.worker_address).transact()
    testerchain.wait_for_receipt(tx)
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # If the non-staker starts to stake, then she can be slashed
    new_staker = non_staker
    tx = escrow.functions.setStakerInfo(new_staker, worker_stake,
                                        ursula.worker_address).transact()
    testerchain.wait_for_receipt(tx)

    evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True)
    capsule = evidence.task.capsule
    cfrag = evidence.task.cfrag
    assert not cfrag.verify_correctness(capsule)

    args = list(evidence.evaluation_arguments())
    data_hash = sha256_digest(capsule, cfrag)
    assert not adjudicator.functions.evaluatedCFrags(data_hash).call()

    tx = adjudicator.functions.evaluateCFrag(*args).transact(
        {'from': investigator})
    testerchain.wait_for_receipt(tx)
    number_of_evaluations += 1

    assert adjudicator.functions.evaluatedCFrags(data_hash).call()

    penalty, reward = compute_penalty_and_reward(worker_stake,
                                                 worker_penalty_history)
    worker_stake -= penalty
    investigator_balance += reward
    worker_penalty_history += 1

    assert worker_stake == escrow.functions.getAllTokens(new_staker).call()
    assert investigator_balance == escrow.functions.rewardInfo(
        investigator).call()

    events = evaluation_log.get_all_entries()
    assert number_of_evaluations == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert investigator == event_args['investigator']
    assert not event_args['correctness']
    events = verdict_log.get_all_entries()
    assert number_of_evaluations - 1 == len(events)
    event_args = events[-1]['args']
    assert data_hash == event_args['evaluationHash']
    assert ursula.worker_address == event_args['worker']
    assert new_staker == event_args['staker']