Example #1
0
def _cli_args_to_boot_info_kwargs(args: argparse.Namespace) -> BootInfoKwargs:
    protocol_version = args.protocol_version

    is_ephemeral = args.ephemeral is True
    is_upnp_enabled = not args.disable_upnp

    if args.base_dir is not None:
        base_dir = args.base_dir.expanduser().resolve()
    elif is_ephemeral:
        base_dir = pathlib.Path(tempfile.TemporaryDirectory().name)
    else:
        base_dir = get_xdg_ddht_root()

    if args.port is not None:
        port = args.port
    elif is_ephemeral:
        port = get_open_port()
    else:
        port = DEFAULT_PORT

    listen_on: Optional[AnyIPAddress]

    if args.listen_address is None:
        listen_on = None
    else:
        listen_on = args.listen_address

    if args.bootnodes is None:
        if protocol_version is ProtocolVersion.v5:
            bootnodes = tuple(
                ENR.from_repr(enr_repr) for enr_repr in DEFAULT_V5_BOOTNODES)
        elif protocol_version is ProtocolVersion.v5_1:
            bootnodes = tuple(
                ENR.from_repr(enr_repr) for enr_repr in DEFAULT_V51_BOOTNODES)
        else:
            raise Exception(
                f"Unsupported protocol version: {protocol_version}")
    else:
        bootnodes = args.bootnodes

    private_key: Optional[keys.PrivateKey]

    if args.private_key is not None:
        private_key = keys.PrivateKey(decode_hex(args.private_key))
    else:
        private_key = None

    return BootInfoKwargs(
        protocol_version=protocol_version,
        base_dir=base_dir,
        port=port,
        listen_on=listen_on,
        bootnodes=bootnodes,
        private_key=private_key,
        is_ephemeral=is_ephemeral,
        is_upnp_enabled=is_upnp_enabled,
    )
Example #2
0
def test_extract_forkid():
    enr = ENR.from_repr(
        "enr:-Jq4QO5zEyIBU5lSa9iaen0A2xUB5_IVrCi1DbyASTTnLV5RJan6aGPr8kU0p0MYKU5YezZgdSUE"
        "-GOBEio6Ultyf1Aog2V0aMrJhGN2AZCDGfCggmlkgnY0gmlwhF4_wLuJc2VjcDI1NmsxoQOt7cA_B_Kg"
        "nQ5RmwyA6ji8M1Y0jfINItRGbOOwy7XgbIN0Y3CCdl-DdWRwgnZf")
    assert extract_forkid(enr) == ForkID(hash=to_bytes(hexstr='0x63760190'),
                                         next=1700000)
Example #3
0
def validate_and_extract_destination(
        value: Any) -> Tuple[NodeID, Optional[Endpoint]]:
    node_id: NodeID
    endpoint: Optional[Endpoint]

    if is_hex_node_id(value):
        node_id = NodeID(decode_hex(value))
        endpoint = None
    elif value.startswith("enode://"):
        raw_node_id, _, raw_endpoint = value[8:].partition("@")

        validate_hex_node_id(raw_node_id)
        validate_endpoint(raw_endpoint)

        node_id = NodeID(decode_hex(raw_node_id))

        raw_ip_address, _, raw_port = raw_endpoint.partition(":")
        ip_address = ipaddress.ip_address(raw_ip_address)
        port = int(raw_port)
        endpoint = Endpoint(ip_address.packed, port)
    elif value.startswith("enr:"):
        enr = ENR.from_repr(value)
        node_id = enr.node_id
        endpoint = Endpoint.from_enr(enr)
    else:
        raise RPCError(f"Unrecognized node identifier: {value}")

    return node_id, endpoint
async def test_v51_rpc_findNodes_w3(bob_node_id_param, bob, w3):
    distances = set()

    for _ in range(10):
        enr = ENRFactory()
        distances.add(compute_log_distance(bob.node_id, enr.node_id))
        bob.enr_db.set_enr(enr)

    # request with positional single distance
    enrs_at_0 = await trio.to_thread.run_sync(w3.discv5.find_nodes,
                                              bob_node_id_param, 0)
    assert all(isinstance(enr, ENR) for enr in enrs_at_0)

    # request with multiple distances
    enrs_at_some_distance = await trio.to_thread.run_sync(
        w3.discv5.find_nodes,
        bob_node_id_param,
        tuple(distances),
    )

    # verify that all of the returned ENR records can be parsed as valid ENRs
    for enr_repr in enrs_at_some_distance:
        ENR.from_repr(enr_repr)
