Assessment reports>Radix>Threat Model>Object operations

Object operations

Creation and setup API

The Creation and Setup API provides fundamental operations for creating new objects and converting them into globally addressable entities. These operations form the foundation for object lifecycle management in the system.

OBJECT_NEW

This creates a new owned object instance within the caller's package.

fn new_object( &mut self, blueprint_ident: &str, features: Vec<&str>, generic_args: GenericArgs, fields: IndexMap, kv_entries: IndexMap, KVEntry>>, ) -> Result;

The operation includes the following validations:

  1. Package scoping

    It ensures objects are only created within the caller's package scope:

    let package_address = actor .blueprint_id() .map(|b| b.package_address) .ok_or(RuntimeError::SystemError(SystemError::NoPackageAddress))?;
  2. Field validation

    It checks the validity of all field indexes against the blueprint definition:

    let expected_num_fields = blueprint_interface.state.num_fields(); for field_index in fields.keys() { let field_index: usize = (*field_index) as u32 as usize; if field_index >= expected_num_fields { return Err(RuntimeError::SystemError(SystemError::CreateObjectError(...))); } }

Function: OBJECT_GLOBALIZE

This converts an owned object into a globally addressable one, attaching required modules and validating access.

fn globalize( &mut self, node_id: NodeId, modules: IndexMap, address_reservation: Option ) -> Result

The operation performs the following validations and checks:

  1. Package authorization

    It ensures only the original package can globalize its objects:

    if Some(reserved_blueprint_id.package_address) != actor.package_address() { return Err(RuntimeError::SystemError( SystemError::InvalidGlobalizeAccess(Box::new(InvalidGlobalizeAccess { package_address: reserved_blueprint_id.package_address, blueprint_name: reserved_blueprint_id.blueprint_name, actor_package: actor.package_address(), })), )); }
  2. Required modules

    It confirms mandatory modules (RoleAssignment and Metadata) are included:

    if !modules.contains_key(&AttachedModuleId::RoleAssignment) { return Err(RuntimeError::SystemError(SystemError::MissingModule( ModuleId::RoleAssignment, ))); } if !modules.contains_key(&AttachedModuleId::Metadata) { return Err(RuntimeError::SystemError(SystemError::MissingModule( ModuleId::Metadata, ))); }
  3. Address reservation validation

    It validates the global address reservation:

    match type_info { Some(TypeInfoSubstate::GlobalAddressReservation(x)) => x, _ => { return Err(RuntimeError::SystemError( SystemError::InvalidGlobalAddressReservation, )); } }
  4. Module-type validation

    It ensures attached modules align with their expected types:

    let blueprint_id = self.get_object_info(node_id)?.blueprint_info.blueprint_id; let expected_blueprint = module_id.static_blueprint(); if !blueprint_id.eq(&expected_blueprint) { return Err(RuntimeError::SystemError(SystemError::InvalidModuleType( Box::new(InvalidModuleType { expected_blueprint, actual_blueprint: blueprint_id, }), ))); }
  5. Transient blueprint check

    It prevents globalization of transient blueprints:

    if blueprint_definition.interface.is_transient { return Err(RuntimeError::SystemError( SystemError::GlobalizingTransientBlueprint, )); }

Invocation API

The Invocation API enables invocation of functions on foreign blueprints or modules.

Function: OBJECT_CALL

This method enables the standard method invocation path on objects in the system — SystemModuleMixer::on_call_method(), and hence AuthModule, is used to establish an auth zone and resolve permissions, and the call itself is delegated to the kernel_invoke() function after.

fn call_method( &mut self, receiver: &NodeId, method_name: &str, args: Vec, ) -> Result, E>;
  1. Authorization setup

    It creates an auth zone for the current context using create_auth_zone(), retrieves the blueprint info of the receiver, and resolves the method permissions via resolve_method_permission().

  2. Permission checks

    Resolved permissions are checked through check_permission(), which validates access rules or role lists, enforces role-authorization boundaries, and returns an Unauthorized error on failure.

  3. Function invocation

    It passes the validated function call into the engine for execution using kernel_invoke().

  4. Cleanup

    It performs the required teardown via teardown_auth_zone(), detaching proofs from the auth zone, dropping owned proofs, and destroying the auth zone itself.

  5. Execution result

    It returns SBOR-encoded data or an InvokeError for failures.

Function: BLUEPRINT_CALL

This method facilitates blueprint-level invocations, independent of any specific object instance. Unlike OBJECT_CALL, it does not rely on object states.

fn call_function( &mut self, package_address: PackageAddress, blueprint_name: &str, function_name: &str, args: Vec, ) -> Result, E>;
  1. Preparation and validation

    It constructs a BlueprintId from the package_address and blueprint_name. Auth-zone creation is more generalized and not tied to a specific object instance.

  2. Blueprint permission resolution

    It uses SystemModuleMixer::on_call_function to validate the package and blueprint existence. It also checks function accessibility rules defined at the blueprint level. Functions have no internal role enforcement but can include custom access checks using proofs or external authorization mechanisms.

  3. Function invocation

    It passes the validated function call into the engine for execution using kernel_invoke().

  4. Cleanup

    It performs the required teardown via teardown_auth_zone(), similar to OBJECT_CALL.

  5. Execution result

    It returns SBOR-encoded data or an InvokeError for failures.

