What is Radix Engine?

Radix Engine is a deterministic, transaction-based state machine that updates ledger state by sequentially executing transactions.

Radix Engine was built out of the lack of Blockchain VMs specifically optimized for a DeFi shared computing environment in terms of usability, security, performance, and modularity.

Unlike the EVM and other blockchain VMs, Radix Engine:

  • Is Object-Oriented and Type-Safe
  • Enforces move/ownership semantics
  • Has the ability to add system plugins to extend/modify system functionality

Radix Babylon Network

The Radix Engine running on the Radix Babylon network is a specific system configuration of Radix Engine which makes asset and auth management a native/core feature of the system.

Architecture

Radix Engine is organized into 5 layers. Each layer has specific responsibilities and provides an API to the layer above. Middle layers also provide a Callback API which the layer above must implement.

Layer NameResponsibilities
ApplicationDefines Blueprint Application Logic
VMExecutes Application Code
SystemDefines Actor abstraction (Memory Protection)
Defines Package, Blueprint, Object abstractions
Defines System Standards such as Authorization and Versioning
KernelDefines Node, Partition, Substate abstractions
Maintains Call Frame Stack
Maintains Ownership/Reference invariants
DatabaseDefines PartitionKey, SortKey abstractions

Application Layer

The application layer is responsible for defining high level logic which manipulates objects and produces events for the eventual use by off-ledger systems such as wallets and DApps.

Implementation

An application is added to the system by publishing a Package, which contain zero or more Blueprints. Each blueprint defines object type information and logic which can create, manipulate and destroy objects of that blueprint type.

Object Model

Unlike Ethereum where the ledger state is a flat mapping between addresses and account states, Radix Engine organizes its state into a forest of objects. Child objects are exclusively owned by its parent in the tree hierarchy. Each root object is assigned a global address.

Each object has:

Blueprint Id

Every object is associated with a blueprint id which consists of <package_address> + <blueprint_name>.

A blueprint id uniquely identifies the blueprint of an object. The blueprint of an object specifies both the structure and the logic of the object.

Outer Objects

Objects which have a blueprint which is an Inner Blueprint will have an associated Outer object of a given outer Blueprint. Objects of an Inner Blueprint may directly access the state of its outer object avoiding invocation and new call frame overhead + costs.

Features

An object's features describes a subset of the total features defined in the object's blueprint. Having a feature enabled/disabled can modify both behavior and state stored for the object.

Generic Substitutions

If an object's blueprint defines generic arguments then these are replaced with the types specified in an object's generic substitutions list.

Application State

Each object may have a set of fields and collections which represent that object's state. When created or updated, these fields and collections must conform to the schema specified by the object's blueprint.

Object Modules

The system may define additional state/logic to be stored per globalized object known as an Object Module. The system can define whether an Object Module is required or optional.

An Object Module itself has a Blueprint type along with associated logic to manipulate the state of the object module.

NOTE: In Radix Babylon Network, there currently exists three object modules:

Blueprint

A Blueprint is the Radix Engine equivalent of Classes/Types in Object-Oriented Languages. It acts as a type identifier for an object and describes shared properties and behavior of all objects of that blueprint.

Each blueprint has a unique name within its package and are globally identifiable by <package_address> + <blueprint_name>.

Inner and Outer Blueprints

NOTE: In Radix Babylon Network, Inner Blueprints are currently only available for use by native packages.

A blueprint may be specified as either an Outer or Inner Blueprint. Inner blueprints must specify an associated outer blueprint defined in the same package.

Inner blueprint objects may only be instantiated by an object of the associated outer blueprint.

Transience

NOTE: In Radix Babylon Network, Transience is currently only available for use by native packages.

If a blueprint is specified to be transient, all objects of this blueprint cannot be persisted and must be created/dropped within the lifecycle of a transaction.

Features

NOTE: In Radix Babylon Network, Features are currently only available for use by native packages.

Features provide a mechanism to express conditional execution and conditional stored state (using Field Conditions). The set of features to be used are specified per object on instantiation.

Features are identified by string.

Generics

NOTE: In Radix Babylon Network, Generics are currently only available for use by native packages.

Generics to a blueprint requires an object instantiator to specify generic substitutions during instantiation. Such a generic can then be used in defining function or state schemas.

Generics in a Blueprint are identified by index.

Fields

