refactor: organize scripts/, remove stale root dirs, clean up structure

scripts/ reorganization:
- Sort 14 loose root scripts into subfolders:
  blockchain/ (genesis, proposer, mock chain, testnet BTC)
  dev/ (CLI wrapper, dev services, OpenAPI gen, systemd setup, domain proxy)
  ops/ (coordinator proxy, remote tunnel)
  gpu/ (miner workflow)
- Merge scripts/testing/ into scripts/test/ (eliminate duplicate folder)
- Create scripts/examples/ for usage demos and simulations

Root-level cleanup:
- Move home/ (12 simulation scripts) → scripts/examples/
- Move dev-utils/ (2 files) → scripts/dev/
- Move protocols/receipts/sample → tests/fixtures/
- Delete stale src/ (duplicate of apps/blockchain-node/src/)
- Remove empty home/, dev-utils/, protocols/ directories

Documentation updates:
- Update docs/6_architecture/8_codebase-structure.md tree and table
- Update root README.md tree to reflect new structure
This commit is contained in:
oib
2026-02-13 23:26:53 +01:00
parent 76078221cb
commit 3b4cc69179
47 changed files with 20 additions and 417 deletions

View File

@@ -48,16 +48,21 @@ aitbc/
│ └── zk-circuits/ # ZK proof circuits (Circom) │ └── zk-circuits/ # ZK proof circuits (Circom)
├── cli/ # CLI tools (12 command groups, 90+ subcommands) ├── cli/ # CLI tools (12 command groups, 90+ subcommands)
├── contracts/ # Solidity smart contracts ├── contracts/ # Solidity smart contracts
├── docs/ # Documentation (structure, guides, reference, reports) ├── docs/ # Documentation (10 numbered sections)
├── extensions/ # Browser extensions (Firefox wallet) ├── extensions/ # Browser extensions (Firefox wallet)
├── home/ # Local simulation scripts
├── infra/ # Infrastructure (nginx, k8s, helm, terraform) ├── infra/ # Infrastructure (nginx, k8s, helm, terraform)
├── packages/ # Shared libraries ├── packages/ # Shared libraries
│ ├── py/aitbc-crypto/ # Cryptographic primitives │ ├── py/aitbc-crypto/ # Cryptographic primitives
│ ├── py/aitbc-sdk/ # Python SDK │ ├── py/aitbc-sdk/ # Python SDK
│ └── solidity/aitbc-token/# ERC-20 token contract │ └── solidity/aitbc-token/# ERC-20 token contract
├── plugins/ollama/ # Ollama LLM integration ├── plugins/ollama/ # Ollama LLM integration
├── scripts/ # Deployment, GPU, service, and test scripts ├── scripts/ # All scripts, organized by purpose
│ ├── blockchain/ # Genesis, proposer, mock chain
│ ├── ci/ # CI/CD pipeline
│ ├── dev/ # Dev tools, local services
│ ├── examples/ # Usage examples and simulations
│ ├── ops/ # Coordinator proxy, tunnels
│ └── test/ # Integration and verification
├── systemd/ # Systemd service units ├── systemd/ # Systemd service units
├── tests/ # Test suites (unit, integration, e2e, security, CLI) ├── tests/ # Test suites (unit, integration, e2e, security, CLI)
└── website/ # Public website and HTML documentation └── website/ # Public website and HTML documentation

View File

