Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 9 additions & 20 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,29 +831,18 @@ async def neurons(

Understanding the distribution and status of neurons within a subnet is key to comprehending the network's decentralized structure and the dynamics of its consensus and governance processes.
"""
if not block_hash:
if reuse_block:
block_hash = self.substrate.last_block_hash
else:
block_hash = await self.substrate.get_chain_head()

neurons_lite, weights, bonds = await asyncio.gather(
self.neurons_lite(netuid=netuid, block_hash=block_hash),
self.weights(netuid=netuid, block_hash=block_hash),
self.bonds(netuid=netuid, block_hash=block_hash),
hex_bytes_result = await self.query_runtime_api(
runtime_api="NeuronInfoRuntimeApi",
method="get_neurons",
params=[netuid],
block_hash=block_hash,
reuse_block=reuse_block,
)

weights_as_dict = {uid: w for uid, w in weights}
bonds_as_dict = {uid: b for uid, b in bonds}

neurons = [
NeuronInfo.from_weights_bonds_and_neuron_lite(
neuron_lite, weights_as_dict, bonds_as_dict
)
for neuron_lite in neurons_lite
]
if hex_bytes_result is None:
return []

return neurons
return NeuronInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result))

async def neurons_lite(
self, netuid: int, block_hash: Optional[str] = None, reuse_block: bool = False
Expand Down
54 changes: 54 additions & 0 deletions bittensor/core/chain_data/neuron_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,57 @@ def from_vec_u8(cls, vec_u8: bytes) -> "NeuronInfo":
),
is_null=False,
)

@classmethod
def list_from_vec_u8(cls, vec_u8: bytes) -> list["NeuronInfo"]:
nn = bt_decode.NeuronInfo.decode_vec(bytes(vec_u8))

def fix(n):
stake_dict = process_stake_data(n.stake)
total_stake = sum(stake_dict.values()) if stake_dict else Balance(0)
axon_info = n.axon_info
coldkey = decode_account_id(n.coldkey)
hotkey = decode_account_id(n.hotkey)
return NeuronInfo(
hotkey=hotkey,
coldkey=coldkey,
uid=n.uid,
netuid=n.netuid,
active=n.active,
stake=total_stake,
stake_dict=stake_dict,
total_stake=total_stake,
rank=u16_normalized_float(n.rank),
emission=n.emission / 1e9,
incentive=u16_normalized_float(n.incentive),
consensus=u16_normalized_float(n.consensus),
trust=u16_normalized_float(n.trust),
validator_trust=u16_normalized_float(n.validator_trust),
dividends=u16_normalized_float(n.dividends),
last_update=n.last_update,
validator_permit=n.validator_permit,
weights=[[e[0], e[1]] for e in n.weights],
bonds=[[e[0], e[1]] for e in n.bonds],
pruning_score=n.pruning_score,
prometheus_info=PrometheusInfo(
block=n.prometheus_info.block,
version=n.prometheus_info.version,
ip=str(netaddr.IPAddress(n.prometheus_info.ip)),
port=n.prometheus_info.port,
ip_type=n.prometheus_info.ip_type,
),
axon_info=AxonInfo(
version=axon_info.version,
ip=str(netaddr.IPAddress(axon_info.ip)),
port=axon_info.port,
ip_type=axon_info.ip_type,
placeholder1=axon_info.placeholder1,
placeholder2=axon_info.placeholder2,
protocol=axon_info.protocol,
hotkey=hotkey,
coldkey=coldkey,
),
is_null=False,
)

return [fix(n) for n in nn]
49 changes: 20 additions & 29 deletions tests/unit_tests/test_async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -992,41 +992,32 @@ async def test_neurons(subtensor, mocker):
# Preps
fake_netuid = 1
fake_block_hash = "block_hash"
fake_neurons = [mocker.Mock(), mocker.Mock()]
fake_weights = [(1, [(10, 20), (30, 40)]), (2, [(50, 60), (70, 80)])]
fake_bonds = [(1, [(10, 20), (30, 40)]), (2, [(50, 60), (70, 80)])]

mocked_neurons_lite = mocker.AsyncMock(return_value=fake_neurons)
subtensor.neurons_lite = mocked_neurons_lite

mocked_weights = mocker.AsyncMock(return_value=fake_weights)
subtensor.weights = mocked_weights

mocked_bonds = mocker.AsyncMock(return_value=fake_bonds)
subtensor.bonds = mocked_bonds
fake_reuse_block_hash = True

mocked_neuron_info_method = mocker.Mock()
async_subtensor.NeuronInfo.from_weights_bonds_and_neuron_lite = (
mocked_neuron_info_method
mocked_query_runtime_api = mocker.patch.object(
subtensor, "query_runtime_api", return_value="NOT NONE"
)
mocked_hex_to_bytes = mocker.patch.object(async_subtensor, "hex_to_bytes")
mocked_neuron_info_list_from_vec_u8 = mocker.patch.object(
async_subtensor.NeuronInfo, "list_from_vec_u8"
)

# Call
result = await subtensor.neurons(netuid=fake_netuid, block_hash=fake_block_hash)
result = await subtensor.neurons(
netuid=fake_netuid,
block_hash=fake_block_hash,
reuse_block=fake_reuse_block_hash,
)

# Asserts
mocked_neurons_lite.assert_awaited_once()
mocked_neurons_lite.assert_called_once_with(
netuid=fake_netuid, block_hash=fake_block_hash
)
mocked_weights.assert_awaited_once()
mocked_weights.assert_called_once_with(
netuid=fake_netuid, block_hash=fake_block_hash
mocked_query_runtime_api.assert_called_once_with(
runtime_api="NeuronInfoRuntimeApi",
method="get_neurons",
params=[fake_netuid],
block_hash=fake_block_hash,
reuse_block=fake_reuse_block_hash,
)
mocked_bonds.assert_awaited_once()
mocked_bonds.assert_called_once_with(netuid=fake_netuid, block_hash=fake_block_hash)
assert result == [
mocked_neuron_info_method.return_value for _ in range(len(fake_neurons))
]
mocked_hex_to_bytes.assert_called_once_with(mocked_query_runtime_api.return_value)
assert result == mocked_neuron_info_list_from_vec_u8.return_value


@pytest.mark.parametrize(
Expand Down