NOTE: In Radix Babylon Network, use of more than one Field, Field Conditions and Field Transience are currently only available for use by native packages.

A field is object state which gets loaded at once and maps to a single substate. A schema which describes what is in the data must be specified for every field.

Fields are identified by field index.

Field Condition

Fields may be conditionally included in an object depending on the features instantiated with that object. There are currently three options for field conditions:

NameDescription
AlwaysAlways include the field
IfFeatureOnly include the field if a given feature is specified
IfOuterFeatureOnly include the field if a given feature in the associated outer object is specified

Field Transience

Fields may be specified to be transient. In this case, the field is never persisted. Instead, a default value is initially loaded on first read and may be updated over the course of a transaction. At the end of a transaction the field's value gets discarded.

Collections

NOTE: In Radix Babylon Network, Collections are currently only available for use by native packages.

A collection is a set of data which share the same schema. There are currently three types of collections:

  1. Key-Value Collection
  2. Index Collection
  3. Sorted Index Collection

Collections are identified by collection index.

Events

Events may be emitted during runtime for off-chain processing. A schema which describes the data of every event type must be specified.

Events are identified by string.

Functions/Methods

Functions/Methods define executable logic. A function is executable logic which does not directly depend on the internal state of an object. A method is executable logic which directly depends on internal state of an object, known as receiver.

Every function/method is defined by an input/output schema which describes the signature of the function/method.

Functions/Methods are identified by string.

Hooks

NOTE: In Radix Babylon Network, Hooks are currently only available for use by native packages.

Hooks define logic which get executed when certain system events occur.

There are currently three types of hooks:

Hook NameDescription
OnVirtualizeCalled when a substate fault occurs on a virtual address of this blueprint type.
OnMoveCalled when an object of this blueprint type is moved between call frames.
OnDropCalled when an object of this blueprint type is dropped.

Types

A blueprint may associate a schema to a name. This name combined with the blueprint ID can be used to specify a type.

Types are identified by string.

Blueprint Modules

The system may define additional state to be stored per blueprint known as Blueprint modules. If defined, every blueprint definition must initialize these modules.

NOTE: In Radix Babylon Network, there currently exists two blueprint modules:

Package

A package is a special native object which contains 0 or more blueprint definitions. Because it is an object, packages inherit object-like qualities such as the ability to have object modules (like metadata).

Package Blueprint and Package Package

This type of relationship creates the following circular definition:

An Object is of some Blueprint type.

A Blueprint is part of a Package.

A Package is an Object.

This circular definition creates the notion of the Package Blueprint and the Package Package (similar to Class.class in java). A Package Blueprint is the blueprint type of a Package object and Package Package is the package which contains the Package Blueprint.

Due to the circular dependency, the first object (Package object) is created without following standard object creation process. It's directly flashed into the database.

Type System

The Type System checks that payloads coming from the application layer match the schema defined in the Blueprint. This includes:

  • Object Fields/Collections
  • Function Input/Output
  • Events

The Type Checking System supports generics.

Actor

An actor is the acting entity currently executing and determines what state can be directly read.

There are five types of actors:

Actor NameDescription
RootThe initial application of all transactions.
MethodA call on an object. Has direct access to state of the running object.
FunctionA stateless function call. Has no direct access to any state.
Method HookA callback call on an object defined by the system. Has direct access to state of the running object.
Function HookA callback stateless function call defined by the system. Has no direct access to any state.

VM Layer

The VM Layer is responsible for passing control from the system to the application as well as providing the application layer a Turing-complete computing environment and the interface to the system layer.

Radix Engine currently supports two VM environments:

  • A Scrypto WASM VM which exposes the system layer through WASM extern functions
  • A Native VM which directly compiles applications with Radix Engine in the host's environment

Implementation

The VM Layer is implemented by defining the System Callback Object, which requires two callback implementations:

  1. init which is called on transaction bootup to initialize the vm layer
  2. invoke which is the entrypoint for any function or method invocation

On invoke, the VM layer determines the appropriate VM environment and then calls the associated application layer logic.

Scrypto Wasm VM

On invoke of a method which executes in a Scrypto Wasm VM, the VM layer loads the WASM code associated with the invocation and creates a new WASM instance with a fresh heap and stack. The exported function associated with the invocation is then called with the invocation arguments.

Extern functions are mapped to subset of the system layer's api which the application can call.

Native VM

