mirror of https://github.com/nucypher/nucypher.git
formalizes FerveoKeyMismatch includes acceptance test for mismatch conditions; updates mocks to handle key mismatch
parent
a5f876bc48
commit
7263001db6
|
@ -57,6 +57,7 @@ from nucypher.blockchain.eth.utils import (
|
|||
rpc_endpoint_health_check,
|
||||
truncate_checksum_address,
|
||||
)
|
||||
from nucypher.crypto.ferveo.exceptions import FerveoKeyMismatch
|
||||
from nucypher.crypto.powers import (
|
||||
CryptoPower,
|
||||
RitualisticPower,
|
||||
|
@ -1029,7 +1030,7 @@ class Operator(BaseActor):
|
|||
onchain_key=onchain_ferveo_key,
|
||||
)
|
||||
self.log.critical(message)
|
||||
raise self.ActorError(message)
|
||||
raise FerveoKeyMismatch(message)
|
||||
|
||||
else:
|
||||
emitter.message(
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class FerveoKeyMismatch(Exception):
|
||||
"""
|
||||
Raised when a local ferveo public key does not match the
|
||||
public key published to the Coordinator.
|
||||
"""
|
|
@ -0,0 +1,131 @@
|
|||
import pytest
|
||||
import pytest_twisted
|
||||
from twisted.logger import globalLogPublisher
|
||||
|
||||
from nucypher.blockchain.eth.signers import InMemorySigner
|
||||
from nucypher.crypto.keypairs import RitualisticKeypair
|
||||
from nucypher.crypto.powers import RitualisticPower
|
||||
from nucypher.utilities.warnings import render_ferveo_key_mismatch_warning
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def ritual_id():
|
||||
return 0
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def dkg_size():
|
||||
return 4
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def duration():
|
||||
return 48 * 60 * 60
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def plaintext():
|
||||
return "peace at dawn"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def interval(testerchain):
|
||||
return testerchain.tx_machine._task.interval
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def signer():
|
||||
return InMemorySigner()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def cohort(testerchain, clock, coordinator_agent, ursulas, dkg_size):
|
||||
nodes = list(sorted(ursulas[:dkg_size], key=lambda x: int(x.checksum_address, 16)))
|
||||
assert len(nodes) == dkg_size
|
||||
for node in nodes:
|
||||
node.ritual_tracker.task._task.clock = clock
|
||||
node.ritual_tracker.start()
|
||||
return nodes
|
||||
|
||||
|
||||
@pytest_twisted.inlineCallbacks
|
||||
def test_dkg_failure_with_ferveo_key_mismatch(
|
||||
coordinator_agent,
|
||||
ritual_id,
|
||||
cohort,
|
||||
clock,
|
||||
interval,
|
||||
testerchain,
|
||||
initiator,
|
||||
global_allow_list,
|
||||
duration,
|
||||
accounts,
|
||||
ritual_token,
|
||||
):
|
||||
|
||||
bad_ursula = cohort[0]
|
||||
old_public_key = bad_ursula.public_keys(RitualisticPower)
|
||||
new_keypair = RitualisticKeypair()
|
||||
new_public_key = new_keypair.pubkey
|
||||
|
||||
bad_ursula._crypto_power._CryptoPower__power_ups[RitualisticPower].keypair = (
|
||||
new_keypair
|
||||
)
|
||||
|
||||
assert bytes(old_public_key) != bytes(new_public_key)
|
||||
assert bytes(old_public_key) != bytes(bad_ursula.public_keys(RitualisticPower))
|
||||
assert bytes(new_public_key) == bytes(bad_ursula.public_keys(RitualisticPower))
|
||||
|
||||
onchain_public_key = coordinator_agent.get_provider_public_key(
|
||||
ritual_id=ritual_id, provider=bad_ursula.checksum_address
|
||||
)
|
||||
|
||||
assert bytes(onchain_public_key) == bytes(old_public_key)
|
||||
assert bytes(onchain_public_key) != bytes(new_public_key)
|
||||
assert bytes(onchain_public_key) != bytes(bad_ursula.public_keys(RitualisticPower))
|
||||
print(f"BAD URSULA: {bad_ursula.checksum_address}")
|
||||
|
||||
print("==================== INITIALIZING ====================")
|
||||
|
||||
cohort_staking_provider_addresses = list(u.checksum_address for u in cohort)
|
||||
|
||||
# Approve the ritual token for the coordinator agent to spend
|
||||
amount = coordinator_agent.get_ritual_initiation_cost(
|
||||
providers=cohort_staking_provider_addresses, duration=duration
|
||||
)
|
||||
ritual_token.approve(
|
||||
coordinator_agent.contract_address,
|
||||
amount,
|
||||
sender=accounts[initiator.transacting_power.account],
|
||||
)
|
||||
|
||||
receipt = coordinator_agent.initiate_ritual(
|
||||
providers=cohort_staking_provider_addresses,
|
||||
authority=initiator.transacting_power.account,
|
||||
duration=duration,
|
||||
access_controller=global_allow_list.address,
|
||||
transacting_power=initiator.transacting_power,
|
||||
)
|
||||
|
||||
testerchain.time_travel(seconds=1)
|
||||
testerchain.wait_for_receipt(receipt["transactionHash"])
|
||||
|
||||
log_messages = []
|
||||
|
||||
def log_trapper(event):
|
||||
log_messages.append(event["log_format"])
|
||||
|
||||
globalLogPublisher.addObserver(log_trapper)
|
||||
|
||||
print("==================== AWAITING DKG FAILURE ====================")
|
||||
while len(log_messages) == 0:
|
||||
|
||||
yield clock.advance(interval)
|
||||
yield testerchain.time_travel(seconds=1)
|
||||
|
||||
assert log_messages[0] == render_ferveo_key_mismatch_warning(
|
||||
bytes(new_public_key), bytes(onchain_public_key)
|
||||
)
|
||||
|
||||
testerchain.tx_machine.stop()
|
||||
assert not testerchain.tx_machine.running
|
|
@ -6,6 +6,7 @@ from web3 import Web3
|
|||
from nucypher.blockchain.eth.actors import BaseActor, Operator
|
||||
from nucypher.blockchain.eth.clients import EthereumClient
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.crypto.powers import RitualisticPower
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
|
@ -117,6 +118,13 @@ def test_operator_block_until_ready_success(
|
|||
ursula.checksum_address,
|
||||
]
|
||||
|
||||
# mock key commitment
|
||||
mocker.patch.object(
|
||||
ursula.coordinator_agent,
|
||||
"get_provider_public_key",
|
||||
return_value=bytes(ursula.public_keys(RitualisticPower)),
|
||||
)
|
||||
|
||||
log_messages = []
|
||||
|
||||
def log_trapper(event):
|
||||
|
|
Loading…
Reference in New Issue