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.clone().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#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
71pub enum Actor {
72    /// In System V1, there was an explicit call to initialize the transaction processor.
73    /// This call has to have an actor making the call, which is the Root.
74    ///
75    /// From V2 onwards, we don't have an explicit function call to initialize the transaction
76    /// processor - but we still temporarily set a CallFrameInit with a `Root` actor.
77    /// This is used to set up the initial AuthZone in [`MultiThreadIntentProcessor::init`].
78    ///
79    /// [`MultiThreadIntentProcessor::init`]: crate::system::transaction::multithread_intent_processor::MultiThreadIntentProcessor::init
80    Root,
81    Method(MethodActor),
82    Function(FunctionActor),
83    BlueprintHook(BlueprintHookActor),
84}
85
86// This is only used by `kernel_create_kernel_for_testing` in the testing framework.
87impl Default for Actor {
88    fn default() -> Self {
89        Self::Root
90    }
91}
92
93impl CallFrameReferences for Actor {
94    fn global_references(&self) -> Vec<NodeId> {
95        let mut global_refs = Vec::new();
96
97        if let Some(blueprint_id) = self.blueprint_id() {
98            global_refs.push(blueprint_id.package_address.into_node_id());
99        }
100
101        if let Actor::Method(MethodActor {
102            node_id,
103            object_info,
104            ..
105        }) = self
106        {
107            if let OuterObjectInfo::Some { outer_object } =
108                object_info.blueprint_info.outer_obj_info
109            {
110                global_refs.push(outer_object.clone().into_node_id());
111            }
112
113            if node_id.is_global() {
114                global_refs.push(node_id.clone());
115            }
116        }
117
118        global_refs
119    }
120
121    fn direct_access_references(&self) -> Vec<NodeId> {
122        if self.is_direct_access() {
123            self.node_id().into_iter().collect()
124        } else {
125            vec![]
126        }
127    }
128
129    fn stable_transient_references(&self) -> Vec<NodeId> {
130        let mut references = vec![];
131        references.extend(self.self_auth_zone());
132
133        if !self.is_direct_access() {
134            references.extend(self.node_id().filter(|n| !n.is_global()));
135        }
136
137        references
138    }
139
140    fn len(&self) -> usize {
141        match self {
142            Actor::Root => 1,
143            Actor::Method(MethodActor { ident, node_id, .. }) => {
144                node_id.as_bytes().len() + ident.len()
145            }
146            Actor::Function(FunctionActor {
147                blueprint_id,
148                ident,
149                ..
150            }) => {
151                blueprint_id.package_address.as_bytes().len()
152                    + blueprint_id.blueprint_name.len()
153                    + ident.len()
154            }
155            Actor::BlueprintHook(BlueprintHookActor { blueprint_id, .. }) => {
156                blueprint_id.package_address.as_bytes().len()
157                    + blueprint_id.blueprint_name.len()
158                    + 1
159            }
160        }
161    }
162}
163
164impl Actor {
165    pub fn is_root(&self) -> bool {
166        matches!(self, Actor::Root)
167    }
168
169    pub fn self_auth_zone(&self) -> Option<NodeId> {
170        match self {
171            Actor::Root | Actor::BlueprintHook(..) => None,
172            Actor::Method(method_actor) => Some(method_actor.auth_zone),
173            Actor::Function(function_actor) => Some(function_actor.auth_zone),
174        }
175    }
176
177    pub fn instance_context(&self) -> Option<InstanceContext> {
178        let method_actor = match self {
179            Actor::Method(method_actor) => method_actor,
180            _ => return None,
181        };
182
183        match method_actor.method_type {
184            MethodType::Main | MethodType::Direct => {
185                if method_actor.object_info.is_global() {
186                    Some(InstanceContext {
187                        outer_object: GlobalAddress::new_or_panic(method_actor.node_id.0),
188                    })
189                } else {
190                    match &method_actor.object_info.blueprint_info.outer_obj_info {
191                        OuterObjectInfo::Some { outer_object } => Some(InstanceContext {
192                            outer_object: outer_object.clone(),
193                        }),
194                        OuterObjectInfo::None { .. } => None,
195                    }
196                }
197            }
198            _ => None,
199        }
200    }
201
202    pub fn get_object_id(&self) -> Option<(NodeId, Option<AttachedModuleId>)> {
203        match self {
204            Actor::Method(method_actor) => Some((
205                method_actor.node_id,
206                method_actor.method_type.module_id().into(),
207            )),
208            Actor::BlueprintHook(BlueprintHookActor {
209                receiver: Some(node_id),
210                ..
211            }) => Some((*node_id, None)),
212            Actor::BlueprintHook(..) | Actor::Root | Actor::Function(..) => None,
213        }
214    }
215
216    pub fn is_barrier(&self) -> bool {
217        match self {
218            Actor::Method(MethodActor { object_info, .. }) => object_info.is_global(),
219            Actor::Function { .. } => true,
220            Actor::BlueprintHook { .. } => true,
221            Actor::Root { .. } => false,
222        }
223    }
224
225    pub fn node_id(&self) -> Option<NodeId> {
226        match self {
227            Actor::Method(MethodActor { node_id, .. }) => Some(*node_id),
228            Actor::BlueprintHook(BlueprintHookActor {
229                receiver: node_id, ..
230            }) => node_id.clone(),
231            _ => None,
232        }
233    }
234
235    pub fn is_direct_access(&self) -> bool {
236        match self {
237            Actor::Method(MethodActor { method_type, .. }) => {
238                matches!(method_type, MethodType::Direct)
239            }
240            _ => false,
241        }
242    }
243
244    pub fn blueprint_id(&self) -> Option<BlueprintId> {
245        match self {
246            Actor::Method(actor) => Some(actor.get_blueprint_id()),
247            Actor::Function(FunctionActor { blueprint_id, .. })
248            | Actor::BlueprintHook(BlueprintHookActor { blueprint_id, .. }) => {
249                Some(blueprint_id.clone())
250            }
251            Actor::Root => None,
252        }
253    }
254
255    pub fn package_address(&self) -> Option<PackageAddress> {
256        self.blueprint_id().map(|id| id.package_address)
257    }
258}