On invoke of a function/method which executes natively, the function must have been compiled with Radix Engine and is just called directly. Because all applications using the Native VM are trusted, Native VM applications are not isolated from the Radix Engine and share the same memory space.

The full system layer API is also exposed to Native VM applications.

System Layer

The System Layer is responsible for:

Implementation

The System Layer is implemented by defining the Kernel Callback Object and defining the Actor/Package/Blueprint/Object abstractings on top of the kernel's Node/Partition/Substate abstractions.

Object Implementation

The system layer defines the Object abstraction on top of the kernel's Node/Partition/Substate abstraction.

The system layer maps every object to a unique NodeId and under every NodeId the partitions are mapped in the following manner:

Partition Number
Type Info0
Schema Data1
Object Modules2-31
Reserved32-63
Application State64-255

Type Info

For a given object, type-related info is stored under the object's NodeId in the TypeInfo substate found in PartitionNumber 0 and SubstateKey::Field 0. This includes information such as:

Blueprint Implementation

State to Partition Mapping

The mapping from the Fields and Collection indices to Partition Number is managed by the System Layer and done at a per blueprint basis. This includes the Fields and Collection indices of the object modules for and object.

Package Implementation

The package abstraction is implemented as a native blueprint. In order to get around the circular definition problem, the package logic and structure must be flashed into the system at genesis.

Type System

The system layer is responsible for implementing the type system abstraction.

For a given object, the BlueprintId, GenericSubstitutions, and other type-related info is stored under the object's NodeId in the TypeInfo substate found in PartitionNumber 0 and SubstateKey::Field 0.

Local Scrypto Schemas for the object are stored in the object's NodeId with PartitionNumber 2 with a content addressable substate key.

Remote Scrypto Schemas are stored in the blueprint's package NodeId with PartitionNumber 2 with a content addressable substate key.

Actor Implementation

The system layer is responsible for defining the Actor abstraction.

The state of the current actor is stored per call frame as CallFrameData. The system exposes an interface which can access the state of the currently acting object (if there is one). Thus, the system prevents higher layers from accessing state of call frame objects which aren't the actor.

System Modules

System Modules are modules which can be added to the system to extend functionality.

System Modules are stateful and may expose a set of functions to the application layer as well as execute additional logic within a system call.

Kernel Layer

The kernel layer is responsible for:

  • Defining the Node/Partition/Substate abstraction
  • Defining the Call Frame abstraction
  • Maintaining Ownership/Reference invariants
  • Managing transaction state updates, which are to be subsequently committed to the database at the end of the transaction

Implementation

The kernel layer is implemented on top of the database layer's Partition Key and Sort Key abstractions.

Database Layer

The database layer is responsible for defining the partition-key / sort-key abstractions.

Implementation

The database layer is implemented on top of a key-value database.

Transaction Lifecycle

Radix Engine is a transactional state machine which accepts a transaction and a given state and outputs a state change and additional output.

radix_engine(State, Transaction) -> (StateChange, Output)

The state change can then be applied to the database to update it's state:

state_commit(State, StateChange) -> State

Three Stages

There are three stages in the transaction lifecycle:

  1. Bootup, which consists of initializing the layers of the stack
  2. Execution, which is the execution of the application logic specified by the transaction
  3. Shutdown, which consists of cleaning up each layer and creating the final StateChange and Output

Transaction Bootup

The bootup and initialization of a transaction consists of two steps:

  1. Initialize Stack
  2. Invoke Transaction Processor

Initialize Stack

Before a transaction is executed, initialization of the Kernel/System/VM stack occurs. During this initialization phase, configuration is loaded from the database and the state of each layer is initialized.

LayerInitialization Description
KernelLoad Kernel version
Check transaction references
Create Initial Call Frame
SystemLoad System module configurations
Verify transaction has not been previously executed
Verify transaction is valid within epoch bounds
Initialize enabled System Modules
VMLoad Scrypto VM version

Invoke Transaction Processor

Once the entire stack has been initialized along with the initial call frame, an invocation of a well-known blueprint, TRANSACTION_PROCESSOR, is made with the arguments specified in the transaction. From this point forward, normal transaction execution occurs.

Transaction Runtime

Once transaction bootup has finished, the Transaction Processor Blueprint function run is then executed with transaction data as its argument. It executes on top of the initial call frame created during kernel initialization in a standard application environment.

Once the run function has finished executing transaction shutdown begins.

