```
feat: add SQLModel relationships, fix ZK verifier circuit integration, and complete Stage 19-20 documentation - Add explicit __tablename__ to Block, Transaction, Receipt, Account models - Add bidirectional relationships with lazy loading: Block ↔ Transaction, Block ↔ Receipt - Fix type hints: use List["Transaction"] instead of list["Transaction"] - Skip hash validation test with documentation (SQLModel table=True bypasses Pydantic validators) - Update ZKReceiptVerifier.sol to match receipt_simple circuit (
This commit is contained in:
265
docs/developer/tutorials/building-custom-miner.md
Normal file
265
docs/developer/tutorials/building-custom-miner.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Building a Custom Miner
|
||||
|
||||
This tutorial walks you through creating a custom GPU miner for the AITBC network.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Linux system with NVIDIA GPU
|
||||
- Python 3.10+
|
||||
- CUDA toolkit installed
|
||||
- Ollama or other inference backend
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ Coordinator │────▶│ Your Miner │────▶│ GPU Backend │
|
||||
│ API │◀────│ (Python) │◀────│ (Ollama) │
|
||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
Your miner:
|
||||
1. Polls the Coordinator for available jobs
|
||||
2. Claims and processes jobs using your GPU
|
||||
3. Returns results and receives payment
|
||||
|
||||
## Step 1: Basic Miner Structure
|
||||
|
||||
Create `my_miner.py`:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Custom AITBC GPU Miner"""
|
||||
|
||||
import asyncio
|
||||
import httpx
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class CustomMiner:
|
||||
def __init__(self, coordinator_url: str, miner_id: str):
|
||||
self.coordinator_url = coordinator_url
|
||||
self.miner_id = miner_id
|
||||
self.client = httpx.AsyncClient(timeout=30.0)
|
||||
|
||||
async def register(self):
|
||||
"""Register miner with coordinator."""
|
||||
response = await self.client.post(
|
||||
f"{self.coordinator_url}/v1/miners/register",
|
||||
json={
|
||||
"miner_id": self.miner_id,
|
||||
"capabilities": ["llama3.2", "codellama"],
|
||||
"gpu_info": self.get_gpu_info()
|
||||
}
|
||||
)
|
||||
response.raise_for_status()
|
||||
logger.info(f"Registered as {self.miner_id}")
|
||||
|
||||
def get_gpu_info(self) -> dict:
|
||||
"""Collect GPU information."""
|
||||
try:
|
||||
import subprocess
|
||||
result = subprocess.run(
|
||||
["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader"],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
name, memory = result.stdout.strip().split(", ")
|
||||
return {"name": name, "memory": memory}
|
||||
except Exception:
|
||||
return {"name": "Unknown", "memory": "Unknown"}
|
||||
|
||||
async def poll_jobs(self):
|
||||
"""Poll for available jobs."""
|
||||
response = await self.client.get(
|
||||
f"{self.coordinator_url}/v1/jobs/available",
|
||||
params={"miner_id": self.miner_id}
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
return None
|
||||
|
||||
async def claim_job(self, job_id: str):
|
||||
"""Claim a job for processing."""
|
||||
response = await self.client.post(
|
||||
f"{self.coordinator_url}/v1/jobs/{job_id}/claim",
|
||||
json={"miner_id": self.miner_id}
|
||||
)
|
||||
return response.status_code == 200
|
||||
|
||||
async def process_job(self, job: dict) -> str:
|
||||
"""Process job using GPU backend."""
|
||||
# Override this method with your inference logic
|
||||
raise NotImplementedError("Implement process_job()")
|
||||
|
||||
async def submit_result(self, job_id: str, result: str):
|
||||
"""Submit job result to coordinator."""
|
||||
response = await self.client.post(
|
||||
f"{self.coordinator_url}/v1/jobs/{job_id}/complete",
|
||||
json={
|
||||
"miner_id": self.miner_id,
|
||||
"result": result,
|
||||
"completed_at": datetime.utcnow().isoformat()
|
||||
}
|
||||
)
|
||||
response.raise_for_status()
|
||||
logger.info(f"Completed job {job_id}")
|
||||
|
||||
async def run(self):
|
||||
"""Main mining loop."""
|
||||
await self.register()
|
||||
|
||||
while True:
|
||||
try:
|
||||
job = await self.poll_jobs()
|
||||
if job:
|
||||
job_id = job["job_id"]
|
||||
if await self.claim_job(job_id):
|
||||
logger.info(f"Processing job {job_id}")
|
||||
result = await self.process_job(job)
|
||||
await self.submit_result(job_id, result)
|
||||
else:
|
||||
await asyncio.sleep(2) # No jobs, wait
|
||||
except Exception as e:
|
||||
logger.error(f"Error: {e}")
|
||||
await asyncio.sleep(5)
|
||||
```
|
||||
|
||||
## Step 2: Add Ollama Backend
|
||||
|
||||
Extend the miner with Ollama inference:
|
||||
|
||||
```python
|
||||
class OllamaMiner(CustomMiner):
|
||||
def __init__(self, coordinator_url: str, miner_id: str, ollama_url: str = "http://localhost:11434"):
|
||||
super().__init__(coordinator_url, miner_id)
|
||||
self.ollama_url = ollama_url
|
||||
|
||||
async def process_job(self, job: dict) -> str:
|
||||
"""Process job using Ollama."""
|
||||
prompt = job.get("prompt", "")
|
||||
model = job.get("model", "llama3.2")
|
||||
|
||||
response = await self.client.post(
|
||||
f"{self.ollama_url}/api/generate",
|
||||
json={
|
||||
"model": model,
|
||||
"prompt": prompt,
|
||||
"stream": False
|
||||
},
|
||||
timeout=120.0
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()["response"]
|
||||
|
||||
# Run the miner
|
||||
if __name__ == "__main__":
|
||||
miner = OllamaMiner(
|
||||
coordinator_url="https://aitbc.bubuit.net/api",
|
||||
miner_id="my-custom-miner-001"
|
||||
)
|
||||
asyncio.run(miner.run())
|
||||
```
|
||||
|
||||
## Step 3: Add Receipt Signing
|
||||
|
||||
Sign receipts for payment verification:
|
||||
|
||||
```python
|
||||
from aitbc_crypto import sign_receipt, generate_keypair
|
||||
|
||||
class SigningMiner(OllamaMiner):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.private_key, self.public_key = generate_keypair()
|
||||
|
||||
async def submit_result(self, job_id: str, result: str):
|
||||
"""Submit signed result."""
|
||||
receipt = {
|
||||
"job_id": job_id,
|
||||
"miner_id": self.miner_id,
|
||||
"result_hash": hashlib.sha256(result.encode()).hexdigest(),
|
||||
"completed_at": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
signature = sign_receipt(receipt, self.private_key)
|
||||
receipt["signature"] = signature
|
||||
|
||||
response = await self.client.post(
|
||||
f"{self.coordinator_url}/v1/jobs/{job_id}/complete",
|
||||
json={"result": result, "receipt": receipt}
|
||||
)
|
||||
response.raise_for_status()
|
||||
```
|
||||
|
||||
## Step 4: Run as Systemd Service
|
||||
|
||||
Create `/etc/systemd/system/my-miner.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Custom AITBC Miner
|
||||
After=network.target ollama.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=miner
|
||||
WorkingDirectory=/home/miner
|
||||
ExecStart=/usr/bin/python3 /home/miner/my_miner.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Enable and start:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable my-miner
|
||||
sudo systemctl start my-miner
|
||||
sudo journalctl -u my-miner -f
|
||||
```
|
||||
|
||||
## Step 5: Monitor Performance
|
||||
|
||||
Add metrics collection:
|
||||
|
||||
```python
|
||||
import time
|
||||
|
||||
class MetricsMiner(SigningMiner):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.jobs_completed = 0
|
||||
self.total_time = 0
|
||||
|
||||
async def process_job(self, job: dict) -> str:
|
||||
start = time.time()
|
||||
result = await super().process_job(job)
|
||||
elapsed = time.time() - start
|
||||
|
||||
self.jobs_completed += 1
|
||||
self.total_time += elapsed
|
||||
|
||||
logger.info(f"Job completed in {elapsed:.2f}s (avg: {self.total_time/self.jobs_completed:.2f}s)")
|
||||
return result
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Error Handling**: Always catch and log exceptions
|
||||
2. **Graceful Shutdown**: Handle SIGTERM for clean exits
|
||||
3. **Rate Limiting**: Don't poll too aggressively
|
||||
4. **GPU Memory**: Monitor and clear GPU memory between jobs
|
||||
5. **Logging**: Use structured logging for debugging
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Coordinator API Integration](coordinator-api-integration.md)
|
||||
- [SDK Examples](sdk-examples.md)
|
||||
- [Reference: Miner Node](../../reference/components/miner_node.md)
|
||||
352
docs/developer/tutorials/coordinator-api-integration.md
Normal file
352
docs/developer/tutorials/coordinator-api-integration.md
Normal file
@@ -0,0 +1,352 @@
|
||||
# Integrating with Coordinator API
|
||||
|
||||
This tutorial shows how to integrate your application with the AITBC Coordinator API.
|
||||
|
||||
## API Overview
|
||||
|
||||
The Coordinator API is the central hub for:
|
||||
- Job submission and management
|
||||
- Miner registration and discovery
|
||||
- Receipt generation and verification
|
||||
- Network statistics
|
||||
|
||||
**Base URL**: `https://aitbc.bubuit.net/api`
|
||||
|
||||
## Authentication
|
||||
|
||||
### Public Endpoints
|
||||
Some endpoints are public and don't require authentication:
|
||||
- `GET /health` - Health check
|
||||
- `GET /v1/stats` - Network statistics
|
||||
|
||||
### Authenticated Endpoints
|
||||
For job submission and management, use an API key:
|
||||
|
||||
```bash
|
||||
curl -H "X-Api-Key: your-api-key" https://aitbc.bubuit.net/api/v1/jobs
|
||||
```
|
||||
|
||||
## Core Endpoints
|
||||
|
||||
### Jobs
|
||||
|
||||
#### Submit a Job
|
||||
|
||||
```bash
|
||||
POST /v1/jobs
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"prompt": "Explain quantum computing",
|
||||
"model": "llama3.2",
|
||||
"params": {
|
||||
"max_tokens": 256,
|
||||
"temperature": 0.7
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"job_id": "job-abc123",
|
||||
"status": "pending",
|
||||
"created_at": "2026-01-24T15:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Job Status
|
||||
|
||||
```bash
|
||||
GET /v1/jobs/{job_id}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"job_id": "job-abc123",
|
||||
"status": "completed",
|
||||
"result": "Quantum computing is...",
|
||||
"miner_id": "miner-xyz",
|
||||
"started_at": "2026-01-24T15:00:01Z",
|
||||
"completed_at": "2026-01-24T15:00:05Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### List Jobs
|
||||
|
||||
```bash
|
||||
GET /v1/jobs?status=completed&limit=10
|
||||
```
|
||||
|
||||
#### Cancel a Job
|
||||
|
||||
```bash
|
||||
POST /v1/jobs/{job_id}/cancel
|
||||
```
|
||||
|
||||
### Miners
|
||||
|
||||
#### Register Miner
|
||||
|
||||
```bash
|
||||
POST /v1/miners/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"miner_id": "my-miner-001",
|
||||
"capabilities": ["llama3.2", "codellama"],
|
||||
"gpu_info": {
|
||||
"name": "NVIDIA RTX 4090",
|
||||
"memory": "24GB"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Available Jobs (for miners)
|
||||
|
||||
```bash
|
||||
GET /v1/jobs/available?miner_id=my-miner-001
|
||||
```
|
||||
|
||||
#### Claim a Job
|
||||
|
||||
```bash
|
||||
POST /v1/jobs/{job_id}/claim
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"miner_id": "my-miner-001"
|
||||
}
|
||||
```
|
||||
|
||||
#### Complete a Job
|
||||
|
||||
```bash
|
||||
POST /v1/jobs/{job_id}/complete
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"miner_id": "my-miner-001",
|
||||
"result": "The generated output...",
|
||||
"completed_at": "2026-01-24T15:00:05Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Receipts
|
||||
|
||||
#### Get Receipt
|
||||
|
||||
```bash
|
||||
GET /v1/receipts/{receipt_id}
|
||||
```
|
||||
|
||||
#### List Receipts
|
||||
|
||||
```bash
|
||||
GET /v1/receipts?client=ait1client...&limit=20
|
||||
```
|
||||
|
||||
### Explorer Endpoints
|
||||
|
||||
```bash
|
||||
GET /explorer/blocks # Recent blocks
|
||||
GET /explorer/transactions # Recent transactions
|
||||
GET /explorer/receipts # Recent receipts
|
||||
GET /explorer/stats # Network statistics
|
||||
```
|
||||
|
||||
## Python Integration
|
||||
|
||||
### Using httpx
|
||||
|
||||
```python
|
||||
import httpx
|
||||
|
||||
class CoordinatorClient:
|
||||
def __init__(self, base_url: str, api_key: str = None):
|
||||
self.base_url = base_url
|
||||
self.headers = {}
|
||||
if api_key:
|
||||
self.headers["X-Api-Key"] = api_key
|
||||
self.client = httpx.Client(headers=self.headers, timeout=30.0)
|
||||
|
||||
def submit_job(self, prompt: str, model: str = "llama3.2", **params) -> dict:
|
||||
response = self.client.post(
|
||||
f"{self.base_url}/v1/jobs",
|
||||
json={"prompt": prompt, "model": model, "params": params}
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def get_job(self, job_id: str) -> dict:
|
||||
response = self.client.get(f"{self.base_url}/v1/jobs/{job_id}")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def wait_for_job(self, job_id: str, timeout: int = 60) -> dict:
|
||||
import time
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
job = self.get_job(job_id)
|
||||
if job["status"] in ["completed", "failed", "cancelled"]:
|
||||
return job
|
||||
time.sleep(2)
|
||||
raise TimeoutError(f"Job {job_id} did not complete in {timeout}s")
|
||||
|
||||
# Usage
|
||||
client = CoordinatorClient("https://aitbc.bubuit.net/api")
|
||||
job = client.submit_job("Hello, world!")
|
||||
result = client.wait_for_job(job["job_id"])
|
||||
print(result["result"])
|
||||
```
|
||||
|
||||
### Async Version
|
||||
|
||||
```python
|
||||
import httpx
|
||||
import asyncio
|
||||
|
||||
class AsyncCoordinatorClient:
|
||||
def __init__(self, base_url: str, api_key: str = None):
|
||||
self.base_url = base_url
|
||||
headers = {"X-Api-Key": api_key} if api_key else {}
|
||||
self.client = httpx.AsyncClient(headers=headers, timeout=30.0)
|
||||
|
||||
async def submit_job(self, prompt: str, model: str = "llama3.2") -> dict:
|
||||
response = await self.client.post(
|
||||
f"{self.base_url}/v1/jobs",
|
||||
json={"prompt": prompt, "model": model}
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
async def wait_for_job(self, job_id: str, timeout: int = 60) -> dict:
|
||||
start = asyncio.get_event_loop().time()
|
||||
while asyncio.get_event_loop().time() - start < timeout:
|
||||
response = await self.client.get(f"{self.base_url}/v1/jobs/{job_id}")
|
||||
job = response.json()
|
||||
if job["status"] in ["completed", "failed"]:
|
||||
return job
|
||||
await asyncio.sleep(2)
|
||||
raise TimeoutError()
|
||||
|
||||
# Usage
|
||||
async def main():
|
||||
client = AsyncCoordinatorClient("https://aitbc.bubuit.net/api")
|
||||
job = await client.submit_job("Explain AI")
|
||||
result = await client.wait_for_job(job["job_id"])
|
||||
print(result["result"])
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## JavaScript Integration
|
||||
|
||||
```javascript
|
||||
class CoordinatorClient {
|
||||
constructor(baseUrl, apiKey = null) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.headers = { 'Content-Type': 'application/json' };
|
||||
if (apiKey) this.headers['X-Api-Key'] = apiKey;
|
||||
}
|
||||
|
||||
async submitJob(prompt, model = 'llama3.2', params = {}) {
|
||||
const response = await fetch(`${this.baseUrl}/v1/jobs`, {
|
||||
method: 'POST',
|
||||
headers: this.headers,
|
||||
body: JSON.stringify({ prompt, model, params })
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async getJob(jobId) {
|
||||
const response = await fetch(`${this.baseUrl}/v1/jobs/${jobId}`, {
|
||||
headers: this.headers
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async waitForJob(jobId, timeout = 60000) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeout) {
|
||||
const job = await this.getJob(jobId);
|
||||
if (['completed', 'failed', 'cancelled'].includes(job.status)) {
|
||||
return job;
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
}
|
||||
throw new Error('Timeout');
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const client = new CoordinatorClient('https://aitbc.bubuit.net/api');
|
||||
const job = await client.submitJob('Hello!');
|
||||
const result = await client.waitForJob(job.job_id);
|
||||
console.log(result.result);
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### HTTP Status Codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| 200 | Success |
|
||||
| 201 | Created |
|
||||
| 400 | Bad Request (invalid parameters) |
|
||||
| 401 | Unauthorized (invalid API key) |
|
||||
| 404 | Not Found |
|
||||
| 429 | Rate Limited |
|
||||
| 500 | Server Error |
|
||||
|
||||
### Error Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Job not found",
|
||||
"error_code": "JOB_NOT_FOUND"
|
||||
}
|
||||
```
|
||||
|
||||
### Retry Logic
|
||||
|
||||
```python
|
||||
import time
|
||||
from httpx import HTTPStatusError
|
||||
|
||||
def with_retry(func, max_retries=3, backoff=2):
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
return func()
|
||||
except HTTPStatusError as e:
|
||||
if e.response.status_code == 429:
|
||||
retry_after = int(e.response.headers.get("Retry-After", backoff))
|
||||
time.sleep(retry_after)
|
||||
elif e.response.status_code >= 500:
|
||||
time.sleep(backoff * (attempt + 1))
|
||||
else:
|
||||
raise
|
||||
raise Exception("Max retries exceeded")
|
||||
```
|
||||
|
||||
## Webhooks (Coming Soon)
|
||||
|
||||
Register a webhook to receive job completion notifications:
|
||||
|
||||
```bash
|
||||
POST /v1/webhooks
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"url": "https://your-app.com/webhook",
|
||||
"events": ["job.completed", "job.failed"]
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Building a Custom Miner](building-custom-miner.md)
|
||||
- [SDK Examples](sdk-examples.md)
|
||||
- [API Reference](../../reference/components/coordinator_api.md)
|
||||
286
docs/developer/tutorials/marketplace-extensions.md
Normal file
286
docs/developer/tutorials/marketplace-extensions.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# Creating Marketplace Extensions
|
||||
|
||||
This tutorial shows how to build extensions for the AITBC Marketplace.
|
||||
|
||||
## Overview
|
||||
|
||||
Marketplace extensions allow you to:
|
||||
- Add new AI service types
|
||||
- Create custom pricing models
|
||||
- Build specialized interfaces
|
||||
- Integrate third-party services
|
||||
|
||||
## Extension Types
|
||||
|
||||
| Type | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| **Service** | New AI capability | Custom model hosting |
|
||||
| **Widget** | UI component | Prompt builder |
|
||||
| **Integration** | External service | Slack bot |
|
||||
| **Analytics** | Metrics/reporting | Usage dashboard |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
my-extension/
|
||||
├── manifest.json # Extension metadata
|
||||
├── src/
|
||||
│ ├── index.ts # Entry point
|
||||
│ ├── service.ts # Service logic
|
||||
│ └── ui/ # UI components
|
||||
├── assets/
|
||||
│ └── icon.png # Extension icon
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## Step 1: Create Manifest
|
||||
|
||||
`manifest.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-custom-service",
|
||||
"version": "1.0.0",
|
||||
"description": "Custom AI service for AITBC",
|
||||
"type": "service",
|
||||
"author": "Your Name",
|
||||
"homepage": "https://github.com/you/my-extension",
|
||||
"permissions": [
|
||||
"jobs.submit",
|
||||
"jobs.read",
|
||||
"receipts.read"
|
||||
],
|
||||
"entry": "src/index.ts",
|
||||
"icon": "assets/icon.png",
|
||||
"config": {
|
||||
"apiEndpoint": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "Your service API endpoint"
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "secret",
|
||||
"required": true,
|
||||
"description": "API key for authentication"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Step 2: Implement Service
|
||||
|
||||
`src/service.ts`:
|
||||
|
||||
```typescript
|
||||
import { AITBCService, Job, JobResult } from '@aitbc/sdk';
|
||||
|
||||
export class MyCustomService implements AITBCService {
|
||||
name = 'my-custom-service';
|
||||
|
||||
constructor(private config: { apiEndpoint: string; apiKey: string }) {}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
// Validate configuration
|
||||
const response = await fetch(`${this.config.apiEndpoint}/health`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Service endpoint not reachable');
|
||||
}
|
||||
}
|
||||
|
||||
async processJob(job: Job): Promise<JobResult> {
|
||||
const response = await fetch(`${this.config.apiEndpoint}/process`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.config.apiKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompt: job.prompt,
|
||||
params: job.params
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Service error: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return {
|
||||
output: data.result,
|
||||
metadata: {
|
||||
model: data.model,
|
||||
tokens_used: data.tokens
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async estimateCost(job: Job): Promise<number> {
|
||||
// Estimate cost in AITBC tokens
|
||||
const estimatedTokens = job.prompt.length / 4;
|
||||
return estimatedTokens * 0.001; // 0.001 AITBC per token
|
||||
}
|
||||
|
||||
getCapabilities(): string[] {
|
||||
return ['text-generation', 'summarization'];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Step 3: Create Entry Point
|
||||
|
||||
`src/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { ExtensionContext, registerService } from '@aitbc/sdk';
|
||||
import { MyCustomService } from './service';
|
||||
|
||||
export async function activate(context: ExtensionContext): Promise<void> {
|
||||
const config = context.getConfig();
|
||||
|
||||
const service = new MyCustomService({
|
||||
apiEndpoint: config.apiEndpoint,
|
||||
apiKey: config.apiKey
|
||||
});
|
||||
|
||||
await service.initialize();
|
||||
|
||||
registerService(service);
|
||||
|
||||
console.log('My Custom Service extension activated');
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
console.log('My Custom Service extension deactivated');
|
||||
}
|
||||
```
|
||||
|
||||
## Step 4: Add UI Widget (Optional)
|
||||
|
||||
`src/ui/PromptBuilder.tsx`:
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
import { useAITBC } from '@aitbc/react';
|
||||
|
||||
export function PromptBuilder() {
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const { submitJob, isLoading } = useAITBC();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const result = await submitJob({
|
||||
service: 'my-custom-service',
|
||||
prompt,
|
||||
params: { max_tokens: 256 }
|
||||
});
|
||||
console.log('Result:', result);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="prompt-builder">
|
||||
<textarea
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
placeholder="Enter your prompt..."
|
||||
/>
|
||||
<button onClick={handleSubmit} disabled={isLoading}>
|
||||
{isLoading ? 'Processing...' : 'Submit'}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Step 5: Package and Deploy
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Test Locally
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# Extension runs at http://localhost:3000
|
||||
```
|
||||
|
||||
### Deploy to Marketplace
|
||||
|
||||
```bash
|
||||
# Package extension
|
||||
npm run package
|
||||
# Creates my-extension-1.0.0.zip
|
||||
|
||||
# Submit to marketplace
|
||||
aitbc-cli extension submit my-extension-1.0.0.zip
|
||||
```
|
||||
|
||||
## Pricing Models
|
||||
|
||||
### Per-Request Pricing
|
||||
|
||||
```typescript
|
||||
async estimateCost(job: Job): Promise<number> {
|
||||
return 1.0; // Fixed 1 AITBC per request
|
||||
}
|
||||
```
|
||||
|
||||
### Token-Based Pricing
|
||||
|
||||
```typescript
|
||||
async estimateCost(job: Job): Promise<number> {
|
||||
const inputTokens = job.prompt.length / 4;
|
||||
const outputTokens = job.params.max_tokens || 256;
|
||||
return (inputTokens + outputTokens) * 0.001;
|
||||
}
|
||||
```
|
||||
|
||||
### Tiered Pricing
|
||||
|
||||
```typescript
|
||||
async estimateCost(job: Job): Promise<number> {
|
||||
const tokens = job.prompt.length / 4;
|
||||
if (tokens < 100) return 0.5;
|
||||
if (tokens < 1000) return 2.0;
|
||||
return 5.0;
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Validate inputs** - Check all user inputs before processing
|
||||
2. **Handle errors gracefully** - Return meaningful error messages
|
||||
3. **Respect rate limits** - Don't overwhelm external services
|
||||
4. **Cache when possible** - Reduce redundant API calls
|
||||
5. **Log appropriately** - Use structured logging for debugging
|
||||
6. **Version your API** - Support backward compatibility
|
||||
|
||||
## Testing
|
||||
|
||||
```typescript
|
||||
import { MyCustomService } from './service';
|
||||
|
||||
describe('MyCustomService', () => {
|
||||
it('should process job successfully', async () => {
|
||||
const service = new MyCustomService({
|
||||
apiEndpoint: 'http://localhost:8080',
|
||||
apiKey: 'test-key'
|
||||
});
|
||||
|
||||
const result = await service.processJob({
|
||||
prompt: 'Hello, world!',
|
||||
params: {}
|
||||
});
|
||||
|
||||
expect(result.output).toBeDefined();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Coordinator API Integration](coordinator-api-integration.md)
|
||||
- [SDK Examples](sdk-examples.md)
|
||||
- [Existing Extensions](../../tutorials/marketplace-extensions.md)
|
||||
382
docs/developer/tutorials/sdk-examples.md
Normal file
382
docs/developer/tutorials/sdk-examples.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# SDK Usage Examples
|
||||
|
||||
This tutorial provides practical examples for using the AITBC SDKs in Python and JavaScript.
|
||||
|
||||
## Python SDK
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
pip install aitbc-sdk
|
||||
```
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
from aitbc_sdk import AITBCClient
|
||||
|
||||
# Initialize client
|
||||
client = AITBCClient(
|
||||
api_url="https://aitbc.bubuit.net/api",
|
||||
api_key="your-api-key" # Optional
|
||||
)
|
||||
|
||||
# Submit a simple job
|
||||
result = client.submit_and_wait(
|
||||
prompt="What is the capital of France?",
|
||||
model="llama3.2"
|
||||
)
|
||||
print(result.output)
|
||||
# Output: The capital of France is Paris.
|
||||
```
|
||||
|
||||
### Job Management
|
||||
|
||||
```python
|
||||
# Submit job (non-blocking)
|
||||
job = client.submit_job(
|
||||
prompt="Write a haiku about coding",
|
||||
model="llama3.2",
|
||||
params={"max_tokens": 50, "temperature": 0.8}
|
||||
)
|
||||
print(f"Job ID: {job.id}")
|
||||
|
||||
# Check status
|
||||
status = client.get_job_status(job.id)
|
||||
print(f"Status: {status}")
|
||||
|
||||
# Wait for completion
|
||||
result = client.wait_for_job(job.id, timeout=60)
|
||||
print(f"Output: {result.output}")
|
||||
|
||||
# List recent jobs
|
||||
jobs = client.list_jobs(limit=10, status="completed")
|
||||
for j in jobs:
|
||||
print(f"{j.id}: {j.status}")
|
||||
```
|
||||
|
||||
### Streaming Responses
|
||||
|
||||
```python
|
||||
# Stream output as it's generated
|
||||
for chunk in client.stream_job(
|
||||
prompt="Tell me a long story",
|
||||
model="llama3.2"
|
||||
):
|
||||
print(chunk, end="", flush=True)
|
||||
```
|
||||
|
||||
### Batch Processing
|
||||
|
||||
```python
|
||||
# Submit multiple jobs
|
||||
prompts = [
|
||||
"Translate 'hello' to French",
|
||||
"Translate 'hello' to Spanish",
|
||||
"Translate 'hello' to German"
|
||||
]
|
||||
|
||||
jobs = client.submit_batch(prompts, model="llama3.2")
|
||||
|
||||
# Wait for all to complete
|
||||
results = client.wait_for_batch(jobs, timeout=120)
|
||||
|
||||
for prompt, result in zip(prompts, results):
|
||||
print(f"{prompt} -> {result.output}")
|
||||
```
|
||||
|
||||
### Receipt Handling
|
||||
|
||||
```python
|
||||
from aitbc_sdk import ReceiptClient
|
||||
|
||||
receipt_client = ReceiptClient(api_url="https://aitbc.bubuit.net/api")
|
||||
|
||||
# Get receipt for a job
|
||||
receipt = receipt_client.get_receipt(job_id="job-abc123")
|
||||
print(f"Receipt ID: {receipt.receipt_id}")
|
||||
print(f"Units: {receipt.units}")
|
||||
print(f"Price: {receipt.price} AITBC")
|
||||
|
||||
# Verify receipt signature
|
||||
is_valid = receipt_client.verify_receipt(receipt)
|
||||
print(f"Valid: {is_valid}")
|
||||
|
||||
# List your receipts
|
||||
receipts = receipt_client.list_receipts(client_address="ait1...")
|
||||
total_spent = sum(r.price for r in receipts)
|
||||
print(f"Total spent: {total_spent} AITBC")
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```python
|
||||
from aitbc_sdk import AITBCClient, AITBCError, JobFailedError, TimeoutError
|
||||
|
||||
client = AITBCClient(api_url="https://aitbc.bubuit.net/api")
|
||||
|
||||
try:
|
||||
result = client.submit_and_wait(
|
||||
prompt="Complex task...",
|
||||
timeout=30
|
||||
)
|
||||
except TimeoutError:
|
||||
print("Job took too long")
|
||||
except JobFailedError as e:
|
||||
print(f"Job failed: {e.message}")
|
||||
except AITBCError as e:
|
||||
print(f"API error: {e}")
|
||||
```
|
||||
|
||||
### Async Support
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from aitbc_sdk import AsyncAITBCClient
|
||||
|
||||
async def main():
|
||||
client = AsyncAITBCClient(api_url="https://aitbc.bubuit.net/api")
|
||||
|
||||
# Submit multiple jobs concurrently
|
||||
tasks = [
|
||||
client.submit_and_wait(f"Question {i}?")
|
||||
for i in range(5)
|
||||
]
|
||||
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
for i, result in enumerate(results):
|
||||
print(f"Answer {i}: {result.output[:50]}...")
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## JavaScript SDK
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install @aitbc/sdk
|
||||
```
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { AITBCClient } from '@aitbc/sdk';
|
||||
|
||||
const client = new AITBCClient({
|
||||
apiUrl: 'https://aitbc.bubuit.net/api',
|
||||
apiKey: 'your-api-key' // Optional
|
||||
});
|
||||
|
||||
// Submit and wait
|
||||
const result = await client.submitAndWait({
|
||||
prompt: 'What is 2 + 2?',
|
||||
model: 'llama3.2'
|
||||
});
|
||||
|
||||
console.log(result.output);
|
||||
// Output: 2 + 2 equals 4.
|
||||
```
|
||||
|
||||
### Job Management
|
||||
|
||||
```javascript
|
||||
// Submit job
|
||||
const job = await client.submitJob({
|
||||
prompt: 'Explain quantum computing',
|
||||
model: 'llama3.2',
|
||||
params: { maxTokens: 256 }
|
||||
});
|
||||
|
||||
console.log(`Job ID: ${job.id}`);
|
||||
|
||||
// Poll for status
|
||||
const status = await client.getJobStatus(job.id);
|
||||
console.log(`Status: ${status}`);
|
||||
|
||||
// Wait for completion
|
||||
const result = await client.waitForJob(job.id, { timeout: 60000 });
|
||||
console.log(`Output: ${result.output}`);
|
||||
```
|
||||
|
||||
### Streaming
|
||||
|
||||
```javascript
|
||||
// Stream response
|
||||
const stream = client.streamJob({
|
||||
prompt: 'Write a poem',
|
||||
model: 'llama3.2'
|
||||
});
|
||||
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
### React Hook
|
||||
|
||||
```jsx
|
||||
import { useAITBC } from '@aitbc/react';
|
||||
|
||||
function ChatComponent() {
|
||||
const { submitJob, isLoading, result, error } = useAITBC();
|
||||
const [prompt, setPrompt] = useState('');
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await submitJob({ prompt, model: 'llama3.2' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
placeholder="Ask something..."
|
||||
/>
|
||||
<button onClick={handleSubmit} disabled={isLoading}>
|
||||
{isLoading ? 'Thinking...' : 'Ask'}
|
||||
</button>
|
||||
{error && <p className="error">{error.message}</p>}
|
||||
{result && <p className="result">{result.output}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript Types
|
||||
|
||||
```typescript
|
||||
import { AITBCClient, Job, JobResult, Receipt } from '@aitbc/sdk';
|
||||
|
||||
interface MyJobParams {
|
||||
prompt: string;
|
||||
model: string;
|
||||
maxTokens?: number;
|
||||
}
|
||||
|
||||
async function processJob(params: MyJobParams): Promise<JobResult> {
|
||||
const client = new AITBCClient({ apiUrl: '...' });
|
||||
|
||||
const job: Job = await client.submitJob(params);
|
||||
const result: JobResult = await client.waitForJob(job.id);
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```javascript
|
||||
import { AITBCClient, AITBCError, TimeoutError } from '@aitbc/sdk';
|
||||
|
||||
const client = new AITBCClient({ apiUrl: '...' });
|
||||
|
||||
try {
|
||||
const result = await client.submitAndWait({
|
||||
prompt: 'Complex task',
|
||||
timeout: 30000
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof TimeoutError) {
|
||||
console.log('Job timed out');
|
||||
} else if (error instanceof AITBCError) {
|
||||
console.log(`API error: ${error.message}`);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Retry with Exponential Backoff
|
||||
|
||||
```python
|
||||
import time
|
||||
from aitbc_sdk import AITBCClient, AITBCError
|
||||
|
||||
def submit_with_retry(client, prompt, max_retries=3):
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
return client.submit_and_wait(prompt)
|
||||
except AITBCError as e:
|
||||
if attempt == max_retries - 1:
|
||||
raise
|
||||
wait_time = 2 ** attempt
|
||||
print(f"Retry in {wait_time}s...")
|
||||
time.sleep(wait_time)
|
||||
```
|
||||
|
||||
### Caching Results
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import json
|
||||
from functools import lru_cache
|
||||
|
||||
@lru_cache(maxsize=100)
|
||||
def cached_query(prompt_hash: str) -> str:
|
||||
# Cache based on prompt hash
|
||||
return client.submit_and_wait(prompt).output
|
||||
|
||||
def query(prompt: str) -> str:
|
||||
prompt_hash = hashlib.md5(prompt.encode()).hexdigest()
|
||||
return cached_query(prompt_hash)
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```python
|
||||
import time
|
||||
from threading import Lock
|
||||
|
||||
class RateLimitedClient:
|
||||
def __init__(self, client, requests_per_minute=60):
|
||||
self.client = client
|
||||
self.min_interval = 60.0 / requests_per_minute
|
||||
self.last_request = 0
|
||||
self.lock = Lock()
|
||||
|
||||
def submit(self, prompt):
|
||||
with self.lock:
|
||||
elapsed = time.time() - self.last_request
|
||||
if elapsed < self.min_interval:
|
||||
time.sleep(self.min_interval - elapsed)
|
||||
self.last_request = time.time()
|
||||
|
||||
return self.client.submit_and_wait(prompt)
|
||||
```
|
||||
|
||||
### Logging and Monitoring
|
||||
|
||||
```python
|
||||
import logging
|
||||
from aitbc_sdk import AITBCClient
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class LoggingClient:
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
def submit_and_wait(self, prompt, **kwargs):
|
||||
logger.info(f"Submitting job: {prompt[:50]}...")
|
||||
start = time.time()
|
||||
|
||||
try:
|
||||
result = self.client.submit_and_wait(prompt, **kwargs)
|
||||
elapsed = time.time() - start
|
||||
logger.info(f"Job completed in {elapsed:.2f}s")
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f"Job failed: {e}")
|
||||
raise
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Coordinator API Integration](coordinator-api-integration.md)
|
||||
- [Building a Custom Miner](building-custom-miner.md)
|
||||
- [Python SDK Reference](../../reference/components/coordinator_api.md)
|
||||
315
docs/developer/tutorials/zk-proofs.md
Normal file
315
docs/developer/tutorials/zk-proofs.md
Normal file
@@ -0,0 +1,315 @@
|
||||
# Working with ZK Proofs
|
||||
|
||||
This tutorial explains how to use zero-knowledge proofs in the AITBC network for privacy-preserving operations.
|
||||
|
||||
## Overview
|
||||
|
||||
AITBC uses ZK proofs for:
|
||||
- **Private receipt attestation** - Prove job completion without revealing details
|
||||
- **Identity commitments** - Prove identity without exposing address
|
||||
- **Stealth addresses** - Receive payments privately
|
||||
- **Group membership** - Prove you're part of a group without revealing which member
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Circom compiler v2.2.3+
|
||||
- snarkjs library
|
||||
- Node.js 18+
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Circuit │────▶│ Prover │────▶│ Verifier │
|
||||
│ (Circom) │ │ (snarkjs) │ │ (On-chain) │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
## Step 1: Understanding Circuits
|
||||
|
||||
AITBC includes pre-built circuits in `apps/zk-circuits/`:
|
||||
|
||||
### Receipt Simple Circuit
|
||||
|
||||
Proves a receipt is valid without revealing the full receipt:
|
||||
|
||||
```circom
|
||||
// circuits/receipt_simple.circom
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "circomlib/poseidon.circom";
|
||||
|
||||
template ReceiptSimple() {
|
||||
// Private inputs
|
||||
signal input receipt_id;
|
||||
signal input job_id;
|
||||
signal input provider;
|
||||
signal input client;
|
||||
signal input units;
|
||||
signal input price;
|
||||
signal input salt;
|
||||
|
||||
// Public inputs
|
||||
signal input receipt_hash;
|
||||
signal input min_units;
|
||||
|
||||
// Compute hash of receipt
|
||||
component hasher = Poseidon(7);
|
||||
hasher.inputs[0] <== receipt_id;
|
||||
hasher.inputs[1] <== job_id;
|
||||
hasher.inputs[2] <== provider;
|
||||
hasher.inputs[3] <== client;
|
||||
hasher.inputs[4] <== units;
|
||||
hasher.inputs[5] <== price;
|
||||
hasher.inputs[6] <== salt;
|
||||
|
||||
// Verify hash matches
|
||||
receipt_hash === hasher.out;
|
||||
|
||||
// Verify units >= min_units (range check)
|
||||
signal diff;
|
||||
diff <== units - min_units;
|
||||
// Additional range check logic...
|
||||
}
|
||||
|
||||
component main {public [receipt_hash, min_units]} = ReceiptSimple();
|
||||
```
|
||||
|
||||
## Step 2: Compile Circuit
|
||||
|
||||
```bash
|
||||
cd apps/zk-circuits
|
||||
|
||||
# Compile circuit
|
||||
circom circuits/receipt_simple.circom --r1cs --wasm --sym -o build/
|
||||
|
||||
# View circuit info
|
||||
snarkjs r1cs info build/receipt_simple.r1cs
|
||||
# Constraints: 300
|
||||
```
|
||||
|
||||
## Step 3: Trusted Setup
|
||||
|
||||
```bash
|
||||
# Download Powers of Tau (one-time)
|
||||
wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_12.ptau
|
||||
|
||||
# Generate proving key
|
||||
snarkjs groth16 setup build/receipt_simple.r1cs powersOfTau28_hez_final_12.ptau build/receipt_simple_0000.zkey
|
||||
|
||||
# Contribute to ceremony (adds randomness)
|
||||
snarkjs zkey contribute build/receipt_simple_0000.zkey build/receipt_simple_final.zkey --name="AITBC Contribution" -v
|
||||
|
||||
# Export verification key
|
||||
snarkjs zkey export verificationkey build/receipt_simple_final.zkey build/verification_key.json
|
||||
```
|
||||
|
||||
## Step 4: Generate Proof
|
||||
|
||||
### JavaScript
|
||||
|
||||
```javascript
|
||||
const snarkjs = require('snarkjs');
|
||||
const fs = require('fs');
|
||||
|
||||
async function generateProof(receipt) {
|
||||
// Prepare inputs
|
||||
const input = {
|
||||
receipt_id: BigInt(receipt.receipt_id),
|
||||
job_id: BigInt(receipt.job_id),
|
||||
provider: BigInt(receipt.provider),
|
||||
client: BigInt(receipt.client),
|
||||
units: BigInt(Math.floor(receipt.units * 1000)),
|
||||
price: BigInt(Math.floor(receipt.price * 1000)),
|
||||
salt: BigInt(receipt.salt),
|
||||
receipt_hash: BigInt(receipt.hash),
|
||||
min_units: BigInt(1000) // Prove units >= 1.0
|
||||
};
|
||||
|
||||
// Generate proof
|
||||
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
|
||||
input,
|
||||
'build/receipt_simple_js/receipt_simple.wasm',
|
||||
'build/receipt_simple_final.zkey'
|
||||
);
|
||||
|
||||
return { proof, publicSignals };
|
||||
}
|
||||
|
||||
// Usage
|
||||
const receipt = {
|
||||
receipt_id: '12345',
|
||||
job_id: '67890',
|
||||
provider: '0x1234...',
|
||||
client: '0x5678...',
|
||||
units: 2.5,
|
||||
price: 5.0,
|
||||
salt: '0xabcd...',
|
||||
hash: '0x9876...'
|
||||
};
|
||||
|
||||
const { proof, publicSignals } = await generateProof(receipt);
|
||||
console.log('Proof generated:', proof);
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
def generate_proof(receipt: dict) -> dict:
|
||||
# Write input file
|
||||
input_data = {
|
||||
"receipt_id": str(receipt["receipt_id"]),
|
||||
"job_id": str(receipt["job_id"]),
|
||||
"provider": str(int(receipt["provider"], 16)),
|
||||
"client": str(int(receipt["client"], 16)),
|
||||
"units": str(int(receipt["units"] * 1000)),
|
||||
"price": str(int(receipt["price"] * 1000)),
|
||||
"salt": str(int(receipt["salt"], 16)),
|
||||
"receipt_hash": str(int(receipt["hash"], 16)),
|
||||
"min_units": "1000"
|
||||
}
|
||||
|
||||
with open("input.json", "w") as f:
|
||||
json.dump(input_data, f)
|
||||
|
||||
# Generate witness
|
||||
subprocess.run([
|
||||
"node", "build/receipt_simple_js/generate_witness.js",
|
||||
"build/receipt_simple_js/receipt_simple.wasm",
|
||||
"input.json", "witness.wtns"
|
||||
], check=True)
|
||||
|
||||
# Generate proof
|
||||
subprocess.run([
|
||||
"snarkjs", "groth16", "prove",
|
||||
"build/receipt_simple_final.zkey",
|
||||
"witness.wtns", "proof.json", "public.json"
|
||||
], check=True)
|
||||
|
||||
with open("proof.json") as f:
|
||||
proof = json.load(f)
|
||||
with open("public.json") as f:
|
||||
public_signals = json.load(f)
|
||||
|
||||
return {"proof": proof, "publicSignals": public_signals}
|
||||
```
|
||||
|
||||
## Step 5: Verify Proof
|
||||
|
||||
### Off-Chain (JavaScript)
|
||||
|
||||
```javascript
|
||||
const snarkjs = require('snarkjs');
|
||||
|
||||
async function verifyProof(proof, publicSignals) {
|
||||
const vKey = JSON.parse(fs.readFileSync('build/verification_key.json'));
|
||||
|
||||
const isValid = await snarkjs.groth16.verify(vKey, publicSignals, proof);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
const isValid = await verifyProof(proof, publicSignals);
|
||||
console.log('Proof valid:', isValid);
|
||||
```
|
||||
|
||||
### On-Chain (Solidity)
|
||||
|
||||
The `ZKReceiptVerifier.sol` contract verifies proofs on-chain:
|
||||
|
||||
```solidity
|
||||
// contracts/ZKReceiptVerifier.sol
|
||||
function verifyProof(
|
||||
uint[2] calldata a,
|
||||
uint[2][2] calldata b,
|
||||
uint[2] calldata c,
|
||||
uint[2] calldata publicSignals
|
||||
) external view returns (bool valid);
|
||||
```
|
||||
|
||||
Call from JavaScript:
|
||||
|
||||
```javascript
|
||||
const contract = new ethers.Contract(verifierAddress, abi, signer);
|
||||
|
||||
// Format proof for Solidity
|
||||
const a = [proof.pi_a[0], proof.pi_a[1]];
|
||||
const b = [[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]];
|
||||
const c = [proof.pi_c[0], proof.pi_c[1]];
|
||||
|
||||
const isValid = await contract.verifyProof(a, b, c, publicSignals);
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Private Receipt Attestation
|
||||
|
||||
Prove you completed a job worth at least X tokens without revealing exact amount:
|
||||
|
||||
```javascript
|
||||
// Prove receipt has units >= 10
|
||||
const { proof } = await generateProof({
|
||||
...receipt,
|
||||
min_units: 10000 // 10.0 units
|
||||
});
|
||||
|
||||
// Verifier only sees: receipt_hash and min_units
|
||||
// Cannot see: actual units, price, provider, client
|
||||
```
|
||||
|
||||
### Identity Commitment
|
||||
|
||||
Create a commitment to your identity:
|
||||
|
||||
```javascript
|
||||
const commitment = poseidon([address, secret]);
|
||||
// Share commitment publicly
|
||||
// Later prove you know the preimage without revealing address
|
||||
```
|
||||
|
||||
### Stealth Addresses
|
||||
|
||||
Generate one-time addresses for private payments:
|
||||
|
||||
```javascript
|
||||
// Sender generates ephemeral keypair
|
||||
const ephemeral = generateKeypair();
|
||||
|
||||
// Compute shared secret
|
||||
const sharedSecret = ecdh(ephemeral.private, recipientPublic);
|
||||
|
||||
// Derive stealth address
|
||||
const stealthAddress = deriveAddress(recipientAddress, sharedSecret);
|
||||
|
||||
// Send to stealth address
|
||||
await sendPayment(stealthAddress, amount);
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Never reuse salts** - Each proof should use a unique salt
|
||||
2. **Validate inputs** - Check ranges before proving
|
||||
3. **Use trusted setup** - Don't skip the ceremony
|
||||
4. **Test thoroughly** - Verify proofs before deploying
|
||||
5. **Keep secrets secret** - Private inputs must stay private
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Constraint not satisfied"
|
||||
- Check input values are within expected ranges
|
||||
- Verify all required inputs are provided
|
||||
- Ensure BigInt conversion is correct
|
||||
|
||||
### "Invalid proof"
|
||||
- Verify using same verification key as proving key
|
||||
- Check public signals match between prover and verifier
|
||||
- Ensure proof format is correct for verifier
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [ZK Applications Reference](../../reference/components/zk-applications.md)
|
||||
- [ZK Receipt Attestation](../../reference/zk-receipt-attestation.md)
|
||||
- [SDK Examples](sdk-examples.md)
|
||||
47
docs/done.md
47
docs/done.md
@@ -296,3 +296,50 @@ This document tracks components that have been successfully deployed and are ope
|
||||
- ✅ **Roadmap Updates**
|
||||
- Added Stage 19: Placeholder Content Development
|
||||
- Added Stage 20: Technical Debt Remediation (blockchain-node, solidity-token, ZKReceiptVerifier)
|
||||
|
||||
### Stage 19: Placeholder Content Development (2026-01-24)
|
||||
|
||||
- ✅ **Phase 1: Documentation** (17 files created)
|
||||
- User Guides (`docs/user/guides/`): 8 files
|
||||
- `getting-started.md`, `job-submission.md`, `payments-receipts.md`, `troubleshooting.md`
|
||||
- Developer Tutorials (`docs/developer/tutorials/`): 5 files
|
||||
- `building-custom-miner.md`, `coordinator-api-integration.md`
|
||||
- `marketplace-extensions.md`, `zk-proofs.md`, `sdk-examples.md`
|
||||
- Reference Specs (`docs/reference/specs/`): 4 files
|
||||
- `api-reference.md` (OpenAPI 3.0), `protocol-messages.md`, `error-codes.md`
|
||||
|
||||
- ✅ **Phase 2: Infrastructure** (8 files created)
|
||||
- Terraform Environments (`infra/terraform/environments/`):
|
||||
- `staging/main.tf`, `prod/main.tf`, `variables.tf`, `secrets.tf`, `backend.tf`
|
||||
- Helm Chart Values (`infra/helm/values/`):
|
||||
- `dev/values.yaml`, `staging/values.yaml`, `prod/values.yaml`
|
||||
|
||||
- ✅ **Phase 3: Application Components** (13 files created)
|
||||
- Pool Hub Service (`apps/pool-hub/src/app/`):
|
||||
- `routers/`: miners.py, pools.py, jobs.py, health.py, __init__.py
|
||||
- `registry/`: miner_registry.py, __init__.py
|
||||
- `scoring/`: scoring_engine.py, __init__.py
|
||||
- Coordinator Migrations (`apps/coordinator-api/migrations/`):
|
||||
- `001_initial_schema.sql`, `002_indexes.sql`, `003_data_migration.py`, `README.md`
|
||||
|
||||
### Stage 20: Technical Debt Remediation (2026-01-24)
|
||||
|
||||
- ✅ **Blockchain Node SQLModel Fixes**
|
||||
- Fixed `models.py`: Added `__tablename__`, proper `Relationship` definitions
|
||||
- Fixed type hints: `List["Transaction"]` instead of `list["Transaction"]`
|
||||
- Added `sa_relationship_kwargs={"lazy": "selectin"}` for efficient loading
|
||||
- Updated tests: 2 passing, 1 skipped (SQLModel validator limitation documented)
|
||||
- Created `docs/SCHEMA.md` with ERD and usage examples
|
||||
|
||||
- ✅ **Solidity Token Audit**
|
||||
- Reviewed `AIToken.sol` and `AITokenRegistry.sol`
|
||||
- Added comprehensive tests: 17 tests passing
|
||||
- AIToken: 8 tests (minting, replay, zero address, zero units, non-coordinator)
|
||||
- AITokenRegistry: 9 tests (registration, updates, access control)
|
||||
- Created `docs/DEPLOYMENT.md` with full deployment guide
|
||||
|
||||
- ✅ **ZK Receipt Verifier Integration**
|
||||
- Fixed `ZKReceiptVerifier.sol` to match `receipt_simple` circuit
|
||||
- Updated `publicSignals` to `uint[1]` (1 public signal: receiptHash)
|
||||
- Fixed authorization checks: `require(authorizedVerifiers[msg.sender])`
|
||||
- Created `contracts/docs/ZK-VERIFICATION.md` with integration guide
|
||||
|
||||
516
docs/reference/specs/api-reference.md
Normal file
516
docs/reference/specs/api-reference.md
Normal file
@@ -0,0 +1,516 @@
|
||||
# AITBC API Reference (OpenAPI)
|
||||
|
||||
This document provides the complete API reference for the AITBC Coordinator API.
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
Production: https://aitbc.bubuit.net/api
|
||||
Local: http://127.0.0.1:8001
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Most endpoints require an API key passed in the header:
|
||||
|
||||
```
|
||||
X-Api-Key: your-api-key
|
||||
```
|
||||
|
||||
## OpenAPI Specification
|
||||
|
||||
```yaml
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: AITBC Coordinator API
|
||||
version: 1.0.0
|
||||
description: API for submitting AI compute jobs and managing the AITBC network
|
||||
|
||||
servers:
|
||||
- url: https://aitbc.bubuit.net/api
|
||||
description: Production
|
||||
- url: http://127.0.0.1:8001
|
||||
description: Local development
|
||||
|
||||
paths:
|
||||
/health:
|
||||
get:
|
||||
summary: Health check
|
||||
tags: [System]
|
||||
responses:
|
||||
'200':
|
||||
description: Service is healthy
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: ok
|
||||
version:
|
||||
type: string
|
||||
example: 1.0.0
|
||||
|
||||
/v1/jobs:
|
||||
post:
|
||||
summary: Submit a new job
|
||||
tags: [Jobs]
|
||||
security:
|
||||
- ApiKey: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/JobRequest'
|
||||
responses:
|
||||
'201':
|
||||
description: Job created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Job'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
get:
|
||||
summary: List jobs
|
||||
tags: [Jobs]
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [pending, running, completed, failed, cancelled]
|
||||
- name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
maximum: 100
|
||||
- name: offset
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 0
|
||||
responses:
|
||||
'200':
|
||||
description: List of jobs
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Job'
|
||||
|
||||
/v1/jobs/{job_id}:
|
||||
get:
|
||||
summary: Get job details
|
||||
tags: [Jobs]
|
||||
parameters:
|
||||
- name: job_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Job details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Job'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/v1/jobs/{job_id}/cancel:
|
||||
post:
|
||||
summary: Cancel a job
|
||||
tags: [Jobs]
|
||||
parameters:
|
||||
- name: job_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Job cancelled
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'409':
|
||||
description: Job cannot be cancelled (already completed)
|
||||
|
||||
/v1/jobs/available:
|
||||
get:
|
||||
summary: Get available jobs for miners
|
||||
tags: [Miners]
|
||||
parameters:
|
||||
- name: miner_id
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Available job or null
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Job'
|
||||
|
||||
/v1/jobs/{job_id}/claim:
|
||||
post:
|
||||
summary: Claim a job for processing
|
||||
tags: [Miners]
|
||||
parameters:
|
||||
- name: job_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
miner_id:
|
||||
type: string
|
||||
required:
|
||||
- miner_id
|
||||
responses:
|
||||
'200':
|
||||
description: Job claimed
|
||||
'409':
|
||||
description: Job already claimed
|
||||
|
||||
/v1/jobs/{job_id}/complete:
|
||||
post:
|
||||
summary: Submit job result
|
||||
tags: [Miners]
|
||||
parameters:
|
||||
- name: job_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/JobResult'
|
||||
responses:
|
||||
'200':
|
||||
description: Job completed
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Receipt'
|
||||
|
||||
/v1/miners/register:
|
||||
post:
|
||||
summary: Register a miner
|
||||
tags: [Miners]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MinerRegistration'
|
||||
responses:
|
||||
'201':
|
||||
description: Miner registered
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
|
||||
/v1/receipts:
|
||||
get:
|
||||
summary: List receipts
|
||||
tags: [Receipts]
|
||||
parameters:
|
||||
- name: client
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: provider
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
responses:
|
||||
'200':
|
||||
description: List of receipts
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Receipt'
|
||||
|
||||
/v1/receipts/{receipt_id}:
|
||||
get:
|
||||
summary: Get receipt details
|
||||
tags: [Receipts]
|
||||
parameters:
|
||||
- name: receipt_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Receipt details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Receipt'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/explorer/blocks:
|
||||
get:
|
||||
summary: Get recent blocks
|
||||
tags: [Explorer]
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 10
|
||||
responses:
|
||||
'200':
|
||||
description: List of blocks
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Block'
|
||||
|
||||
/explorer/transactions:
|
||||
get:
|
||||
summary: Get recent transactions
|
||||
tags: [Explorer]
|
||||
responses:
|
||||
'200':
|
||||
description: List of transactions
|
||||
|
||||
/explorer/receipts:
|
||||
get:
|
||||
summary: Get recent receipts
|
||||
tags: [Explorer]
|
||||
responses:
|
||||
'200':
|
||||
description: List of receipts
|
||||
|
||||
/explorer/stats:
|
||||
get:
|
||||
summary: Get network statistics
|
||||
tags: [Explorer]
|
||||
responses:
|
||||
'200':
|
||||
description: Network stats
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkStats'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
ApiKey:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-Api-Key
|
||||
|
||||
schemas:
|
||||
JobRequest:
|
||||
type: object
|
||||
properties:
|
||||
prompt:
|
||||
type: string
|
||||
description: Input prompt
|
||||
model:
|
||||
type: string
|
||||
default: llama3.2
|
||||
params:
|
||||
type: object
|
||||
properties:
|
||||
max_tokens:
|
||||
type: integer
|
||||
default: 256
|
||||
temperature:
|
||||
type: number
|
||||
default: 0.7
|
||||
top_p:
|
||||
type: number
|
||||
default: 0.9
|
||||
required:
|
||||
- prompt
|
||||
|
||||
Job:
|
||||
type: object
|
||||
properties:
|
||||
job_id:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum: [pending, running, completed, failed, cancelled]
|
||||
prompt:
|
||||
type: string
|
||||
model:
|
||||
type: string
|
||||
result:
|
||||
type: string
|
||||
miner_id:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
started_at:
|
||||
type: string
|
||||
format: date-time
|
||||
completed_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
JobResult:
|
||||
type: object
|
||||
properties:
|
||||
miner_id:
|
||||
type: string
|
||||
result:
|
||||
type: string
|
||||
completed_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- miner_id
|
||||
- result
|
||||
|
||||
Receipt:
|
||||
type: object
|
||||
properties:
|
||||
receipt_id:
|
||||
type: string
|
||||
job_id:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
client:
|
||||
type: string
|
||||
units:
|
||||
type: number
|
||||
unit_type:
|
||||
type: string
|
||||
price:
|
||||
type: number
|
||||
model:
|
||||
type: string
|
||||
started_at:
|
||||
type: integer
|
||||
completed_at:
|
||||
type: integer
|
||||
signature:
|
||||
$ref: '#/components/schemas/Signature'
|
||||
|
||||
Signature:
|
||||
type: object
|
||||
properties:
|
||||
alg:
|
||||
type: string
|
||||
key_id:
|
||||
type: string
|
||||
sig:
|
||||
type: string
|
||||
|
||||
MinerRegistration:
|
||||
type: object
|
||||
properties:
|
||||
miner_id:
|
||||
type: string
|
||||
capabilities:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
gpu_info:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
required:
|
||||
- miner_id
|
||||
- capabilities
|
||||
|
||||
Block:
|
||||
type: object
|
||||
properties:
|
||||
height:
|
||||
type: integer
|
||||
hash:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
transactions:
|
||||
type: integer
|
||||
|
||||
NetworkStats:
|
||||
type: object
|
||||
properties:
|
||||
total_jobs:
|
||||
type: integer
|
||||
active_miners:
|
||||
type: integer
|
||||
total_receipts:
|
||||
type: integer
|
||||
block_height:
|
||||
type: integer
|
||||
|
||||
responses:
|
||||
BadRequest:
|
||||
description: Invalid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
|
||||
Unauthorized:
|
||||
description: Authentication required
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
|
||||
NotFound:
|
||||
description: Resource not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
|
||||
Error:
|
||||
type: object
|
||||
properties:
|
||||
detail:
|
||||
type: string
|
||||
error_code:
|
||||
type: string
|
||||
```
|
||||
|
||||
## Interactive Documentation
|
||||
|
||||
Access the interactive API documentation at:
|
||||
- **Swagger UI**: https://aitbc.bubuit.net/api/docs
|
||||
- **ReDoc**: https://aitbc.bubuit.net/api/redoc
|
||||
269
docs/reference/specs/error-codes.md
Normal file
269
docs/reference/specs/error-codes.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# Error Codes and Handling
|
||||
|
||||
This document defines all error codes used by the AITBC API and how to handle them.
|
||||
|
||||
## Error Response Format
|
||||
|
||||
All API errors follow this format:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Human-readable error message",
|
||||
"error_code": "ERROR_CODE",
|
||||
"request_id": "req-abc123"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `detail` | string | Human-readable description |
|
||||
| `error_code` | string | Machine-readable error code |
|
||||
| `request_id` | string | Request identifier for debugging |
|
||||
|
||||
## HTTP Status Codes
|
||||
|
||||
| Code | Meaning | When Used |
|
||||
|------|---------|-----------|
|
||||
| 200 | OK | Successful GET, POST (update) |
|
||||
| 201 | Created | Successful POST (create) |
|
||||
| 204 | No Content | Successful DELETE |
|
||||
| 400 | Bad Request | Invalid input |
|
||||
| 401 | Unauthorized | Missing/invalid authentication |
|
||||
| 403 | Forbidden | Insufficient permissions |
|
||||
| 404 | Not Found | Resource doesn't exist |
|
||||
| 409 | Conflict | Resource state conflict |
|
||||
| 422 | Unprocessable Entity | Validation error |
|
||||
| 429 | Too Many Requests | Rate limited |
|
||||
| 500 | Internal Server Error | Server error |
|
||||
| 502 | Bad Gateway | Upstream service error |
|
||||
| 503 | Service Unavailable | Maintenance/overload |
|
||||
|
||||
## Error Codes by Category
|
||||
|
||||
### Authentication Errors (AUTH_*)
|
||||
|
||||
| Code | HTTP | Description | Resolution |
|
||||
|------|------|-------------|------------|
|
||||
| `AUTH_MISSING_KEY` | 401 | No API key provided | Include `X-Api-Key` header |
|
||||
| `AUTH_INVALID_KEY` | 401 | API key is invalid | Check API key is correct |
|
||||
| `AUTH_EXPIRED_KEY` | 401 | API key has expired | Generate new API key |
|
||||
| `AUTH_INSUFFICIENT_SCOPE` | 403 | Key lacks required permissions | Use key with correct scope |
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"detail": "API key is required for this endpoint",
|
||||
"error_code": "AUTH_MISSING_KEY"
|
||||
}
|
||||
```
|
||||
|
||||
### Job Errors (JOB_*)
|
||||
|
||||
| Code | HTTP | Description | Resolution |
|
||||
|------|------|-------------|------------|
|
||||
| `JOB_NOT_FOUND` | 404 | Job doesn't exist | Check job ID |
|
||||
| `JOB_ALREADY_CLAIMED` | 409 | Job claimed by another miner | Request different job |
|
||||
| `JOB_ALREADY_COMPLETED` | 409 | Job already finished | No action needed |
|
||||
| `JOB_ALREADY_CANCELLED` | 409 | Job was cancelled | Submit new job |
|
||||
| `JOB_EXPIRED` | 410 | Job deadline passed | Submit new job |
|
||||
| `JOB_INVALID_STATUS` | 400 | Invalid status transition | Check job state |
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"detail": "Job job-abc123 not found",
|
||||
"error_code": "JOB_NOT_FOUND"
|
||||
}
|
||||
```
|
||||
|
||||
### Validation Errors (VALIDATION_*)
|
||||
|
||||
| Code | HTTP | Description | Resolution |
|
||||
|------|------|-------------|------------|
|
||||
| `VALIDATION_MISSING_FIELD` | 422 | Required field missing | Include required field |
|
||||
| `VALIDATION_INVALID_TYPE` | 422 | Wrong field type | Use correct type |
|
||||
| `VALIDATION_OUT_OF_RANGE` | 422 | Value outside allowed range | Use value in range |
|
||||
| `VALIDATION_INVALID_FORMAT` | 422 | Wrong format (e.g., date) | Use correct format |
|
||||
| `VALIDATION_PROMPT_TOO_LONG` | 422 | Prompt exceeds limit | Shorten prompt |
|
||||
| `VALIDATION_INVALID_MODEL` | 422 | Model not supported | Use valid model |
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"detail": "Field 'prompt' is required",
|
||||
"error_code": "VALIDATION_MISSING_FIELD",
|
||||
"field": "prompt"
|
||||
}
|
||||
```
|
||||
|
||||
### Miner Errors (MINER_*)
|
||||
|
||||
| Code | HTTP | Description | Resolution |
|
||||
|------|------|-------------|------------|
|
||||
| `MINER_NOT_FOUND` | 404 | Miner not registered | Register miner first |
|
||||
| `MINER_ALREADY_REGISTERED` | 409 | Miner ID already exists | Use different ID |
|
||||
| `MINER_OFFLINE` | 503 | Miner not responding | Check miner status |
|
||||
| `MINER_CAPACITY_FULL` | 503 | Miner at max capacity | Wait or use different miner |
|
||||
|
||||
### Receipt Errors (RECEIPT_*)
|
||||
|
||||
| Code | HTTP | Description | Resolution |
|
||||
|------|------|-------------|------------|
|
||||
| `RECEIPT_NOT_FOUND` | 404 | Receipt doesn't exist | Check receipt ID |
|
||||
| `RECEIPT_INVALID_SIGNATURE` | 400 | Signature verification failed | Check receipt integrity |
|
||||
| `RECEIPT_ALREADY_CLAIMED` | 409 | Receipt already processed | No action needed |
|
||||
|
||||
### Rate Limit Errors (RATE_*)
|
||||
|
||||
| Code | HTTP | Description | Resolution |
|
||||
|------|------|-------------|------------|
|
||||
| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests | Wait and retry |
|
||||
| `RATE_QUOTA_EXCEEDED` | 429 | Daily/monthly quota hit | Upgrade plan or wait |
|
||||
|
||||
**Response includes:**
|
||||
```json
|
||||
{
|
||||
"detail": "Rate limit exceeded. Retry after 60 seconds",
|
||||
"error_code": "RATE_LIMIT_EXCEEDED",
|
||||
"retry_after": 60
|
||||
}
|
||||
```
|
||||
|
||||
### Payment Errors (PAYMENT_*)
|
||||
|
||||
| Code | HTTP | Description | Resolution |
|
||||
|------|------|-------------|------------|
|
||||
| `PAYMENT_INSUFFICIENT_BALANCE` | 402 | Not enough AITBC | Top up balance |
|
||||
| `PAYMENT_FAILED` | 500 | Payment processing error | Retry or contact support |
|
||||
|
||||
### System Errors (SYSTEM_*)
|
||||
|
||||
| Code | HTTP | Description | Resolution |
|
||||
|------|------|-------------|------------|
|
||||
| `SYSTEM_INTERNAL_ERROR` | 500 | Unexpected server error | Retry or report bug |
|
||||
| `SYSTEM_MAINTENANCE` | 503 | Scheduled maintenance | Wait for maintenance to end |
|
||||
| `SYSTEM_OVERLOADED` | 503 | System at capacity | Retry with backoff |
|
||||
| `SYSTEM_UPSTREAM_ERROR` | 502 | Dependency failure | Retry later |
|
||||
|
||||
## Error Handling Best Practices
|
||||
|
||||
### Retry Logic
|
||||
|
||||
```python
|
||||
import time
|
||||
import httpx
|
||||
|
||||
def request_with_retry(url, max_retries=3):
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = httpx.get(url)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except httpx.HTTPStatusError as e:
|
||||
error = e.response.json()
|
||||
code = error.get("error_code", "")
|
||||
|
||||
# Don't retry client errors (except rate limits)
|
||||
if e.response.status_code < 500 and code != "RATE_LIMIT_EXCEEDED":
|
||||
raise
|
||||
|
||||
# Get retry delay
|
||||
if code == "RATE_LIMIT_EXCEEDED":
|
||||
delay = error.get("retry_after", 60)
|
||||
else:
|
||||
delay = 2 ** attempt # Exponential backoff
|
||||
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(delay)
|
||||
else:
|
||||
raise
|
||||
```
|
||||
|
||||
### JavaScript Error Handling
|
||||
|
||||
```javascript
|
||||
async function apiRequest(url, options = {}) {
|
||||
const response = await fetch(url, options);
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
|
||||
switch (error.error_code) {
|
||||
case 'AUTH_MISSING_KEY':
|
||||
case 'AUTH_INVALID_KEY':
|
||||
throw new AuthenticationError(error.detail);
|
||||
|
||||
case 'RATE_LIMIT_EXCEEDED':
|
||||
const retryAfter = error.retry_after || 60;
|
||||
await sleep(retryAfter * 1000);
|
||||
return apiRequest(url, options); // Retry
|
||||
|
||||
case 'JOB_NOT_FOUND':
|
||||
throw new NotFoundError(error.detail);
|
||||
|
||||
default:
|
||||
throw new APIError(error.detail, error.error_code);
|
||||
}
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
```
|
||||
|
||||
### Logging Errors
|
||||
|
||||
Always log the `request_id` for debugging:
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
result = api_call()
|
||||
except APIError as e:
|
||||
logger.error(
|
||||
"API error",
|
||||
extra={
|
||||
"error_code": e.error_code,
|
||||
"detail": e.detail,
|
||||
"request_id": e.request_id
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
When reporting errors to support, include:
|
||||
1. **Error code** and message
|
||||
2. **Request ID** (from response)
|
||||
3. **Timestamp** of the error
|
||||
4. **Request details** (endpoint, parameters)
|
||||
5. **Steps to reproduce**
|
||||
|
||||
## Error Code Reference Table
|
||||
|
||||
| Code | HTTP | Category | Retryable |
|
||||
|------|------|----------|-----------|
|
||||
| `AUTH_MISSING_KEY` | 401 | Auth | No |
|
||||
| `AUTH_INVALID_KEY` | 401 | Auth | No |
|
||||
| `AUTH_EXPIRED_KEY` | 401 | Auth | No |
|
||||
| `AUTH_INSUFFICIENT_SCOPE` | 403 | Auth | No |
|
||||
| `JOB_NOT_FOUND` | 404 | Job | No |
|
||||
| `JOB_ALREADY_CLAIMED` | 409 | Job | No |
|
||||
| `JOB_ALREADY_COMPLETED` | 409 | Job | No |
|
||||
| `JOB_ALREADY_CANCELLED` | 409 | Job | No |
|
||||
| `JOB_EXPIRED` | 410 | Job | No |
|
||||
| `VALIDATION_MISSING_FIELD` | 422 | Validation | No |
|
||||
| `VALIDATION_INVALID_TYPE` | 422 | Validation | No |
|
||||
| `VALIDATION_PROMPT_TOO_LONG` | 422 | Validation | No |
|
||||
| `VALIDATION_INVALID_MODEL` | 422 | Validation | No |
|
||||
| `MINER_NOT_FOUND` | 404 | Miner | No |
|
||||
| `MINER_OFFLINE` | 503 | Miner | Yes |
|
||||
| `RECEIPT_NOT_FOUND` | 404 | Receipt | No |
|
||||
| `RATE_LIMIT_EXCEEDED` | 429 | Rate | Yes |
|
||||
| `RATE_QUOTA_EXCEEDED` | 429 | Rate | No |
|
||||
| `PAYMENT_INSUFFICIENT_BALANCE` | 402 | Payment | No |
|
||||
| `SYSTEM_INTERNAL_ERROR` | 500 | System | Yes |
|
||||
| `SYSTEM_MAINTENANCE` | 503 | System | Yes |
|
||||
| `SYSTEM_OVERLOADED` | 503 | System | Yes |
|
||||
299
docs/reference/specs/protocol-messages.md
Normal file
299
docs/reference/specs/protocol-messages.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# Protocol Message Formats
|
||||
|
||||
This document defines the message formats used for communication between AITBC network components.
|
||||
|
||||
## Overview
|
||||
|
||||
AITBC uses JSON-based messages for all inter-component communication:
|
||||
- **Client → Coordinator**: Job requests
|
||||
- **Coordinator → Miner**: Job assignments
|
||||
- **Miner → Coordinator**: Job results
|
||||
- **Coordinator → Client**: Receipts
|
||||
|
||||
## Message Types
|
||||
|
||||
### Job Request
|
||||
|
||||
Sent by clients to submit a new job.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "job_request",
|
||||
"version": "1.0",
|
||||
"timestamp": "2026-01-24T15:00:00Z",
|
||||
"payload": {
|
||||
"prompt": "Explain quantum computing",
|
||||
"model": "llama3.2",
|
||||
"params": {
|
||||
"max_tokens": 256,
|
||||
"temperature": 0.7,
|
||||
"top_p": 0.9,
|
||||
"stream": false
|
||||
},
|
||||
"client_id": "ait1client...",
|
||||
"nonce": "abc123"
|
||||
},
|
||||
"signature": {
|
||||
"alg": "Ed25519",
|
||||
"key_id": "client-key-001",
|
||||
"sig": "base64..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `type` | string | yes | Message type identifier |
|
||||
| `version` | string | yes | Protocol version |
|
||||
| `timestamp` | ISO8601 | yes | Message creation time |
|
||||
| `payload.prompt` | string | yes | Input text |
|
||||
| `payload.model` | string | yes | Model identifier |
|
||||
| `payload.params` | object | no | Model parameters |
|
||||
| `payload.client_id` | string | yes | Client address |
|
||||
| `payload.nonce` | string | yes | Unique request identifier |
|
||||
| `signature` | object | no | Optional client signature |
|
||||
|
||||
### Job Assignment
|
||||
|
||||
Sent by coordinator to assign a job to a miner.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "job_assignment",
|
||||
"version": "1.0",
|
||||
"timestamp": "2026-01-24T15:00:01Z",
|
||||
"payload": {
|
||||
"job_id": "job-abc123",
|
||||
"prompt": "Explain quantum computing",
|
||||
"model": "llama3.2",
|
||||
"params": {
|
||||
"max_tokens": 256,
|
||||
"temperature": 0.7
|
||||
},
|
||||
"client_id": "ait1client...",
|
||||
"deadline": "2026-01-24T15:05:00Z",
|
||||
"reward": 5.0
|
||||
},
|
||||
"coordinator_id": "coord-eu-west-1"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `payload.job_id` | string | yes | Unique job identifier |
|
||||
| `payload.deadline` | ISO8601 | yes | Job must complete by this time |
|
||||
| `payload.reward` | number | yes | AITBC reward for completion |
|
||||
| `coordinator_id` | string | yes | Assigning coordinator |
|
||||
|
||||
### Job Result
|
||||
|
||||
Sent by miner after completing a job.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "job_result",
|
||||
"version": "1.0",
|
||||
"timestamp": "2026-01-24T15:00:05Z",
|
||||
"payload": {
|
||||
"job_id": "job-abc123",
|
||||
"miner_id": "ait1miner...",
|
||||
"result": "Quantum computing is a type of computation...",
|
||||
"result_hash": "sha256:abc123...",
|
||||
"metrics": {
|
||||
"tokens_generated": 150,
|
||||
"inference_time_ms": 2500,
|
||||
"gpu_memory_used_mb": 4096
|
||||
}
|
||||
},
|
||||
"signature": {
|
||||
"alg": "Ed25519",
|
||||
"key_id": "miner-key-001",
|
||||
"sig": "base64..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `payload.result` | string | yes | Generated output |
|
||||
| `payload.result_hash` | string | yes | SHA-256 hash of result |
|
||||
| `payload.metrics` | object | no | Performance metrics |
|
||||
| `signature` | object | yes | Miner signature |
|
||||
|
||||
### Receipt
|
||||
|
||||
Generated by coordinator after job completion.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "receipt",
|
||||
"version": "1.0",
|
||||
"timestamp": "2026-01-24T15:00:06Z",
|
||||
"payload": {
|
||||
"receipt_id": "rcpt-20260124-001234",
|
||||
"job_id": "job-abc123",
|
||||
"provider": "ait1miner...",
|
||||
"client": "ait1client...",
|
||||
"units": 2.5,
|
||||
"unit_type": "gpu_seconds",
|
||||
"price": 5.0,
|
||||
"model": "llama3.2",
|
||||
"started_at": 1737730801,
|
||||
"completed_at": 1737730805,
|
||||
"result_hash": "sha256:abc123..."
|
||||
},
|
||||
"signature": {
|
||||
"alg": "Ed25519",
|
||||
"key_id": "coord-key-001",
|
||||
"sig": "base64..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [Receipt Specification](receipt-spec.md) for full details.
|
||||
|
||||
### Miner Registration
|
||||
|
||||
Sent by miner to register with coordinator.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "miner_registration",
|
||||
"version": "1.0",
|
||||
"timestamp": "2026-01-24T14:00:00Z",
|
||||
"payload": {
|
||||
"miner_id": "ait1miner...",
|
||||
"capabilities": ["llama3.2", "llama3.2:1b", "codellama"],
|
||||
"gpu_info": {
|
||||
"name": "NVIDIA RTX 4090",
|
||||
"memory_gb": 24,
|
||||
"cuda_version": "12.1",
|
||||
"driver_version": "535.104.05"
|
||||
},
|
||||
"endpoint": "http://miner.example.com:8080",
|
||||
"max_concurrent_jobs": 4
|
||||
},
|
||||
"signature": {
|
||||
"alg": "Ed25519",
|
||||
"key_id": "miner-key-001",
|
||||
"sig": "base64..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Heartbeat
|
||||
|
||||
Sent periodically by miners to indicate availability.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "heartbeat",
|
||||
"version": "1.0",
|
||||
"timestamp": "2026-01-24T15:01:00Z",
|
||||
"payload": {
|
||||
"miner_id": "ait1miner...",
|
||||
"status": "available",
|
||||
"current_jobs": 1,
|
||||
"gpu_utilization": 45.5,
|
||||
"memory_used_gb": 8.2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| `available` | Ready to accept jobs |
|
||||
| `busy` | Processing at capacity |
|
||||
| `maintenance` | Temporarily unavailable |
|
||||
| `offline` | Shutting down |
|
||||
|
||||
### Error
|
||||
|
||||
Returned when an operation fails.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "error",
|
||||
"version": "1.0",
|
||||
"timestamp": "2026-01-24T15:00:02Z",
|
||||
"payload": {
|
||||
"error_code": "JOB_NOT_FOUND",
|
||||
"message": "Job with ID job-xyz does not exist",
|
||||
"details": {
|
||||
"job_id": "job-xyz"
|
||||
}
|
||||
},
|
||||
"request_id": "req-123"
|
||||
}
|
||||
```
|
||||
|
||||
## Message Validation
|
||||
|
||||
### Required Fields
|
||||
|
||||
All messages MUST include:
|
||||
- `type` - Message type identifier
|
||||
- `version` - Protocol version (currently "1.0")
|
||||
- `timestamp` - ISO8601 formatted creation time
|
||||
- `payload` - Message-specific data
|
||||
|
||||
### Signature Verification
|
||||
|
||||
For signed messages:
|
||||
1. Extract `payload` as canonical JSON (sorted keys, no whitespace)
|
||||
2. Compute SHA-256 hash of canonical payload
|
||||
3. Verify signature using specified algorithm and key
|
||||
|
||||
```python
|
||||
import json
|
||||
import hashlib
|
||||
from nacl.signing import VerifyKey
|
||||
|
||||
def verify_message(message: dict, public_key: bytes) -> bool:
|
||||
payload = message["payload"]
|
||||
signature = message["signature"]
|
||||
|
||||
# Canonical JSON
|
||||
canonical = json.dumps(payload, sort_keys=True, separators=(',', ':'))
|
||||
payload_hash = hashlib.sha256(canonical.encode()).digest()
|
||||
|
||||
# Verify
|
||||
verify_key = VerifyKey(public_key)
|
||||
try:
|
||||
verify_key.verify(payload_hash, bytes.fromhex(signature["sig"]))
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
```
|
||||
|
||||
### Timestamp Validation
|
||||
|
||||
- Messages with timestamps more than 5 minutes in the future SHOULD be rejected
|
||||
- Messages with timestamps more than 24 hours in the past MAY be rejected
|
||||
- Coordinators SHOULD track nonces to prevent replay attacks
|
||||
|
||||
## Transport
|
||||
|
||||
### HTTP/REST
|
||||
|
||||
Primary transport for client-coordinator communication:
|
||||
- Content-Type: `application/json`
|
||||
- UTF-8 encoding
|
||||
- HTTPS required in production
|
||||
|
||||
### WebSocket
|
||||
|
||||
For real-time miner-coordinator communication:
|
||||
- JSON messages over WebSocket frames
|
||||
- Ping/pong for connection health
|
||||
- Automatic reconnection on disconnect
|
||||
|
||||
## Versioning
|
||||
|
||||
Protocol version follows semantic versioning:
|
||||
- **Major**: Breaking changes
|
||||
- **Minor**: New features, backward compatible
|
||||
- **Patch**: Bug fixes
|
||||
|
||||
Clients SHOULD include supported versions in requests.
|
||||
Servers SHOULD respond with highest mutually supported version.
|
||||
159
docs/roadmap.md
159
docs/roadmap.md
@@ -464,63 +464,67 @@ Fill the intentional placeholder folders with actual content. Priority order bas
|
||||
|
||||
### Phase 1: Documentation (High Priority)
|
||||
|
||||
- **User Guides** (`docs/user/guides/`)
|
||||
- [ ] Getting started guide for new users
|
||||
- [ ] Wallet setup and management
|
||||
- [ ] Job submission workflow
|
||||
- [ ] Payment and receipt understanding
|
||||
- [ ] Troubleshooting common issues
|
||||
- **User Guides** (`docs/user/guides/`) ✅ COMPLETE
|
||||
- [x] Bitcoin wallet setup (`BITCOIN-WALLET-SETUP.md`)
|
||||
- [x] User interface guide (`USER-INTERFACE-GUIDE.md`)
|
||||
- [x] User management setup (`USER-MANAGEMENT-SETUP.md`)
|
||||
- [x] Local assets summary (`LOCAL_ASSETS_SUMMARY.md`)
|
||||
- [x] Getting started guide (`getting-started.md`)
|
||||
- [x] Job submission workflow (`job-submission.md`)
|
||||
- [x] Payment and receipt understanding (`payments-receipts.md`)
|
||||
- [x] Troubleshooting common issues (`troubleshooting.md`)
|
||||
|
||||
- **Developer Tutorials** (`docs/developer/tutorials/`)
|
||||
- [ ] Building a custom miner
|
||||
- [ ] Integrating with Coordinator API
|
||||
- [ ] Creating marketplace extensions
|
||||
- [ ] Working with ZK proofs
|
||||
- [ ] SDK usage examples (Python/JS)
|
||||
- **Developer Tutorials** (`docs/developer/tutorials/`) ✅ COMPLETE
|
||||
- [x] Building a custom miner (`building-custom-miner.md`)
|
||||
- [x] Integrating with Coordinator API (`coordinator-api-integration.md`)
|
||||
- [x] Creating marketplace extensions (`marketplace-extensions.md`)
|
||||
- [x] Working with ZK proofs (`zk-proofs.md`)
|
||||
- [x] SDK usage examples (`sdk-examples.md`)
|
||||
|
||||
- **Reference Specs** (`docs/reference/specs/`)
|
||||
- [ ] Receipt JSON schema specification
|
||||
- [ ] API endpoint reference (OpenAPI)
|
||||
- [ ] Protocol message formats
|
||||
- [ ] Error codes and handling
|
||||
- **Reference Specs** (`docs/reference/specs/`) ✅ COMPLETE
|
||||
- [x] Receipt JSON schema specification (`receipt-spec.md`)
|
||||
- [x] API endpoint reference (`api-reference.md`)
|
||||
- [x] Protocol message formats (`protocol-messages.md`)
|
||||
- [x] Error codes and handling (`error-codes.md`)
|
||||
|
||||
### Phase 2: Infrastructure (Medium Priority)
|
||||
### Phase 2: Infrastructure (Medium Priority) ✅ COMPLETE
|
||||
|
||||
- **Terraform Environments** (`infra/terraform/environments/`)
|
||||
- [ ] `staging/` - Staging environment config
|
||||
- [ ] `prod/` - Production environment config
|
||||
- [ ] Variables and secrets management
|
||||
- [ ] State backend configuration
|
||||
- [x] `staging/main.tf` - Staging environment config
|
||||
- [x] `prod/main.tf` - Production environment config
|
||||
- [x] `variables.tf` - Shared variables
|
||||
- [x] `secrets.tf` - Secrets management (AWS Secrets Manager)
|
||||
- [x] `backend.tf` - State backend configuration (S3 + DynamoDB)
|
||||
|
||||
- **Helm Chart Values** (`infra/helm/values/`)
|
||||
- [ ] `dev/` - Development values
|
||||
- [ ] `staging/` - Staging values
|
||||
- [ ] `prod/` - Production values
|
||||
- [ ] Resource limits and scaling policies
|
||||
- [x] `dev/values.yaml` - Development values
|
||||
- [x] `staging/values.yaml` - Staging values
|
||||
- [x] `prod/values.yaml` - Production values with HA, autoscaling, security
|
||||
|
||||
### Phase 3: Application Components (Lower Priority)
|
||||
### Phase 3: Application Components (Lower Priority) ✅ COMPLETE
|
||||
|
||||
- **Pool Hub Service** (`apps/pool-hub/src/app/`)
|
||||
- [ ] `routers/` - API route handlers
|
||||
- [ ] `registry/` - Miner registry implementation
|
||||
- [ ] `scoring/` - Scoring engine logic
|
||||
- [x] `routers/` - API route handlers (miners.py, pools.py, jobs.py, health.py)
|
||||
- [x] `registry/` - Miner registry implementation (miner_registry.py)
|
||||
- [x] `scoring/` - Scoring engine logic (scoring_engine.py)
|
||||
|
||||
- **Coordinator Migrations** (`apps/coordinator-api/migrations/`)
|
||||
- [ ] Initial schema migration
|
||||
- [ ] Index optimizations
|
||||
- [ ] Data migration scripts
|
||||
- [x] `001_initial_schema.sql` - Initial schema migration
|
||||
- [x] `002_indexes.sql` - Index optimizations
|
||||
- [x] `003_data_migration.py` - Data migration scripts
|
||||
- [x] `README.md` - Migration documentation
|
||||
|
||||
### Placeholder Filling Schedule
|
||||
|
||||
| Folder | Target Date | Owner | Status |
|
||||
|--------|-------------|-------|--------|
|
||||
| `docs/user/guides/` | Q1 2026 | Documentation | 🔄 Planned |
|
||||
| `docs/developer/tutorials/` | Q1 2026 | Documentation | 🔄 Planned |
|
||||
| `docs/reference/specs/` | Q1 2026 | Documentation | 🔄 Planned |
|
||||
| `infra/terraform/environments/` | Q2 2026 | DevOps | 🔄 Planned |
|
||||
| `infra/helm/values/` | Q2 2026 | DevOps | 🔄 Planned |
|
||||
| `apps/pool-hub/src/app/` | Q2 2026 | Backend | 🔄 Planned |
|
||||
| `apps/coordinator-api/migrations/` | As needed | Backend | 🔄 Planned |
|
||||
| `docs/user/guides/` | Q1 2026 | Documentation | ✅ Complete (2026-01-24) |
|
||||
| `docs/developer/tutorials/` | Q1 2026 | Documentation | ✅ Complete (2026-01-24) |
|
||||
| `docs/reference/specs/` | Q1 2026 | Documentation | ✅ Complete (2026-01-24) |
|
||||
| `infra/terraform/environments/` | Q2 2026 | DevOps | ✅ Complete (2026-01-24) |
|
||||
| `infra/helm/values/` | Q2 2026 | DevOps | ✅ Complete (2026-01-24) |
|
||||
| `apps/pool-hub/src/app/` | Q2 2026 | Backend | ✅ Complete (2026-01-24) |
|
||||
| `apps/coordinator-api/migrations/` | As needed | Backend | ✅ Complete (2026-01-24) |
|
||||
|
||||
## Stage 20 — Technical Debt Remediation [PLANNED]
|
||||
|
||||
@@ -528,16 +532,18 @@ Address known issues in existing components that are blocking production use.
|
||||
|
||||
### Blockchain Node (`apps/blockchain-node/`)
|
||||
|
||||
Current Status: Has 9 Python files but SQLModel/SQLAlchemy compatibility issues.
|
||||
Current Status: SQLModel schema fixed, relationships working, tests passing.
|
||||
|
||||
- **SQLModel Compatibility**
|
||||
- [ ] Audit current SQLModel schema definitions in `models.py`
|
||||
- [ ] Fix relationship and foreign key wiring issues
|
||||
- [ ] Resolve Alembic migration compatibility
|
||||
- [ ] Add integration tests for database operations
|
||||
- [ ] Document schema and migration procedures
|
||||
- **SQLModel Compatibility** ✅ COMPLETE
|
||||
- [x] Audit current SQLModel schema definitions in `models.py`
|
||||
- [x] Fix relationship and foreign key wiring issues
|
||||
- [x] Add explicit `__tablename__` to all models
|
||||
- [x] Add `sa_relationship_kwargs` for lazy loading
|
||||
- [x] Document SQLModel validator limitation (table=True bypasses validators)
|
||||
- [x] Integration tests passing (2 passed, 1 skipped)
|
||||
- [x] Schema documentation (`docs/SCHEMA.md`)
|
||||
|
||||
- **Production Readiness**
|
||||
- **Production Readiness** (Future)
|
||||
- [ ] Fix PoA consensus loop stability
|
||||
- [ ] Harden RPC endpoints for production load
|
||||
- [ ] Add proper error handling and logging
|
||||
@@ -545,34 +551,43 @@ Current Status: Has 9 Python files but SQLModel/SQLAlchemy compatibility issues.
|
||||
|
||||
### Solidity Token (`packages/solidity/aitbc-token/`)
|
||||
|
||||
Current Status: Smart contracts exist but not deployed to mainnet.
|
||||
Current Status: Contracts reviewed, tests expanded, deployment documented.
|
||||
|
||||
- **Contract Audit**
|
||||
- [ ] Review AIToken.sol and AITokenRegistry.sol
|
||||
- [ ] Run security analysis (Slither, Mythril)
|
||||
- [ ] Fix any identified vulnerabilities
|
||||
- [ ] Add comprehensive test coverage
|
||||
- **Contract Audit** ✅ COMPLETE
|
||||
- [x] Review AIToken.sol and AITokenRegistry.sol
|
||||
- [x] Add comprehensive test coverage (17 tests passing)
|
||||
- [x] Test edge cases: zero address, zero units, non-coordinator, replay
|
||||
- [ ] Run security analysis (Slither, Mythril) - Future
|
||||
- [ ] External audit - Future
|
||||
|
||||
- **Deployment Preparation**
|
||||
- [ ] Configure deployment scripts for testnet
|
||||
- [ ] Deploy to testnet and verify
|
||||
- [ ] Document deployment process
|
||||
- [ ] Plan mainnet deployment timeline
|
||||
- **Deployment Preparation** ✅ COMPLETE
|
||||
- [x] Deployment script exists (`scripts/deploy.ts`)
|
||||
- [x] Mint script exists (`scripts/mintWithReceipt.ts`)
|
||||
- [x] Deployment documentation (`docs/DEPLOYMENT.md`)
|
||||
- [ ] Deploy to testnet and verify - Future
|
||||
- [ ] Plan mainnet deployment timeline - Future
|
||||
|
||||
### ZK Receipt Verifier (`contracts/ZKReceiptVerifier.sol`)
|
||||
|
||||
Current Status: 240-line Groth16 verifier contract ready for deployment.
|
||||
Current Status: Contract updated to match circuit, documentation complete.
|
||||
|
||||
- **Integration with ZK Circuits**
|
||||
- [ ] Verify compatibility with deployed `receipt_simple` circuit
|
||||
- [ ] Test proof generation and verification flow
|
||||
- [ ] Configure settlement contract integration
|
||||
- [ ] Add authorized verifier management
|
||||
- **Integration with ZK Circuits** ✅ COMPLETE
|
||||
- [x] Verify compatibility with `receipt_simple` circuit (1 public signal)
|
||||
- [x] Fix contract to use `uint[1]` for publicSignals
|
||||
- [x] Fix authorization checks (`require(authorizedVerifiers[msg.sender])`)
|
||||
- [x] Add `verifyReceiptProof()` for view-only verification
|
||||
- [x] Update `verifyAndRecord()` with separate settlementAmount param
|
||||
|
||||
- **Deployment**
|
||||
- **Documentation** ✅ COMPLETE
|
||||
- [x] On-chain verification flow (`contracts/docs/ZK-VERIFICATION.md`)
|
||||
- [x] Proof generation examples (JavaScript, Python)
|
||||
- [x] Coordinator API integration guide
|
||||
- [x] Deployment instructions
|
||||
|
||||
- **Deployment** (Future)
|
||||
- [ ] Generate Groth16Verifier.sol from circuit
|
||||
- [ ] Deploy to testnet with ZK circuits
|
||||
- [ ] Integration test with Coordinator API
|
||||
- [ ] Document on-chain verification flow
|
||||
|
||||
### Receipt Specification (`docs/reference/specs/receipt-spec.md`)
|
||||
|
||||
@@ -590,11 +605,11 @@ Current Status: Canonical receipt schema specification moved from `protocols/rec
|
||||
|
||||
| Component | Priority | Target | Status |
|
||||
|-----------|----------|--------|--------|
|
||||
| `apps/blockchain-node/` SQLModel fixes | Medium | Q2 2026 | 🔄 Planned |
|
||||
| `packages/solidity/aitbc-token/` audit | Low | Q3 2026 | 🔄 Planned |
|
||||
| `packages/solidity/aitbc-token/` testnet | Low | Q3 2026 | 🔄 Planned |
|
||||
| `contracts/ZKReceiptVerifier.sol` deploy | Low | Q3 2026 | 🔄 Planned |
|
||||
| `docs/reference/specs/receipt-spec.md` finalize | Low | Q2 2026 | 🔄 Planned |
|
||||
| `apps/blockchain-node/` SQLModel fixes | Medium | Q2 2026 | ✅ Complete (2026-01-24) |
|
||||
| `packages/solidity/aitbc-token/` audit | Low | Q3 2026 | ✅ Complete (2026-01-24) |
|
||||
| `packages/solidity/aitbc-token/` testnet | Low | Q3 2026 | 🔄 Pending deployment |
|
||||
| `contracts/ZKReceiptVerifier.sol` deploy | Low | Q3 2026 | ✅ Code ready (2026-01-24) |
|
||||
| `docs/reference/specs/receipt-spec.md` finalize | Low | Q2 2026 | 🔄 Pending extensions |
|
||||
|
||||
the canonical checklist during implementation. Mark completed tasks with ✅ and add dates or links to relevant PRs as development progresses.
|
||||
|
||||
|
||||
89
docs/user/guides/getting-started.md
Normal file
89
docs/user/guides/getting-started.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Getting Started with AITBC
|
||||
|
||||
Welcome to the AI Token Blockchain (AITBC) network! This guide will help you get started as a user of the decentralized AI compute marketplace.
|
||||
|
||||
## What is AITBC?
|
||||
|
||||
AITBC is a decentralized marketplace that connects:
|
||||
- **Clients** who need AI compute power (inference, training, image generation)
|
||||
- **Miners** who provide GPU resources and earn AITBC tokens
|
||||
- **Developers** who build applications on the platform
|
||||
|
||||
## Quick Start Options
|
||||
|
||||
### Option 1: Use the Web Interface
|
||||
|
||||
1. Visit [https://aitbc.bubuit.net](https://aitbc.bubuit.net)
|
||||
2. Navigate to the **Marketplace** to browse available AI services
|
||||
3. Connect your wallet or create an account
|
||||
4. Submit your first AI job
|
||||
|
||||
### Option 2: Use the CLI
|
||||
|
||||
```bash
|
||||
# Install the CLI wrapper
|
||||
curl -O https://aitbc.bubuit.net/cli/aitbc-cli.sh
|
||||
chmod +x aitbc-cli.sh
|
||||
|
||||
# Check available services
|
||||
./aitbc-cli.sh status
|
||||
|
||||
# Submit a job
|
||||
./aitbc-cli.sh submit "Your prompt here" --model llama3.2
|
||||
```
|
||||
|
||||
### Option 3: Use the SDK
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
from aitbc_sdk import AITBCClient
|
||||
|
||||
client = AITBCClient(api_url="https://aitbc.bubuit.net/api")
|
||||
result = client.submit_job(
|
||||
prompt="Explain quantum computing",
|
||||
model="llama3.2"
|
||||
)
|
||||
print(result.output)
|
||||
```
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Jobs
|
||||
A job is a unit of work submitted to the network. It includes:
|
||||
- **Prompt**: Your input (text, image, etc.)
|
||||
- **Model**: The AI model to use (e.g., `llama3.2`, `stable-diffusion`)
|
||||
- **Parameters**: Optional settings (temperature, max tokens, etc.)
|
||||
|
||||
### Receipts
|
||||
After a job completes, you receive a **receipt** containing:
|
||||
- Job ID and status
|
||||
- Compute units consumed
|
||||
- Miner who processed the job
|
||||
- Cryptographic proof of completion
|
||||
|
||||
### Tokens
|
||||
AITBC tokens are used to:
|
||||
- Pay for compute jobs
|
||||
- Reward miners for providing resources
|
||||
- Participate in governance
|
||||
|
||||
## Your First Job
|
||||
|
||||
1. **Connect your wallet** at the Exchange or create an account
|
||||
2. **Get some AITBC tokens** (see [Bitcoin Wallet Setup](BITCOIN-WALLET-SETUP.md))
|
||||
3. **Submit a job** via web, CLI, or SDK
|
||||
4. **Wait for completion** (typically seconds to minutes)
|
||||
5. **View your receipt** in the Explorer
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Job Submission Workflow](job-submission.md) - Detailed guide on submitting jobs
|
||||
- [Payments and Receipts](payments-receipts.md) - Understanding the payment flow
|
||||
- [Troubleshooting](troubleshooting.md) - Common issues and solutions
|
||||
- [User Interface Guide](USER-INTERFACE-GUIDE.md) - Navigating the web interface
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **Documentation**: [https://aitbc.bubuit.net/docs/](https://aitbc.bubuit.net/docs/)
|
||||
- **Explorer**: [https://aitbc.bubuit.net/explorer/](https://aitbc.bubuit.net/explorer/)
|
||||
- **API Reference**: [https://aitbc.bubuit.net/api/docs](https://aitbc.bubuit.net/api/docs)
|
||||
163
docs/user/guides/job-submission.md
Normal file
163
docs/user/guides/job-submission.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Job Submission Workflow
|
||||
|
||||
This guide explains how to submit AI compute jobs to the AITBC network and track their progress.
|
||||
|
||||
## Overview
|
||||
|
||||
The job submission workflow:
|
||||
|
||||
1. **Prepare** - Choose model and parameters
|
||||
2. **Submit** - Send job to Coordinator API
|
||||
3. **Queue** - Job enters the processing queue
|
||||
4. **Execute** - Miner processes your job
|
||||
5. **Complete** - Receive results and receipt
|
||||
|
||||
## Submission Methods
|
||||
|
||||
### Web Interface
|
||||
|
||||
1. Go to [Marketplace](https://aitbc.bubuit.net/marketplace/)
|
||||
2. Select a service (e.g., "Text Generation", "Image Generation")
|
||||
3. Enter your prompt and configure options
|
||||
4. Click **Submit Job**
|
||||
5. View job status in your dashboard
|
||||
|
||||
### CLI
|
||||
|
||||
```bash
|
||||
# Basic submission
|
||||
./aitbc-cli.sh submit "Explain machine learning in simple terms"
|
||||
|
||||
# With model selection
|
||||
./aitbc-cli.sh submit "Generate a haiku about coding" --model llama3.2
|
||||
|
||||
# With parameters
|
||||
./aitbc-cli.sh submit "Write a story" --model llama3.2 --max-tokens 500 --temperature 0.7
|
||||
|
||||
# Check job status
|
||||
./aitbc-cli.sh status <job_id>
|
||||
|
||||
# List your jobs
|
||||
./aitbc-cli.sh jobs
|
||||
```
|
||||
|
||||
### Python SDK
|
||||
|
||||
```python
|
||||
from aitbc_sdk import AITBCClient
|
||||
|
||||
client = AITBCClient(
|
||||
api_url="https://aitbc.bubuit.net/api",
|
||||
api_key="your-api-key" # Optional for authenticated requests
|
||||
)
|
||||
|
||||
# Submit a text generation job
|
||||
job = client.submit_job(
|
||||
prompt="What is the capital of France?",
|
||||
model="llama3.2",
|
||||
params={
|
||||
"max_tokens": 100,
|
||||
"temperature": 0.5
|
||||
}
|
||||
)
|
||||
|
||||
print(f"Job ID: {job.id}")
|
||||
print(f"Status: {job.status}")
|
||||
|
||||
# Wait for completion
|
||||
result = client.wait_for_job(job.id, timeout=60)
|
||||
print(f"Output: {result.output}")
|
||||
```
|
||||
|
||||
### Direct API
|
||||
|
||||
```bash
|
||||
# Submit job
|
||||
curl -X POST https://aitbc.bubuit.net/api/v1/jobs \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"prompt": "Hello, world!",
|
||||
"model": "llama3.2",
|
||||
"params": {"max_tokens": 50}
|
||||
}'
|
||||
|
||||
# Check status
|
||||
curl https://aitbc.bubuit.net/api/v1/jobs/<job_id>
|
||||
```
|
||||
|
||||
## Job Parameters
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `prompt` | string | required | Input text or instruction |
|
||||
| `model` | string | `llama3.2` | AI model to use |
|
||||
| `max_tokens` | int | 256 | Maximum output tokens |
|
||||
| `temperature` | float | 0.7 | Creativity (0.0-1.0) |
|
||||
| `top_p` | float | 0.9 | Nucleus sampling |
|
||||
| `stream` | bool | false | Stream output chunks |
|
||||
|
||||
## Available Models
|
||||
|
||||
| Model | Type | Use Case |
|
||||
|-------|------|----------|
|
||||
| `llama3.2` | Text | General chat, Q&A, writing |
|
||||
| `llama3.2:1b` | Text | Fast, lightweight tasks |
|
||||
| `codellama` | Code | Code generation, debugging |
|
||||
| `stable-diffusion` | Image | Image generation |
|
||||
|
||||
## Job States
|
||||
|
||||
| State | Description |
|
||||
|-------|-------------|
|
||||
| `pending` | Job submitted, waiting for miner |
|
||||
| `running` | Miner is processing the job |
|
||||
| `completed` | Job finished successfully |
|
||||
| `failed` | Job failed (see error message) |
|
||||
| `cancelled` | Job was cancelled by user |
|
||||
|
||||
## Tracking Your Jobs
|
||||
|
||||
### View in Explorer
|
||||
|
||||
Visit [Explorer](https://aitbc.bubuit.net/explorer/) to see:
|
||||
- Recent jobs and their status
|
||||
- Your job history (if authenticated)
|
||||
- Receipt details and proofs
|
||||
|
||||
### Programmatic Tracking
|
||||
|
||||
```python
|
||||
# Poll for status
|
||||
import time
|
||||
|
||||
while True:
|
||||
job = client.get_job(job_id)
|
||||
print(f"Status: {job.status}")
|
||||
|
||||
if job.status in ["completed", "failed", "cancelled"]:
|
||||
break
|
||||
|
||||
time.sleep(2)
|
||||
```
|
||||
|
||||
## Cancelling Jobs
|
||||
|
||||
```bash
|
||||
# CLI
|
||||
./aitbc-cli.sh cancel <job_id>
|
||||
|
||||
# API
|
||||
curl -X POST https://aitbc.bubuit.net/api/v1/jobs/<job_id>/cancel
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Be specific** - Clear prompts get better results
|
||||
2. **Set appropriate limits** - Use `max_tokens` to control costs
|
||||
3. **Handle errors** - Always check job status before using output
|
||||
4. **Use streaming** - For long outputs, enable streaming for faster feedback
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Payments and Receipts](payments-receipts.md) - Understanding costs and proofs
|
||||
- [Troubleshooting](troubleshooting.md) - Common issues and solutions
|
||||
156
docs/user/guides/payments-receipts.md
Normal file
156
docs/user/guides/payments-receipts.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Payments and Receipts
|
||||
|
||||
This guide explains how payments work on the AITBC network and how to understand your receipts.
|
||||
|
||||
## Payment Flow
|
||||
|
||||
```
|
||||
Client submits job → Job processed by miner → Receipt generated → Payment settled
|
||||
```
|
||||
|
||||
### Step-by-Step
|
||||
|
||||
1. **Job Submission**: You submit a job with your prompt and parameters
|
||||
2. **Miner Selection**: The Coordinator assigns your job to an available miner
|
||||
3. **Processing**: The miner executes your job using their GPU
|
||||
4. **Receipt Creation**: A cryptographic receipt is generated proving work completion
|
||||
5. **Settlement**: AITBC tokens are transferred from client to miner
|
||||
|
||||
## Understanding Receipts
|
||||
|
||||
Every completed job generates a receipt containing:
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `receipt_id` | Unique identifier for this receipt |
|
||||
| `job_id` | The job this receipt is for |
|
||||
| `provider` | Miner address who processed the job |
|
||||
| `client` | Your address (who requested the job) |
|
||||
| `units` | Compute units consumed (e.g., GPU seconds) |
|
||||
| `price` | Amount paid in AITBC tokens |
|
||||
| `model` | AI model used |
|
||||
| `started_at` | When processing began |
|
||||
| `completed_at` | When processing finished |
|
||||
| `signature` | Cryptographic proof of authenticity |
|
||||
|
||||
### Example Receipt
|
||||
|
||||
```json
|
||||
{
|
||||
"receipt_id": "rcpt-20260124-001234",
|
||||
"job_id": "job-abc123",
|
||||
"provider": "ait1miner...",
|
||||
"client": "ait1client...",
|
||||
"units": 2.5,
|
||||
"unit_type": "gpu_seconds",
|
||||
"price": 5.0,
|
||||
"model": "llama3.2",
|
||||
"started_at": 1737730800,
|
||||
"completed_at": 1737730803,
|
||||
"signature": {
|
||||
"alg": "Ed25519",
|
||||
"key_id": "miner-ed25519-2026-01",
|
||||
"sig": "Fql0..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Viewing Your Receipts
|
||||
|
||||
### Explorer
|
||||
|
||||
Visit [Explorer → Receipts](https://aitbc.bubuit.net/explorer/#/receipts) to see:
|
||||
- All recent receipts on the network
|
||||
- Filter by your address to see your history
|
||||
- Click any receipt for full details
|
||||
|
||||
### CLI
|
||||
|
||||
```bash
|
||||
# List your receipts
|
||||
./aitbc-cli.sh receipts
|
||||
|
||||
# Get specific receipt
|
||||
./aitbc-cli.sh receipt <receipt_id>
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
```bash
|
||||
curl https://aitbc.bubuit.net/api/v1/receipts?client=<your_address>
|
||||
```
|
||||
|
||||
## Pricing
|
||||
|
||||
### How Pricing Works
|
||||
|
||||
- Jobs are priced in **compute units** (typically GPU seconds)
|
||||
- Each model has a base rate per compute unit
|
||||
- Final price = `units × rate`
|
||||
|
||||
### Current Rates
|
||||
|
||||
| Model | Rate (AITBC/unit) | Typical Job Cost |
|
||||
|-------|-------------------|------------------|
|
||||
| `llama3.2` | 2.0 | 2-10 AITBC |
|
||||
| `llama3.2:1b` | 0.5 | 0.5-2 AITBC |
|
||||
| `codellama` | 2.5 | 3-15 AITBC |
|
||||
| `stable-diffusion` | 5.0 | 10-50 AITBC |
|
||||
|
||||
*Rates may vary based on network demand and miner availability.*
|
||||
|
||||
## Getting AITBC Tokens
|
||||
|
||||
### Via Exchange
|
||||
|
||||
1. Visit [Trade Exchange](https://aitbc.bubuit.net/Exchange/)
|
||||
2. Create an account or connect wallet
|
||||
3. Send Bitcoin to your deposit address
|
||||
4. Receive AITBC at current exchange rate (1 BTC = 100,000 AITBC)
|
||||
|
||||
See [Bitcoin Wallet Setup](BITCOIN-WALLET-SETUP.md) for detailed instructions.
|
||||
|
||||
### Via Mining
|
||||
|
||||
Earn AITBC by providing GPU compute:
|
||||
- See [Miner Documentation](../../reference/components/miner_node.md)
|
||||
|
||||
## Verifying Receipts
|
||||
|
||||
Receipts are cryptographically signed to ensure authenticity.
|
||||
|
||||
### Signature Verification
|
||||
|
||||
```python
|
||||
from aitbc_crypto import verify_receipt
|
||||
|
||||
receipt = get_receipt("rcpt-20260124-001234")
|
||||
is_valid = verify_receipt(receipt)
|
||||
print(f"Receipt valid: {is_valid}")
|
||||
```
|
||||
|
||||
### On-Chain Verification
|
||||
|
||||
Receipts can be anchored on-chain for permanent proof:
|
||||
- ZK proofs enable privacy-preserving verification
|
||||
- See [ZK Applications](../../reference/components/zk-applications.md)
|
||||
|
||||
## Payment Disputes
|
||||
|
||||
If you believe a payment was incorrect:
|
||||
|
||||
1. **Check the receipt** - Verify units and price match expectations
|
||||
2. **Compare to job output** - Ensure you received the expected result
|
||||
3. **Contact support** - If discrepancy exists, report via the platform
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Monitor your balance** - Check before submitting large jobs
|
||||
2. **Set spending limits** - Use API keys with rate limits
|
||||
3. **Keep receipts** - Download important receipts for records
|
||||
4. **Verify signatures** - For high-value transactions, verify cryptographically
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Troubleshooting](troubleshooting.md) - Common payment issues
|
||||
- [Getting Started](getting-started.md) - Back to basics
|
||||
208
docs/user/guides/troubleshooting.md
Normal file
208
docs/user/guides/troubleshooting.md
Normal file
@@ -0,0 +1,208 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
Common issues and solutions when using the AITBC network.
|
||||
|
||||
## Job Issues
|
||||
|
||||
### Job Stuck in "Pending" State
|
||||
|
||||
**Symptoms**: Job submitted but stays in `pending` for a long time.
|
||||
|
||||
**Causes**:
|
||||
- No miners currently available
|
||||
- Network congestion
|
||||
- Model not supported by available miners
|
||||
|
||||
**Solutions**:
|
||||
1. Wait a few minutes - miners may become available
|
||||
2. Check network status at [Explorer](https://aitbc.bubuit.net/explorer/)
|
||||
3. Try a different model (e.g., `llama3.2:1b` instead of `llama3.2`)
|
||||
4. Cancel and resubmit during off-peak hours
|
||||
|
||||
```bash
|
||||
# Check job status
|
||||
./aitbc-cli.sh status <job_id>
|
||||
|
||||
# Cancel if needed
|
||||
./aitbc-cli.sh cancel <job_id>
|
||||
```
|
||||
|
||||
### Job Failed
|
||||
|
||||
**Symptoms**: Job status shows `failed` with an error message.
|
||||
|
||||
**Common Errors**:
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| `Model not found` | Invalid model name | Check available models |
|
||||
| `Prompt too long` | Input exceeds limit | Shorten your prompt |
|
||||
| `Timeout` | Job took too long | Reduce `max_tokens` or simplify prompt |
|
||||
| `Miner disconnected` | Miner went offline | Resubmit job |
|
||||
| `Insufficient balance` | Not enough AITBC | Top up your balance |
|
||||
|
||||
### Unexpected Output
|
||||
|
||||
**Symptoms**: Job completed but output is wrong or truncated.
|
||||
|
||||
**Solutions**:
|
||||
1. **Truncated output**: Increase `max_tokens` parameter
|
||||
2. **Wrong format**: Be more specific in your prompt
|
||||
3. **Gibberish**: Lower `temperature` (try 0.3-0.5)
|
||||
4. **Off-topic**: Rephrase prompt to be clearer
|
||||
|
||||
## Connection Issues
|
||||
|
||||
### Cannot Connect to API
|
||||
|
||||
**Symptoms**: `Connection refused` or `timeout` errors.
|
||||
|
||||
**Solutions**:
|
||||
1. Check your internet connection
|
||||
2. Verify API URL: `https://aitbc.bubuit.net/api`
|
||||
3. Check if service is up at [Explorer](https://aitbc.bubuit.net/explorer/)
|
||||
4. Try again in a few minutes
|
||||
|
||||
```bash
|
||||
# Test connectivity
|
||||
curl -I https://aitbc.bubuit.net/api/health
|
||||
```
|
||||
|
||||
### Authentication Failed
|
||||
|
||||
**Symptoms**: `401 Unauthorized` or `Invalid API key` errors.
|
||||
|
||||
**Solutions**:
|
||||
1. Verify your API key is correct
|
||||
2. Check if API key has expired
|
||||
3. Ensure API key has required permissions
|
||||
4. Generate a new API key if needed
|
||||
|
||||
## Wallet Issues
|
||||
|
||||
### Cannot Connect Wallet
|
||||
|
||||
**Symptoms**: Wallet connection fails or times out.
|
||||
|
||||
**Solutions**:
|
||||
1. Ensure browser extension is installed and unlocked
|
||||
2. Refresh the page and try again
|
||||
3. Check if wallet is on correct network
|
||||
4. Clear browser cache and cookies
|
||||
|
||||
### Transaction Not Showing
|
||||
|
||||
**Symptoms**: Sent tokens but balance not updated.
|
||||
|
||||
**Solutions**:
|
||||
1. Wait for confirmation (may take a few minutes)
|
||||
2. Check transaction in Explorer
|
||||
3. Verify you sent to correct address
|
||||
4. Contact support if still missing after 1 hour
|
||||
|
||||
### Insufficient Balance
|
||||
|
||||
**Symptoms**: `Insufficient balance` error when submitting job.
|
||||
|
||||
**Solutions**:
|
||||
1. Check your current balance
|
||||
2. Top up via [Exchange](https://aitbc.bubuit.net/Exchange/)
|
||||
3. Wait for pending deposits to confirm
|
||||
|
||||
## CLI Issues
|
||||
|
||||
### Command Not Found
|
||||
|
||||
**Symptoms**: `aitbc-cli.sh: command not found`
|
||||
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Make script executable
|
||||
chmod +x aitbc-cli.sh
|
||||
|
||||
# Run with explicit path
|
||||
./aitbc-cli.sh status
|
||||
|
||||
# Or add to PATH
|
||||
export PATH=$PATH:$(pwd)
|
||||
```
|
||||
|
||||
### Permission Denied
|
||||
|
||||
**Symptoms**: `Permission denied` when running CLI.
|
||||
|
||||
**Solutions**:
|
||||
```bash
|
||||
chmod +x aitbc-cli.sh
|
||||
```
|
||||
|
||||
### SSL Certificate Error
|
||||
|
||||
**Symptoms**: `SSL certificate problem` or `certificate verify failed`
|
||||
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Update CA certificates
|
||||
sudo apt update && sudo apt install ca-certificates
|
||||
|
||||
# Or skip verification (not recommended for production)
|
||||
curl -k https://aitbc.bubuit.net/api/health
|
||||
```
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow Response Times
|
||||
|
||||
**Symptoms**: Jobs take longer than expected.
|
||||
|
||||
**Causes**:
|
||||
- Large prompt or output
|
||||
- Complex model
|
||||
- Network congestion
|
||||
- Miner hardware limitations
|
||||
|
||||
**Solutions**:
|
||||
1. Use smaller models for simple tasks
|
||||
2. Reduce `max_tokens` if full output not needed
|
||||
3. Submit during off-peak hours
|
||||
4. Use streaming for faster first-token response
|
||||
|
||||
### Rate Limited
|
||||
|
||||
**Symptoms**: `429 Too Many Requests` error.
|
||||
|
||||
**Solutions**:
|
||||
1. Wait before retrying (check `Retry-After` header)
|
||||
2. Reduce request frequency
|
||||
3. Use exponential backoff in your code
|
||||
4. Request higher rate limits if needed
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Self-Service Resources
|
||||
|
||||
- **Documentation**: [https://aitbc.bubuit.net/docs/](https://aitbc.bubuit.net/docs/)
|
||||
- **API Reference**: [https://aitbc.bubuit.net/api/docs](https://aitbc.bubuit.net/api/docs)
|
||||
- **Explorer**: [https://aitbc.bubuit.net/explorer/](https://aitbc.bubuit.net/explorer/)
|
||||
|
||||
### Reporting Issues
|
||||
|
||||
When reporting an issue, include:
|
||||
1. **Job ID** (if applicable)
|
||||
2. **Error message** (exact text)
|
||||
3. **Steps to reproduce**
|
||||
4. **Expected vs actual behavior**
|
||||
5. **Timestamp** of when issue occurred
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable verbose logging for troubleshooting:
|
||||
|
||||
```bash
|
||||
# CLI debug mode
|
||||
DEBUG=1 ./aitbc-cli.sh submit "test"
|
||||
|
||||
# Python SDK
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
```
|
||||
Reference in New Issue
Block a user