Example #5
0
def _cli_args_to_boot_info_kwargs(
        args: argparse.Namespace) -> AlexandriaBootInfoKwargs:
    if args.alexandria_bootnodes is None:
        bootnodes = tuple(
            ENR.from_repr(enr_repr) for enr_repr in DEFAULT_BOOTNODES)
    else:
        bootnodes = args.alexandria_bootnodes

    max_advertisement_count: int

    if args.alexandria_max_advertisement_count is None:
        max_advertisement_count = DEFAULT_MAX_ADVERTISEMENTS
    else:
        max_advertisement_count = args.alexandria_max_advertisement_count

    commons_storage_size: int

    if args.alexandria_commons_storage_size is None:
        commons_storage_size = DEFAULT_COMMONS_STORAGE_SIZE
    else:
        commons_storage_size = args.alexandria_commons_storage_size

    commons_storage: Optional[Union[Literal[":memory:"], pathlib.Path]]

    if args.alexandria_commons_storage == ":memory:":
        commons_storage = ":memory:"
    elif args.alexandria_commons_storage is not None:
        commons_storage = (pathlib.Path(
            args.alexandria_commons_storage).expanduser().resolve())
    else:
        commons_storage = None

    pinned_storage: Optional[Union[Literal[":memory:"], pathlib.Path]]

    if args.alexandria_pinned_storage == ":memory:":
        pinned_storage = ":memory:"
    elif args.alexandria_pinned_storage is not None:
        pinned_storage = (pathlib.Path(
            args.alexandria_pinned_storage).expanduser().resolve())
    else:
        pinned_storage = None

    return AlexandriaBootInfoKwargs(
        bootnodes=bootnodes,
        max_advertisement_count=max_advertisement_count,
        commons_storage_size=commons_storage_size,
        commons_storage=commons_storage,
        pinned_storage=pinned_storage,
    )
async def test_v51_rpc_findNodes(make_request, bob_node_id_param, bob):
    distances = set()

    for _ in range(10):
        enr = ENRFactory()
        distances.add(compute_log_distance(bob.node_id, enr.node_id))
        bob.enr_db.set_enr(enr)

    # request with positional single distance
    enrs_at_0 = await make_request("discv5_findNodes", [bob_node_id_param, 0])

    # verify that all of the returned ENR records can be parsed as valid ENRs
    for enr_repr in enrs_at_0:
        ENR.from_repr(enr_repr)

    # request with multiple distances
    enrs_at_some_distance = await make_request(
        "discv5_findNodes",
        [bob_node_id_param, tuple(distances)],
    )

    # verify that all of the returned ENR records can be parsed as valid ENRs
    for enr_repr in enrs_at_some_distance:
        ENR.from_repr(enr_repr)
async def test_rpc_updateNodeInfo(make_request, enr_manager):
    # add a kv pair
    first_response = await make_request("discv5_updateNodeInfo",
                                        [("0xabcd", "0x1234")])
    first_enr = ENR.from_repr(first_response["enr"])
    assert first_enr.sequence_number == 2
    assert enr_manager.enr.sequence_number == 2
    assert first_enr._kv_pairs[b"\xab\xcd"] == b"\x124"
    assert enr_manager.enr._kv_pairs[b"\xab\xcd"] == b"\x124"

    # update a kv pair
    second_response = await make_request("discv5_updateNodeInfo",
                                         [("0xabcd", "0x6789")])
    second_enr = ENR.from_repr(second_response["enr"])
    assert second_enr._kv_pairs[b"\xab\xcd"] == b"g\x89"
    assert second_enr.sequence_number == 3

    # test with multiple kv_pairs, and remove 'foo' key
    third_response = await make_request("discv5_updateNodeInfo",
                                        [("0xabcd", None), ("0xdef1", 123)])
    third_enr = ENR.from_repr(third_response["enr"])
    assert b"\xab\xcd" not in third_enr._kv_pairs
    assert third_enr._kv_pairs[b"\xde\xf1"] == to_bytes(123)
    assert third_enr.sequence_number == 4
Example #8
0
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        value: Any,
        option_string: str = None,
    ) -> None:
        if value is None:
            return

        enr = ENR.from_repr(value)

        if getattr(namespace, self.dest) is None:
            setattr(namespace, self.dest, ())

        enr_list = getattr(namespace, self.dest)
        enr_list += (enr, )

        setattr(namespace, self.dest, enr_list)
Example #9
0
    def extract_params(
            self, request: RPCRequest) -> Tuple[NodeID, Optional[Endpoint]]:
        try:
            raw_params = request["params"]
        except KeyError as err:
            raise RPCError(f"Missiing call params: {err}")

        if len(raw_params) != 1:
            raise RPCError(f"`ddht_ping` endpoint expects a single parameter: "
                           f"Got {len(raw_params)} params: {raw_params}")

        value = raw_params[0]

        node_id: NodeID
        endpoint: Optional[Endpoint]

        if is_hex_node_id(value):
            node_id = NodeID(decode_hex(value))
            endpoint = None
        elif value.startswith("enode://"):
            raw_node_id, _, raw_endpoint = value[8:].partition("@")

            validate_hex_node_id(raw_node_id)
            validate_endpoint(raw_endpoint)

            node_id = NodeID(decode_hex(raw_node_id))

            raw_ip_address, _, raw_port = raw_endpoint.partition(":")
            ip_address = ipaddress.ip_address(raw_ip_address)
            port = int(raw_port)
            endpoint = Endpoint(ip_address.packed, port)
        elif value.startswith("enr:"):
            enr = ENR.from_repr(value)
            node_id = enr.node_id
            endpoint = Endpoint.from_enr(enr)
        else:
            raise RPCError(f"Unrecognized node identifier: {value}")

        return node_id, endpoint
