Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Commit-reveal re-added & e2e coverage #2224

Merged
merged 3 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
270 changes: 270 additions & 0 deletions bittensor/core/subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,14 @@
from bittensor.utils.deprecated.extrinsics.set_weights import (
set_weights_extrinsic,
)
from bittensor.utils.deprecated.extrinsics.commit_weights import (
commit_weights_extrinsic,
reveal_weights_extrinsic,
)
from bittensor.utils.deprecated.extrinsics.transfer import (
transfer_extrinsic,
)
from bittensor.utils.weight_utils import generate_weight_hash
from bittensor.core import settings
from bittensor.core.axon import Axon
from bittensor.core.chain_data import (
Expand Down Expand Up @@ -1836,3 +1841,268 @@ def get_existential_deposit(
if result is None or not hasattr(result, "value"):
return None
return Balance.from_rao(result.value)

def commit_weights(
self,
wallet: "Wallet",
netuid: int,
salt: List[int],
uids: Union[NDArray[np.int64], list],
weights: Union[NDArray[np.int64], list],
version_key: int = settings.version_as_int,
wait_for_inclusion: bool = False,
wait_for_finalization: bool = False,
prompt: bool = False,
max_retries: int = 5,
) -> Tuple[bool, str]:
"""
Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet.
This action serves as a commitment or snapshot of the neuron's current weight distribution.

Args:
wallet (bittensor.wallet): The wallet associated with the neuron committing the weights.
netuid (int): The unique identifier of the subnet.
salt (List[int]): list of randomly generated integers as salt to generated weighted hash.
uids (np.ndarray): NumPy array of neuron UIDs for which weights are being committed.
weights (np.ndarray): NumPy array of weight values corresponding to each UID.
version_key (int, optional): Version key for compatibility with the network.
wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block.
wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain.
prompt (bool, optional): If ``True``, prompts for user confirmation before proceeding.
max_retries (int, optional): The number of maximum attempts to commit weights. (Default: 5)

Returns:
Tuple[bool, str]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string
value describing the success or potential error.

This function allows neurons to create a tamper-proof record of their weight distribution at a specific point in time,
enhancing transparency and accountability within the Bittensor network.
"""
retries = 0
success = False
message = "No attempt made. Perhaps it is too soon to commit weights!"

logging.info(
"Committing weights with params: netuid={}, uids={}, weights={}, version_key={}".format(
netuid, uids, weights, version_key
)
)

# Generate the hash of the weights
commit_hash = generate_weight_hash(
address=wallet.hotkey.ss58_address,
netuid=netuid,
uids=list(uids),
values=list(weights),
salt=salt,
version_key=version_key,
)

logging.info("Commit Hash: {}".format(commit_hash))
ibraheem-opentensor marked this conversation as resolved.
Show resolved Hide resolved

while retries < max_retries:
try:
success, message = commit_weights_extrinsic(
subtensor=self,
wallet=wallet,
netuid=netuid,
commit_hash=commit_hash,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
prompt=prompt,
)
if success:
break
except Exception as e:
logging.error(f"Error committing weights: {e}")
finally:
retries += 1

return success, message

def _do_commit_weights(
self,
wallet: "Wallet",
netuid: int,
commit_hash: str,
wait_for_inclusion: bool = False,
wait_for_finalization: bool = False,
) -> Tuple[bool, Optional[str]]:
"""
Internal method to send a transaction to the Bittensor blockchain, committing the hash of a neuron's weights.
This method constructs and submits the transaction, handling retries and blockchain communication.

Args:
wallet (bittensor.wallet): The wallet associated with the neuron committing the weights.
netuid (int): The unique identifier of the subnet.
commit_hash (str): The hash of the neuron's weights to be committed.
wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block.
wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain.

Returns:
Tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message.

This method ensures that the weight commitment is securely recorded on the Bittensor blockchain, providing a
verifiable record of the neuron's weight distribution at a specific point in time.
"""

@retry(delay=1, tries=3, backoff=2, max_delay=4, logger=logging)
def make_substrate_call_with_retry():
call = self.substrate.compose_call(
call_module="SubtensorModule",
call_function="commit_weights",
call_params={
"netuid": netuid,
"commit_hash": commit_hash,
},
)
extrinsic = self.substrate.create_signed_extrinsic(
call=call,
keypair=wallet.hotkey,
)
response = self.substrate.submit_extrinsic(
extrinsic,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

if not wait_for_finalization and not wait_for_inclusion:
return True, None

response.process_events()
if response.is_success:
return True, None
else:
return False, response.error_message

return make_substrate_call_with_retry()

def reveal_weights(
self,
wallet: "Wallet",
netuid: int,
uids: Union[NDArray[np.int64], list],
weights: Union[NDArray[np.int64], list],
salt: Union[NDArray[np.int64], list],
version_key: int = settings.version_as_int,
wait_for_inclusion: bool = False,
wait_for_finalization: bool = False,
prompt: bool = False,
max_retries: int = 5,
) -> Tuple[bool, str]:
"""
Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet.
This action serves as a revelation of the neuron's previously committed weight distribution.

Args:
wallet (bittensor.wallet): The wallet associated with the neuron revealing the weights.
netuid (int): The unique identifier of the subnet.
uids (np.ndarray): NumPy array of neuron UIDs for which weights are being revealed.
weights (np.ndarray): NumPy array of weight values corresponding to each UID.
salt (np.ndarray): NumPy array of salt values corresponding to the hash function.
version_key (int, optional): Version key for compatibility with the network.
wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block.
wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain.
prompt (bool, optional): If ``True``, prompts for user confirmation before proceeding.
max_retries (int, optional): The number of maximum attempts to reveal weights. (Default: 5)

Returns:
Tuple[bool, str]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string
value describing the success or potential error.

This function allows neurons to reveal their previously committed weight distribution, ensuring transparency
and accountability within the Bittensor network.
"""

retries = 0
success = False
message = "No attempt made. Perhaps it is too soon to reveal weights!"

while retries < max_retries:
try:
success, message = reveal_weights_extrinsic(
subtensor=self,
wallet=wallet,
netuid=netuid,
uids=list(uids),
weights=list(weights),
salt=list(salt),
version_key=version_key,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
prompt=prompt,
)
if success:
break
except Exception as e:
logging.error(f"Error revealing weights: {e}")
finally:
retries += 1

return success, message

def _do_reveal_weights(
self,
wallet: "Wallet",
netuid: int,
uids: List[int],
values: List[int],
salt: List[int],
version_key: int,
wait_for_inclusion: bool = False,
wait_for_finalization: bool = False,
) -> Tuple[bool, Optional[str]]:
"""
Internal method to send a transaction to the Bittensor blockchain, revealing the weights for a specific subnet.
This method constructs and submits the transaction, handling retries and blockchain communication.

Args:
wallet (bittensor.wallet): The wallet associated with the neuron revealing the weights.
netuid (int): The unique identifier of the subnet.
uids (List[int]): List of neuron UIDs for which weights are being revealed.
values (List[int]): List of weight values corresponding to each UID.
salt (List[int]): List of salt values corresponding to the hash function.
version_key (int): Version key for compatibility with the network.
wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block.
wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain.

Returns:
Tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message.

This method ensures that the weight revelation is securely recorded on the Bittensor blockchain, providing transparency
and accountability for the neuron's weight distribution.
"""

@retry(delay=1, tries=3, backoff=2, max_delay=4, logger=logging)
def make_substrate_call_with_retry():
call = self.substrate.compose_call(
call_module="SubtensorModule",
call_function="reveal_weights",
call_params={
"netuid": netuid,
"uids": uids,
"values": values,
"salt": salt,
"version_key": version_key,
},
)
extrinsic = self.substrate.create_signed_extrinsic(
call=call,
keypair=wallet.hotkey,
)
response = self.substrate.submit_extrinsic(
extrinsic,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

if not wait_for_finalization and not wait_for_inclusion:
return True, None

response.process_events()
if response.is_success:
return True, None
else:
return False, format_error_message(response.error_message)

return make_substrate_call_with_retry()
Loading
Loading