feat: migrate wallet daemon and CLI to use centralized aitbc package utilities
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 9s
CLI Tests / test-cli (push) Failing after 3s
Integration Tests / test-service-integration (push) Successful in 41s
Python Tests / test-python (push) Failing after 18s
Security Scanning / security-scan (push) Failing after 2m0s

- Migrate simple_daemon.py from mock data to real keystore and blockchain RPC integration
- Add httpx for async HTTP client in wallet daemon
- Implement real wallet listing from keystore directory
- Implement blockchain balance queries via RPC
- Update CLI to use aitbc.AITBCHTTPClient instead of requests
- Add aitbc imports: constants, http_client, exceptions, logging, paths, validation
- Add address and amount validation in
This commit is contained in:
aitbc
2026-04-24 22:05:55 +02:00
parent 154627cdfa
commit cbd8700984
13 changed files with 724 additions and 455 deletions

View File

@@ -8,9 +8,11 @@ import json
import base64
from typing import Dict, Any, Optional, List
from pathlib import Path
import httpx
from dataclasses import dataclass
from aitbc.http_client import AITBCHTTPClient
from aitbc.exceptions import NetworkError
from utils import error, success
from config import Config
@@ -65,10 +67,10 @@ class WalletDaemonClient:
self.config = config
self.base_url = config.wallet_url.rstrip('/')
self.timeout = getattr(config, 'timeout', 30)
def _get_http_client(self) -> httpx.Client:
def _get_http_client(self) -> AITBCHTTPClient:
"""Create HTTP client with appropriate settings"""
return httpx.Client(
return AITBCHTTPClient(
base_url=self.base_url,
timeout=self.timeout,
headers={"Content-Type": "application/json"}
@@ -77,47 +79,46 @@ class WalletDaemonClient:
def is_available(self) -> bool:
"""Check if wallet daemon is available and responsive"""
try:
with self._get_http_client() as client:
response = client.get("/health")
return response.status_code == 200
client = self._get_http_client()
client.get("/health")
return True
except NetworkError:
return False
except Exception:
return False
def get_status(self) -> Dict[str, Any]:
"""Get wallet daemon status information"""
try:
with self._get_http_client() as client:
response = client.get("/health")
if response.status_code == 200:
return response.json()
else:
return {"status": "unavailable", "error": f"HTTP {response.status_code}"}
client = self._get_http_client()
return client.get("/health")
except NetworkError as e:
return {"status": "unavailable", "error": str(e)}
except Exception as e:
return {"status": "error", "error": str(e)}
def create_wallet(self, wallet_id: str, password: str, metadata: Optional[Dict[str, Any]] = None) -> WalletInfo:
"""Create a new wallet in the daemon"""
try:
with self._get_http_client() as client:
payload = {
"wallet_id": wallet_id,
"password": password,
"metadata": metadata or {}
}
response = client.post("/v1/wallets", json=payload)
if response.status_code == 201:
data = response.json()
return WalletInfo(
wallet_id=data["wallet_id"],
public_key=data["public_key"],
address=data.get("address"),
created_at=data.get("created_at"),
metadata=data.get("metadata")
)
else:
error(f"Failed to create wallet: {response.text}")
raise Exception(f"HTTP {response.status_code}: {response.text}")
client = self._get_http_client()
payload = {
"wallet_id": wallet_id,
"password": password,
"metadata": metadata or {}
}
data = client.post("/v1/wallets", json=payload)
return WalletInfo(
wallet_id=data["wallet_id"],
chain_id=data.get("chain_id", "default"),
public_key=data["public_key"],
address=data.get("address"),
created_at=data.get("created_at"),
metadata=data.get("metadata")
)
except NetworkError as e:
error(f"Error creating wallet: {e}")
raise
except Exception as e:
error(f"Error creating wallet: {str(e)}")
raise
@@ -125,23 +126,24 @@ class WalletDaemonClient:
def list_wallets(self) -> List[WalletInfo]:
"""List all wallets in the daemon"""
try:
with self._get_http_client() as client:
response = client.get("/v1/wallets")
if response.status_code == 200:
data = response.json()
wallets = []
for wallet_data in data.get("wallets", []):
wallets.append(WalletInfo(
wallet_id=wallet_data["wallet_id"],
public_key=wallet_data["public_key"],
address=wallet_data.get("address"),
created_at=wallet_data.get("created_at"),
metadata=wallet_data.get("metadata")
))
return wallets
else:
error(f"Failed to list wallets: {response.text}")
raise Exception(f"HTTP {response.status_code}: {response.text}")
client = self._get_http_client()
data = client.get("/v1/wallets")
wallets = []
# Handle both "wallets" and "items" keys for compatibility
wallet_list = data.get("wallets", data.get("items", []))
for wallet_data in wallet_list:
wallets.append(WalletInfo(
wallet_id=wallet_data.get("wallet_id", wallet_data.get("wallet_name", "")),
chain_id=wallet_data.get("chain_id", "default"),
public_key=wallet_data.get("public_key", ""),
address=wallet_data.get("address", ""),
created_at=wallet_data.get("created_at", ""),
metadata=wallet_data.get("metadata", {})
))
return wallets
except NetworkError as e:
error(f"Failed to list daemon wallets: {str(e)}")
raise
except Exception as e:
error(f"Error listing wallets: {str(e)}")
raise
@@ -149,47 +151,41 @@ class WalletDaemonClient:
def get_wallet_info(self, wallet_id: str) -> Optional[WalletInfo]:
"""Get information about a specific wallet"""
try:
with self._get_http_client() as client:
response = client.get(f"/v1/wallets/{wallet_id}")
if response.status_code == 200:
data = response.json()
return WalletInfo(
wallet_id=data["wallet_id"],
public_key=data["public_key"],
address=data.get("address"),
created_at=data.get("created_at"),
metadata=data.get("metadata")
)
elif response.status_code == 404:
return None
else:
error(f"Failed to get wallet info: {response.text}")
raise Exception(f"HTTP {response.status_code}: {response.text}")
client = self._get_http_client()
data = client.get(f"/v1/wallets/{wallet_id}")
return WalletInfo(
wallet_id=data["wallet_id"],
chain_id=data.get("chain_id", "default"),
public_key=data["public_key"],
address=data.get("address"),
created_at=data.get("created_at"),
metadata=data.get("metadata")
)
except NetworkError as e:
error(f"Failed to get wallet info: {e}")
return None
except Exception as e:
error(f"Error getting wallet info: {str(e)}")
raise
return None
def get_wallet_balance(self, wallet_id: str) -> Optional[WalletBalance]:
"""Get wallet balance from daemon"""
try:
with self._get_http_client() as client:
response = client.get(f"/v1/wallets/{wallet_id}/balance")
if response.status_code == 200:
data = response.json()
return WalletBalance(
wallet_id=wallet_id,
balance=data["balance"],
address=data.get("address"),
last_updated=data.get("last_updated")
)
elif response.status_code == 404:
return None
else:
error(f"Failed to get wallet balance: {response.text}")
raise Exception(f"HTTP {response.status_code}: {response.text}")
client = self._get_http_client()
data = client.get(f"/v1/wallets/{wallet_id}/balance")
return WalletBalance(
wallet_id=wallet_id,
chain_id=data.get("chain_id", "default"),
balance=data["balance"],
address=data.get("address"),
last_updated=data.get("last_updated")
)
except NetworkError as e:
error(f"Failed to get wallet balance: {e}")
return None
except Exception as e:
error(f"Error getting wallet balance: {str(e)}")
raise
return None
def sign_message(self, wallet_id: str, password: str, message: bytes) -> str:
"""Sign a message with wallet private key"""
@@ -349,6 +345,31 @@ class WalletDaemonClient:
error(f"Error creating chain: {str(e)}")
raise
def create_wallet(self, wallet_id: str, password: str, metadata: Optional[Dict[str, Any]] = None) -> WalletInfo:
"""Create a new wallet in the daemon"""
try:
client = self._get_http_client()
payload = {
"wallet_id": wallet_id,
"password": password,
"metadata": metadata or {}
}
data = client.post("/v1/wallets", json=payload)
return WalletInfo(
wallet_id=data["wallet_id"],
public_key=data["public_key"],
address=data.get("address"),
created_at=data.get("created_at"),
metadata=data.get("metadata")
)
except NetworkError as e:
error(f"Failed to create wallet: {e}")
raise
except Exception as e:
error(f"Error creating wallet: {str(e)}")
raise
def create_wallet_in_chain(self, chain_id: str, wallet_id: str, password: str,
metadata: Optional[Dict[str, Any]] = None) -> WalletInfo:
"""Create a wallet in a specific chain"""