Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Post-SOL fixes #178

Merged
merged 7 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 23 additions & 28 deletions src/aleph/sdk/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def load_chain_account_type(chain: Chain) -> Type[AccountFromPrivateKey]:
chain_account_map: Dict[Chain, Type[AccountFromPrivateKey]] = {
Chain.ETH: ETHAccount,
Chain.AVAX: ETHAccount,
Chain.SOL: SOLAccount,
Chain.BASE: ETHAccount,
Chain.SOL: SOLAccount,
}
return chain_account_map.get(chain) or ETHAccount

Expand All @@ -43,34 +43,29 @@ def _load_account(
private_key_path: Optional[Path] = None,
account_type: Optional[Type[AccountFromPrivateKey]] = None,
) -> AccountFromPrivateKey:
"""Load private key from a string or a file. takes the string argument in priority"""
if private_key_str or (private_key_path and private_key_path.is_file()):
if account_type:
if private_key_path and private_key_path.is_file():
return account_from_file(private_key_path, account_type)
elif private_key_str:
return account_from_hex_string(private_key_str, account_type)
else:
raise ValueError("Any private key specified")
"""Load an account from a private key string or file, or from the configuration file."""

# Loads configuration if no account_type is specified
if not account_type:
config = load_main_configuration(settings.CONFIG_FILE)
if config and hasattr(config, "chain"):
account_type = load_chain_account_type(config.chain)
logger.debug(
f"Detected {config.chain} account for path {settings.CONFIG_FILE}"
)
else:
main_configuration = load_main_configuration(settings.CONFIG_FILE)
if main_configuration:
account_type = load_chain_account_type(main_configuration.chain)
logger.debug(
f"Detected {main_configuration.chain} account for path {settings.CONFIG_FILE}"
)
else:
account_type = ETHAccount # Defaults to ETHAccount
logger.warning(
f"No main configuration data found in {settings.CONFIG_FILE}, defaulting to {account_type.__name__}"
)
if private_key_path and private_key_path.is_file():
return account_from_file(private_key_path, account_type)
elif private_key_str:
return account_from_hex_string(private_key_str, account_type)
else:
raise ValueError("Any private key specified")
account_type = ETHAccount # Defaults to ETHAccount
logger.warning(
f"No main configuration data found in {settings.CONFIG_FILE}, defaulting to {account_type.__name__}"
)

# Loads private key from a string
if private_key_str:
return account_from_hex_string(private_key_str, account_type)
# Loads private key from a file
elif private_key_path and private_key_path.is_file():
return account_from_file(private_key_path, account_type)
# For ledger keys
elif settings.REMOTE_CRYPTO_HOST:
logger.debug("Using remote account")
loop = asyncio.get_event_loop()
Expand All @@ -80,8 +75,8 @@ def _load_account(
unix_socket=settings.REMOTE_CRYPTO_UNIX_SOCKET,
)
)
# Fallback: config.path if set, else generate a new private key
else:
account_type = ETHAccount # Defaults to ETHAccount
new_private_key = get_fallback_private_key()
account = account_type(private_key=new_private_key)
logger.info(
Expand Down
5 changes: 5 additions & 0 deletions src/aleph/sdk/chains/ethereum.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import base64
from decimal import Decimal
from pathlib import Path
from typing import Awaitable, Optional, Union
Expand Down Expand Up @@ -61,6 +62,10 @@ def from_mnemonic(mnemonic: str, chain: Optional[Chain] = None) -> "ETHAccount":
private_key=Account.from_mnemonic(mnemonic=mnemonic).key, chain=chain
)

def export_private_key(self) -> str:
"""Export the private key using standard format."""
return f"0x{base64.b16encode(self.private_key).decode().lower()}"

def get_address(self) -> str:
return self._account.address

Expand Down
6 changes: 6 additions & 0 deletions src/aleph/sdk/chains/solana.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ async def sign_raw(self, buffer: bytes) -> bytes:
sig = self._signing_key.sign(buffer)
return sig.signature

def export_private_key(self) -> str:
"""Export the private key using Phantom format."""
return base58.b58encode(
self.private_key + self._signing_key.verify_key.encode()
).decode()

def get_address(self) -> str:
return encode(self._signing_key.verify_key)

Expand Down
17 changes: 12 additions & 5 deletions src/aleph/sdk/client/vm_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from urllib.parse import urlparse

import aiohttp
from aleph_message.models import ItemHash
from aleph_message.models import Chain, ItemHash
from eth_account.messages import encode_defunct
from jwcrypto import jwk

from aleph.sdk.chains.solana import SOLAccount
from aleph.sdk.types import Account
from aleph.sdk.utils import (
create_vm_control_payload,
Expand Down Expand Up @@ -36,11 +37,13 @@ def __init__(
self.account = account
self.ephemeral_key = jwk.JWK.generate(kty="EC", crv="P-256")
self.node_url = node_url.rstrip("/")
self.pubkey_payload = self._generate_pubkey_payload()
self.pubkey_payload = self._generate_pubkey_payload(
Chain.SOL if isinstance(account, SOLAccount) else Chain.ETH
)
self.pubkey_signature_header = ""
self.session = session or aiohttp.ClientSession()

def _generate_pubkey_payload(self) -> Dict[str, Any]:
def _generate_pubkey_payload(self, chain: Chain = Chain.ETH) -> Dict[str, Any]:
return {
"pubkey": json.loads(self.ephemeral_key.export_public()),
"alg": "ECDSA",
Expand All @@ -50,12 +53,16 @@ def _generate_pubkey_payload(self) -> Dict[str, Any]:
datetime.datetime.utcnow() + datetime.timedelta(days=1)
).isoformat()
+ "Z",
"chain": chain.value,
}

async def _generate_pubkey_signature_header(self) -> str:
pubkey_payload = json.dumps(self.pubkey_payload).encode("utf-8").hex()
signable_message = encode_defunct(hexstr=pubkey_payload)
buffer_to_sign = signable_message.body
if isinstance(self.account, SOLAccount):
buffer_to_sign = bytes(pubkey_payload, encoding="utf-8")
else:
signable_message = encode_defunct(hexstr=pubkey_payload)
buffer_to_sign = signable_message.body

signed_message = await self.account.sign_raw(buffer_to_sign)
pubkey_signature = to_0x_hex(signed_message)
Expand Down
2 changes: 2 additions & 0 deletions src/aleph/sdk/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def __init__(self, private_key: bytes): ...

async def sign_raw(self, buffer: bytes) -> bytes: ...

def export_private_key(self) -> str: ...


GenericMessage = TypeVar("GenericMessage", bound=AlephMessage)

Expand Down
Loading