network: add hub registration, Redis persistence, and federated mesh join protocol
Some checks failed
CLI Tests / test-cli (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
Documentation Validation / validate-docs (push) Has been cancelled
API Endpoint Tests / test-api-endpoints (push) Has been cancelled
Systemd Sync / sync-systemd (push) Has been cancelled

- Change default P2P port from 7070 to 8001 in config and .env.example
- Add redis_url configuration option for hub persistence (default: redis://localhost:6379)
- Implement DNS-based hub registration/unregistration via HTTPS API endpoints
- Add Redis persistence for hub registrations with 1-hour TTL
- Add island join request/response protocol with member list and blockchain credentials
- Add GPU marketplace tracking (offers, bids, providers) in hub manager
- Add
This commit is contained in:
aitbc
2026-04-13 11:47:34 +02:00
parent fefa6c4435
commit d72945f20c
42 changed files with 3802 additions and 1022 deletions

View File

@@ -60,7 +60,7 @@ def _serialize_receipt(receipt: Receipt) -> Dict[str, Any]:
class TransactionRequest(BaseModel):
type: str = Field(description="Transaction type, e.g. TRANSFER or RECEIPT_CLAIM")
type: str = Field(description="Transaction type, e.g. TRANSFER, RECEIPT_CLAIM, GPU_MARKETPLACE, EXCHANGE")
sender: str
nonce: int
fee: int = Field(ge=0)
@@ -70,8 +70,9 @@ class TransactionRequest(BaseModel):
@model_validator(mode="after")
def normalize_type(self) -> "TransactionRequest": # type: ignore[override]
normalized = self.type.upper()
if normalized not in {"TRANSFER", "RECEIPT_CLAIM"}:
raise ValueError(f"unsupported transaction type: {self.type}")
valid_types = {"TRANSFER", "RECEIPT_CLAIM", "GPU_MARKETPLACE", "EXCHANGE"}
if normalized not in valid_types:
raise ValueError(f"unsupported transaction type: {normalized}. Valid types: {valid_types}")
self.type = normalized
return self
@@ -201,31 +202,83 @@ async def get_mempool(chain_id: str = None, limit: int = 100) -> Dict[str, Any]:
@router.get("/accounts/{address}", summary="Get account information")
async def get_account(address: str) -> Dict[str, Any]:
"""Get account information including balance"""
from ..models import Account
async def get_account(address: str, chain_id: str = None) -> Dict[str, Any]:
"""Get account information"""
chain_id = get_chain_id(chain_id)
try:
with session_scope() as session:
account = session.exec(select(Account).where(Account.address == address)).first()
with session_scope() as session:
account = session.exec(select(Account).where(Account.address == address).where(Account.chain_id == chain_id)).first()
if not account:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found")
return {
"address": account.address,
"balance": account.balance,
"nonce": account.nonce,
"chain_id": account.chain_id
}
@router.get("/transactions", summary="Query transactions")
async def query_transactions(
transaction_type: Optional[str] = None,
island_id: Optional[str] = None,
pair: Optional[str] = None,
status: Optional[str] = None,
order_id: Optional[str] = None,
limit: Optional[int] = 100,
chain_id: str = None
) -> List[Dict[str, Any]]:
"""Query transactions with optional filters"""
chain_id = get_chain_id(chain_id)
with session_scope() as session:
query = select(Transaction).where(Transaction.chain_id == chain_id)
# Apply filters based on payload fields
transactions = session.exec(query).all()
results = []
for tx in transactions:
# Filter by transaction type in payload
if transaction_type and tx.payload.get('type') != transaction_type:
continue
if account is None:
return {
"address": address,
"balance": 0,
"nonce": 0,
"exists": False
}
# Filter by island_id in payload
if island_id and tx.payload.get('island_id') != island_id:
continue
return {
"address": account.address,
"balance": account.balance,
"nonce": account.nonce,
"exists": True
}
except Exception as e:
_logger.error("Failed to get account", extra={"error": str(e), "address": address})
raise HTTPException(status_code=500, detail=f"Failed to get account: {str(e)}")
# Filter by pair in payload
if pair and tx.payload.get('pair') != pair:
continue
# Filter by status in payload
if status and tx.payload.get('status') != status:
continue
# Filter by order_id in payload
if order_id and tx.payload.get('order_id') != order_id and tx.payload.get('offer_id') != order_id and tx.payload.get('bid_id') != order_id:
continue
results.append({
"transaction_id": tx.id,
"tx_hash": tx.tx_hash,
"sender": tx.sender,
"recipient": tx.recipient,
"payload": tx.payload,
"status": tx.status,
"created_at": tx.created_at.isoformat(),
"timestamp": tx.timestamp,
"nonce": tx.nonce,
"value": tx.value,
"fee": tx.fee
})
# Apply limit
if limit:
results = results[:limit]
return results
@router.get("/blocks-range", summary="Get blocks in height range")