Skip to content

Commit 9631464

Browse files
authored
Merge branch 'staging' into fix/roman/balance-unit
2 parents 7050f6f + 4bf4007 commit 9631464

File tree

8 files changed

+348
-16
lines changed

8 files changed

+348
-16
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ workflows:
305305
- build-and-test:
306306
matrix:
307307
parameters:
308-
python-version: [ "3.9.13", "3.10.6", "3.11.4", "3.12.7" ]
308+
python-version: [ "3.9.13", "3.10.6", "3.11.4", "3.12.7"]
309309
requires:
310310
- check-if-pr-is-draft
311311
- unit-tests-all-python-versions:
@@ -314,7 +314,7 @@ workflows:
314314
- lint-and-type-check:
315315
matrix:
316316
parameters:
317-
python-version: [ "3.9.13", "3.10.6", "3.11.4", "3.12.7" ]
317+
python-version: [ "3.9.13", "3.10.6", "3.11.4", "3.12.7"]
318318
requires:
319319
- check-if-pr-is-draft
320320
#- coveralls:

bittensor/core/async_subtensor.py

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,14 @@
2727
decode_account_id,
2828
DynamicInfo,
2929
)
30+
from bittensor_commit_reveal import get_encrypted_commitment
3031
from bittensor.core.chain_data.chain_identity import ChainIdentity
3132
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+
)
3338
from bittensor.core.config import Config
3439
from bittensor.core.errors import ChainError, SubstrateRequestException
3540
from bittensor.core.extrinsics.asyncex.commit_reveal import commit_reveal_v3_extrinsic
@@ -1059,6 +1064,68 @@ async def get_all_commitments(
10591064
result[decode_account_id(id_[0])] = decode_metadata(value)
10601065
return result
10611066

1067+
async def get_revealed_commitment(
1068+
self,
1069+
netuid: int,
1070+
hotkey_ss58_address: Optional[str] = None,
1071+
block: Optional[int] = None,
1072+
block_hash: Optional[str] = None,
1073+
reuse_block: bool = False,
1074+
) -> Optional[tuple[int, str]]:
1075+
"""Returns hotkey related revealed commitment for a given netuid.
1076+
1077+
Arguments:
1078+
netuid (int): The unique identifier of the subnetwork.
1079+
block (Optional[int]): The block number to retrieve the commitment from. Default is ``None``.
1080+
hotkey_ss58_address (str): The ss58 address of the committee member.
1081+
block_hash (Optional[str]): The hash of the block to retrieve the subnet unique identifiers from.
1082+
reuse_block (bool): Whether to reuse the last-used block hash.
1083+
1084+
Returns:
1085+
result (tuple[int, str): A tuple of reveal block and commitment message.
1086+
"""
1087+
query = await self.query_module(
1088+
module="Commitments",
1089+
name="RevealedCommitments",
1090+
params=[netuid, hotkey_ss58_address],
1091+
block=block,
1092+
block_hash=block_hash,
1093+
reuse_block=reuse_block,
1094+
)
1095+
return decode_revealed_commitment(query)
1096+
1097+
async def get_all_revealed_commitments(
1098+
self,
1099+
netuid: int,
1100+
block: Optional[int] = None,
1101+
block_hash: Optional[str] = None,
1102+
reuse_block: bool = False,
1103+
) -> dict[str, tuple[int, str]]:
1104+
"""Returns all revealed commitments for a given netuid.
1105+
1106+
Arguments:
1107+
netuid (int): The unique identifier of the subnetwork.
1108+
block (Optional[int]): The block number to retrieve the commitment from. Default is ``None``.
1109+
block_hash (Optional[str]): The hash of the block to retrieve the subnet unique identifiers from.
1110+
reuse_block (bool): Whether to reuse the last-used block hash.
1111+
1112+
Returns:
1113+
result (dict): A dictionary of all revealed commitments in view {ss58_address: (reveal block, commitment message)}.
1114+
"""
1115+
query = await self.query_map(
1116+
module="Commitments",
1117+
name="RevealedCommitments",
1118+
params=[netuid],
1119+
block=block,
1120+
block_hash=block_hash,
1121+
reuse_block=reuse_block,
1122+
)
1123+
1124+
result = {}
1125+
async for item in query:
1126+
result.update(decode_revealed_commitment_with_hotkey(item))
1127+
return result
1128+
10621129
async def get_current_weight_commit_info(
10631130
self,
10641131
netuid: int,
@@ -2650,6 +2717,46 @@ async def recycle(
26502717
)
26512718
return None if call is None else Balance.from_rao(int(call))
26522719

2720+
async def set_reveal_commitment(
2721+
self,
2722+
wallet,
2723+
netuid: int,
2724+
data: str,
2725+
blocks_until_reveal: int = 360,
2726+
block_time: Union[int, float] = 12,
2727+
) -> tuple[bool, int]:
2728+
"""
2729+
Commits arbitrary data to the Bittensor network by publishing metadata.
2730+
2731+
Arguments:
2732+
wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data.
2733+
netuid (int): The unique identifier of the subnetwork.
2734+
data (str): The data to be committed to the network.
2735+
blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`.
2736+
Then amount of blocks in one epoch.
2737+
block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`.
2738+
2739+
Returns:
2740+
bool: `True` if the commitment was successful, `False` otherwise.
2741+
2742+
Note: A commitment can be set once per subnet epoch and is reset at the next epoch in the chain automatically.
2743+
"""
2744+
2745+
encrypted, reveal_round = get_encrypted_commitment(
2746+
data, blocks_until_reveal, block_time
2747+
)
2748+
2749+
# increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed
2750+
# and stored.
2751+
data_ = {"encrypted": encrypted, "reveal_round": reveal_round}
2752+
return await publish_metadata(
2753+
subtensor=self,
2754+
wallet=wallet,
2755+
netuid=netuid,
2756+
data_type=f"TimelockEncrypted",
2757+
data=data_,
2758+
), reveal_round
2759+
26532760
async def subnet(
26542761
self,
26552762
netuid: int,

bittensor/core/chain_data/utils.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Chain data helper functions and data."""
22

33
from enum import Enum
4-
from typing import Optional, Union
4+
from typing import Optional, Union, TYPE_CHECKING
55

66
from scalecodec.base import RuntimeConfiguration, ScaleBytes
77
from scalecodec.type_registry import load_type_registry_preset
@@ -10,6 +10,9 @@
1010
from bittensor.core.settings import SS58_FORMAT
1111
from bittensor.utils.balance import Balance
1212

13+
if TYPE_CHECKING:
14+
from async_substrate_interface.types import ScaleObj
15+
1316

1417
class ChainDataType(Enum):
1518
NeuronInfo = 1
@@ -135,3 +138,36 @@ def decode_metadata(metadata: dict) -> str:
135138
commitment = metadata["info"]["fields"][0][0]
136139
bytes_tuple = commitment[next(iter(commitment.keys()))][0]
137140
return bytes(bytes_tuple).decode()
141+
142+
143+
def decode_revealed_commitment(
144+
value: Optional["ScaleObj"],
145+
) -> Optional[tuple[int, str]]:
146+
"""Decode the revealed commitment data from the given input if it is not None."""
147+
if value is None:
148+
return None
149+
150+
def scale_decode_offset(data: bytes) -> int:
151+
"""Decodes the scale offset from a given byte data sequence."""
152+
first_byte = data[0]
153+
mode = first_byte & 0b11
154+
if mode == 0:
155+
return 1
156+
elif mode == 1:
157+
return 2
158+
else:
159+
return 4
160+
161+
v = next(iter(value))
162+
revealed_block = v[1]
163+
cut = scale_decode_offset(v[0])
164+
165+
revealed_commitment = bytes(v[0][cut:]).decode("utf-8", errors="ignore")
166+
return revealed_block, revealed_commitment
167+
168+
169+
def decode_revealed_commitment_with_hotkey(data: list) -> dict[str, tuple[int, str]]:
170+
"""Decode revealed commitment using a hotkey."""
171+
key, value = data
172+
ss58_address = decode_account_id(key[0])
173+
return {ss58_address: decode_revealed_commitment(value)}

bittensor/core/extrinsics/asyncex/serving.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import asyncio
2-
from typing import Optional, TYPE_CHECKING
2+
from typing import Optional, Union, TYPE_CHECKING
33

44
from bittensor.core.errors import MetadataError
55
from bittensor.core.settings import version_as_int
@@ -226,7 +226,7 @@ async def publish_metadata(
226226
wallet: "Wallet",
227227
netuid: int,
228228
data_type: str,
229-
data: bytes,
229+
data: Union[bytes, dict],
230230
wait_for_inclusion: bool = False,
231231
wait_for_finalization: bool = True,
232232
) -> bool:
@@ -240,8 +240,8 @@ async def publish_metadata(
240240
data_type (str): The data type of the information being submitted. It should be one of the following:
241241
``'Sha256'``, ``'Blake256'``, ``'Keccak256'``, or ``'Raw0-128'``. This specifies the format or hashing
242242
algorithm used for the data.
243-
data (str): The actual metadata content to be published. This should be formatted or hashed according to the
244-
``type`` specified. (Note: max ``str`` length is 128 bytes)
243+
data (Union[bytes, dict]): The actual metadata content to be published. This should be formatted or hashed
244+
according to the ``type`` specified. (Note: max ``str`` length is 128 bytes for ``'Raw0-128'``.)
245245
wait_for_inclusion (bool, optional): If ``True``, the function will wait for the extrinsic to be included in a
246246
block before returning. Defaults to ``False``.
247247
wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized

bittensor/core/extrinsics/serving.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, TYPE_CHECKING
1+
from typing import Optional, Union, TYPE_CHECKING
22

33
from bittensor.core.errors import MetadataError
44
from bittensor.core.settings import version_as_int
@@ -222,7 +222,7 @@ def publish_metadata(
222222
wallet: "Wallet",
223223
netuid: int,
224224
data_type: str,
225-
data: bytes,
225+
data: Union[bytes, dict],
226226
wait_for_inclusion: bool = False,
227227
wait_for_finalization: bool = True,
228228
) -> bool:
@@ -236,8 +236,8 @@ def publish_metadata(
236236
data_type (str): The data type of the information being submitted. It should be one of the following:
237237
``'Sha256'``, ``'Blake256'``, ``'Keccak256'``, or ``'Raw0-128'``. This specifies the format or hashing
238238
algorithm used for the data.
239-
data (str): The actual metadata content to be published. This should be formatted or hashed according to the
240-
``type`` specified. (Note: max ``str`` length is 128 bytes)
239+
data (Union[bytes, dict]): The actual metadata content to be published. This should be formatted or hashed
240+
according to the ``type`` specified. (Note: max ``str`` length is 128 bytes for ``'Raw0-128'``.)
241241
wait_for_inclusion (bool, optional): If ``True``, the function will wait for the extrinsic to be included in a
242242
block before returning. Defaults to ``False``.
243243
wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized

bittensor/core/subtensor.py

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import copy
22
from datetime import datetime, timezone
3-
43
from functools import lru_cache
54
from typing import TYPE_CHECKING, Any, Iterable, Optional, Union, cast
65

76
import numpy as np
87
import scalecodec
98
from async_substrate_interface.errors import SubstrateRequestException
10-
from async_substrate_interface.types import ScaleObj
119
from async_substrate_interface.sync_substrate import SubstrateInterface
10+
from async_substrate_interface.types import ScaleObj
11+
from bittensor_commit_reveal import get_encrypted_commitment
1212
from numpy.typing import NDArray
1313

1414
from bittensor.core.async_subtensor import ProposalVoteData
@@ -28,7 +28,11 @@
2828
decode_account_id,
2929
)
3030
from bittensor.core.chain_data.chain_identity import ChainIdentity
31-
from bittensor.core.chain_data.utils import decode_metadata
31+
from bittensor.core.chain_data.utils import (
32+
decode_metadata,
33+
decode_revealed_commitment,
34+
decode_revealed_commitment_with_hotkey,
35+
)
3236
from bittensor.core.config import Config
3337
from bittensor.core.errors import ChainError
3438
from bittensor.core.extrinsics.commit_reveal import commit_reveal_v3_extrinsic
@@ -803,6 +807,54 @@ def get_all_commitments(
803807
result[decode_account_id(id_[0])] = decode_metadata(value)
804808
return result
805809

810+
def get_revealed_commitment(
811+
self,
812+
netuid: int,
813+
hotkey_ss58_address: Optional[str] = None,
814+
block: Optional[int] = None,
815+
) -> Optional[tuple[int, str]]:
816+
"""Returns hotkey related revealed commitment for a given netuid.
817+
818+
Arguments:
819+
netuid (int): The unique identifier of the subnetwork.
820+
block (Optional[int]): The block number to retrieve the commitment from. Default is ``None``.
821+
hotkey_ss58_address (str): The ss58 address of the committee member.
822+
823+
Returns:
824+
result (tuple[int, str): A tuple of reveal block and commitment message.
825+
"""
826+
query = self.query_module(
827+
module="Commitments",
828+
name="RevealedCommitments",
829+
params=[netuid, hotkey_ss58_address],
830+
block=block,
831+
)
832+
return decode_revealed_commitment(query)
833+
834+
def get_all_revealed_commitments(
835+
self, netuid: int, block: Optional[int] = None
836+
) -> dict[str, tuple[int, str]]:
837+
"""Returns all revealed commitments for a given netuid.
838+
839+
Arguments:
840+
netuid (int): The unique identifier of the subnetwork.
841+
block (Optional[int]): The block number to retrieve the commitment from. Default is ``None``.
842+
843+
Returns:
844+
result (dict): A dictionary of all revealed commitments in view {ss58_address: (reveal block, commitment message)}.
845+
"""
846+
query = self.query_map(
847+
module="Commitments",
848+
name="RevealedCommitments",
849+
params=[netuid],
850+
block=block,
851+
)
852+
853+
result = {}
854+
for item in query:
855+
result.update(decode_revealed_commitment_with_hotkey(item))
856+
return result
857+
806858
def get_current_weight_commit_info(
807859
self, netuid: int, block: Optional[int] = None
808860
) -> list:
@@ -2191,6 +2243,46 @@ def recycle(self, netuid: int, block: Optional[int] = None) -> Optional[Balance]
21912243
call = self.get_hyperparameter(param_name="Burn", netuid=netuid, block=block)
21922244
return None if call is None else Balance.from_rao(int(call))
21932245

2246+
def set_reveal_commitment(
2247+
self,
2248+
wallet,
2249+
netuid: int,
2250+
data: str,
2251+
blocks_until_reveal: int = 360,
2252+
block_time: Union[int, float] = 12,
2253+
) -> tuple[bool, int]:
2254+
"""
2255+
Commits arbitrary data to the Bittensor network by publishing metadata.
2256+
2257+
Arguments:
2258+
wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data.
2259+
netuid (int): The unique identifier of the subnetwork.
2260+
data (str): The data to be committed to the network.
2261+
blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`.
2262+
Then amount of blocks in one epoch.
2263+
block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`.
2264+
2265+
Returns:
2266+
bool: `True` if the commitment was successful, `False` otherwise.
2267+
2268+
Note: A commitment can be set once per subnet epoch and is reset at the next epoch in the chain automatically.
2269+
"""
2270+
2271+
encrypted, reveal_round = get_encrypted_commitment(
2272+
data, blocks_until_reveal, block_time
2273+
)
2274+
2275+
# increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed
2276+
# and stored.
2277+
data_ = {"encrypted": encrypted, "reveal_round": reveal_round}
2278+
return publish_metadata(
2279+
subtensor=self,
2280+
wallet=wallet,
2281+
netuid=netuid,
2282+
data_type=f"TimelockEncrypted",
2283+
data=data_,
2284+
), reveal_round
2285+
21942286
def subnet(self, netuid: int, block: Optional[int] = None) -> Optional[DynamicInfo]:
21952287
"""
21962288
Retrieves the subnet information for a single subnet in the network.

requirements/prod.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ python-Levenshtein
2121
scalecodec==1.2.11
2222
uvicorn
2323
websockets>=14.1
24-
bittensor-commit-reveal>=0.2.0
24+
bittensor-commit-reveal>=0.3.1
2525
bittensor-wallet>=3.0.4
2626
async-substrate-interface>=1.0.4

0 commit comments

Comments
 (0)