@@ -10,17 +10,21 @@ aitbc/
├── assets/ # Shared frontend assets (CSS, JS, fonts) ├── assets/ # Shared frontend assets (CSS, JS, fonts)
├── cli/ # Command-line interface tools ├── cli/ # Command-line interface tools
├── contracts/ # Solidity smart contracts (standalone) ├── contracts/ # Solidity smart contracts (standalone)
├── dev-utils/ # Developer utilities and path configs ├── docs/ # Markdown documentation (10 numbered sections)
├── docs/ # Markdown documentation
├── examples/ # Usage examples and demos
├── extensions/ # Browser extensions (Firefox wallet) ├── extensions/ # Browser extensions (Firefox wallet)
├── home/ # Local workflow scripts (client/miner simulation)
├── infra/ # Infrastructure configs (nginx, k8s, terraform, helm) ├── infra/ # Infrastructure configs (nginx, k8s, terraform, helm)
├── packages/ # Shared libraries and SDKs ├── packages/ # Shared libraries and SDKs
├── plugins/ # Plugin integrations (Ollama) ├── plugins/ # Plugin integrations (Ollama)
├── protocols/ # Protocol specs and sample data ├── scripts/ # All scripts, organized by purpose
├── scripts/ # Operational and deployment scripts │ ├── blockchain/ # Genesis, proposer, mock chain, testnet
├── src/ # Shared Python source (cross-site sync, RPC) ├── ci/ # CI/CD pipeline scripts
│ ├── deploy/ # Container and service deployment (gitignored)
│ ├── dev/ # Dev tools, local services, OpenAPI gen
│ ├── examples/ # Usage examples and simulation scripts
│ ├── gpu/ # GPU miner setup and management (gitignored)
│ ├── ops/ # Coordinator proxy, remote tunnel
│ ├── service/ # Service management (gitignored)
│ └── test/ # Integration and verification scripts
├── systemd/ # Systemd service unit files ├── systemd/ # Systemd service unit files
├── tests/ # Pytest test suites (unit, integration, e2e, security, load) ├── tests/ # Pytest test suites (unit, integration, e2e, security, load)
├── website/ # Public-facing website and HTML documentation ├── website/ # Public-facing website and HTML documentation
@@ -257,12 +261,8 @@ website/
|-----------|---------| |-----------|---------|
| `cli/` | AITBC CLI package (12 command groups, 90+ subcommands, 141 unit + 24 integration tests, CI/CD, man page, plugins) | | `cli/` | AITBC CLI package (12 command groups, 90+ subcommands, 141 unit + 24 integration tests, CI/CD, man page, plugins) |
| `plugins/ollama/` | Ollama LLM integration (client plugin, miner plugin, service layer) | | `plugins/ollama/` | Ollama LLM integration (client plugin, miner plugin, service layer) |
| `home/` | Local simulation scripts for client/miner workflows |
| `extensions/` | Firefox wallet extension source code | | `extensions/` | Firefox wallet extension source code |
| `contracts/` | Standalone Solidity contracts (ZKReceiptVerifier) | | `contracts/` | Standalone Solidity contracts (ZKReceiptVerifier) |
| `protocols/` | Protocol sample data (signed receipts) |
| `src/` | Shared Python modules (cross-site sync, RPC router) |
| `systemd/` | Systemd unit files for all AITBC services | | `systemd/` | Systemd unit files for all AITBC services |
| `dev-utils/` | Developer utilities (Python path config) | | `docs/` | Markdown documentation (10 numbered sections, guides, reference, architecture) |
| `docs/` | Markdown documentation (guides, reports, reference, tutorials, operator docs) |
| `assets/` | Shared frontend assets (Tailwind CSS, FontAwesome, Lucide icons, Axios) | | `assets/` | Shared frontend assets (Tailwind CSS, FontAwesome, Lucide icons, Axios) |

View File

