11import asyncio
2- import json
32import logging
43from pathlib import Path
5- from typing import Dict , List , Optional , Type , TypeVar , Union , overload
4+ from typing import Dict , Optional , Type , TypeVar
65
7- import base58
86from aleph_message .models import Chain
97
108from aleph .sdk .chains .common import get_fallback_private_key
119from aleph .sdk .chains .ethereum import ETHAccount
1210from aleph .sdk .chains .remote import RemoteAccount
13- from aleph .sdk .chains .solana import (
14- SOLAccount ,
15- parse_solana_private_key ,
16- solana_private_key_from_bytes ,
17- )
18- from aleph .sdk .conf import settings
11+ from aleph .sdk .chains .solana import SOLAccount
12+ from aleph .sdk .conf import load_main_configuration , settings
1913from aleph .sdk .types import AccountFromPrivateKey
20- from aleph .sdk .utils import load_account_key_context
2114
2215logger = logging .getLogger (__name__ )
2316
2417T = TypeVar ("T" , bound = AccountFromPrivateKey )
2518
26- CHAIN_TO_ACCOUNT_MAP : Dict [Chain , Type [AccountFromPrivateKey ]] = {
27- Chain .ETH : ETHAccount ,
28- Chain .AVAX : ETHAccount ,
29- Chain .SOL : SOLAccount ,
30- Chain .BASE : ETHAccount ,
31- }
3219
33-
34- def detect_chain_from_private_key (private_key : Union [str , List [int ], bytes ]) -> Chain :
35- """
36- Detect the blockchain chain based on the private key format.
37- - Chain.ETH for Ethereum (EVM) private keys
38- - Chain.SOL for Solana private keys (base58 or uint8 format).
39-
40- Raises:
41- ValueError: If the private key format is invalid or not recognized.
42- """
43- if isinstance (private_key , (str , bytes )) and is_valid_private_key (
44- private_key , ETHAccount
45- ):
46- return Chain .ETH
47-
48- elif is_valid_private_key (private_key , SOLAccount ):
49- return Chain .SOL
50-
51- else :
52- raise ValueError ("Unsupported private key format. Unable to detect chain." )
53-
54-
55- @overload
56- def is_valid_private_key (
57- private_key : Union [str , bytes ], account_type : Type [ETHAccount ]
58- ) -> bool : ...
59-
60-
61- @overload
62- def is_valid_private_key (
63- private_key : Union [str , List [int ], bytes ], account_type : Type [SOLAccount ]
64- ) -> bool : ...
65-
66-
67- def is_valid_private_key (
68- private_key : Union [str , List [int ], bytes ], account_type : Type [T ]
69- ) -> bool :
70- """
71- Check if the private key is valid for either Ethereum or Solana based on the account type.
72- """
73- try :
74- if account_type == ETHAccount :
75- # Handle Ethereum private key validation
76- if isinstance (private_key , str ):
77- if private_key .startswith ("0x" ):
78- private_key = private_key [2 :]
79- private_key = bytes .fromhex (private_key )
80- elif isinstance (private_key , list ):
81- raise ValueError ("Ethereum keys cannot be a list of integers" )
82-
83- account_type (private_key )
84-
85- elif account_type == SOLAccount :
86- # Handle Solana private key validation
87- if isinstance (private_key , bytes ):
88- return len (private_key ) == 64
89- elif isinstance (private_key , str ):
90- decoded_key = base58 .b58decode (private_key )
91- return len (decoded_key ) == 64
92- elif isinstance (private_key , list ):
93- return len (private_key ) == 64 and all (
94- isinstance (i , int ) and 0 <= i <= 255 for i in private_key
95- )
96-
97- return True
98- except Exception :
99- return False
20+ def load_chain_account_type (chain : Chain ) -> Type [AccountFromPrivateKey ]:
21+ chain_account_map : Dict [Chain , Type [AccountFromPrivateKey ]] = {
22+ Chain .ETH : ETHAccount ,
23+ Chain .AVAX : ETHAccount ,
24+ Chain .SOL : SOLAccount ,
25+ Chain .BASE : ETHAccount ,
26+ }
27+ return chain_account_map .get (chain ) or ETHAccount
10028
10129
10230def account_from_hex_string (private_key_str : str , account_type : Type [T ]) -> T :
@@ -107,72 +35,42 @@ def account_from_hex_string(private_key_str: str, account_type: Type[T]) -> T:
10735
10836def account_from_file (private_key_path : Path , account_type : Type [T ]) -> T :
10937 private_key = private_key_path .read_bytes ()
110- if account_type == SOLAccount :
111- private_key = parse_solana_private_key (
112- solana_private_key_from_bytes (private_key )
113- )
114-
11538 return account_type (private_key )
11639
11740
11841def _load_account (
11942 private_key_str : Optional [str ] = None ,
12043 private_key_path : Optional [Path ] = None ,
121- account_type : Type [AccountFromPrivateKey ] = ETHAccount ,
44+ account_type : Optional [ Type [AccountFromPrivateKey ]] = None ,
12245) -> AccountFromPrivateKey :
12346 """Load private key from a string or a file. takes the string argument in priority"""
124-
125- if private_key_str :
126- # Check Account type based on private-key string format (base58 / uint for solana)
127- private_key_chain = detect_chain_from_private_key (private_key = private_key_str )
128- if private_key_chain == Chain .SOL :
129- account_type = SOLAccount
130- logger .debug ("Solana private key is detected" )
131- parsed_key = parse_solana_private_key (private_key_str )
132- return account_type (parsed_key )
133- logger .debug ("Using account from string" )
134- return account_from_hex_string (private_key_str , account_type )
135- elif private_key_path and private_key_path .is_file ():
136- if private_key_path :
137- account_type = ETHAccount # Default account type
138-
139- try :
140- account_data = load_account_key_context (settings .CONFIG_FILE )
141-
142- if account_data :
143- chain = Chain (account_data .chain )
144- account_type = (
145- CHAIN_TO_ACCOUNT_MAP .get (chain , ETHAccount ) or ETHAccount
146- )
147- logger .debug (
148- f"Detected { chain } account for path { private_key_path } "
149- )
150- else :
151- logger .warning (
152- f"No account data found in { private_key_path } , defaulting to { account_type .__name__ } "
153- )
154-
155- except FileNotFoundError :
156- logger .warning (
157- f"{ private_key_path } not found, using default account type { account_type .__name__ } "
47+ if private_key_str or (private_key_path and private_key_path .is_file ()):
48+ if account_type :
49+ if private_key_path and private_key_path .is_file ():
50+ return account_from_file (private_key_path , account_type )
51+ elif private_key_str :
52+ return account_from_hex_string (private_key_str , account_type )
53+ else :
54+ raise ValueError ("Any private key specified" )
55+ else :
56+ main_configuration = load_main_configuration (settings .CONFIG_FILE )
57+ if main_configuration :
58+ account_type = load_chain_account_type (main_configuration .chain )
59+ logger .debug (
60+ f"Detected { main_configuration .chain } account for path { settings .CONFIG_FILE } "
15861 )
159- except json .JSONDecodeError :
160- logger .error (
161- f"Invalid format in { private_key_path } , unable to load account info."
162- )
163- raise ValueError (f"Invalid format in { private_key_path } ." )
164- except KeyError as e :
165- logger .error (f"Missing key in account config: { e } " )
166- raise ValueError (
167- f"Invalid account data in { private_key_path } . Key { e } is missing."
168- )
169- except Exception as e :
170- logger .error (f"Error loading account from { private_key_path } : { e } " )
171- raise ValueError (
172- f"Could not load account data from { private_key_path } ."
62+ else :
63+ account_type = ETHAccount # Defaults to ETHAccount
64+ logger .warning (
65+ f"No main configuration data found in { settings .CONFIG_FILE } , defaulting to { account_type .__name__ } "
17366 )
67+ if private_key_path and private_key_path .is_file ():
68+ return account_from_file (private_key_path , account_type )
69+ elif private_key_str :
70+ return account_from_hex_string (private_key_str , account_type )
71+ else :
72+ raise ValueError ("Any private key specified" )
17473
175- return account_from_file (private_key_path , account_type )
17674 elif settings .REMOTE_CRYPTO_HOST :
17775 logger .debug ("Using remote account" )
17876 loop = asyncio .get_event_loop ()
@@ -183,6 +81,7 @@ def _load_account(
18381 )
18482 )
18583 else :
84+ account_type = ETHAccount # Defaults to ETHAccount
18685 new_private_key = get_fallback_private_key ()
18786 account = account_type (private_key = new_private_key )
18887 logger .info (
0 commit comments