pyasic

Miner Network

A class to handle a network containing miners. Handles scanning and gets miners via MinerFactory.

Parameters:

Name Type Description Default
hosts List[IPv4Address]

A list of ipaddress.IPv4Address to be used when scanning.

required
Source code in pyasic/network/__init__.py
class MinerNetwork:
    """A class to handle a network containing miners. Handles scanning and gets miners via [`MinerFactory`][pyasic.miners.factory.MinerFactory].

    Parameters:
        hosts: A list of `ipaddress.IPv4Address` to be used when scanning.
    """

    def __init__(self, hosts: List[ipaddress.IPv4Address]):
        self.hosts = hosts

    def __len__(self):
        return len(self.hosts)

    @classmethod
    def from_list(cls, addresses: list) -> "MinerNetwork":
        """Parse a list of address constructors into a MinerNetwork.

        Parameters:
            addresses: A list of address constructors, such as `["10.1-2.1.1-50", "10.4.1-2.1-50"]`.
        """
        hosts = []
        for address in addresses:
            hosts = [*hosts, *cls.from_address(address).hosts]
        return cls(sorted(list(set(hosts))))

    @classmethod
    def from_address(cls, address: str) -> "MinerNetwork":
        """Parse an address constructor into a MinerNetwork.

        Parameters:
            address: An address constructor, such as `"10.1-2.1.1-50"`.
        """
        octets = address.split(".")
        if len(octets) > 4:
            raise ValueError("Too many octets in IP constructor.")
        if len(octets) < 4:
            raise ValueError("Too few octets in IP constructor.")
        return cls.from_octets(*octets)

    @classmethod
    def from_octets(
        cls, oct_1: str, oct_2: str, oct_3: str, oct_4: str
    ) -> "MinerNetwork":
        """Parse 4 octet constructors into a MinerNetwork.

        Parameters:
            oct_1: An octet constructor, such as `"10"`.
            oct_2: An octet constructor, such as `"1-2"`.
            oct_3: An octet constructor, such as `"1"`.
            oct_4: An octet constructor, such as `"1-50"`.
        """

        hosts = []

        oct_1_start, oct_1_end = compute_oct_range(oct_1)
        for oct_1_idx in range((abs(oct_1_end - oct_1_start)) + 1):
            oct_1_val = str(oct_1_idx + oct_1_start)

            oct_2_start, oct_2_end = compute_oct_range(oct_2)
            for oct_2_idx in range((abs(oct_2_end - oct_2_start)) + 1):
                oct_2_val = str(oct_2_idx + oct_2_start)

                oct_3_start, oct_3_end = compute_oct_range(oct_3)
                for oct_3_idx in range((abs(oct_3_end - oct_3_start)) + 1):
                    oct_3_val = str(oct_3_idx + oct_3_start)

                    oct_4_start, oct_4_end = compute_oct_range(oct_4)
                    for oct_4_idx in range((abs(oct_4_end - oct_4_start)) + 1):
                        oct_4_val = str(oct_4_idx + oct_4_start)

                        hosts.append(
                            ipaddress.ip_address(
                                ".".join([oct_1_val, oct_2_val, oct_3_val, oct_4_val])
                            )
                        )
        return cls(sorted(hosts))

    @classmethod
    def from_subnet(cls, subnet: str) -> "MinerNetwork":
        """Parse a subnet into a MinerNetwork.

        Parameters:
            subnet: A subnet string, such as `"10.0.0.1/24"`.
        """
        return cls(list(ipaddress.ip_network(subnet, strict=False).hosts()))

    async def scan(self) -> List[AnyMiner]:
        """Scan the network for miners.

        Returns:
            A list of found miners.
        """
        return await self.scan_network_for_miners()

    async def scan_network_for_miners(self) -> List[AnyMiner]:
        logging.debug(f"{self} - (Scan Network For Miners) - Scanning")

        miners = await asyncio.gather(
            *[self.ping_and_get_miner(host) for host in self.hosts]
        )

        # remove all None from the miner list
        miners = list(filter(None, miners))
        logging.debug(
            f"{self} - (Scan Network For Miners) - Found {len(miners)} miners"
        )

        # return the miner objects
        return miners

    async def scan_network_generator(self) -> AsyncIterator[AnyMiner]:
        """
        Scan the network for miners using an async generator.

        Returns:
             An asynchronous generator containing found miners.
        """
        # get the current event loop
        loop = asyncio.get_event_loop()

        # create a list of scan tasks
        miners = asyncio.as_completed(
            [loop.create_task(self.ping_and_get_miner(host)) for host in self.hosts]
        )
        for miner in miners:
            try:
                yield await miner
            except TimeoutError:
                yield None

    @staticmethod
    async def ping_and_get_miner(ip: ipaddress.ip_address) -> Union[None, AnyMiner]:
        try:
            return await ping_and_get_miner(ip)
        except ConnectionRefusedError:
            tasks = [ping_and_get_miner(ip, port=port) for port in [4028, 4029, 8889]]
            for miner in asyncio.as_completed(tasks):
                try:
                    return await miner
                except ConnectionRefusedError:
                    pass

