use crate::{ComputeCapacity, DataId, JobId, WorkerId};
use borsh::{BorshDeserialize, BorshSerialize};
use bytes::Bytes;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum InputsModeDto {
InputsNone,
InputsPath(String),
InputsData(Bytes),
InputsStream(String),
}
impl std::fmt::Debug for InputsModeDto {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InputsModeDto::InputsNone => write!(f, "InputsNone"),
InputsModeDto::InputsPath(path) => write!(f, "InputsPath({path})"),
InputsModeDto::InputsData(data) => write!(f, "InputsData({} bytes)", data.len()),
InputsModeDto::InputsStream(uri) => write!(f, "InputsStream({uri})"),
}
}
}
pub use zisk_common::AirInstanceCount;
pub use zisk_common::ProofKind;
pub use zisk_common::StatsCostPerType;
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum HintsModeDto {
HintsNone,
HintsPath(String),
HintsData(Bytes),
HintsStream(String),
}
impl std::fmt::Debug for HintsModeDto {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HintsModeDto::HintsNone => write!(f, "HintsNone"),
HintsModeDto::HintsPath(path) => write!(f, "HintsPath({path})"),
HintsModeDto::HintsData(data) => write!(f, "HintsData({} bytes)", data.len()),
HintsModeDto::HintsStream(uri) => write!(f, "HintsStream({uri})"),
}
}
}
pub struct LaunchProofRequestDto {
pub data_id: DataId,
pub hash_id: String,
pub compute_capacity: Option<u32>,
pub minimal_compute_capacity: Option<u32>,
pub inputs_mode: InputsModeDto,
pub hints_mode: HintsModeDto,
pub simulated_node: Option<u32>,
pub metadata: Option<std::collections::BTreeMap<String, String>>,
pub execution_only: bool,
pub proof_type: ProofKind,
}
pub struct LaunchProofResponseDto {
pub job_id: JobId,
}
pub struct LaunchWrapRequestDto {
pub proof_data: Vec<u8>,
pub proof_dest: i32,
}
pub struct WorkerRegisterRequestDto {
pub worker_id: WorkerId,
pub compute_capacity: ComputeCapacity,
}
pub struct WorkerReconnectRequestDto {
pub worker_id: WorkerId,
pub compute_capacity: ComputeCapacity,
pub last_known_job_id: Option<JobId>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReconnectionDirectiveDto {
Idle,
KeepComputing,
CancelStaleJob,
}
pub enum CoordinatorMessageDto {
Heartbeat(HeartbeatDto),
Shutdown(ShutdownDto),
WorkerRegisterResponse(WorkerRegisterResponseDto),
ExecuteTaskRequest(ExecuteTaskRequestDto),
JobCancelled(JobCancelledDto),
StreamData(StreamDataDto),
SetupProgram(SetupProgramDto),
InputStreamData(InputStreamDataDto),
SetupAggregationProgram(SetupAggregationProgramDto),
RunAggregateProofs(RunAggregateProofsDto),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NormalizeCircuitDto {
pub body: String,
}
#[derive(Debug, Clone)]
pub struct AggregationProgramSpecDto {
pub normalize: Option<NormalizeCircuitDto>,
pub aggregate_publics_body: String,
pub n_free: u64,
pub n_publics_agg: u64,
pub program_vks: Vec<[String; 4]>,
}
#[derive(Debug, Clone)]
pub struct SetupAggregationProgramDto {
pub job_id: String,
pub recurser_id: String,
pub spec: AggregationProgramSpecDto,
}
#[derive(Debug, Clone)]
pub struct RunAggregateProofsDto {
pub job_id: String,
pub recurser_id: String,
pub proof_a: Vec<u8>,
pub proof_b: Vec<u8>,
pub free_inputs_a: Vec<u64>,
pub free_inputs_b: Vec<u64>,
pub root_c_recurser_agg: Option<[u64; 4]>,
}
#[derive(Debug, Clone)]
pub struct SetupAggregationProgramAckDto {
pub job_id: String,
pub worker_id: WorkerId,
pub recurser_id: String,
pub success: bool,
pub error_message: Option<String>,
pub vk: Vec<u8>,
pub hash_mode: String,
}
#[derive(Debug, Clone)]
pub struct RunAggregateProofsAckDto {
pub job_id: String,
pub worker_id: WorkerId,
pub success: bool,
pub error_message: Option<String>,
pub proof: Vec<u8>,
}
pub struct InputStreamDataDto {
pub job_id: JobId,
pub payload: Bytes,
}
pub struct SetupProgramDto {
pub job_id: String,
pub elf_bytes: Vec<u8>,
pub hash_id: String,
pub program_name: String,
pub with_hints: bool,
pub emulator_only: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum StreamMessageKind {
Start,
Data,
End,
}
#[derive(Debug, Clone)]
pub struct StreamDataDto {
pub job_id: JobId,
pub stream_type: StreamMessageKind,
pub stream_payload: Option<StreamPayloadDto>,
}
#[derive(Debug, Clone)]
pub struct StreamPayloadDto {
pub sequence_number: u32,
pub payload: Bytes,
}
pub struct HeartbeatDto {
pub timestamp: DateTime<Utc>,
}
pub struct ShutdownDto {
pub reason: String,
pub grace_period_seconds: u32,
}
pub struct WorkerRegisterResponseDto {
pub worker_id: WorkerId,
pub accepted: bool,
pub message: String,
pub registered_at: DateTime<Utc>,
}
pub struct JobCancelledDto {
pub job_id: JobId,
pub reason: String,
}
pub struct ExecuteTaskRequestDto {
pub worker_id: WorkerId,
pub job_id: JobId,
pub params: ExecuteTaskRequestTypeDto,
pub metadata: Option<std::collections::BTreeMap<String, String>>,
}
pub enum ExecuteTaskRequestTypeDto {
ContributionParams(ContributionParamsDto),
ProveParams(ProveParamsDto),
AggParams(AggParamsDto),
ExecutionParams(ContributionParamsDto),
WrapParams(WrapParamsDto),
}
pub struct WrapParamsDto {
pub proof_data: Vec<u8>,
pub proof_dest: i32,
}
pub struct ContributionParamsDto {
pub hash_id: String,
pub data_id: DataId,
pub input_source: InputSourceDto,
pub hints_source: HintsSourceDto,
pub rank_id: u32,
pub total_workers: u32,
pub worker_allocation: Vec<u32>,
pub job_compute_units: ComputeCapacity,
}
#[derive(Clone)]
pub enum InputSourceDto {
InputPath(String),
InputData(Bytes),
InputNull,
}
impl std::fmt::Debug for InputSourceDto {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InputSourceDto::InputPath(path) => write!(f, "InputPath({path})"),
InputSourceDto::InputData(data) => write!(f, "InputData({} bytes)", data.len()),
InputSourceDto::InputNull => write!(f, "InputNull"),
}
}
}
impl BorshSerialize for InputSourceDto {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
match self {
InputSourceDto::InputPath(path) => {
BorshSerialize::serialize(&0u8, writer)?;
BorshSerialize::serialize(path, writer)
}
InputSourceDto::InputData(data) => {
BorshSerialize::serialize(&1u8, writer)?;
let len = u32::try_from(data.len()).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"InputSourceDto::InputData payload exceeds the u32 borsh length prefix",
)
})?;
BorshSerialize::serialize(&len, writer)?;
writer.write_all(data)
}
InputSourceDto::InputNull => BorshSerialize::serialize(&2u8, writer),
}
}
}
impl BorshDeserialize for InputSourceDto {
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
match u8::deserialize_reader(reader)? {
0 => Ok(InputSourceDto::InputPath(String::deserialize_reader(reader)?)),
1 => {
let len = u32::deserialize_reader(reader)? as usize;
let mut buf = vec![0u8; len];
reader.read_exact(&mut buf)?;
Ok(InputSourceDto::InputData(Bytes::from(buf)))
}
2 => Ok(InputSourceDto::InputNull),
other => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid InputSourceDto variant {other}"),
)),
}
}
}
#[derive(Clone)]
pub enum HintsSourceDto {
HintsPath(String),
HintsData(Bytes),
HintsStream(String),
HintsNull,
}
impl std::fmt::Debug for HintsSourceDto {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HintsSourceDto::HintsPath(path) => write!(f, "HintsPath({path})"),
HintsSourceDto::HintsData(data) => write!(f, "HintsData({} bytes)", data.len()),
HintsSourceDto::HintsStream(uri) => write!(f, "HintsStream({uri})"),
HintsSourceDto::HintsNull => write!(f, "HintsNull"),
}
}
}
impl BorshSerialize for HintsSourceDto {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
match self {
HintsSourceDto::HintsPath(path) => {
BorshSerialize::serialize(&0u8, writer)?;
BorshSerialize::serialize(path, writer)
}
HintsSourceDto::HintsData(data) => {
BorshSerialize::serialize(&1u8, writer)?;
let len = u32::try_from(data.len()).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"HintsSourceDto::HintsData payload exceeds the u32 borsh length prefix",
)
})?;
BorshSerialize::serialize(&len, writer)?;
writer.write_all(data)
}
HintsSourceDto::HintsStream(uri) => {
BorshSerialize::serialize(&2u8, writer)?;
BorshSerialize::serialize(uri, writer)
}
HintsSourceDto::HintsNull => BorshSerialize::serialize(&3u8, writer),
}
}
}
impl BorshDeserialize for HintsSourceDto {
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
match u8::deserialize_reader(reader)? {
0 => Ok(HintsSourceDto::HintsPath(String::deserialize_reader(reader)?)),
1 => {
let len = u32::deserialize_reader(reader)? as usize;
let mut buf = vec![0u8; len];
reader.read_exact(&mut buf)?;
Ok(HintsSourceDto::HintsData(Bytes::from(buf)))
}
2 => Ok(HintsSourceDto::HintsStream(String::deserialize_reader(reader)?)),
3 => Ok(HintsSourceDto::HintsNull),
other => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid HintsSourceDto variant {other}"),
)),
}
}
}
pub struct ProveParamsDto {
pub challenges: Vec<ChallengesDto>,
}
#[derive(Clone)]
pub struct WitnessInfoDto {
pub witness_time: f32,
pub publics: Vec<u64>,
pub proof_values: Vec<u64>,
pub summary_info: String,
pub total_instances: u64,
}
#[derive(Clone)]
pub struct ZiskExecutorTimeDto {
pub total_duration: f32,
pub execution_duration: f32,
pub count_and_plan_duration: f32,
pub count_and_plan_mo_duration: f32,
pub asm_execution_duration: Option<AsmExecutionInfoDto>,
pub task_received_time: f64,
}
#[derive(Clone)]
pub struct AsmExecutionInfoDto {
pub time: f32,
pub mhz: f32,
}
#[derive(Clone)]
pub struct ChallengesDto {
pub worker_index: u32,
pub airgroup_id: u32,
pub challenge: Vec<u64>,
}
pub struct ExecutionResultDataDto {
pub instances: u64,
pub executed_steps: u64,
pub zisk_executor_time: ZiskExecutorTimeDto,
pub publics: Vec<u64>,
pub cost_per_type: StatsCostPerType,
pub plan: Vec<AirInstanceCount>,
}
pub struct AggParamsDto {
pub agg_proofs: Vec<ProofStarkDto>,
pub last_proof: bool,
pub final_proof: bool,
pub proof_type: ProofKind,
}
pub struct ProofStarkDto {
pub worker_idx: u32,
pub airgroup_id: u64,
pub values: Vec<u64>,
}
pub struct FinalProofDto {
pub proof_data: Vec<u8>,
pub executed_steps: u64,
pub instances: u64,
}
pub struct ExecuteTaskResponseDto {
pub job_id: JobId,
pub worker_id: WorkerId,
pub success: bool,
pub error_message: Option<String>,
pub result_data: Option<ExecuteTaskResponseResultDataDto>,
pub worker_in_recovery: bool,
}
pub struct ContributionsResultDataDto {
pub challenges: Vec<ChallengesDto>,
pub witness_info: WitnessInfoDto,
pub zisk_executor_time: ZiskExecutorTimeDto,
pub cost_per_type: StatsCostPerType,
}
pub enum ExecuteTaskResponseResultDataDto {
Execution(ExecutionResultDataDto),
Challenges(ContributionsResultDataDto),
Proofs(Vec<ProofStarkDto>),
FinalProof(FinalProofDto),
WrapResult(WrapResultDto),
}
pub struct WrapResultDto {
pub proof_data: Vec<u8>,
}
pub struct HeartbeatAckDto {
pub worker_id: WorkerId,
}
pub struct SetupProgramAckDto {
pub job_id: String,
pub worker_id: WorkerId,
pub hash_id: String,
pub success: bool,
pub error_message: Option<String>,
pub vk: Vec<u8>,
pub hash_mode: String,
}
pub struct WorkerErrorDto {
pub worker_id: WorkerId,
pub job_id: JobId,
pub error_message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebhookErrorDto {
pub code: String,
pub message: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WebhookPayloadDto {
pub job_id: String,
pub success: bool,
pub duration_ms: u64,
pub executed_steps: Option<u64>,
pub timestamp: String,
pub error: Option<WebhookErrorDto>,
#[serde(skip_serializing_if = "Option::is_none")]
pub proof_data: Option<Vec<u8>>,
}
impl WebhookPayloadDto {
pub fn success(
job_id: String,
duration_ms: u64,
executed_steps: Option<u64>,
proof_data: Option<Vec<u8>>,
) -> Self {
Self {
job_id,
success: true,
duration_ms,
executed_steps,
timestamp: chrono::Utc::now().to_rfc3339(),
error: None,
proof_data,
}
}
pub fn failure(job_id: String, duration_ms: u64, error: WebhookErrorDto) -> Self {
Self {
job_id,
success: false,
duration_ms,
executed_steps: None,
timestamp: chrono::Utc::now().to_rfc3339(),
error: Some(error),
proof_data: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn input_source_borsh_layout_matches_derived_vec_encoding() {
let encoded = borsh::to_vec(&InputSourceDto::InputData(Bytes::from_static(&[7, 8, 9])))
.expect("serialize");
assert_eq!(encoded, vec![1, 3, 0, 0, 0, 7, 8, 9]);
}
#[test]
fn hints_source_borsh_layout_matches_derived_vec_encoding() {
let encoded =
borsh::to_vec(&HintsSourceDto::HintsData(Bytes::from_static(&[1, 2]))).expect("ser");
assert_eq!(encoded, vec![1, 2, 0, 0, 0, 1, 2]);
}
#[test]
fn input_source_round_trips_every_variant() {
let cases = [
InputSourceDto::InputPath("/tmp/in".to_string()),
InputSourceDto::InputData(Bytes::from_static(&[0, 255, 128])),
InputSourceDto::InputData(Bytes::new()),
InputSourceDto::InputNull,
];
for case in cases {
let bytes = borsh::to_vec(&case).expect("serialize");
let back: InputSourceDto = borsh::from_slice(&bytes).expect("deserialize");
assert_eq!(format!("{case:?}"), format!("{back:?}"));
if let (InputSourceDto::InputData(a), InputSourceDto::InputData(b)) = (&case, &back) {
assert_eq!(a, b);
}
}
}
#[test]
fn hints_source_round_trips_every_variant() {
let cases = [
HintsSourceDto::HintsPath("/tmp/h".to_string()),
HintsSourceDto::HintsData(Bytes::from_static(&[3, 4, 5])),
HintsSourceDto::HintsStream("quic://127.0.0.1:9".to_string()),
HintsSourceDto::HintsNull,
];
for case in cases {
let bytes = borsh::to_vec(&case).expect("serialize");
let back: HintsSourceDto = borsh::from_slice(&bytes).expect("deserialize");
assert_eq!(format!("{case:?}"), format!("{back:?}"));
if let (HintsSourceDto::HintsData(a), HintsSourceDto::HintsData(b)) = (&case, &back) {
assert_eq!(a, b);
}
}
}
#[test]
fn payload_debug_does_not_dump_bytes() {
let big = InputsModeDto::InputsData(Bytes::from(vec![0u8; 4096]));
assert_eq!(format!("{big:?}"), "InputsData(4096 bytes)");
let src = InputSourceDto::InputData(Bytes::from(vec![0u8; 4096]));
assert_eq!(format!("{src:?}"), "InputData(4096 bytes)");
}
#[test]
fn invalid_variant_index_is_an_error_not_a_panic() {
assert!(borsh::from_slice::<InputSourceDto>(&[9]).is_err());
assert!(borsh::from_slice::<HintsSourceDto>(&[9]).is_err());
}
}