use crate::primitives::{Address, Hash, Signature, Timestamp};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub enum TrainingTier {
#[default]
Open,
Verified,
Confidential,
}
impl fmt::Display for TrainingTier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Open => write!(f, "open"),
Self::Verified => write!(f, "verified"),
Self::Confidential => write!(f, "confidential"),
}
}
}
impl TrainingTier {
pub fn from_str_lossy(s: &str) -> Self {
match s.to_ascii_lowercase().as_str() {
"verified" => Self::Verified,
"confidential" => Self::Confidential,
_ => Self::Open,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TrainingModality {
Language,
Timeseries,
Vision,
Multimodal,
}
impl fmt::Display for TrainingModality {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Language => write!(f, "language"),
Self::Timeseries => write!(f, "timeseries"),
Self::Vision => write!(f, "vision"),
Self::Multimodal => write!(f, "multimodal"),
}
}
}
impl TrainingModality {
pub fn from_str_lossy(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"language" | "lm" | "text" => Some(Self::Language),
"timeseries" | "ts" | "forecast" | "forecasting" => Some(Self::Timeseries),
"vision" | "image" | "vis" => Some(Self::Vision),
"multimodal" | "mm" | "clip" => Some(Self::Multimodal),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum AggregationRule {
#[default]
Mean,
TrimmedMean { alpha_bps: u16 },
CoordinateMedian,
Krum { f: u32 },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArchitectureSpec {
pub family: String,
pub param_count: u64,
pub modality: TrainingModality,
pub fragment_count: u32,
pub dtype: Option<String>,
#[serde(default)]
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TrainingTaskSpec {
pub task_id: String,
pub sponsor_did: String,
pub sponsor_address: Address,
pub architecture: ArchitectureSpec,
pub tier: TrainingTier,
pub aggregation: AggregationRule,
pub trainer_count: u32,
pub quorum: u32,
pub inner_steps: u32,
pub max_rounds: u32,
pub grace_window_ms: u64,
pub reward_pool: u128,
pub dataset_ref: String,
pub dataset_hash: Hash,
pub min_throughput: Option<u64>,
pub created_at: Timestamp,
#[serde(default)]
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OuterGradient {
pub task_id: String,
pub round: u32,
pub fragment: u32,
pub trainer_did: String,
pub trainer_address: Address,
pub safetensors_hash: Hash,
pub payload_bytes: u64,
pub inner_step_count: u64,
pub submitted_at: Timestamp,
pub signature: Signature,
pub attestation: Option<TrainingAttestation>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TrainingAttestation {
pub vendor: String,
pub report_hex: String,
pub program_hash: Hash,
pub shard_hash: Hash,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SyncRound {
pub task_id: String,
pub round: u32,
pub fragment_quorums: HashMap<u32, FragmentQuorumStatus>,
pub state_root: Hash,
pub syncer_signature: Signature,
pub published_at: Timestamp,
pub no_quorum_witnesses: Option<Vec<Signature>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FragmentQuorumStatus {
pub fragment: u32,
pub accepted: u32,
pub accepted_hashes: Vec<Hash>,
pub quorum_met: bool,
pub post_step_hash: Hash,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TrainingReceipt {
pub task_id: String,
pub task_spec: TrainingTaskSpec,
pub final_model_hash: Hash,
pub syncer_did: String,
pub syncer_address: Address,
pub trainer_contributions: HashMap<String, u32>,
pub trainer_rewards: HashMap<String, u128>,
pub network_commission: u128,
pub round_state_roots: Vec<Hash>,
pub run_root: Hash,
pub syncer_attestation: TrainingAttestation,
pub finalized_at: Timestamp,
pub syncer_signature: Signature,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SealedShardEnvelope {
pub trainer_did: String,
pub shard_index: u32,
pub shard_ciphertext_hash: Hash,
pub shard_ciphertext_bytes: u64,
pub wrapped_data_key: Vec<u8>,
pub wrap_alg: String,
pub enclave_pubkey: Vec<u8>,
pub enclave_measurements_hex: String,
pub created_at: Timestamp,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SealedDatasetManifest {
pub task_id: String,
pub sponsor_did: String,
pub manifest_hash: Hash,
pub envelopes: Vec<SealedShardEnvelope>,
pub sponsor_signature: Signature,
pub created_at: Timestamp,
}
impl SealedDatasetManifest {
pub fn envelope_for(&self, trainer_did: &str) -> Option<&SealedShardEnvelope> {
self.envelopes.iter().find(|e| e.trainer_did == trainer_did)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TrainingRunStatus {
Pending,
Enrolling,
Training,
Completed,
Failed,
Cancelled,
}
impl fmt::Display for TrainingRunStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pending => write!(f, "pending"),
Self::Enrolling => write!(f, "enrolling"),
Self::Training => write!(f, "training"),
Self::Completed => write!(f, "completed"),
Self::Failed => write!(f, "failed"),
Self::Cancelled => write!(f, "cancelled"),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TrainingRun {
pub task_id: String,
pub task_spec: TrainingTaskSpec,
pub status: TrainingRunStatus,
pub syncer_did: Option<String>,
pub syncer_address: Option<Address>,
pub trainers: Vec<String>,
pub current_round: u32,
pub round_state_roots: Vec<Hash>,
pub created_at: Timestamp,
pub last_update: Timestamp,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tier_roundtrip() {
for s in ["open", "OPEN", "Open", "garbage"] {
assert_eq!(TrainingTier::from_str_lossy(s), TrainingTier::Open);
}
assert_eq!(TrainingTier::from_str_lossy("verified"), TrainingTier::Verified);
assert_eq!(
TrainingTier::from_str_lossy("CONFIDENTIAL"),
TrainingTier::Confidential
);
}
#[test]
fn modality_roundtrip() {
assert_eq!(
TrainingModality::from_str_lossy("timeseries"),
Some(TrainingModality::Timeseries)
);
assert_eq!(
TrainingModality::from_str_lossy("ts"),
Some(TrainingModality::Timeseries)
);
assert_eq!(
TrainingModality::from_str_lossy("language"),
Some(TrainingModality::Language)
);
assert_eq!(TrainingModality::from_str_lossy("nonsense"), None);
}
#[test]
fn aggregation_default_is_mean() {
assert_eq!(AggregationRule::default(), AggregationRule::Mean);
}
#[test]
fn run_status_display() {
assert_eq!(TrainingRunStatus::Pending.to_string(), "pending");
assert_eq!(TrainingRunStatus::Training.to_string(), "training");
}
}