Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
b3781bb
Uses a different asyncio loop caller.
thewhaleking Jan 15, 2025
1701d7c
Applies EventLoopManager broadly, adds factory functions for creating…
thewhaleking Jan 15, 2025
ba23119
Renaming
thewhaleking Jan 15, 2025
f79f77b
Pass the EventLoopManager to substrate
thewhaleking Jan 15, 2025
54591b9
Remove execute_coroutine import
thewhaleking Jan 16, 2025
8ce006e
Remove attr
thewhaleking Jan 16, 2025
6801270
[WIP] Check-in
thewhaleking Jan 18, 2025
82fa80d
All non-extrinsics methods ported.
thewhaleking Jan 18, 2025
72b0f7d
Test stuff
thewhaleking Jan 21, 2025
7489e68
Serving extrinsic
thewhaleking Jan 21, 2025
b79ffad
Staking extrinsics
thewhaleking Jan 21, 2025
b2dea07
Registration extrinsics
thewhaleking Jan 21, 2025
428211a
Weights extrinsics
thewhaleking Jan 21, 2025
333ba83
Sets nest_asyncio as default off.
thewhaleking Jan 21, 2025
1a4dedd
Import order
thewhaleking Jan 21, 2025
8c796b5
Root extrinsics
thewhaleking Jan 22, 2025
fa84405
Commit reveal extrinsic
thewhaleking Jan 22, 2025
4be4592
Transfer extrinsic
thewhaleking Jan 22, 2025
7db23fc
Unstaking extrinsic
thewhaleking Jan 22, 2025
04eda9a
Cleanup
thewhaleking Jan 22, 2025
8f1d639
Move SubtensorMixin to types file to avoid import conflicts.
thewhaleking Jan 22, 2025
4acf20b
imports
thewhaleking Jan 22, 2025
1781e8b
Remove ujson
thewhaleking Jan 22, 2025
c812324
Apply new json generally.
thewhaleking Jan 22, 2025
57cc58e
test: fix async unittests
zyzniewski-reef Jan 23, 2025
0e72ec0
Metagraph/tests
thewhaleking Jan 23, 2025
3b498bc
Call function
thewhaleking Jan 23, 2025
f3558ec
Raise correct error
thewhaleking Jan 23, 2025
7ec2df3
Metagraph rewrite
thewhaleking Jan 23, 2025
9a83347
Metagraph rewrite part 2
thewhaleking Jan 23, 2025
74c8d3e
Serving tests
thewhaleking Jan 23, 2025
a7bf8da
Merge remote-tracking branch 'origin/staging-pre-merge-new-async' int…
thewhaleking Jan 23, 2025
35f1a12
E2E tests passing.
thewhaleking Jan 23, 2025
2eab16c
Unit tests
thewhaleking Jan 23, 2025
4fb0586
Revert "update `bittensor/core/extrinsics/serving.py` unit tests"
zyzniewski-reef Jan 23, 2025
dcc6e5b
Revert "update `bittensor/core/extrinsics/transfer.py` unit tests"
zyzniewski-reef Jan 23, 2025
e4cde0f
Revert "update `bittensor/core/extrinsics/set_weights.py` unit tests"
zyzniewski-reef Jan 23, 2025
d593bcf
Revert "update `bittensor/core/extrinsics/root.py` unit tests"
zyzniewski-reef Jan 23, 2025
7f64dd1
Revert "typo"
zyzniewski-reef Jan 23, 2025
57e92a7
Revert "ruff"
zyzniewski-reef Jan 23, 2025
901ad69
Revert "update `commit_weights.py` unit test"
zyzniewski-reef Jan 23, 2025
f674a01
Revert "update `commit_reveal` unit test"
zyzniewski-reef Jan 23, 2025
ef50ff5
Revert "update `registration.py` unit test"
zyzniewski-reef Jan 23, 2025
b9e70f0
test: fix old (sync subtensor) unittests
zyzniewski-reef Jan 24, 2025
2b747bc
fix `tests/integration_tests/test_metagraph_integration.py`
Jan 24, 2025
f546cd8
add `metagraph[chain_getBlockHash]` result
Jan 24, 2025
ababac2
fix `tests/integration_tests/test_subtensor_integration.py`
Jan 24, 2025
e841b41
improve helper to use with sync integration tests
Jan 24, 2025
b0eba57
test: fix extrinsics unittests
zyzniewski-reef Jan 24, 2025
ed9466e
Merge remote-tracking branch 'origin/test/async_unittests' into test/…
Jan 24, 2025
af26e98
ruff
Jan 24, 2025
28b3fdc
improve integration subtensor test
Jan 24, 2025
64065d2
remove comment
Jan 24, 2025
155949b
Merge pull request #2600 from opentensor/test/async_unittests
zyzniewski-reef Jan 24, 2025
59fc0b5
Mypy
thewhaleking Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
296 changes: 57 additions & 239 deletions bittensor/core/async_subtensor.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bittensor/core/axon.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import contextlib
import copy
import inspect
import json
import threading
import time
import traceback
Expand All @@ -15,6 +14,7 @@
from inspect import signature, Signature, Parameter
from typing import Any, Awaitable, Callable, Optional, Tuple

