Files
aitbc/apps/wallet/src/app/api_jsonrpc.py
oib 15427c96c0 chore: update file permissions to executable across repository
- Change file mode from 644 to 755 for all project files
- Add chain_id parameter to get_balance RPC endpoint with default "ait-devnet"
- Rename Miner.extra_meta_data to extra_metadata for consistency
2026-03-06 22:17:54 +01:00

119 lines
5.4 KiB
Python
Executable File

from __future__ import annotations
import base64
from typing import Any, Dict, Optional
from fastapi import APIRouter, Depends
from .deps import get_receipt_service, get_keystore, get_ledger
from .models import ReceiptVerificationModel, from_validation_result
from .keystore.persistent_service import PersistentKeystoreService
from .ledger_mock import SQLiteLedgerAdapter
from .receipts.service import ReceiptVerifierService
router = APIRouter(tags=["jsonrpc"])
def _response(result: Optional[Dict[str, Any]] = None, error: Optional[Dict[str, Any]] = None, *, request_id: Any = None) -> Dict[str, Any]:
payload: Dict[str, Any] = {"jsonrpc": "2.0", "id": request_id}
if error is not None:
payload["error"] = error
else:
payload["result"] = result
return payload
@router.post("/rpc", summary="JSON-RPC endpoint")
def handle_jsonrpc(
request: Dict[str, Any],
service: ReceiptVerifierService = Depends(get_receipt_service),
keystore: KeystoreService = Depends(get_keystore),
ledger: SQLiteLedgerAdapter = Depends(get_ledger),
) -> Dict[str, Any]:
method = request.get("method")
params = request.get("params") or {}
request_id = request.get("id")
if method == "receipts.verify_latest":
job_id = params.get("job_id")
if not job_id:
return _response(error={"code": -32602, "message": "job_id required"}, request_id=request_id)
result = service.verify_latest(str(job_id))
if result is None:
return _response(error={"code": -32004, "message": "receipt not found"}, request_id=request_id)
model = from_validation_result(result)
return _response(result=model.model_dump(), request_id=request_id)
if method == "receipts.verify_history":
job_id = params.get("job_id")
if not job_id:
return _response(error={"code": -32602, "message": "job_id required"}, request_id=request_id)
results = [from_validation_result(item).model_dump() for item in service.verify_history(str(job_id))]
return _response(result={"items": results}, request_id=request_id)
if method == "wallet.list":
items = []
for record in keystore.list_records():
ledger_record = ledger.get_wallet(record.wallet_id)
metadata = ledger_record.metadata if ledger_record else record.metadata
items.append({"wallet_id": record.wallet_id, "public_key": record.public_key, "metadata": metadata})
return _response(result={"items": items}, request_id=request_id)
if method == "wallet.create":
wallet_id = params.get("wallet_id")
password = params.get("password")
metadata = params.get("metadata") or {}
secret_b64 = params.get("secret_key")
if not wallet_id or not password:
return _response(error={"code": -32602, "message": "wallet_id and password required"}, request_id=request_id)
secret = base64.b64decode(secret_b64) if secret_b64 else None
record = keystore.create_wallet(wallet_id=wallet_id, password=password, secret=secret, metadata=metadata)
ledger.upsert_wallet(record.wallet_id, record.public_key, record.metadata)
ledger.record_event(record.wallet_id, "created", {"metadata": record.metadata})
return _response(
result={
"wallet": {
"wallet_id": record.wallet_id,
"public_key": record.public_key,
"metadata": record.metadata,
}
},
request_id=request_id,
)
if method == "wallet.unlock":
wallet_id = params.get("wallet_id")
password = params.get("password")
if not wallet_id or not password:
return _response(error={"code": -32602, "message": "wallet_id and password required"}, request_id=request_id)
try:
keystore.unlock_wallet(wallet_id, password)
ledger.record_event(wallet_id, "unlocked", {"success": True})
return _response(result={"wallet_id": wallet_id, "unlocked": True}, request_id=request_id)
except (KeyError, ValueError):
ledger.record_event(wallet_id, "unlocked", {"success": False})
return _response(error={"code": -32001, "message": "invalid credentials"}, request_id=request_id)
if method == "wallet.sign":
wallet_id = params.get("wallet_id")
password = params.get("password")
message_b64 = params.get("message")
if not wallet_id or not password or not message_b64:
return _response(error={"code": -32602, "message": "wallet_id, password, message required"}, request_id=request_id)
try:
message = base64.b64decode(message_b64)
except Exception:
return _response(error={"code": -32602, "message": "invalid base64 message"}, request_id=request_id)
try:
signature = keystore.sign_message(wallet_id, password, message)
ledger.record_event(wallet_id, "sign", {"success": True})
except (KeyError, ValueError):
ledger.record_event(wallet_id, "sign", {"success": False})
return _response(error={"code": -32001, "message": "invalid credentials"}, request_id=request_id)
signature_b64 = base64.b64encode(signature).decode()
return _response(result={"wallet_id": wallet_id, "signature": signature_b64}, request_id=request_id)
return _response(error={"code": -32601, "message": "Method not found"}, request_id=request_id)