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 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}