from async_substrate_interface.utils import json
import uvicorn
from bittensor_wallet import Wallet, Keypair
from fastapi import APIRouter, Depends, FastAPI
Expand Down
2 changes: 1 addition & 1 deletion bittensor/core/chain_data/axon_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
in the bittensor network.
"""

import json
from dataclasses import asdict, dataclass
from typing import Any, Union

from async_substrate_interface.utils import json
from bittensor.utils import networking
from bittensor.utils.btlogging import logging
from bittensor.utils.registration import torch, use_torch
Expand Down
10 changes: 9 additions & 1 deletion bittensor/core/dendrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from bittensor.core.settings import version_as_int
from bittensor.core.stream import StreamingSynapse
from bittensor.core.synapse import Synapse, TerminalInfo
from bittensor.utils import networking, event_loop_is_running
from bittensor.utils import networking
from bittensor.utils.btlogging import logging
from bittensor.utils.registration import torch, use_torch

Expand All @@ -31,6 +31,14 @@
DENDRITE_DEFAULT_ERROR = ("422", "Failed to parse response")


def event_loop_is_running():
try:
asyncio.get_running_loop()
return True
except RuntimeError:
return False


class DendriteMixin:
"""
The Dendrite class represents the abstracted implementation of a network client module.
Expand Down
8 changes: 8 additions & 0 deletions bittensor/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
ExtrinsicNotFound = ExtrinsicNotFound


class MaxSuccessException(Exception):
"""Raised when the POW Solver has reached the max number of successful solutions."""


class MaxAttemptsException(Exception):
"""Raised when the POW Solver has reached the max number of attempts."""


class ChainError(SubstrateRequestException):
"""Base error for any chain related errors."""

Expand Down
23 changes: 3 additions & 20 deletions bittensor/core/extrinsics/asyncex/commit_reveal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from numpy.typing import NDArray

from bittensor.core.settings import version_as_int
from bittensor.utils import format_error_message
from bittensor.utils.btlogging import logging
from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit

Expand All @@ -31,7 +30,7 @@ async def _do_commit_reveal_v3(
finalization.

Arguments:
subtensor: An instance of the Subtensor class.
subtensor: An instance of the AsyncSubtensor class.
wallet: Wallet An instance of the Wallet class containing the user's keypair.
netuid: int The network unique identifier.
commit bytes The commit data in bytes format.
Expand All @@ -57,26 +56,10 @@ async def _do_commit_reveal_v3(
"reveal_round": reveal_round,
},
)

extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call,
keypair=wallet.hotkey,
)

