Files
aitbc/apps/coordinator-api/src/app/schemas.py
oib 5120861e17 feat: add GPU-specific fields to marketplace offers and create dedicated GPU marketplace router
- Add GPU fields (model, memory, count, CUDA version, price, region) to MarketplaceOffer model
- Create new marketplace_gpu router for GPU-specific operations
- Update offer sync to populate GPU fields from miner capabilities
- Move GPU attributes from generic attributes dict to dedicated fields
- Update MarketplaceOfferView schema with GPU fields
- Expand CLI README with comprehensive documentation and
2026-02-12 19:08:17 +01:00

467 lines
11 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, Optional, List
from base64 import b64encode, b64decode
from enum import Enum
from pydantic import BaseModel, Field, ConfigDict
from .types import JobState, Constraints
# Payment schemas
class JobPaymentCreate(BaseModel):
"""Request to create a payment for a job"""
job_id: str
amount: float
currency: str = "AITBC" # Jobs paid with AITBC tokens
payment_method: str = "aitbc_token" # Primary method for job payments
escrow_timeout_seconds: int = 3600 # 1 hour default
class JobPaymentView(BaseModel):
"""Payment information for a job"""
job_id: str
payment_id: str
amount: float
currency: str
status: str
payment_method: str
escrow_address: Optional[str] = None
refund_address: Optional[str] = None
created_at: datetime
updated_at: datetime
released_at: Optional[datetime] = None
refunded_at: Optional[datetime] = None
transaction_hash: Optional[str] = None
refund_transaction_hash: Optional[str] = None
class PaymentRequest(BaseModel):
"""Request to pay for a job"""
job_id: str
amount: float
currency: str = "BTC"
refund_address: Optional[str] = None
class PaymentReceipt(BaseModel):
"""Receipt for a payment"""
payment_id: str
job_id: str
amount: float
currency: str
status: str
transaction_hash: Optional[str] = None
created_at: datetime
verified_at: Optional[datetime] = None
class EscrowRelease(BaseModel):
"""Request to release escrow payment"""
job_id: str
payment_id: str
reason: Optional[str] = None
class RefundRequest(BaseModel):
"""Request to refund a payment"""
job_id: str
payment_id: str
reason: str
# User management schemas
class UserCreate(BaseModel):
email: str
username: str
password: Optional[str] = None
class UserLogin(BaseModel):
wallet_address: str
signature: Optional[str] = None
class UserProfile(BaseModel):
user_id: str
email: str
username: str
created_at: str
session_token: Optional[str] = None
class UserBalance(BaseModel):
user_id: str
address: str
balance: float
updated_at: Optional[str] = None
class Transaction(BaseModel):
id: str
type: str
status: str
amount: float
fee: float
description: Optional[str]
created_at: str
confirmed_at: Optional[str] = None
class TransactionHistory(BaseModel):
user_id: str
transactions: List[Transaction]
total: int
class ExchangePaymentRequest(BaseModel):
user_id: str
aitbc_amount: float
btc_amount: float
class ExchangePaymentResponse(BaseModel):
payment_id: str
user_id: str
aitbc_amount: float
btc_amount: float
payment_address: str
status: str
created_at: int
expires_at: int
class JobCreate(BaseModel):
payload: Dict[str, Any]
constraints: Constraints = Field(default_factory=Constraints)
ttl_seconds: int = 900
payment_amount: Optional[float] = None # Amount to pay for the job
payment_currency: str = "AITBC" # Jobs paid with AITBC tokens
class JobView(BaseModel):
job_id: str
state: JobState
assigned_miner_id: Optional[str] = None
requested_at: datetime
expires_at: datetime
error: Optional[str] = None
payment_id: Optional[str] = None
payment_status: Optional[str] = None
class JobResult(BaseModel):
result: Optional[Dict[str, Any]] = None
receipt: Optional[Dict[str, Any]] = None
class MinerRegister(BaseModel):
capabilities: Dict[str, Any]
concurrency: int = 1
region: Optional[str] = None
class MinerHeartbeat(BaseModel):
inflight: int = 0
status: str = "ONLINE"
metadata: Dict[str, Any] = Field(default_factory=dict)
class PollRequest(BaseModel):
max_wait_seconds: int = 15
class AssignedJob(BaseModel):
job_id: str
payload: Dict[str, Any]
constraints: Constraints
class JobResultSubmit(BaseModel):
result: Dict[str, Any]
metrics: Dict[str, Any] = Field(default_factory=dict)
class JobFailSubmit(BaseModel):
error_code: str
error_message: str
metrics: Dict[str, Any] = Field(default_factory=dict)
class MarketplaceOfferView(BaseModel):
id: str
provider: str
capacity: int
price: float
sla: str
status: str
created_at: datetime
gpu_model: Optional[str] = None
gpu_memory_gb: Optional[int] = None
gpu_count: Optional[int] = 1
cuda_version: Optional[str] = None
price_per_hour: Optional[float] = None
region: Optional[str] = None
class MarketplaceStatsView(BaseModel):
totalOffers: int
openCapacity: int
averagePrice: float
activeBids: int
class MarketplaceBidRequest(BaseModel):
provider: str = Field(..., min_length=1)
capacity: int = Field(..., gt=0)
price: float = Field(..., gt=0)
notes: Optional[str] = Field(default=None, max_length=1024)
class BlockSummary(BaseModel):
model_config = ConfigDict(populate_by_name=True)
height: int
hash: str
timestamp: datetime
txCount: int
proposer: str
class BlockListResponse(BaseModel):
model_config = ConfigDict(populate_by_name=True)
items: list[BlockSummary]
next_offset: Optional[str | int] = None
class TransactionSummary(BaseModel):
model_config = ConfigDict(populate_by_name=True, ser_json_tuples=True)
hash: str
block: str | int
from_address: str = Field(alias="from")
to_address: Optional[str] = Field(default=None, alias="to")
value: str
status: str
class TransactionListResponse(BaseModel):
model_config = ConfigDict(populate_by_name=True)
items: list[TransactionSummary]
next_offset: Optional[str | int] = None
class AddressSummary(BaseModel):
model_config = ConfigDict(populate_by_name=True)
address: str
balance: str
txCount: int
lastActive: datetime
recentTransactions: Optional[list[str]] = Field(default=None)
class AddressListResponse(BaseModel):
model_config = ConfigDict(populate_by_name=True)
items: list[AddressSummary]
next_offset: Optional[str | int] = None
class ReceiptSummary(BaseModel):
model_config = ConfigDict(populate_by_name=True)
receiptId: str
jobId: Optional[str] = None
miner: str
coordinator: str
issuedAt: datetime
status: str
payload: Optional[Dict[str, Any]] = None
class ReceiptListResponse(BaseModel):
model_config = ConfigDict(populate_by_name=True)
jobId: str
items: list[ReceiptSummary]
class Receipt(BaseModel):
"""Receipt model for zk-proof generation"""
receiptId: str
miner: str
coordinator: str
issuedAt: datetime
status: str
payload: Optional[Dict[str, Any]] = None
# Confidential Transaction Models
class ConfidentialTransaction(BaseModel):
"""Transaction with optional confidential fields"""
# Public fields (always visible)
transaction_id: str
job_id: str
timestamp: datetime
status: str
# Confidential fields (encrypted when opt-in)
amount: Optional[str] = None
pricing: Optional[Dict[str, Any]] = None
settlement_details: Optional[Dict[str, Any]] = None
# Encryption metadata
confidential: bool = False
encrypted_data: Optional[str] = None # Base64 encoded
encrypted_keys: Optional[Dict[str, str]] = None # Base64 encoded
algorithm: Optional[str] = None
# Access control
participants: List[str] = []
access_policies: Dict[str, Any] = {}
model_config = ConfigDict(populate_by_name=True)
class ConfidentialTransactionCreate(BaseModel):
"""Request to create confidential transaction"""
job_id: str
amount: Optional[str] = None
pricing: Optional[Dict[str, Any]] = None
settlement_details: Optional[Dict[str, Any]] = None
# Privacy options
confidential: bool = False
participants: List[str] = []
# Access policies
access_policies: Dict[str, Any] = {}
class ConfidentialTransactionView(BaseModel):
"""Response for confidential transaction view"""
transaction_id: str
job_id: str
timestamp: datetime
status: str
# Decrypted fields (only if authorized)
amount: Optional[str] = None
pricing: Optional[Dict[str, Any]] = None
settlement_details: Optional[Dict[str, Any]] = None
# Metadata
confidential: bool
participants: List[str]
has_encrypted_data: bool
class ConfidentialAccessRequest(BaseModel):
"""Request to access confidential transaction data"""
transaction_id: str
requester: str
purpose: str
justification: Optional[str] = None
class ConfidentialAccessResponse(BaseModel):
"""Response for confidential data access"""
success: bool
data: Optional[Dict[str, Any]] = None
error: Optional[str] = None
access_id: Optional[str] = None
# Key Management Models
class KeyPair(BaseModel):
"""Encryption key pair for participant"""
participant_id: str
private_key: bytes
public_key: bytes
algorithm: str = "X25519"
created_at: datetime
version: int = 1
model_config = ConfigDict(arbitrary_types_allowed=True)
class KeyRotationLog(BaseModel):
"""Log of key rotation events"""
participant_id: str
old_version: int
new_version: int
rotated_at: datetime
reason: str
class AuditAuthorization(BaseModel):
"""Authorization for audit access"""
issuer: str
subject: str
purpose: str
created_at: datetime
expires_at: datetime
signature: str
class KeyRegistrationRequest(BaseModel):
"""Request to register encryption keys"""
participant_id: str
public_key: str # Base64 encoded
algorithm: str = "X25519"
class KeyRegistrationResponse(BaseModel):
"""Response for key registration"""
success: bool
participant_id: str
key_version: int
registered_at: datetime
error: Optional[str] = None
# Access Log Models
class ConfidentialAccessLog(BaseModel):
"""Audit log for confidential data access"""
transaction_id: Optional[str]
participant_id: str
purpose: str
timestamp: datetime
authorized_by: str
data_accessed: List[str]
success: bool
error: Optional[str] = None
ip_address: Optional[str] = None
user_agent: Optional[str] = None
class AccessLogQuery(BaseModel):
"""Query for access logs"""
transaction_id: Optional[str] = None
participant_id: Optional[str] = None
purpose: Optional[str] = None
start_time: Optional[datetime] = None
end_time: Optional[datetime] = None
limit: int = 100
offset: int = 0
class AccessLogResponse(BaseModel):
"""Response for access log query"""
logs: List[ConfidentialAccessLog]
total_count: int
has_more: bool