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()
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']