diff --git a/nucypher/characters/lawful.py b/nucypher/characters/lawful.py index d182e28ca..9c5ced2f2 100644 --- a/nucypher/characters/lawful.py +++ b/nucypher/characters/lawful.py @@ -81,6 +81,7 @@ from nucypher.blockchain.eth.registry import ( BaseContractRegistry, InMemoryContractRegistry, ) +from nucypher.blockchain.eth.signers import Signer from nucypher.blockchain.eth.signers.software import Web3Signer from nucypher.characters.banners import ( ALICE_BANNER, @@ -1457,9 +1458,14 @@ class Enrico: banner = ENRICO_BANNER - def __init__(self, encrypting_key: Union[PublicKey, DkgPublicKey]): + def __init__( + self, + encrypting_key: Union[PublicKey, DkgPublicKey], + signer: Optional[Signer] = None, + ): + self.signer = signer self.signing_power = SigningPower() - self._policy_pubkey = encrypting_key + self._encrypting_key = encrypting_key self.log = Logger(f"{self.__class__.__name__}-{encrypting_key}") self.log.info(self.banner.format(encrypting_key)) @@ -1477,20 +1483,27 @@ class Enrico: return message_kit def encrypt_for_dkg( - self, plaintext: bytes, conditions: Lingo + self, + plaintext: bytes, + conditions: Lingo, ) -> ThresholdMessageKit: + if not self.signer: + raise TypeError("This Enrico doesn't have a signer.") + validate_condition_lingo(conditions) conditions_json = json.dumps(conditions) access_conditions = Conditions(conditions_json) + # encrypt for DKG ciphertext, auth_data = encrypt_for_dkg( plaintext, self.policy_pubkey, access_conditions ) - # authentication for AllowLogic - # TODO Replace with `Signer` to be passed as parameter + # authentication message for TACo header_hash = keccak_digest(bytes(ciphertext.header)) - authorization = self.signing_power.keypair.sign(header_hash).to_be_bytes() + authorization = self.signer.sign_message( + message=header_hash, account=self.signer.accounts[0] + ) return ThresholdMessageKit( ciphertext=ciphertext, @@ -1509,8 +1522,8 @@ class Enrico: @property def policy_pubkey(self): - if not self._policy_pubkey: + if not self._encrypting_key: raise TypeError( "This Enrico doesn't know which policy encrypting key he used. Oh well." ) - return self._policy_pubkey + return self._encrypting_key diff --git a/tests/acceptance/actors/test_dkg_ritual.py b/tests/acceptance/actors/test_dkg_ritual.py index e5e45c662..b5a251333 100644 --- a/tests/acceptance/actors/test_dkg_ritual.py +++ b/tests/acceptance/actors/test_dkg_ritual.py @@ -2,6 +2,7 @@ import pytest import pytest_twisted from twisted.internet.threads import deferToThread +from nucypher.blockchain.eth.signers.software import Web3Signer from nucypher.blockchain.eth.trackers.dkg import EventScannerTask from nucypher.characters.lawful import Enrico from nucypher.policy.conditions.lingo import ConditionLingo @@ -134,9 +135,12 @@ def test_ursula_ritualist( # prepare message and conditions plaintext = PLAINTEXT.encode() + # create Enrico + signer = Web3Signer(client=testerchain.client) + enrico = Enrico(encrypting_key=encrypting_key, signer=signer) + # encrypt - # print(f'encrypting for DKG with key {bytes(encrypting_key.to_bytes()).hex()}') - enrico = Enrico(encrypting_key=encrypting_key) + print(f"encrypting for DKG with key {bytes(encrypting_key).hex()}") threshold_message_kit = enrico.encrypt_for_dkg( plaintext=plaintext, conditions=CONDITIONS ) diff --git a/tests/acceptance/characters/test_transacting_power.py b/tests/acceptance/characters/test_transacting_power.py index a9aa0368a..890b48f4f 100644 --- a/tests/acceptance/characters/test_transacting_power.py +++ b/tests/acceptance/characters/test_transacting_power.py @@ -60,6 +60,7 @@ def test_character_transacting_power_signing(testerchain, test_registry): restored_dict = restored_transaction.as_dict() assert to_checksum_address(restored_dict['to']) == transaction_dict['to'] + def test_transacting_power_sign_message(testerchain): # Manually create a TransactingPower diff --git a/tests/integration/blockchain/test_dkg_ritual.py b/tests/integration/blockchain/test_dkg_ritual.py index a052dc3e8..dd4145661 100644 --- a/tests/integration/blockchain/test_dkg_ritual.py +++ b/tests/integration/blockchain/test_dkg_ritual.py @@ -10,6 +10,7 @@ from twisted.internet.threads import deferToThread from web3.datastructures import AttributeDict from nucypher.blockchain.eth.agents import CoordinatorAgent +from nucypher.blockchain.eth.signers.software import Web3Signer from nucypher.characters.lawful import Enrico, Ursula from nucypher.policy.conditions.lingo import ConditionLingo from tests.constants import TESTERCHAIN_CHAIN_ID @@ -116,6 +117,7 @@ def execute_round_2(ritual_id: int, cohort: List[Ursula]): ursula.ritual_tracker._handle_ritual_event(event, get_block_when=lambda x: event) +@pytest.mark.usefixtures("mock_sign_message") @pytest.mark.parametrize('dkg_size, ritual_id, variant', PARAMS) @pytest_twisted.inlineCallbacks() def test_ursula_ritualist( @@ -195,9 +197,12 @@ def test_ursula_ritualist( # prepare message and conditions plaintext = PLAINTEXT.encode() + # create Enrico + signer = Web3Signer(client=testerchain.client) + enrico = Enrico(encrypting_key=encrypting_key, signer=signer) + # encrypt - # print(f'encrypting for DKG with key {bytes(encrypting_key.to_bytes()).hex()}') - enrico = Enrico(encrypting_key=encrypting_key) + print(f"encrypting for DKG with key {bytes(encrypting_key).hex()}") threshold_message_kit = enrico.encrypt_for_dkg( plaintext=plaintext, conditions=CONDITIONS ) diff --git a/tests/integration/characters/test_dkg_and_testnet_bypass.py b/tests/integration/characters/test_dkg_and_testnet_bypass.py index 8bebfbbb3..d42845d3d 100644 --- a/tests/integration/characters/test_dkg_and_testnet_bypass.py +++ b/tests/integration/characters/test_dkg_and_testnet_bypass.py @@ -1,5 +1,6 @@ import pytest +from nucypher.blockchain.eth.signers.software import Web3Signer from nucypher.characters.chaotic import ( NiceGuyEddie, ThisBobAlwaysDecrypts, @@ -14,10 +15,11 @@ from tests.constants import ( ) -def _attempt_decryption(BobClass, plaintext): +def _attempt_decryption(BobClass, plaintext, testerchain): trinket = 80 # Doens't matter. - enrico = NiceGuyEddie(encrypting_key=trinket) + signer = Web3Signer(client=testerchain.client) + enrico = NiceGuyEddie(encrypting_key=trinket, signer=signer) bob = BobClass( registry=MOCK_REGISTRY_FILEPATH, domain="lynx", @@ -46,13 +48,15 @@ def _attempt_decryption(BobClass, plaintext): return decrypted_cleartext -def test_user_controls_success(): +@pytest.mark.usefixtures("mock_sign_message") +def test_user_controls_success(testerchain): plaintext = b"ever thus to deadbeats" - result = _attempt_decryption(ThisBobAlwaysDecrypts, plaintext) + result = _attempt_decryption(ThisBobAlwaysDecrypts, plaintext, testerchain) assert bytes(result) == bytes(plaintext) -def test_user_controls_failure(): +@pytest.mark.usefixtures("mock_sign_message") +def test_user_controls_failure(testerchain): plaintext = b"ever thus to deadbeats" with pytest.raises(Ursula.NotEnoughUrsulas): - _ = _attempt_decryption(ThisBobAlwaysFails, plaintext) + _ = _attempt_decryption(ThisBobAlwaysFails, plaintext, testerchain) diff --git a/tests/integration/config/test_keystore_integration.py b/tests/integration/config/test_keystore_integration.py index 053360afc..7c993466a 100644 --- a/tests/integration/config/test_keystore_integration.py +++ b/tests/integration/config/test_keystore_integration.py @@ -151,6 +151,7 @@ def test_tls_hosting_certificate_remains_the_same(temp_dir_path, mocker): recreated_ursula.disenchant() +@pytest.mark.usefixtures("mock_sign_message") def test_ritualist(temp_dir_path, testerchain, dkg_public_key): keystore = Keystore.generate( password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=temp_dir_path @@ -186,11 +187,15 @@ def test_ritualist(temp_dir_path, testerchain, dkg_public_key): }, } + # create enrico + signer = Web3Signer(client=testerchain.client) + enrico = Enrico(encrypting_key=dkg_public_key, signer=signer) + # encrypt - enrico = Enrico(encrypting_key=dkg_public_key) threshold_message_kit = enrico.encrypt_for_dkg( plaintext=plaintext, conditions=CONDITIONS ) + decryption_request = ThresholdDecryptionRequest( ritual_id=ritual_id, variant=FerveoVariant.Simple, diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 5f6739c2f..1d16cab38 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,3 +1,4 @@ +import os from pathlib import Path from typing import Iterable, Optional @@ -20,6 +21,7 @@ from nucypher.blockchain.eth.interfaces import ( from nucypher.blockchain.eth.networks import NetworksInventory from nucypher.blockchain.eth.registry import InMemoryContractRegistry from nucypher.blockchain.eth.signers import KeystoreSigner +from nucypher.blockchain.eth.signers.software import Web3Signer from nucypher.characters.lawful import Ursula from nucypher.cli.types import ChecksumAddress from nucypher.config.characters import UrsulaConfiguration @@ -59,6 +61,14 @@ def mock_sample_reservoir(testerchain, mock_contract_agency): mock_agent.get_staking_provider_reservoir = mock_reservoir +@pytest.fixture(scope="function") +def mock_sign_message(mocker): + mocked_sign_message = mocker.patch.object( + Web3Signer, "sign_message", return_value=os.urandom(32) + ) + return mocked_sign_message + + @pytest.fixture(scope="function", autouse=True) def mock_application_agent( testerchain, application_economics, mock_contract_agency, mocker diff --git a/tests/mock/interfaces.py b/tests/mock/interfaces.py index a6add7ab0..c82599dc2 100644 --- a/tests/mock/interfaces.py +++ b/tests/mock/interfaces.py @@ -7,7 +7,7 @@ from typing import Union from hexbytes import HexBytes -from nucypher.blockchain.eth.clients import EthereumClient +from nucypher.blockchain.eth.clients import EthereumTesterClient from nucypher.blockchain.eth.networks import NetworksInventory from nucypher.blockchain.eth.registry import ( BaseContractRegistry, @@ -82,7 +82,7 @@ class MockBlockchain(TesterBlockchain): return self.FAKE_RECEIPT -class MockEthereumClient(EthereumClient): +class MockEthereumClient(EthereumTesterClient): def __init__(self, w3): super().__init__(w3=w3, node_technology=None, version=None, platform=None, backend=None)