Function: OBJECT_CALL_MODULE

fn call_module_method( &mut self, receiver: &NodeId, module_id: AttachedModuleId, method_name: &str, args: Vec, ) -> Result, E>;

This method enables calls to attached modules (Metadata, Royalty, RoleAssignment) on global objects. Each module enforces its own authorization templates and rules.

  1. Module validation

    It verifies that the object has global status, checks for module existence in the object's module map, ensures the module type aligns with system constraints, and rejects early if module requirements are not satisfied.

  2. Authorization setup

    It creates an auth zone specific to the module context, maps the module_id to the corresponding blueprint info, resolves the correct auth template for the module type, and handles additional requirements (e.g., RoleAssignment requiring global address context).

  3. Actor configuration

    It configures the actor for the module-specific call.

    Actor::Method(MethodActor { method_type: MethodType::Module(module_id), // Key distinction node_id: receiver.clone(), ident: method_name.to_string(), auth_zone: auth_actor_info.clone(), object_info, })
  4. Partition isolation

    Module partitions are isolated from each other, ensuring the key space remains collision-free within the context of the same object boundary as well as preventing cross-module state access.

Function: OBJECT_CALL_DIRECT

This method is used for invocations similar to object_call() but provides direct access to objects, bypassing certain authorization checks. It is specifically designed for high-privilege operations on objects like vaults.

fn call_direct_access_method( &mut self, receiver: &NodeId, method_name: &str, args: Vec, ) -> Result, E>;
  1. Receiver-type validation

It validates that the receiver explicitly allows direct access.

fn is_direct_access_receiver(receiver: &Option) -> bool { if let Some(ref receiver) = receiver { match (&receiver.receiver, receiver.ref_types) { (Receiver::SelfRef | Receiver::SelfRefMut, RefTypes::DIRECT_ACCESS) => true, _ => false, } } else { false } }
  1. Blueprint access control

Only specific system blueprints (like vaults) allow direct access.

if blueprint_id.package_address.eq(&RESOURCE_PACKAGE) && (blueprint_id.blueprint_name.eq(FUNGIBLE_VAULT_BLUEPRINT) || blueprint_id.blueprint_name.eq(NON_FUNGIBLE_VAULT_BLUEPRINT)) { Ok(StableReferenceType::DirectAccess) }
  1. Method actor setup

It creates a special method actor with the Direct method type.

Actor::Method(MethodActor { method_type: MethodType::Direct, // Key distinction node_id: receiver.clone(), ident: method_name.to_string(), auth_zone: auth_actor_info.clone(), object_info, })
  1. Authorization resolution

Direct access methods have specific restrictions around global addressing.

match module_id { ModuleId::Main => { if is_direct_access { return Err(RuntimeError::SystemError( SystemError::GlobalAddressDoesNotExist, )); }

Address operations

The address API provides utilities for global address management.

Function: ADDRESS_ALLOCATE

This handles global address allocation.

fn allocate_address(&mut self) -> Result;
  1. Uniqueness guarantee

    It ensures globally unique address generation.

    let address = self.address_allocator.next_address()?; if self.address_exists(address) { return Err(RuntimeError::SystemError(SystemError::AddressCollision)); }
  2. Reservation management

    It tracks address reservations until used.

    self.reserved_addresses.insert( address, ReservationInfo { reserved_at: self.current_epoch(), reserved_by: self.get_current_actor() } );

Function: ADDRESS_GET_RESERVATION_ADDRESS

This retrieves an address from the reservation:

fn get_reservation_address(&self, reservation: &GlobalAddressReservation) -> Result;
  1. Reservation validation

    It verifies a reservation exists and has not expired.

    if !self.is_valid_reservation(reservation) { return Err(RuntimeError::SystemError(SystemError::InvalidReservation)); }
  2. Address resolution

    It maps a reservation to a concrete address.

    match self.reserved_addresses.get(&reservation.id) { Some(info) => Ok(info.address), None => Err(RuntimeError::SystemError(SystemError::ReservationNotFound)) }
  3. Life cycle management

    It tracks reservation usage and expiration.

    if self.is_reservation_expired(reservation) { self.reserved_addresses.remove(&reservation.id); return Err(RuntimeError::SystemError(SystemError::ReservationExpired)); }

Utility API

The Utility API provides a set of helper methods for querying and validating object properties, relationships, and type information. These methods support common object inspection and verification tasks.

Function: OBJECT_INSTANCE_OF

This method checks if an object is an instance of a specific blueprint by comparing the object's blueprint ID with the provided package address and blueprint name.

fn instance_of(&mut self, object_id: Vec, package_address: Vec, blueprint_name: Vec) -> Result

Function: OBJECT_GET_BLUEPRINT_ID

This method retrieves the blueprint ID of an object though the get_object_info() method.

fn get_blueprint_id(&mut self, node_id: &NodeId) -> Result;

Function: OBJECT_GET_OUTER_OBJECT

This method retrieves the outer object of an object through the get_object_info() method.

fn get_outer_object(&mut self, node_id: &NodeId) -> Result;
Zellic © 2025Back to top ↑