def test_repr(mock_identity_scheme, identity_scheme_registry): unsigned_enr = UnsignedENR(0, {b"id": b"mock"}, identity_scheme_registry) enr = unsigned_enr.to_signed_enr(b"\x00" * 32) base64_encoded_enr = base64.urlsafe_b64encode(rlp.encode(enr)) represented_enr = repr(enr) assert represented_enr.startswith("enr:") assert base64_encoded_enr.rstrip(b"=").decode() == represented_enr[4:] assert ENR.from_repr(represented_enr, identity_scheme_registry) == enr
def test_signing(mock_identity_scheme, identity_scheme_registry): unsigned_enr = UnsignedENR( sequence_number=0, kv_pairs={b"id": b"mock"}, identity_scheme_registry=identity_scheme_registry, ) private_key = b"\x00" * 32 enr = unsigned_enr.to_signed_enr(private_key) assert enr.signature == mock_identity_scheme.create_enr_signature( enr, private_key)
def test_official_test_vector(): enr = ENR.from_repr( OFFICIAL_TEST_DATA["repr"]) # use default identity scheme registry assert enr.sequence_number == OFFICIAL_TEST_DATA["sequence_number"] assert dict(enr) == OFFICIAL_TEST_DATA["kv_pairs"] assert enr.public_key == OFFICIAL_TEST_DATA["public_key"] assert enr.node_id == OFFICIAL_TEST_DATA["node_id"] assert enr.identity_scheme is OFFICIAL_TEST_DATA["identity_scheme"] assert repr(enr) == OFFICIAL_TEST_DATA["repr"] unsigned_enr = UnsignedENR(enr.sequence_number, dict(enr)) reconstructed_enr = unsigned_enr.to_signed_enr( OFFICIAL_TEST_DATA["private_key"]) assert reconstructed_enr == enr
def update(self, *kv_pairs: ENR_KV) -> None: if not kv_pairs: return keys, values = tuple(zip(*kv_pairs)) if len(keys) != len(set(keys)): raise ValidationError("Duplicate keys found in: %s", keys) needs_update = any( ( # key needs to be deleted (value is None and key in self._enr) # key is not present or does not match the provided value or ( value is not None and (key not in self._enr or self._enr[key] != value) ) ) for key, value in kv_pairs ) if needs_update: merged_kv_pairs = { key: value for key, value in merge(dict(self._enr), dict(kv_pairs)).items() if value is not None } self._enr = UnsignedENR( sequence_number=self._enr.sequence_number + 1, kv_pairs=merged_kv_pairs, identity_scheme_registry=self._identity_scheme_registry, ).to_signed_enr(self._private_key.to_bytes()) self._enr_db.set_enr(self._enr) self.logger.info( "ENR Updated: seq=%d enr=%r", self.enr.sequence_number, self.enr )
class ENRFactory(factory.Factory): # type: ignore class Meta: model = ENR sequence_number = factory.Faker("pyint", min_value=0, max_value=100) kv_pairs = factory.LazyAttribute( lambda o: merge( { b"id": b"v4", b"secp256k1": keys.PrivateKey( o.private_key ).public_key.to_compressed_bytes(), b"ip": o.address.ip_packed, b"udp": o.address.udp_port, b"tcp": o.address.tcp_port, }, o.custom_kv_pairs, ) ) signature = factory.LazyAttribute( lambda o: UnsignedENR(o.sequence_number, o.kv_pairs) .to_signed_enr(o.private_key) .signature ) class Params: private_key = factory.Faker("binary", length=V4IdentityScheme.private_key_size) address = factory.SubFactory(AddressFactory) custom_kv_pairs: Dict[bytes, Any] = {}
def test_v4_structure_validation(invalid_kv_pairs, identity_scheme_registry): with pytest.raises(ValidationError): UnsignedENR( sequence_number=0, kv_pairs=invalid_kv_pairs, identity_scheme_registry=identity_scheme_registry, )
def test_inititialization(identity_scheme_registry): valid_sequence_number = 0 valid_kv_pairs = {b"id": b"mock"} valid_signature = b"" # signature is not validated during initialization assert UnsignedENR( sequence_number=valid_sequence_number, kv_pairs=valid_kv_pairs, identity_scheme_registry=identity_scheme_registry, ) assert ENR( sequence_number=valid_sequence_number, kv_pairs=valid_kv_pairs, signature=valid_signature, identity_scheme_registry=identity_scheme_registry, ) with pytest.raises(ValidationError): UnsignedENR( sequence_number=valid_sequence_number, kv_pairs={b"no-id": b""}, identity_scheme_registry=identity_scheme_registry, ) with pytest.raises(ValidationError): ENR( sequence_number=valid_sequence_number, kv_pairs={b"no-id": b""}, signature=valid_signature, identity_scheme_registry=identity_scheme_registry, ) with pytest.raises(ValidationError): UnsignedENR( sequence_number=-1, kv_pairs=valid_kv_pairs, identity_scheme_registry=identity_scheme_registry, ) with pytest.raises(ValidationError): ENR( sequence_number=-1, kv_pairs=valid_kv_pairs, signature=valid_signature, identity_scheme_registry=identity_scheme_registry, )
def deserialize( cls, serialized_enr: Sequence[bytes], identity_scheme_registry: IdentitySchemeRegistryAPI = default_id_scheme_registry, ) -> UnsignedENRAPI: from eth_enr.enr import UnsignedENR # noqa: F811 cls._validate_serialized_length(serialized_enr) sequence_number = big_endian_int.deserialize(serialized_enr[0]) kv_pairs = cls._deserialize_kv_pairs(serialized_enr) return UnsignedENR(sequence_number, kv_pairs, identity_scheme_registry)
def test_signature_validation(mock_identity_scheme, identity_scheme_registry): unsigned_enr = UnsignedENR(0, {b"id": b"mock"}, identity_scheme_registry) private_key = b"\x00" * 32 enr = unsigned_enr.to_signed_enr(private_key) enr.validate_signature() invalid_signature = b"\xff" * 64 invalid_enr = ENR( enr.sequence_number, dict(enr), invalid_signature, identity_scheme_registry=identity_scheme_registry, ) with pytest.raises(ValidationError): invalid_enr.validate_signature() with pytest.raises(UnknownIdentityScheme): ENR( 0, {b"id": b"unknown"}, b"", identity_scheme_registry=identity_scheme_registry, )
def __init__( self, private_key: keys.PrivateKey, enr_db: ENRDatabaseAPI, kv_pairs: Optional[Mapping[bytes, bytes]] = None, identity_scheme_registry: IdentitySchemeRegistryAPI = default_identity_scheme_registry, # noqa: E501 ) -> None: self._identity_scheme_registry = identity_scheme_registry self._private_key = private_key self._enr_db = enr_db if kv_pairs is None: kv_pairs = {} if b"id" in kv_pairs: identity_kv_pairs = {} else: identity_kv_pairs = { b"id": b"v4", b"secp256k1": self._private_key.public_key.to_compressed_bytes(), } minimal_enr = UnsignedENR( sequence_number=1, kv_pairs=merge(identity_kv_pairs, kv_pairs), identity_scheme_registry=self._identity_scheme_registry, ).to_signed_enr(self._private_key.to_bytes()) self._node_id = minimal_enr.node_id try: base_enr = self._enr_db.get_enr(minimal_enr.node_id) except KeyError: self.logger.info( "ENR created: seq=%d enr=%r", minimal_enr.sequence_number, minimal_enr, ) self._enr = minimal_enr self._enr_db.set_enr(self._enr) else: self._enr = base_enr self.update(*tuple(kv_pairs.items()))
def test_node_id(mock_identity_scheme, identity_scheme_registry): unsigned_enr = UnsignedENR(0, {b"id": b"mock"}, identity_scheme_registry) private_key = b"\x00" * 32 enr = unsigned_enr.to_signed_enr(private_key) assert enr.node_id == private_key
def test_public_key(mock_identity_scheme, identity_scheme_registry): unsigned_enr = UnsignedENR(0, {b"id": b"mock"}, identity_scheme_registry) private_key = b"\x00" * 32 enr = unsigned_enr.to_signed_enr(private_key) assert enr.public_key == mock_identity_scheme.extract_public_key(enr)