Tutorials
Deploy a smart rollup
Last Updated: 9th October 2023
This tutorial covers how to deploy a smart rollup in a Tezos sandbox. To run this tutorial, you should have a basic understanding of how Tezos works and the ability to use the command-line terminal on your computer. If you are new to Tezos, try the tutorial Deploy a smart contract first.
In this tutorial, you will learn:
- What a smart rollup is and how they help scale Tezos
- How information passes between Tezos and smart rollups
- How to respond to messages from Tezos in a smart rollup
What is a smart rollup?
Smart rollups are processing units that run outside the Tezos network but communicate with Tezos on a regular basis. These processing units can run arbitrarily large amounts of code without waiting for Tezos baking nodes to run and verify that code. Smart rollups use Tezos for information and transactions but can run large applications at their own speed, independently of the Tezos baking system.
In this way, smart rollups allow Tezos to scale to support large, complex applications without slowing Tezos itself. The processing that runs on Tezos itself is referred to as layer 1 and the processing that smart rollups run is referred to as layer 2.
Smart rollups can run any kind of applications that they want, such as:
- Financial applications that use information and transactions from Tezos
- Gaming applications that manipulate assets and keep them in sync with Tezos
- Applications that run complex logic on NFTs or other types of tokens
- Applications that communicate with other blockchains
Smart rollups stay in sync with Tezos by passing messages to Tezos and receiving messages from Tezos and other rollups. Each Tezos block contains a global rollups inbox that contains messages from Tezos layer 1 to all rollups. Anyone can add a message to this inbox and all messages are visible to all rollups. Rollups receive this inbox, filter it to the messages that they are interested in, and act on them accordingly.
Rollups also have an outbox, which consists of calls to smart contracts on layer 1. These calls are how rollups send messages back to Tezos.
Rollups maintain consensus by publishing the hash of their state to Tezos, which other nodes can use to verify the rollup's behavior. The specific way that rollups publish their states and maintain consensus is beyond the scope of this tutorial. For more information about rollups and their consensus mechanism, see Smart Optimistic Rollups.
This diagram shows a smart rollup interacting with layer 1 by receiving a message, running processing based on that message, and sending a transaction to layer 1:
Smart rollup analogy
Businesses talk about horizontal scaling versus vertical scaling. If a business is growing and its employees are being overworked, the business could use vertical scaling to hire more employees or use better tools to improve the productivity of each employee. Scaling Tezos in this way would mean using more processing power to process each new block, which would increase the cost to run baking nodes. Also, if the business hires more employees, the amount of communication between employees increases because, for example, they have to make sure that they are working in the same way and not doing duplicate jobs.
By contrast, smart rollups behave like horizontal scaling. In horizontal scaling, businesses create specialized teams that work on different portions of the workload. These teams can work independently of other teams and take advantage of efficiencies of being focused on a specific task. They also need to communicate less with other teams, which speeds up their work. Smart rollups are like separate horizontally scaled teams, with Tezos layer 1 as the source of communication between teams.
Prerequisites
To run this tutorial, make sure that the following tools are installed:
Rust
The application in this tutorial uses Rust because of its support for WebAssembly (WASM), the language that smart rollups use to communicate. Rollups can use any language that has WASM compilation support.
To install Rust via the
rustup
command, run this command:curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
You can see other ways of installing Rust at https://www.rust-lang.org.
Clang and LLVM
Clang and LLVM are required for compilation to WebAssembly. Version 11 or later of Clang is required. Here are instructions for installing the appropriate tools on different operating systems:
MacOS
brew install llvm export CC="$(brew --prefix llvm)/bin/clang"
Ubuntu
sudo apt-get install clang-11 export CC=clang-11
Fedora
dnf install clang export CC=clang
Arch Linux
pacman -S clang export CC=clang
The
export CC
command sets Clang as the default C/C++ compiler.After you run these commands, run
CC --version
to verify that you have version 11 or greater installed.Also, ensure that your version of Clang
wasm32
target with by running the commandCC -print-targets | grep WebAssembly
and verifying that the results includewasm32
.AR (macOS only)
To compile to WebAssembly on macOS, you need to use the LLVM archiver. If you've used Homebrew to install LLVM, you can configure it to use the archiver by running this command:
export AR="$(brew --prefix llvm)/bin/llvm-ar"
WebAssembly Toolkit
The the WebAssembly Toolkit (
wabt
) provides tooling for reducing (or stripping) the size of WebAssembly binaries (with thewasm-strip
command) and conversion utilities between the textual and binary representations of WebAssembly (including thewat2wasm
andwasm2wat
commands).Most distributions ship a
wabt
package, which you can install with the appropriate command for your operating system:MacOS
brew install wabt
Ubuntu
sudo apt install wabt
Fedora
dnf install wabt
Arch Linux
pacman -S wabt
To verify that
wabt
is installed, run the commandwasm-strip --version
and verify that the version is at least 1.0.31. If not, you can download this version directly and extract its files: https://github.com/WebAssembly/wabt/releases/tag/1.0.31. Then, whenever you have to usewasm-strip
, you can use.<path_to_wabt_1.0.31>/bin/wasm-strip
instead.
Tutorial application
Despite the number of command-line tools needed, the code for the core of the rollup itself is relatively simple. This core is called the kernel and is responsible for accepting messages from layer 1 and sending messages to layer 1.
The code for the tutorial application is here: https://gitlab.com/trili/hello-world-kernel.
The code for the kernel is in the src/lib.rs
file. It is written in the Rust programming language and looks like this:
use tezos_smart_rollup::inbox::InboxMessage; use tezos_smart_rollup::kernel_entry; use tezos_smart_rollup::michelson::MichelsonBytes; use tezos_smart_rollup::prelude::*; kernel_entry!(hello_kernel); fn handle_message(host: &mut impl Runtime, msg: impl AsRef<[u8]>) { if let Some((_, msg)) = InboxMessage::<MichelsonBytes>::parse(msg.as_ref()).ok() { debug_msg!(host, "Got message: {:?}\n", msg); } } pub fn hello_kernel(host: &mut impl Runtime) { debug_msg!(host, "Hello, kernel!\n"); while let Some(msg) = host.read_input().unwrap() { handle_message(host, msg); } }
This example kernel has these major parts:
- It imports resources that allow it to access and decode messages from layer 1.
- It runs the Rust macro
kernel_entry!
to set the main function for the kernel. - It declares the
handle_message
function, which accepts, decodes, and processes messages from layer 1. In this case, the function decodes the message (which is sent as a sequence of bytes) and prints it to the log. The function could call any other logic that the application needs to run. - It declares the
hello_kernel
function, which prints a logging message each time it is called and then runs thehandle_message
function on each message from layer 1.
You don't need to access the other files in the application directly, but here are descriptions of them:
src/lib.rs
: The Rust code for the kernelCargo.toml
: The dependencies for the build processrustup-toolchain.toml
: The required Rust versionsandbox_node.sh
: A script that sets up a Tezos sandbox for testing the rollup
The tutorial repository also includes two files that represent example message inboxes in layer 1 blocks:
empty_input.json
: An empty rollup message inboxtwo_inputs.json
: A rollup message inbox with two messages
Part 1: Configure the tutorial application
Follow these steps to get the application code and build it:
Clone the repository with the tutorial application:
git clone https://gitlab.com/trili/hello-world-kernel.git cd hello-world-kernel/
Configure Rust to build WebAssembly applications:
Verify that you have Rust version 1.66.0 or later installed by running
cargo --version
.If you have an earlier version of Rust, use the
rustup
command to use version 1.66.0:rustup override set 1.66.0
Add WASM as a compilation target for Rust by running this command:
rustup target add wasm32-unknown-unknown
Build the application by running this command:
cargo build --target wasm32-unknown-unknown
If the application builds correctly, the terminal shows a message that looks like "Finished dev [unoptimized + debuginfo] target(s) in 0.44s." You can see the compiled application in the
target/wasm32-unknown-unknown/debug
folder. In particular, the compiled kernel is in thehello_world_kernel.wasm
file.If you see a message that says "error: package
time v0.3.29
cannot be built because it requires rustc 1.67.0 or newer, while the currently active rustc version is 1.66.0" or "error: packagetime-core v0.1.2
cannot be built because it requires rustc 1.67.0 or newer, while the currently active rustc version is 1.66.0," run this command to use an earlier version of thetime
package:cargo update -p time@0.3.29 --precise 0.3.23
Then, run the
cargo build --target wasm32-unknown-unknown
command again.
Part 2: Start a Tezos sandbox
Follow these steps to set up a Tezos sandbox that you can use to test sending messages to the smart rollup. These steps use the Octez command-line client to set up a sandbox in a Docker container:
Pull the most recent Tezos Docker image, which contains the most recent version of Octez:
docker pull tezos/tezos:master
You can also install Octez directly on your system, but keeping it in Docker is faster and more convenient for running the tutorial application.
Make sure you are in the
hello-world-kernel
folder, at the same level as theCargo.toml
andsandbox_node.sh
files.Run this command to start the Docker image, open a command-line terminal in that image, and mount the
hello-world-kernel
folder in it:docker run -it --rm --volume $(pwd):/home/tezos/hello-world-kernel --entrypoint /bin/sh --name octez-container tezos/tezos:master
Your command-line prompt changes to indicate that it is now inside the running Docker container. This image includes the Octez command-line client and other Tezos tools. It also uses the docker
--volume
argument to mount the contents of thehello-world-kernel
folder in the container so you can use those files within the container.Verify that the container has the necessary tools by running these commands:
octez-node --version octez-smart-rollup-wasm-debugger --version octez-smart-rollup-node-alpha --version octez-client --version
Each of these commands should print a version number. The specific version number is not important as long as you retrieved the latest image with the
docker pull tezos/tezos:master
command.Don't close this terminal window or exit the Docker terminal session, because Docker will close the container. If you accidentally close the container, you can run the
docker run ...
command again to restart it.Within the container, go to the
hello-world-kernel
folder:cd hello-world-kernel
Also inside the container, start the Tezos sandbox by running this command:
./sandbox_node.sh
This command starts a Tezos testing environment, including a baking node running in sandbox mode and a group of test accounts. The console shows repeated messages that show that the node is baking blocks. For more information about sandbox mode, see sandbox mode.
If you see an error that says "Unable to connect to the node," you can ignore it because it happens only once while the node is starting.
Leave that terminal instance running for the rest of the tutorial.
Open a new terminal window.
In the new terminal window, enter the Docker container by running this command:
docker exec -it octez-container /bin/sh
Now the second terminal window is running inside the container just like the first one.
In the second terminal window, run this command to verify that the sandbox is running with the correct protocol:
octez-client rpc get /chains/main/blocks/head/metadata | grep protocol
The response shows the protocol that the sandbox is running, as in this example:
{ "protocol": "ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK", "next_protocol": "ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK",
If you don't see a message that looks like this one, check for errors in the first terminal window.
Part 3: Run the kernel in debug mode
Octez provides a command named octez-smart-rollup-wasm-debugger
that runs smart rollups in debug mode to make it easier to test and observe them. Later, you will deploy the rollup to the sandbox, but running it in debug mode first verifies that it built correctly.
In the second terminal window inside the Docker container, go to the
hello_world_kernel
folder.Run this command to start the rollup and pass an empty message inbox to it:
octez-smart-rollup-wasm-debugger --kernel target/wasm32-unknown-unknown/debug/hello_world_kernel.wasm --inputs empty_input.json
The command prompt changes again to show that you are in debugging mode, which steps through commands.
At the debugging prompt, run this command to send the message inbox to the kernel:
step inbox
The response shows the logging information for the kernel, including these parts:
- The message "Hello, kernel" from the
hello_kernel
function - The message "Got message: Internal(StartOfLevel)," which represents the start of the message inbox
- The message "Got message: Internal(InfoPerLevel(InfoPerLevel ...," which provides the hash of the previous block
- The message "Got message: Internal(EndOfLevel)," which represents the end of the message inbox
Now you know that the kernel works. In the next section, you optimize and deploy it to the sandbox.
- The message "Hello, kernel" from the
Press Ctrl + C to end debugging mode.
Part 4: Optimize the kernel
Because the kernel is deployed on Tezos level 1, it must be efficient with space and processing power. In particular, the size of the kernel needs to be smaller than the manager operation size limit.
Run this command to print the current size of the kernel:
du -h target/wasm32-unknown-unknown/debug/hello_world_kernel.wasm
You can run this command inside or outside of the Docker container.
The size of the application and its dependencies may be 18MB or more, which is too large to deploy to Tezos.
In a terminal window outside of the Docker container, run the
wasm-strip
command to reduce the size of the kernel:wasm-strip target/wasm32-unknown-unknown/debug/hello_world_kernel.wasm
This command removes WebAssembly code that is not necessary to run rollups. You must run this command outside of the Docker container because it does not have the
wasm-strip
command.Run the
du
command again to see the new size of the kernel:du -h target/wasm32-unknown-unknown/debug/hello_world_kernel.wasm
The size of the kernel is smaller now. Note that the changes that you make to the kernel outside of the Docker container also appear in the container because the folder is mounted with the Docker
--volume
argument.To optimize the kernel further, you can convert it to an installer kernel, which includes only enough information to start the kernel, like an installation program. This installer kernel keeps the rest of its logic and data in separate files called preimages.
Outside of the Docker container, run this command to install the installer kernel tool:
cargo install tezos-smart-rollup-installer
Outside of the Docker container, run this command to convert the kernel to an installer kernel:
smart-rollup-installer get-reveal-installer --upgrade-to target/wasm32-unknown-unknown/debug/hello_world_kernel.wasm --output hello_world_kernel_installer.hex --preimages-dir preimages/
This command creates the following files:
hello_world_kernel_installer.hex
: The hexadecimal representation of the installer kernelpreimages/
: A directory that contains the preimages that allow nodes to restore the installer kernel to the original kernel code
When a node runs the installer kernel, it retrieves the preimages through the reveal data channel. For more information about the reveal data channel, see reveal data channel.
Verify the size of the installer kernel by running this command:
du -h hello_world_kernel_installer.hex
Now the kernel is small enough to be deployed on layer 1. In fact, when it is deployed, it will be even smaller than the result of this command because the command is checking the hexadecimal representation and the deployed kernel will be in binary.
Part 5: Deploy (originate) the kernel
Smart rollup kernels are originated to Tezos in a way similar to smart contracts. Instead of running the octez-client originate contract
, you run the octez-client originate smart rollup
command.
In the second terminal window, in the Docker container, in the
hello-world-kernel
folder, run this command to deploy the installer kernel to the Tezos sandbox:octez-client originate smart rollup "test_smart_rollup" from "bootstrap1" of kind wasm_2_0_0 of type bytes with kernel file:hello_world_kernel_installer.hex --burn-cap 3
Like the command to originate a smart contract, this command uses the
--burn-cap
argument to allow the transaction to take fees from the account. Also like deploying a smart contract, the response in the terminal shows information about the transaction and the address of the originated smart rollup, which starts withsr1
.If you need to open a new terminal window within the Docker container, run the command
docker exec -it octez-container /bin/sh
.
Part 6: Run the smart rollup node
Now that the smart rollup kernel is set up on level 1, anyone can run a smart rollup node based on that kernel. Smart rollup nodes are similar to baking nodes, but they run the smart rollup code instead of baking Tezos blocks. In these steps, you start a smart rollup node, but note that anyone can run a node based on your kernel, including people who want to verify the rollup's behavior.
Copy the contents of the preimages folder to a folder that the rollup node can access by running these commands:
mkdir -p ~/.tezos-rollup-node/wasm_2_0_0 cp preimages/* ~/.tezos-rollup-node/wasm_2_0_0/
In the second terminal window, in the Docker container, start the rollup node:
octez-smart-rollup-node-alpha run operator for "test_smart_rollup" with operators "bootstrap2" --data-dir ~/.tezos-rollup-node/ --log-kernel-debug --log-kernel-debug-file hello_kernel.debug
Now the node is running and writing to the log file
hello_kernel.debug
. Leave this command running in the terminal window just like you left the first terminal window running the Tezos sandbox.Open a third terminal window and enter the Docker container again:
docker exec -it octez-container /bin/sh
In the container, go to the
hello_world_kernel
folder.Print the contents of the log file:
tail -f hello_kernel.debug
Now, each time a block is baked, the smart rollup node prints the contents of the messages in the smart rollup inbox, as in this example:
# Hello, kernel! # Got message: Internal(StartOfLevel) # Got message: Internal(InfoPerLevel(InfoPerLevel { predecessor_timestamp: 2023-06-07T15:31:09Z, predecessor: BlockHash("BLQucC2rFyNhoeW4tuh1zS1g6H6ukzs2DQDUYArWNALGr6g2Jdq") })) # Got message: Internal(EndOfLevel)
Stop the command by pressing Ctrl + C.
Run this command to watch for external messages to the rollup:
tail -f hello_kernel.debug | grep External
No output appears at first because the rollup has not received any messages aside from the internal messages that indicate the beginning and end of the inbox.
Leave this command running.
Open a fourth terminal window, enter the Docker container with the command
docker exec -it octez-container /bin/sh
, and go to thehello_world_kernel
folder.In this fourth terminal window, run this command to simulate adding a message to the smart rollup inbox:
octez-client send smart rollup message '[ "test" ]' from "bootstrap3"
Go back to the third terminal window.
This window shows the message that you sent in the fourth window, which it received in binary format, with the numbers representing the letters in "test."
Got message: External([116, 101, 115, 116])
Now you can send messages to this rollup via Tezos level 1 and act on them in the rollup code.
Next steps
Currently, your rollup and kernel are running in sandbox mode. If you want to explore further, you can try deploying the rollup to a testnet as you do in the Deploy a smart contract tutorial. The workflow for deploying to a testnet is similar to the workflow that you used to deploy to the sandbox:
- Configure the network to use the testnet
- Run a node (needs to synchronize with the network — can make use of snapshots)
- Create or import an account and fun it by a faucet
- Originate the rollup to the testnet
- Start the rollup node
- Check the log file
You can also explore other smart rollup examples from the dedicated kernel gallery or create your own.