async def test_v51_rpc_recursiveFindNodes(tester, bob, make_request):
    async with AsyncExitStack() as stack:
        await stack.enter_async_context(bob.network())
        bootnodes = collections.deque((bob.enr, ), maxlen=4)
        nodes = [bob]
        target_node_id = None
        for _ in range(8):
            node = tester.node()
            nodes.append(node)
            await stack.enter_async_context(node.network(bootnodes=bootnodes))
            bootnodes.append(node.enr)
            if (not target_node_id
                    and compute_log_distance(node.node_id, bob.node_id) < 256):
                target_node_id = node.node_id

        # give the the network some time to interconnect.
        with trio.fail_after(60):
            for _ in range(1000):
                await trio.lowlevel.checkpoint()

        await make_request("discv5_bond", [bob.node_id.hex()])

        try:
            with trio.fail_after(60):
                found_enrs = await make_request("discv5_recursiveFindNodes",
                                                [target_node_id.hex()])
        except trio.TooSlowError:
            # These tests are flakey.  Timeouts are expected in the testing
            # environment so we silently pass on timeouts.  This still allows
            # this test to provide some value in the case that a non-timeout
            # based error shows up.
            return

        found_enrs = tuple(
            ENR.from_repr(enr_repr).node_id for enr_repr in found_enrs)
        assert len(found_enrs) > 0
Example #11
0
def test_default_bootnodes_valid(enr_repr):
    enr = ENR.from_repr(enr_repr)
    assert b"ip" in enr or b"ip6" in enr
    assert b"udp" in enr
Example #12
0
from typing import Dict, Tuple

from eth_enr import ENR
import factory

from ddht.boot_info import BootInfo
from ddht.constants import DEFAULT_PORT, ProtocolVersion
from ddht.v5.constants import DEFAULT_BOOTNODES as DEFAULT_V5_BOOTNODES
from ddht.v5_1.constants import DEFAULT_BOOTNODES as DEFAULT_V51_BOOTNODES
from ddht.xdg import get_xdg_ddht_root

BOOTNODES_V5 = tuple(
    ENR.from_repr(enr_repr) for enr_repr in DEFAULT_V5_BOOTNODES)
BOOTNODES_V5_1 = tuple(
    ENR.from_repr(enr_repr) for enr_repr in DEFAULT_V51_BOOTNODES)

BOOTNODES: Dict[ProtocolVersion, Tuple[ENR, ...]] = {
    ProtocolVersion.v5: BOOTNODES_V5,
    ProtocolVersion.v5_1: BOOTNODES_V5_1,
}


class BootInfoFactory(factory.Factory):  # type: ignore
    class Meta:
        model = BootInfo

    protocol_version = ProtocolVersion.v5
    private_key = None
    base_dir = factory.LazyFunction(get_xdg_ddht_root)
    port = DEFAULT_PORT
    listen_on = None
Example #13
0
 def __setstate__(self, state: Dict[Any, Any]) -> None:
     self._init(ENR.from_repr(state.pop('enr')))
Example #14
0
         "request_id": 0x01,
         "total": 0x01,
         "enrs": []
     },
     decode_hex("0x04c30101c0"),
 ],
 [
     NodesMessage,
     {
         "request_id":
         0x01,
         "total":
         0x01,
         "enrs": [
             ENR.from_repr(
                 "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxa"  # noqa: E501
                 "agKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAP"  # noqa: E501
                 "MljNMTg"),
             ENR.from_repr(
                 "enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_"  # noqa: E501
                 "KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8x"  # noqa: E501
                 "fVw50jU"),
         ],
     },
     decode_hex(
         "0x04f8f20101f8eef875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc6"  # noqa: E501
         "55448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa532801826964827634897365"  # noqa: E501
         "63703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875"  # noqa: E501
         "b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b44"  # noqa: E501
         "5946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e"  # noqa: E501
         "2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235"
     ),
Example #15
0
 def from_rpc_response(cls, response: GetENRResponse) -> "GetENRPayload":
     return cls(enr=ENR.from_repr(response["enr_repr"]))
Example #16
0
 def from_rpc_response(cls, response: NodeInfoResponse) -> "NodeInfo":
     return cls(
         node_id=NodeID(decode_hex(response["node_id"])),
         enr=ENR.from_repr(response["enr"]),
     )
Example #17
0
 def from_rpc_response(cls,
                       response: NodeInfoResponse) -> "UpdateENRPayload":
     return cls(enr=ENR.from_repr(response["enr"]))
Example #18
0
 def from_enr_repr(cls: Type[TNode], uri: str) -> TNode:
     return cls(ENR.from_repr(uri))
Example #19
0
def find_nodes_response_formatter(
        enr_reprs: Sequence[str]) -> Tuple[ENRAPI, ...]:
    return tuple(ENR.from_repr(enr_repr) for enr_repr in enr_reprs)