@@ -1,259 +0,0 @@
#!/usr/bin/env python3
"""
Ollama GPU Provider Test with Blockchain Verification using Home Directory Users
Tests the complete flow: Client -> Coordinator -> GPU Miner -> Receipt -> Blockchain
"""
import os
import sys
import subprocess
import time
import json
from typing import Optional
import httpx
# Add parent directories to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'cli'))
# Configuration
DEFAULT_COORDINATOR = "http://127.0.0.1:18000"
DEFAULT_BLOCKCHAIN = "http://aitbc.keisanki.net/rpc"
DEFAULT_PROMPT = "What is the capital of France?"
DEFAULT_MODEL = "llama3.2:latest"
DEFAULT_TIMEOUT = 180
POLL_INTERVAL = 3
def get_wallet_balance(wallet_dir: str) -> float:
"""Get wallet balance from home directory wallet"""
result = subprocess.run(
f'cd {wallet_dir} && python3 wallet.py balance',
shell=True,
capture_output=True,
text=True
)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if 'Balance:' in line:
# Extract the value after "Balance:"
balance_part = line.split('Balance:')[1].strip()
balance_value = balance_part.split()[0] # Get the numeric part before "AITBC"
try:
return float(balance_value)
except ValueError:
continue
return 0.0
def get_wallet_address(wallet_dir: str) -> Optional[str]:
"""Get wallet address from home directory wallet"""
result = subprocess.run(
f'cd {wallet_dir} && python3 wallet.py address',
shell=True,
capture_output=True,
text=True
)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if 'Address:' in line:
return line.split()[-1]
return None
def submit_job_via_client(prompt: str, model: str) -> Optional[str]:
"""Submit job using the CLI client"""
cmd = f'cd ../cli && python3 client.py submit inference --prompt "{prompt}" --model {model}'
result = subprocess.run(
cmd,
shell=True,
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"❌ Job submission failed: {result.stderr}")
return None
# Extract job ID
for line in result.stdout.split('\n'):
if "Job ID:" in line:
return line.split()[-1]
return None
def get_job_status(job_id: str) -> Optional[str]:
"""Get job status using CLI client"""
result = subprocess.run(
f'cd ../cli && python3 client.py status {job_id}',
shell=True,
capture_output=True,
text=True
)
if result.returncode != 0:
return None
# Extract state
for line in result.stdout.split('\n'):
if "State:" in line:
return line.split()[-1]
return None
def get_job_result(job_id: str) -> Optional[dict]:
"""Get job result via API"""
with httpx.Client() as client:
response = client.get(
f"{DEFAULT_COORDINATOR}/v1/jobs/{job_id}/result",
headers={"X-Api-Key": "${CLIENT_API_KEY}"},
timeout=10,
)
if response.status_code == 200:
return response.json()
return None
def check_blockchain_transaction(receipt_id: str) -> Optional[dict]:
"""Check if receipt is recorded on blockchain"""
try:
with httpx.Client() as client:
# Try to get recent transactions
response = client.get(
f"{DEFAULT_BLOCKCHAIN}/transactions",
timeout=10,
)
if response.status_code == 200:
data = response.json()
transactions = data.get("transactions", data.get("items", []))
# Look for matching receipt
for tx in transactions:
payload = tx.get("payload", {})
if payload.get("receipt_id") == receipt_id:
return tx
return None
except httpx.ConnectError:
print(f"⚠️ Blockchain node not available at {DEFAULT_BLOCKCHAIN}")
return None
except Exception as e:
print(f"⚠️ Error checking blockchain: {e}")
return None
def main():
print("🚀 Ollama GPU Provider Test with Home Directory Users")
print("=" * 60)
# Get initial balances
print("\n💰 Initial Wallet Balances:")
print("-" * 40)
client_balance = get_wallet_balance("client")
miner_balance = get_wallet_balance("miner")
print(f" Client: {client_balance} AITBC")
print(f" Miner: {miner_balance} AITBC")
# Submit job
print(f"\n📤 Submitting Inference Job:")
print("-" * 40)
print(f" Prompt: {DEFAULT_PROMPT}")
print(f" Model: {DEFAULT_MODEL}")
job_id = submit_job_via_client(DEFAULT_PROMPT, DEFAULT_MODEL)
if not job_id:
print("❌ Failed to submit job")
return 1
print(f"✅ Job submitted: {job_id}")
# Monitor job progress
print(f"\n⏳ Monitoring Job Progress:")
print("-" * 40)
deadline = time.time() + DEFAULT_TIMEOUT
while time.time() < deadline:
state = get_job_status(job_id)
if not state:
print(" ⚠️ Could not fetch status")
time.sleep(POLL_INTERVAL)
continue
print(f" State: {state}")
if state == "COMPLETED":
break
elif state in {"FAILED", "CANCELED", "EXPIRED"}:
print(f"❌ Job ended in state: {state}")
return 1
time.sleep(POLL_INTERVAL)
if state != "COMPLETED":
print("❌ Job did not complete within timeout")
return 1
# Get job result
print(f"\n📊 Job Result:")
print("-" * 40)
result = get_job_result(job_id)
if result:
output = result.get("result", {}).get("output", "No output")
receipt = result.get("receipt")
print(f" Output: {output[:200]}{'...' if len(output) > 200 else ''}")
if receipt:
print(f"\n🧾 Receipt Information:")
print(f" Receipt ID: {receipt.get('receipt_id')}")
print(f" Provider: {receipt.get('provider')}")
print(f" Units: {receipt.get('units')} {receipt.get('unit_type', 'seconds')}")
print(f" Unit Price: {receipt.get('unit_price')} AITBC")
print(f" Total Price: {receipt.get('price')} AITBC")
# Check blockchain
print(f"\n⛓️ Checking Blockchain:")
print("-" * 40)
tx = check_blockchain_transaction(receipt.get('receipt_id'))
if tx:
print(f"✅ Transaction found on blockchain!")
print(f" TX Hash: {tx.get('tx_hash')}")
print(f" Block: {tx.get('block_height')}")
print(f" From: {tx.get('sender')}")
print(f" To: {tx.get('recipient')}")
print(f" Amount: {tx.get('amount')} AITBC")
else:
print(f"⚠️ Transaction not yet found on blockchain")
print(f" (May take a few moments to be mined)")
else:
print(f"⚠️ No receipt generated")
else:
print(" Could not fetch result")
# Show final balances
print(f"\n💰 Final Wallet Balances:")
print("-" * 40)
client_balance = get_wallet_balance("client")
miner_balance = get_wallet_balance("miner")
print(f" Client: {client_balance} AITBC")
print(f" Miner: {miner_balance} AITBC")
# Calculate difference
client_diff = client_balance - get_wallet_balance("client")
print(f"\n📈 Transaction Summary:")
print("-" * 40)
print(f" Client spent: {abs(client_diff):.4f} AITBC")
print(f" Miner earned: {abs(client_diff):.4f} AITBC")
print(f"\n✅ Test completed successfully!")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -1,28 +0,0 @@
{
"private_key_hex": "e2c35a46318ec09cf1cc04f06c4ba18bf2cd3f0ed318d986f01228bdbf9fd326",
"verify_key_hex": "4c9bc7d00f69924d1cdee40c3650a2bcbb37191de2d53d93b5f21b2d3d27dbd4",
"payload": {
"version": "1.0",
"receipt_id": "rcpt-20250926-000123",
"job_id": "job-abc123",
"provider": "ait1minerxyz...",
"client": "ait1clientabc...",
"units": 1.9,
"unit_type": "gpu_seconds",
"price": 4.2,
"model": "runwayml/stable-diffusion-v1-5",
"prompt_hash": "sha256:cf1f...",
"started_at": 1695720000,
"completed_at": 1695720002,
"artifact_hash": "sha256:deadbeef...",
"coordinator_id": "coord-eu-west-1",
"nonce": "b7f3d10b",
"chain_id": 12345
},
"canonical": "{\"artifact_hash\":\"sha256:deadbeef...\",\"chain_id\":12345,\"client\":\"ait1clientabc...\",\"completed_at\":1695720002,\"coordinator_id\":\"coord-eu-west-1\",\"job_id\":\"job-abc123\",\"model\":\"runwayml/stable-diffusion-v1-5\",\"nonce\":\"b7f3d10b\",\"price\":4.2,\"prompt_hash\":\"sha256:cf1f...\",\"provider\":\"ait1minerxyz...\",\"receipt_id\":\"rcpt-20250926-000123\",\"started_at\":1695720000,\"unit_type\":\"gpu_seconds\",\"units\":1.9,\"version\":\"1.0\"}",
"signature": {
"alg": "Ed25519",
"key_id": "TJvH0A9pkk0c3uQMNlCivLs3GR3i1T2TtfIbLT0n29Q",
"sig": "fSmokpSz5-qf0XwwuBOBNseIONRM3Y6Bmrko4oE6mY0nS_noq_3zQ9y87Cd3IkMct3S5Ve-URC7D8TVBiBkvAg"
}
}

