Skip to content

Commit

Permalink
Added e2e test for CRv3 + enhancements (#2532)
Browse files Browse the repository at this point in the history
* Added e2e test for crv3 + enhancements

* Fixes type

* Ruff

* Fixes return type

* Renames method
  • Loading branch information
ibraheem-opentensor authored Dec 12, 2024
1 parent 1b453f7 commit ac9cc8b
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 165 deletions.
34 changes: 34 additions & 0 deletions bittensor/core/subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,40 @@ def neurons(self, netuid: int, block: Optional[int] = None) -> list["NeuronInfo"

return neurons

def last_drand_round(
self,
) -> Optional[int]:
"""
Retrieves the last drand round emitted in bittensor. This corresponds when committed weights will be revealed.
Returns:
int: The latest Drand round emitted in bittensor.
"""
result = self.substrate.query(
module="Drand", storage_function="LastStoredRound"
)
return getattr(result, "value", None)

def get_weight_commits(self, netuid: int, block: Optional[int] = None) -> list:
"""
Retrieves CRV3 weight commit information for a specific subnet.
Args:
netuid (int): The unique identifier of the subnet.
block (Optional[int]): The blockchain block number for the query.
Returns:
list: A list of commit details, where each entry is a dictionary with keys 'who',
'serialized_commit', and 'reveal_round', or an empty list if no data is found.
"""
result = self.query_map(
module="SubtensorModule",
name="CRV3WeightCommits",
params=[netuid],
block=block,
)
return result.records[0][1].value if result and result.records else []

def get_total_subnets(self, block: Optional[int] = None) -> Optional[int]:
"""
Retrieves the total number of subnets within the Bittensor network as of a specific blockchain block.
Expand Down
188 changes: 188 additions & 0 deletions tests/e2e_tests/test_commit_reveal_v3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import re

import numpy as np
import pytest
from bittensor.utils.btlogging import logging
from bittensor.core.subtensor import Subtensor
from bittensor.utils.balance import Balance
from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit
from tests.e2e_tests.utils.chain_interactions import (
add_stake,
register_subnet,
sudo_set_hyperparameter_bool,
sudo_set_hyperparameter_values,
wait_interval,
sudo_set_admin_utils,
next_tempo,
)
from tests.e2e_tests.utils.e2e_test_utils import setup_wallet


# Skipping till we have CRV3 on testnet
@pytest.mark.skip
@pytest.mark.parametrize("local_chain", [False], indirect=True)
@pytest.mark.asyncio
async def test_commit_and_reveal_weights_cr3(local_chain):
"""
Tests the commit/reveal weights mechanism (CR3)
Steps:
1. Register a subnet through Alice
2. Register Alice's neuron and add stake
3. Enable commit-reveal mechanism on the subnet
4. Lower weights rate limit
5. Change the tempo for subnet 1
5. Commit weights and ensure they are committed.
6. Wait interval & reveal weights and verify
Raises:
AssertionError: If any of the checks or verifications fail
"""
netuid = 1
logging.console.info("Testing test_commit_and_reveal_weights")

# Register root as Alice
keypair, alice_wallet = setup_wallet("//Alice")
assert register_subnet(local_chain, alice_wallet), "Unable to register the subnet"

# Verify subnet 1 created successfully
assert local_chain.query(
"SubtensorModule", "NetworksAdded", [1]
).serialize(), "Subnet wasn't created successfully"

logging.console.info("Subnet 1 is registered")

subtensor = Subtensor(network="ws://localhost:9945")

# Register Alice to the subnet
assert subtensor.burned_register(
alice_wallet, netuid
), "Unable to register Alice as a neuron"
logging.console.info("Registered Alice to subnet 1")

# Stake to become to top neuron after the first epoch
add_stake(local_chain, alice_wallet, Balance.from_tao(100_000))
logging.console.info("Stake added by Alice")

# Enable commit_reveal on the subnet
assert sudo_set_hyperparameter_bool(
local_chain,
alice_wallet,
"sudo_set_commit_reveal_weights_enabled",
True,
netuid,
), "Unable to enable commit reveal on the subnet"

# Verify commit_reveal was enabled
assert subtensor.get_subnet_hyperparameters(
netuid=netuid,
).commit_reveal_weights_enabled, "Failed to enable commit/reveal"
logging.console.info("Commit reveal enabled")

# Change the weights rate limit on the subnet
assert sudo_set_hyperparameter_values(
local_chain,
alice_wallet,
call_function="sudo_set_weights_set_rate_limit",
call_params={"netuid": netuid, "weights_set_rate_limit": "0"},
return_error_message=True,
)

# Verify weights rate limit was changed
assert (
subtensor.get_subnet_hyperparameters(netuid=netuid).weights_rate_limit == 0
), "Failed to set weights_rate_limit"
assert subtensor.weights_rate_limit(netuid=netuid) == 0
logging.console.info("sudo_set_weights_set_rate_limit executed: set to 0")

# Change the tempo of the subnet from default 360
# Since this is in normal blocks, this is necessary
tempo_set = 10
assert sudo_set_admin_utils(
local_chain,
alice_wallet,
call_function="sudo_set_tempo",
call_params={"netuid": netuid, "tempo": tempo_set},
return_error_message=True,
)
tempo = subtensor.get_subnet_hyperparameters(netuid=netuid).tempo
assert tempo_set == tempo
logging.console.info(f"sudo_set_tempo executed: set to {tempo_set}")

# Commit-reveal values - setting weights to self
uids = np.array([0], dtype=np.int64)
revealed_weights = np.array([0.1], dtype=np.float32)
weight_uids, weight_vals = convert_weights_and_uids_for_emit(
uids=uids, weights=revealed_weights
)

# Fetch current block and calculate next tempo for the subnet
current_block = subtensor.get_current_block()
upcoming_tempo = next_tempo(current_block, tempo, netuid)

# Lower than this might mean weights will get revealed before we can check them
if upcoming_tempo - current_block < 3:
await wait_interval(
tempo,
subtensor,
netuid=netuid,
reporting_interval=1,
)

# Commit weights
success, message = subtensor.set_weights(
alice_wallet,
netuid,
uids=weight_uids,
weights=weight_vals,
wait_for_inclusion=True,
wait_for_finalization=True,
)

# Assert committing was a success
assert success is True
assert bool(re.match(r"reveal_round:\d+", message))
logging.console.info(
f"Successfully set weights: uids {weight_uids}, weights {weight_vals}"
)

# Parse expected reveal_round
expected_reveal_round = int(message.split(":")[1])

# Fetch current commits pending on the chain
commits_on_chain = subtensor.get_weight_commits(netuid=netuid)
address, commit, reveal_round = commits_on_chain[0]

# Assert correct values are committed on the chain
assert expected_reveal_round == reveal_round
assert address == alice_wallet.hotkey.ss58_address

# Ensure no weights are available as of now
assert subtensor.weights(netuid=netuid) == []

# Wait for the next tempo so weights can be revealed
await wait_interval(
subtensor.get_subnet_hyperparameters(netuid=netuid).tempo,
subtensor,
netuid=netuid,
reporting_interval=1,
)

# Fetch the latest drand pulse
latest_drand_round = subtensor.last_drand_round()

# Fetch weights on the chain as they should be revealed now
revealed_weights = subtensor.weights(netuid=netuid)[0][1]

# Assert correct weights were revealed
assert weight_uids[0] == revealed_weights[0][0]
assert weight_vals[0] == revealed_weights[0][1]

# Now that the commit has been revealed, there shouldn't be any pending commits
assert subtensor.get_weight_commits(netuid=netuid) == []

# Ensure the drand_round is always in the positive w.r.t expected when revealed
assert (
latest_drand_round - expected_reveal_round >= 0
), f"latest_drand_round ({latest_drand_round}) is less than expected_reveal_round ({expected_reveal_round})"

logging.console.info("✅ Passed commit_reveal v3")
Loading

0 comments on commit ac9cc8b

Please sign in to comment.