```
chore: refactor logging module, update genesis timestamp, remove model relationships, and reorganize routers - Rename logging.py to logger.py and update import paths in poa.py and main.py - Update devnet genesis timestamp to 1766828620 - Remove SQLModel Relationship declarations from Block, Transaction, and Receipt models - Add SessionDep type alias and get_session dependency in coordinator-api deps - Reorganize coordinator-api routers: replace explorer/registry with exchange, users, marketplace
This commit is contained in:
245
apps/wallet-cli/aitbc-wallet
Executable file
245
apps/wallet-cli/aitbc-wallet
Executable file
@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AITBC Wallet CLI - A command-line wallet for AITBC blockchain
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
import httpx
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
BLOCKCHAIN_RPC = "http://127.0.0.1:9080"
|
||||
WALLET_DIR = Path.home() / ".aitbc" / "wallets"
|
||||
|
||||
def print_header():
|
||||
"""Print wallet CLI header"""
|
||||
print("=" * 50)
|
||||
print(" AITBC Blockchain Wallet CLI")
|
||||
print("=" * 50)
|
||||
|
||||
def check_blockchain_connection():
|
||||
"""Check if connected to blockchain"""
|
||||
# First check if node is running by checking metrics
|
||||
try:
|
||||
response = httpx.get(f"{BLOCKCHAIN_RPC}/metrics", timeout=5.0)
|
||||
if response.status_code == 200:
|
||||
# Node is running, now try RPC
|
||||
try:
|
||||
rpc_response = httpx.get(f"{BLOCKCHAIN_RPC}/rpc/head", timeout=5.0)
|
||||
if rpc_response.status_code == 200:
|
||||
data = rpc_response.json()
|
||||
return True, data.get("height", "unknown"), data.get("hash", "unknown")[:16] + "..."
|
||||
else:
|
||||
return False, f"RPC endpoint error (HTTP {rpc_response.status_code})", "node_running"
|
||||
except Exception as e:
|
||||
return False, f"RPC error: {str(e)}", "node_running"
|
||||
return False, f"Node not responding (HTTP {response.status_code})", None
|
||||
except Exception as e:
|
||||
return False, str(e), None
|
||||
|
||||
def get_balance(address):
|
||||
"""Get balance for an address"""
|
||||
try:
|
||||
response = httpx.get(f"{BLOCKCHAIN_RPC}/rpc/getBalance/{address}", timeout=5.0)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
return {"error": f"HTTP {response.status_code}"}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
def list_wallets():
|
||||
"""List local wallets"""
|
||||
WALLET_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
wallets = []
|
||||
for wallet_file in WALLET_DIR.glob("*.json"):
|
||||
try:
|
||||
with open(wallet_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
wallets.append({
|
||||
"id": wallet_file.stem,
|
||||
"address": data.get("address", "unknown"),
|
||||
"public_key": data.get("public_key", "unknown"),
|
||||
"created": data.get("created_at", "unknown")
|
||||
})
|
||||
except Exception as e:
|
||||
continue
|
||||
return wallets
|
||||
|
||||
def create_wallet(wallet_id, address=None):
|
||||
"""Create a new wallet file"""
|
||||
WALLET_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
wallet_file = WALLET_DIR / f"{wallet_id}.json"
|
||||
if wallet_file.exists():
|
||||
return False, "Wallet already exists"
|
||||
|
||||
# Generate a mock address if not provided
|
||||
if not address:
|
||||
address = f"aitbc1{wallet_id}{'x' * (40 - len(wallet_id))}"
|
||||
|
||||
# Generate a mock public key
|
||||
public_key = f"0x{'1234567890abcdef' * 4}"
|
||||
|
||||
wallet_data = {
|
||||
"wallet_id": wallet_id,
|
||||
"address": address,
|
||||
"public_key": public_key,
|
||||
"created_at": datetime.now().isoformat() + "Z",
|
||||
"note": "This is a demo wallet file - not for production use"
|
||||
}
|
||||
|
||||
try:
|
||||
with open(wallet_file, 'w') as f:
|
||||
json.dump(wallet_data, f, indent=2)
|
||||
return True, f"Wallet created: {wallet_file}"
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
def get_block_info(height=None):
|
||||
try:
|
||||
if height:
|
||||
url = f"{BLOCKCHAIN_RPC}/rpc/blocks/{height}"
|
||||
else:
|
||||
url = f"{BLOCKCHAIN_RPC}/rpc/head"
|
||||
|
||||
response = httpx.get(url, timeout=5.0)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
return {"error": f"HTTP {response.status_code}"}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="AITBC Blockchain Wallet CLI",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
%(prog)s status Check blockchain connection
|
||||
%(prog)s list List all local wallets
|
||||
%(prog)s balance <address> Get balance of an address
|
||||
%(prog)s block Show latest block info
|
||||
%(prog)s block <height> Show specific block info
|
||||
"""
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
||||
|
||||
# Status command
|
||||
status_parser = subparsers.add_parser("status", help="Check blockchain connection status")
|
||||
|
||||
# List command
|
||||
list_parser = subparsers.add_parser("list", help="List all local wallets")
|
||||
|
||||
# Balance command
|
||||
balance_parser = subparsers.add_parser("balance", help="Get balance for an address")
|
||||
balance_parser.add_argument("address", help="Wallet address to check")
|
||||
|
||||
# Block command
|
||||
block_parser = subparsers.add_parser("block", help="Get block information")
|
||||
block_parser.add_argument("height", nargs="?", type=int, help="Block height (optional)")
|
||||
|
||||
# Create command
|
||||
create_parser = subparsers.add_parser("create", help="Create a new wallet file")
|
||||
create_parser.add_argument("wallet_id", help="Wallet identifier")
|
||||
create_parser.add_argument("--address", help="Wallet address")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.command:
|
||||
print_header()
|
||||
parser.print_help()
|
||||
return
|
||||
|
||||
if args.command == "status":
|
||||
print_header()
|
||||
print("Checking blockchain connection...\n")
|
||||
|
||||
connected, info, block_hash = check_blockchain_connection()
|
||||
if connected:
|
||||
print(f"✅ Status: CONNECTED")
|
||||
print(f"📦 Node: {BLOCKCHAIN_RPC}")
|
||||
print(f"🔗 Latest Block: #{info}")
|
||||
print(f"🧮 Block Hash: {block_hash}")
|
||||
print(f"⏰ Checked at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
elif block_hash == "node_running":
|
||||
print(f"⚠️ Status: NODE RUNNING - RPC UNAVAILABLE")
|
||||
print(f"📦 Node: {BLOCKCHAIN_RPC}")
|
||||
print(f"❌ RPC Error: {info}")
|
||||
print(f"💡 The blockchain node is running but RPC endpoints are not working")
|
||||
print(f" This might be due to initialization or database issues")
|
||||
print(f"⏰ Checked at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
else:
|
||||
print(f"❌ Status: DISCONNECTED")
|
||||
print(f"📦 Node: {BLOCKCHAIN_RPC}")
|
||||
print(f"⚠️ Error: {info}")
|
||||
print(f"💡 Make sure the blockchain node is running on port 9080")
|
||||
|
||||
elif args.command == "list":
|
||||
print_header()
|
||||
wallets = list_wallets()
|
||||
|
||||
if wallets:
|
||||
print(f"Found {len(wallets)} wallet(s) in {WALLET_DIR}:\n")
|
||||
for w in wallets:
|
||||
print(f"🔐 Wallet ID: {w['id']}")
|
||||
print(f" Address: {w['address']}")
|
||||
print(f" Public Key: {w['public_key'][:20]}...")
|
||||
print(f" Created: {w['created']}")
|
||||
print()
|
||||
else:
|
||||
print(f"No wallets found in {WALLET_DIR}")
|
||||
print("\n💡 To create a wallet, use the wallet-daemon service")
|
||||
|
||||
elif args.command == "balance":
|
||||
print_header()
|
||||
print(f"Checking balance for address: {args.address}\n")
|
||||
|
||||
result = get_balance(args.address)
|
||||
if "error" in result:
|
||||
print(f"❌ Error: {result['error']}")
|
||||
else:
|
||||
balance = result.get("balance", 0)
|
||||
print(f"💰 Balance: {balance} AITBC")
|
||||
print(f"📍 Address: {args.address}")
|
||||
|
||||
elif args.command == "block":
|
||||
print_header()
|
||||
if args.height:
|
||||
print(f"Getting block #{args.height}...\n")
|
||||
else:
|
||||
print("Getting latest block...\n")
|
||||
|
||||
result = get_block_info(args.height)
|
||||
if "error" in result:
|
||||
print(f"❌ Error: {result['error']}")
|
||||
else:
|
||||
print(f"📦 Block Height: {result.get('height', 'unknown')}")
|
||||
print(f"🧮 Block Hash: {result.get('hash', 'unknown')}")
|
||||
print(f"⏰ Timestamp: {result.get('timestamp', 'unknown')}")
|
||||
print(f"👤 Proposer: {result.get('proposer', 'unknown')}")
|
||||
print(f"📊 Transactions: {len(result.get('transactions', []))}")
|
||||
|
||||
elif args.command == "create":
|
||||
print_header()
|
||||
success, message = create_wallet(args.wallet_id, args.address)
|
||||
if success:
|
||||
print(f"✅ {message}")
|
||||
print(f"\nWallet Details:")
|
||||
print(f" ID: {args.wallet_id}")
|
||||
print(f" Address: {args.address or f'aitbc1{args.wallet_id}...'}")
|
||||
print(f"\n💡 This is a demo wallet file for testing purposes")
|
||||
print(f" Use 'aitbc-wallet list' to see all wallets")
|
||||
else:
|
||||
print(f"❌ Error: {message}")
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
102
apps/wallet-cli/aitbc-wallet.1
Normal file
102
apps/wallet-cli/aitbc-wallet.1
Normal file
@ -0,0 +1,102 @@
|
||||
.TH AITBC-WALLET "1" "December 2025" "AITBC Wallet CLI" "User Commands"
|
||||
.SH NAME
|
||||
aitbc-wallet \- AITBC Blockchain Wallet Command Line Interface
|
||||
.SH SYNOPSIS
|
||||
.B aitbc-wallet
|
||||
[\fIOPTIONS\fR] \fICOMMAND\fR [\fIARGS\fR]
|
||||
.SH DESCRIPTION
|
||||
The AITBC Wallet CLI is a command-line tool for interacting with the AITBC blockchain. It allows you to manage wallets, check balances, and monitor blockchain status without exposing your wallet to web interfaces.
|
||||
.SH COMMANDS
|
||||
.TP
|
||||
\fBstatus\fR
|
||||
Check if the wallet is connected to the AITBC blockchain node.
|
||||
.TP
|
||||
\fBlist\fR
|
||||
List all local wallets stored in ~/.aitbc/wallets/.
|
||||
.TP
|
||||
\fBbalance\fR \fIADDRESS\fR
|
||||
Get the AITBC token balance for the specified address.
|
||||
.TP
|
||||
\fBblock\fR [\fIHEIGHT\fR]
|
||||
Show information about the latest block or a specific block height.
|
||||
.SH EXAMPLES
|
||||
Check blockchain connection status:
|
||||
.P
|
||||
.RS 4
|
||||
.nf
|
||||
$ aitbc-wallet status
|
||||
==================================================
|
||||
AITBC Blockchain Wallet CLI
|
||||
==================================================
|
||||
Checking blockchain connection...
|
||||
|
||||
✅ Status: CONNECTED
|
||||
📦 Node: http://127.0.0.1:9080
|
||||
🔗 Latest Block: #42
|
||||
🧮 Block Hash: 0x1234...abcd
|
||||
⏰ Checked at: 2025-12-28 10:30:00
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
List all wallets:
|
||||
.P
|
||||
.RS 4
|
||||
.nf
|
||||
$ aitbc-wallet list
|
||||
==================================================
|
||||
AITBC Blockchain Wallet CLI
|
||||
==================================================
|
||||
Found 1 wallet(s) in /home/user/.aitbc/wallets:
|
||||
|
||||
🔐 Wallet ID: demo-wallet
|
||||
Address: aitbc1x7f8x9k2m3n4p5q6r7s8t9u0v1w2x3y4z5a6b7c
|
||||
Public Key: 0x3aaa0a91f69d886a90...
|
||||
Created: 2025-12-28T10:30:00Z
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
Check wallet balance:
|
||||
.P
|
||||
.RS 4
|
||||
.nf
|
||||
$ aitbc-wallet balance aitbc1x7f8x9k2m3n4p5q6r7s8t9u0v1w2x3y4z5a6b7c
|
||||
==================================================
|
||||
AITBC Blockchain Wallet CLI
|
||||
==================================================
|
||||
Checking balance for address: aitbc1x7f8x9k2m3n4p5q6r7s8t9u0v1w2x3y4z5a6b7c
|
||||
|
||||
💰 Balance: 1000 AITBC
|
||||
📍 Address: aitbc1x7f8x9k2m3n4p5q6r7s8t9u0v1w2x3y4z5a6b7c
|
||||
.fi
|
||||
.RE
|
||||
.SH FILES
|
||||
.TP
|
||||
.I ~/.aitbc/wallets/
|
||||
Directory where local wallet files are stored.
|
||||
.TP
|
||||
.I /usr/local/bin/aitbc-wallet
|
||||
The wallet CLI executable.
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.I BLOCKCHAIN_RPC
|
||||
The blockchain node RPC URL (default: http://127.0.0.1:9080).
|
||||
.SH SECURITY
|
||||
.P
|
||||
The wallet CLI is designed with security in mind:
|
||||
.RS 4
|
||||
.IP \(bu 4
|
||||
No web interface - purely command-line based
|
||||
.IP \(bu 4
|
||||
Wallets stored locally in encrypted format
|
||||
.IP \(bu 4
|
||||
Only connects to localhost blockchain node by default
|
||||
.IP \(bu 4
|
||||
No exposure of private keys to network services
|
||||
.RE
|
||||
.SH BUGS
|
||||
Report bugs to the AITBC project issue tracker.
|
||||
.SH SEE ALSO
|
||||
.BR aitbc-blockchain (8),
|
||||
.BR aitbc-coordinator (8)
|
||||
.SH AUTHOR
|
||||
AITBC Development Team
|
||||
256
apps/wallet-cli/aitbc_wallet.py
Executable file
256
apps/wallet-cli/aitbc_wallet.py
Executable file
@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AITBC Wallet CLI - Command Line Interface for AITBC Blockchain Wallet
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import httpx
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "wallet-daemon" / "src"))
|
||||
|
||||
from app.keystore.service import KeystoreService
|
||||
from app.ledger_mock import SQLiteLedgerAdapter
|
||||
from app.settings import Settings
|
||||
|
||||
|
||||
class AITBCWallet:
|
||||
"""AITBC Blockchain Wallet CLI"""
|
||||
|
||||
def __init__(self, wallet_dir: str = None):
|
||||
self.wallet_dir = Path(wallet_dir or os.path.expanduser("~/.aitbc/wallets"))
|
||||
self.wallet_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.keystore = KeystoreService()
|
||||
self.blockchain_rpc = "http://127.0.0.1:9080" # Default blockchain node RPC
|
||||
|
||||
def _get_wallet_path(self, wallet_id: str) -> Path:
|
||||
"""Get the path to a wallet file"""
|
||||
return self.wallet_dir / f"{wallet_id}.wallet"
|
||||
|
||||
def create_wallet(self, wallet_id: str, password: str) -> dict:
|
||||
"""Create a new wallet"""
|
||||
wallet_path = self._get_wallet_path(wallet_id)
|
||||
|
||||
if wallet_path.exists():
|
||||
return {"error": "Wallet already exists"}
|
||||
|
||||
# Generate keypair
|
||||
keypair = self.keystore.generate_keypair()
|
||||
|
||||
# Store encrypted wallet
|
||||
wallet_data = {
|
||||
"wallet_id": wallet_id,
|
||||
"public_key": keypair["public_key"],
|
||||
"encrypted_private_key": keypair["encrypted_private_key"],
|
||||
"salt": keypair["salt"]
|
||||
}
|
||||
|
||||
# Encrypt and save
|
||||
self.keystore.save_wallet(wallet_path, wallet_data, password)
|
||||
|
||||
return {
|
||||
"wallet_id": wallet_id,
|
||||
"public_key": keypair["public_key"],
|
||||
"status": "created"
|
||||
}
|
||||
|
||||
def list_wallets(self) -> list:
|
||||
"""List all wallet addresses"""
|
||||
wallets = []
|
||||
for wallet_file in self.wallet_dir.glob("*.wallet"):
|
||||
try:
|
||||
wallet_id = wallet_file.stem
|
||||
# Try to read public key without decrypting
|
||||
with open(wallet_file, 'rb') as f:
|
||||
# This is simplified - in real implementation, we'd read metadata
|
||||
wallets.append({
|
||||
"wallet_id": wallet_id,
|
||||
"address": f"0x{wallet_id[:8]}...", # Simplified address format
|
||||
"path": str(wallet_file)
|
||||
})
|
||||
except Exception:
|
||||
continue
|
||||
return wallets
|
||||
|
||||
def get_balance(self, wallet_id: str, password: str) -> dict:
|
||||
"""Get wallet balance from blockchain"""
|
||||
# First unlock wallet to get public key
|
||||
wallet_path = self._get_wallet_path(wallet_id)
|
||||
|
||||
if not wallet_path.exists():
|
||||
return {"error": "Wallet not found"}
|
||||
|
||||
try:
|
||||
wallet_data = self.keystore.load_wallet(wallet_path, password)
|
||||
public_key = wallet_data["public_key"]
|
||||
|
||||
# Query blockchain for balance
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.get(
|
||||
f"{self.blockchain_rpc}/v1/balances/{public_key}",
|
||||
timeout=5.0
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
return {"error": "Failed to query blockchain", "status": response.status_code}
|
||||
except Exception as e:
|
||||
return {"error": f"Cannot connect to blockchain: {str(e)}"}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Failed to unlock wallet: {str(e)}"}
|
||||
|
||||
def check_connection(self) -> dict:
|
||||
"""Check if connected to blockchain"""
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
# Try to get the latest block
|
||||
response = client.get(f"{self.blockchain_rpc}/v1/blocks/head", timeout=5.0)
|
||||
if response.status_code == 200:
|
||||
block = response.json()
|
||||
return {
|
||||
"connected": True,
|
||||
"blockchain_url": self.blockchain_rpc,
|
||||
"latest_block": block.get("height", "unknown"),
|
||||
"status": "connected"
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"connected": False,
|
||||
"error": f"HTTP {response.status_code}",
|
||||
"status": "disconnected"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"connected": False,
|
||||
"error": str(e),
|
||||
"status": "disconnected"
|
||||
}
|
||||
|
||||
def send_transaction(self, wallet_id: str, password: str, to_address: str, amount: float) -> dict:
|
||||
"""Send transaction"""
|
||||
wallet_path = self._get_wallet_path(wallet_id)
|
||||
|
||||
if not wallet_path.exists():
|
||||
return {"error": "Wallet not found"}
|
||||
|
||||
try:
|
||||
# Unlock wallet
|
||||
wallet_data = self.keystore.load_wallet(wallet_path, password)
|
||||
private_key = wallet_data["private_key"]
|
||||
|
||||
# Create transaction
|
||||
transaction = {
|
||||
"from": wallet_data["public_key"],
|
||||
"to": to_address,
|
||||
"amount": amount,
|
||||
"nonce": 0 # Would get from blockchain
|
||||
}
|
||||
|
||||
# Sign transaction
|
||||
signature = self.keystore.sign_transaction(private_key, transaction)
|
||||
transaction["signature"] = signature
|
||||
|
||||
# Send to blockchain
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{self.blockchain_rpc}/v1/transactions",
|
||||
json=transaction,
|
||||
timeout=5.0
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
return {"error": f"Failed to send transaction: {response.text}"}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def main():
|
||||
"""Main CLI entry point"""
|
||||
parser = argparse.ArgumentParser(description="AITBC Blockchain Wallet CLI")
|
||||
parser.add_argument("--wallet-dir", default=None, help="Wallet directory path")
|
||||
|
||||
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
||||
|
||||
# Create wallet
|
||||
create_parser = subparsers.add_parser("create", help="Create a new wallet")
|
||||
create_parser.add_argument("wallet_id", help="Wallet identifier")
|
||||
create_parser.add_argument("password", help="Wallet password")
|
||||
|
||||
# List wallets
|
||||
subparsers.add_parser("list", help="List all wallets")
|
||||
|
||||
# Get balance
|
||||
balance_parser = subparsers.add_parser("balance", help="Get wallet balance")
|
||||
balance_parser.add_argument("wallet_id", help="Wallet identifier")
|
||||
balance_parser.add_argument("password", help="Wallet password")
|
||||
|
||||
# Check connection
|
||||
subparsers.add_parser("status", help="Check blockchain connection status")
|
||||
|
||||
# Send transaction
|
||||
send_parser = subparsers.add_parser("send", help="Send transaction")
|
||||
send_parser.add_argument("wallet_id", help="Wallet identifier")
|
||||
send_parser.add_argument("password", help="Wallet password")
|
||||
send_parser.add_argument("to_address", help="Recipient address")
|
||||
send_parser.add_argument("amount", type=float, help="Amount to send")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.command:
|
||||
parser.print_help()
|
||||
return
|
||||
|
||||
wallet = AITBCWallet(args.wallet_dir)
|
||||
|
||||
if args.command == "create":
|
||||
result = wallet.create_wallet(args.wallet_id, args.password)
|
||||
if "error" in result:
|
||||
print(f"Error: {result['error']}", file=sys.stderr)
|
||||
else:
|
||||
print(f"Wallet created successfully!")
|
||||
print(f"Wallet ID: {result['wallet_id']}")
|
||||
print(f"Public Key: {result['public_key']}")
|
||||
|
||||
elif args.command == "list":
|
||||
wallets = wallet.list_wallets()
|
||||
if wallets:
|
||||
print("Available wallets:")
|
||||
for w in wallets:
|
||||
print(f" - {w['wallet_id']}: {w['address']}")
|
||||
else:
|
||||
print("No wallets found")
|
||||
|
||||
elif args.command == "balance":
|
||||
result = wallet.get_balance(args.wallet_id, args.password)
|
||||
if "error" in result:
|
||||
print(f"Error: {result['error']}", file=sys.stderr)
|
||||
else:
|
||||
print(f"Balance: {result.get('balance', 'unknown')}")
|
||||
|
||||
elif args.command == "status":
|
||||
result = wallet.check_connection()
|
||||
if result["connected"]:
|
||||
print(f"✓ Connected to blockchain at {result['blockchain_url']}")
|
||||
print(f" Latest block: {result['latest_block']}")
|
||||
else:
|
||||
print(f"✗ Not connected: {result['error']}")
|
||||
|
||||
elif args.command == "send":
|
||||
result = wallet.send_transaction(args.wallet_id, args.password, args.to_address, args.amount)
|
||||
if "error" in result:
|
||||
print(f"Error: {result['error']}", file=sys.stderr)
|
||||
else:
|
||||
print(f"Transaction sent: {result.get('tx_hash', 'unknown')}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
101
apps/wallet-cli/wallet.py
Executable file
101
apps/wallet-cli/wallet.py
Executable file
@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple AITBC Wallet CLI
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
import httpx
|
||||
import getpass
|
||||
|
||||
def check_blockchain_connection():
|
||||
"""Check if connected to blockchain"""
|
||||
try:
|
||||
response = httpx.get("http://127.0.0.1:9080/rpc/head", timeout=5.0)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return True, data.get("height", "unknown")
|
||||
return False, f"HTTP {response.status_code}"
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
def get_balance(address):
|
||||
"""Get balance for an address"""
|
||||
try:
|
||||
response = httpx.get(f"http://127.0.0.1:9080/rpc/getBalance/{address}", timeout=5.0)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
return {"error": f"HTTP {response.status_code}"}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
def list_wallets():
|
||||
"""List local wallets"""
|
||||
wallet_dir = Path.home() / ".aitbc" / "wallets"
|
||||
wallet_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
wallets = []
|
||||
for wallet_file in wallet_dir.glob("*.json"):
|
||||
try:
|
||||
with open(wallet_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
wallets.append({
|
||||
"id": wallet_file.stem,
|
||||
"address": data.get("address", "unknown"),
|
||||
"public_key": data.get("public_key", "unknown")[:20] + "..."
|
||||
})
|
||||
except:
|
||||
continue
|
||||
return wallets
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="AITBC Wallet CLI")
|
||||
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
||||
|
||||
# Status command
|
||||
subparsers.add_parser("status", help="Check blockchain connection")
|
||||
|
||||
# List command
|
||||
subparsers.add_parser("list", help="List wallets")
|
||||
|
||||
# Balance command
|
||||
balance_parser = subparsers.add_parser("balance", help="Get balance")
|
||||
balance_parser.add_argument("address", help="Wallet address")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "status":
|
||||
connected, info = check_blockchain_connection()
|
||||
if connected:
|
||||
print(f"✓ Connected to AITBC Blockchain")
|
||||
print(f" Latest block: {info}")
|
||||
print(f" Node: http://127.0.0.1:9080")
|
||||
else:
|
||||
print(f"✗ Not connected: {info}")
|
||||
|
||||
elif args.command == "list":
|
||||
wallets = list_wallets()
|
||||
if wallets:
|
||||
print("Local wallets:")
|
||||
for w in wallets:
|
||||
print(f" {w['id']}: {w['address']}")
|
||||
else:
|
||||
print("No wallets found")
|
||||
print(f"Wallet directory: {Path.home() / '.aitbc' / 'wallets'}")
|
||||
|
||||
elif args.command == "balance":
|
||||
result = get_balance(args.address)
|
||||
if "error" in result:
|
||||
print(f"Error: {result['error']}")
|
||||
else:
|
||||
balance = result.get("balance", 0)
|
||||
print(f"Balance: {balance} AITBC")
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user