refactor: improve gossip broker cleanup and enhance CLI wallet transaction handling
Some checks failed
AITBC CI/CD Pipeline / lint-and-test (3.11) (push) Has been cancelled
AITBC CI/CD Pipeline / lint-and-test (3.12) (push) Has been cancelled
AITBC CI/CD Pipeline / lint-and-test (3.13) (push) Has been cancelled
AITBC CI/CD Pipeline / test-cli (push) Has been cancelled
AITBC CI/CD Pipeline / test-services (push) Has been cancelled
AITBC CI/CD Pipeline / test-production-services (push) Has been cancelled
AITBC CI/CD Pipeline / security-scan (push) Has been cancelled
AITBC CI/CD Pipeline / build (push) Has been cancelled
AITBC CI/CD Pipeline / deploy-staging (push) Has been cancelled
AITBC CI/CD Pipeline / deploy-production (push) Has been cancelled
AITBC CI/CD Pipeline / performance-test (push) Has been cancelled
AITBC CI/CD Pipeline / docs (push) Has been cancelled
AITBC CI/CD Pipeline / release (push) Has been cancelled
AITBC CI/CD Pipeline / notify (push) Has been cancelled
Security Scanning / Bandit Security Scan (apps/coordinator-api/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (cli/aitbc_cli) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-core/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-crypto/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-sdk/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (tests) (push) Has been cancelled
Security Scanning / CodeQL Security Analysis (javascript) (push) Has been cancelled
Security Scanning / CodeQL Security Analysis (python) (push) Has been cancelled
Security Scanning / Dependency Security Scan (push) Has been cancelled
Security Scanning / Container Security Scan (push) Has been cancelled
Security Scanning / OSSF Scorecard (push) Has been cancelled
Security Scanning / Security Summary Report (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.11) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.12) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.13) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-summary (push) Has been cancelled
Some checks failed
AITBC CI/CD Pipeline / lint-and-test (3.11) (push) Has been cancelled
AITBC CI/CD Pipeline / lint-and-test (3.12) (push) Has been cancelled
AITBC CI/CD Pipeline / lint-and-test (3.13) (push) Has been cancelled
AITBC CI/CD Pipeline / test-cli (push) Has been cancelled
AITBC CI/CD Pipeline / test-services (push) Has been cancelled
AITBC CI/CD Pipeline / test-production-services (push) Has been cancelled
AITBC CI/CD Pipeline / security-scan (push) Has been cancelled
AITBC CI/CD Pipeline / build (push) Has been cancelled
AITBC CI/CD Pipeline / deploy-staging (push) Has been cancelled
AITBC CI/CD Pipeline / deploy-production (push) Has been cancelled
AITBC CI/CD Pipeline / performance-test (push) Has been cancelled
AITBC CI/CD Pipeline / docs (push) Has been cancelled
AITBC CI/CD Pipeline / release (push) Has been cancelled
AITBC CI/CD Pipeline / notify (push) Has been cancelled
Security Scanning / Bandit Security Scan (apps/coordinator-api/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (cli/aitbc_cli) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-core/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-crypto/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-sdk/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (tests) (push) Has been cancelled
Security Scanning / CodeQL Security Analysis (javascript) (push) Has been cancelled
Security Scanning / CodeQL Security Analysis (python) (push) Has been cancelled
Security Scanning / Dependency Security Scan (push) Has been cancelled
Security Scanning / Container Security Scan (push) Has been cancelled
Security Scanning / OSSF Scorecard (push) Has been cancelled
Security Scanning / Security Summary Report (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.11) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.12) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.13) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-summary (push) Has been cancelled
- Refactor _InProcessBroadcast.subscribe to use asynccontextmanager for cleaner resource cleanup - Remove manual release callback in _InProcessSubscriber - Update file-based wallet send to submit transactions via blockchain RPC - Fetch balance and nonce from chain before sending transactions - Add tx_hash and status tracking to local transaction history - Enhance run_subprocess to support additional kwargs and return Comp
This commit is contained in:
@@ -290,8 +290,11 @@ class DualModeWalletAdapter:
|
||||
|
||||
def _send_transaction_file(self, wallet_name: str, password: str, to_address: str,
|
||||
amount: float, description: Optional[str]) -> Dict[str, Any]:
|
||||
"""Send transaction using file-based storage"""
|
||||
"""Send transaction using file-based storage and blockchain RPC"""
|
||||
from .commands.wallet import _load_wallet, _save_wallet
|
||||
import httpx
|
||||
from .utils import error, success
|
||||
from datetime import datetime
|
||||
|
||||
wallet_path = self.wallet_dir / f"{wallet_name}.json"
|
||||
|
||||
@@ -300,36 +303,79 @@ class DualModeWalletAdapter:
|
||||
raise Exception("Wallet not found")
|
||||
|
||||
wallet_data = _load_wallet(wallet_path, wallet_name)
|
||||
balance = wallet_data.get("balance", 0)
|
||||
|
||||
if balance < amount:
|
||||
error(f"Insufficient balance. Available: {balance}, Required: {amount}")
|
||||
# Fetch current balance and nonce from blockchain
|
||||
from_address = wallet_data.get("address")
|
||||
if not from_address:
|
||||
error("Wallet does not have an address configured")
|
||||
raise Exception("Invalid wallet")
|
||||
|
||||
rpc_url = self.config.blockchain_rpc_url
|
||||
try:
|
||||
resp = httpx.get(f"{rpc_url}/rpc/getBalance/{from_address}?chain_id=ait-mainnet", timeout=5)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
chain_balance = data.get("balance", 0)
|
||||
nonce = data.get("nonce", 0)
|
||||
else:
|
||||
error(f"Failed to get balance from chain: {resp.text}")
|
||||
raise Exception("Chain error")
|
||||
except Exception as e:
|
||||
error(f"Failed to connect to blockchain RPC: {e}")
|
||||
raise
|
||||
|
||||
if chain_balance < amount:
|
||||
error(f"Insufficient blockchain balance. Available: {chain_balance}, Required: {amount}")
|
||||
raise Exception("Insufficient balance")
|
||||
|
||||
# Construct and send transaction
|
||||
tx_payload = {
|
||||
"type": "TRANSFER",
|
||||
"sender": from_address,
|
||||
"nonce": nonce,
|
||||
"fee": 0,
|
||||
"payload": {"to": to_address, "value": amount},
|
||||
"sig": "mock_signature" # Replace with real signature when implemented
|
||||
}
|
||||
|
||||
# Add transaction
|
||||
try:
|
||||
resp = httpx.post(f"{rpc_url}/rpc/sendTx", json=tx_payload, timeout=5)
|
||||
if resp.status_code not in (200, 201):
|
||||
error(f"Failed to submit transaction to chain: {resp.text}")
|
||||
raise Exception("Chain submission failed")
|
||||
tx_hash = resp.json().get("tx_hash")
|
||||
except Exception as e:
|
||||
error(f"Failed to send transaction to RPC: {e}")
|
||||
raise
|
||||
|
||||
# Add transaction to local history
|
||||
transaction = {
|
||||
"type": "send",
|
||||
"amount": -amount,
|
||||
"to_address": to_address,
|
||||
"description": description or "",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"tx_hash": tx_hash,
|
||||
"status": "pending"
|
||||
}
|
||||
|
||||
if "transactions" not in wallet_data:
|
||||
wallet_data["transactions"] = []
|
||||
|
||||
wallet_data["transactions"].append(transaction)
|
||||
wallet_data["balance"] = balance - amount
|
||||
wallet_data["balance"] = chain_balance - amount
|
||||
|
||||
# Save wallet
|
||||
save_password = password if wallet_data.get("encrypted") else None
|
||||
_save_wallet(wallet_path, wallet_data, save_password)
|
||||
|
||||
success(f"Sent {amount} AITBC to {to_address}")
|
||||
success(f"Submitted transaction {tx_hash} to send {amount} AITBC to {to_address}")
|
||||
return {
|
||||
"mode": "file",
|
||||
"wallet_name": wallet_name,
|
||||
"to_address": to_address,
|
||||
"amount": amount,
|
||||
"description": description,
|
||||
"new_balance": wallet_data["balance"],
|
||||
"tx_hash": tx_hash,
|
||||
"timestamp": transaction["timestamp"]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List, Optional, Union
|
||||
from typing import List, Optional, Union, Any
|
||||
from . import error, output
|
||||
|
||||
def run_subprocess(cmd: List[str], check: bool = True, capture_output: bool = True, shell: bool = False) -> Optional[str]:
|
||||
def run_subprocess(cmd: List[str], check: bool = True, capture_output: bool = True, shell: bool = False, **kwargs: Any) -> Optional[Union[str, subprocess.CompletedProcess]]:
|
||||
"""Run a subprocess command safely with logging"""
|
||||
try:
|
||||
if shell:
|
||||
# When shell=True, cmd should be a string
|
||||
cmd_str = " ".join(cmd) if isinstance(cmd, list) else cmd
|
||||
result = subprocess.run(cmd_str, shell=True, check=check, capture_output=capture_output, text=True)
|
||||
result = subprocess.run(cmd_str, shell=True, check=check, capture_output=capture_output, text=True, **kwargs)
|
||||
else:
|
||||
result = subprocess.run(cmd, check=check, capture_output=capture_output, text=True)
|
||||
result = subprocess.run(cmd, check=check, capture_output=capture_output, text=True, **kwargs)
|
||||
|
||||
if capture_output:
|
||||
return result.stdout.strip()
|
||||
return None
|
||||
return result
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
error(f"Command failed with exit code {e.returncode}")
|
||||
if capture_output and e.stderr:
|
||||
if capture_output and getattr(e, 'stderr', None):
|
||||
print(e.stderr, file=sys.stderr)
|
||||
if check:
|
||||
sys.exit(e.returncode)
|
||||
return None
|
||||
return getattr(e, 'stdout', None) if capture_output else None
|
||||
except Exception as e:
|
||||
error(f"Failed to execute command: {e}")
|
||||
if check:
|
||||
|
||||
Reference in New Issue
Block a user