tenzro_types/task.rs
1//! Task marketplace types for Tenzro Network
2//!
3//! Defines the types for the decentralized task marketplace where
4//! agents and users can post tasks for AI agents to discover and fulfill.
5
6use crate::primitives::{Address, Timestamp};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10/// Status of a task in the marketplace
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum TaskStatus {
14 /// Task is open and available for agents to bid on / accept
15 Open,
16 /// Task has been assigned to a specific agent
17 Assigned,
18 /// Task is actively being worked on
19 InProgress,
20 /// Task has been completed successfully
21 Completed,
22 /// Task was cancelled by the poster
23 Cancelled,
24 /// Task deadline passed without completion
25 Expired,
26 /// Task is in dispute
27 Disputed,
28}
29
30/// Type of task being requested
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(rename_all = "snake_case")]
33pub enum TaskType {
34 /// AI inference/completion task
35 Inference,
36 /// Code review or generation task
37 CodeReview,
38 /// Data analysis task
39 DataAnalysis,
40 /// Content generation task
41 ContentGeneration,
42 /// Execute an agent from the agent marketplace
43 AgentExecution,
44 /// Translation task
45 Translation,
46 /// Research task
47 Research,
48 /// Custom task type
49 Custom(String),
50}
51
52impl TaskType {
53 pub fn as_str(&self) -> &str {
54 match self {
55 TaskType::Inference => "inference",
56 TaskType::CodeReview => "code_review",
57 TaskType::DataAnalysis => "data_analysis",
58 TaskType::ContentGeneration => "content_generation",
59 TaskType::AgentExecution => "agent_execution",
60 TaskType::Translation => "translation",
61 TaskType::Research => "research",
62 TaskType::Custom(s) => s.as_str(),
63 }
64 }
65}
66
67/// Priority level for a task
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(rename_all = "snake_case")]
70#[derive(Default)]
71pub enum TaskPriority {
72 Low,
73 #[default]
74 Normal,
75 High,
76 Urgent,
77}
78
79/// The class of proof a task completion must carry to be accepted.
80///
81/// These variants map 1:1 onto `tenzro_settlement::SettlementEngine`'s proof
82/// dispatch (ZeroKnowledge / TeeAttestation / Cryptographic / Oracle / Merkle).
83/// We keep a self-contained copy here so `tenzro-types` does not depend on the
84/// settlement crate (settlement depends on types, not the reverse).
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case", tag = "kind")]
87pub enum ProofRequirement {
88 /// A zero-knowledge proof, optionally pinned to a circuit and/or the model
89 /// the inference was claimed to run on.
90 ZeroKnowledge {
91 #[serde(default)]
92 circuit_id: Option<String>,
93 #[serde(default)]
94 model_id: Option<String>,
95 },
96 /// A TEE attestation report, optionally restricted to a provider/enclave class.
97 TeeAttestation {
98 #[serde(default)]
99 provider_class: Option<String>,
100 },
101 /// A plain cryptographic signature over the result by the provider.
102 Cryptographic,
103 /// An oracle-signed attestation of the result.
104 Oracle,
105 /// A Merkle inclusion proof against a poster-pinned root. The completion
106 /// proof's `proof_data` must be `leaf(32) ‖ leaf_index(u32-le, 4) ‖
107 /// siblings(32·k)`; the task layer recomputes the positional SHA-256 root
108 /// and requires it to equal `expected_root`.
109 Merkle {
110 /// Hex Merkle root (poster-supplied, trusted task state) the inclusion
111 /// proof must reproduce. `0x` prefix optional; compared case-insensitively.
112 expected_root: String,
113 },
114}
115
116/// Structured acceptance criteria for a task (lifecycle Stage 1 / P1).
117///
118/// When present on a `TaskInfo`, `tenzro_completeTask` validates the submitted
119/// output and, if `required_proof` is set, routes the completion proof through
120/// the `SettlementEngine` proof dispatch before releasing escrow.
121#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
122pub struct AcceptanceCriteria {
123 /// Proof the completion must carry (None = free-form, no proof gate).
124 #[serde(default)]
125 pub required_proof: Option<ProofRequirement>,
126 /// Required output MIME/format, e.g. "markdown", "application/json".
127 #[serde(default)]
128 pub format: Option<String>,
129 /// Minimum word count of the textual output.
130 #[serde(default)]
131 pub min_word_count: Option<u64>,
132 /// Sections/keys that must be present in the output.
133 #[serde(default)]
134 pub required_sections: Vec<String>,
135 /// Arbitrary additional structured constraints (free-form key/value).
136 #[serde(default)]
137 pub constraints: HashMap<String, String>,
138}
139
140/// A Merkle inclusion proof that a provider holds an ERC-8004 reputation leaf
141/// at or above some score, without an RPC round-trip to read the registry
142/// (lifecycle Stage 2 / P3). The poster recomputes the root from
143/// `leaf` + `merkle_path` and compares it to the on-chain `ReputationRegistry`
144/// root for `registry_root`.
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146pub struct ReputationProof {
147 /// ERC-8004 agent id (decimal string) the leaf belongs to.
148 pub agent_id: String,
149 /// Score claimed by this proof.
150 pub claimed_score: u32,
151 /// Expected Merkle root (hex), matched against the registry's published root.
152 pub registry_root: String,
153 /// Hex-encoded leaf preimage hash for `(agent_id, claimed_score)`.
154 pub leaf: String,
155 /// Sibling hashes (hex) from leaf to root, in order.
156 pub merkle_path: Vec<String>,
157 /// Index of the leaf in the tree (determines left/right hashing order).
158 pub leaf_index: u64,
159}
160
161/// Dispute state attached to a task (lifecycle P4).
162#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
163pub struct TaskDispute {
164 /// Who raised the dispute.
165 pub raised_by: Address,
166 /// Human-readable reason.
167 pub reason: String,
168 /// When it was raised.
169 pub raised_at: Timestamp,
170 /// Resolution once an oracle/governance hook arbitrates (None while open).
171 #[serde(default)]
172 pub resolution: Option<DisputeResolution>,
173}
174
175/// Outcome of an arbitrated task dispute.
176#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
177#[serde(rename_all = "snake_case")]
178pub enum DisputeResolution {
179 /// Escrow released to the assignee (provider wins).
180 ReleaseToProvider,
181 /// Escrow refunded to the poster (poster wins).
182 RefundToPoster,
183}
184
185
186/// A task posted to the Tenzro Network task marketplace
187#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
188pub struct TaskInfo {
189 /// Unique task identifier (UUID v4)
190 pub task_id: String,
191
192 /// Short title describing the task
193 pub title: String,
194
195 /// Detailed description of the task and expected output
196 pub description: String,
197
198 /// Type of task
199 pub task_type: TaskType,
200
201 /// Address of the entity that posted the task
202 pub poster: Address,
203
204 /// Optional: agent assigned to fulfill the task
205 pub assignee: Option<Address>,
206
207 /// Maximum price the poster is willing to pay (in TNZO micro-units)
208 pub max_price: u128,
209
210 /// Actual price quoted/agreed (set when task is assigned)
211 pub quoted_price: Option<u128>,
212
213 /// Current status of the task
214 pub status: TaskStatus,
215
216 /// When the task was posted
217 pub created_at: Timestamp,
218
219 /// Deadline for task completion (Unix timestamp seconds)
220 pub deadline: Option<u64>,
221
222 /// Minimum model capability required (e.g., "7b", "70b", "any")
223 pub required_model: Option<String>,
224
225 /// Specific model ID to use (if None, any capable model is acceptable)
226 pub preferred_model_id: Option<String>,
227
228 /// Input data or prompt for the task
229 pub input: String,
230
231 /// Output/result (populated when completed)
232 pub output: Option<String>,
233
234 /// Task priority level
235 pub priority: TaskPriority,
236
237 /// Additional task-specific metadata
238 pub metadata: HashMap<String, String>,
239
240 /// Transaction hash of the task posting (for escrow reference)
241 pub tx_hash: Option<String>,
242
243 /// Structured acceptance criteria / proof spec (Stage 1, P1). When present,
244 /// completion must satisfy these constraints and, if `required_proof` is
245 /// set, carry a proof verified by the `SettlementEngine`.
246 #[serde(default)]
247 pub acceptance_criteria: Option<AcceptanceCriteria>,
248
249 /// Escrow id that locks the reward once the task is assigned (Stage 3, P0).
250 /// `None` until `tenzro_assignTask` funds the escrow vault.
251 #[serde(default)]
252 pub escrow_id: Option<String>,
253
254 /// Dispute state (P4). `None` unless `tenzro_disputeTask` was called.
255 #[serde(default)]
256 pub dispute: Option<TaskDispute>,
257}
258
259impl TaskInfo {
260 /// Creates a new open task
261 pub fn new(
262 title: String,
263 description: String,
264 task_type: TaskType,
265 poster: Address,
266 max_price: u128,
267 input: String,
268 ) -> Self {
269 let task_id = uuid::Uuid::new_v4().to_string();
270 Self {
271 task_id,
272 title,
273 description,
274 task_type,
275 poster,
276 assignee: None,
277 max_price,
278 quoted_price: None,
279 status: TaskStatus::Open,
280 created_at: Timestamp(chrono::Utc::now().timestamp()),
281 deadline: None,
282 required_model: None,
283 preferred_model_id: None,
284 input,
285 output: None,
286 priority: TaskPriority::Normal,
287 metadata: HashMap::new(),
288 tx_hash: None,
289 acceptance_criteria: None,
290 escrow_id: None,
291 dispute: None,
292 }
293 }
294
295 /// Returns true if the task can still be accepted by an agent
296 pub fn is_available(&self) -> bool {
297 self.status == TaskStatus::Open
298 }
299
300 /// Returns true if the task is in a terminal state
301 pub fn is_terminal(&self) -> bool {
302 matches!(
303 self.status,
304 TaskStatus::Completed | TaskStatus::Cancelled | TaskStatus::Expired
305 )
306 }
307}
308
309/// A quote for a task from a provider agent
310#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
311pub struct TaskQuote {
312 /// The task being quoted
313 pub task_id: String,
314
315 /// Provider agent address
316 pub provider: Address,
317
318 /// Quoted price in TNZO micro-units
319 pub price: u128,
320
321 /// Estimated time to complete (seconds)
322 pub estimated_duration_secs: u64,
323
324 /// Model the provider will use
325 pub model_id: String,
326
327 /// Provider's confidence score (0-100)
328 pub confidence: u8,
329
330 /// Quote expiry (Unix timestamp)
331 pub expires_at: u64,
332
333 /// Optional notes from the provider
334 pub notes: Option<String>,
335
336 /// Optional TEE attestation bound to this quote (base64-encoded attestation
337 /// report), so the poster can prefer attested providers (Stage 2, P3).
338 #[serde(default)]
339 pub provider_attestation: Option<String>,
340
341 /// Optional Merkle proof that the provider's ERC-8004 reputation is at or
342 /// above a threshold, avoiding an RPC round-trip to the registry (P3).
343 #[serde(default)]
344 pub provider_reputation_proof: Option<ReputationProof>,
345}
346
347/// Filter parameters for listing tasks
348#[derive(Debug, Clone, Default, Serialize, Deserialize)]
349pub struct TaskFilter {
350 /// Filter by task type
351 pub task_type: Option<TaskType>,
352
353 /// Filter by status
354 pub status: Option<TaskStatus>,
355
356 /// Filter by poster address
357 pub poster: Option<String>,
358
359 /// Filter by assignee address
360 pub assignee: Option<String>,
361
362 /// Maximum price filter (only show tasks at or below this price)
363 pub max_price: Option<u128>,
364
365 /// Filter tasks requiring a specific model
366 pub required_model: Option<String>,
367
368 /// Maximum number of results to return
369 pub limit: Option<usize>,
370
371 /// Offset for pagination
372 pub offset: Option<usize>,
373}