zc2 0.0.13

P2P compute broker with credit-based billing, WAL, and broker mesh support
//! Publish-lock task distribution for P2P broker mesh.
//!
//! Instead of brokers calling remote workers directly, a broker that receives a
//! request it cannot serve locally **publishes** the task to its peers.  A peer
//! whose local worker matches the requirements and whose price aligns **locks**
//! the task, executes it on its own verified worker, and returns the result.
//!
//! This guarantees that only brokers with real, verified workers can execute
//! tasks — eliminating phantom-worker and phantom-owner problems by design.

use serde::{Deserialize, Serialize};

/// Task offer broadcast by the originating broker to peers.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskOffer {
    /// Unique task identifier (same as the original request_id)
    pub task_id: String,
    /// Serialised request body (opaque bytes, base64-encoded for JSON transport)
    pub payload_b64: String,
    /// Maximum price the requester is willing to pay (credits/hr).
    /// A peer will only accept if its worker's price_per_hour ≤ this value.
    pub max_price_per_hour: f64,
    /// Estimated duration in seconds (used for cost estimation)
    pub estimated_duration_secs: f64,
    /// Hard timeout — peer must abort execution after this many seconds.
    pub timeout_secs: f64,
    /// Minimum CPU cores required
    #[serde(default = "default_cpus")]
    pub cpus: f64,
    /// Minimum memory in bytes
    #[serde(default = "default_memory")]
    pub memory_bytes: u64,
    /// GPUs required
    #[serde(default)]
    pub gpus: u32,
    /// Required worker type (optional tag filter)
    #[serde(default)]
    pub worker_type: Option<String>,
    /// Required tags (all must match)
    #[serde(default)]
    pub tags: Vec<String>,
    /// The user_id being charged for this task
    pub requester_user_id: String,
    /// The originating broker's node name (for audit)
    pub source_broker: String,
}

fn default_cpus() -> f64 { 1.0 }
fn default_memory() -> u64 { 1024 * 1024 * 1024 }

/// Result returned by the peer that locked and executed the task.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskResult {
    /// Echoed task identifier
    pub task_id: String,
    /// Response payload (base64-encoded)
    pub payload_b64: String,
    /// Actual wall-clock execution time (ms)
    pub duration_ms: f64,
    /// Actual cost charged (credits)
    pub actual_cost: f64,
    /// Name of the worker that executed the task
    pub worker_name: String,
    /// URI of the worker that executed the task
    pub worker_uri: String,
    /// Worker's price_per_hour (zkcr/hr)
    pub price_per_hour: f64,
    /// The peer broker's verified owner_user_id (the entity that earns credits)
    pub executor_owner: String,
    /// Worker process ID (from response header X-Zakuro-Pid, if present)
    #[serde(default)]
    pub worker_pid: Option<String>,
    /// Worker IP (from response header X-Zakuro-IP or worker URI)
    #[serde(default)]
    pub worker_ip: Option<String>,
}

/// Rejection returned when a peer cannot accept a task offer.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskReject {
    pub task_id: String,
    pub reason: String,
}

/// Control message: broker with workers subscribes to a peer to receive task offers (push).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubscribeMessage {
    pub action: String,
    /// Subscriber's own broker URL (so the publisher can identify and push offers).
    pub peer_url: String,
}

/// Identity exchanged during peer handshake.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeerIdentity {
    /// Broker's verified owner_user_id (validated against dashboard at startup)
    pub owner_user_id: String,
    /// Human-readable node name
    pub node_name: String,
    /// Whether this broker's owner was verified against the dashboard
    pub verified: bool,
    /// Summary of available workers and their pricing
    pub workers: Vec<WorkerSummary>,
    /// QUIC UDP port for task offers (0 = not available)
    #[serde(default)]
    pub quic_port: u16,
}

/// Lightweight worker descriptor shared during handshake.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkerSummary {
    pub name: String,
    pub price_per_hour: f64,
    pub status: String,
    pub cpus: f64,
    pub memory_bytes: u64,
    pub gpus: u32,
}