1use crate::{
2 evaluation::{response::EvaluatorError, runner::types::EvaluateInfo},
3 governance::data::GovernanceData,
4 model::common::viewpoints::validate_fact_viewpoints,
5};
6use ave_common::{
7 Namespace, SchemaType, ValueWrapper,
8 identity::{DigestIdentifier, PublicKey, Signed},
9 request::EventRequest,
10};
11
12use borsh::{BorshDeserialize, BorshSerialize};
13use serde::{Deserialize, Serialize};
14use std::collections::{BTreeMap, BTreeSet};
15
16#[derive(Debug, Clone, Default)]
17pub enum EvalWorkerContext {
18 #[default]
19 Empty,
20 Governance {
21 issuers: BTreeSet<PublicKey>,
22 issuer_any: bool,
23 },
24 TrackerFact {
25 issuers: BTreeSet<PublicKey>,
26 issuer_any: bool,
27 schema_viewpoints: BTreeSet<String>,
28 },
29 TrackerTransfer {
30 members: BTreeSet<PublicKey>,
31 creators: BTreeMap<PublicKey, BTreeSet<Namespace>>,
32 },
33}
34
35impl EvalWorkerContext {
36 pub const fn issuers(&self) -> Option<(&BTreeSet<PublicKey>, bool)> {
37 match self {
38 Self::Governance {
39 issuers,
40 issuer_any,
41 }
42 | Self::TrackerFact {
43 issuers,
44 issuer_any,
45 ..
46 } => Some((issuers, *issuer_any)),
47 Self::Empty | Self::TrackerTransfer { .. } => None,
48 }
49 }
50}
51
52#[derive(
54 Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
55)]
56pub struct EvaluationReq {
57 pub event_request: Signed<EventRequest>,
59
60 pub governance_id: DigestIdentifier,
61
62 pub data: EvaluateData,
63
64 pub sn: u64,
65
66 pub gov_version: u64,
67
68 pub namespace: Namespace,
69
70 pub schema_id: SchemaType,
71
72 pub signer: PublicKey,
73
74 pub signer_is_owner: bool,
75}
76
77impl EvaluationReq {
78 fn validate_fact_viewpoints(
79 fact_viewpoints: &BTreeSet<String>,
80 schema_id: &SchemaType,
81 schema_viewpoints: Option<&BTreeSet<String>>,
82 ) -> Result<(), EvaluatorError> {
83 validate_fact_viewpoints(fact_viewpoints, schema_id, schema_viewpoints)
84 .map_err(EvaluatorError::InvalidEventRequest)
85 }
86
87 pub fn build_evaluate_info(
88 &self,
89 init_state: &Option<ValueWrapper>,
90 worker_context: &EvalWorkerContext,
91 ) -> Result<EvaluateInfo, EvaluatorError> {
92 match (self.event_request.content(), &self.data) {
93 (
94 EventRequest::Fact(fact_request),
95 EvaluateData::GovFact { state },
96 ) => {
97 Self::validate_fact_viewpoints(
98 &fact_request.viewpoints,
99 &self.schema_id,
100 None,
101 )?;
102
103 Ok(EvaluateInfo::GovFact {
104 payload: fact_request.payload.clone(),
105 state: state.clone(),
106 })
107 }
108 (
109 EventRequest::Fact(fact_request),
110 EvaluateData::TrackerSchemasFact {
111 state,
112 },
113 ) => init_state.as_ref().map_or_else(
114 || {
115 Err(EvaluatorError::InternalError(
116 "Init state must be some".to_owned(),
117 ))
118 },
119 |init_state| {
120 let EvalWorkerContext::TrackerFact {
121 schema_viewpoints,
122 ..
123 } = worker_context
124 else {
125 return Err(EvaluatorError::InternalError(
126 "Tracker fact evaluation context is missing"
127 .to_owned(),
128 ));
129 };
130
131 Self::validate_fact_viewpoints(
132 &fact_request.viewpoints,
133 &self.schema_id,
134 Some(schema_viewpoints),
135 )?;
136
137 Ok(EvaluateInfo::TrackerSchemasFact {
138 contract: format!(
139 "{}_{}",
140 self.governance_id, self.schema_id
141 ),
142 init_state: init_state.clone(),
143 state: state.clone(),
144 payload: fact_request.payload.clone(),
145 })
146 },
147 ),
148 (
149 EventRequest::Transfer(transfer_request),
150 EvaluateData::GovTransfer { state },
151 ) => Ok(EvaluateInfo::GovTransfer {
152 new_owner: transfer_request.new_owner.clone(),
153 state: state.clone(),
154 }),
155 (
156 EventRequest::Transfer(transfer_request),
157 EvaluateData::TrackerSchemasTransfer { .. },
158 ) => {
159 let EvalWorkerContext::TrackerTransfer {
160 members,
161 creators,
162 } = worker_context
163 else {
164 return Err(EvaluatorError::InternalError(
165 "Tracker transfer evaluation context is missing"
166 .to_owned(),
167 ));
168 };
169
170 Ok(EvaluateInfo::TrackerSchemasTransfer {
171 new_owner: transfer_request.new_owner.clone(),
172 old_owner: self.event_request.signature().signer.clone(),
173 namespace: self.namespace.clone(),
174 schema_id: self.schema_id.clone(),
175 members: members.clone(),
176 creators: creators.clone(),
177 })
178 }
179 (
180 EventRequest::Confirm(confirm_request),
181 EvaluateData::GovConfirm { state },
182 ) => Ok(EvaluateInfo::GovConfirm {
183 new_owner: self.event_request.signature().signer.clone(),
184 old_owner_name: confirm_request.name_old_owner.clone(),
185 state: state.clone(),
186 }),
187 _ => Err(EvaluatorError::InvalidEventRequest(
188 "Evaluate data does not correspond to the type of request"
189 .to_string(),
190 )),
191 }
192 }
193}
194
195#[derive(
196 Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
197)]
198pub enum EvaluateData {
199 GovFact {
200 state: GovernanceData,
201 },
202 GovTransfer {
203 state: GovernanceData,
204 },
205 GovConfirm {
206 state: GovernanceData,
207 },
208 TrackerSchemasFact {
209 state: ValueWrapper,
210 },
211 TrackerSchemasTransfer {
212 state: ValueWrapper,
213 },
214}
215
216impl EvaluateData {
217 pub const fn is_gov_event(&self) -> bool {
218 match self {
219 Self::GovFact { .. }
220 | Self::GovTransfer { .. }
221 | Self::GovConfirm { .. } => true,
222 Self::TrackerSchemasFact { .. }
223 | Self::TrackerSchemasTransfer { .. } => false,
224 }
225 }
226}
227
228#[derive(
230 Debug,
231 Clone,
232 Serialize,
233 Deserialize,
234 Eq,
235 PartialEq,
236 BorshSerialize,
237 BorshDeserialize,
238)]
239pub struct SubjectContext {
240 pub subject_id: DigestIdentifier,
241 pub governance_id: DigestIdentifier,
242 pub schema_id: SchemaType,
243 pub is_owner: bool,
244 pub namespace: Namespace,
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use ave_common::identity::{
251 Signed,
252 keys::{Ed25519Signer, KeyPair},
253 };
254 use serde_json::json;
255
256 #[test]
257 fn test_build_evaluate_info_rejects_governance_fact_viewpoints() {
258 let signer = Ed25519Signer::generate().unwrap();
259 let public_key = KeyPair::Ed25519(signer.clone()).public_key();
260 let request = EventRequest::Fact(ave_common::request::FactRequest {
261 subject_id: DigestIdentifier::default(),
262 payload: ValueWrapper(json!({ "members": { "add": [] } })),
263 viewpoints: BTreeSet::from(["agua".to_owned()]),
264 });
265
266 let event_request = Signed::new(request, &signer).unwrap();
267
268 let req = EvaluationReq {
269 event_request,
270 governance_id: DigestIdentifier::default(),
271 data: EvaluateData::GovFact {
272 state: GovernanceData::new(public_key.clone()),
273 },
274 sn: 1,
275 gov_version: 0,
276 namespace: Namespace::default(),
277 schema_id: SchemaType::Governance,
278 signer: public_key,
279 signer_is_owner: true,
280 };
281
282 let error = req
283 .build_evaluate_info(&None, &EvalWorkerContext::default())
284 .unwrap_err();
285 assert!(matches!(error, EvaluatorError::InvalidEventRequest(_)));
286 }
287
288 #[test]
289 fn test_build_evaluate_info_rejects_unknown_tracker_fact_viewpoint() {
290 let signer = Ed25519Signer::generate().unwrap();
291 let public_key = KeyPair::Ed25519(signer.clone()).public_key();
292 let request = EventRequest::Fact(ave_common::request::FactRequest {
293 subject_id: DigestIdentifier::default(),
294 payload: ValueWrapper(json!({ "ModOne": { "data": 1 } })),
295 viewpoints: BTreeSet::from(["vidrio".to_owned()]),
296 });
297
298 let event_request = Signed::new(request, &signer).unwrap();
299
300 let req = EvaluationReq {
301 event_request,
302 governance_id: DigestIdentifier::default(),
303 data: EvaluateData::TrackerSchemasFact {
304 state: ValueWrapper(json!({ "one": 0, "two": 0, "three": 0 })),
305 },
306 sn: 1,
307 gov_version: 0,
308 namespace: Namespace::default(),
309 schema_id: SchemaType::Type("Example".to_owned()),
310 signer: public_key,
311 signer_is_owner: true,
312 };
313
314 let error = req
315 .build_evaluate_info(
316 &Some(ValueWrapper(json!({}))),
317 &EvalWorkerContext::TrackerFact {
318 issuers: BTreeSet::new(),
319 issuer_any: false,
320 schema_viewpoints: BTreeSet::from([
321 "agua".to_owned(),
322 "basura".to_owned(),
323 ]),
324 },
325 )
326 .unwrap_err();
327 assert!(matches!(error, EvaluatorError::InvalidEventRequest(_)));
328 }
329
330 #[test]
331 fn test_build_evaluate_info_rejects_all_viewpoints_in_tracker_fact() {
332 let signer = Ed25519Signer::generate().unwrap();
333 let public_key = KeyPair::Ed25519(signer.clone()).public_key();
334 let request = EventRequest::Fact(ave_common::request::FactRequest {
335 subject_id: DigestIdentifier::default(),
336 payload: ValueWrapper(json!({ "ModOne": { "data": 1 } })),
337 viewpoints: BTreeSet::from(["AllViewpoints".to_owned()]),
338 });
339
340 let event_request = Signed::new(request, &signer).unwrap();
341
342 let req = EvaluationReq {
343 event_request,
344 governance_id: DigestIdentifier::default(),
345 data: EvaluateData::TrackerSchemasFact {
346 state: ValueWrapper(json!({ "one": 0, "two": 0, "three": 0 })),
347 },
348 sn: 1,
349 gov_version: 0,
350 namespace: Namespace::default(),
351 schema_id: SchemaType::Type("Example".to_owned()),
352 signer: public_key,
353 signer_is_owner: true,
354 };
355
356 let error = req
357 .build_evaluate_info(
358 &Some(ValueWrapper(json!({}))),
359 &EvalWorkerContext::TrackerFact {
360 issuers: BTreeSet::new(),
361 issuer_any: false,
362 schema_viewpoints: BTreeSet::from([
363 "agua".to_owned(),
364 "basura".to_owned(),
365 ]),
366 },
367 )
368 .unwrap_err();
369 assert!(matches!(error, EvaluatorError::InvalidEventRequest(_)));
370 }
371
372 #[test]
373 fn test_build_evaluate_info_rejects_unknown_no_viewpoints_viewpoint() {
374 let signer = Ed25519Signer::generate().unwrap();
375 let public_key = KeyPair::Ed25519(signer.clone()).public_key();
376 let request = EventRequest::Fact(ave_common::request::FactRequest {
377 subject_id: DigestIdentifier::default(),
378 payload: ValueWrapper(json!({ "ModOne": { "data": 1 } })),
379 viewpoints: BTreeSet::from(["NoViewpoints".to_owned()]),
380 });
381
382 let event_request = Signed::new(request, &signer).unwrap();
383
384 let req = EvaluationReq {
385 event_request,
386 governance_id: DigestIdentifier::default(),
387 data: EvaluateData::TrackerSchemasFact {
388 state: ValueWrapper(json!({ "one": 0, "two": 0, "three": 0 })),
389 },
390 sn: 1,
391 gov_version: 0,
392 namespace: Namespace::default(),
393 schema_id: SchemaType::Type("Example".to_owned()),
394 signer: public_key,
395 signer_is_owner: true,
396 };
397
398 let error = req
399 .build_evaluate_info(
400 &Some(ValueWrapper(json!({}))),
401 &EvalWorkerContext::TrackerFact {
402 issuers: BTreeSet::new(),
403 issuer_any: false,
404 schema_viewpoints: BTreeSet::from([
405 "agua".to_owned(),
406 "basura".to_owned(),
407 ]),
408 },
409 )
410 .unwrap_err();
411 assert!(matches!(error, EvaluatorError::InvalidEventRequest(_)));
412 }
413}