@@ -500,6 +500,95 @@ async def encode_params(
500500
501501 return param_data .to_hex ()
502502
503+ async def _runtime_method_exists (
504+ self , api : str , method : str , block_hash : str
505+ ) -> bool :
506+ """
507+ Check if a runtime call method exists at the given block.
508+
509+ The complicated logic here comes from the fact that there are two ways in which runtime calls
510+ are stored: the new and primary method is through the Metadata V15, but the V14 is a good backup (implemented
511+ around mid 2024)
512+
513+ Returns:
514+ True if the runtime call method exists, False otherwise.
515+ """
516+ runtime = await self .substrate .init_runtime (block_hash = block_hash )
517+ if runtime .metadata_v15 is not None :
518+ metadata_v15_value = runtime .metadata_v15 .value ()
519+ apis = {entry ["name" ]: entry for entry in metadata_v15_value ["apis" ]}
520+ try :
521+ api_entry = apis [api ]
522+ methods = {entry ["name" ]: entry for entry in api_entry ["methods" ]}
523+ _ = methods [method ]
524+ return True
525+ except KeyError :
526+ return False
527+ else :
528+ try :
529+ await self .substrate .get_metadata_runtime_call_function (
530+ api = api ,
531+ method = method ,
532+ block_hash = block_hash ,
533+ )
534+ return True
535+ except ValueError :
536+ return False
537+
538+ async def _query_with_fallback (
539+ self ,
540+ * args : tuple [str , str , Optional [list [Any ]]],
541+ block_hash : Optional [str ] = None ,
542+ default_value : Any = ValueError ,
543+ ):
544+ """
545+ Queries the subtensor node with a given set of args, falling back to the next group if the method
546+ does not exist at the given block. This method exists to support backwards compatibility for blocks.
547+
548+ Parameters:
549+ *args: Tuples containing (module, storage_function, params) in the order they should be attempted.
550+ block_hash: The hash of the block being queried. If not provided, the chain tip will be used.
551+ default_value: The default value to return if none of the methods exist at the given block.
552+
553+ Returns:
554+ The value returned by the subtensor node, or the default value if none of the methods exist at the given
555+ block.
556+
557+ Raises:
558+ ValueError: If no default value is provided, and none of the methods exist at the given block, a
559+ ValueError will be raised.
560+
561+ Example:
562+ value = await self._query_with_fallback(
563+ # the first attempt will be made to SubtensorModule.MechanismEmissionSplit with params `[1]`
564+ ("SubtensorModule", "MechanismEmissionSplit", [1]),
565+ # if it does not exist at the given block, the next attempt will be made to
566+ # SubtensorModule.MechanismEmission with params `None`
567+ ("SubtensorModule", "MechanismEmission", None),
568+ block_hash="0x1234",
569+ # if none of the methods exist at the given block, the default value of `None` will be returned
570+ default_value=None,
571+ )
572+ """
573+ if block_hash is None :
574+ block_hash = await self .substrate .get_chain_head ()
575+ for module , storage_function , params in args :
576+ if await self .substrate .get_metadata_storage_function (
577+ module_name = module ,
578+ storage_name = storage_function ,
579+ block_hash = block_hash ,
580+ ):
581+ return await self .substrate .query (
582+ module = module ,
583+ storage_function = storage_function ,
584+ block_hash = block_hash ,
585+ params = params ,
586+ )
587+ if not isinstance (default_value , ValueError ):
588+ return default_value
589+ else :
590+ raise default_value
591+
503592 async def get_hyperparameter (
504593 self ,
505594 param_name : str ,
@@ -2673,19 +2762,10 @@ async def get_mechanism_emission_split(
26732762 whole numbers). Returns None if emission is evenly split or if the data is unavailable.
26742763 """
26752764 block_hash = await self .determine_block_hash (block , block_hash , reuse_block )
2676- module = "SubtensorModule"
2677- storage_function = "MechanismEmissionSplit"
2678- if not await self .substrate .get_metadata_storage_function (
2679- module ,
2680- storage_function ,
2681- block_hash = block_hash ,
2682- ):
2683- return None
2684- result = await self .substrate .query (
2685- module = module ,
2686- storage_function = storage_function ,
2687- params = [netuid ],
2765+ result = await self ._query_with_fallback (
2766+ ("SubtensorModule" , "MechanismEmissionSplit" , [netuid ]),
26882767 block_hash = block_hash ,
2768+ default_value = None
26892769 )
26902770 if result is None or not hasattr (result , "value" ):
26912771 return None
@@ -2711,57 +2791,13 @@ async def get_mechanism_count(
27112791 The number of mechanisms for the given subnet.
27122792 """
27132793 block_hash = await self .determine_block_hash (block , block_hash , reuse_block )
2714- module = "SubtensorModule"
2715- storage_function = "MechanismCountCurrent"
2716- if not await self .substrate .get_metadata_storage_function (
2717- module ,
2718- storage_function ,
2719- block_hash = block_hash ,
2720- ):
2721- return 1
2722- query = await self .substrate .query (
2723- module = module ,
2724- storage_function = storage_function ,
2725- params = [netuid ],
2794+ query = await self ._query_with_fallback (
2795+ ("SubtensorModule" , "MechanismCountCurrent" , [netuid ]),
27262796 block_hash = block_hash ,
2797+ default_value = None ,
27272798 )
27282799 return getattr (query , "value" , 1 )
27292800
2730- async def _runtime_method_exists (
2731- self , api : str , method : str , block_hash : str
2732- ) -> bool :
2733- """
2734- Check if a runtime call method exists at the given block.
2735-
2736- The complicated logic here comes from the fact that there are two ways in which runtime calls
2737- are stored: the new and primary method is through the Metadata V15, but the V14 is a good backup (implemented
2738- around mid 2024)
2739-
2740- Returns:
2741- True if the runtime call method exists, False otherwise.
2742- """
2743- runtime = await self .substrate .init_runtime (block_hash = block_hash )
2744- if runtime .metadata_v15 is not None :
2745- metadata_v15_value = runtime .metadata_v15 .value ()
2746- apis = {entry ["name" ]: entry for entry in metadata_v15_value ["apis" ]}
2747- try :
2748- api_entry = apis [api ]
2749- methods = {entry ["name" ]: entry for entry in api_entry ["methods" ]}
2750- _ = methods [method ]
2751- return True
2752- except KeyError :
2753- return False
2754- else :
2755- try :
2756- await self .substrate .get_metadata_runtime_call_function (
2757- api = api ,
2758- method = method ,
2759- block_hash = block_hash ,
2760- )
2761- return True
2762- except ValueError :
2763- return False
2764-
27652801 async def get_metagraph_info (
27662802 self ,
27672803 netuid : int ,
0 commit comments