1
1
import asyncio
2
2
import copy
3
- from datetime import datetime , timezone
4
3
import ssl
4
+ from datetime import datetime , timezone
5
5
from functools import partial
6
6
from typing import Optional , Any , Union , Iterable , TYPE_CHECKING
7
7
8
8
import asyncstdlib as a
9
9
import numpy as np
10
10
import scalecodec
11
11
from async_substrate_interface import AsyncSubstrateInterface
12
+ from bittensor_commit_reveal import get_encrypted_commitment
12
13
from bittensor_wallet .utils import SS58_FORMAT
13
14
from numpy .typing import NDArray
14
15
from scalecodec import GenericCall
29
30
)
30
31
from bittensor .core .chain_data .chain_identity import ChainIdentity
31
32
from bittensor .core .chain_data .delegate_info import DelegatedInfo
32
- from bittensor .core .chain_data .utils import decode_metadata
33
+ from bittensor .core .chain_data .utils import (
34
+ decode_metadata ,
35
+ decode_revealed_commitment ,
36
+ decode_revealed_commitment_with_hotkey ,
37
+ )
33
38
from bittensor .core .config import Config
34
39
from bittensor .core .errors import ChainError , SubstrateRequestException
35
40
from bittensor .core .extrinsics .asyncex .commit_reveal import commit_reveal_v3_extrinsic
41
+ from bittensor .core .extrinsics .asyncex .move_stake import (
42
+ transfer_stake_extrinsic ,
43
+ swap_stake_extrinsic ,
44
+ move_stake_extrinsic ,
45
+ )
36
46
from bittensor .core .extrinsics .asyncex .registration import (
37
47
burned_register_extrinsic ,
38
48
register_extrinsic ,
39
49
register_subnet_extrinsic ,
40
50
set_subnet_identity_extrinsic ,
41
51
)
42
- from bittensor .core .extrinsics .asyncex .move_stake import (
43
- transfer_stake_extrinsic ,
44
- swap_stake_extrinsic ,
45
- move_stake_extrinsic ,
46
- )
47
52
from bittensor .core .extrinsics .asyncex .root import (
48
53
set_root_weights_extrinsic ,
49
54
root_register_extrinsic ,
79
84
decode_hex_identity_dict ,
80
85
float_to_u64 ,
81
86
format_error_message ,
87
+ is_valid_ss58_address ,
82
88
torch ,
83
89
u16_normalized_float ,
84
90
u64_normalized_float ,
@@ -1059,6 +1065,115 @@ async def get_all_commitments(
1059
1065
result [decode_account_id (id_ [0 ])] = decode_metadata (value )
1060
1066
return result
1061
1067
1068
+ async def get_revealed_commitment_by_hotkey (
1069
+ self ,
1070
+ netuid : int ,
1071
+ hotkey_ss58_address : Optional [str ] = None ,
1072
+ block : Optional [int ] = None ,
1073
+ block_hash : Optional [str ] = None ,
1074
+ reuse_block : bool = False ,
1075
+ ) -> Optional [tuple [tuple [int , str ], ...]]:
1076
+ """Returns hotkey related revealed commitment for a given netuid.
1077
+
1078
+ Arguments:
1079
+ netuid (int): The unique identifier of the subnetwork.
1080
+ block (Optional[int]): The block number to retrieve the commitment from. Default is ``None``.
1081
+ hotkey_ss58_address (str): The ss58 address of the committee member.
1082
+ block_hash (Optional[str]): The hash of the block to retrieve the subnet unique identifiers from.
1083
+ reuse_block (bool): Whether to reuse the last-used block hash.
1084
+
1085
+ Returns:
1086
+ result (tuple[int, str): A tuple of reveal block and commitment message.
1087
+ """
1088
+ if not is_valid_ss58_address (address = hotkey_ss58_address ):
1089
+ raise ValueError (f"Invalid ss58 address { hotkey_ss58_address } provided." )
1090
+
1091
+ query = await self .query_module (
1092
+ module = "Commitments" ,
1093
+ name = "RevealedCommitments" ,
1094
+ params = [netuid , hotkey_ss58_address ],
1095
+ block = block ,
1096
+ block_hash = block_hash ,
1097
+ reuse_block = reuse_block ,
1098
+ )
1099
+ if query is None :
1100
+ return None
1101
+ return tuple (decode_revealed_commitment (pair ) for pair in query )
1102
+
1103
+ async def get_revealed_commitment (
1104
+ self ,
1105
+ netuid : int ,
1106
+ uid : int ,
1107
+ block : Optional [int ] = None ,
1108
+ ) -> Optional [tuple [tuple [int , str ], ...]]:
1109
+ """Returns uid related revealed commitment for a given netuid.
1110
+
1111
+ Arguments:
1112
+ netuid (int): The unique identifier of the subnetwork.
1113
+ uid (int): The neuron uid to retrieve the commitment from.
1114
+ block (Optional[int]): The block number to retrieve the commitment from. Default is ``None``.
1115
+
1116
+ Returns:
1117
+ result (Optional[tuple[int, str]]: A tuple of reveal block and commitment message.
1118
+
1119
+ Example of result:
1120
+ ( (12, "Alice message 1"), (152, "Alice message 2") )
1121
+ ( (12, "Bob message 1"), (147, "Bob message 2") )
1122
+ """
1123
+ try :
1124
+ meta_info = await self .get_metagraph_info (netuid , block = block )
1125
+ if meta_info :
1126
+ hotkey_ss58_address = meta_info .hotkeys [uid ]
1127
+ else :
1128
+ raise ValueError (f"Subnet with netuid { netuid } does not exist." )
1129
+ except IndexError :
1130
+ raise ValueError (f"Subnet { netuid } does not have a neuron with uid { uid } ." )
1131
+
1132
+ return await self .get_revealed_commitment_by_hotkey (
1133
+ netuid = netuid , hotkey_ss58_address = hotkey_ss58_address , block = block
1134
+ )
1135
+
1136
+ async def get_all_revealed_commitments (
1137
+ self ,
1138
+ netuid : int ,
1139
+ block : Optional [int ] = None ,
1140
+ block_hash : Optional [str ] = None ,
1141
+ reuse_block : bool = False ,
1142
+ ) -> dict [str , tuple [tuple [int , str ], ...]]:
1143
+ """Returns all revealed commitments for a given netuid.
1144
+
1145
+ Arguments:
1146
+ netuid (int): The unique identifier of the subnetwork.
1147
+ block (Optional[int]): The block number to retrieve the commitment from. Default is ``None``.
1148
+ block_hash (Optional[str]): The hash of the block to retrieve the subnet unique identifiers from.
1149
+ reuse_block (bool): Whether to reuse the last-used block hash.
1150
+
1151
+ Returns:
1152
+ result (dict): A dictionary of all revealed commitments in view {ss58_address: (reveal block, commitment message)}.
1153
+
1154
+ Example of result:
1155
+ {
1156
+ "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY": ( (12, "Alice message 1"), (152, "Alice message 2") ),
1157
+ "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty": ( (12, "Bob message 1"), (147, "Bob message 2") ),
1158
+ }
1159
+ """
1160
+ query = await self .query_map (
1161
+ module = "Commitments" ,
1162
+ name = "RevealedCommitments" ,
1163
+ params = [netuid ],
1164
+ block = block ,
1165
+ block_hash = block_hash ,
1166
+ reuse_block = reuse_block ,
1167
+ )
1168
+
1169
+ result = {}
1170
+ async for pair in query :
1171
+ hotkey_ss58_address , commitment_message = (
1172
+ decode_revealed_commitment_with_hotkey (pair )
1173
+ )
1174
+ result [hotkey_ss58_address ] = commitment_message
1175
+ return result
1176
+
1062
1177
async def get_current_weight_commit_info (
1063
1178
self ,
1064
1179
netuid : int ,
@@ -1572,6 +1687,36 @@ async def get_neuron_for_pubkey_and_subnet(
1572
1687
reuse_block = reuse_block ,
1573
1688
)
1574
1689
1690
+ async def get_owned_hotkeys (
1691
+ self ,
1692
+ coldkey_ss58 : str ,
1693
+ block : Optional [int ] = None ,
1694
+ block_hash : Optional [str ] = None ,
1695
+ reuse_block : bool = False ,
1696
+ ) -> list [str ]:
1697
+ """
1698
+ Retrieves all hotkeys owned by a specific coldkey address.
1699
+
1700
+ Args:
1701
+ coldkey_ss58 (str): The SS58 address of the coldkey to query.
1702
+ block (int): The blockchain block number for the query.
1703
+ block_hash (str): The hash of the blockchain block number for the query.
1704
+ reuse_block (bool): Whether to reuse the last-used blockchain block hash.
1705
+
1706
+ Returns:
1707
+ list[str]: A list of hotkey SS58 addresses owned by the coldkey.
1708
+ """
1709
+ block_hash = await self .determine_block_hash (block , block_hash , reuse_block )
1710
+ owned_hotkeys = await self .substrate .query (
1711
+ module = "SubtensorModule" ,
1712
+ storage_function = "OwnedHotkeys" ,
1713
+ params = [coldkey_ss58 ],
1714
+ block_hash = block_hash ,
1715
+ reuse_block_hash = reuse_block ,
1716
+ )
1717
+
1718
+ return [decode_account_id (hotkey [0 ]) for hotkey in owned_hotkeys or []]
1719
+
1575
1720
async def get_stake (
1576
1721
self ,
1577
1722
coldkey_ss58 : str ,
@@ -2620,6 +2765,46 @@ async def recycle(
2620
2765
)
2621
2766
return None if call is None else Balance .from_rao (int (call ))
2622
2767
2768
+ async def set_reveal_commitment (
2769
+ self ,
2770
+ wallet ,
2771
+ netuid : int ,
2772
+ data : str ,
2773
+ blocks_until_reveal : int = 360 ,
2774
+ block_time : Union [int , float ] = 12 ,
2775
+ ) -> tuple [bool , int ]:
2776
+ """
2777
+ Commits arbitrary data to the Bittensor network by publishing metadata.
2778
+
2779
+ Arguments:
2780
+ wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data.
2781
+ netuid (int): The unique identifier of the subnetwork.
2782
+ data (str): The data to be committed to the network.
2783
+ blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`.
2784
+ Then amount of blocks in one epoch.
2785
+ block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`.
2786
+
2787
+ Returns:
2788
+ bool: `True` if the commitment was successful, `False` otherwise.
2789
+
2790
+ Note: A commitment can be set once per subnet epoch and is reset at the next epoch in the chain automatically.
2791
+ """
2792
+
2793
+ encrypted , reveal_round = get_encrypted_commitment (
2794
+ data , blocks_until_reveal , block_time
2795
+ )
2796
+
2797
+ # increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed
2798
+ # and stored.
2799
+ data_ = {"encrypted" : encrypted , "reveal_round" : reveal_round }
2800
+ return await publish_metadata (
2801
+ subtensor = self ,
2802
+ wallet = wallet ,
2803
+ netuid = netuid ,
2804
+ data_type = f"TimelockEncrypted" ,
2805
+ data = data_ ,
2806
+ ), reveal_round
2807
+
2623
2808
async def subnet (
2624
2809
self ,
2625
2810
netuid : int ,
@@ -3627,6 +3812,7 @@ async def set_weights(
3627
3812
wait_for_inclusion : bool = False ,
3628
3813
wait_for_finalization : bool = False ,
3629
3814
max_retries : int = 5 ,
3815
+ block_time : float = 12.0 ,
3630
3816
):
3631
3817
"""
3632
3818
Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or
@@ -3646,6 +3832,7 @@ async def set_weights(
3646
3832
wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is
3647
3833
``False``.
3648
3834
max_retries (int): The number of maximum attempts to set weights. Default is ``5``.
3835
+ block_time (float): The amount of seconds for block duration. Default is 12.0 seconds.
3649
3836
3650
3837
Returns:
3651
3838
tuple[bool, str]: ``True`` if the setting of weights is successful, False otherwise. And `msg`, a string
@@ -3694,6 +3881,7 @@ async def _blocks_weight_limit() -> bool:
3694
3881
version_key = version_key ,
3695
3882
wait_for_inclusion = wait_for_inclusion ,
3696
3883
wait_for_finalization = wait_for_finalization ,
3884
+ block_time = block_time ,
3697
3885
)
3698
3886
retries += 1
3699
3887
return success , message
0 commit comments