From 26076b1d980d4ed4bde6c4e6e1f2376e95944489 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Mon, 17 May 2021 21:57:41 -0700 Subject: [PATCH] Remove IndisputableEvidence and related functions and tests --- nucypher/blockchain/eth/agents.py | 9 +- nucypher/characters/lawful.py | 6 +- nucypher/crypto/utils.py | 41 +- nucypher/policy/collections.py | 172 ------- nucypher/types.py | 1 - .../blockchain/actors/test_investigator.py | 108 ---- .../agents/test_adjudicator_agent.py | 113 ---- .../test_intercontract_integration.py | 133 +---- .../lib/test_reencryption_validator.py | 197 ------- tests/contracts/main/adjudicator/conftest.py | 46 -- .../main/adjudicator/test_adjudicator.py | 487 ------------------ tests/metrics/estimate_gas.py | 63 --- .../crypto/test_coordinates_serialization.py | 39 -- tests/unit/crypto/test_umbral_signatures.py | 94 ---- tests/unit/test_work_orders.py | 8 +- tests/utils/ursula.py | 15 +- 16 files changed, 31 insertions(+), 1501 deletions(-) delete mode 100644 tests/acceptance/blockchain/actors/test_investigator.py delete mode 100644 tests/acceptance/blockchain/agents/test_adjudicator_agent.py delete mode 100644 tests/contracts/lib/test_reencryption_validator.py delete mode 100644 tests/contracts/main/adjudicator/conftest.py delete mode 100644 tests/contracts/main/adjudicator/test_adjudicator.py delete mode 100644 tests/unit/crypto/test_coordinates_serialization.py delete mode 100644 tests/unit/crypto/test_umbral_signatures.py diff --git a/nucypher/blockchain/eth/agents.py b/nucypher/blockchain/eth/agents.py index 9855952a7..26a311185 100644 --- a/nucypher/blockchain/eth/agents.py +++ b/nucypher/blockchain/eth/agents.py @@ -68,7 +68,6 @@ from nucypher.types import ( StakerInfo, PeriodDelta, StakingEscrowParameters, - Evidence ) from nucypher.utilities.logging import Logger # type: ignore @@ -915,7 +914,7 @@ class AdjudicatorAgent(EthereumContractAgent): _proxy_name: str = DISPATCHER_CONTRACT_NAME @contract_api(TRANSACTION) - def evaluate_cfrag(self, evidence: Evidence, transacting_power: TransactingPower) -> TxReceipt: + def evaluate_cfrag(self, evidence, transacting_power: TransactingPower) -> TxReceipt: """Submits proof that a worker created wrong CFrag""" payload: TxParams = {'gas': Wei(500_000)} # TODO #842: gas needed for use with geth. contract_function: ContractFunction = self.contract.functions.evaluateCFrag(*evidence.evaluation_arguments()) @@ -925,7 +924,7 @@ class AdjudicatorAgent(EthereumContractAgent): return receipt @contract_api(CONTRACT_CALL) - def was_this_evidence_evaluated(self, evidence: Evidence) -> bool: + def was_this_evidence_evaluated(self, evidence) -> bool: data_hash: bytes = sha256_digest(evidence.task.capsule, evidence.task.cfrag) result: bool = self.contract.functions.evaluatedCFrags(data_hash).call() return result @@ -1003,7 +1002,7 @@ class WorkLockAgent(EthereumContractAgent): def cancel_bid(self, transacting_power: TransactingPower) -> TxReceipt: """Cancel bid and refund deposited ETH.""" contract_function: ContractFunction = self.contract.functions.cancelBid() - receipt = self.blockchain.send_transaction(contract_function=contract_function, + receipt = self.blockchain.send_transaction(contract_function=contract_function, transacting_power=transacting_power) return receipt @@ -1012,7 +1011,7 @@ class WorkLockAgent(EthereumContractAgent): """Force refund to bidders who can get tokens more than maximum allowed.""" addresses = sorted(addresses, key=str.casefold) contract_function: ContractFunction = self.contract.functions.forceRefund(addresses) - receipt = self.blockchain.send_transaction(contract_function=contract_function, + receipt = self.blockchain.send_transaction(contract_function=contract_function, transacting_power=transacting_power) return receipt diff --git a/nucypher/characters/lawful.py b/nucypher/characters/lawful.py index 53e920da8..e993949e7 100644 --- a/nucypher/characters/lawful.py +++ b/nucypher/characters/lawful.py @@ -780,7 +780,7 @@ class Bob(Character): def _reencrypt(self, work_order: 'WorkOrder', retain_cfrags: bool = False - ) -> Tuple[bool, Union[List['IndisputableEvidence'], List['CapsuleFrag']]]: + ) -> Tuple[bool, Union[List['Ursula'], List['CapsuleFrag']]]: if work_order.completed: raise TypeError( @@ -811,10 +811,8 @@ class Bob(Character): for capsule, pre_task in work_order.tasks.items(): if not pre_task.cfrag.verify_correctness(capsule): # TODO: WARNING - This block is untested. - from nucypher.policy.collections import IndisputableEvidence - evidence = IndisputableEvidence(task=pre_task, work_order=work_order) # I got a lot of problems with you people ... - the_airing_of_grievances.append(evidence) + the_airing_of_grievances.append(work_order.ursula) if the_airing_of_grievances: return False, the_airing_of_grievances diff --git a/nucypher/crypto/utils.py b/nucypher/crypto/utils.py index d85e36831..c6cc86089 100644 --- a/nucypher/crypto/utils.py +++ b/nucypher/crypto/utils.py @@ -19,7 +19,6 @@ from coincurve import PublicKey from eth_keys import KeyAPI as EthKeyAPI from typing import Any, Union from umbral.keys import UmbralPublicKey -from umbral.point import Point from umbral.signing import Signature from nucypher.crypto.api import keccak_digest @@ -42,49 +41,13 @@ def construct_policy_id(label: bytes, stamp: bytes) -> bytes: return keccak_digest(label + stamp) -def canonical_address_from_umbral_key(public_key: UmbralPublicKey) -> bytes: +def canonical_address_from_umbral_key(public_key: Union[UmbralPublicKey, SignatureStamp]) -> bytes: pubkey_raw_bytes = get_coordinates_as_bytes(public_key) eth_pubkey = EthKeyAPI.PublicKey(pubkey_raw_bytes) canonical_address = eth_pubkey.to_canonical_address() return canonical_address -def recover_pubkey_from_signature(message: bytes, - signature: Union[bytes, Signature], - v_value_to_try: int, - is_prehashed: bool = False) -> bytes: - """ - Recovers a serialized, compressed public key from a signature. - It allows to specify a potential v value, in which case it assumes the signature - has the traditional (r,s) raw format. If a v value is not present, it assumes - the signature has the recoverable format (r, s, v). - - :param message: Signed message - :param signature: The signature from which the pubkey is recovered - :param v_value_to_try: A potential v value to try - :param is_prehashed: True if the message is already pre-hashed. Default is False, and message will be hashed with SHA256 - :return: The compressed byte-serialized representation of the recovered public key - """ - - signature = bytes(signature) - expected_signature_size = Signature.expected_bytes_length() - if not len(signature) == expected_signature_size: - raise ValueError(f"The signature size should be {expected_signature_size} B.") - - if v_value_to_try in (0, 1, 27, 28): - if v_value_to_try >= 27: - v_value_to_try -= 27 - signature = signature + v_value_to_try.to_bytes(1, 'big') - else: - raise ValueError("Wrong v value. It should be 0, 1, 27 or 28.") - - kwargs = dict(hasher=None) if is_prehashed else {} - pubkey = PublicKey.from_signature_and_message(serialized_sig=signature, - message=message, - **kwargs) - return pubkey.format(compressed=True) - - def get_signature_recovery_value(message: bytes, signature: Union[bytes, Signature], public_key: Union[bytes, UmbralPublicKey], @@ -117,7 +80,7 @@ def get_signature_recovery_value(message: bytes, "Either the message, the signature or the public key is not correct") -def get_coordinates_as_bytes(point: Union[Point, UmbralPublicKey, SignatureStamp], +def get_coordinates_as_bytes(point: UmbralPublicKey, x_coord=True, y_coord=True) -> bytes: if isinstance(point, SignatureStamp): diff --git a/nucypher/policy/collections.py b/nucypher/policy/collections.py index 10a03704f..80f84a060 100644 --- a/nucypher/policy/collections.py +++ b/nucypher/policy/collections.py @@ -31,7 +31,6 @@ from cryptography.hazmat.primitives import hashes from eth_utils import to_canonical_address, to_checksum_address from typing import Optional, Tuple from umbral.config import default_params -from umbral.curvebn import CurveBN from umbral.keys import UmbralPublicKey from umbral.pre import Capsule @@ -46,8 +45,6 @@ from nucypher.crypto.splitters import capsule_splitter, key_splitter from nucypher.crypto.splitters import cfrag_splitter from nucypher.crypto.utils import ( canonical_address_from_umbral_key, - get_coordinates_as_bytes, - get_signature_recovery_value ) from nucypher.network.middleware import RestMiddleware @@ -569,172 +566,3 @@ class Revocation: raise InvalidSignature( "Revocation has an invalid signature: {}".format(self.signature)) return True - - -# TODO: Change name to EvaluationEvidence -class IndisputableEvidence: - - def __init__(self, - task: 'WorkOrder.Task', - work_order: 'WorkOrder', - delegating_pubkey: UmbralPublicKey = None, - receiving_pubkey: UmbralPublicKey = None, - verifying_pubkey: UmbralPublicKey = None, - ) -> None: - - self.task = task - self.ursula_pubkey = work_order.ursula.stamp.as_umbral_pubkey() - self.ursula_identity_evidence = work_order.ursula.decentralized_identity_evidence - self.bob_verifying_key = work_order.bob.stamp.as_umbral_pubkey() - self.blockhash = work_order.blockhash - self.alice_address = work_order.alice_address - - keys = self.task.capsule.get_correctness_keys() - key_types = ("delegating", "receiving", "verifying") - if all(keys[key_type] for key_type in key_types): - self.delegating_pubkey = keys["delegating"] - self.receiving_pubkey = keys["receiving"] - self.verifying_pubkey = keys["verifying"] - elif all((delegating_pubkey, receiving_pubkey, verifying_pubkey)): - self.delegating_pubkey = delegating_pubkey - self.receiving_pubkey = receiving_pubkey - self.verifying_pubkey = verifying_pubkey - else: - raise ValueError("All correctness keys are required to compute evidence. " - "Either pass them as arguments or in the capsule.") - - # TODO: check that the metadata is correct. - - def get_proof_challenge_scalar(self) -> CurveBN: - capsule = self.task.capsule - cfrag = self.task.cfrag - - umbral_params = default_params() - e, v, _ = capsule.components() - - e1 = cfrag.point_e1 - v1 = cfrag.point_v1 - e2 = cfrag.proof.point_e2 - v2 = cfrag.proof.point_v2 - u = umbral_params.u - u1 = cfrag.proof.point_kfrag_commitment - u2 = cfrag.proof.point_kfrag_pok - metadata = cfrag.proof.metadata - - from umbral.random_oracles import hash_to_curvebn, ExtendedKeccak - - hash_input = (e, e1, e2, v, v1, v2, u, u1, u2, metadata) - - h = hash_to_curvebn(*hash_input, params=umbral_params, hash_class=ExtendedKeccak) - return h - - def precompute_values(self) -> bytes: - capsule = self.task.capsule - cfrag = self.task.cfrag - - umbral_params = default_params() - e, v, _ = capsule.components() - - e1 = cfrag.point_e1 - v1 = cfrag.point_v1 - e2 = cfrag.proof.point_e2 - v2 = cfrag.proof.point_v2 - u = umbral_params.u - u1 = cfrag.proof.point_kfrag_commitment - u2 = cfrag.proof.point_kfrag_pok - metadata = cfrag.proof.metadata - - h = self.get_proof_challenge_scalar() - - e1h = h * e1 - v1h = h * v1 - u1h = h * u1 - - z = cfrag.proof.bn_sig - ez = z * e - vz = z * v - uz = z * u - - only_y_coord = dict(x_coord=False, y_coord=True) - # E points - e_y = get_coordinates_as_bytes(e, **only_y_coord) - ez_xy = get_coordinates_as_bytes(ez) - e1_y = get_coordinates_as_bytes(e1, **only_y_coord) - e1h_xy = get_coordinates_as_bytes(e1h) - e2_y = get_coordinates_as_bytes(e2, **only_y_coord) - # V points - v_y = get_coordinates_as_bytes(v, **only_y_coord) - vz_xy = get_coordinates_as_bytes(vz) - v1_y = get_coordinates_as_bytes(v1, **only_y_coord) - v1h_xy = get_coordinates_as_bytes(v1h) - v2_y = get_coordinates_as_bytes(v2, **only_y_coord) - # U points - uz_xy = get_coordinates_as_bytes(uz) - u1_y = get_coordinates_as_bytes(u1, **only_y_coord) - u1h_xy = get_coordinates_as_bytes(u1h) - u2_y = get_coordinates_as_bytes(u2, **only_y_coord) - - # Get hashed KFrag validity message - hash_function = hashes.Hash(hashes.SHA256(), backend=backend) - - kfrag_id = cfrag.kfrag_id - precursor = cfrag.point_precursor - delegating_pubkey = self.delegating_pubkey - receiving_pubkey = self.receiving_pubkey - - validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1, precursor) - kfrag_validity_message = bytes().join(bytes(item) for item in validity_input) - hash_function.update(kfrag_validity_message) - hashed_kfrag_validity_message = hash_function.finalize() - - # Get KFrag signature's v value - kfrag_signature_v = get_signature_recovery_value(message=hashed_kfrag_validity_message, - signature=cfrag.proof.kfrag_signature, - public_key=self.verifying_pubkey, - is_prehashed=True) - - cfrag_signature_v = get_signature_recovery_value(message=bytes(cfrag), - signature=self.task.cfrag_signature, - public_key=self.ursula_pubkey) - - metadata_signature_v = get_signature_recovery_value(message=self.task.signature, - signature=metadata, - public_key=self.ursula_pubkey) - - specification = self.task.get_specification(ursula_pubkey=self.ursula_pubkey, - alice_address=self.alice_address, - blockhash=self.blockhash, - ursula_identity_evidence=self.ursula_identity_evidence) - - specification_signature_v = get_signature_recovery_value(message=specification, - signature=self.task.signature, - public_key=self.bob_verifying_key) - - ursula_pubkey_prefix_byte = bytes(self.ursula_pubkey)[0:1] - - # Bundle everything together - pieces = ( - e_y, ez_xy, e1_y, e1h_xy, e2_y, - v_y, vz_xy, v1_y, v1h_xy, v2_y, - uz_xy, u1_y, u1h_xy, u2_y, - hashed_kfrag_validity_message, - self.alice_address, - # The following single-byte values are interpreted as a single bytes5 variable by the Solidity contract - kfrag_signature_v, - cfrag_signature_v, - metadata_signature_v, - specification_signature_v, - ursula_pubkey_prefix_byte, - ) - return b''.join(pieces) - - def evaluation_arguments(self) -> Tuple: - return (bytes(self.task.capsule), - bytes(self.task.cfrag), - bytes(self.task.cfrag_signature), - bytes(self.task.signature), - get_coordinates_as_bytes(self.bob_verifying_key), - get_coordinates_as_bytes(self.ursula_pubkey), - bytes(self.ursula_identity_evidence), - self.precompute_values() - ) diff --git a/nucypher/types.py b/nucypher/types.py index ac0546f0a..8eb30733d 100644 --- a/nucypher/types.py +++ b/nucypher/types.py @@ -26,7 +26,6 @@ Work = NewType("Work", int) Agent = TypeVar('Agent', bound='EthereumContractAgent') Period = NewType('Period', int) PeriodDelta = NewType('PeriodDelta', int) -Evidence = TypeVar('Evidence', bound='IndisputableEvidence') ContractReturnValue = TypeVar('ContractReturnValue', bound=Union[TxReceipt, Wei, int, str, bool]) diff --git a/tests/acceptance/blockchain/actors/test_investigator.py b/tests/acceptance/blockchain/actors/test_investigator.py deleted file mode 100644 index 722e78ed2..000000000 --- a/tests/acceptance/blockchain/actors/test_investigator.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -This file is part of nucypher. - -nucypher is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -nucypher is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with nucypher. If not, see . -""" - - -from umbral.keys import UmbralPrivateKey -from umbral.signing import Signer - -from nucypher.config.constants import TEMPORARY_DOMAIN -from nucypher.blockchain.eth.signers.software import Web3Signer -from nucypher.crypto.powers import TransactingPower -from nucypher.blockchain.eth.actors import Investigator, Staker -from nucypher.blockchain.eth.agents import ContractAgency, NucypherTokenAgent, StakingEscrowAgent -from nucypher.blockchain.eth.constants import NULL_ADDRESS -from nucypher.blockchain.eth.token import NU -from nucypher.crypto.signing import SignatureStamp -from tests.constants import INSECURE_DEVELOPMENT_PASSWORD - - -def mock_ursula(testerchain, account, mocker): - ursula_privkey = UmbralPrivateKey.gen_key() - ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.pubkey, - signer=Signer(ursula_privkey)) - - signed_stamp = testerchain.client.sign_message(account=account, - message=bytes(ursula_stamp)) - - ursula = mocker.Mock(stamp=ursula_stamp, decentralized_identity_evidence=signed_stamp) - return ursula - - -def test_investigator_requests_slashing(testerchain, - test_registry, - agency, - mock_ursula_reencrypts, - token_economics, - mocker): - - staker_account = testerchain.staker_account(0) - worker_account = testerchain.ursula_account(0) - - ##### STAKING ESCROW STUFF ##### - - token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=test_registry) - staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry) - - locked_tokens = token_economics.minimum_allowed_locked * 5 - - # The staker receives an initial amount of tokens - tpower = TransactingPower(account=testerchain.etherbase_account, signer=Web3Signer(testerchain.client)) - _txhash = token_agent.transfer(amount=locked_tokens, - target_address=staker_account, - transacting_power=tpower) - - # Deposit: The staker deposits tokens in the StakingEscrow contract. - staker_tpower = TransactingPower(account=staker_account, signer=Web3Signer(testerchain.client)) - staker = Staker(transacting_power=staker_tpower, - domain=TEMPORARY_DOMAIN, - registry=test_registry) - - staker.initialize_stake(amount=NU(locked_tokens, 'NuNit'), - lock_periods=token_economics.minimum_locked_periods) - assert staker.locked_tokens(periods=1) == locked_tokens - - # The staker hasn't bond a worker yet - assert NULL_ADDRESS == staking_agent.get_worker_from_staker(staker_address=staker_account) - - _txhash = staking_agent.bond_worker(transacting_power=staker_tpower, worker_address=worker_account) - - assert worker_account == staking_agent.get_worker_from_staker(staker_address=staker_account) - assert staker_account == staking_agent.get_staker_from_worker(worker_address=worker_account) - - ###### END OF STAKING ESCROW STUFF #### - - bob_account = testerchain.bob_account - bob_tpower = TransactingPower(account=bob_account, signer=Web3Signer(testerchain.client)) - investigator = Investigator(registry=test_registry, - transacting_power=bob_tpower, - domain=TEMPORARY_DOMAIN) - ursula = mock_ursula(testerchain, worker_account, mocker=mocker) - - # Let's create a bad cfrag - evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True) - - assert not investigator.was_this_evidence_evaluated(evidence) - bobby_old_balance = investigator.token_balance - - investigator.request_evaluation(evidence=evidence) - - assert investigator.was_this_evidence_evaluated(evidence) - investigator_reward = investigator.token_balance - bobby_old_balance - - assert investigator_reward > 0 - assert investigator_reward == token_economics.base_penalty / token_economics.reward_coefficient - assert staker.locked_tokens(periods=1) < locked_tokens diff --git a/tests/acceptance/blockchain/agents/test_adjudicator_agent.py b/tests/acceptance/blockchain/agents/test_adjudicator_agent.py deleted file mode 100644 index 270b9b657..000000000 --- a/tests/acceptance/blockchain/agents/test_adjudicator_agent.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -This file is part of nucypher. - -nucypher is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -nucypher is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with nucypher. If not, see . -""" - - -from umbral.keys import UmbralPrivateKey -from umbral.signing import Signer - -from nucypher.config.constants import TEMPORARY_DOMAIN -from nucypher.blockchain.eth.actors import NucypherTokenActor, Staker -from nucypher.blockchain.eth.agents import ( - AdjudicatorAgent, - StakingEscrowAgent, - ContractAgency, - NucypherTokenAgent -) -from nucypher.blockchain.eth.constants import NULL_ADDRESS -from nucypher.blockchain.eth.signers.software import Web3Signer -from nucypher.blockchain.eth.token import NU -from nucypher.crypto.powers import TransactingPower -from nucypher.crypto.signing import SignatureStamp - - -def mock_ursula(testerchain, account, mocker): - ursula_privkey = UmbralPrivateKey.gen_key() - ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.pubkey, - signer=Signer(ursula_privkey)) - - signed_stamp = testerchain.client.sign_message(account=account, - message=bytes(ursula_stamp)) - - ursula = mocker.Mock(stamp=ursula_stamp, decentralized_identity_evidence=signed_stamp) - return ursula - - -def test_adjudicator_slashes(agency, - testerchain, - mock_ursula_reencrypts, - token_economics, - test_registry, - mocker): - - staker_account = testerchain.staker_account(0) - worker_account = testerchain.ursula_account(0) - - ##### STAKING ESCROW STUFF ##### - - token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=test_registry) - staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry) - - locked_tokens = token_economics.minimum_allowed_locked * 5 - - # The staker receives an initial amount of tokens - tpower = TransactingPower(account=testerchain.etherbase_account, signer=Web3Signer(testerchain.client)) - _txhash = token_agent.transfer(amount=locked_tokens, - target_address=staker_account, - transacting_power=tpower) - - # Deposit: The staker deposits tokens in the StakingEscrow contract. - tpower = TransactingPower(account=staker_account, signer=Web3Signer(testerchain.client)) - staker = Staker(domain=TEMPORARY_DOMAIN, - registry=test_registry, - transacting_power=tpower) - - staker.initialize_stake(amount=NU(locked_tokens, 'NuNit'), - lock_periods=token_economics.minimum_locked_periods) - assert staker.locked_tokens(periods=1) == locked_tokens - - # The staker hasn't bond a worker yet - assert NULL_ADDRESS == staking_agent.get_worker_from_staker(staker_address=staker_account) - - _txhash = staking_agent.bond_worker(transacting_power=tpower, worker_address=worker_account) - - assert worker_account == staking_agent.get_worker_from_staker(staker_address=staker_account) - assert staker_account == staking_agent.get_staker_from_worker(worker_address=worker_account) - - ###### END OF STAKING ESCROW STUFF #### - - adjudicator_agent = AdjudicatorAgent(registry=test_registry) - bob_account = testerchain.bob_account - bobby = NucypherTokenActor(checksum_address=bob_account, - domain=TEMPORARY_DOMAIN, - registry=test_registry) - ursula = mock_ursula(testerchain, worker_account, mocker=mocker) - - # Let's create a bad cfrag - evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True) - - assert not adjudicator_agent.was_this_evidence_evaluated(evidence) - bobby_old_balance = bobby.token_balance - bob_tpower = TransactingPower(account=bob_account, signer=Web3Signer(testerchain.client)) - - adjudicator_agent.evaluate_cfrag(evidence=evidence, transacting_power=bob_tpower) - - assert adjudicator_agent.was_this_evidence_evaluated(evidence) - investigator_reward = bobby.token_balance - bobby_old_balance - - assert investigator_reward > 0 - assert investigator_reward == token_economics.base_penalty / token_economics.reward_coefficient - assert staker.locked_tokens(periods=1) < locked_tokens diff --git a/tests/contracts/integration/test_intercontract_integration.py b/tests/contracts/integration/test_intercontract_integration.py index 9df2f7089..b9e322c13 100644 --- a/tests/contracts/integration/test_intercontract_integration.py +++ b/tests/contracts/integration/test_intercontract_integration.py @@ -205,14 +205,6 @@ def mock_ursula(testerchain, account, mocker): return ursula -# TODO organize support functions -def generate_args_for_slashing(mock_ursula_reencrypts, ursula): - evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True) - args = list(evidence.evaluation_arguments()) - data_hash = sha256_digest(evidence.task.capsule, evidence.task.cfrag) - return data_hash, args - - @pytest.fixture(scope='module') def staking_interface(testerchain, token, escrow, policy_manager, worklock, deploy_contract): # Creator deploys the staking interface @@ -1023,115 +1015,6 @@ def test_upgrading_and_rollback(testerchain, assert staking_interface_v2.address == staking_interface_router.functions.target().call() -def test_slashing(testerchain, - token_economics, - token, - escrow, - adjudicator, - preallocation_escrow_1, - mock_ursula_reencrypts, - mocker): - creator, staker1, staker2, staker3, staker4, alice1, alice2, *contracts_owners =\ - testerchain.client.accounts - ursula1_with_stamp = mock_ursula(testerchain, staker1, mocker=mocker) - ursula2_with_stamp = mock_ursula(testerchain, staker2, mocker=mocker) - ursula3_with_stamp = mock_ursula(testerchain, staker3, mocker=mocker) - - # Slash stakers - # Make a commitment to two periods - tx = escrow.functions.commitToNextPeriod().transact({'from': staker1}) - testerchain.wait_for_receipt(tx) - tx = escrow.functions.commitToNextPeriod().transact({'from': staker2}) - testerchain.wait_for_receipt(tx) - tx = escrow.functions.commitToNextPeriod().transact({'from': staker3}) - testerchain.wait_for_receipt(tx) - testerchain.time_travel(hours=1) - tx = escrow.functions.commitToNextPeriod().transact({'from': staker1}) - testerchain.wait_for_receipt(tx) - tx = escrow.functions.commitToNextPeriod().transact({'from': staker2}) - testerchain.wait_for_receipt(tx) - tx = escrow.functions.commitToNextPeriod().transact({'from': staker3}) - testerchain.wait_for_receipt(tx) - testerchain.time_travel(hours=1) - - # Can't slash directly using the escrow contract - with pytest.raises((TransactionFailed, ValueError)): - tx = escrow.functions.slashStaker(staker1, 100, alice1, 10).transact() - testerchain.wait_for_receipt(tx) - - # Slash part of the free amount of tokens - current_period = escrow.functions.getCurrentPeriod().call() - tokens_amount = escrow.functions.getAllTokens(staker1).call() - previous_lock = escrow.functions.getLockedTokensInPast(staker1, 1).call() - lock = escrow.functions.getLockedTokens(staker1, 0).call() - next_lock = escrow.functions.getLockedTokens(staker1, 1).call() - total_previous_lock = escrow.functions.lockedPerPeriod(current_period - 1).call() - total_lock = escrow.functions.lockedPerPeriod(current_period).call() - alice1_balance = token.functions.balanceOf(alice1).call() - - algorithm_sha256, base_penalty, *coefficients = token_economics.slashing_deployment_parameters - penalty_history_coefficient, percentage_penalty_coefficient, reward_coefficient = coefficients - - data_hash, slashing_args = generate_args_for_slashing(mock_ursula_reencrypts, ursula1_with_stamp) - assert not adjudicator.functions.evaluatedCFrags(data_hash).call() - tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice1}) - testerchain.wait_for_receipt(tx) - assert adjudicator.functions.evaluatedCFrags(data_hash).call() - assert tokens_amount - base_penalty == escrow.functions.getAllTokens(staker1).call() - assert previous_lock == escrow.functions.getLockedTokensInPast(staker1, 1).call() - assert lock == escrow.functions.getLockedTokens(staker1, 0).call() - assert next_lock == escrow.functions.getLockedTokens(staker1, 1).call() - assert total_previous_lock == escrow.functions.lockedPerPeriod(current_period - 1).call() - assert total_lock == escrow.functions.lockedPerPeriod(current_period).call() - assert 0 == escrow.functions.lockedPerPeriod(current_period + 1).call() - assert alice1_balance + base_penalty / reward_coefficient == token.functions.balanceOf(alice1).call() - - # Slash part of the one sub stake - tokens_amount = escrow.functions.getAllTokens(staker2).call() - unlocked_amount = tokens_amount - escrow.functions.getLockedTokens(staker2, 0).call() - tx = escrow.functions.withdraw(unlocked_amount).transact({'from': staker2}) - testerchain.wait_for_receipt(tx) - previous_lock = escrow.functions.getLockedTokensInPast(staker2, 1).call() - lock = escrow.functions.getLockedTokens(staker2, 0).call() - next_lock = escrow.functions.getLockedTokens(staker2, 1).call() - data_hash, slashing_args = generate_args_for_slashing(mock_ursula_reencrypts, ursula2_with_stamp) - assert not adjudicator.functions.evaluatedCFrags(data_hash).call() - tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice1}) - testerchain.wait_for_receipt(tx) - assert adjudicator.functions.evaluatedCFrags(data_hash).call() - assert lock - base_penalty == escrow.functions.getAllTokens(staker2).call() - assert previous_lock == escrow.functions.getLockedTokensInPast(staker2, 1).call() - assert lock - base_penalty == escrow.functions.getLockedTokens(staker2, 0).call() - assert next_lock - base_penalty == escrow.functions.getLockedTokens(staker2, 1).call() - assert total_previous_lock == escrow.functions.lockedPerPeriod(current_period - 1).call() - assert total_lock - base_penalty == escrow.functions.lockedPerPeriod(current_period).call() - assert 0 == escrow.functions.lockedPerPeriod(current_period + 1).call() - assert alice1_balance + base_penalty == token.functions.balanceOf(alice1).call() - - # Slash preallocation escrow - tokens_amount = escrow.functions.getAllTokens(preallocation_escrow_1.address).call() - previous_lock = escrow.functions.getLockedTokensInPast(preallocation_escrow_1.address, 1).call() - lock = escrow.functions.getLockedTokens(preallocation_escrow_1.address, 0).call() - next_lock = escrow.functions.getLockedTokens(preallocation_escrow_1.address, 1).call() - total_previous_lock = escrow.functions.lockedPerPeriod(current_period - 1).call() - total_lock = escrow.functions.lockedPerPeriod(current_period).call() - alice1_balance = token.functions.balanceOf(alice1).call() - - data_hash, slashing_args = generate_args_for_slashing(mock_ursula_reencrypts, ursula3_with_stamp) - assert not adjudicator.functions.evaluatedCFrags(data_hash).call() - tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice1}) - testerchain.wait_for_receipt(tx) - assert adjudicator.functions.evaluatedCFrags(data_hash).call() - assert tokens_amount - base_penalty == escrow.functions.getAllTokens(preallocation_escrow_1.address).call() - assert previous_lock == escrow.functions.getLockedTokensInPast(preallocation_escrow_1.address, 1).call() - assert lock - base_penalty == escrow.functions.getLockedTokens(preallocation_escrow_1.address, 0).call() - assert next_lock - base_penalty == escrow.functions.getLockedTokens(preallocation_escrow_1.address, 1).call() - assert total_previous_lock == escrow.functions.lockedPerPeriod(current_period - 1).call() - assert total_lock - base_penalty == escrow.functions.lockedPerPeriod(current_period).call() - assert 0 == escrow.functions.lockedPerPeriod(current_period + 1).call() - assert alice1_balance + base_penalty / reward_coefficient == token.functions.balanceOf(alice1).call() - - def test_upgrading_adjudicator(testerchain, token_economics, escrow, @@ -1201,6 +1084,22 @@ def test_withdraw(testerchain, preallocation_escrow_2): staker1, staker2, staker3, staker4 = testerchain.client.accounts[1:5] + # Make a commitment to two periods + tx = escrow.functions.commitToNextPeriod().transact({'from': staker1}) + testerchain.wait_for_receipt(tx) + tx = escrow.functions.commitToNextPeriod().transact({'from': staker2}) + testerchain.wait_for_receipt(tx) + tx = escrow.functions.commitToNextPeriod().transact({'from': staker3}) + testerchain.wait_for_receipt(tx) + testerchain.time_travel(hours=1) + tx = escrow.functions.commitToNextPeriod().transact({'from': staker1}) + testerchain.wait_for_receipt(tx) + tx = escrow.functions.commitToNextPeriod().transact({'from': staker2}) + testerchain.wait_for_receipt(tx) + tx = escrow.functions.commitToNextPeriod().transact({'from': staker3}) + testerchain.wait_for_receipt(tx) + testerchain.time_travel(hours=1) + # Can't prolong stake by too low duration with pytest.raises((TransactionFailed, ValueError)): tx = escrow.functions.prolongStake(0, 1).transact({'from': staker2, 'gas_price': 0}) diff --git a/tests/contracts/lib/test_reencryption_validator.py b/tests/contracts/lib/test_reencryption_validator.py deleted file mode 100644 index 7cda6f268..000000000 --- a/tests/contracts/lib/test_reencryption_validator.py +++ /dev/null @@ -1,197 +0,0 @@ -""" -This file is part of nucypher. - -nucypher is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -nucypher is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with nucypher. If not, see . -""" - -import os -import pytest -from eth_tester.exceptions import TransactionFailed -from umbral.config import default_params -from umbral.curvebn import CurveBN -from umbral.keys import UmbralPrivateKey -from umbral.point import Point -from umbral.random_oracles import ExtendedKeccak, hash_to_curvebn -from umbral.signing import Signer - -from nucypher.crypto.signing import SignatureStamp - - -@pytest.fixture(scope='module') -def reencryption_validator(testerchain, deploy_contract): - contract, _ = deploy_contract('ReEncryptionValidatorMock') - return contract - - -def test_extended_keccak_to_bn(testerchain, reencryption_validator): - test_data = os.urandom(40) - h = hash_to_curvebn(test_data, params=default_params(), hash_class=ExtendedKeccak) - assert int(h) == reencryption_validator.functions.extendedKeccakToBN(test_data).call() - - -def test_ec_point_operations(testerchain, reencryption_validator): - valid_point = Point.gen_rand() - x, y = valid_point.to_affine() - - # Test isOnCurve - assert reencryption_validator.functions.isOnCurve(x, y).call() - - bad_y = y - 1 - assert not reencryption_validator.functions.isOnCurve(x, bad_y).call() - - # Test checkCompressedPoint - sign = 2 + (y % 2) - assert reencryption_validator.functions.checkCompressedPoint(sign, x, y).call() - - bad_sign = 3 - (y % 2) - assert not reencryption_validator.functions.checkCompressedPoint(bad_sign, x, y).call() - - # Test checkSerializedCoordinates - coords = valid_point.to_bytes(is_compressed=False)[1:] - assert reencryption_validator.functions.checkSerializedCoordinates(coords).call() - - coords = coords[:-1] + ((coords[-1] + 42) % 256).to_bytes(1, 'big') - assert not reencryption_validator.functions.checkSerializedCoordinates(coords).call() - - # Test ecmulVerify - P = valid_point - scalar = CurveBN.gen_rand() - Q = scalar * P - qx, qy = Q.to_affine() - - assert reencryption_validator.functions.ecmulVerify(x, y, int(scalar), qx, qy).call() - assert not reencryption_validator.functions.ecmulVerify(x, y, int(scalar), x, y).call() - - # Test eqAffineJacobian - Q_affine = [qx, qy] - Q_jacobian = [qx, qy, 1] - assert reencryption_validator.functions.eqAffineJacobian(Q_affine, Q_jacobian).call() - - P_jacobian = [x, y, 1] - assert not reencryption_validator.functions.eqAffineJacobian(Q_affine, P_jacobian).call() - - point_at_infinity = [x, y, 0] - random_point = Point.gen_rand() - assert not reencryption_validator.functions.eqAffineJacobian(random_point.to_affine(), point_at_infinity).call() - - # Test doubleJacobian - doubleP = reencryption_validator.functions.doubleJacobian(P_jacobian).call() - assert reencryption_validator.functions.eqAffineJacobian((P + P).to_affine(), doubleP).call() - - # Test addAffineJacobian - scalar1 = CurveBN.gen_rand() - scalar2 = CurveBN.gen_rand() - R1 = scalar1 * P - R2 = scalar2 * P - - assert R1 + R2 == (scalar1 + scalar2) * P - R = reencryption_validator.functions.addAffineJacobian(R1.to_affine(), R2.to_affine()).call() - assert reencryption_validator.functions.eqAffineJacobian((R1 + R2).to_affine(), R).call() - - P_plus_P = reencryption_validator.functions.addAffineJacobian(P.to_affine(), P.to_affine()).call() - assert reencryption_validator.functions.eqAffineJacobian((P + P).to_affine(), P_plus_P).call() - - -def test_umbral_constants(testerchain, reencryption_validator): - umbral_params = default_params() - u_xcoord, u_ycoord = umbral_params.u.to_affine() - u_sign = 2 + (u_ycoord % 2) - assert u_sign == reencryption_validator.functions.UMBRAL_PARAMETER_U_SIGN().call() - assert u_xcoord == reencryption_validator.functions.UMBRAL_PARAMETER_U_XCOORD().call() - assert u_ycoord == reencryption_validator.functions.UMBRAL_PARAMETER_U_YCOORD().call() - - secp256k1_field_order = 2**256 - 0x1000003D1 - assert secp256k1_field_order == reencryption_validator.functions.FIELD_ORDER().call() - assert secp256k1_field_order - 2 == reencryption_validator.functions.MINUS_2().call() - assert (secp256k1_field_order - 1) // 2 == reencryption_validator.functions.MINUS_ONE_HALF().call() - - -def test_compute_proof_challenge_scalar(testerchain, reencryption_validator, mock_ursula_reencrypts, mocker): - ursula_privkey = UmbralPrivateKey.gen_key() - ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.pubkey, - signer=Signer(ursula_privkey)) - ursula = mocker.Mock(stamp=ursula_stamp, decentralized_identity_evidence=b'') - - # Bob prepares supporting Evidence - evidence = mock_ursula_reencrypts(ursula) - capsule = evidence.task.capsule - cfrag = evidence.task.cfrag - capsule_bytes = capsule.to_bytes() - cfrag_bytes = cfrag.to_bytes() - proof_challenge_scalar = int(evidence.get_proof_challenge_scalar()) - computeProofChallengeScalar = reencryption_validator.functions.computeProofChallengeScalar - assert proof_challenge_scalar == computeProofChallengeScalar(capsule_bytes, cfrag_bytes).call() - - -def test_validate_cfrag(testerchain, reencryption_validator, mock_ursula_reencrypts, mocker): - ursula_privkey = UmbralPrivateKey.gen_key() - ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.pubkey, - signer=Signer(ursula_privkey)) - ursula = mocker.Mock(stamp=ursula_stamp, decentralized_identity_evidence=b'') - - ############################### - # Test: Ursula produces correct proof: - ############################### - - # Bob prepares supporting Evidence - evidence = mock_ursula_reencrypts(ursula) - evidence_data = evidence.precompute_values() - assert len(evidence_data) == 20 * 32 + 32 + 20 + 5 - - # Challenge using good data - capsule = evidence.task.capsule - cfrag = evidence.task.cfrag - capsule_bytes = capsule.to_bytes() - cfrag_bytes = cfrag.to_bytes() - args = (capsule_bytes, cfrag_bytes, evidence_data) - assert reencryption_validator.functions.validateCFrag(*args).call() - - ############################### - # Test: Ursula produces incorrect proof: - ############################### - evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True) - capsule = evidence.task.capsule - cfrag = evidence.task.cfrag - capsule_bytes = capsule.to_bytes() - cfrag_bytes = cfrag.to_bytes() - assert not cfrag.verify_correctness(capsule) - - evidence_data = evidence.precompute_values() - args = (capsule_bytes, cfrag_bytes, evidence_data) - assert not reencryption_validator.functions.validateCFrag(*args).call() - - ############################### - # Test: Bob produces wrong precomputed data - ############################### - evidence = mock_ursula_reencrypts(ursula) - capsule = evidence.task.capsule - cfrag = evidence.task.cfrag - capsule_bytes = capsule.to_bytes() - cfrag_bytes = cfrag.to_bytes() - assert cfrag.verify_correctness(capsule) - - evidence_data = evidence.precompute_values() - - # 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 = (capsule_bytes, cfrag_bytes, evidence_data) - - # Evaluation must fail since Bob precomputed wrong values - with pytest.raises(TransactionFailed): - _ = reencryption_validator.functions.validateCFrag(*args).call() diff --git a/tests/contracts/main/adjudicator/conftest.py b/tests/contracts/main/adjudicator/conftest.py deleted file mode 100644 index 78837cbc1..000000000 --- a/tests/contracts/main/adjudicator/conftest.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -This file is part of nucypher. - -nucypher is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -nucypher is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with nucypher. If not, see . -""" - -import pytest -from web3.contract import Contract - -from tests.constants import INSECURE_DEVELOPMENT_PASSWORD - - -@pytest.fixture() -def escrow(testerchain, deploy_contract): - escrow, _ = deploy_contract('StakingEscrowForAdjudicatorMock') - return escrow - - -@pytest.fixture(params=[False, True]) -def adjudicator(testerchain, escrow, request, token_economics, deploy_contract): - contract, _ = deploy_contract( - 'Adjudicator', - escrow.address, - *token_economics.slashing_deployment_parameters) - - if request.param: - dispatcher, _ = deploy_contract('Dispatcher', contract.address) - - # Deploy second version of the government contract - contract = testerchain.client.get_contract( - abi=contract.abi, - address=dispatcher.address, - ContractFactoryClass=Contract) - - return contract diff --git a/tests/contracts/main/adjudicator/test_adjudicator.py b/tests/contracts/main/adjudicator/test_adjudicator.py deleted file mode 100644 index 828fffdfd..000000000 --- a/tests/contracts/main/adjudicator/test_adjudicator.py +++ /dev/null @@ -1,487 +0,0 @@ -""" -This file is part of nucypher. - -nucypher is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -nucypher is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with nucypher. If not, see . -""" - - -import os -import pytest -from eth_tester.exceptions import TransactionFailed -from typing import Tuple -from umbral.keys import UmbralPrivateKey -from umbral.point import Point -from web3.contract import Contract - -from nucypher.crypto.api import sha256_digest - -ALGORITHM_KECCAK256 = 0 -ALGORITHM_SHA256 = 1 - - -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] = bytes(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'] - - -def test_upgrading(testerchain, deploy_contract): - creator = testerchain.client.accounts[0] - - # Only escrow contract is allowed in Adjudicator constructor - with pytest.raises((TransactionFailed, ValueError)): - deploy_contract('Adjudicator', creator, ALGORITHM_KECCAK256, 1, 2, 3, 4) - - # Deploy contracts - escrow1, _ = deploy_contract('StakingEscrowForAdjudicatorMock') - escrow2, _ = deploy_contract('StakingEscrowForAdjudicatorMock') - address1 = escrow1.address - address2 = escrow2.address - contract_library_v1, _ = deploy_contract( - 'Adjudicator', address1, ALGORITHM_KECCAK256, 1, 2, 3, 4) - dispatcher, _ = deploy_contract('Dispatcher', contract_library_v1.address) - - # Deploy second version of the contract - contract_library_v2, _ = deploy_contract( - 'AdjudicatorV2Mock', address2, ALGORITHM_SHA256, 5, 6, 7, 8) - contract = testerchain.client.get_contract( - abi=contract_library_v2.abi, - address=dispatcher.address, - ContractFactoryClass=Contract) - - # Can't call `finishUpgrade` and `verifyState` methods outside upgrade lifecycle - with pytest.raises((TransactionFailed, ValueError)): - tx = contract_library_v1.functions.finishUpgrade(contract.address).transact({'from': creator}) - testerchain.wait_for_receipt(tx) - with pytest.raises((TransactionFailed, ValueError)): - tx = contract_library_v1.functions.verifyState(contract.address).transact({'from': creator}) - testerchain.wait_for_receipt(tx) - - # Upgrade to the second version - assert address1 == contract.functions.escrow().call() - assert ALGORITHM_KECCAK256 == contract.functions.hashAlgorithm().call() - assert 1 == contract.functions.basePenalty().call() - assert 2 == contract.functions.penaltyHistoryCoefficient().call() - assert 3 == contract.functions.percentagePenaltyCoefficient().call() - assert 4 == contract.functions.rewardCoefficient().call() - tx = dispatcher.functions.upgrade(contract_library_v2.address).transact({'from': creator}) - testerchain.wait_for_receipt(tx) - # Check constructor and storage values - assert contract_library_v2.address == dispatcher.functions.target().call() - assert address2 == contract.functions.escrow().call() - assert ALGORITHM_SHA256 == contract.functions.hashAlgorithm().call() - assert 5 == contract.functions.basePenalty().call() - assert 6 == contract.functions.penaltyHistoryCoefficient().call() - assert 7 == contract.functions.percentagePenaltyCoefficient().call() - assert 8 == contract.functions.rewardCoefficient().call() - # Check new ABI - tx = contract.functions.setValueToCheck(3).transact({'from': creator}) - testerchain.wait_for_receipt(tx) - assert 3 == contract.functions.valueToCheck().call() - - # Can't upgrade to the previous version or to the bad version - contract_library_bad, _ = deploy_contract('AdjudicatorBad') - with pytest.raises(TransactionFailed): - tx = dispatcher.functions.upgrade(contract_library_v1.address).transact({'from': creator}) - testerchain.wait_for_receipt(tx) - with pytest.raises(TransactionFailed): - tx = dispatcher.functions.upgrade(contract_library_bad.address).transact({'from': creator}) - testerchain.wait_for_receipt(tx) - - # But can rollback - tx = dispatcher.functions.rollback().transact({'from': creator}) - testerchain.wait_for_receipt(tx) - assert contract_library_v1.address == dispatcher.functions.target().call() - assert address1 == contract.functions.escrow().call() - assert ALGORITHM_KECCAK256 == contract.functions.hashAlgorithm().call() - assert 1 == contract.functions.basePenalty().call() - assert 2 == contract.functions.penaltyHistoryCoefficient().call() - assert 3 == contract.functions.percentagePenaltyCoefficient().call() - assert 4 == contract.functions.rewardCoefficient().call() - # After rollback new ABI is unavailable - with pytest.raises(TransactionFailed): - tx = contract.functions.setValueToCheck(2).transact({'from': creator}) - testerchain.wait_for_receipt(tx) - - # Try to upgrade to the bad version - with pytest.raises(TransactionFailed): - tx = dispatcher.functions.upgrade(contract_library_bad.address).transact({'from': creator}) - testerchain.wait_for_receipt(tx) - - events = dispatcher.events.StateVerified.createFilter(fromBlock=0).get_all_entries() - assert 4 == len(events) - event_args = events[0]['args'] - assert contract_library_v1.address == event_args['testTarget'] - assert creator == event_args['sender'] - event_args = events[1]['args'] - assert contract_library_v2.address == event_args['testTarget'] - assert creator == event_args['sender'] - assert event_args == events[2]['args'] - event_args = events[3]['args'] - assert contract_library_v2.address == event_args['testTarget'] - assert creator == event_args['sender'] - - events = dispatcher.events.UpgradeFinished.createFilter(fromBlock=0).get_all_entries() - assert 3 == len(events) - event_args = events[0]['args'] - assert contract_library_v1.address == event_args['target'] - assert creator == event_args['sender'] - event_args = events[1]['args'] - assert contract_library_v2.address == event_args['target'] - assert creator == event_args['sender'] - event_args = events[2]['args'] - assert contract_library_v1.address == event_args['target'] - assert creator == event_args['sender'] diff --git a/tests/metrics/estimate_gas.py b/tests/metrics/estimate_gas.py index dfc826515..494259bdc 100755 --- a/tests/metrics/estimate_gas.py +++ b/tests/metrics/estimate_gas.py @@ -52,11 +52,6 @@ from nucypher.policy.policies import Policy from nucypher.utilities.logging import Logger from tests.utils.blockchain import TesterBlockchain -try: - from tests.utils.ursula import _mock_ursula_reencrypts as mock_ursula_reencrypts -except ImportError: - raise DevelopmentInstallationRequired(importable_name='tests.utils.ursula') - ALGORITHM_SHA256 = 1 TOKEN_ECONOMICS = StandardTokenEconomics() @@ -146,12 +141,6 @@ def mock_ursula(testerchain, account): return ursula -def generate_args_for_slashing(ursula, corrupt_cfrag: bool = True): - evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=corrupt_cfrag) - args = list(evidence.evaluation_arguments()) - return args - - def estimate_gas(analyzer: AnalyzeGas = None) -> None: """ Execute a linear sequence of NyCypher transactions mimicking @@ -520,45 +509,8 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None: testerchain.time_travel(periods=1) transact(staker_functions.commitToNextPeriod(), {'from': staker1}) - # - # Slashing tests - # testerchain.time_travel(periods=1) - # - # Slashing - # - slashing_args = generate_args_for_slashing(ursula_with_stamp) - transact_and_log("Slash just value", adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1}) - - deposit = staker_functions.stakerInfo(staker1).call()[0] - unlocked = deposit - staker_functions.getLockedTokens(staker1, 0).call() - transact(staker_functions.withdraw(unlocked), {'from': staker1}) - - sub_stakes_length = str(staker_functions.getSubStakesLength(staker1).call()) - slashing_args = generate_args_for_slashing(ursula_with_stamp) - transact_and_log("Slashing one sub stake and saving old one (" + sub_stakes_length + " sub stakes), 1st", - adjudicator_functions.evaluateCFrag(*slashing_args), - {'from': alice1}) - - sub_stakes_length = str(staker_functions.getSubStakesLength(staker1).call()) - slashing_args = generate_args_for_slashing(ursula_with_stamp) - transact_and_log("Slashing one sub stake and saving old one (" + sub_stakes_length + " sub stakes), 2nd", - adjudicator_functions.evaluateCFrag(*slashing_args), - {'from': alice1}) - - sub_stakes_length = str(staker_functions.getSubStakesLength(staker1).call()) - slashing_args = generate_args_for_slashing(ursula_with_stamp) - transact_and_log("Slashing one sub stake and saving old one (" + sub_stakes_length + " sub stakes), 3rd", - adjudicator_functions.evaluateCFrag(*slashing_args), - {'from': alice1}) - - sub_stakes_length = str(staker_functions.getSubStakesLength(staker1).call()) - slashing_args = generate_args_for_slashing(ursula_with_stamp) - transact_and_log("Slashing two sub stakes and saving old one (" + sub_stakes_length + " sub stakes)", - adjudicator_functions.evaluateCFrag(*slashing_args), - {'from': alice1}) - for index in range(18): transact(staker_functions.commitToNextPeriod(), {'from': staker1}) testerchain.time_travel(periods=1) @@ -568,21 +520,6 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None: unlocked = deposit - staker_functions.getLockedTokens(staker1, 1).call() transact(staker_functions.withdraw(unlocked), {'from': staker1}) - sub_stakes_length = str(staker_functions.getSubStakesLength(staker1).call()) - slashing_args = generate_args_for_slashing(ursula_with_stamp) - transact_and_log("Slashing two sub stakes, shortest and new one (" + sub_stakes_length + " sub stakes)", - adjudicator_functions.evaluateCFrag(*slashing_args), - {'from': alice1}) - - sub_stakes_length = str(staker_functions.getSubStakesLength(staker1).call()) - slashing_args = generate_args_for_slashing(ursula_with_stamp) - transact_and_log("Slashing three sub stakes, two shortest and new one (" + sub_stakes_length + " sub stakes)", - adjudicator_functions.evaluateCFrag(*slashing_args), - {'from': alice1}) - - slashing_args = generate_args_for_slashing(ursula_with_stamp, corrupt_cfrag=False) - transact_and_log("Evaluating correct CFrag", adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1}) - transact_and_log("Prolong stake", staker_functions.prolongStake(0, 20), {'from': staker1}) transact_and_log("Merge sub-stakes", staker_functions.mergeStake(2, 3), {'from': staker1}) diff --git a/tests/unit/crypto/test_coordinates_serialization.py b/tests/unit/crypto/test_coordinates_serialization.py deleted file mode 100644 index 55c3f0c8f..000000000 --- a/tests/unit/crypto/test_coordinates_serialization.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -This file is part of nucypher. - -nucypher is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -nucypher is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with nucypher. If not, see . -""" - -import pytest -from umbral.keys import UmbralPrivateKey - -from nucypher.crypto.signing import SignatureStamp -from nucypher.crypto.utils import get_coordinates_as_bytes - - -def test_coordinates_as_bytes(): - pubkey = UmbralPrivateKey.gen_key().pubkey - point = pubkey.point_key - stamp = SignatureStamp(verifying_key=pubkey) - - x, y = point.to_affine() - x = x.to_bytes(32, 'big') - y = y.to_bytes(32, 'big') - - for p in (point, pubkey, stamp): - assert get_coordinates_as_bytes(p) == x + y - assert get_coordinates_as_bytes(p, x_coord=False) == y - assert get_coordinates_as_bytes(p, y_coord=False) == x - with pytest.raises(ValueError): - _ = get_coordinates_as_bytes(p, x_coord=False, y_coord=False) diff --git a/tests/unit/crypto/test_umbral_signatures.py b/tests/unit/crypto/test_umbral_signatures.py deleted file mode 100644 index 415a82082..000000000 --- a/tests/unit/crypto/test_umbral_signatures.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -This file is part of nucypher. - -nucypher is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -nucypher is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with nucypher. If not, see . -""" -import pytest -from cryptography.hazmat.backends.openssl import backend -from cryptography.hazmat.primitives import hashes -from umbral.keys import UmbralPrivateKey - -from nucypher.crypto.api import ecdsa_sign, verify_ecdsa -from nucypher.crypto.signing import Signature, Signer -from nucypher.crypto.utils import get_signature_recovery_value, recover_pubkey_from_signature - - -def test_signature_can_verify(): - privkey = UmbralPrivateKey.gen_key() - message = b"peace at dawn" - der_sig_bytes = ecdsa_sign(message, privkey) - assert verify_ecdsa(message, der_sig_bytes, privkey.get_pubkey()) - signature = Signature.from_bytes(der_sig_bytes, der_encoded=True) - assert signature.verify(message, privkey.get_pubkey()) - - -def test_signature_rs_serialization(): - privkey = UmbralPrivateKey.gen_key() - message = b"peace at dawn" - der_sig_bytes = ecdsa_sign(message, privkey) - - signature_from_der = Signature.from_bytes(der_sig_bytes, der_encoded=True) - rs_sig_bytes = bytes(signature_from_der) - assert len(rs_sig_bytes) == 64 - - signature_from_rs = Signature.from_bytes(rs_sig_bytes, der_encoded=False) - - assert signature_from_rs == signature_from_der - assert signature_from_rs == der_sig_bytes - assert signature_from_rs.verify(message, privkey.get_pubkey()) - - -@pytest.mark.parametrize('execution_number', range(100)) # Run this test 100 times. -def test_ecdsa_signature_recovery(execution_number): - privkey = UmbralPrivateKey.gen_key() - pubkey = privkey.get_pubkey() - signer = Signer(private_key=privkey) - message = b"peace at dawn" - signature = signer(message=message) - - assert signature.verify(message, pubkey) - - v_value = 27 - pubkey_bytes = recover_pubkey_from_signature(message=message, - signature=signature, - v_value_to_try=v_value) - if not pubkey_bytes == pubkey.to_bytes(): - v_value = 28 - pubkey_bytes = recover_pubkey_from_signature(message=message, - signature=signature, - v_value_to_try=v_value) - - assert pubkey_bytes == pubkey.to_bytes() - assert bytes([v_value - 27]) == get_signature_recovery_value(message, signature, pubkey) - - hash_function = hashes.Hash(hashes.SHA256(), backend=backend) - hash_function.update(message) - prehashed_message = hash_function.finalize() - - v_value = 27 - pubkey_bytes = recover_pubkey_from_signature(message=prehashed_message, - signature=signature, - v_value_to_try=v_value, - is_prehashed=True) - if not pubkey_bytes == pubkey.to_bytes(): - v_value = 28 - pubkey_bytes = recover_pubkey_from_signature(message=prehashed_message, - signature=signature, - v_value_to_try=v_value, - is_prehashed=True) - assert pubkey_bytes == pubkey.to_bytes() - assert bytes([v_value - 27]) == get_signature_recovery_value(prehashed_message, - signature, - pubkey, - is_prehashed=True) diff --git a/tests/unit/test_work_orders.py b/tests/unit/test_work_orders.py index 4d01da9d7..989bef26a 100644 --- a/tests/unit/test_work_orders.py +++ b/tests/unit/test_work_orders.py @@ -43,8 +43,9 @@ def ursula(mocker): def test_pre_task(mock_ursula_reencrypts, ursula, get_random_checksum_address): identity_evidence = ursula.decentralized_identity_evidence - evidence = mock_ursula_reencrypts(ursula) - capsule = evidence.task.capsule + task = mock_ursula_reencrypts(ursula) + cfrag = task.cfrag + capsule = task.capsule capsule_bytes = capsule.to_bytes() signature = ursula.stamp(capsule_bytes) @@ -61,7 +62,6 @@ def test_pre_task(mock_ursula_reencrypts, ursula, get_random_checksum_address): assert signature == deserialized_task.signature # Attaching cfrags to the task - cfrag = evidence.task.cfrag cfrag_bytes = bytes(VariableLengthBytestring(cfrag.to_bytes())) cfrag_signature = ursula.stamp(cfrag_bytes) @@ -101,7 +101,7 @@ def test_work_order_with_multiple_capsules(mock_ursula_reencrypts, federated_alice, number): - tasks = [mock_ursula_reencrypts(ursula).task for _ in range(number)] + tasks = [mock_ursula_reencrypts(ursula) for _ in range(number)] material = [(task.capsule, task.signature, task.cfrag, task.cfrag_signature) for task in tasks] capsules, signatures, cfrags, cfrag_signatures = zip(*material) diff --git a/tests/utils/ursula.py b/tests/utils/ursula.py index b6f680fad..5f4334c0b 100644 --- a/tests/utils/ursula.py +++ b/tests/utils/ursula.py @@ -21,7 +21,6 @@ import socket from cryptography.x509 import Certificate from typing import Iterable, List, Optional, Set from umbral import pre -from umbral.curvebn import CurveBN from umbral.keys import UmbralPrivateKey from umbral.signing import Signer @@ -31,7 +30,7 @@ from nucypher.characters.lawful import Bob from nucypher.characters.lawful import Ursula from nucypher.config.characters import UrsulaConfiguration from nucypher.crypto.utils import canonical_address_from_umbral_key -from nucypher.policy.collections import WorkOrder, IndisputableEvidence +from nucypher.policy.collections import WorkOrder from tests.constants import NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK from tests.mock.datastore import MOCK_DB @@ -165,7 +164,7 @@ MOCK_KNOWN_URSULAS_CACHE = dict() MOCK_URSULA_STARTING_PORT = 51000 # select_test_port() -def _mock_ursula_reencrypts(ursula, corrupt_cfrag: bool = False): +def _mock_ursula_reencrypts(ursula): delegating_privkey = UmbralPrivateKey.gen_key() _symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey()) signing_privkey = UmbralPrivateKey.gen_key() @@ -199,15 +198,7 @@ def _mock_ursula_reencrypts(ursula, corrupt_cfrag: bool = False): metadata = bytes(ursula.stamp(task_signature)) cfrag = pre.reencrypt(kfrags[0], capsule, metadata=metadata) - - if corrupt_cfrag: - cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve) - cfrag_signature = ursula.stamp(bytes(cfrag)) bob = Bob.from_public_keys(verifying_key=pub_key_bob) - task = WorkOrder.PRETask(capsule, task_signature, cfrag, cfrag_signature) - work_order = WorkOrder(bob, None, alice_address, {capsule: task}, None, ursula, blockhash) - - evidence = IndisputableEvidence(task, work_order) - return evidence + return WorkOrder.PRETask(capsule, task_signature, cfrag, cfrag_signature)