mirror of https://github.com/nucypher/nucypher.git
parent
edb95407c3
commit
6cd4d2fde3
|
@ -817,11 +817,16 @@ class Learner:
|
|||
|
||||
except node.NotStaking:
|
||||
self.log.warn(f'Verification Failed - '
|
||||
f'{node} has no active stakes in the current period ({self.staking_agent.get_current_period()}')
|
||||
f'{node} has no active stakes in the current period '
|
||||
f'({self.staking_agent.get_current_period()}')
|
||||
|
||||
except node.InvalidWalletSignature:
|
||||
except node.InvalidWorkerSignature:
|
||||
self.log.warn(f'Verification Failed - '
|
||||
f'{node} has an invalid wallet signature for {node.checksum_public_address}')
|
||||
f'{node} has an invalid wallet signature for {node.decentralized_identity_evidence}')
|
||||
|
||||
except node.DetachedWorker:
|
||||
self.log.warn(f'Verification Failed - '
|
||||
f'{node} is not bonded to a Staker.')
|
||||
|
||||
except node.InvalidNode:
|
||||
self.log.warn(node.invalid_metadata_message.format(node))
|
||||
|
@ -916,12 +921,15 @@ class Teacher:
|
|||
class StampNotSigned(InvalidStamp):
|
||||
"""Raised when a node does not have a stamp signature when one is required for verification"""
|
||||
|
||||
class InvalidWalletSignature(InvalidStamp):
|
||||
"""Raised when a stamp fails signature verification or recovers an unexpected wallet address"""
|
||||
class InvalidWorkerSignature(InvalidStamp):
|
||||
"""Raised when a stamp fails signature verification or recovers an unexpected worker address"""
|
||||
|
||||
class NotStaking(InvalidStamp):
|
||||
"""Raised when a node fails verification because it is not currently staking"""
|
||||
|
||||
class DetachedWorker(InvalidNode):
|
||||
"""Raised when a node fails verification because it is not bonded to a Staker"""
|
||||
|
||||
class WrongMode(TypeError):
|
||||
"""Raised when a Character tries to use another Character as decentralized when the latter is federated_only."""
|
||||
|
||||
|
@ -973,26 +981,33 @@ class Teacher:
|
|||
# Stamp
|
||||
#
|
||||
|
||||
def _stamp_has_valid_wallet_signature(self) -> bool:
|
||||
"""Off-chain Signature Verification of ethereum client signature of stamp"""
|
||||
def _stamp_has_valid_signature_by_worker(self) -> bool:
|
||||
"""Off-chain Signature Verification of stamp signature by Worker's ETH account"""
|
||||
if self.__decentralized_identity_evidence is NOT_SIGNED:
|
||||
return False
|
||||
signature_is_valid = verify_eip_191(message=bytes(self.stamp),
|
||||
signature=self.__decentralized_identity_evidence,
|
||||
address=self.checksum_address)
|
||||
address=self.worker_address)
|
||||
return signature_is_valid
|
||||
|
||||
def _is_valid_worker(self) -> bool:
|
||||
def _worker_is_bonded_to_staker(self) -> bool:
|
||||
"""
|
||||
This method assumes the stamp's signature is valid and accurate.
|
||||
As a follow-up, validate the Staker and Worker on-chain.
|
||||
As a follow-up, this checks that the worker is linked to a staker, but it may be
|
||||
the case that the "staker" isn't "staking" (e.g., all her tokens have been slashed).
|
||||
"""
|
||||
staker_address = self.staking_agent.get_staker_from_worker(worker_address=self.worker_address)
|
||||
return staker_address == self.checksum_address
|
||||
|
||||
TODO: #1033 - Verify Staker <-> Worker relationship on-chain
|
||||
def _staker_is_really_staking(self) -> bool:
|
||||
"""
|
||||
This method assumes the stamp's signature is valid and accurate.
|
||||
As a follow-up, this checks that the staker is, indeed, staking.
|
||||
"""
|
||||
locked_tokens = self.staking_agent.get_locked_tokens(staker_address=self.checksum_address)
|
||||
return locked_tokens > 0
|
||||
|
||||
def validate_stamp(self, verify_staking: bool = True) -> None:
|
||||
def validate_worker(self, verify_staking: bool = True) -> None:
|
||||
|
||||
# Federated
|
||||
if self.federated_only:
|
||||
|
@ -1008,12 +1023,15 @@ class Teacher:
|
|||
raise self.StampNotSigned
|
||||
|
||||
# Off-chain signature verification
|
||||
if not self._stamp_has_valid_wallet_signature():
|
||||
raise self.InvalidWalletSignature
|
||||
if not self._stamp_has_valid_signature_by_worker():
|
||||
raise self.InvalidWorkerSignature
|
||||
|
||||
# On-chain staking check
|
||||
if verify_staking:
|
||||
if self._is_valid_worker(): # <-- Blockchain CALL
|
||||
if not self._worker_is_bonded_to_staker(): # <-- Blockchain CALL
|
||||
raise self.DetachedWorker
|
||||
|
||||
if self._staker_is_really_staking(): # <-- Blockchain CALL
|
||||
self.verified_worker = True
|
||||
else:
|
||||
raise self.NotStaking
|
||||
|
@ -1034,7 +1052,7 @@ class Teacher:
|
|||
|
||||
# Offline check of valid stamp signature by worker
|
||||
try:
|
||||
self.validate_stamp(verify_staking=verify_staking)
|
||||
self.validate_worker(verify_staking=verify_staking)
|
||||
except self.WrongMode:
|
||||
if not accept_federated_only:
|
||||
raise
|
||||
|
|
|
@ -26,7 +26,7 @@ from nucypher.crypto.api import verify_eip_191
|
|||
from nucypher.crypto.powers import SigningPower, CryptoPower
|
||||
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
|
||||
from nucypher.utilities.sandbox.middleware import MockRestMiddleware
|
||||
from nucypher.utilities.sandbox.ursula import make_federated_ursulas
|
||||
from nucypher.utilities.sandbox.ursula import make_federated_ursulas, make_decentralized_ursulas
|
||||
|
||||
|
||||
def test_new_federated_ursula_announces_herself(ursula_federated_test_config):
|
||||
|
@ -51,16 +51,29 @@ def test_new_federated_ursula_announces_herself(ursula_federated_test_config):
|
|||
assert ursula_in_a_house in ursula_with_a_mouse.known_nodes
|
||||
|
||||
|
||||
def test_stakers_bond_to_ursulas(testerchain, stakers, ursula_decentralized_test_config):
|
||||
|
||||
ursulas = make_decentralized_ursulas(ursula_config=ursula_decentralized_test_config,
|
||||
stakers_addresses=testerchain.stakers_accounts,
|
||||
workers_addresses=testerchain.ursulas_accounts,
|
||||
confirm_activity=False)
|
||||
|
||||
assert len(ursulas) == len(stakers)
|
||||
for ursula in ursulas:
|
||||
ursula.validate_worker(verify_staking=True)
|
||||
assert ursula.verified_worker
|
||||
|
||||
|
||||
def test_blockchain_ursula_substantiates_stamp(blockchain_ursulas):
|
||||
first_ursula = list(blockchain_ursulas)[0]
|
||||
signature_as_bytes = first_ursula.decentralized_identity_evidence
|
||||
signature_as_bytes = to_standard_signature_bytes(signature_as_bytes)
|
||||
assert verify_eip_191(address=first_ursula.checksum_address,
|
||||
assert verify_eip_191(address=first_ursula.worker_address,
|
||||
message=bytes(first_ursula.stamp),
|
||||
signature=signature_as_bytes)
|
||||
|
||||
# This method is a shortcut for the above.
|
||||
assert first_ursula._stamp_has_valid_wallet_signature
|
||||
assert first_ursula._stamp_has_valid_signature_by_worker()
|
||||
|
||||
|
||||
def test_blockchain_ursula_verifies_stamp(blockchain_ursulas):
|
||||
|
@ -68,7 +81,7 @@ def test_blockchain_ursula_verifies_stamp(blockchain_ursulas):
|
|||
|
||||
# This Ursula does not yet have a verified stamp
|
||||
first_ursula.verified_stamp = False
|
||||
first_ursula.validate_stamp()
|
||||
first_ursula.validate_worker()
|
||||
|
||||
# ...but now it's verified.
|
||||
assert first_ursula.verified_stamp
|
||||
|
@ -89,7 +102,7 @@ def test_vladimir_cannot_verify_interface_with_ursulas_signing_key(blockchain_ur
|
|||
# Vladimir can substantiate the stamp using his own ether address...
|
||||
vladimir.substantiate_stamp(client_password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||
vladimir._is_valid_worker = lambda: True
|
||||
vladimir.validate_stamp()
|
||||
vladimir.validate_worker()
|
||||
|
||||
# Now, even though his public signing key matches Ursulas...
|
||||
assert vladimir.stamp == his_target.stamp
|
||||
|
|
Loading…
Reference in New Issue