Skip to main content

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}