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:
oib
2025-12-28 21:05:53 +01:00
parent cdaf1122c3
commit ff5486fe08
146 changed files with 33301 additions and 219 deletions

245
apps/wallet-cli/aitbc-wallet Executable file
View 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()

View 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
View 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
View 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()