GM World rollup
☀️ Introduction
In this tutorial, we will build a sovereign gm-world
rollup using Rollkit and Celestia’s data availability and consensus layer to submit Rollkit blocks.
This tutorial will cover setting up Ignite CLI, building a Cosmos-SDK application-specific rollup blockchain, and posting data to Celestia. First, we will test on a local DA network and then we will deploy to a live testnet.
The Cosmos SDK is a framework for building blockchain applications. The Cosmos Ecosystem uses Inter-Blockchain Communication (IBC) to allow blockchains to communicate with one another.
The development journey for your rollup will look something like this:
- Part one: Run your rollup and post DA to a local devnet, and make sure everything works as expected
- Part two: Deploy the rollup, posting to a DA testnet. Confirm again that everything is functioning properly
- Part three: Deploy your rollup to the DA layer's mainnet
TIP
This tutorial will explore developing with Rollkit, which is still in Alpha stage. If you run into bugs, please write a Github Issue ticket or let us know in our Telegram.
Learn how to restart your rollup.
WARNING
The script for this tutorial is built for Celestia's Arabica devnet.
🤔 What is GM?
GM means good morning. It's GM o'clock somewhere, so there's never a bad time to say GM, Gm, or gm. You can think of "GM" as the new version of "hello world".
Dependencies
- Operating systems: GNU/Linux or macOS
- Golang 1.20+
- Ignite CLI v0.27.1
- Homebrew
- wget
- A Celestia Light Node
TIP
If you are only planning to complete part one, feel free to skip to the part two.
Be sure to use the same testnet installation instructions through this entire tutorial.
Linux setup
🏃 Install Golang on Linux
Celestia-App, Celestia-Node, and Cosmos-SDK are written in the Golang programming language. You will need Golang to build and run them.
You can install Golang here.
🔥 Install Ignite CLI on Linux
First, you will need to create /usr/local/bin
if you have not already:
sudo mkdir -p -m 775 /usr/local/bin
Run this command in your terminal to install Ignite CLI:
curl https://get.ignite.com/[email protected]! | bash
TIP
✋ On some machines, you may run into permissions errors like the one below. You can resolve this error by following the guidance here or below.
# Error
jcs @ ~ % curl https://get.ignite.com/[email protected]! | bash
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3967 0 3967 0 0 16847 0 --:--:-- --:--:-- --:--:-- 17475
Installing ignite v0.27.1.....
######################################################################## 100.0%
mv: rename ./ignite to /usr/local/bin/ignite: Permission denied
============
Error: mv failed
The following command will resolve the permissions error:
sudo curl https://get.ignite.com/[email protected]! | sudo bash
A successful installation will return something similar to the response below:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4073 0 4073 0 0 4363 0 --:--:-- --:--:-- --:--:-- 4379
Installing ignite v0.27.1.....
######################################################################## 100.0%
Password:
Installed at /usr/local/bin/ignite
Verify you’ve installed Ignite CLI by running:
ignite version
The response that you receive should look something like this:
jcs @ ~ % ignite version
Ignite CLI version: v0.27.1
Ignite CLI build date: 2023-06-13T13:42:09Z
Ignite CLI source hash: 4acd1f185afb6d8d1a837e54f04c091121cfae01
Ignite CLI config version: v1
Cosmos SDK version: v0.47.3
Your OS: darwin
Your arch: arm64
Your Node.js version: v20.4.0
Your go version: go version go1.20.2 darwin/arm64
Your uname -a: Darwin Joshs-MacBook-Air.local 22.5.0 Darwin Kernel Version 22.5.0: Thu Jun 8 22:21:34 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T8112 arm64
Your cwd: /Users/joshstein
Is on Gitpod: false
macOS setup
TIP
If you are only planning to complete part one, feel free to skip to the part two.
Be sure to use the same testnet installation instructions through this entire tutorial.
🏃 Install Golang on macOS
Celestia-App, Celestia-Node, and Cosmos-SDK are written in the Golang programming language. You will need Golang to build and run them.
You can install Golang here.
🔥 Install Ignite CLI on macOS
First, you will need to create /usr/local/bin
if you have not already:
sudo mkdir -p -m 775 /usr/local/bin
Run this command in your terminal to install Ignite CLI:
curl https://get.ignite.com/[email protected]! | bash
TIP
✋ On some machines, you may run into permissions errors like the one below. You can resolve this error by following the guidance here or below.
# Error
jcs @ ~ % curl https://get.ignite.com/[email protected]! | bash
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3967 0 3967 0 0 16847 0 --:--:-- --:--:-- --:--:-- 17475
Installing ignite v0.27.1.....
######################################################################## 100.0%
mv: rename ./ignite to /usr/local/bin/ignite: Permission denied
============
Error: mv failed
The following command will resolve the permissions error:
sudo curl https://get.ignite.com/[email protected]! | sudo bash
A successful installation will return something similar the response below:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3967 0 3967 0 0 15586 0 --:--:-- --:--:-- --:--:-- 15931
Installing ignite v0.27.1.....
######################################################################## 100.0%
Installed at /usr/local/bin/ignite
Verify you’ve installed Ignite CLI by running:
ignite version
The response that you receive should look something like this:
jcs @ ~ % ignite version
Ignite CLI version: v0.27.1
Ignite CLI build date: 2023-06-13T13:42:09Z
Ignite CLI source hash: 4acd1f185afb6d8d1a837e54f04c091121cfae01
Ignite CLI config version: v1
Cosmos SDK version: v0.47.3
Your OS: darwin
Your arch: arm64
Your Node.js version: v20.4.0
Your go version: go version go1.20.2 darwin/arm64
Your uname -a: Darwin Joshs-MacBook-Air.local 22.5.0 Darwin Kernel Version 22.5.0: Thu Jun 8 22:21:34 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T8112 arm64
Your cwd: /Users/joshstein
Is on Gitpod: false
🍺 Install Homebrew on macOS
Homebrew will allow us to install dependencies for our Mac:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Be sure to run the commands similar to the output below from the successful installation:
==> Next steps:
- Run these three commands in your terminal to add Homebrew to your PATH:
echo '# Set PATH, MANPATH, etc., for Homebrew.' >> /Users/joshstein/.zprofile
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/joshstein/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
🏃 Install wget on macOS
wget is an Internet file retriever:
brew install wget
Part one
This part of the tutorial will teach developers how to easily run a local data availability (DA) devnet on their own machine (or in the cloud). Running a local devnet for DA to test your rollup is the recommended first step before deploying to a testnet. This eliminates the need for testnet tokens and deploying to a testnet until you are ready.
WARNING
Part one of the tutorial has only been tested on an AMD machine running Ubuntu 22.10 x64.
Whether you're a developer simply testing things on your laptop or using a virtual machine in the cloud, this process can be done on any machine of your choosing. We tested out the Devnet section (part one) on a machine with the following specs:
- Memory: 1 GB RAM
- CPU: Single Core AMD
- Disk: 25 GB SSD Storage
- OS: Ubuntu 22.10 x64
💻 Prerequisites
- Docker installed on your machine
🏠 Running local devnet with a Rollkit rollup
First, run the local-celestia-devnet
by running the following command:
docker run -i -t --platform linux/amd64 -p 26657:26657 -p 26658:26658 -p 26659:26659 ghcr.io/rollkit/local-celestia-devnet:v0.11.0
When passing the --rollkit.da_config
flag later in the tutorial, it will require auth_token
to be passed in. The auth token with write permission is required to submit blobs and can be obtained with the following command once your local-celestia-devnet is running:
docker exec $(docker ps -q) celestia bridge --node.store /home/celestia/bridge/ auth admin
This will give you the local-celestia-devnet bridge node auth token. This assumes that there is only one container, otherwise you can pass the container name.
We'll use the variable later on to start our rollup.
🔎 Query your balance
Open a new terminal instance. Check the balance on your account that you'll be using to post blocks to the local network, this will make sure you can post rollup blocks to your Celestia Devnet for DA & consensus.
First, set your auth token:
export CELESTIA_NODE_AUTH_TOKEN=$(docker exec $(docker ps -q) celestia bridge --node.store /home/celestia/bridge/ auth admin)
Next, check your balance:
docker exec $(docker ps -q) celestia state balance --token $CELESTIA_NODE_AUTH_TOKEN
You will see something like this, denoting your balance in TIA x 10-6:
{
"result": {
"denom": "utia",
"amount": "999994999970000"
}
}
🏗️ Building your sovereign rollup
Now that you have a Celestia devnet running, we are ready to use Golang to build and run our Cosmos-SDK blockchain.
The Ignite CLI comes with scaffolding commands to make development of blockchains quicker by creating everything that is needed to start a new Cosmos SDK blockchain.
Check your version:
ignite version
Open a new tab or window in your terminal and run this command to scaffold your rollup. Scaffold the chain:
cd $HOME
ignite scaffold chain gm --address-prefix gm
TIP
The --address-prefix gm
flag will change the address prefix from cosmos
to gm
. Read more on the Cosmos docs.
The response will look similar to below:
WARNING
Do not run ignite chain serve
as we will build the chain later in the tutorial.
jcs @ ~ % ignite scaffold chain gm --address-prefix gm
⭐️ Successfully created a new blockchain 'gm'.
👉 Get started with the following commands:
% cd gm
% ignite chain serve
Documentation: https://docs.ignite.com
This command has created a Cosmos SDK blockchain in the gm
directory. The gm
directory contains a fully functional blockchain. The following standard Cosmos SDK modules have been imported:
staking
- for delegated Proof-of-Stake (PoS) consensus mechanismbank
- for fungible token transfers between accountsgov
- for on-chain governancemint
- for minting new units of staking tokennft
- for creating, transferring, and updating NFTs- and more
Change to the gm
directory:
cd gm
You can learn more about the gm
directory’s file structure here. Most of our work in this tutorial will happen in the x
directory.
🗞️ Install Rollkit
To swap out CometBFT for Rollkit, run the following command from inside the gm
directory:
go mod edit -replace github.com/cosmos/cosmos-sdk=github.com/rollkit/[email protected]
go mod edit -replace github.com/gogo/protobuf=github.com/regen-network/[email protected]
go mod tidy
go mod download
go mod edit -replace github.com/cosmos/cosmos-sdk=github.com/rollkit/[email protected]
go mod edit -replace github.com/gogo/protobuf=github.com/regen-network/[email protected]
go mod tidy
go mod download
▶️ Start your rollup
Download the init-local.sh
script to start the chain:
# From inside the `gm` directory
wget https://raw.githubusercontent.com/rollkit/docs/main/scripts/gm/init-local.sh
Next, you'll need to set the auth token in your terminal to be consumed by your init-local.sh
script.
In the terminal that you will run the script in, set the auth token for the local-celestia-devnet. This is so that you can post data to the local DA.
Remember that the following command assumes that there is only one container, otherwise you can pass the container name.
export AUTH_TOKEN=$(docker exec $(docker ps -q) celestia bridge --node.store /home/celestia/bridge/ auth admin)
Run the init-local.sh
script:
bash init-local.sh
This will start your rollup, connected to the local Celestia devnet you have running.
Now let's explore a bit.
TIP
If you are restarting your rollup, you'll need to clear the old chain history and binary:
rm -rf $HOME/.gm
rm $HOME/go/bin/gmd
🔑 Keys
List your keys:
gmd keys list --keyring-backend test
You should see an output like the following
- address: gm1sa3xvrkvwhktjppxzaayst7s7z4ar06rk37jq7
name: gm-key-2
pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AlXXb6Op8DdwCejeYkGWbF4G3pDLDO+rYiVWKPKuvYaz"}'
type: local
- address: gm13nf52x452c527nycahthqq4y9phcmvat9nejl2
name: gm-key
pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AwigPerY+eeC2WAabA6iW1AipAQora5Dwmo1SnMnjavt"}'
type: local
💸 Transactions
Now we can test sending a transaction from one of our keys to the other. We can do that with the following command:
gmd tx bank send [from_key_or_address] [to_address] [amount] [flags]
Set your keys as variables to make it easier to add the address:
export KEY1=gm1sa3xvrkvwhktjppxzaayst7s7z4ar06rk37jq7
export KEY2=gm13nf52x452c527nycahthqq4y9phcmvat9nejl2
So using our information from the keys command, we can construct the transaction command like so to send 42069stake from one address to another:
gmd tx bank send $KEY1 $KEY2 42069stake --keyring-backend test \
--node tcp://127.0.0.1:36657
TIP
We're using the --node [ip:port]
flag to point to port 36657, which is the custom port we used in the init-local.sh
script to avoid clashing with 26657 on local-celestia-devnet. We set it here:
--rpc.laddr tcp://127.0.0.1:36657
You'll be prompted to accept the transaction:
auth_info:
fee:
amount: []
gas_limit: "200000"
granter: ""
payer: ""
signer_infos: []
tip: null
body:
extension_options: []
memo: ""
messages:
- '@type': /cosmos.bank.v1beta1.MsgSend
amount:
- amount: "42069"
denom: stake
from_address: gm1sa3xvrkvwhktjppxzaayst7s7z4ar06rk37jq7
to_address: gm13nf52x452c527nycahthqq4y9phcmvat9nejl2
non_critical_extension_options: []
timeout_height: "0"
signatures: []
confirm transaction before signing and broadcasting [y/N]:
Type y
if you'd like to confirm and sign the transaction. Then, you'll see the confirmation:
code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 677CAF6C80B85ACEF6F9EC7906FB3CB021322AAC78B015FA07D5112F2F824BFF
⚖️ Balances
Then, query your balance:
gmd query bank balances $KEY2 --node tcp://127.0.0.1:36657
This is the key that received the balance, so it should have increased past the initial STAKING_AMOUNT
:
balances:
- amount: "10000000000000000000042069"
denom: stake
pagination:
next_key: null
total: "0"
The other key, should have decreased in balance:
gmd query bank balances $KEY1 --node tcp://127.0.0.1:36657
Response:
balances:
- amount: "9999999999999999999957931"
denom: stake
pagination:
next_key: null
total: "0"
Part two
🪶 Run a Celestia light node
Follow instructions to install and start your Celestia data availability layer light node selecting the Arabica network. You can find instructions to install and run the node here.
After you have Go and Ignite CLI installed, and your Celestia Light Node running on your machine, you're ready to build, test, and launch your own sovereign rollup.
An example start command on arabica-9
would look like this:
celestia light start --core.ip consensus-full-arabica-9.celestia-arabica.com --p2p.network arabica
💬 Say gm world
Now, we're going to get our blockchain to say gm world!
- in order to do so you need to make the following changes:
- Modify a protocol buffer file
- Create a keeper query function that returns data
Protocol buffer files contain proto RPC calls that define Cosmos SDK queries and message handlers, and proto messages that define Cosmos SDK types. The RPC calls are also responsible for exposing an HTTP API.
The Keeper
is required for each Cosmos SDK module and is an abstraction for modifying the state of the blockchain. Keeper functions allow us to query or write to the state.
✋ Create your first query
Open a new terminal instance that is not the same that you started the chain in.
In your new terminal, cd
into the gm
directory and run this command to create the gm
query:
ignite scaffold query gm --response text
Response:
modify proto/gm/gm/query.proto
modify x/gm/client/cli/query.go
create x/gm/client/cli/query_gm.go
create x/gm/keeper/query_gm.go
🎉 Created a query `gm`.
What just happened? query
accepts the name of the query (gm
), an optional list of request parameters (empty in this tutorial), and an optional comma-separated list of response field with a --response
flag (text
in this tutorial).
Navigate to the gm/proto/gm/gm/query.proto
file, you’ll see that Gm
RPC has been added to the Query
service:
service Query {
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/gm/gm/params";
}
rpc Gm(QueryGmRequest) returns (QueryGmResponse) {
option (google.api.http).get = "/gm/gm/gm";
}
}
The Gm
RPC for the Query
service:
- is responsible for returning a
text
string - Accepts request parameters (
QueryGmRequest
) - Returns response of type
QueryGmResponse
- The
option
defines the endpoint that is used by gRPC to generate an HTTP API
📨 Query request and response types
In the same file, we will find:
QueryGmRequest
is empty because it does not require parametersQueryGmResponse
containstext
that is returned from the chain
message QueryGmRequest {
}
message QueryGmResponse {
string text = 1;
}
👋 Gm keeper function
The gm/x/gm/keeper/query_gm.go
file contains the Gm
keeper function that handles the query and returns data.
func (k Keeper) Gm(goCtx context.Context, req *types.QueryGmRequest) (*types.QueryGmResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}
ctx := sdk.UnwrapSDKContext(goCtx)
_ = ctx
return &types.QueryGmResponse{}, nil
}
The Gm
function performs the following actions:
- Makes a basic check on the request and throws an error if it’s
nil
- Stores context in a
ctx
variable that contains information about the environment of the request - Returns a response of type
QueryGmResponse
Currently, the response is empty and you'll need to update the keeper function.
Our query.proto
file defines that the response accepts text
. Use your text editor to modify the keeper function in gm/x/gm/keeper/query_gm.go
.
func (k Keeper) Gm(goCtx context.Context, req *types.QueryGmRequest) (*types.QueryGmResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}
ctx := sdk.UnwrapSDKContext(goCtx)
_ = ctx
return &types.QueryGmResponse{Text: "gm world!"}, nil
}
🟢 Start your sovereign rollup
We have a handy init-testnet.sh
found in this repo here.
We can copy it over to our directory with the following commands:
# From inside the `gm` directory
wget https://raw.githubusercontent.com/rollkit/docs/main/scripts/gm/init-testnet.sh
This copies over our init-testnet.sh
script to initialize our gm
rollup.
You can view the contents of the script to see how we initialize the gm rollup.
Clear previous chain history
Before starting the rollup, we need to remove the old project folders:
rm -r $HOME/go/bin/gmd && rm -rf $HOME/.gm
Set the auth token for your light node
You will also need to set the auth token for your Celestia light node before running the rollup. In the terminal that you will run the init-testnet.sh
script in, run the following:
export AUTH_TOKEN=$(celestia light auth admin --p2p.network arabica)
Start the new chain
Now, you can initialize the script with the following command:
bash init-testnet.sh
With that, we have kickstarted our second gmd
network!
The query
command has also scaffolded x/gm/client/cli/query_gm.go
that implements a CLI equivalent of the gm query and mounted this command in x/gm/client/cli/query.go
.
In a separate window, run the following command:
gmd q gm gm
We will get the following JSON response:
text: gm world!
Congratulations 🎉 you've successfully built your first rollup and queried it!
If you're interested in looking at the demo repository for this tutorial, you can at https://github.com/rollkit/gm.
Part three
In this section, we will cover how to deploy to Celestia's Mainnet Beta.
For this portion, you will need to stop the rollup that you have running from above using Control + C
in the terminal.
Start your Celestia light node with state access (using the
--core.ip string
flag), this time oncelestia
, which is the chain ID for Mainnet Beta.bashcelestia light start --core.ip rpc.celestia.pops.one
Download the script for deploying to Celestia's Mainnet Beta:
bash# From inside the `gm` directory wget https://raw.githubusercontent.com/rollkit/docs/main/scripts/gm/init-mainnet.sh
Ensure that the account for your light node is funded.
Run the
init-mainnet.sh
script:bashbash init-mainnet.sh
Watch as your rollup posts blocks to Celestia!
To deploy to a different DA layer, modify the script to fit your architecture.
Next steps
If you're interested in setting up a full node alongside your sequencer, see the Full and sequencer node rollup setup tutorial.