Transaction Shutdown

Once the TRANSACTION_PROCESSOR call returns, the transaction shutdown procedure begins.

Transaction Shutdown consists of two steps:

  1. Finalize State Updates
  2. Create a Transaction Receipt

Application Environment

Every method/function execution has a call frame associated with it managed by the Kernel.

A call frame contains all owned and referenced objects usable by the running function. These objects are referrable by NodeId and system-defined indices.

Invocations

Owned and referenced objects may have methods invoked (creating a new call frame). Owned objects may be passed in as arguments and may be received in these invocations.

Object Creation/Destruction/Globalization

Objects of the current blueprint may be instantiated, creating a new owned object into the call frame, or dropped, in which case the owned object gets removed from the call frame.

Actor State Read/Write

A call frame also contains a reference to the actor, or callee object (i.e. self in object-oriented languages). This is maintained to allow read/writes of state for the given actor.

System Module Functions

Additional system functions are available to the application layer implemented by System Modules. Currently, these include:

  • Events
  • Logging
  • Costing
  • Transaction Runtime

Object Lifecycle

Instantiation

An object may be instantiated by using one of the system calls:

  • object_new_simple_object
  • object_new_object

On instantiation the set of features, generic arguments, and initial state must be passed in to construct the object. Only blueprints of the currently acting package may be instantiated. If the blueprint is an inner blueprint, only an acting outer blueprint component may instantiate that inner blueprint.

Destruction

An object may be dropped by using the object_drop system call.

Globalization

An object may be globalized using one of the system calls:

  • object_globalize
  • object_globalize_with_address_and_create_inner_object_and_emit_event

Once globalized an object is associated with a global address and may be referenced without ownership of the object. Thus, it may be referenced in a transaction or in blueprint code.

Moved to ownership by another object

A call frame owned object may be moved to ownership by another object if it is moved via one of the system calls:

  • object_new_simple_object
  • object_new_object
  • key_value_entry_set
  • field_write

Invocations

An invocation is started by the application layer by calling one of the invocation system calls:

  • object_call_method
  • object_call_direct_access_method
  • object_call_module_method
  • blueprint_call_function

On one of these calls, the system then follows three phases:

  1. Call Frame Setup
  2. Execution
  3. Call Frame Exit and Return

Call Frame Setup

System module does its own checks (e.g. auth).

Kernel invoke is called which setups a new call frame. The arguments are verified against the input schema of the function defined by the blueprint definition.

Execution

Once the new call frame is setup, execution is passed to the application layer which may then execute it's own logic in its application environment.

Call Frame Exit

Once finished the system layer checks that the return value is of the correct schema given by the blueprint definition. The kernel verifies that owned objects and references in the return value are valid and the caller call frame is updated with any of these owned objects/references.

State Reads/Writes

State Reads and Writes from objects may be only be done by the current actor using the following system calls:

  • actor_open_field
  • actor_open_key_value_entry
  • actor_index_insert
  • actor_index_remove
  • actor_index_scan_keys
  • actor_index_drain
  • actor_sorted_index_insert
  • actor_sorted_index_remove
  • actor_sorted_index_scan

Key Value Stores are a special type of object though which may be read/written to as long as one has a reference to that key value store. This is accessible via the system call:

  • key_value_store_open_entry

Fields

Collections

Transaction Processor

The transaction processor is the initial application layer call frame made during the transaction boot process and executes a transaction manifest which is encoded in a transaction.

It consists of a blueprint with a single run function.

Transaction Processor Blueprint

The transaction processor blueprint has a single run function which accepts a transaction manifest.

The manifest is a series of instructions which the transaction processor interprets and executes.

Resources

The Resource system manages asset logic across the system.

It is composed of:

  • Resource Package which consists of
    • Fungible/Non-Fungible Bucket Blueprint
    • Fungible/Non-Fungible Proof Blueprint
    • Fungible/Non-Fungible Vault Blueprint
    • Fungible/Non-Fungible Resource Manager Blueprint

Auth

Unlike the majority of blockchains which rely on a caller identifier for access control, the Auth system uses a more distributed "Proof" system. Before accessing a protected method a caller must provide specific "Proofs" of resources they have access to. These proofs must then match the requirements of the callee function or method.

