Created
July 22, 2025 23:31
-
-
Save areshand/507d4248237489d2acef5d0c26cd202b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import subprocess | |
| import json | |
| import sys | |
| # Feature flag definitions from types/src/on_chain_config/aptos_features.rs | |
| FEATURE_FLAGS = { | |
| 1: "CODE_DEPENDENCY_CHECK", | |
| 2: "TREAT_FRIEND_AS_PRIVATE", | |
| 3: "SHA_512_AND_RIPEMD_160_NATIVES", | |
| 4: "APTOS_STD_CHAIN_ID_NATIVES", | |
| 5: "VM_BINARY_FORMAT_V6", | |
| 6: "_DEPRECATED_COLLECT_AND_DISTRIBUTE_GAS_FEES", | |
| 7: "MULTI_ED25519_PK_VALIDATE_V2_NATIVES", | |
| 8: "BLAKE2B_256_NATIVE", | |
| 9: "RESOURCE_GROUPS", | |
| 10: "MULTISIG_ACCOUNTS", | |
| 11: "DELEGATION_POOLS", | |
| 12: "CRYPTOGRAPHY_ALGEBRA_NATIVES", | |
| 13: "BLS12_381_STRUCTURES", | |
| 14: "ED25519_PUBKEY_VALIDATE_RETURN_FALSE_WRONG_LENGTH", | |
| 15: "STRUCT_CONSTRUCTORS", | |
| 16: "PERIODICAL_REWARD_RATE_DECREASE", | |
| 17: "PARTIAL_GOVERNANCE_VOTING", | |
| 18: "SIGNATURE_CHECKER_V2", | |
| 19: "STORAGE_SLOT_METADATA", | |
| 20: "CHARGE_INVARIANT_VIOLATION", | |
| 21: "DELEGATION_POOL_PARTIAL_GOVERNANCE_VOTING", | |
| 22: "GAS_PAYER_ENABLED", | |
| 23: "APTOS_UNIQUE_IDENTIFIERS", | |
| 24: "BULLETPROOFS_NATIVES", | |
| 25: "SIGNER_NATIVE_FORMAT_FIX", | |
| 26: "MODULE_EVENT", | |
| 27: "EMIT_FEE_STATEMENT", | |
| 28: "STORAGE_DELETION_REFUND", | |
| 29: "SIGNATURE_CHECKER_V2_SCRIPT_FIX", | |
| 30: "AGGREGATOR_V2_API", | |
| 31: "SAFER_RESOURCE_GROUPS", | |
| 32: "SAFER_METADATA", | |
| 33: "SINGLE_SENDER_AUTHENTICATOR", | |
| 34: "SPONSORED_AUTOMATIC_ACCOUNT_V1_CREATION", | |
| 35: "FEE_PAYER_ACCOUNT_OPTIONAL", | |
| 36: "AGGREGATOR_V2_DELAYED_FIELDS", | |
| 37: "CONCURRENT_TOKEN_V2", | |
| 38: "LIMIT_MAX_IDENTIFIER_LENGTH", | |
| 39: "OPERATOR_BENEFICIARY_CHANGE", | |
| 40: "VM_BINARY_FORMAT_V7", | |
| 41: "RESOURCE_GROUPS_SPLIT_IN_VM_CHANGE_SET", | |
| 42: "COMMISSION_CHANGE_DELEGATION_POOL", | |
| 43: "BN254_STRUCTURES", | |
| 44: "WEBAUTHN_SIGNATURE", | |
| 45: "_DEPRECATED_RECONFIGURE_WITH_DKG", | |
| 46: "KEYLESS_ACCOUNTS", | |
| 47: "KEYLESS_BUT_ZKLESS_ACCOUNTS", | |
| 48: "_DEPRECATED_REMOVE_DETAILED_ERROR_FROM_HASH", | |
| 49: "JWK_CONSENSUS", | |
| 50: "CONCURRENT_FUNGIBLE_ASSETS", | |
| 51: "REFUNDABLE_BYTES", | |
| 52: "OBJECT_CODE_DEPLOYMENT", | |
| 53: "MAX_OBJECT_NESTING_CHECK", | |
| 54: "KEYLESS_ACCOUNTS_WITH_PASSKEYS", | |
| 55: "MULTISIG_V2_ENHANCEMENT", | |
| 56: "DELEGATION_POOL_ALLOWLISTING", | |
| 57: "MODULE_EVENT_MIGRATION", | |
| 58: "_REJECT_UNSTABLE_BYTECODE", | |
| 59: "TRANSACTION_CONTEXT_EXTENSION", | |
| 60: "COIN_TO_FUNGIBLE_ASSET_MIGRATION", | |
| 61: "PRIMARY_APT_FUNGIBLE_STORE_AT_USER_ADDRESS", | |
| 62: "OBJECT_NATIVE_DERIVED_ADDRESS", | |
| 63: "DISPATCHABLE_FUNGIBLE_ASSET", | |
| 64: "NEW_ACCOUNTS_DEFAULT_TO_FA_APT_STORE", | |
| 65: "OPERATIONS_DEFAULT_TO_FA_APT_STORE", | |
| 66: "AGGREGATOR_V2_IS_AT_LEAST_API", | |
| 67: "CONCURRENT_FUNGIBLE_BALANCE", | |
| 68: "DEFAULT_TO_CONCURRENT_FUNGIBLE_BALANCE", | |
| 69: "_LIMIT_VM_TYPE_SIZE", | |
| 70: "ABORT_IF_MULTISIG_PAYLOAD_MISMATCH", | |
| 71: "_DISALLOW_USER_NATIVES", | |
| 72: "ALLOW_SERIALIZED_SCRIPT_ARGS", | |
| 73: "_USE_COMPATIBILITY_CHECKER_V2", | |
| 74: "ENABLE_ENUM_TYPES", | |
| 75: "ENABLE_RESOURCE_ACCESS_CONTROL", | |
| 76: "_REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT", | |
| 77: "FEDERATED_KEYLESS", | |
| 78: "TRANSACTION_SIMULATION_ENHANCEMENT", | |
| 79: "COLLECTION_OWNER", | |
| 80: "NATIVE_MEMORY_OPERATIONS", | |
| 81: "_ENABLE_LOADER_V2", | |
| 82: "_DISALLOW_INIT_MODULE_TO_PUBLISH_MODULES", | |
| 83: "ENABLE_CALL_TREE_AND_INSTRUCTION_VM_CACHE", | |
| 84: "PERMISSIONED_SIGNER", | |
| 85: "ACCOUNT_ABSTRACTION", | |
| 86: "VM_BINARY_FORMAT_V8", | |
| 87: "BULLETPROOFS_BATCH_NATIVES", | |
| 88: "DERIVABLE_ACCOUNT_ABSTRACTION", | |
| 89: "ENABLE_FUNCTION_VALUES", | |
| 90: "NEW_ACCOUNTS_DEFAULT_TO_FA_STORE", | |
| 91: "DEFAULT_ACCOUNT_RESOURCE", | |
| 92: "JWK_CONSENSUS_PER_KEY_MODE", | |
| 93: "TRANSACTION_PAYLOAD_V2", | |
| 94: "ORDERLESS_TRANSACTIONS", | |
| 95: "ENABLE_LAZY_LOADING", | |
| 96: "CALCULATE_TRANSACTION_FEE_FOR_DISTRIBUTION", | |
| 97: "DISTRIBUTE_TRANSACTION_FEE", | |
| } | |
| def fetch_features(node_url="http://localhost:8080"): | |
| """Fetch features from Aptos node using curl""" | |
| try: | |
| # Ensure URL doesn't end with /v1 if it's already included | |
| if node_url.endswith('/v1'): | |
| api_url = f'{node_url}/accounts/0x1/resource/0x1::features::Features' | |
| else: | |
| api_url = f'{node_url}/v1/accounts/0x1/resource/0x1::features::Features' | |
| result = subprocess.run([ | |
| 'curl', '-s', api_url | |
| ], capture_output=True, text=True) | |
| if result.returncode != 0: | |
| raise Exception(f"Curl failed with return code {result.returncode}: {result.stderr}") | |
| if not result.stdout.strip(): | |
| raise Exception("Empty response from server") | |
| # Debug: print raw response if it's not valid JSON | |
| try: | |
| data = json.loads(result.stdout) | |
| except json.JSONDecodeError as e: | |
| print(f"Raw response: {result.stdout[:200]}...") | |
| raise Exception(f"Invalid JSON response: {e}") | |
| if 'data' not in data or 'features' not in data['data']: | |
| raise Exception(f"Unexpected response format: {data}") | |
| return data['data']['features'] | |
| except Exception as e: | |
| print(f"Error fetching features from {node_url}: {e}") | |
| return None | |
| def is_feature_enabled(features_hex, flag_number): | |
| """ | |
| Check if a feature flag is enabled using the exact same logic as Rust code. | |
| This matches the is_enabled function in types/src/on_chain_config/aptos_features.rs | |
| """ | |
| if features_hex.startswith('0x'): | |
| features_hex = features_hex[2:] | |
| features_bytes = bytes.fromhex(features_hex) | |
| # Rust logic: byte_index = (val / 8), bit_mask = 1 << (val % 8) | |
| byte_index = flag_number // 8 | |
| bit_mask = 1 << (flag_number % 8) | |
| if byte_index < len(features_bytes): | |
| return bool(features_bytes[byte_index] & bit_mask) | |
| return False | |
| def get_max_vm_version(features_hex): | |
| """ | |
| Determine maximum VM version using same logic as Rust get_max_binary_format_version | |
| """ | |
| if is_feature_enabled(features_hex, 86): # VM_BINARY_FORMAT_V8 | |
| return 8 | |
| elif is_feature_enabled(features_hex, 40): # VM_BINARY_FORMAT_V7 | |
| return 7 | |
| elif is_feature_enabled(features_hex, 5): # VM_BINARY_FORMAT_V6 | |
| return 6 | |
| else: | |
| return 5 | |
| def list_enabled_features(features_hex): | |
| """List all enabled features""" | |
| enabled = [] | |
| for flag_num in range(1, 98): # Check all known flags | |
| if is_feature_enabled(features_hex, flag_num): | |
| feature_name = FEATURE_FLAGS.get(flag_num, f"UNKNOWN_FEATURE_{flag_num}") | |
| enabled.append((flag_num, feature_name)) | |
| return enabled | |
| def main(): | |
| if len(sys.argv) > 1: | |
| if sys.argv[1] == "--help" or sys.argv[1] == "-h": | |
| print("Usage:") | |
| print(" python3 aptos_feature_checker.py [node_url] [feature_number]") | |
| print(" python3 aptos_feature_checker.py # Check localhost VM version") | |
| print(" python3 aptos_feature_checker.py localhost 5 # Check specific feature") | |
| print(" python3 aptos_feature_checker.py localhost --list # List all enabled features") | |
| return | |
| node_url = sys.argv[1] if sys.argv[1] != "localhost" else "http://localhost:8080" | |
| if len(sys.argv) > 2: | |
| if sys.argv[2] == "--list": | |
| # List all enabled features | |
| features_hex = fetch_features(node_url) | |
| if not features_hex: | |
| return | |
| enabled = list_enabled_features(features_hex) | |
| print(f"Features hex: {features_hex}") | |
| print(f"\nEnabled features ({len(enabled)} total):") | |
| print("=" * 50) | |
| for flag_num, feature_name in enabled: | |
| print(f"Feature {flag_num:2d}: {feature_name}") | |
| return | |
| else: | |
| # Check specific feature | |
| try: | |
| feature_number = int(sys.argv[2]) | |
| features_hex = fetch_features(node_url) | |
| if not features_hex: | |
| return | |
| is_enabled = is_feature_enabled(features_hex, feature_number) | |
| feature_name = FEATURE_FLAGS.get(feature_number, f"UNKNOWN_FEATURE_{feature_number}") | |
| print(f"Features hex: {features_hex}") | |
| print(f"Feature {feature_number} ({feature_name}): {'ENABLED' if is_enabled else 'DISABLED'}") | |
| return | |
| except ValueError: | |
| print("Error: Feature number must be an integer") | |
| return | |
| else: | |
| node_url = "http://localhost:8080" | |
| # Default: Check VM version | |
| features_hex = fetch_features(node_url) | |
| if not features_hex: | |
| return | |
| print(f"Features hex: {features_hex}") | |
| print() | |
| # Check VM version features | |
| vm_v6_enabled = is_feature_enabled(features_hex, 5) | |
| vm_v7_enabled = is_feature_enabled(features_hex, 40) | |
| vm_v8_enabled = is_feature_enabled(features_hex, 86) | |
| print("VM Version Analysis:") | |
| print("===================") | |
| print(f"VM_BINARY_FORMAT_V6 (feature 5): {'ENABLED' if vm_v6_enabled else 'DISABLED'}") | |
| print(f"VM_BINARY_FORMAT_V7 (feature 40): {'ENABLED' if vm_v7_enabled else 'DISABLED'}") | |
| print(f"VM_BINARY_FORMAT_V8 (feature 86): {'ENABLED' if vm_v8_enabled else 'DISABLED'}") | |
| max_version = get_max_vm_version(features_hex) | |
| print(f"\n🎯 Maximum VM Version: {max_version}") | |
| print(f"\nSupported Features:") | |
| print(f"==================") | |
| if max_version >= 8: | |
| print("✅ VM v8 features: (future features)") | |
| if max_version >= 7: | |
| print("✅ VM v7 features: enums, access specifiers, variant operations") | |
| if max_version >= 6: | |
| print("✅ VM v6 features: u16, u32, u256 types") | |
| print("✅ VM v1-v5 features: basic Move functionality") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment