Skip to main content

ave_core/evaluation/runner/
mod.rs

1use std::{
2    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
3    sync::Arc,
4    time::Instant,
5};
6
7use async_trait::async_trait;
8use ave_actors::{
9    Actor, ActorContext, ActorError, ActorPath, Handler, Message,
10    NotPersistentActor, Response,
11};
12use ave_common::{
13    Namespace, SchemaType, ValueWrapper, identity::PublicKey,
14    schematype::ReservedWords,
15};
16use borsh::{BorshDeserialize, to_vec};
17use serde::{Deserialize, Serialize};
18use serde_json::json;
19use tokio::sync::RwLock;
20use tracing::{Span, debug, error, info_span};
21use types::{ContractResult, RunnerResult};
22use wasmtime::{Module, Store};
23
24use crate::{
25    evaluation::runner::{error::RunnerError, types::EvaluateInfo},
26    governance::{
27        data::GovernanceData,
28        events::{
29            GovernanceEvent, MemberEvent, PoliciesEvent, RolesEvent,
30            SchemasEvent, gov_policie_change_is_empty,
31            gov_role_event_check_data, gov_role_event_is_empty,
32            governance_event_is_empty, member_event_is_empty,
33            policies_event_is_empty, roles_event_is_empty,
34            schema_change_is_empty, schema_id_policie_is_empty,
35            schema_id_role_check_data, schema_id_role_is_empty,
36            schema_policie_change_is_empty, schemas_event_is_empty,
37            tracker_schemas_role_event_check_data,
38            tracker_schemas_role_event_is_empty,
39        },
40        model::{CreatorWitness, RoleCreator, Schema},
41    },
42    metrics::try_core_metrics,
43    model::common::contract::{
44        MAX_FUEL, MemoryManager, WasmLimits, WasmRuntime, generate_linker,
45    },
46};
47
48type AddRemoveChangeSchema = (
49    HashSet<SchemaType>,
50    HashSet<SchemaType>,
51    HashSet<SchemaType>,
52);
53
54pub mod error;
55pub mod types;
56
57#[derive(Clone, Debug, Serialize, Deserialize)]
58pub struct Runner;
59
60impl Runner {
61    fn adapt_creator_witnesses_to_schema_viewpoints(
62        governance: &mut GovernanceData,
63        schema_id: &SchemaType,
64        schema_viewpoints: &BTreeSet<String>,
65    ) {
66        let Some(roles_schema) = governance.roles_schema.get_mut(schema_id)
67        else {
68            return;
69        };
70
71        roles_schema.creator = roles_schema
72            .creator
73            .iter()
74            .map(|creator| {
75                let mut creator = creator.clone();
76                creator.witnesses = creator
77                    .witnesses
78                    .iter()
79                    .map(|witness| {
80                        if witness.viewpoints.is_empty()
81                            || witness.viewpoints.contains(
82                                &ReservedWords::AllViewpoints.to_string(),
83                            )
84                        {
85                            witness.clone()
86                        } else {
87                            CreatorWitness {
88                                name: witness.name.clone(),
89                                viewpoints: witness
90                                    .viewpoints
91                                    .intersection(schema_viewpoints)
92                                    .cloned()
93                                    .collect(),
94                            }
95                        }
96                    })
97                    .collect();
98                creator
99            })
100            .collect::<BTreeSet<RoleCreator>>();
101    }
102
103    async fn execute_contract(
104        ctx: &ActorContext<Self>,
105        data: &EvaluateInfo,
106        is_owner: bool,
107    ) -> Result<(RunnerResult, Vec<SchemaType>), RunnerError> {
108        match data {
109            EvaluateInfo::GovFact { payload, state } => {
110                Self::execute_fact_gov(state.clone(), payload).await
111            }
112            EvaluateInfo::GovTransfer { new_owner, state } => {
113                Self::execute_transfer_gov(state, new_owner)
114            }
115            EvaluateInfo::GovConfirm {
116                new_owner,
117                old_owner_name,
118                state,
119            } => Self::execute_confirm_gov(
120                state.clone(),
121                old_owner_name.clone(),
122                new_owner,
123            ),
124            EvaluateInfo::TrackerSchemasFact {
125                contract,
126                init_state,
127                state,
128                payload,
129            } => {
130                Self::execute_fact_not_gov(
131                    ctx, state, init_state, payload, contract, is_owner,
132                )
133                .await
134            }
135            EvaluateInfo::TrackerSchemasTransfer {
136                new_owner,
137                old_owner,
138                namespace,
139                schema_id,
140                members,
141                creators,
142            } => Self::execute_transfer_not_gov(
143                members,
144                creators,
145                new_owner,
146                old_owner,
147                namespace.clone(),
148                schema_id,
149            ),
150        }
151    }
152
153    fn execute_transfer_not_gov(
154        members: &BTreeSet<PublicKey>,
155        creators: &BTreeMap<PublicKey, BTreeSet<Namespace>>,
156        new_owner: &PublicKey,
157        old_owner: &PublicKey,
158        namespace: Namespace,
159        schema_id: &SchemaType,
160    ) -> Result<(RunnerResult, Vec<SchemaType>), RunnerError> {
161        if new_owner.is_empty() {
162            return Err(RunnerError::InvalidEvent {
163                location: "execute_transfer_not_gov",
164                kind: error::InvalidEventKind::Empty {
165                    what: "new owner PublicKey".to_owned(),
166                },
167            });
168        }
169
170        if new_owner == old_owner {
171            return Err(RunnerError::InvalidEvent {
172                location: "execute_transfer_not_gov",
173                kind: error::InvalidEventKind::SameValue {
174                    what: "new owner (same as current owner)".to_owned(),
175                },
176            });
177        }
178
179        if !members.contains(new_owner) {
180            return Err(RunnerError::InvalidEvent {
181                location: "execute_transfer_not_gov",
182                kind: error::InvalidEventKind::NotMember {
183                    who: format!("new owner {}", new_owner),
184                },
185            });
186        }
187
188        if !creators.get(new_owner).is_some_and(|namespaces| {
189            namespaces.iter().any(|creator_namespace| {
190                creator_namespace.is_ancestor_or_equal_of(&namespace)
191            })
192        }) {
193            return Err(RunnerError::InvalidEvent {
194                location: "execute_transfer_not_gov",
195                kind: error::InvalidEventKind::MissingRole {
196                    who: new_owner.to_string(),
197                    role: "Creator".to_owned(),
198                    context: format!(
199                        "schema {} with namespace {}",
200                        schema_id, namespace
201                    ),
202                },
203            });
204        }
205
206        Ok((
207            RunnerResult {
208                approval_required: false,
209                final_state: ValueWrapper(json!([])),
210            },
211            vec![],
212        ))
213    }
214
215    fn execute_transfer_gov(
216        governance: &GovernanceData,
217        new_owner: &PublicKey,
218    ) -> Result<(RunnerResult, Vec<SchemaType>), RunnerError> {
219        if new_owner.is_empty() {
220            return Err(RunnerError::InvalidEvent {
221                location: "execute_transfer_gov",
222                kind: error::InvalidEventKind::Empty {
223                    what: "new owner PublicKey".to_owned(),
224                },
225            });
226        }
227
228        let Some(owner_key) =
229            governance.members.get(&ReservedWords::Owner.to_string())
230        else {
231            return Err(RunnerError::InvalidEvent {
232                location: "execute_transfer_gov",
233                kind: error::InvalidEventKind::NotFound {
234                    what: "member".to_owned(),
235                    id: ReservedWords::Owner.to_string(),
236                },
237            });
238        };
239
240        if owner_key == new_owner {
241            return Err(RunnerError::InvalidEvent {
242                location: "execute_transfer_gov",
243                kind: error::InvalidEventKind::SameValue {
244                    what: "new owner (same as current owner)".to_owned(),
245                },
246            });
247        }
248
249        if !governance.is_member(new_owner) {
250            return Err(RunnerError::InvalidEvent {
251                location: "execute_transfer_gov",
252                kind: error::InvalidEventKind::NotMember {
253                    who: format!("new owner {}", new_owner),
254                },
255            });
256        }
257
258        Ok((
259            RunnerResult {
260                approval_required: false,
261                final_state: ValueWrapper(json!([])),
262            },
263            vec![],
264        ))
265    }
266
267    fn execute_confirm_gov(
268        mut governance: GovernanceData,
269        old_owner_name: Option<String>,
270        new_owner: &PublicKey,
271    ) -> Result<(RunnerResult, Vec<SchemaType>), RunnerError> {
272        if new_owner.is_empty() {
273            return Err(RunnerError::InvalidEvent {
274                location: "execute_confirm_gov",
275                kind: error::InvalidEventKind::Empty {
276                    what: "new owner PublicKey".to_owned(),
277                },
278            });
279        }
280
281        let Some(old_owner_key) = governance
282            .members
283            .get(&ReservedWords::Owner.to_string())
284            .cloned()
285        else {
286            return Err(RunnerError::InvalidEvent {
287                location: "execute_confirm_gov",
288                kind: error::InvalidEventKind::NotFound {
289                    what: "member".to_owned(),
290                    id: ReservedWords::Owner.to_string(),
291                },
292            });
293        };
294
295        let Some(new_owner_member) = governance
296            .members
297            .iter()
298            .find(|x| x.1 == new_owner)
299            .map(|x| x.0)
300            .cloned()
301        else {
302            return Err(RunnerError::InvalidEvent {
303                location: "execute_confirm_gov",
304                kind: error::InvalidEventKind::NotMember {
305                    who: format!("new owner {}", new_owner),
306                },
307            });
308        };
309
310        governance
311            .members
312            .insert(ReservedWords::Owner.to_string(), new_owner.clone());
313        governance.members.remove(&new_owner_member);
314
315        governance.update_name_role(new_owner_member);
316
317        if let Some(old_owner_name) = old_owner_name {
318            if old_owner_name != old_owner_name.trim() {
319                return Err(RunnerError::InvalidEvent {
320                    location: "execute_confirm_gov",
321                    kind: error::InvalidEventKind::InvalidValue {
322                        field: "new name for old owner".to_owned(),
323                        reason: "cannot have leading or trailing whitespace"
324                            .to_owned(),
325                    },
326                });
327            }
328
329            if old_owner_name.is_empty() {
330                return Err(RunnerError::InvalidEvent {
331                    location: "execute_confirm_gov",
332                    kind: error::InvalidEventKind::Empty {
333                        what: "new name for old owner".to_owned(),
334                    },
335                });
336            }
337
338            if old_owner_name.len() > 100 {
339                return Err(RunnerError::InvalidEvent {
340                    location: "execute_confirm_gov",
341                    kind: error::InvalidEventKind::InvalidSize {
342                        field: "old owner new name".to_owned(),
343                        actual: old_owner_name.len(),
344                        max: 100,
345                    },
346                });
347            }
348
349            if old_owner_name == ReservedWords::Any.to_string()
350                || old_owner_name == ReservedWords::Witnesses.to_string()
351                || old_owner_name == ReservedWords::Owner.to_string()
352            {
353                return Err(RunnerError::InvalidEvent {
354                    location: "execute_confirm_gov",
355                    kind: error::InvalidEventKind::ReservedWord {
356                        field: "old owner new name".to_owned(),
357                        value: old_owner_name,
358                    },
359                });
360            }
361
362            if governance
363                .members
364                .insert(old_owner_name.clone(), old_owner_key)
365                .is_some()
366            {
367                return Err(RunnerError::InvalidEvent {
368                    location: "execute_confirm_gov",
369                    kind: error::InvalidEventKind::AlreadyExists {
370                        what: "member".to_owned(),
371                        id: old_owner_name,
372                    },
373                });
374            }
375
376            governance.roles_gov.witness.insert(old_owner_name);
377        }
378
379        let mod_governance = governance.to_value_wrapper();
380
381        Ok((
382            RunnerResult {
383                final_state: mod_governance,
384                approval_required: false,
385            },
386            vec![],
387        ))
388    }
389
390    async fn execute_fact_not_gov(
391        ctx: &ActorContext<Self>,
392        state: &ValueWrapper,
393        init_state: &ValueWrapper,
394        payload: &ValueWrapper,
395        contract_name: &str,
396        is_owner: bool,
397    ) -> Result<(RunnerResult, Vec<SchemaType>), RunnerError> {
398        let Some(wasm_runtime) = ctx
399            .system()
400            .get_helper::<Arc<WasmRuntime>>("wasm_runtime")
401            .await
402        else {
403            return Err(RunnerError::MissingHelper {
404                name: "wasm_runtime",
405            });
406        };
407
408        let Some(contracts) = ctx
409            .system()
410            .get_helper::<Arc<RwLock<HashMap<String, Arc<Module>>>>>(
411                "contracts",
412            )
413            .await
414        else {
415            return Err(RunnerError::MissingHelper { name: "contracts" });
416        };
417
418        let module = {
419            let contracts = contracts.read().await;
420            let Some(module) = contracts.get(contract_name) else {
421                return Err(RunnerError::ContractNotFound {
422                    name: contract_name.to_owned(),
423                });
424            };
425            let result = module.clone();
426            drop(contracts);
427            result
428        };
429
430        let (context, state_ptr, init_state_ptr, event_ptr) =
431            Self::generate_context(
432                state,
433                init_state,
434                payload,
435                &wasm_runtime.limits,
436            )?;
437
438        let mut store = Store::new(&wasm_runtime.engine, context);
439
440        // Limit WASM linear memory and table growth to prevent resource exhaustion.
441        store.limiter(|data| &mut data.store_limits);
442
443        store
444            .set_fuel(MAX_FUEL)
445            .map_err(|e| RunnerError::WasmError {
446                operation: "set fuel",
447                details: e.to_string(),
448            })?;
449
450        let linker = generate_linker(&wasm_runtime.engine)?;
451
452        let instance =
453            linker.instantiate(&mut store, &module).map_err(|e| {
454                RunnerError::WasmError {
455                    operation: "instantiate",
456                    details: e.to_string(),
457                }
458            })?;
459
460        let contract_entrypoint = instance
461            .get_typed_func::<(u32, u32, u32, u32), u32>(
462                &mut store,
463                "main_function",
464            )
465            .map_err(|e| RunnerError::WasmError {
466                operation: "get entrypoint main_function",
467                details: e.to_string(),
468            })?;
469
470        let result_ptr = contract_entrypoint
471            .call(
472                &mut store,
473                (
474                    state_ptr,
475                    init_state_ptr,
476                    event_ptr,
477                    if is_owner { 1 } else { 0 },
478                ),
479            )
480            .map_err(|e| RunnerError::WasmError {
481                operation: "call entrypoint",
482                details: e.to_string(),
483            })?;
484
485        let result = Self::get_result(&store, result_ptr)?;
486        Ok((
487            RunnerResult {
488                approval_required: false,
489                final_state: result.final_state,
490            },
491            vec![],
492        ))
493    }
494
495    async fn execute_fact_gov(
496        mut governance: GovernanceData,
497        event: &ValueWrapper,
498    ) -> Result<(RunnerResult, Vec<SchemaType>), RunnerError> {
499        let event: GovernanceEvent = serde_json::from_value(event.0.clone())
500            .map_err(|e| RunnerError::InvalidEvent {
501                location: "execute_fact_gov",
502                kind: error::InvalidEventKind::Other {
503                    msg: format!(
504                        "failed to deserialize GovernanceEvent: {}",
505                        e
506                    ),
507                },
508            })?;
509
510        if governance_event_is_empty(&event) {
511            return Err(RunnerError::InvalidEvent {
512                location: "execute_fact_gov",
513                kind: error::InvalidEventKind::Empty {
514                    what: "GovernanceEvent".to_owned(),
515                },
516            });
517        }
518
519        if let Some(member_event) = event.members {
520            let remove = Self::check_members(&member_event, &mut governance)?;
521            if !remove.is_empty() {
522                governance.remove_member_role(&remove);
523            }
524        }
525
526        let add_change_schemas = if let Some(schema_event) = event.schemas {
527            let (add_schemas, remove_schemas, change_schemas) =
528                Self::apply_schemas(&schema_event, &mut governance)?;
529            governance.remove_schema(remove_schemas);
530            governance.add_schema(add_schemas.clone());
531
532            add_schemas
533                .union(&change_schemas)
534                .cloned()
535                .collect::<Vec<SchemaType>>()
536        } else {
537            vec![]
538        };
539
540        if let Some(roles_event) = event.roles {
541            Self::check_roles(roles_event, &mut governance)?;
542        }
543
544        if let Some(policies_event) = event.policies {
545            Self::check_policies(policies_event, &mut governance)?;
546        }
547
548        if !governance.check_basic_gov() {
549            return Err(RunnerError::InvalidEvent {
550                location: "execute_fact_gov",
551                kind: error::InvalidEventKind::CannotModify {
552                    what: "governance owner basic roles".to_owned(),
553                    reason: "these roles are protected".to_owned(),
554                },
555            });
556        }
557
558        let mod_governance = governance.to_value_wrapper();
559
560        Ok((
561            RunnerResult {
562                final_state: mod_governance,
563                approval_required: true,
564            },
565            add_change_schemas,
566        ))
567    }
568
569    fn check_policies(
570        policies_event: PoliciesEvent,
571        governance: &mut GovernanceData,
572    ) -> Result<(), RunnerError> {
573        if policies_event_is_empty(&policies_event) {
574            return Err(RunnerError::InvalidEvent {
575                location: "check_policies",
576                kind: error::InvalidEventKind::Empty {
577                    what: "PoliciesEvent".to_owned(),
578                },
579            });
580        }
581
582        if let Some(gov) = policies_event.governance {
583            if gov_policie_change_is_empty(&gov.change) {
584                return Err(RunnerError::InvalidEvent {
585                    location: "check_policies",
586                    kind: error::InvalidEventKind::Empty {
587                        what: "GovPolicieEvent change".to_owned(),
588                    },
589                });
590            }
591
592            let mut new_policies = governance.policies_gov.clone();
593
594            if let Some(approve) = gov.change.approve {
595                approve.check_values().map_err(|e| {
596                    RunnerError::InvalidEvent {
597                        location: "check_policies",
598                        kind: error::InvalidEventKind::InvalidQuorum {
599                            context: "governance approve policy".to_owned(),
600                            details: e,
601                        },
602                    }
603                })?;
604                if approve == new_policies.approve {
605                    return Err(RunnerError::InvalidEvent {
606                        location: "check_policies",
607                        kind: error::InvalidEventKind::SameValue {
608                            what: "governance approve policy".to_owned(),
609                        },
610                    });
611                }
612                new_policies.approve = approve;
613            }
614
615            if let Some(evaluate) = gov.change.evaluate {
616                evaluate.check_values().map_err(|e| {
617                    RunnerError::InvalidEvent {
618                        location: "check_policies",
619                        kind: error::InvalidEventKind::InvalidQuorum {
620                            context: "governance evaluate policy".to_owned(),
621                            details: e,
622                        },
623                    }
624                })?;
625                if evaluate == new_policies.evaluate {
626                    return Err(RunnerError::InvalidEvent {
627                        location: "check_policies",
628                        kind: error::InvalidEventKind::SameValue {
629                            what: "governance evaluate policy".to_owned(),
630                        },
631                    });
632                }
633                new_policies.evaluate = evaluate;
634            }
635
636            if let Some(validate) = gov.change.validate {
637                validate.check_values().map_err(|e| {
638                    RunnerError::InvalidEvent {
639                        location: "check_policies",
640                        kind: error::InvalidEventKind::InvalidQuorum {
641                            context: "governance validate policy".to_owned(),
642                            details: e,
643                        },
644                    }
645                })?;
646                if validate == new_policies.validate {
647                    return Err(RunnerError::InvalidEvent {
648                        location: "check_policies",
649                        kind: error::InvalidEventKind::SameValue {
650                            what: "governance validate policy".to_owned(),
651                        },
652                    });
653                }
654                new_policies.validate = validate;
655            }
656
657            governance.policies_gov = new_policies;
658        }
659
660        if let Some(schemas) = policies_event.schema {
661            if schemas.is_empty() {
662                return Err(RunnerError::InvalidEvent {
663                    location: "check_policies",
664                    kind: error::InvalidEventKind::Empty {
665                        what: "SchemaIdPolicie vec".to_owned(),
666                    },
667                });
668            }
669
670            let mut new_policies = governance.policies_schema.clone();
671            let mut seen_schema_ids: HashSet<SchemaType> = HashSet::new();
672
673            for schema in schemas {
674                if !seen_schema_ids.insert(schema.schema_id.clone()) {
675                    return Err(RunnerError::InvalidEvent {
676                        location: "check_policies",
677                        kind: error::InvalidEventKind::Duplicate {
678                            what: "schema".to_owned(),
679                            id: schema.schema_id.to_string(),
680                        },
681                    });
682                }
683
684                if schema_id_policie_is_empty(&schema) {
685                    return Err(RunnerError::InvalidEvent {
686                        location: "check_policies",
687                        kind: error::InvalidEventKind::Empty {
688                            what: "SchemaIdPolicie".to_owned(),
689                        },
690                    });
691                }
692
693                let Some(policies_schema) =
694                    new_policies.get_mut(&schema.schema_id)
695                else {
696                    return Err(RunnerError::InvalidEvent {
697                        location: "check_policies",
698                        kind: error::InvalidEventKind::NotSchema {
699                            id: schema.schema_id.to_string(),
700                        },
701                    });
702                };
703
704                if schema_policie_change_is_empty(&schema.change) {
705                    return Err(RunnerError::InvalidEvent {
706                        location: "check_policies",
707                        kind: error::InvalidEventKind::Empty {
708                            what: "SchemaIdPolicie change".to_owned(),
709                        },
710                    });
711                }
712
713                if let Some(evaluate) = schema.change.evaluate {
714                    evaluate.check_values().map_err(|e| {
715                        RunnerError::InvalidEvent {
716                            location: "check_policies",
717                            kind: error::InvalidEventKind::InvalidQuorum {
718                                context: format!(
719                                    "schema {} evaluate policy",
720                                    schema.schema_id
721                                ),
722                                details: e,
723                            },
724                        }
725                    })?;
726                    if evaluate == policies_schema.evaluate {
727                        return Err(RunnerError::InvalidEvent {
728                            location: "check_policies",
729                            kind: error::InvalidEventKind::SameValue {
730                                what: format!(
731                                    "schema {} evaluate policy",
732                                    schema.schema_id
733                                ),
734                            },
735                        });
736                    }
737                    policies_schema.evaluate = evaluate;
738                }
739
740                if let Some(validate) = schema.change.validate {
741                    validate.check_values().map_err(|e| {
742                        RunnerError::InvalidEvent {
743                            location: "check_policies",
744                            kind: error::InvalidEventKind::InvalidQuorum {
745                                context: format!(
746                                    "schema {} validate policy",
747                                    schema.schema_id
748                                ),
749                                details: e,
750                            },
751                        }
752                    })?;
753                    if validate == policies_schema.validate {
754                        return Err(RunnerError::InvalidEvent {
755                            location: "check_policies",
756                            kind: error::InvalidEventKind::SameValue {
757                                what: format!(
758                                    "schema {} validate policy",
759                                    schema.schema_id
760                                ),
761                            },
762                        });
763                    }
764                    policies_schema.validate = validate;
765                }
766            }
767
768            governance.policies_schema = new_policies;
769        }
770
771        Ok(())
772    }
773
774    fn check_roles(
775        roles_event: RolesEvent,
776        governance: &mut GovernanceData,
777    ) -> Result<(), RunnerError> {
778        if roles_event_is_empty(&roles_event) {
779            return Err(RunnerError::InvalidEvent {
780                location: "check_roles",
781                kind: error::InvalidEventKind::Empty {
782                    what: "RolesEvent".to_owned(),
783                },
784            });
785        }
786
787        if let Some(gov) = roles_event.governance {
788            if gov_role_event_is_empty(&gov) {
789                return Err(RunnerError::InvalidEvent {
790                    location: "check_roles",
791                    kind: error::InvalidEventKind::Empty {
792                        what: "GovRoleEvent".to_owned(),
793                    },
794                });
795            }
796
797            let mut new_roles = governance.roles_gov.clone();
798
799            gov_role_event_check_data(&gov, governance, &mut new_roles)?;
800
801            governance.roles_gov = new_roles;
802        }
803
804        if let Some(schemas) = roles_event.schema {
805            if schemas.is_empty() {
806                return Err(RunnerError::InvalidEvent {
807                    location: "check_roles",
808                    kind: error::InvalidEventKind::Empty {
809                        what: "SchemaIdRole vec".to_owned(),
810                    },
811                });
812            }
813
814            let mut new_roles = governance.roles_schema.clone();
815            let mut seen_schema_ids: HashSet<SchemaType> = HashSet::new();
816
817            for schema in schemas {
818                if !seen_schema_ids.insert(schema.schema_id.clone()) {
819                    return Err(RunnerError::InvalidEvent {
820                        location: "check_roles",
821                        kind: error::InvalidEventKind::Duplicate {
822                            what: "schema".to_owned(),
823                            id: schema.schema_id.to_string(),
824                        },
825                    });
826                }
827
828                if schema_id_role_is_empty(&schema) {
829                    return Err(RunnerError::InvalidEvent {
830                        location: "check_roles",
831                        kind: error::InvalidEventKind::Empty {
832                            what: "SchemaIdRole".to_owned(),
833                        },
834                    });
835                }
836
837                let Some(roles_schema) = new_roles.get_mut(&schema.schema_id)
838                else {
839                    return Err(RunnerError::InvalidEvent {
840                        location: "check_roles",
841                        kind: error::InvalidEventKind::NotSchema {
842                            id: schema.schema_id.to_string(),
843                        },
844                    });
845                };
846
847                schema_id_role_check_data(
848                    &schema,
849                    governance,
850                    roles_schema,
851                    &schema.schema_id,
852                )?;
853            }
854
855            governance.roles_schema = new_roles;
856        }
857
858        if let Some(tracker_schemas) = roles_event.tracker_schemas {
859            if tracker_schemas_role_event_is_empty(&tracker_schemas) {
860                return Err(RunnerError::InvalidEvent {
861                    location: "check_roles",
862                    kind: error::InvalidEventKind::Empty {
863                        what: "TrackerSchemasRoleEvent".to_owned(),
864                    },
865                });
866            }
867
868            let new_roles = governance.roles_tracker_schemas.clone();
869
870            let new_roles = tracker_schemas_role_event_check_data(
871                &tracker_schemas,
872                governance,
873                new_roles,
874                &SchemaType::TrackerSchemas,
875            )?;
876
877            governance.roles_tracker_schemas = new_roles;
878        }
879
880        Ok(())
881    }
882
883    fn apply_schemas(
884        schema_event: &SchemasEvent,
885        governance: &mut GovernanceData,
886    ) -> Result<AddRemoveChangeSchema, RunnerError> {
887        fn validate_viewpoints(
888            schema_id: &SchemaType,
889            viewpoints: &[String],
890            field: &str,
891        ) -> Result<BTreeSet<String>, RunnerError> {
892            let mut unique = BTreeSet::new();
893
894            for viewpoint in viewpoints {
895                if viewpoint != viewpoint.trim() {
896                    return Err(RunnerError::InvalidEvent {
897                        location: "check_schemas",
898                        kind: error::InvalidEventKind::InvalidValue {
899                            field: format!("{field} for schema {}", schema_id),
900                            reason:
901                                "cannot have leading or trailing whitespace"
902                                    .to_owned(),
903                        },
904                    });
905                }
906
907                if viewpoint.is_empty() {
908                    return Err(RunnerError::InvalidEvent {
909                        location: "check_schemas",
910                        kind: error::InvalidEventKind::Empty {
911                            what: format!("{field} for schema {}", schema_id),
912                        },
913                    });
914                }
915
916                if viewpoint == &ReservedWords::AllViewpoints.to_string() {
917                    return Err(RunnerError::InvalidEvent {
918                        location: "check_schemas",
919                        kind: error::InvalidEventKind::ReservedWord {
920                            field: format!("{field} for schema {}", schema_id),
921                            value: ReservedWords::AllViewpoints.to_string(),
922                        },
923                    });
924                }
925
926                if viewpoint.len() > 100 {
927                    return Err(RunnerError::InvalidEvent {
928                        location: "check_schemas",
929                        kind: error::InvalidEventKind::InvalidSize {
930                            field: format!("{field} for schema {}", schema_id),
931                            actual: viewpoint.len(),
932                            max: 100,
933                        },
934                    });
935                }
936
937                if !unique.insert(viewpoint.clone()) {
938                    return Err(RunnerError::InvalidEvent {
939                        location: "check_schemas",
940                        kind: error::InvalidEventKind::Duplicate {
941                            what: field.to_owned(),
942                            id: viewpoint.clone(),
943                        },
944                    });
945                }
946            }
947
948            Ok(unique)
949        }
950
951        if schemas_event_is_empty(schema_event) {
952            return Err(RunnerError::InvalidEvent {
953                location: "check_schemas",
954                kind: error::InvalidEventKind::Empty {
955                    what: "SchemasEvent".to_owned(),
956                },
957            });
958        }
959
960        if let (Some(add), Some(remove)) =
961            (&schema_event.add, &schema_event.remove)
962        {
963            let add_ids: HashSet<&SchemaType> =
964                add.iter().map(|s| &s.id).collect();
965            for r in remove {
966                if add_ids.contains(r) {
967                    return Err(RunnerError::InvalidEvent {
968                        location: "check_schemas",
969                        kind: error::InvalidEventKind::CannotModify {
970                            what: r.to_string(),
971                            reason: "cannot add and remove the same schema in the same event"
972                                .to_owned(),
973                        },
974                    });
975                }
976            }
977        }
978
979        if let (Some(add), Some(change)) =
980            (&schema_event.add, &schema_event.change)
981        {
982            let add_ids: HashSet<&SchemaType> =
983                add.iter().map(|s| &s.id).collect();
984            for c in change {
985                if add_ids.contains(&c.actual_id) {
986                    return Err(RunnerError::InvalidEvent {
987                        location: "check_schemas",
988                        kind: error::InvalidEventKind::CannotModify {
989                            what: c.actual_id.to_string(),
990                            reason: "cannot add and change the same schema in the same event"
991                                .to_owned(),
992                        },
993                    });
994                }
995            }
996        }
997
998        let mut remove_schemas = HashSet::new();
999        let mut add_schemas = HashSet::new();
1000        let mut change_schemas = HashSet::new();
1001
1002        let mut new_schemas = governance.schemas.clone();
1003
1004        if let Some(add) = schema_event.add.clone() {
1005            if add.is_empty() {
1006                return Err(RunnerError::InvalidEvent {
1007                    location: "check_schemas",
1008                    kind: error::InvalidEventKind::Empty {
1009                        what: "SchemaAdd vec".to_owned(),
1010                    },
1011                });
1012            }
1013
1014            for new_schema in add {
1015                if !new_schema.id.is_valid() {
1016                    return Err(RunnerError::InvalidEvent {
1017                        location: "check_schemas",
1018                        kind: error::InvalidEventKind::Empty {
1019                            what: "schema id".to_owned(),
1020                        },
1021                    });
1022                }
1023
1024                let id_str = new_schema.id.to_string();
1025                if id_str != id_str.trim() {
1026                    return Err(RunnerError::InvalidEvent {
1027                        location: "check_schemas",
1028                        kind: error::InvalidEventKind::InvalidValue {
1029                            field: "schema id".to_owned(),
1030                            reason:
1031                                "cannot have leading or trailing whitespace"
1032                                    .to_owned(),
1033                        },
1034                    });
1035                }
1036
1037                if new_schema.id.len() > 100 {
1038                    return Err(RunnerError::InvalidEvent {
1039                        location: "check_schemas",
1040                        kind: error::InvalidEventKind::InvalidSize {
1041                            field: "schema id".to_owned(),
1042                            actual: new_schema.id.len(),
1043                            max: 100,
1044                        },
1045                    });
1046                }
1047
1048                if new_schema.id == SchemaType::TrackerSchemas {
1049                    return Err(RunnerError::InvalidEvent {
1050                        location: "check_schemas",
1051                        kind: error::InvalidEventKind::ReservedWord {
1052                            field: "schema id".to_owned(),
1053                            value: ReservedWords::TrackerSchemas.to_string(),
1054                        },
1055                    });
1056                }
1057
1058                if new_schema.id == SchemaType::Governance {
1059                    return Err(RunnerError::InvalidEvent {
1060                        location: "check_schemas",
1061                        kind: error::InvalidEventKind::ReservedWord {
1062                            field: "schema id".to_owned(),
1063                            value: ReservedWords::Governance.to_string(),
1064                        },
1065                    });
1066                }
1067
1068                if new_schema.contract.is_empty() {
1069                    return Err(RunnerError::InvalidEvent {
1070                        location: "check_schemas",
1071                        kind: error::InvalidEventKind::Empty {
1072                            what: format!(
1073                                "contract for schema {}",
1074                                new_schema.id
1075                            ),
1076                        },
1077                    });
1078                }
1079
1080                if new_schema.contract.contains(' ') {
1081                    return Err(RunnerError::InvalidEvent {
1082                        location: "check_schemas",
1083                        kind: error::InvalidEventKind::InvalidValue {
1084                            field: format!(
1085                                "contract for schema {}",
1086                                new_schema.id
1087                            ),
1088                            reason: "cannot contain blank spaces".to_owned(),
1089                        },
1090                    });
1091                }
1092
1093                if new_schemas
1094                    .insert(
1095                        new_schema.id.clone(),
1096                        Schema {
1097                            initial_value: ValueWrapper(
1098                                new_schema.initial_value,
1099                            ),
1100                            contract: new_schema.contract,
1101                            viewpoints: validate_viewpoints(
1102                                &new_schema.id,
1103                                &new_schema.viewpoints,
1104                                "viewpoints",
1105                            )?,
1106                        },
1107                    )
1108                    .is_some()
1109                {
1110                    return Err(RunnerError::InvalidEvent {
1111                        location: "check_schemas",
1112                        kind: error::InvalidEventKind::AlreadyExists {
1113                            what: "schema".to_owned(),
1114                            id: new_schema.id.to_string(),
1115                        },
1116                    });
1117                }
1118
1119                add_schemas.insert(new_schema.id);
1120            }
1121        }
1122
1123        if let Some(remove) = schema_event.remove.clone() {
1124            if remove.is_empty() {
1125                return Err(RunnerError::InvalidEvent {
1126                    location: "check_schemas",
1127                    kind: error::InvalidEventKind::Empty {
1128                        what: "remove schema vec".to_owned(),
1129                    },
1130                });
1131            }
1132
1133            for remove_schema in &remove {
1134                if !remove_schema.is_valid() {
1135                    return Err(RunnerError::InvalidEvent {
1136                        location: "check_schemas",
1137                        kind: error::InvalidEventKind::Empty {
1138                            what: "schema id to remove".to_owned(),
1139                        },
1140                    });
1141                }
1142
1143                if new_schemas.remove(remove_schema).is_none() {
1144                    return Err(RunnerError::InvalidEvent {
1145                        location: "check_schemas",
1146                        kind: error::InvalidEventKind::CannotRemove {
1147                            what: remove_schema.to_string(),
1148                            reason: "not a schema".to_owned(),
1149                        },
1150                    });
1151                }
1152            }
1153
1154            remove_schemas = remove;
1155        }
1156
1157        if let Some(change) = schema_event.change.clone() {
1158            if change.is_empty() {
1159                return Err(RunnerError::InvalidEvent {
1160                    location: "check_schemas",
1161                    kind: error::InvalidEventKind::Empty {
1162                        what: "SchemaChange vec".to_owned(),
1163                    },
1164                });
1165            }
1166
1167            let mut seen_change_ids: HashSet<SchemaType> = HashSet::new();
1168
1169            for change_schema in change {
1170                if !seen_change_ids.insert(change_schema.actual_id.clone()) {
1171                    return Err(RunnerError::InvalidEvent {
1172                        location: "check_schemas",
1173                        kind: error::InvalidEventKind::Duplicate {
1174                            what: "schema in change".to_owned(),
1175                            id: change_schema.actual_id.to_string(),
1176                        },
1177                    });
1178                }
1179
1180                if schema_change_is_empty(&change_schema) {
1181                    return Err(RunnerError::InvalidEvent {
1182                        location: "check_schemas",
1183                        kind: error::InvalidEventKind::Empty {
1184                            what: format!(
1185                                "change data for schema {}",
1186                                change_schema.actual_id
1187                            ),
1188                        },
1189                    });
1190                }
1191
1192                let Some(schema_data) =
1193                    new_schemas.get_mut(&change_schema.actual_id)
1194                else {
1195                    return Err(RunnerError::InvalidEvent {
1196                        location: "check_schemas",
1197                        kind: error::InvalidEventKind::NotSchema {
1198                            id: change_schema.actual_id.to_string(),
1199                        },
1200                    });
1201                };
1202
1203                if let Some(new_contract) = change_schema.new_contract {
1204                    if new_contract.is_empty() {
1205                        return Err(RunnerError::InvalidEvent {
1206                            location: "check_schemas",
1207                            kind: error::InvalidEventKind::Empty {
1208                                what: format!(
1209                                    "new contract for schema {}",
1210                                    change_schema.actual_id
1211                                ),
1212                            },
1213                        });
1214                    }
1215
1216                    if new_contract.contains(' ') {
1217                        return Err(RunnerError::InvalidEvent {
1218                            location: "check_schemas",
1219                            kind: error::InvalidEventKind::InvalidValue {
1220                                field: format!(
1221                                    "new contract for schema {}",
1222                                    change_schema.actual_id
1223                                ),
1224                                reason: "cannot contain blank spaces"
1225                                    .to_owned(),
1226                            },
1227                        });
1228                    }
1229
1230                    if new_contract == schema_data.contract {
1231                        return Err(RunnerError::InvalidEvent {
1232                            location: "check_schemas",
1233                            kind: error::InvalidEventKind::SameValue {
1234                                what: format!(
1235                                    "contract for schema {}",
1236                                    change_schema.actual_id
1237                                ),
1238                            },
1239                        });
1240                    }
1241
1242                    schema_data.contract = new_contract;
1243                }
1244
1245                if let Some(init_value) = change_schema.new_initial_value {
1246                    if init_value == schema_data.initial_value.0 {
1247                        return Err(RunnerError::InvalidEvent {
1248                            location: "check_schemas",
1249                            kind: error::InvalidEventKind::SameValue {
1250                                what: format!(
1251                                    "initial_value for schema {}",
1252                                    change_schema.actual_id
1253                                ),
1254                            },
1255                        });
1256                    }
1257
1258                    schema_data.initial_value = ValueWrapper(init_value);
1259                }
1260
1261                if let Some(new_viewpoints) = change_schema.new_viewpoints {
1262                    let new_viewpoints = validate_viewpoints(
1263                        &change_schema.actual_id,
1264                        &new_viewpoints,
1265                        "new viewpoints",
1266                    )?;
1267
1268                    if new_viewpoints == schema_data.viewpoints {
1269                        return Err(RunnerError::InvalidEvent {
1270                            location: "check_schemas",
1271                            kind: error::InvalidEventKind::SameValue {
1272                                what: format!(
1273                                    "viewpoints for schema {}",
1274                                    change_schema.actual_id
1275                                ),
1276                            },
1277                        });
1278                    }
1279
1280                    schema_data.viewpoints = new_viewpoints;
1281                    Self::adapt_creator_witnesses_to_schema_viewpoints(
1282                        governance,
1283                        &change_schema.actual_id,
1284                        &schema_data.viewpoints,
1285                    );
1286                }
1287
1288                change_schemas.insert(change_schema.actual_id);
1289            }
1290        }
1291
1292        governance.schemas = new_schemas;
1293        Ok((add_schemas, remove_schemas, change_schemas))
1294    }
1295
1296    fn check_members(
1297        member_event: &MemberEvent,
1298        governance: &mut GovernanceData,
1299    ) -> Result<Vec<String>, RunnerError> {
1300        if member_event_is_empty(member_event) {
1301            return Err(RunnerError::InvalidEvent {
1302                location: "check_members",
1303                kind: error::InvalidEventKind::Empty {
1304                    what: "MemberEvent".to_owned(),
1305                },
1306            });
1307        }
1308
1309        if let (Some(add), Some(remove)) =
1310            (&member_event.add, &member_event.remove)
1311        {
1312            let add_names: HashSet<String> =
1313                add.iter().map(|m| m.name.trim().to_owned()).collect();
1314            for r in remove {
1315                if add_names.contains(r.as_str()) {
1316                    return Err(RunnerError::InvalidEvent {
1317                        location: "check_members",
1318                        kind: error::InvalidEventKind::CannotModify {
1319                            what: r.clone(),
1320                            reason: "cannot add and remove the same member in the same event".to_owned(),
1321                        },
1322                    });
1323                }
1324            }
1325        }
1326
1327        let mut new_members = governance.members.clone();
1328
1329        if let Some(add) = member_event.add.clone() {
1330            if add.is_empty() {
1331                return Err(RunnerError::InvalidEvent {
1332                    location: "check_members",
1333                    kind: error::InvalidEventKind::Empty {
1334                        what: "NewMember vec".to_owned(),
1335                    },
1336                });
1337            }
1338
1339            for new_member in add {
1340                if new_member.name != new_member.name.trim() {
1341                    return Err(RunnerError::InvalidEvent {
1342                        location: "check_members",
1343                        kind: error::InvalidEventKind::InvalidValue {
1344                            field: "member name".to_owned(),
1345                            reason:
1346                                "cannot have leading or trailing whitespace"
1347                                    .to_owned(),
1348                        },
1349                    });
1350                }
1351
1352                if new_member.name.is_empty() {
1353                    return Err(RunnerError::InvalidEvent {
1354                        location: "check_members",
1355                        kind: error::InvalidEventKind::Empty {
1356                            what: "member name".to_owned(),
1357                        },
1358                    });
1359                }
1360
1361                if new_member.name.len() > 100 {
1362                    return Err(RunnerError::InvalidEvent {
1363                        location: "check_members",
1364                        kind: error::InvalidEventKind::InvalidSize {
1365                            field: "member name".to_owned(),
1366                            actual: new_member.name.len(),
1367                            max: 100,
1368                        },
1369                    });
1370                }
1371
1372                if new_member.name == ReservedWords::Any.to_string() {
1373                    return Err(RunnerError::InvalidEvent {
1374                        location: "check_members",
1375                        kind: error::InvalidEventKind::ReservedWord {
1376                            field: "member name".to_owned(),
1377                            value: ReservedWords::Any.to_string(),
1378                        },
1379                    });
1380                }
1381
1382                if new_member.name == ReservedWords::Witnesses.to_string() {
1383                    return Err(RunnerError::InvalidEvent {
1384                        location: "check_members",
1385                        kind: error::InvalidEventKind::ReservedWord {
1386                            field: "member name".to_owned(),
1387                            value: ReservedWords::Witnesses.to_string(),
1388                        },
1389                    });
1390                }
1391
1392                if new_member.name == ReservedWords::Owner.to_string() {
1393                    return Err(RunnerError::InvalidEvent {
1394                        location: "check_members",
1395                        kind: error::InvalidEventKind::ReservedWord {
1396                            field: "member name".to_owned(),
1397                            value: ReservedWords::Owner.to_string(),
1398                        },
1399                    });
1400                }
1401
1402                if new_member.key.is_empty() {
1403                    return Err(RunnerError::InvalidEvent {
1404                        location: "check_members",
1405                        kind: error::InvalidEventKind::Empty {
1406                            what: format!("key for member {}", new_member.name),
1407                        },
1408                    });
1409                }
1410
1411                if new_members
1412                    .insert(new_member.name.clone(), new_member.key)
1413                    .is_some()
1414                {
1415                    return Err(RunnerError::InvalidEvent {
1416                        location: "check_members",
1417                        kind: error::InvalidEventKind::AlreadyExists {
1418                            what: "member".to_owned(),
1419                            id: new_member.name,
1420                        },
1421                    });
1422                }
1423            }
1424        }
1425
1426        let remove_members = if let Some(remove) = member_event.remove.clone() {
1427            if remove.is_empty() {
1428                return Err(RunnerError::InvalidEvent {
1429                    location: "check_members",
1430                    kind: error::InvalidEventKind::Empty {
1431                        what: "remove member vec".to_owned(),
1432                    },
1433                });
1434            }
1435
1436            for remove_member in remove.clone() {
1437                if remove_member != remove_member.trim() {
1438                    return Err(RunnerError::InvalidEvent {
1439                        location: "check_members",
1440                        kind: error::InvalidEventKind::InvalidValue {
1441                            field: "member name to remove".to_owned(),
1442                            reason:
1443                                "cannot have leading or trailing whitespace"
1444                                    .to_owned(),
1445                        },
1446                    });
1447                }
1448
1449                if remove_member.is_empty() {
1450                    return Err(RunnerError::InvalidEvent {
1451                        location: "check_members",
1452                        kind: error::InvalidEventKind::Empty {
1453                            what: "member name to remove".to_owned(),
1454                        },
1455                    });
1456                }
1457
1458                if remove_member == ReservedWords::Owner.to_string() {
1459                    return Err(RunnerError::InvalidEvent {
1460                        location: "check_members",
1461                        kind: error::InvalidEventKind::CannotRemove {
1462                            what: ReservedWords::Owner.to_string(),
1463                            reason: "governance owner cannot be removed"
1464                                .to_owned(),
1465                        },
1466                    });
1467                }
1468
1469                if new_members.remove(&remove_member).is_none() {
1470                    return Err(RunnerError::InvalidEvent {
1471                        location: "check_members",
1472                        kind: error::InvalidEventKind::CannotRemove {
1473                            what: remove_member,
1474                            reason: "not a member".to_owned(),
1475                        },
1476                    });
1477                }
1478            }
1479
1480            remove.iter().cloned().collect::<Vec<String>>()
1481        } else {
1482            vec![]
1483        };
1484
1485        let members_name: HashSet<String> =
1486            new_members.keys().cloned().collect();
1487        let members_value: HashSet<PublicKey> =
1488            new_members.values().cloned().collect();
1489
1490        if new_members.contains_key(&ReservedWords::Any.to_string()) {
1491            return Err(RunnerError::InvalidEvent {
1492                location: "check_members",
1493                kind: error::InvalidEventKind::ReservedWord {
1494                    field: "member name".to_owned(),
1495                    value: ReservedWords::Any.to_string(),
1496                },
1497            });
1498        }
1499
1500        if new_members.contains_key(&ReservedWords::Witnesses.to_string()) {
1501            return Err(RunnerError::InvalidEvent {
1502                location: "check_members",
1503                kind: error::InvalidEventKind::ReservedWord {
1504                    field: "member name".to_owned(),
1505                    value: ReservedWords::Witnesses.to_string(),
1506                },
1507            });
1508        }
1509
1510        if members_name.len() != members_value.len() {
1511            return Err(RunnerError::InvalidEvent {
1512                location: "check_members",
1513                kind: error::InvalidEventKind::Duplicate {
1514                    what: "member key".to_owned(),
1515                    id: "multiple members share the same key".to_owned(),
1516                },
1517            });
1518        }
1519
1520        governance.members = new_members;
1521
1522        Ok(remove_members)
1523    }
1524
1525    fn generate_context(
1526        state: &ValueWrapper,
1527        init_state: &ValueWrapper,
1528        event: &ValueWrapper,
1529        limits: &WasmLimits,
1530    ) -> Result<(MemoryManager, u32, u32, u32), RunnerError> {
1531        let mut context = MemoryManager::from_limits(limits);
1532
1533        let state_bytes =
1534            to_vec(&state).map_err(|e| RunnerError::SerializationError {
1535                context: "serialize state",
1536                details: e.to_string(),
1537            })?;
1538        let state_ptr = context.add_data_raw(&state_bytes)?;
1539
1540        let init_state_bytes = to_vec(&init_state).map_err(|e| {
1541            RunnerError::SerializationError {
1542                context: "serialize init_state",
1543                details: e.to_string(),
1544            }
1545        })?;
1546        let init_state_ptr = context.add_data_raw(&init_state_bytes)?;
1547
1548        let event_bytes =
1549            to_vec(&event).map_err(|e| RunnerError::SerializationError {
1550                context: "serialize event",
1551                details: e.to_string(),
1552            })?;
1553        let event_ptr = context.add_data_raw(&event_bytes)?;
1554
1555        Ok((
1556            context,
1557            state_ptr as u32,
1558            init_state_ptr as u32,
1559            event_ptr as u32,
1560        ))
1561    }
1562
1563    fn get_result(
1564        store: &Store<MemoryManager>,
1565        pointer: u32,
1566    ) -> Result<ContractResult, RunnerError> {
1567        let bytes = store.data().read_data(pointer as usize)?;
1568
1569        let contract_result: ContractResult =
1570            BorshDeserialize::try_from_slice(bytes).map_err(|e| {
1571                RunnerError::SerializationError {
1572                    context: "deserialize ContractResult",
1573                    details: e.to_string(),
1574                }
1575            })?;
1576
1577        if contract_result.success {
1578            Ok(contract_result)
1579        } else {
1580            Err(RunnerError::ContractFailed {
1581                details: format!(
1582                    "Contract execution in running was not successful: {}",
1583                    contract_result.error
1584                ),
1585            })
1586        }
1587    }
1588}
1589
1590#[derive(Debug, Clone)]
1591pub struct RunnerMessage {
1592    pub data: EvaluateInfo,
1593    pub is_owner: bool,
1594}
1595
1596impl Message for RunnerMessage {}
1597
1598#[derive(Debug, Clone)]
1599pub enum RunnerResponse {
1600    Ok {
1601        result: RunnerResult,
1602        compilations: Vec<SchemaType>,
1603    },
1604    Error(RunnerError),
1605}
1606
1607impl Response for RunnerResponse {}
1608
1609#[async_trait]
1610impl Actor for Runner {
1611    type Event = ();
1612    type Message = RunnerMessage;
1613    type Response = RunnerResponse;
1614
1615    fn get_span(_id: &str, parent_span: Option<Span>) -> tracing::Span {
1616        parent_span.map_or_else(
1617            || info_span!("Runner"),
1618            |parent_span| info_span!(parent: parent_span, "Runner"),
1619        )
1620    }
1621}
1622
1623impl NotPersistentActor for Runner {}
1624
1625#[async_trait]
1626impl Handler<Self> for Runner {
1627    async fn handle_message(
1628        &mut self,
1629        _sender: ActorPath,
1630        msg: RunnerMessage,
1631        ctx: &mut ActorContext<Self>,
1632    ) -> Result<RunnerResponse, ActorError> {
1633        let started_at = Instant::now();
1634        match Self::execute_contract(ctx, &msg.data, msg.is_owner).await {
1635            Ok((result, compilations)) => {
1636                if let Some(metrics) = try_core_metrics() {
1637                    metrics.observe_contract_execution(
1638                        "success",
1639                        started_at.elapsed(),
1640                    );
1641                }
1642                debug!(
1643                    msg_type = "Execute",
1644                    approval_required = result.approval_required,
1645                    compilations_count = compilations.len(),
1646                    is_owner = msg.is_owner,
1647                    "Contract executed successfully"
1648                );
1649                Ok(RunnerResponse::Ok {
1650                    result,
1651                    compilations,
1652                })
1653            }
1654            Err(e) => {
1655                if let Some(metrics) = try_core_metrics() {
1656                    metrics.observe_contract_execution(
1657                        "error",
1658                        started_at.elapsed(),
1659                    );
1660                }
1661                error!(
1662                    msg_type = "Execute",
1663                    error = %e,
1664                    is_owner = msg.is_owner,
1665                    "Contract execution failed"
1666                );
1667                Ok(RunnerResponse::Error(e))
1668            }
1669        }
1670    }
1671}