View File

@@ -1,115 +0,0 @@
#!/usr/bin/env python3
"""
Complete miner workflow - poll for jobs and assign proposer
"""
import httpx
import json
import time
from datetime import datetime
# Configuration
COORDINATOR_URL = "http://localhost:8001"
MINER_API_KEY = "${MINER_API_KEY}"
MINER_ID = "localhost-gpu-miner"
def poll_and_accept_job():
"""Poll for a job and accept it"""
print("🔍 Polling for jobs...")
with httpx.Client() as client:
# Poll for a job
response = client.post(
f"{COORDINATOR_URL}/v1/miners/poll",
headers={
"Content-Type": "application/json",
"X-Api-Key": MINER_API_KEY
},
json={"max_wait_seconds": 5}
)
if response.status_code == 200:
job = response.json()
print(f"✅ Received job: {job['job_id']}")
print(f" Task: {job['payload'].get('task', 'unknown')}")
# Simulate processing
print("⚙️ Processing job...")
time.sleep(2)
# Submit result
result_data = {
"result": {
"status": "completed",
"output": f"Job {job['job_id']} completed successfully",
"execution_time_ms": 2000,
"miner_id": MINER_ID
},
"metrics": {
"compute_time": 2.0,
"energy_used": 0.1
}
}
print(f"📤 Submitting result for job {job['job_id']}...")
result_response = client.post(
f"{COORDINATOR_URL}/v1/miners/{job['job_id']}/result",
headers={
"Content-Type": "application/json",
"X-Api-Key": MINER_API_KEY
},
json=result_data
)
if result_response.status_code == 200:
print("✅ Result submitted successfully!")
return job['job_id']
else:
print(f"❌ Failed to submit result: {result_response.status_code}")
print(f" Response: {result_response.text}")
return None
elif response.status_code == 204:
print(" No jobs available")
return None
else:
print(f"❌ Failed to poll: {response.status_code}")
return None
def check_block_proposer(job_id):
"""Check if the block now has a proposer"""
print(f"\n🔍 Checking proposer for job {job_id}...")
with httpx.Client() as client:
response = client.get(f"{COORDINATOR_URL}/v1/explorer/blocks")
if response.status_code == 200:
blocks = response.json()
for block in blocks['items']:
if block['hash'] == job_id:
print(f"📦 Block Info:")
print(f" Height: {block['height']}")
print(f" Hash: {block['hash']}")
print(f" Proposer: {block['proposer']}")
print(f" Time: {block['timestamp']}")
return block
return None
def main():
print("⛏️ AITBC Miner Workflow Demo")
print(f" Miner ID: {MINER_ID}")
print(f" Coordinator: {COORDINATOR_URL}")
print()
# Poll and accept a job
job_id = poll_and_accept_job()
if job_id:
# Check if the block has a proposer now
time.sleep(1) # Give the server a moment to update
check_block_proposer(job_id)
else:
print("\n💡 Tip: Create a job first using example_client_remote.py")
if __name__ == "__main__":
main()