The Access Control System is composed of four parts:

  1. An Access Control Blueprint Module, which defines function rules and roles available to use for a given blueprint in a package and which roles are able to access which methods.
  2. A Role Assignment Object Module, which assigns access rules for each role on object instantiation.
  3. An AuthZone Blueprint, which allows a caller to update the proofs in their authzone.
  4. An Access Control System Module, which creates a new AuthZone for every new call frame and verifies that AuthZone proofs match the requirements of accessing an object's method.

Auth Blueprint Module

The auth blueprint module defines three things for every blueprint:

  • Function AccessRules
  • Method accessibility
  • Role Specification

Function AccessRules

Each function is assigned an immutable access rule.

Method Accessibility

Each method is assigned an accessibility rule, of which there are four options:

Accessibility RuleDescription
PublicAnyone can access the method
Outer Object OnlyOnly outer objects may access the method
Role ProtectedOnly callers who have satisfied any role in a given list may access the method
Own Package OnlyOnly the package this method is a part of may access the method

Role Specification

The roles which must be assigned on object instantiated are defined in role specification. Furthermore, roles which may update the rules of other roles must be specified.

For inner blueprints, it is also possible to defer role specification to the outer blueprint.

Role Assignment Object Module

The role assignment object module assigns each role to an access rule.

The rules associated with a role may be updated or defaulted to the "owner role".

AuthZone

To call a protected method, the caller must place these proofs into their AuthZone, a space dedicated for using proofs for the purpose of authorized method access.

An AuthZone has the following methods:

  • pop
  • push
  • create_proof_of_amount
  • create_proof_of_non_fungibles
  • create_proof_of_all
  • drop_proofs
  • drop_signature_proofs
  • drop_regular_proofs
  • drain
  • assert_access_rule

Auth System Module

The Auth System Module is a system module which operates on every invocation:

  1. Creates a new AuthZone
  2. Resolve the requirements to access the method/function invocation
  3. Verifies that the Global Caller AuthZone has sufficient proofs to meet the requirements

AuthZone Creation

At the start of every invocation, the access control system module creates a new AuthZone in the call frame of the caller and adds a reference to this object in the callee's call frame. This AuthZone effectively becomes the "Local AuthZone" of the callee.

Every AuthZone references a global caller AuthZone and a parent AuthZone, the values of which are dependent on if the invocation is a global object context switch or not.

If the invocation is a global object context switch, the global caller of the new AuthZone will reference the caller's AuthZone and will not have a parent AuthZone. If the invocation is a local context switch, the caller's global caller is copied into the new AuthZone and the parent will reference the caller's AuthZone.

This pattern generates a stack which looks like:

Permission Resolving

Permission resolving involves loading up relevant state of the callee and generating a permission object from this state.

If the callee is a function then the permission is loaded from the function access rules specified in the blueprint's access control blueprint module.

If the callee is a method then the Method Accessibility is loaded from the callee's access control blueprint module as well as the state in the callee's Role Assignment Object Module. From these two states, the permission to access the method is derived.

Auth Verification

Auth verification then checks the resolved permission against the AuthZones in the current global context as well as the Global Caller's context.

In the above drawing, Call Frame 6 is making a new invocation and the AuthZones checked are 3/4/5/6, the AuthZones belonging to the current Global Context as well as the Global Caller's Context.

Costing / Limits

The Costing and Limits System is responsible for bounding physical resources used in a transaction. It does this by maintaining a System Module which interacts with the resource system such that resources used in transaction require a payment of some resource.

Royalties

Royalties allow a package deployer and a component owner to receive royalties on every function or method call.

This is implemented in two parts:

Package Royalties Blueprint Module

The package royalties blueprint module allows the blueprint owner to set royalties per function and withdraw earned royalties.

Component Royalties Object Module

The component royalties object module allows the owner to set royalties per method and withdraw earned royalties.

Metadata

Metadata stores string keys with metadata values for any object and package.

This is implemented as a Metadata Object Module.

Metadata Object Module

The metadata object module allows a user to set/update/delete metadata associated with a given object.

Genesis Bootstrap

Bootstrapping a Radix Engine requires flashing several system substates and then the execution of several genesis transactions.

Specifically, the substates of the Package blueprint and object module blueprints are flashed.

Once flashed, Package::publish calls may now be called to create the rest of the native blueprints.

Protocol Updates

Similar to genesis bootstrapping, a protocol update is a series of transactions which includes a set of substates to flash and a set of transactions to execute.