Virtual Machine
Last updated
Last updated
L1X Virtual Machine (L1X VM) provides a secure and deterministic environment for the execution of smart contracts on the L1X blockchain. It ensures determinism in the smart contract execution lifecycle through the process of transaction and block creation. It limits the boundaries of smart contract execution by providing an isolated environment that restricts its interference with the blockchain network.
The entire smart contract development flow goes through three phases:
Frontend. A developer writing a smart contract on L1X. They will be using Rust programming language which provides a safe and efficient environment for contract development. L1X Smart Contract SDK provides them with a safe API that they can use to build such contracts.
Middle End. The code goes through various transformations to reach its final form.
Macro pre-processing. By leveraging Rust’s powerful metaprogramming capabilities, high-level abstractions for the ease of development on L1X are created.
WASM. The LLVM compiler infrastructure is used for compiling and optimizing Rust code into WASM.
WASM -> eBPF. L1X compiler takes the generated WASM file and compiles it to eBPF bytecode.
Backend. The resulting ELF file with eBPF bytecode is the contract binary that the developer publishes on-chain.
L1X VM. The Layer One X Virtual Machine is responsible for executing the smart contracts in a secure and efficient manner, integrating with the L1X blockchain and handling state updates.
The L1X VM is designed to provide a robust and efficient environment for executing smart contracts on the Layer One X blockchain platform. It is built on top of the eBPF bytecode interpreter and leverages the LLVM compiler infrastructure to compile and optimize smart contracts written in the Rust programming language.
The following comprise the components of the L1X VM:
Contract SDK is a collection of three crates (going from lowest level to highest level):
l1x-sys: A header file specifying all of the available system calls using their native signatures. A developer is unlikely to use any of these directly unless they want to build something very low-level without involving l1x-sdk.
l1x-sdk-macros: A collection of macros that create an abstraction over some of the fine details in VM. For example, it enables developers to accept arguments and return values as if this was just a regular Rust program.
l1x-sdk: Contains two main things right now: higher-level system calls (e.g., a low-level system call deals with raw pointers and data length, while a high-level system call just accepts &str) and on-chain collections.
L1X System Calls can be categorized as listed below:
Register API: Provides a register abstraction between guest and host machines. These system calls enable guests to write into registers, read from registers, and access the length of data contained within. Please note that these registers are different from the actual WASM or eBPF registers.
Storage API: Provides guest machine with capabilities to store data on the trie (i.e. a key- value storage that is a part of the L1X consensus). The system calls enable guests to read, write or remove data from the trie.
Context API: Provides guest machine access to the context in which the current call is being executed. Input writes the user-supplied arguments in the specified register. Output registers a certain byte slice as the result of this call’s execution.
Misc. API: Other system calls are covered under this category. panic interrupts the execution immediately. msg emits an on-chain message using the supplied byte slice as a UTF-8 string.
L1X-SDK-Macros provide pre-defined functionality at a higher level of abstraction. They list any constants, functions, or data types that are required for development on the L1X platform. Currently, this crate supplies one macro #[program] that translates conventional Rust code to something that employs l1x-sys primitives.
The L1X SDK is designed to provide all of the above plus high-level abstractions on top as a single dependency package.
Although a smart contract developer can choose to store data in the native Rust collections, sometimes storing them in an on-chain collection is a much smarter choice. Consider a situation where you have 100K different balance holders in a single fungible token contract. It is unreasonable to load the entire 100K-sized map when you only need to access one of the key-value pairs. This is where on-chain collections come in.
Currently, the following collection types are supported by the SDK:
LookupMap
LookupSet
Vector
L1X VM is designed to be able to run ELF files containing eBPF bytecode in a secure and self- contained environment. This is done in two steps:
Relocation: Any eBPF file is a relocatable object file, meaning it has specific ELF sections that can be used to relocate .text instructions, .data bytes, and function calls. This is done by inspecting the .rel sections in the object file and following specific logic implemented in l1x-ebpf-runtime crate as follows:
.text: Whenever the eBPF program tries to access a global variable, there is a matching .rel entry that can be used to locate the ld*/st* instruction and change it. All global variables are relocated to the metadata buffer.
.data: Const values are written to a .rodata.* subsection that is also relocated to the metadata buffer (but with a different offset to the global variables from text).
Calls: Usually, eBPF does not support calling internal functions, but our variant of eBPF needs to have this capability to be on par with WASM. Calls instructions have src and imm sections. src is used to distinguish between internal and external calls (0 means external, everything else means internal). If it is an internal call, imm points to the callee’s offset the .text section.
Otherwise, imm has a MurmurHash3-hashed value of the external function’s name.
Interpretation: rBPF is used as the basis for L1X interpretation logic, yet it has been modified to handle some non-standard extensions such as internal functions.
L1X SDK is available to the developer to create smart contracts in the RUST programming language.
The Rust smart contracts contain L1X SDK macros that are converted to low-level Rust code using L1X System calls.
Rustc compiler compiles this low-level rust code into WASM bytecode.
L1X WASM eBPF Compiler compiles the WASM bytecode to eBPF Executable and Linkable Format i.e., eBPF ELF file.
Thus, the smart contract is deployed on the L1X blockchain as an eBPF ELF file.
Whenever a user calls any function of this deployed smart contract, ELF is relocated.
Modified rBPF acts as the interpreter which takes required details from on-chain trie and execution context to execute the transaction.
The result of the transaction execution is produced by the rBPF.
L1X core node creates the block containing the executed transaction.
The L1X Virtual Machine encompasses several fundamental components that contribute to its robustness and efficiency. These components can be categorized into libraries, custom optimization passes, and system calls.
The libraries play a crucial role in supporting various functionalities within the L1X Virtual Machine. The multi-chain wallet signing library enables secure signing of cross-chain transactions, facilitating seamless interaction between L1X and other chains. The multi-signature library is utilized for implementing multi-signature transactions, enhancing security and accommodating cross-chain transaction requirements. The state management library efficiently handles the storage, retrieval, and modification of data on the L1X blockchain. Additionally, the consensus mechanism integration library establishes a clear interface between the L1X VM and the underlying consensus process, ensuring accurate transaction execution and updates to the blockchain state.
Interoperability libraries further enhance the L1X Virtual Machine's capabilities by enabling secure communication and interaction with other connected blockchains. The decentralized adapters library provides functions and interfaces for implementing and managing decentralized adapters, facilitating seamless connectivity. The decentralized translator’s library assists in translating messages between different blockchains or applications within the same blockchain. The messaging standard library implements secure and efficient communication standards for the Interoperability Stack. The decentralized signing library ensures secure and authenticated communication between components. Lastly, the decentralized execution register library guarantees deterministic execution of transactions and prevents double-spending across multiple blockchains.
Custom fundamental optimization passes optimize various aspects of the L1X Virtual Machine. The instruction selection pass enhances performance and compatibility by optimizing the selection of eBPF instructions. The register allocation pass minimizes resource usage by optimizing register allocation within the eBPF bytecode. The state management pass optimizes the handling of state- related operations, improving efficiency and mitigating potential issues. The consensus mechanism integration pass ensures compliance with the requirements and constraints of the L1X consensus mechanism.
System calls provide essential functionalities for smart contracts running on the L1X Virtual Machine. The read system call allows contracts to retrieve data from the L1X blockchain state, while the write system call enables writing data to the blockchain. The send transaction system call initiates transactions on the L1X blockchain or other connected chains.
Interoperability system calls facilitate seamless communication and interaction with other blockchains, including inter-chain data transfer, cross-chain event listening, cross-chain function invocation, decentralized adapter registration, and decentralized execution register interaction.
These fundamental components collectively contribute to the robustness, efficiency, and interoperability of the L1X Virtual Machine, empowering developers to build complex and innovative decentralized applications on the Layer One X blockchain platform.
L1X Virtual Machine is a critical component of the L1X blockchain, enabling the execution of Rust smart contracts on the blockchain. As a result, creating the right virtual machine is essential to ensure optimal performance, efficient memory usage, and ease of use. This section focuses on a comparative study of two approaches, the WebAssembly (WASM) interpreter and the eBPF runtime in user space, and their performance in running smart contracts.
Here, the performance of WASM and eBPF for running smart contracts is compared. The metrics for comparison include the time taken for the contract to execute, the amount of memory used, and the lines of code required for the execution.
eBPF outperforms WASM in both execution time and memory usage. The eBPF programs execute faster than the WASM programs, with an average speed up of 2.5x. The eBPF programs also used less memory than the WASM programs, with an average memory usage reduction of 1.7x. The number of lines of code required was also significantly less for eBPF, averaging 20 compared to 60 for WASM.
Table 3 WASM vs eBPF
The study's results indicate that eBPF offers superior performance, memory usage, and ease of use compared to WASM when it comes to running smart contracts. The primary reason for this is eBPF's kernel-based nature, which allows it to leverage the full power of the kernel and use the entire kernel's resources. In contrast, WASM is a low-level bytecode format designed for web browsers and has limitations when it comes to memory usage and performance. The interpreter for WASM is also slower than the eBPF runtime.
eBPF's design provides several advantages over WASM. First, the eBPF runtime is more efficient in memory usage, enabling it to execute smart contracts using significantly less memory than WASM. This is crucial in environments where memory usage is a concern. Second, the eBPF runtime executes smart contracts significantly faster than the WASM interpreter, allowing the smart contracts to execute more quickly.
Additionally, eBPF's design allows it to be more user-friendly than WASM. It requires fewer lines of code to execute smart contracts, which reduces development time and cost. This can be especially important for companies looking to develop smart contracts for the blockchain, as it allows them to focus on the smart contract's functionality rather than the implementation details.
Stack-based execution involves the use of a stack data structure to manage program execution. The stack contains a sequence of instructions and data, and the program execution involves manipulating the stack through push and pop operations. Stack-based execution is relatively easy to implement and is therefore popular. On the other hand, register-based execution involves the use of registers to store and manipulate data during program execution. Registers are temporary storage locations within the CPU, and their use can result in faster program execution due to the reduced need to access memory.
The research study shows that eBPF has a higher performance than WASM, primarily due to its register-based architecture. The eBPF's use of registers provides faster access to data during program execution, resulting in improved performance. Here is a comparison of stack-based WASM and register-based eBPF virtual machines.
Table 4 Stack-based vs Register-based Virtual Machines
As shown in the table, register-based eBPF virtual machines have several advantages over stack-based WASM, including faster instruction execution speed, faster access to data, and lower memory usage. However, eBPF's instruction set is more complex than WASM, which can make it more difficult to implement.
In conclusion, eBPF is a more suitable technology for running smart contracts on the blockchain than WASM. eBPF provides superior performance, lower memory usage, and ease of use when it comes to executing smart contracts. Owing to these reasons L1X Virtual Machine harnesses the benefits of eBPF to provide high-speed performance.
Metrics
eBPF
WASM
Execution time
0.003 seconds
0.05 seconds
Memory Usage
6 MB
20 MB
Lines of code
20
60
Metrics
Stack-based WASM
Register-based eBPF
Memory
High
Low
Instruction Execution Speed
Low
High
Data Access
Slow
Fast
Instruction Set Complexity
Simple
Complex