RADIX WikiRADIX Wiki

Introduction

The kernel is the lowest layer of the Radix Engine. It owns four things: nodes and the substates they hold; the call frames that run against them; the rules deciding which frame may reach which node; and the charge applied to each of those operations. It has no notion of a resource, a blueprint, a badge or a role. Those are defined by the system layer above it, and the kernel moves the state that carries them without reading it.

What the Kernel Owns

The module list in radix-engine/src/kernel/mod.rs maps one to one onto those responsibilities. kernel.rs holds the boot sequence and the execution loop. call_frame.rs defines a frame and the table of references it may use. substate_io.rs and substate_locks.rs carry the read and write path and the locks that serialise it. heap.rs stores state created during a transaction and not yet committed. id_allocator.rs hands out node identifiers. The remaining two are the interfaces to the layer above: kernel_api.rs for calls downward, and kernel_callback_api.rs for calls back upward.

That second interface is how the engine charges for anything. KernelCallbackObject, which the source describes as the "upper layer callback object which a kernel interacts with during execution", declares a hook for every kernel operation there is: on_create_node, on_drop_node, on_open_substate, on_read_substate, on_write_substate, on_scan_keys, on_allocate_node_id and a dozen more. The system layer implements them, and its costing module runs inside each one. The kernel therefore does not know what a fee is; it announces what it just did, and something above it decides the price.

Nodes, Substates and Two Devices

A node is an addressed entity and a substate is a unit of state held under one, addressed by partition and key. Every read and write in the engine resolves to a substate on one of exactly two devices, and SubstateDevice in substate_io.rs has exactly two variants to say which: Heap and Store. The heap holds nodes created during the transaction now running. The store holds committed ledger state. A new object begins on the heap, and moves to the store when it is globalized.

The distinction is what makes an uncommitted object cheap. A blueprint that builds a component, fills its fields and then fails has touched the heap only, and nothing it wrote reaches the database. It also sets the boundary the substate locks defend: two frames holding the same substate open at once is a conflict the kernel refuses rather than resolves.

What a Call Frame Can Reach

A call frame may only touch a node it can see, and Visibility in call_frame.rs enumerates the three ways it can: a stable reference, either to a global address or to a directly accessed node; frame-owned, meaning the frame holds the node outright; and borrowed, meaning the frame reached it through something else and the origin is recorded.

Each invocation opens a new frame, and the caller sends a CallFrameMessage saying which nodes move to the callee, which global references are copied and which direct-access references are copied. The source is explicit that the message is "just an intent, not checked/allowed by kernel yet": the kernel validates it before the callee runs, and that validation is where ownership transfer is enforced. A bucket passed into a component leaves the caller’s frame, so after the move the caller holds no reference to it and cannot spend it twice. No blueprint code performs that check, and none can decline it.

One interface manages several frame stacks rather than one. KernelStackApi exposes a current stack id, a context switch between stacks, and a way to move objects from the current frame to another stack; its only caller in the engine is multithread_intent_processor.rs, the processor for subintents. Each intent in a multi-intent transaction runs on its own stack, and yielding between intents is a kernel-level context switch.

Kernel Versions

The kernel carries a version on the ledger, read before execution, in the same way the system layer does. KernelBoot lives on the boot-loader partition of the transaction tracker, and when the substate is absent the engine falls back to V1, which is the state of a ledger that has not left Babylon. Bottlenose wrote the substate for the first time, at V1. Cuttlefish asserts the stored value is V1 and replaces it with V2, and panics rather than continuing if it finds anything else.

V2 carries one field, and it selects which set of always-visible global nodes each call frame starts with. Those are well-known addresses a blueprint may reference without having declared the dependency when its package was published: 25 of them under V1 and 26 under V2. The address V2 adds is the Locker package, behind the account locker that Bottlenose introduced – published one update earlier, reachable without a declared dependency one update later. The list is not meant to last: a comment above it calls it "a temporary solution", removable once Scrypto can declare dependencies and bootstrapping is split into state flushing and transaction execution.

HydrateLast updated Sep 21, 2026v2.0.06 revisionsVerified Sep 21, 2026