radix_engine/system/
actor.rs

1use crate::internal_prelude::*;
2use crate::kernel::kernel_callback_api::CallFrameReferences;
3use radix_engine_interface::api::{AttachedModuleId, ModuleId};
4
5#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
6pub struct InstanceContext {
7    pub outer_object: GlobalAddress,
8}
9
10#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
11pub enum MethodType {
12    Main,
13    Direct,
14    Module(AttachedModuleId),
15}
16
17impl MethodType {
18    pub fn module_id(&self) -> ModuleId {
19        match self {
20            MethodType::Module(module_id) => (*module_id).into(),
21            MethodType::Main | MethodType::Direct => ModuleId::Main,
22        }
23    }
24}
25
26#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
27pub struct MethodActor {
28    pub method_type: MethodType,
29    pub node_id: NodeId,
30    pub ident: String,
31
32    pub auth_zone: NodeId,
33
34    // Cached info
35    pub object_info: ObjectInfo,
36}
37
38impl MethodActor {
39    pub fn get_blueprint_id(&self) -> BlueprintId {
40        match self.method_type {
41            MethodType::Main | MethodType::Direct => {
42                self.object_info.blueprint_info.blueprint_id.clone()
43            }
44            MethodType::Module(module_id) => module_id.static_blueprint(),
45        }
46    }
47}
48
49#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
50pub struct FunctionActor {
51    pub blueprint_id: BlueprintId,
52    pub ident: String,
53
54    pub auth_zone: NodeId,
55}
56
57impl FunctionActor {
58    pub fn as_global_caller(&self) -> GlobalCaller {
59        GlobalCaller::PackageBlueprint(self.blueprint_id.clone())
60    }
61}
62
63#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
64pub struct BlueprintHookActor {
65    pub receiver: Option<NodeId>,
66    pub hook: BlueprintHook,
67    pub blueprint_id: BlueprintId,
68}
69
70#[allow(clippy::large_enum_variant)]
71#[derive(Debug, Default, Clone, ScryptoSbor, PartialEq, Eq)]
72pub enum Actor {
73    /// In System V1, there was an explicit call to initialize the transaction processor.
74    /// This call has to have an actor making the call, which is the Root.
75    ///
76    /// From V2 onwards, we don't have an explicit function call to initialize the transaction
77    /// processor - but we still temporarily set a CallFrameInit with a `Root` actor.
78    /// This is used to set up the initial AuthZone in [`MultiThreadIntentProcessor::init`].
79    ///
80    /// [`MultiThreadIntentProcessor::init`]: crate::system::transaction::multithread_intent_processor::MultiThreadIntentProcessor::init
81    // Default is only used by `kernel_create_kernel_for_testing` in the testing framework.
82    #[default]
83    Root,
84    Method(MethodActor),
85    Function(FunctionActor),
86    BlueprintHook(BlueprintHookActor),
87}
88
89impl CallFrameReferences for Actor {
90    fn global_references(&self) -> Vec<NodeId> {
91        let mut global_refs = Vec::new();
92
93        if let Some(blueprint_id) = self.blueprint_id() {
94            global_refs.push(blueprint_id.package_address.into_node_id());
95        }
96
97        if let Actor::Method(MethodActor {
98            node_id,
99            object_info,
100            ..
101        }) = self
102        {
103            if let OuterObjectInfo::Some { outer_object } =
104                object_info.blueprint_info.outer_obj_info
105            {
106                global_refs.push(outer_object.into_node_id());
107            }
108
109            if node_id.is_global() {
110                global_refs.push(*node_id);
111            }
112        }
113
114        global_refs
115    }
116
117    fn direct_access_references(&self) -> Vec<NodeId> {
118        if self.is_direct_access() {
119            self.node_id().into_iter().collect()
120        } else {
121            vec![]
122        }
123    }
124
125    fn stable_transient_references(&self) -> Vec<NodeId> {
126        let mut references = vec![];
127        references.extend(self.self_auth_zone());
128
129        if !self.is_direct_access() {
130            references.extend(self.node_id().filter(|n| !n.is_global()));
131        }
132
133        references
134    }
135
136    fn len(&self) -> usize {
137        match self {
138            Actor::Root => 1,
139            Actor::Method(MethodActor { ident, node_id, .. }) => {
140                node_id.as_bytes().len() + ident.len()
141            }
142            Actor::Function(FunctionActor {
143                blueprint_id,
144                ident,
145                ..
146            }) => {
147                blueprint_id.package_address.as_bytes().len()
148                    + blueprint_id.blueprint_name.len()
149                    + ident.len()
150            }
151            Actor::BlueprintHook(BlueprintHookActor { blueprint_id, .. }) => {
152                blueprint_id.package_address.as_bytes().len()
153                    + blueprint_id.blueprint_name.len()
154                    + 1
155            }
156        }
157    }
158}
159
160impl Actor {
161    pub fn is_root(&self) -> bool {
162        matches!(self, Actor::Root)
163    }
164
165    pub fn self_auth_zone(&self) -> Option<NodeId> {
166        match self {
167            Actor::Root | Actor::BlueprintHook(..) => None,
168            Actor::Method(method_actor) => Some(method_actor.auth_zone),
169            Actor::Function(function_actor) => Some(function_actor.auth_zone),
170        }
171    }
172
173    pub fn instance_context(&self) -> Option<InstanceContext> {
174        let method_actor = match self {
175            Actor::Method(method_actor) => method_actor,
176            _ => return None,
177        };
178
179        match method_actor.method_type {
180            MethodType::Main | MethodType::Direct => {
181                if method_actor.object_info.is_global() {
182                    Some(InstanceContext {
183                        outer_object: GlobalAddress::new_or_panic(method_actor.node_id.0),
184                    })
185                } else {
186                    match &method_actor.object_info.blueprint_info.outer_obj_info {
187                        OuterObjectInfo::Some { outer_object } => Some(InstanceContext {
188                            outer_object: *outer_object,
189                        }),
190                        OuterObjectInfo::None => None,
191                    }
192                }
193            }
194            _ => None,
195        }
196    }
197
198    pub fn get_object_id(&self) -> Option<(NodeId, Option<AttachedModuleId>)> {
199        match self {
200            Actor::Method(method_actor) => Some((
201                method_actor.node_id,
202                method_actor.method_type.module_id().into(),
203            )),
204            Actor::BlueprintHook(BlueprintHookActor {
205                receiver: Some(node_id),
206                ..
207            }) => Some((*node_id, None)),
208            Actor::BlueprintHook(..) | Actor::Root | Actor::Function(..) => None,
209        }
210    }
211
212    pub fn is_barrier(&self) -> bool {
213        match self {
214            Actor::Method(MethodActor { object_info, .. }) => object_info.is_global(),
215            Actor::Function { .. } => true,
216            Actor::BlueprintHook { .. } => true,
217            Actor::Root => false,
218        }
219    }
220
221    pub fn node_id(&self) -> Option<NodeId> {
222        match self {
223            Actor::Method(MethodActor { node_id, .. }) => Some(*node_id),
224            Actor::BlueprintHook(BlueprintHookActor {
225                receiver: node_id, ..
226            }) => *node_id,
227            _ => None,
228        }
229    }
230
231    pub fn is_direct_access(&self) -> bool {
232        match self {
233            Actor::Method(MethodActor { method_type, .. }) => {
234                matches!(method_type, MethodType::Direct)
235            }
236            _ => false,
237        }
238    }
239
240    pub fn blueprint_id(&self) -> Option<BlueprintId> {
241        match self {
242            Actor::Method(actor) => Some(actor.get_blueprint_id()),
243            Actor::Function(FunctionActor { blueprint_id, .. })
244            | Actor::BlueprintHook(BlueprintHookActor { blueprint_id, .. }) => {
245                Some(blueprint_id.clone())
246            }
247            Actor::Root => None,
248        }
249    }
250
251    pub fn package_address(&self) -> Option<PackageAddress> {
252        self.blueprint_id().map(|id| id.package_address)
253    }
254}