@zenithcore/core
Version:
Core functionality for ZenithKernel framework
261 lines (231 loc) • 11.9 kB
text/typescript
import { ExposeRest } from "@decorators/ExposeRest";
import { HttpRoute } from "@decorators/HttpRoute";
import { randomBytes } from "crypto";
import { RegistryMeta } from "@components/registryMeta"; // Assuming this path is correct
import { SystemComponent } from "@decorators/SystemComponent"; // Assuming this path is correct
import { MessagingSystem } from "@core/MessagingSystem"; // Assuming this path is correct
import { ECSManager } from "@core/ECSManager"; // Assuming this is needed for 'this.ecs'
import { createVerifySystem, VerifySystem } from "./VerifySystem";
// Assuming Message type from @types (imported in MessagingSystem) looks something like this:
interface Message {
type: string;
payload: any;
senderId?: string;
// other potential fields like messageId, timestamp etc.
}
// Define a basic structure for what a Hydra entity's manifest might look like
interface HydraEntityManifest {
id: string;
name: string;
version: string;
entryPoint: string; // e.g., path to a WASM module or a component identifier
permissions?: string[];
dependencies?: string[];
metadata?: Record<string, any>;
}
// Define a structure for storing challenges
interface ActiveChallenge {
publicKey: string;
nonce: string;
timestamp: number;
expiresAt: number;
}
// Assume VerifySystem has a method like this (you'll need to implement it there)
export interface VerifySystemInterface {
verifyProof(publicKey: string, challenge: string, proof: string): Promise<boolean>;
}
("RegistryServer")
({
label: "RegistryServer",
component: RegistryMeta, // This component would be rendered by your UI layer for this system
props: { name: "RegistryServer" }
})
export class RegistryServer extends MessagingSystem {
public entity!: number; // Assigned by ECSManager when the system's entity is created
// In-memory store for Hydra entity manifests (replace with a persistent store in production)
private hydraEntityRegistry = new Map<string, HydraEntityManifest>();
// In-memory store for active challenges (replace with a persistent/cached store in production)
private activeChallenges = new Map<string, ActiveChallenge>(); // publicKey -> ActiveChallenge
public readonly channelId = "RegistryServer"; // Implements abstract property from MessagingSystem
private readonly CHALLENGE_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
// Instance of VerifySystem (would be injected or retrieved from kernel)
private verifySystem?: VerifySystemInterface; // You'll need to set this up
constructor(ecs: ECSManager) {
super(ecs); // Pass ECSManager to MessagingSystem's (or BaseSystem's) constructor
this.verifySystem = createVerifySystem(ecs);
console.log("🔻 Initializing RegistryServer with ECSManager and VerifySystem");
}
onUnload?(): void {
// Cleanup logic, e.g., clear intervals, close connections
this.activeChallenges.clear();
console.log("🔻 RegistryServer unloaded");
}
// Overrides MessagingSystem.update to include its own logic + message handling
update(): void {
super.update(); // Call MessagingSystem's update to process messages
this.cleanupExpiredChallenges(); // Perform RegistryServer specific updates
}
onLoad(): void {
// Ensure this.entity is valid (usually set by the ECS when system is added)
if (this.entity === undefined && this.ecs) {
console.warn("RegistryServer: this.entity is not set by ECS. Ensure BaseSystem or system registration handles entity creation.");
}
if (this.ecs && typeof this.entity === 'number') {
try {
this.ecs.addComponent<RegistryMeta>(
this.entity,
RegistryMeta,
new RegistryMeta("RegistryServer") // Assuming RegistryMeta constructor
);
console.log("✅ RegistryServer's RegistryMeta component added to ECS entity:", this.entity);
} catch (e: any) {
console.error("Failed to add RegistryMeta component:", e.message, e.stack);
}
} else {
console.warn("RegistryServer: ECSManager not available or entity not set, skipping component registration.");
}
console.log("✅ RegistryServer loaded and initialized");
}
/**
* Retrieves the manifest or details for a registered Hydra entity.
* This is crucial for the ZenithRouterIntegration.
*/
("GET", "/registry/entity/:hydraId")
public getEntityRegistry(params: { hydraId: string }): HydraEntityManifest | { error: string } {
const hydraId = params.hydraId;
console.log(`[RegistryServer] Request to get entity registry for: ${hydraId}`);
const manifest = this.hydraEntityRegistry.get(hydraId);
if (manifest) {
return manifest;
}
// Consider returning a 404 status code via your HTTP layer
return { error: `Entity with ID '${hydraId}' not found.` };
}
/**
* Registers a new Hydra entity or updates an existing one.
* Can also be triggered via a message.
*/
public registerHydraEntity(manifest: HydraEntityManifest): {success: boolean, id?: string, error?: string} {
if (!manifest || !manifest.id) {
console.error("[RegistryServer] Attempted to register entity with invalid manifest or missing ID.");
return { success: false, error: "Invalid manifest or missing ID." };
}
this.hydraEntityRegistry.set(manifest.id, manifest);
console.log(`[RegistryServer] Entity registered/updated: ${manifest.id}`);
return { success: true, id: manifest.id };
}
("POST", "/auth/challenge")
handleChallenge(req: { body: { publicKey: string } }): { nonce: string } | { error: string } {
try {
const { publicKey } = req.body;
if (!publicKey || typeof publicKey !== 'string' || publicKey.length < 10) {
return { error: "Invalid or missing publicKey." };
}
const nonce = randomBytes(32).toString("hex");
const now = Date.now();
this.activeChallenges.set(publicKey, {
publicKey,
nonce,
timestamp: now,
expiresAt: now + this.CHALLENGE_EXPIRY_MS
});
console.log(`[RegistryServer] Challenge issued for publicKey: ${publicKey.substring(0,10)}... nonce: ${nonce.substring(0,10)}...`);
return { nonce };
} catch (error: any) {
console.error("[RegistryServer] Error in handleChallenge:", error.message, error.stack);
return { error: "Failed to issue challenge." };
}
}
private async _onVerifyRequest(reqBody: { publicKey: string; challenge: string; proof: string }): Promise<{ token?: string; error?: string; verified?: boolean }> {
const { publicKey, challenge, proof } = reqBody;
console.log(`[RegistryServer] Internal verification request for publicKey: ${publicKey.substring(0,10)}...`);
const activeChallenge = this.activeChallenges.get(publicKey);
if (!activeChallenge) {
return { error: "No active challenge found or challenge expired for this publicKey.", verified: false };
}
if (activeChallenge.nonce !== challenge) {
return { error: "Challenge mismatch.", verified: false };
}
if (activeChallenge.expiresAt < Date.now()) {
this.activeChallenges.delete(publicKey);
return { error: "Challenge expired.", verified: false };
}
if (!this.verifySystem) {
console.warn("[RegistryServer] VerifySystem not available. Skipping actual proof verification. Simulating success.");
this.activeChallenges.delete(publicKey);
return { token: `dev-token-${publicKey.slice(0, 12)}-${Date.now()}`, verified: true };
}
try {
const isValid = await this.verifySystem.verifyProof(publicKey, challenge, proof);
if (isValid) {
this.activeChallenges.delete(publicKey);
const token = `zkp-verified-token-${publicKey.slice(0, 10)}...${randomBytes(8).toString("hex")}`;
return { token, verified: true };
} else {
return { error: "Proof verification failed.", verified: false };
}
} catch (error: any) {
console.error("[RegistryServer] Error during proof verification:", error.message, error.stack);
return { error: "An error occurred during proof verification.", verified: false };
}
}
("POST", "/auth/verify")
async handleVerify(req: { body: { publicKey: string; challenge: string; proof: string } }): Promise<{ token?: string; error?: string; verified?: boolean }> {
try {
const { publicKey, challenge, proof } = req.body;
if (!publicKey || !challenge || !proof) {
return { error: "Missing publicKey, challenge, or proof." };
}
return await this._onVerifyRequest({ publicKey, challenge, proof });
} catch (error: any) {
console.error("[RegistryServer] Error in handleVerify:", error.message, error.stack);
return { error: "Failed to verify proof." };
}
}
// Overrides and implements onMessage from MessagingSystem with the correct signature
protected onMessage(message: Message): void {
console.log(`[RegistryServer] Message received - Type: ${message.type}, Sender: ${message.senderId || 'N/A'}, Payload:`, message.payload);
switch (message.type) {
case 'REGISTER_ENTITY_REQUEST':
if (message.payload && typeof message.payload === 'object') {
const result = this.registerHydraEntity(message.payload as HydraEntityManifest);
if (message.senderId) {
this.send(message.senderId, { type: 'REGISTER_ENTITY_RESPONSE', payload: result });
}
} else {
if (message.senderId) {
this.send(message.senderId, { type: 'REGISTER_ENTITY_RESPONSE', payload: { success: false, error: "Invalid payload for entity registration." } });
}
}
break;
case 'GET_ENTITY_REQUEST':
if (message.payload && typeof message.payload.id === 'string') {
const manifest = this.hydraEntityRegistry.get(message.payload.id);
if (message.senderId) {
this.send(message.senderId, { type: 'GET_ENTITY_RESPONSE', payload: manifest || { error: 'Entity not found' } });
}
} else {
if (message.senderId) {
this.send(message.senderId, { type: 'GET_ENTITY_RESPONSE', payload: { error: "Invalid payload for get entity request, 'id' is required." } });
}
}
break;
// Add more message handlers as needed
default:
console.log(`[RegistryServer] Unhandled message type: ${message.type}`);
}
}
private cleanupExpiredChallenges(): void {
const now = Date.now();
let cleanedCount = 0;
this.activeChallenges.forEach((challenge, key) => {
if (challenge.expiresAt < now) {
this.activeChallenges.delete(key);
cleanedCount++;
}
});
if (cleanedCount > 0) {
console.log(`[RegistryServer] Cleaned up ${cleanedCount} expired challenges.`);
}
}
}