Skip to main content

ave_core/evaluation/runner/
mod.rs

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