Consolidate service scripts into apps directories
Some checks failed
Integration Tests / test-service-integration (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
Systemd Sync / sync-systemd (push) Has been cancelled

- Move blockchain scripts to apps/blockchain-node/scripts/
- Move marketplace scripts to apps/marketplace/scripts/
- Move agent daemon to apps/agent-coordinator/scripts/
- Move monitor to apps/monitor/
- Update systemd service files to point to new locations
- Update internal path references in moved scripts
- Remove empty /opt/aitbc/services directory
This commit is contained in:
aitbc
2026-04-15 11:56:03 +02:00
parent 7e630f53fc
commit ca34b6fee3
13 changed files with 13 additions and 11 deletions

View File

@@ -0,0 +1,223 @@
#!/usr/bin/env python3
"""
AITBC Autonomous Agent Listener Daemon
Listens for blockchain transactions addressed to an agent wallet and autonomously replies.
"""
import sys
import time
import requests
import json
import hashlib
import argparse
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# Default configuration
DEFAULT_KEYSTORE_DIR = Path("/var/lib/aitbc/keystore")
DEFAULT_DB_PATH = "/var/lib/aitbc/data/ait-mainnet/chain.db"
DEFAULT_RPC_URL = "http://localhost:8006"
DEFAULT_POLL_INTERVAL = 2
def decrypt_wallet(keystore_path: Path, password: str) -> bytes:
"""Decrypt private key from keystore file.
Supports both keystore formats:
- AES-256-GCM (blockchain-node standard)
- Fernet (scripts/utils standard)
"""
with open(keystore_path) as f:
data = json.load(f)
crypto = data.get('crypto', data) # Handle both nested and flat crypto structures
# Detect encryption method
cipher = crypto.get('cipher', crypto.get('algorithm', ''))
if cipher == 'aes-256-gcm':
# AES-256-GCM (blockchain-node standard)
salt = bytes.fromhex(crypto['kdfparams']['salt'])
ciphertext = bytes.fromhex(crypto['ciphertext'])
nonce = bytes.fromhex(crypto['cipherparams']['nonce'])
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=crypto['kdfparams']['dklen'],
salt=salt,
iterations=crypto['kdfparams']['c'],
backend=default_backend()
)
key = kdf.derive(password.encode())
aesgcm = AESGCM(key)
return aesgcm.decrypt(nonce, ciphertext, None)
elif cipher == 'fernet' or cipher == 'PBKDF2-SHA256-Fernet':
# Fernet (scripts/utils standard)
from cryptography.fernet import Fernet
import base64
kdfparams = crypto.get('kdfparams', {})
if 'salt' in kdfparams:
salt = base64.b64decode(kdfparams['salt'])
else:
salt = bytes.fromhex(kdfparams.get('salt', ''))
# Simple KDF: hash(password + salt) - matches scripts/utils/keystore.py
dk = hashlib.sha256(password.encode() + salt).digest()
fernet_key = base64.urlsafe_b64encode(dk)
f = Fernet(fernet_key)
ciphertext = base64.b64decode(crypto['ciphertext'])
priv = f.decrypt(ciphertext)
return priv.encode()
else:
raise ValueError(f"Unsupported cipher: {cipher}")
def create_tx(private_bytes: bytes, from_addr: str, to_addr: str, amount: float, fee: float, payload: str) -> dict:
"""Create and sign a transaction"""
priv_key = ed25519.Ed25519PrivateKey.from_private_bytes(private_bytes)
pub_hex = priv_key.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
).hex()
tx = {
"type": "transfer",
"from": from_addr,
"to": to_addr,
"amount": amount,
"fee": fee,
"nonce": int(time.time() * 1000),
"payload": payload,
"chain_id": "ait-mainnet"
}
tx_string = json.dumps(tx, sort_keys=True)
tx_hash = hashlib.sha256(tx_string.encode()).hexdigest()
tx["signature"] = priv_key.sign(tx_string.encode()).hex()
tx["public_key"] = pub_hex
return tx
def main():
parser = argparse.ArgumentParser(description="AITBC Autonomous Agent Listener Daemon")
parser.add_argument("--wallet", required=True, help="Wallet name (e.g., temp-agent2)")
parser.add_argument("--address", required=True, help="Agent wallet address")
parser.add_argument("--password", help="Wallet password")
parser.add_argument("--password-file", help="Path to file containing wallet password")
parser.add_argument("--keystore-dir", default=DEFAULT_KEYSTORE_DIR, help="Keystore directory")
parser.add_argument("--db-path", default=DEFAULT_DB_PATH, help="Path to blockchain database")
parser.add_argument("--rpc-url", default=DEFAULT_RPC_URL, help="RPC endpoint URL")
parser.add_argument("--poll-interval", type=int, default=DEFAULT_POLL_INTERVAL, help="Poll interval in seconds")
parser.add_argument("--reply-message", default="pong", help="Message to send as reply")
parser.add_argument("--trigger-message", default="ping", help="Message that triggers reply")
args = parser.parse_args()
# Get password
if args.password_file:
with open(args.password_file) as f:
password = f.read().strip()
elif args.password:
password = args.password
else:
print("Error: password or password-file is required")
sys.exit(1)
# Setup paths
keystore_path = Path(args.keystore_dir) / f"{args.wallet}.json"
print(f"Agent daemon started. Listening for messages to {args.address}...")
print(f"Trigger message: '{args.trigger_message}'")
print(f"Reply message: '{args.reply_message}'")
# Decrypt wallet
try:
priv_bytes = decrypt_wallet(keystore_path, password)
print("Wallet unlocked successfully.")
except Exception as e:
print(f"Failed to unlock wallet: {e}")
sys.exit(1)
sys.stdout.flush()
# Setup database connection
processed_txs = set()
sys.path.insert(0, "/opt/aitbc/apps/blockchain-node/src")
try:
from sqlmodel import create_engine, Session, select
from aitbc_chain.models import Transaction
engine = create_engine(f"sqlite:///{args.db_path}")
print(f"Connected to database: {args.db_path}")
except ImportError as e:
print(f"Error importing sqlmodel: {e}")
print("Make sure sqlmodel is installed in the virtual environment")
sys.exit(1)
sys.stdout.flush()
# Main polling loop
while True:
try:
with Session(engine) as session:
txs = session.exec(
select(Transaction).where(Transaction.recipient == args.address)
).all()
for tx in txs:
if tx.id in processed_txs:
continue
processed_txs.add(tx.id)
# Extract payload
data = ""
if hasattr(tx, "tx_metadata") and tx.tx_metadata:
if isinstance(tx.tx_metadata, dict):
data = tx.tx_metadata.get("payload", "")
elif isinstance(tx.tx_metadata, str):
try:
data = json.loads(tx.tx_metadata).get("payload", "")
except:
pass
elif hasattr(tx, "payload") and tx.payload:
if isinstance(tx.payload, dict):
data = tx.payload.get("payload", "")
sender = tx.sender
# Check if message matches trigger
if sender != args.address and args.trigger_message in str(data):
print(f"Received '{data}' from {sender}! Sending '{args.reply_message}'...")
reply_tx = create_tx(priv_bytes, args.address, sender, 0, 10, args.reply_message)
try:
res = requests.post(f"{args.rpc_url}/rpc/transaction", json=reply_tx, timeout=10)
if res.status_code == 200:
print(f"Reply sent successfully: {res.json()}")
else:
print(f"Failed to send reply: {res.text}")
except requests.RequestException as e:
print(f"Network error sending reply: {e}")
sys.stdout.flush()
except Exception as e:
print(f"Error querying database: {e}")
sys.stdout.flush()
time.sleep(args.poll_interval)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""
Blockchain HTTP Launcher for AITBC Production
"""
import os
import sys
import subprocess
import logging
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def main():
"""Main blockchain HTTP launcher function"""
logger.info("Starting AITBC Blockchain HTTP Launcher")
try:
# Launch blockchain HTTP service
logger.info("Launching blockchain HTTP API")
subprocess.run([
'/opt/aitbc/venv/bin/python',
'-m', 'uvicorn',
'aitbc_chain.app:app',
'--host', '0.0.0.0',
'--port', '8005'
], check=True)
except subprocess.CalledProcessError as e:
logger.error(f"Blockchain HTTP service failed with exit code {e.returncode}: {e}")
# Fallback
import time
while True:
logger.info("Blockchain HTTP service heartbeat (fallback mode)")
time.sleep(30)
except (FileNotFoundError, PermissionError) as e:
logger.error(f"Cannot launch blockchain HTTP service: {type(e).__name__}: {e}")
# Fallback
import time
while True:
logger.info("Blockchain HTTP service heartbeat (fallback mode)")
time.sleep(30)
except Exception as e:
logger.error(f"Unexpected error launching blockchain HTTP: {type(e).__name__}: {e}")
# Fallback
import time
while True:
logger.info("Blockchain HTTP service heartbeat (fallback mode)")
time.sleep(30)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""
Blockchain Node Service for AITBC Production
"""
import os
import sys
import logging
from pathlib import Path
# Add the blockchain app to Python path
sys.path.insert(0, '/opt/aitbc/apps/blockchain-node/src')
sys.path.insert(0, '/opt/aitbc/apps/blockchain-node/scripts')
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def main():
"""Main blockchain service function"""
logger.info("Starting AITBC Blockchain Node Service")
try:
# Set environment variables
os.environ.setdefault('PYTHONPATH', '/opt/aitbc/apps/blockchain-node/src')
os.environ.setdefault('BLOCKCHAIN_DATA_DIR', '/var/lib/aitbc/data/blockchain')
os.environ.setdefault('BLOCKCHAIN_CONFIG_DIR', '/etc/aitbc')
os.environ.setdefault('BLOCKCHAIN_LOG_DIR', '/var/log/aitbc/production/blockchain')
# Try to import and run the actual blockchain node
logger.info("Attempting to start blockchain node...")
# Check if we can import the blockchain app
try:
from aitbc_chain.app import app
logger.info("Successfully imported blockchain app")
# Run the blockchain FastAPI app
import uvicorn
logger.info("Starting blockchain FastAPI app on port 8545")
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("BLOCKCHAIN_PORT", 8545)))
except ImportError as e:
logger.error(f"Failed to import blockchain app: {e}")
# Try to run the main blockchain function
try:
from aitbc_chain.main import main as blockchain_main
logger.info("Successfully imported blockchain main")
blockchain_main()
except ImportError as e2:
logger.error(f"Failed to import blockchain main: {e2}")
logger.info("Starting blockchain node with basic functionality")
basic_blockchain_node()
except Exception as e:
logger.error(f"Error starting blockchain service: {e}")
logger.info("Starting fallback blockchain node")
basic_blockchain_node()
def basic_blockchain_node():
"""Basic blockchain node functionality"""
logger.info("Starting basic blockchain node")
try:
# Create a simple FastAPI app for blockchain node
from fastapi import FastAPI
import uvicorn
import time
import threading
app = FastAPI(title="AITBC Blockchain Node")
# Blockchain state
blockchain_state = {
"status": "running",
"block_height": 0,
"last_block": None,
"peers": [],
"start_time": time.time()
}
@app.get("/health")
async def health():
return {
"status": "healthy",
"service": "blockchain-node",
"block_height": blockchain_state["block_height"],
"uptime": time.time() - blockchain_state["start_time"]
}
@app.get("/")
async def root():
return {
"service": "blockchain-node",
"status": "running",
"endpoints": ["/health", "/", "/blocks", "/status"]
}
@app.get("/blocks")
async def get_blocks():
return {
"blocks": [],
"count": 0,
"latest_height": blockchain_state["block_height"]
}
@app.get("/status")
async def get_status():
return blockchain_state
# Simulate blockchain activity
def blockchain_activity():
while True:
time.sleep(30) # Simulate block generation every 30 seconds
blockchain_state["block_height"] += 1
blockchain_state["last_block"] = f"block_{blockchain_state['block_height']}"
logger.info(f"Generated block {blockchain_state['block_height']}")
# Start blockchain activity in background
activity_thread = threading.Thread(target=blockchain_activity, daemon=True)
activity_thread.start()
logger.info("Starting basic blockchain API on port 8545")
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("BLOCKCHAIN_PORT", 8545)))
except ImportError:
# Fallback to simple heartbeat
logger.info("FastAPI not available, using simple blockchain node")
while True:
logger.info("Blockchain node heartbeat - active")
time.sleep(30)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""
GPU Marketplace Launcher for AITBC Production
"""
import os
import sys
import subprocess
import logging
from pathlib import Path
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def main():
"""Main GPU marketplace launcher function"""
logger.info("Starting AITBC GPU Marketplace Launcher")
try:
# Set environment variables
os.environ.setdefault('PYTHONPATH', '/opt/aitbc/apps/marketplace/scripts:/opt/aitbc/apps/marketplace/src:/opt/aitbc/apps/coordinator-api/src')
# Try to run the GPU marketplace service
logger.info("Launching GPU marketplace service")
# Check if the main marketplace service exists
marketplace_path = '/opt/aitbc/apps/marketplace/scripts/marketplace.py'
if os.path.exists(marketplace_path):
logger.info("Found marketplace service, launching...")
subprocess.run([
'/opt/aitbc/venv/bin/python',
marketplace_path
], check=True)
else:
logger.error(f"Marketplace service not found at {marketplace_path}")
# Fallback to simple service
fallback_service()
except subprocess.CalledProcessError as e:
logger.error(f"GPU marketplace service failed with exit code {e.returncode}: {e}")
logger.info("Starting fallback GPU marketplace service")
fallback_service()
except (FileNotFoundError, PermissionError) as e:
logger.error(f"Cannot launch GPU marketplace service: {type(e).__name__}: {e}")
logger.info("Starting fallback GPU marketplace service")
fallback_service()
except Exception as e:
logger.error(f"Unexpected error launching GPU marketplace: {type(e).__name__}: {e}")
logger.info("Starting fallback GPU marketplace service")
fallback_service()
def fallback_service():
"""Fallback GPU marketplace service"""
logger.info("Starting fallback GPU marketplace service")
try:
# Simple GPU marketplace heartbeat
import time
while True:
logger.info("GPU Marketplace service heartbeat - active")
time.sleep(30)
except KeyboardInterrupt:
logger.info("GPU Marketplace service stopped by user")
except (OSError, IOError) as e:
logger.error(f"System error in fallback service: {type(e).__name__}: {e}")
time.sleep(5)
except Exception as e:
logger.error(f"Unexpected error in fallback service: {type(e).__name__}: {e}")
time.sleep(5)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""
Marketplace Service for AITBC Production
"""
import os
import sys
import time
import logging
from pathlib import Path
# Add paths
sys.path.insert(0, '/opt/aitbc/apps/marketplace/src')
sys.path.insert(0, '/opt/aitbc/apps/coordinator-api/src')
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def main():
"""Main marketplace service function"""
logger.info("Starting AITBC Marketplace Service")
try:
# Try to import and run the actual marketplace service
from production.services.marketplace import app
logger.info("Successfully imported marketplace app")
# Run the marketplace service
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8007)
except ImportError as e:
logger.error(f"Failed to import marketplace app: {e}")
logger.info("Trying alternative marketplace import...")
try:
# Try the unified marketplace
from production.services.unified_marketplace import app
logger.info("Successfully imported unified marketplace app")
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8007)
except ImportError as e2:
logger.error(f"Failed to import unified marketplace: {e2}")
logger.info("Starting simple marketplace heartbeat service")
heartbeat_service()
except Exception as e:
logger.error(f"Error starting marketplace service: {e}")
heartbeat_service()
def heartbeat_service():
"""Simple heartbeat service for marketplace"""
logger.info("Starting marketplace heartbeat service")
try:
# Create a simple FastAPI app for health checks
from fastapi import FastAPI
import uvicorn
app = FastAPI(title="AITBC Marketplace Service")
@app.get("/health")
async def health():
return {"status": "healthy", "service": "marketplace", "message": "Marketplace service running"}
@app.get("/")
async def root():
return {"service": "marketplace", "status": "running", "endpoints": ["/health", "/"]}
logger.info("Starting simple marketplace API on port 8007")
uvicorn.run(app, host="0.0.0.0", port=8007)
except ImportError:
# Fallback to simple heartbeat
logger.info("FastAPI not available, using simple heartbeat")
while True:
logger.info("Marketplace service heartbeat - active")
time.sleep(30)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
Real Marketplace Launcher for AITBC Production
"""
import os
import sys
import subprocess
import logging
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def main():
"""Main real marketplace launcher function"""
logger.info("Starting AITBC Real Marketplace Launcher")
try:
# Launch real marketplace service
logger.info("Launching real marketplace service")
subprocess.run([
'/opt/aitbc/venv/bin/python',
'/opt/aitbc/apps/marketplace/scripts/marketplace.py'
], check=True)
except subprocess.CalledProcessError as e:
logger.error(f"Marketplace service failed with exit code {e.returncode}: {e}")
# Fallback
import time
while True:
logger.info("Real Marketplace service heartbeat (fallback mode)")
time.sleep(30)
except (FileNotFoundError, PermissionError) as e:
logger.error(f"Cannot launch marketplace service: {type(e).__name__}: {e}")
# Fallback
import time
while True:
logger.info("Real Marketplace service heartbeat (fallback mode)")
time.sleep(30)
except Exception as e:
logger.error(f"Unexpected error launching marketplace: {type(e).__name__}: {e}")
# Fallback
import time
while True:
logger.info("Real Marketplace service heartbeat (fallback mode)")
time.sleep(30)
if __name__ == "__main__":
main()

48
apps/monitor/monitor.py Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""
AITBC Monitor Service
"""
import time
import logging
import json
from pathlib import Path
import psutil
def main():
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('aitbc-monitor')
while True:
try:
# System stats
cpu_percent = psutil.cpu_percent()
memory_percent = psutil.virtual_memory().percent
logger.info(f'System: CPU {cpu_percent}%, Memory {memory_percent}%')
# Blockchain stats
blockchain_file = Path('/var/lib/aitbc/data/blockchain/aitbc/blockchain.json')
if blockchain_file.exists():
with open(blockchain_file, 'r') as f:
data = json.load(f)
logger.info(f'Blockchain: {len(data.get("blocks", []))} blocks')
# Marketplace stats
marketplace_dir = Path('/var/lib/aitbc/data/marketplace')
if marketplace_dir.exists():
listings_file = marketplace_dir / 'gpu_listings.json'
if listings_file.exists():
with open(listings_file, 'r') as f:
listings = json.load(f)
logger.info(f'Marketplace: {len(listings)} GPU listings')
time.sleep(30)
except (json.JSONDecodeError, FileNotFoundError, PermissionError, IOError) as e:
logger.error(f'Monitoring error: {type(e).__name__}: {e}')
time.sleep(60)
except psutil.Error as e:
logger.error(f'System monitoring error: {type(e).__name__}: {e}')
time.sleep(60)
if __name__ == "__main__":
main()