Skip to content

Commit

Permalink
Merge pull request #2016 from opentensor/fix/allow-unstake-below-netw…
Browse files Browse the repository at this point in the history
…ork-min

[Fix] Allow unstake below network min
  • Loading branch information
gus-opentensor committed Jun 12, 2024
1 parent 51afab8 commit ea83f43
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 74 deletions.
4 changes: 4 additions & 0 deletions bittensor/extrinsics/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,8 @@ def run_faucet_extrinsic(
if attempts == max_allowed_attempts:
raise MaxAttemptsException
attempts += 1
# Wait a bit before trying again
time.sleep(1)

# Successful registration
else:
Expand All @@ -473,6 +475,8 @@ def run_faucet_extrinsic(

if successes == 3:
raise MaxSuccessException

attempts = 1 # Reset attempts on success
successes += 1

except KeyboardInterrupt:
Expand Down
50 changes: 45 additions & 5 deletions bittensor/extrinsics/staking.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,34 @@
import bittensor
from rich.prompt import Confirm
from time import sleep
from typing import List, Union, Optional
from typing import List, Union, Optional, Tuple
from bittensor.utils.balance import Balance


def _check_threshold_amount(
subtensor: "bittensor.subtensor", stake_balance: Balance
) -> Tuple[bool, Balance]:
"""
Checks if the new stake balance will be above the minimum required stake threshold.
Args:
stake_balance (Balance):
the balance to check for threshold limits.
Returns:
success, threshold (bool, Balance):
``true`` if the staking balance is above the threshold, or ``false`` if the
staking balance is below the threshold.
The threshold balance required to stake.
"""
min_req_stake: Balance = subtensor.get_minimum_required_stake()

if min_req_stake > stake_balance:
return False, min_req_stake
else:
return True, min_req_stake


def add_stake_extrinsic(
subtensor: "bittensor.subtensor",
wallet: "bittensor.wallet",
Expand Down Expand Up @@ -91,6 +115,9 @@ def add_stake_extrinsic(
coldkey_ss58=wallet.coldkeypub.ss58_address, hotkey_ss58=hotkey_ss58
)

# Grab the existential deposit.
existential_deposit = subtensor.get_existential_deposit()

# Convert to bittensor.Balance
if amount is None:
# Stake it all.
Expand All @@ -100,9 +127,10 @@ def add_stake_extrinsic(
else:
staking_balance = amount

# Remove existential balance to keep key alive.
if staking_balance > bittensor.Balance.from_rao(1000):
staking_balance = staking_balance - bittensor.Balance.from_rao(1000)
# Leave existential balance to keep key alive.
if staking_balance > old_balance - existential_deposit:
# If we are staking all, we need to leave at least the existential deposit.
staking_balance = old_balance - existential_deposit
else:
staking_balance = staking_balance

Expand All @@ -115,6 +143,18 @@ def add_stake_extrinsic(
)
return False

# If nominating, we need to check if the new stake balance will be above the minimum required stake threshold.
if not own_hotkey:
new_stake_balance = old_stake + staking_balance
is_above_threshold, threshold = _check_threshold_amount(
subtensor, new_stake_balance
)
if not is_above_threshold:
bittensor.__console__.print(
f":cross_mark: [red]New stake balance of {new_stake_balance} is below the minimum required nomination stake threshold {threshold}.[/red]"
)
return False

# Ask before moving on.
if prompt:
if not own_hotkey:
Expand Down Expand Up @@ -167,7 +207,7 @@ def add_stake_extrinsic(
block = subtensor.get_current_block()
new_stake = subtensor.get_stake_for_coldkey_and_hotkey(
coldkey_ss58=wallet.coldkeypub.ss58_address,
hotkey_ss58=wallet.hotkey.ss58_address,
hotkey_ss58=hotkey_ss58,
block=block,
) # Get current stake

Expand Down
41 changes: 28 additions & 13 deletions bittensor/extrinsics/unstaking.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ def __do_remove_stake_single(


def check_threshold_amount(
subtensor: "bittensor.subtensor", unstaking_balance: Balance
subtensor: "bittensor.subtensor", stake_balance: Balance
) -> bool:
"""
Checks if the unstaking amount is above the threshold or 0
Checks if the remaining stake balance is above the minimum required stake threshold.
Args:
unstaking_balance (Balance):
stake_balance (Balance):
the balance to check for threshold limits.
Returns:
Expand All @@ -88,9 +88,9 @@ def check_threshold_amount(
"""
min_req_stake: Balance = subtensor.get_minimum_required_stake()

if min_req_stake > unstaking_balance > 0:
if min_req_stake > stake_balance > 0:
bittensor.__console__.print(
f":cross_mark: [red]Unstaking balance of {unstaking_balance} less than minimum of {min_req_stake} TAO[/red]"
f":cross_mark: [yellow]Remaining stake balance of {stake_balance} less than minimum of {min_req_stake} TAO[/yellow]"
)
return False
else:
Expand Down Expand Up @@ -141,6 +141,9 @@ def unstake_extrinsic(
coldkey_ss58=wallet.coldkeypub.ss58_address, hotkey_ss58=hotkey_ss58
)

hotkey_owner = subtensor.get_hotkey_owner(hotkey_ss58)
own_hotkey: bool = wallet.coldkeypub.ss58_address == hotkey_owner

# Convert to bittensor.Balance
if amount is None:
# Unstake it all.
Expand All @@ -160,10 +163,14 @@ def unstake_extrinsic(
)
return False

if not check_threshold_amount(
subtensor=subtensor, unstaking_balance=unstaking_balance
# If nomination stake, check threshold.
if not own_hotkey and not check_threshold_amount(
subtensor=subtensor, stake_balance=(stake_on_uid - unstaking_balance)
):
return False
bittensor.__console__.print(
f":warning: [yellow]This action will unstake the entire staked balance![/yellow]"
)
unstaking_balance = stake_on_uid

# Ask before moving on.
if prompt:
Expand Down Expand Up @@ -300,6 +307,7 @@ def unstake_multiple_extrinsic(
wallet.coldkey

old_stakes = []
own_hotkeys = []
with bittensor.__console__.status(
":satellite: Syncing with chain: [white]{}[/white] ...".format(
subtensor.network
Expand All @@ -313,9 +321,12 @@ def unstake_multiple_extrinsic(
) # Get stake on hotkey.
old_stakes.append(old_stake) # None if not registered.

hotkey_owner = subtensor.get_hotkey_owner(hotkey_ss58)
own_hotkeys.append(wallet.coldkeypub.ss58_address == hotkey_owner)

successful_unstakes = 0
for idx, (hotkey_ss58, amount, old_stake) in enumerate(
zip(hotkey_ss58s, amounts, old_stakes)
for idx, (hotkey_ss58, amount, old_stake, own_hotkey) in enumerate(
zip(hotkey_ss58s, amounts, old_stakes, own_hotkeys)
):
# Covert to bittensor.Balance
if amount is None:
Expand All @@ -336,10 +347,14 @@ def unstake_multiple_extrinsic(
)
continue

if not check_threshold_amount(
subtensor=subtensor, unstaking_balance=unstaking_balance
# If nomination stake, check threshold.
if not own_hotkey and not check_threshold_amount(
subtensor=subtensor, stake_balance=(stake_on_uid - unstaking_balance)
):
return False
bittensor.__console__.print(
f":warning: [yellow]This action will unstake the entire staked balance![/yellow]"
)
unstaking_balance = stake_on_uid

# Ask before moving on.
if prompt:
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e_tests/multistep/test_dendrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ async def test_dendrite(local_chain):
metagraph = bittensor.metagraph(netuid=1, network="ws://localhost:9945")
neuron = metagraph.neurons[0]
# assert stake is 10000
assert neuron.stake.tao == 9999.999999
assert neuron.stake.tao == 10_000.0

# assert neuron is not validator
assert neuron.active is True
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e_tests/multistep/test_incentive.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ async def validator_write_output(stream):
alice_neuron = metagraph.neurons[0]
assert alice_neuron.validator_permit is False
assert alice_neuron.dividends == 0
assert alice_neuron.stake.tao == 9999.999999
assert alice_neuron.stake.tao == 10_000.0
assert alice_neuron.validator_trust == 0

# wait until 360 blocks pass (subnet tempo)
Expand Down Expand Up @@ -287,7 +287,7 @@ async def validator_write_output(stream):
alice_neuron = metagraph.neurons[0]
assert alice_neuron.validator_permit is True
assert alice_neuron.dividends == 1
assert alice_neuron.stake.tao == 9999.999999
assert alice_neuron.stake.tao == 10_000.0
assert alice_neuron.validator_trust == 1


Expand Down
2 changes: 1 addition & 1 deletion tests/e2e_tests/subcommands/weights/test_commit_weights.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def test_commit_and_reveal_weights(local_chain):
"--wallet.path",
"/tmp/btcli-wallet2",
"--amount",
"999998998",
"100000",
],
)

Expand Down
Loading

0 comments on commit ea83f43

Please sign in to comment.