from_address(address) classmethod

Parse an address constructor into a MinerNetwork.

Parameters:

Name Type Description Default
address str

An address constructor, such as "10.1-2.1.1-50".

required
Source code in pyasic/network/__init__.py
@classmethod
def from_address(cls, address: str) -> "MinerNetwork":
    """Parse an address constructor into a MinerNetwork.

    Parameters:
        address: An address constructor, such as `"10.1-2.1.1-50"`.
    """
    octets = address.split(".")
    if len(octets) > 4:
        raise ValueError("Too many octets in IP constructor.")
    if len(octets) < 4:
        raise ValueError("Too few octets in IP constructor.")
    return cls.from_octets(*octets)

from_list(addresses) classmethod

Parse a list of address constructors into a MinerNetwork.

Parameters:

Name Type Description Default
addresses list

A list of address constructors, such as ["10.1-2.1.1-50", "10.4.1-2.1-50"].

required
Source code in pyasic/network/__init__.py
@classmethod
def from_list(cls, addresses: list) -> "MinerNetwork":
    """Parse a list of address constructors into a MinerNetwork.

    Parameters:
        addresses: A list of address constructors, such as `["10.1-2.1.1-50", "10.4.1-2.1-50"]`.
    """
    hosts = []
    for address in addresses:
        hosts = [*hosts, *cls.from_address(address).hosts]
    return cls(sorted(list(set(hosts))))

from_octets(oct_1, oct_2, oct_3, oct_4) classmethod

Parse 4 octet constructors into a MinerNetwork.

Parameters:

Name Type Description Default
oct_1 str

An octet constructor, such as "10".

required
oct_2 str

An octet constructor, such as "1-2".

required
oct_3 str

An octet constructor, such as "1".

required
oct_4 str

An octet constructor, such as "1-50".

required
Source code in pyasic/network/__init__.py
@classmethod
def from_octets(
    cls, oct_1: str, oct_2: str, oct_3: str, oct_4: str
) -> "MinerNetwork":
    """Parse 4 octet constructors into a MinerNetwork.

    Parameters:
        oct_1: An octet constructor, such as `"10"`.
        oct_2: An octet constructor, such as `"1-2"`.
        oct_3: An octet constructor, such as `"1"`.
        oct_4: An octet constructor, such as `"1-50"`.
    """

    hosts = []

    oct_1_start, oct_1_end = compute_oct_range(oct_1)
    for oct_1_idx in range((abs(oct_1_end - oct_1_start)) + 1):
        oct_1_val = str(oct_1_idx + oct_1_start)

        oct_2_start, oct_2_end = compute_oct_range(oct_2)
        for oct_2_idx in range((abs(oct_2_end - oct_2_start)) + 1):
            oct_2_val = str(oct_2_idx + oct_2_start)

            oct_3_start, oct_3_end = compute_oct_range(oct_3)
            for oct_3_idx in range((abs(oct_3_end - oct_3_start)) + 1):
                oct_3_val = str(oct_3_idx + oct_3_start)

                oct_4_start, oct_4_end = compute_oct_range(oct_4)
                for oct_4_idx in range((abs(oct_4_end - oct_4_start)) + 1):
                    oct_4_val = str(oct_4_idx + oct_4_start)

                    hosts.append(
                        ipaddress.ip_address(
                            ".".join([oct_1_val, oct_2_val, oct_3_val, oct_4_val])
                        )
                    )
    return cls(sorted(hosts))

from_subnet(subnet) classmethod

Parse a subnet into a MinerNetwork.

Parameters:

Name Type Description Default
subnet str

A subnet string, such as "10.0.0.1/24".

required
Source code in pyasic/network/__init__.py
@classmethod
def from_subnet(cls, subnet: str) -> "MinerNetwork":
    """Parse a subnet into a MinerNetwork.

    Parameters:
        subnet: A subnet string, such as `"10.0.0.1/24"`.
    """
    return cls(list(ipaddress.ip_network(subnet, strict=False).hosts()))

scan() async

Scan the network for miners.

Returns:

Type Description
List[AnyMiner]

A list of found miners.

Source code in pyasic/network/__init__.py
async def scan(self) -> List[AnyMiner]:
    """Scan the network for miners.

    Returns:
        A list of found miners.
    """
    return await self.scan_network_for_miners()

scan_network_generator() async

Scan the network for miners using an async generator.

Returns:

Type Description
AsyncIterator[AnyMiner]

An asynchronous generator containing found miners.

Source code in pyasic/network/__init__.py
async def scan_network_generator(self) -> AsyncIterator[AnyMiner]:
    """
    Scan the network for miners using an async generator.

    Returns:
         An asynchronous generator containing found miners.
    """
    # get the current event loop
    loop = asyncio.get_event_loop()

    # create a list of scan tasks
    miners = asyncio.as_completed(
        [loop.create_task(self.ping_and_get_miner(host)) for host in self.hosts]
    )
    for miner in miners:
        try:
            yield await miner
        except TimeoutError:
            yield None