response = await subtensor.substrate.submit_extrinsic(
extrinsic=extrinsic,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
return await subtensor.sign_and_send_extrinsic(
call, wallet, wait_for_inclusion, wait_for_finalization, sign_with="hotkey"
)

if not wait_for_finalization and not wait_for_inclusion:
return True, "Not waiting for finalization or inclusion."

if await response.is_success:
return True, None

return False, format_error_message(await response.error_message)


async def commit_reveal_v3_extrinsic(
subtensor: "AsyncSubtensor",
Expand Down
114 changes: 41 additions & 73 deletions bittensor/core/extrinsics/asyncex/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,14 @@
import asyncio
from typing import Optional, Union, TYPE_CHECKING

from bittensor.utils import format_error_message
from bittensor.utils import unlock_key
from bittensor.utils.btlogging import logging
from bittensor.utils.registration import log_no_torch_error, create_pow_async
from bittensor.utils.registration import log_no_torch_error, create_pow_async, torch

if TYPE_CHECKING:
import torch
from bittensor_wallet import Wallet
from bittensor.core.async_subtensor import AsyncSubtensor
from bittensor.utils.registration.pow import POWSolution
else:
from bittensor.utils.registration.pow import LazyLoadedTorch

torch = LazyLoadedTorch()


class MaxSuccessException(Exception):
"""Raised when the POW Solver has reached the max number of successful solutions."""


class MaxAttemptsException(Exception):
"""Raised when the POW Solver has reached the max number of attempts."""


async def _do_burned_register(
Expand All @@ -40,21 +26,22 @@ async def _do_burned_register(
wallet: "Wallet",
wait_for_inclusion: bool = False,
wait_for_finalization: bool = True,
) -> tuple[bool, Optional[str]]:
) -> tuple[bool, str]:
"""
Performs a burned register extrinsic call to the Subtensor chain.

This method sends a registration transaction to the Subtensor blockchain using the burned register mechanism.

Args:
subtensor (bittensor.core.subtensor.Subtensor): Subtensor instance.
subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Subtensor instance.
netuid (int): The network unique identifier to register on.
wallet (bittensor_wallet.Wallet): The wallet to be registered.
wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False.
wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True.

Returns:
Tuple[bool, Optional[str]]: A tuple containing a boolean indicating success or failure, and an optional error message.
Tuple[bool, Optional[str]]: A tuple containing a boolean indicating success or failure, and an optional error
message.
"""

# create extrinsic call
Expand All @@ -66,26 +53,13 @@ async def _do_burned_register(
"hotkey": wallet.hotkey.ss58_address,
},
)
extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call, keypair=wallet.coldkey
)
response = await subtensor.substrate.submit_extrinsic(
extrinsic=extrinsic,
return await subtensor.sign_and_send_extrinsic(
call=call,
wallet=wallet,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

# We only wait here if we expect finalization.
if not wait_for_finalization and not wait_for_inclusion:
return True, None

# process if registration successful, try again if pow is still valid
if not await response.is_success:
return False, format_error_message(await response.error_message)
# Successful registration

return True, None


async def burned_register_extrinsic(
subtensor: "AsyncSubtensor",
Expand All @@ -97,19 +71,20 @@ async def burned_register_extrinsic(
"""Registers the wallet to chain by recycling TAO.

Args:
subtensor (bittensor.core.subtensor.Subtensor): Subtensor instance.
subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Subtensor instance.
wallet (bittensor.wallet): Bittensor wallet object.
netuid (int): The ``netuid`` of the subnet to register on.
wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning ``true``, or
returns ``false`` if the extrinsic fails to enter the block within the timeout.
wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning ``True``, or
returns ``False`` if the extrinsic fails to enter the block within the timeout.
wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning
``true``, or returns ``false`` if the extrinsic fails to be finalized within the timeout.
``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout.

Returns:
success (bool): Flag is ``true`` if extrinsic was finalized or uncluded in the block. If we did not wait for
finalization / inclusion, the response is ``true``.
success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for
finalization / inclusion, the response is ``True``.
"""
if not await subtensor.subnet_exists(netuid):
block_hash = await subtensor.substrate.get_chain_head()
if not await subtensor.subnet_exists(netuid, block_hash=block_hash):
logging.error(
f":cross_mark: [red]Failed error:[/red] subnet [blue]{netuid}[/blue] does not exist."
)
Expand All @@ -122,11 +97,17 @@ async def burned_register_extrinsic(
logging.info(
f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue][magenta] ...[/magenta]"
)
neuron = await subtensor.get_neuron_for_pubkey_and_subnet(
wallet.hotkey.ss58_address, netuid=netuid
)

old_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
# We could do this as_completed because we don't actually need old_balance and recycle
# if neuron is null, but the complexity isn't worth it considering the small performance
# gains we'd hypothetically receive in this situation
neuron, old_balance, recycle_amount = await asyncio.gather(
subtensor.get_neuron_for_pubkey_and_subnet(
wallet.hotkey.ss58_address, netuid=netuid, block_hash=block_hash
),
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash=block_hash),
subtensor.recycle(netuid=netuid, block_hash=block_hash),
)

if not neuron.is_null:
logging.info(":white_heavy_check_mark: [green]Already Registered[/green]")
Expand All @@ -136,9 +117,7 @@ async def burned_register_extrinsic(
logging.info(f"\t\tcoldkey: [blue]{neuron.coldkey}[/blue]")
return True

logging.info(":satellite: [magenta]Recycling TAO for Registration...[/magenta]")

recycle_amount = await subtensor.recycle(netuid=netuid)
logging.debug(":satellite: [magenta]Recycling TAO for Registration...[/magenta]")
logging.info(f"Recycling {recycle_amount} to register on subnet:{netuid}")

success, err_msg = await _do_burned_register(
Expand Down Expand Up @@ -196,7 +175,8 @@ async def _do_pow_register(

Returns:
success (bool): ``True`` if the extrinsic was included in a block.
error (Optional[str]): ``None`` on success or not waiting for inclusion/finalization, otherwise the error message.
error (Optional[str]): ``None`` on success or not waiting for inclusion/finalization, otherwise the error
message.
"""
# create extrinsic call
call = await subtensor.substrate.compose_call(
Expand All @@ -211,26 +191,13 @@ async def _do_pow_register(
"coldkey": wallet.coldkeypub.ss58_address,
},
)
extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call, keypair=wallet.hotkey
)
response = await subtensor.substrate.submit_extrinsic(
extrinsic=extrinsic,
return await subtensor.sign_and_send_extrinsic(
call=call,
wallet=wallet,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

# We only wait here if we expect finalization.
if not wait_for_finalization and not wait_for_inclusion:
return True, None

# process if registration successful, try again if pow is still valid
if not await response.is_success:
return False, format_error_message(error_message=await response.error_message)
# Successful registration
else:
return True, None


async def register_extrinsic(
subtensor: "AsyncSubtensor",
Expand Down Expand Up @@ -271,9 +238,9 @@ async def register_extrinsic(
`True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the
response is `True`.
"""

block_hash = await subtensor.substrate.get_chain_head()
logging.debug("[magenta]Checking subnet status... [/magenta]")
if not await subtensor.subnet_exists(netuid):
if not await subtensor.subnet_exists(netuid, block_hash=block_hash):
logging.error(
f":cross_mark: [red]Failed error:[/red] subnet [blue]{netuid}[/blue] does not exist."
)
Expand All @@ -283,8 +250,7 @@ async def register_extrinsic(
f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue] [magenta]...[/magenta]"
)
neuron = await subtensor.get_neuron_for_pubkey_and_subnet(
hotkey_ss58=wallet.hotkey.ss58_address,
netuid=netuid,
hotkey_ss58=wallet.hotkey.ss58_address, netuid=netuid, block_hash=block_hash
)

if not neuron.is_null:
Expand All @@ -296,7 +262,8 @@ async def register_extrinsic(
return True

logging.debug(
f"Registration hotkey: <blue>{wallet.hotkey.ss58_address}</blue>, <green>Public</green> coldkey: <blue>{wallet.coldkey.ss58_address}</blue> in the network: <blue>{subtensor.network}</blue>."
f"Registration hotkey: <blue>{wallet.hotkey.ss58_address}</blue>, <green>Public</green> coldkey: "
f"<blue>{wallet.coldkey.ss58_address}</blue> in the network: <blue>{subtensor.network}</blue>."
)

if not torch:
Expand Down Expand Up @@ -372,7 +339,8 @@ async def register_extrinsic(

if "HotKeyAlreadyRegisteredInSubNet" in err_msg:
logging.info(
f":white_heavy_check_mark: [green]Already Registered on subnet:[/green] [blue]{netuid}[/blue]."
f":white_heavy_check_mark: [green]Already Registered on subnet:[/green] "
f"[blue]{netuid}[/blue]."
)
return True
logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}")
Expand All @@ -399,13 +367,13 @@ async def register_extrinsic(
# Exited loop because pow is no longer valid.
logging.error("[red]POW is stale.[/red]")
# Try again.
# continue

if attempts < max_allowed_attempts:
# Failed registration, retry pow
attempts += 1
logging.error(
f":satellite: [magenta]Failed registration, retrying pow ...[/magenta] [blue]({attempts}/{max_allowed_attempts})[/blue]"
f":satellite: [magenta]Failed registration, retrying pow ...[/magenta] "
f"[blue]({attempts}/{max_allowed_attempts})[/blue]"
)
else:
# Failed to register after max attempts.
Expand Down
Loading