diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index e1f1d1b51d..1dc6971230 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -6600,6 +6600,7 @@ async def sign_and_send_extrinsic( nonce: Optional[int] = None, *, period: Optional[int] = DEFAULT_PERIOD, + era_current: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, @@ -6619,6 +6620,8 @@ async def sign_and_send_extrinsic( period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + era_current: The block number to anchor the mortal era to. If not provided, the substrate interface chooses + the anchor. raise_error: Raises the relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait until the extrinsic call is included on the chain wait_for_finalization: Whether to wait until the extrinsic call is finalized on the chain @@ -6657,6 +6660,8 @@ async def sign_and_send_extrinsic( if period is not None: extrinsic_data["era"] = {"period": period} + if era_current is not None: + extrinsic_data["era"]["current"] = era_current extrinsic_response.extrinsic = await self.substrate.create_signed_extrinsic( **extrinsic_data diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index d4bf360997..37f4dac738 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -148,7 +148,8 @@ async def submit_encrypted_extrinsic( inner_signing_keypair = getattr(wallet, sign_with) effective_period = resolve_mev_shield_period(period) - era = {"period": effective_period} + current_block = await subtensor.substrate.get_block_number() + era = {"period": effective_period, "current": current_block} current_nonce = await subtensor.substrate.get_account_next_index( account_address=inner_signing_keypair.ss58_address @@ -175,6 +176,7 @@ async def submit_encrypted_extrinsic( call=extrinsic_call, nonce=current_nonce, period=effective_period, + era_current=current_block, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index fd618a6ee8..5a3222c280 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -148,7 +148,8 @@ def submit_encrypted_extrinsic( inner_signing_keypair = getattr(wallet, sign_with) effective_period = resolve_mev_shield_period(period) - era = {"period": effective_period} + current_block = subtensor.substrate.get_block_number() + era = {"period": effective_period, "current": current_block} current_nonce = subtensor.substrate.get_account_next_index( account_address=inner_signing_keypair.ss58_address @@ -173,6 +174,7 @@ def submit_encrypted_extrinsic( call=extrinsic_call, nonce=current_nonce, period=effective_period, + era_current=current_block, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 17951b9c15..4827647cc3 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -5267,6 +5267,7 @@ def sign_and_send_extrinsic( nonce: Optional[int] = None, *, period: Optional[int] = DEFAULT_PERIOD, + era_current: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, @@ -5285,6 +5286,8 @@ def sign_and_send_extrinsic( period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + era_current: The block number to anchor the mortal era to. If not provided, the substrate interface chooses + the anchor. raise_error: Raises the relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait until the extrinsic call is included on the chain wait_for_finalization: Whether to wait until the extrinsic call is finalized on the chain @@ -5324,6 +5327,8 @@ def sign_and_send_extrinsic( if period is not None: extrinsic_data["era"] = {"period": period} + if era_current is not None: + extrinsic_data["era"]["current"] = era_current extrinsic_response.extrinsic = self.substrate.create_signed_extrinsic( **extrinsic_data diff --git a/mypy.ini b/mypy.ini index d38bdc7172..054851282e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,6 +1,13 @@ [mypy] ignore_missing_imports = True ignore_errors = True +follow_imports_for_stubs = True + +[mypy-numpy.*] +follow_imports = skip + +[mypy-numpy] +follow_imports = skip [mypy-*.axon.*] ignore_errors = False diff --git a/tests/unit_tests/extrinsics/asyncex/test_mev_shield.py b/tests/unit_tests/extrinsics/asyncex/test_mev_shield.py index 241203f80d..a1c9847243 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_mev_shield.py +++ b/tests/unit_tests/extrinsics/asyncex/test_mev_shield.py @@ -127,6 +127,7 @@ async def test_submit_encrypted_extrinsic_success_with_revealed_execution( signed_extrinsic_hash = f"0x{signed_extrinsic_hash_hex}" current_nonce = 5 next_nonce = 6 + current_block = 12345 block_hash = "0xblockhash" mocked_unlock_wallet = mocker.patch.object( @@ -144,6 +145,11 @@ async def test_submit_encrypted_extrinsic_success_with_revealed_execution( "get_account_next_index", new=mocker.AsyncMock(side_effect=[current_nonce, next_nonce]), ) + mocker.patch.object( + subtensor.substrate, + "get_block_number", + new=mocker.AsyncMock(return_value=current_block), + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = signed_extrinsic_hash_hex mocked_create_signed_extrinsic = mocker.patch.object( @@ -221,7 +227,7 @@ async def test_submit_encrypted_extrinsic_success_with_revealed_execution( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -237,6 +243,7 @@ async def test_submit_encrypted_extrinsic_success_with_revealed_execution( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -272,6 +279,7 @@ async def test_submit_encrypted_extrinsic_success_without_revealed_execution( signed_extrinsic_hash = f"0x{signed_extrinsic_hash_hex}" current_nonce = 5 next_nonce = 6 + current_block = 12345 mocked_unlock_wallet = mocker.patch.object( ExtrinsicResponse, @@ -288,6 +296,11 @@ async def test_submit_encrypted_extrinsic_success_without_revealed_execution( "get_account_next_index", new=mocker.AsyncMock(side_effect=[current_nonce, next_nonce]), ) + mocker.patch.object( + subtensor.substrate, + "get_block_number", + new=mocker.AsyncMock(return_value=current_block), + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = signed_extrinsic_hash_hex mocked_create_signed_extrinsic = mocker.patch.object( @@ -338,7 +351,7 @@ async def test_submit_encrypted_extrinsic_success_without_revealed_execution( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -354,6 +367,7 @@ async def test_submit_encrypted_extrinsic_success_without_revealed_execution( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -484,6 +498,7 @@ async def test_submit_encrypted_extrinsic_encrypted_submitted_event_not_found( mev_ciphertext = b"fake_ciphertext" current_nonce = 5 next_nonce = 6 + current_block = 12345 mocked_unlock_wallet = mocker.patch.object( ExtrinsicResponse, @@ -500,6 +515,11 @@ async def test_submit_encrypted_extrinsic_encrypted_submitted_event_not_found( "get_account_next_index", new=mocker.AsyncMock(side_effect=[current_nonce, next_nonce]), ) + mocker.patch.object( + subtensor.substrate, + "get_block_number", + new=mocker.AsyncMock(return_value=current_block), + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = "abcdef123456" mocked_create_signed_extrinsic = mocker.patch.object( @@ -558,7 +578,7 @@ async def test_submit_encrypted_extrinsic_encrypted_submitted_event_not_found( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -574,6 +594,7 @@ async def test_submit_encrypted_extrinsic_encrypted_submitted_event_not_found( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -604,6 +625,7 @@ async def test_submit_encrypted_extrinsic_failed_to_find_outcome( signed_extrinsic_hash = f"0x{signed_extrinsic_hash_hex}" current_nonce = 5 next_nonce = 6 + current_block = 12345 block_hash = "0xblockhash" mocked_unlock_wallet = mocker.patch.object( @@ -621,6 +643,11 @@ async def test_submit_encrypted_extrinsic_failed_to_find_outcome( "get_account_next_index", new=mocker.AsyncMock(side_effect=[current_nonce, next_nonce]), ) + mocker.patch.object( + subtensor.substrate, + "get_block_number", + new=mocker.AsyncMock(return_value=current_block), + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = signed_extrinsic_hash_hex mocked_create_signed_extrinsic = mocker.patch.object( @@ -695,7 +722,7 @@ async def test_submit_encrypted_extrinsic_failed_to_find_outcome( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -711,6 +738,7 @@ async def test_submit_encrypted_extrinsic_failed_to_find_outcome( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -747,6 +775,7 @@ async def test_submit_encrypted_extrinsic_execution_failure( signed_extrinsic_hash = f"0x{signed_extrinsic_hash_hex}" current_nonce = 5 next_nonce = 6 + current_block = 12345 block_hash = "0xblockhash" error_message = "Execution failed" formatted_error = "Formatted error: Execution failed" @@ -766,6 +795,11 @@ async def test_submit_encrypted_extrinsic_execution_failure( "get_account_next_index", new=mocker.AsyncMock(side_effect=[current_nonce, next_nonce]), ) + mocker.patch.object( + subtensor.substrate, + "get_block_number", + new=mocker.AsyncMock(return_value=current_block), + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = signed_extrinsic_hash_hex mocked_create_signed_extrinsic = mocker.patch.object( @@ -847,7 +881,7 @@ async def test_submit_encrypted_extrinsic_execution_failure( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -863,6 +897,7 @@ async def test_submit_encrypted_extrinsic_execution_failure( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -898,6 +933,7 @@ async def test_submit_encrypted_extrinsic_sign_and_send_failure( mev_ciphertext = b"fake_ciphertext" current_nonce = 5 next_nonce = 6 + current_block = 12345 mocked_unlock_wallet = mocker.patch.object( ExtrinsicResponse, @@ -914,6 +950,11 @@ async def test_submit_encrypted_extrinsic_sign_and_send_failure( "get_account_next_index", new=mocker.AsyncMock(side_effect=[current_nonce, next_nonce]), ) + mocker.patch.object( + subtensor.substrate, + "get_block_number", + new=mocker.AsyncMock(return_value=current_block), + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = "abcdef123456" mocked_create_signed_extrinsic = mocker.patch.object( @@ -963,7 +1004,7 @@ async def test_submit_encrypted_extrinsic_sign_and_send_failure( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -979,6 +1020,7 @@ async def test_submit_encrypted_extrinsic_sign_and_send_failure( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -999,6 +1041,7 @@ async def test_submit_encrypted_extrinsic_with_hotkey(subtensor, fake_wallet, mo mev_ciphertext = b"fake_ciphertext" current_nonce = 5 next_nonce = 6 + current_block = 12345 mocked_unlock_wallet = mocker.patch.object( ExtrinsicResponse, @@ -1015,6 +1058,11 @@ async def test_submit_encrypted_extrinsic_with_hotkey(subtensor, fake_wallet, mo "get_account_next_index", new=mocker.AsyncMock(side_effect=[current_nonce, next_nonce]), ) + mocker.patch.object( + subtensor.substrate, + "get_block_number", + new=mocker.AsyncMock(return_value=current_block), + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = "abcdef123456" mocked_create_signed_extrinsic = mocker.patch.object( @@ -1065,7 +1113,7 @@ async def test_submit_encrypted_extrinsic_with_hotkey(subtensor, fake_wallet, mo call=call, keypair=fake_wallet.hotkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -1081,6 +1129,7 @@ async def test_submit_encrypted_extrinsic_with_hotkey(subtensor, fake_wallet, mo call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -1101,6 +1150,7 @@ async def test_submit_encrypted_extrinsic_with_period(subtensor, fake_wallet, mo mev_ciphertext = b"fake_ciphertext" current_nonce = 5 next_nonce = 6 + current_block = 12345 period = 64 mocked_unlock_wallet = mocker.patch.object( @@ -1118,6 +1168,11 @@ async def test_submit_encrypted_extrinsic_with_period(subtensor, fake_wallet, mo "get_account_next_index", new=mocker.AsyncMock(side_effect=[current_nonce, next_nonce]), ) + mocker.patch.object( + subtensor.substrate, + "get_block_number", + new=mocker.AsyncMock(return_value=current_block), + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = "abcdef123456" mocked_create_signed_extrinsic = mocker.patch.object( @@ -1168,7 +1223,7 @@ async def test_submit_encrypted_extrinsic_with_period(subtensor, fake_wallet, mo call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": MAX_MEV_SHIELD_PERIOD}, + era={"period": MAX_MEV_SHIELD_PERIOD, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -1184,6 +1239,7 @@ async def test_submit_encrypted_extrinsic_with_period(subtensor, fake_wallet, mo call=mock_extrinsic_call, nonce=current_nonce, period=MAX_MEV_SHIELD_PERIOD, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, diff --git a/tests/unit_tests/extrinsics/test_mev_shield.py b/tests/unit_tests/extrinsics/test_mev_shield.py index 549c3d55b9..41aad993a5 100644 --- a/tests/unit_tests/extrinsics/test_mev_shield.py +++ b/tests/unit_tests/extrinsics/test_mev_shield.py @@ -111,6 +111,7 @@ def test_submit_encrypted_extrinsic_success_with_revealed_execution( signed_extrinsic_hash = f"0x{signed_extrinsic_hash_hex}" current_nonce = 5 next_nonce = 6 + current_block = 12345 block_hash = "0xblockhash" mocked_unlock_wallet = mocker.patch.object( @@ -126,6 +127,9 @@ def test_submit_encrypted_extrinsic_success_with_revealed_execution( "get_account_next_index", return_value=current_nonce, ) + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = signed_extrinsic_hash_hex mocked_create_signed_extrinsic = mocker.patch.object( @@ -196,7 +200,7 @@ def test_submit_encrypted_extrinsic_success_with_revealed_execution( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -212,6 +216,7 @@ def test_submit_encrypted_extrinsic_success_with_revealed_execution( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -249,6 +254,7 @@ def test_submit_encrypted_extrinsic_success_without_revealed_execution( signed_extrinsic_hash = f"0x{signed_extrinsic_hash_hex}" current_nonce = 5 next_nonce = 6 + current_block = 12345 mocked_unlock_wallet = mocker.patch.object( ExtrinsicResponse, @@ -263,6 +269,9 @@ def test_submit_encrypted_extrinsic_success_without_revealed_execution( "get_account_next_index", return_value=current_nonce, ) + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = signed_extrinsic_hash_hex mocked_create_signed_extrinsic = mocker.patch.object( @@ -308,7 +317,7 @@ def test_submit_encrypted_extrinsic_success_without_revealed_execution( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -324,6 +333,7 @@ def test_submit_encrypted_extrinsic_success_without_revealed_execution( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -445,6 +455,7 @@ def test_submit_encrypted_extrinsic_encrypted_submitted_event_not_found( mev_ciphertext = b"fake_ciphertext" current_nonce = 5 next_nonce = 6 + current_block = 12345 mocked_unlock_wallet = mocker.patch.object( ExtrinsicResponse, @@ -459,6 +470,9 @@ def test_submit_encrypted_extrinsic_encrypted_submitted_event_not_found( "get_account_next_index", return_value=current_nonce, ) + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = "abcdef123456" mocked_create_signed_extrinsic = mocker.patch.object( @@ -510,7 +524,7 @@ def test_submit_encrypted_extrinsic_encrypted_submitted_event_not_found( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -526,6 +540,7 @@ def test_submit_encrypted_extrinsic_encrypted_submitted_event_not_found( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -555,6 +570,7 @@ def test_submit_encrypted_extrinsic_failed_to_find_outcome( signed_extrinsic_hash = f"0x{signed_extrinsic_hash_hex}" current_nonce = 5 next_nonce = 6 + current_block = 12345 block_hash = "0xblockhash" mocked_unlock_wallet = mocker.patch.object( @@ -570,6 +586,9 @@ def test_submit_encrypted_extrinsic_failed_to_find_outcome( "get_account_next_index", return_value=current_nonce, ) + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = signed_extrinsic_hash_hex mocked_create_signed_extrinsic = mocker.patch.object( @@ -636,7 +655,7 @@ def test_submit_encrypted_extrinsic_failed_to_find_outcome( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -652,6 +671,7 @@ def test_submit_encrypted_extrinsic_failed_to_find_outcome( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -685,6 +705,7 @@ def test_submit_encrypted_extrinsic_execution_failure(subtensor, fake_wallet, mo signed_extrinsic_hash = f"0x{signed_extrinsic_hash_hex}" current_nonce = 5 next_nonce = 6 + current_block = 12345 block_hash = "0xblockhash" error_message = "Execution failed" formatted_error = "Formatted error: Execution failed" @@ -702,6 +723,9 @@ def test_submit_encrypted_extrinsic_execution_failure(subtensor, fake_wallet, mo "get_account_next_index", return_value=current_nonce, ) + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = signed_extrinsic_hash_hex mocked_create_signed_extrinsic = mocker.patch.object( @@ -775,7 +799,7 @@ def test_submit_encrypted_extrinsic_execution_failure(subtensor, fake_wallet, mo call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -791,6 +815,7 @@ def test_submit_encrypted_extrinsic_execution_failure(subtensor, fake_wallet, mo call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -825,6 +850,7 @@ def test_submit_encrypted_extrinsic_sign_and_send_failure( mev_ciphertext = b"fake_ciphertext" current_nonce = 5 next_nonce = 6 + current_block = 12345 mocked_unlock_wallet = mocker.patch.object( ExtrinsicResponse, @@ -839,6 +865,9 @@ def test_submit_encrypted_extrinsic_sign_and_send_failure( "get_account_next_index", return_value=current_nonce, ) + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = "abcdef123456" mocked_create_signed_extrinsic = mocker.patch.object( @@ -883,7 +912,7 @@ def test_submit_encrypted_extrinsic_sign_and_send_failure( call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -899,6 +928,7 @@ def test_submit_encrypted_extrinsic_sign_and_send_failure( call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -918,6 +948,7 @@ def test_submit_encrypted_extrinsic_with_hotkey(subtensor, fake_wallet, mocker): mev_ciphertext = b"fake_ciphertext" current_nonce = 5 next_nonce = 6 + current_block = 12345 mocked_unlock_wallet = mocker.patch.object( ExtrinsicResponse, @@ -932,6 +963,9 @@ def test_submit_encrypted_extrinsic_with_hotkey(subtensor, fake_wallet, mocker): "get_account_next_index", return_value=current_nonce, ) + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = "abcdef123456" mocked_create_signed_extrinsic = mocker.patch.object( @@ -977,7 +1011,7 @@ def test_submit_encrypted_extrinsic_with_hotkey(subtensor, fake_wallet, mocker): call=call, keypair=fake_wallet.hotkey, nonce=next_nonce, - era={"period": 8}, + era={"period": 8, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -993,6 +1027,7 @@ def test_submit_encrypted_extrinsic_with_hotkey(subtensor, fake_wallet, mocker): call=mock_extrinsic_call, nonce=current_nonce, period=8, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, @@ -1012,6 +1047,7 @@ def test_submit_encrypted_extrinsic_with_period(subtensor, fake_wallet, mocker): mev_ciphertext = b"fake_ciphertext" current_nonce = 5 next_nonce = 6 + current_block = 12345 period = 64 mocked_unlock_wallet = mocker.patch.object( @@ -1027,6 +1063,9 @@ def test_submit_encrypted_extrinsic_with_period(subtensor, fake_wallet, mocker): "get_account_next_index", return_value=current_nonce, ) + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block + ) mock_signed_extrinsic = mocker.MagicMock() mock_signed_extrinsic.extrinsic_hash.hex.return_value = "abcdef123456" mocked_create_signed_extrinsic = mocker.patch.object( @@ -1072,7 +1111,7 @@ def test_submit_encrypted_extrinsic_with_period(subtensor, fake_wallet, mocker): call=call, keypair=fake_wallet.coldkey, nonce=next_nonce, - era={"period": MAX_MEV_SHIELD_PERIOD}, + era={"period": MAX_MEV_SHIELD_PERIOD, "current": current_block}, ) mocked_get_mev_shielded_ciphertext.assert_called_once_with( signed_ext=mock_signed_extrinsic, @@ -1088,6 +1127,7 @@ def test_submit_encrypted_extrinsic_with_period(subtensor, fake_wallet, mocker): call=mock_extrinsic_call, nonce=current_nonce, period=MAX_MEV_SHIELD_PERIOD, + era_current=current_block, raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 94029b20ab..993a7063db 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -1784,6 +1784,42 @@ async def fake_is_success(): assert result.data is None +@pytest.mark.asyncio +async def test_sign_and_send_extrinsic_uses_era_current(subtensor, fake_wallet, mocker): + """Verify sign_and_send_extrinsic forwards an explicit era anchor.""" + fake_call = mocker.Mock() + fake_extrinsic = mocker.Mock() + fake_response = mocker.Mock() + fake_response.total_fee_amount = mocker.AsyncMock(return_value=1)() + fake_response.is_success = mocker.AsyncMock(return_value=True)() + + mocked_create_signed_extrinsic = mocker.AsyncMock(return_value=fake_extrinsic) + subtensor.substrate.create_signed_extrinsic = mocked_create_signed_extrinsic + + mocked_submit_extrinsic = mocker.AsyncMock(return_value=fake_response) + subtensor.substrate.submit_extrinsic = mocked_submit_extrinsic + + result = await subtensor.sign_and_send_extrinsic( + call=fake_call, + wallet=fake_wallet, + period=8, + era_current=12345, + ) + + mocked_create_signed_extrinsic.assert_awaited_once_with( + call=fake_call, + keypair=fake_wallet.coldkey, + era={"period": 8, "current": 12345}, + ) + mocked_submit_extrinsic.assert_awaited_once_with( + extrinsic=fake_extrinsic, + wait_for_inclusion=True, + wait_for_finalization=False, + ) + assert result.success is True + assert result.message == "Success" + + @pytest.mark.asyncio async def test_sign_and_send_extrinsic_error_finalization( subtensor, fake_wallet, mocker diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 4407381deb..fe71877095 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -101,6 +101,45 @@ def test_methods_comparable(mock_substrate): ) +def test_sign_and_send_extrinsic_uses_era_current(subtensor, fake_wallet, mocker): + """Verify sign_and_send_extrinsic forwards an explicit era anchor.""" + fake_call = mocker.Mock() + fake_extrinsic = mocker.Mock() + fake_response = mocker.Mock(is_success=True, total_fee_amount=1) + fake_wallet.coldkey = mocker.Mock() + + mocked_create_signed_extrinsic = mocker.patch.object( + subtensor.substrate, + "create_signed_extrinsic", + return_value=fake_extrinsic, + ) + mocked_submit_extrinsic = mocker.patch.object( + subtensor.substrate, + "submit_extrinsic", + return_value=fake_response, + ) + + result = subtensor.sign_and_send_extrinsic( + call=fake_call, + wallet=fake_wallet, + period=8, + era_current=12345, + ) + + mocked_create_signed_extrinsic.assert_called_once_with( + call=fake_call, + keypair=fake_wallet.coldkey, + era={"period": 8, "current": 12345}, + ) + mocked_submit_extrinsic.assert_called_once_with( + extrinsic=fake_extrinsic, + wait_for_inclusion=True, + wait_for_finalization=False, + ) + assert result.success is True + assert result.message == "Success" + + def test_serve_axon_with_external_ip_set(): internal_ip: str = "192.0.2.146" external_ip: str = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"