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:
11
README.md
11
README.md
@@ -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
|
||||||
|
|||||||
@@ -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) |
|
||||||
|
|||||||
@@ -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())
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
Reference in New Issue
Block a user