diff --git a/bittensor_cli/src/bittensor/extrinsics/mev_shield.py b/bittensor_cli/src/bittensor/extrinsics/mev_shield.py index 4eabbd2b3..81750f2a2 100644 --- a/bittensor_cli/src/bittensor/extrinsics/mev_shield.py +++ b/bittensor_cli/src/bittensor/extrinsics/mev_shield.py @@ -1,4 +1,3 @@ -import hashlib from typing import TYPE_CHECKING, Optional from async_substrate_interface import AsyncExtrinsicReceipt @@ -37,18 +36,15 @@ async def encrypt_extrinsic( plaintext = bytes(signed_extrinsic.data.data) # Encrypt using ML-KEM-768 - ciphertext = encrypt_mlkem768(ml_kem_768_public_key, plaintext) - - # Commitment: blake2_256(payload_core) - commitment_hash = hashlib.blake2b(plaintext, digest_size=32).digest() - commitment_hex = "0x" + commitment_hash.hex() + ciphertext = encrypt_mlkem768( + ml_kem_768_public_key, plaintext, include_key_hash=True + ) # Create the MevShield.submit_encrypted call encrypted_call = await subtensor.substrate.compose_call( call_module="MevShield", call_function="submit_encrypted", call_params={ - "commitment": commitment_hex, "ciphertext": ciphertext, }, ) @@ -56,29 +52,9 @@ async def encrypt_extrinsic( return encrypted_call -async def extract_mev_shield_id(response: "AsyncExtrinsicReceipt") -> Optional[str]: - """ - Extract the MEV Shield wrapper ID from an extrinsic response. - - After submitting a MEV Shield encrypted call, the EncryptedSubmitted event - contains the wrapper ID needed to track execution. - - Args: - response: The extrinsic receipt from submit_extrinsic. - - Returns: - The wrapper ID (hex string) or None if not found. - """ - for event in await response.triggered_events: - if event["event_id"] == "EncryptedSubmitted": - return event["attributes"]["id"] - return None - - async def wait_for_extrinsic_by_hash( subtensor: "SubtensorInterface", extrinsic_hash: str, - shield_id: str, submit_block_hash: str, timeout_blocks: int = 2, status=None, @@ -112,7 +88,7 @@ async def _noop(_): return True starting_block = await subtensor.substrate.get_block_number(submit_block_hash) - current_block = starting_block + 1 + current_block = starting_block while current_block - starting_block <= timeout_blocks: if status: @@ -137,20 +113,6 @@ async def _noop(_): result_idx = idx break - # Failure: Decryption failed - call = extrinsic.value.get("call", {}) - if ( - call.get("call_module") == "MevShield" - and call.get("call_function") == "mark_decryption_failed" - ): - call_args = call.get("call_args", []) - for arg in call_args: - if arg.get("name") == "id" and arg.get("value") == shield_id: - result_idx = idx - break - if result_idx is not None: - break - if result_idx is not None: receipt = AsyncExtrinsicReceipt( substrate=subtensor.substrate, diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index 7eb93aa7c..2711e8820 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -1302,6 +1302,11 @@ async def create_signed(call_to_sign, n): return False, format_error_message(await response.error_message), None except SubstrateRequestException as e: err_msg = format_error_message(e) + if mev_protection and "'result': 'invalid'" in str(e).lower(): + err_msg = ( + f"MEV Shield extrinsic rejected as invalid. " + f"This usually means the MEV Shield NextKey changed between fetching and submission." + ) if proxy and "Invalid Transaction" in err_msg: extrinsic_fee, signer_balance = await asyncio.gather( self.get_extrinsic_fee( diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py index 7372d6a21..d44d4ff99 100644 --- a/bittensor_cli/src/commands/stake/add.py +++ b/bittensor_cli/src/commands/stake/add.py @@ -11,7 +11,6 @@ from bittensor_cli.src import COLOR_PALETTE from bittensor_cli.src.bittensor.balances import Balance from bittensor_cli.src.bittensor.extrinsics.mev_shield import ( - extract_mev_shield_id, wait_for_extrinsic_by_hash, ) from bittensor_cli.src.bittensor.utils import ( @@ -167,11 +166,9 @@ async def safe_stake_extrinsic( else: if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status_, ) @@ -259,11 +256,9 @@ async def stake_extrinsic( else: if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status_, ) diff --git a/bittensor_cli/src/commands/stake/move.py b/bittensor_cli/src/commands/stake/move.py index 2e83e78cd..b7b28c213 100644 --- a/bittensor_cli/src/commands/stake/move.py +++ b/bittensor_cli/src/commands/stake/move.py @@ -9,7 +9,6 @@ from bittensor_cli.src import COLOR_PALETTE from bittensor_cli.src.bittensor.balances import Balance from bittensor_cli.src.bittensor.extrinsics.mev_shield import ( - extract_mev_shield_id, wait_for_extrinsic_by_hash, ) from bittensor_cli.src.bittensor.utils import ( @@ -691,11 +690,9 @@ async def move_stake( if success_: if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status, ) @@ -909,11 +906,9 @@ async def transfer_stake( if success_: if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status, ) @@ -1147,11 +1142,9 @@ async def swap_stake( if success_: if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status, ) diff --git a/bittensor_cli/src/commands/stake/remove.py b/bittensor_cli/src/commands/stake/remove.py index 9cc782be6..2c000eacf 100644 --- a/bittensor_cli/src/commands/stake/remove.py +++ b/bittensor_cli/src/commands/stake/remove.py @@ -11,7 +11,6 @@ from bittensor_cli.src import COLOR_PALETTE from bittensor_cli.src.bittensor.extrinsics.mev_shield import ( - extract_mev_shield_id, wait_for_extrinsic_by_hash, ) from bittensor_cli.src.bittensor.balances import Balance @@ -652,11 +651,9 @@ async def _unstake_extrinsic( if success: if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status, ) @@ -767,11 +764,9 @@ async def _safe_unstake_extrinsic( if success: if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status, ) @@ -897,11 +892,9 @@ async def _unstake_all_extrinsic( if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status, ) diff --git a/bittensor_cli/src/commands/subnets/subnets.py b/bittensor_cli/src/commands/subnets/subnets.py index f627de7fb..fcbc7dc03 100644 --- a/bittensor_cli/src/commands/subnets/subnets.py +++ b/bittensor_cli/src/commands/subnets/subnets.py @@ -20,7 +20,6 @@ ) from bittensor_cli.src.bittensor.extrinsics.root import root_register_extrinsic from bittensor_cli.src.bittensor.extrinsics.mev_shield import ( - extract_mev_shield_id, wait_for_extrinsic_by_hash, ) from rich.live import Live @@ -272,11 +271,9 @@ async def _find_event_attributes_in_extrinsic_receipt( # Check for MEV shield execution if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(response) mev_success, mev_error, response = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=response.block_hash, status=status, ) diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index abd76d354..84bd13c02 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -24,7 +24,6 @@ NeuronInfoLite, ) from bittensor_cli.src.bittensor.extrinsics.mev_shield import ( - extract_mev_shield_id, wait_for_extrinsic_by_hash, ) from bittensor_cli.src.bittensor.extrinsics.registration import ( @@ -2176,11 +2175,9 @@ async def announce_coldkey_swap( if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(ext_receipt) mev_success, mev_error, ext_receipt = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=ext_receipt.block_hash, status=status, ) @@ -2307,7 +2304,7 @@ async def dispute_coldkey_swap( if not unlock_key(wallet).success: return False - with console.status(":satellite: Disputing coldkey swap on-chain..."): + with console.status(":satellite: Disputing coldkey swap on-chain...") as status: call = await subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="dispute_coldkey_swap", @@ -2325,6 +2322,20 @@ async def dispute_coldkey_swap( print_error(f"Failed to dispute coldkey swap: {err_msg}") return False + if mev_protection: + inner_hash = err_msg + mev_success, mev_error, ext_receipt = await wait_for_extrinsic_by_hash( + subtensor=subtensor, + extrinsic_hash=inner_hash, + submit_block_hash=ext_receipt.block_hash, + status=status, + ) + if not mev_success: + print_error( + f"Failed to dispute coldkey swap: {mev_error}", status=status + ) + return False + print_success("[dark_sea_green3]Coldkey swap disputed.") await print_extrinsic_id(ext_receipt) @@ -2458,11 +2469,9 @@ async def execute_coldkey_swap( if mev_protection: inner_hash = err_msg - mev_shield_id = await extract_mev_shield_id(ext_receipt) mev_success, mev_error, ext_receipt = await wait_for_extrinsic_by_hash( subtensor=subtensor, extrinsic_hash=inner_hash, - shield_id=mev_shield_id, submit_block_hash=ext_receipt.block_hash, status=status, ) diff --git a/pyproject.toml b/pyproject.toml index 3b1ad3b62..798c82123 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,10 +29,10 @@ classifiers = [ ] dependencies = [ "wheel", - "async-substrate-interface>=1.6.0", + "async-substrate-interface>=1.6.2", "aiohttp~=3.13", "backoff~=2.2.1", - "bittensor-drand>=1.2.0", + "bittensor-drand>=1.3.0", "GitPython>=3.0.0", "netaddr~=1.3.0", "numpy>=2.0.1,<3.0.0",