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