Skip to main content

ave_core/evaluation/runner/
mod.rs

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