An application-specific blockchain built using Cosmos SDK and created with Ignite CLI. The blockchain offers a variety of zkSNARK implementations to verify proofs sent over transactions, and stores their results.
Cosmos SDK provides a framework to build an application layer on top of a consensus layer interacting via ABCI (Application BlockChain Interface). By default, CometBFT (a fork of Tendermint) is used in the consensus and network layer.
Ignite CLI is used to generate boilerplate code for a Cosmos SDK application, making it easier to deploy a blockchain to production.
- Table of Contents
- Requirements
- Example Local Blockchain
- Verifiers
- Trying our testnet
- Joining Our Testnet
- Joining Our Testnet With Docker
- Testnet public IPs
- How It Works
- Tutorials
- Troubleshooting
- Acknowledgements
To run a single node blockchain, run:
make run-macos # or
make run-linux
This command installs dependencies, builds, initializes, and starts your blockchain in development.
Before starting, you must define a chain-id, e.g. export CHAIN_ID=alignedlayer
.
You can try to send an example proof used in the repo with the following command:
alignedlayerd tx verify gnark-plonk --from alice --chain-id $CHAIN_ID \
$(cat ./prover_examples/gnark_plonk/example/proof.base64.example) \
$(cat ./prover_examples/gnark_plonk/example/public_inputs.base64.example) \
$(cat ./prover_examples/gnark_plonk/example/verifying_key.base64.example)
This will output the transaction result (usually containing default values as it doesn't wait for the blockchain to execute it), and the transaction hash.
...
txhash: F105EAD99F96289914EF16CB164CE43A330AEDB93CAE2A1CFA5FAE013B5CC515
To get the transaction result, run:
alignedlayerd query tx <txhash> | grep proof_verifies -B 10
Information on the parameters received by the CLI when sending transactions can be found by running:
alignedlayerd tx verify --help
Currently, verify supports four proof systems: gnark-plonk, cairo-platinum, sp1 and kimchi.
Upon verification, the transaction produces an event called verification_finished
which contains a boolean attriute proof_verifies
indicating the result.
We also provide the script send_verify_tx.sh to send verification transactions. You may use it according to the following syntax:
bash send_verify_tx.sh <verifier> <account> <proof_file>
If you want to generate a Gnark Plonk proof by yourself, you must edit the circuit definition and witness in ./prover_examples/gnark_plonk/gnark_plonk.go
and run the following command:
go run ./prover_examples/gnark_plonk/gnark_plonk.go
This will compile the circuit and create a proof in the root folder that is ready to be sent with:
alignedlayerd tx verify gnark-plonk --from alice --chain-id $CHAIN_ID \
$(cat proof.base64) \
$(cat public_inputs.base64) \
$(cat verifying_key.base64)
To send a Cairo Platinum verification transaction, we can use the following script, which generates the proof manually by reading the file in order to bypass the shell limit (the size of Cairo proofs tends to be large).
bash send_verify_tx.sh cairo-platinum alice ./prover_examples/cairo_platinum/example/fibonacci_10.proof
If we need, we can set GAS and FEES as env vars before running the script.
Tip
The script already converts the .proof
to .proof.base64
, but base64
can be used as follows to encode the proofs:
base64 -i ./prover_examples/cairo_platinum/example/fibonacci_10.proof -o ./prover_examples/cairo_platinum/example/fibonacci_10.base64
To create your own proofs, visit CairoVM.
To send a Kimchi verification transaction, run the following command:
bash send_verify_tx.sh kimchi alice ./prover_examples/kimchi/example/kimchi_ec_add.proof
To send a Sp1 verification transaction, run the following command:
bash send_sp1_tx.sh alice ./prover_examples/sp1/example/fibonacci.proof ./verifiers/sp1/lib/elf/riscv32im-succinct-zkvm-el
Compile with:
make build-macos # or
make build-linux
Create some keys:
alignedlayerd keys add <your_key_name>
After adding the keys you will get an address, use it in the faucet to get more gas for paying fees.
If you forgot your address, you can get it again with:
alignedlayerd keys list
To send a gnark-plonk proof to the blockchain, use:
alignedlayerd tx verify gnark-plonk --from <your_key_name> \
--chain-id $CHAIN_ID \
--node tcp://rpc-node.alignedlayer.com:26657 \
--fees 50stake \
$(cat ./prover_examples/gnark_plonk/example/proof.base64.example) \
$(cat ./prover_examples/gnark_plonk/example/public_inputs.base64.example) \
$(cat ./prover_examples/gnark_plonk/example/verifying_key.base64.example)
- CPU: 4 cores
- Memory: 16GB
- Disk: 160GB
To join our network as a full-node, you need a list of public nodes to first connect to. This must be set on a PEER_ADDR env variable:
make clean
export PEER_ADDR=91.107.239.79,116.203.81.174,88.99.174.203,128.140.3.188
A list of our testnet public IP addresses can be found below.
Also, you may set the CHAIN_ID env var if you will follow the manual steps. The current chain-id is alignedlayer
:
export CHAIN_ID=alignedlayer
The fastest way to setup a new node is with our script. It receives the new node's moniker as argument:
bash setup_node.sh <your-node-name>
Then we can start the node with:
alignedlayerd start
Tip
Don't stop this process, and continue the setup on another terminal
Steps
If you want to do a more detailed step by step setup, follow this instructions:First, build the app:
make build_<macos or linux>
To make sure the installation was successful, run the following command:
alignedlayerd version
To initialize the node, run
alignedlayerd init <your-node-name> --chain-id $CHAIN_ID
If you have already run this command, you can use the -o
flag to overwrite previously generated files.
You now need to download the blockchain genesis file and replace the one which was automatically generated for you. Running this command gets the genesis from the first address in $PEER_ADDR
:
curl -s $(echo $PEER_ADDR | cut -d, -f1):26657/genesis | jq '.result.genesis' > ~/.alignedlayer/config/genesis.json
You now need to build a initial node list. This is the list of nodes you will first connect to, preferablly you should use add all of our public nodes. The list should have this structure:
<node1_ID>@<node1_IP>:26656,<node2_ID>@<node2_IP>:26656,...
You can get the initial node list by running:
export INIT_NODES=""; for ip in $(echo $PEER_ADDR | sed 's/,/ /g'); do export INIT_NODES="$INIT_NODES$(curl -s $ip:26657/status | jq -r '.result.node_info.id')@$ip:26656,"; done; export INIT_NODES=${INIT_NODES%?}
To check if the list was created correctly you can print the list:
echo $INIT_NODES
To configure persistent peers and gas prices, run the following commands:
alignedlayerd config set config p2p.persistent_peers "$INIT_NODES" --skip-validate
alignedlayerd config set app minimum-gas-prices 0.0001stake --skip-validate
The two most important ports are 26656 and 26657.
The former is used to establish P2P communication with other nodes. This port should be open to world, in order to allow others to communicate with you. Check that the $HOME/.alignedlayer/config/config.toml
file contains the right address in the p2p section:
laddr = "tcp://0.0.0.0:26656"
The second port is used for the RPC server. If you want to allow remote conections to your node to make queries and transactions, open this port. Note that by default the config sets the address (rpc.laddr
) to tcp://127.0.0.1:26657
, you might change the IP to.
Finally, start your node:
alignedlayerd start
You should keep this shell session attached to this process.
The node will start to sync up with the blockchain. To check if your node is already synced:
curl -s localhost:26657/status | jq '.result.sync_info.catching_up'
It should return false
. If not, try again some minutes later.
The following command shows all the possible operations regarding keys:
alignedlayerd keys --help
Set a new key:
alignedlayerd keys add <account-name>
This commands will return the following information:
address: alignedxxxxxxxxxxxx
name: your-account-name
pubkey: '{"@type":"xxxxxx","key":"xxxxxx"}'
type: local
You'll be encouraged to save a mnemomic in case you need to recover your account.
Tip
If you don't remember the address, you can do the following:
alignedlayerd keys show <address>
or alignedlayerd keys list
To check the balance of an address using the binary:
alignedlayerd query bank balances <account-address-or-name>
To ask for tokens, connect to our faucet with your browser. You'll be asked to specify your account address alignedxxxxxxxxxxxx
, which you obtained in the previuos step.
The fastest way to setup a new node is with our script. It receives the amount to stake as an argument:
bash setup_validator.sh <account-name-or-address> 1050000stake
This will configure your node and send a transaction for creating a validator.
Steps
If you want to do a more detailed step by step registering, follow this instructions:First, obtain your validator pubkey:
alignedlayerd tendermint show-validator
Now create the validator.json file:
{
"pubkey": {"@type": "...", "key": "..."}, // <-- Replace this with your pubkey
"amount": "XXXXXstake", // <-- Replace the XXXXX with the amount you want to stake
"moniker": "your-node-name", // <-- Replace this with your validator name
"commission-rate": "0.1",
"commission-max-rate": "0.2",
"commission-max-change-rate": "0.01",
"min-self-delegation": "1"
}
Now, run:
alignedlayerd tx staking create-validator validator.json --from <account-name-or-address> --node tcp://$PEER_ADDR:26657 --fees 50stake --chain-id $CHAIN_ID
Check whether your validator was accepted with:
alignedlayerd query tendermint-validator-set | grep $(alignedlayerd tendermint show-address)
It should return something like:
- address: alignedvalcons1yead8vgxnmtvmtfrfpleuntslx2jk85drx3ug3
Tip
This is an alternative to the previous Joining Our Testnet guide
If you want to run a node on Docker, you first need to build the image by running:
docker build . -t alignedlayerd_i
Then go into the docker
directory and run the following command to setup the node:
bash docker.sh setup <your_node_name>
After that, you can decide if you want to be a validator or not. To start the node, run:
bash docker.sh run[-validator] <your_node_name>
Once you do a run-validator
, that node name will be a validator, even if you later run it with run
.
Our public nodes have the following IPs. Please be aware that they are in development stage, so expect inconsistency.
91.107.239.79
116.203.81.174
88.99.174.203
128.140.3.188
The core of the state machine App
is defined in app.go. The application inherits from Cosmos' BaseApp
, which routes messages to the appropriate module for handling. A transaction contains any number of messages.
Cosmos SDK provides an Application Module interface to facilitate the composition of modules to form a functional unified application. Custom modules are defined in the x directory.
A module defines a message service for handling messages. These services are defined in a protobuf file. The methods are then implemented in a message server, which is registered in the main application.
Each message's type is identified by its fully-qualified name. For example, the verify message has the type /alignedlayer.verify.MsgVerify
.
A module usually defines a keeper which encapsulates the sub-state of each module, tipically through a key-value store. A reference to the keeper is stored in the message server to be accesed by the handlers.
The boilerplate for creating custom modules and messages can be generated using Ignite CLI. To generate a new module, run:
ignite scaffold module <module-name>
To generate a message handler for the module, run:
ignite scaffold message --module <module-name> <message-name> \
<parameters...> \
--response <response-fields...>
See the Ignite CLI reference to learn about other scaffolding commands.
A transaction can be created and sent with protobuf with ignite CLI. A JSON representation of the transaction can be obtained with the --generate-only
flag. It contains transaction metadata and a set of messages. A message contains the fully-qualified type to route it correctly, and its parameters.
{
"body": {
"messages": [
{
"@type": "/alignedlayer.verify.MsgName",
"creator": "aligned1524vzjchy064rr98d2de7u6uvl4qr3egfq67xn",
"parameter1": "argument1"
"parameter2": "argument2"
...
}
],
"memo": "",
"timeout_height": "0",
"extension_options": [],
"non_critical_extension_options": []
},
"auth_info": {
"signer_infos": [],
"fee": {
"amount": [],
"gas_limit": "200000",
"payer": "",
"granter": ""
},
"tip": null
},
"signatures": []
}
After Comet BFT receives the transaction, its relayed to the application through the ABCI methods checkTx
and deliverTx
.
checkTx
: The defaultBaseApp
implementation does the following.- Checks that a handler exists for every message based on its type.
- A
ValidateBasic
method (optionally implemented for each message type) is executed for every message, allowing stateless validation. This step is deprecated and should be avoided. - The
AnteHandler
's are executed, by default verifying transaction authentication and gas fees.
deliverTx
: In addition to thecheckTx
steps previously mentioned, the following is executed to.- The corresponding handler is called for every message.
- The
PostHandler
's are executed.
The response is then encoded in the transaction result, and added to the blockchain.
The full-node exposes three different types of endpoints for interacting with it.
The node exposes a gRPC server on port 9090.
To get a list with all services, run:
grpcurl -plaintext localhost:9090 list
The requests can be made programatically with any programming language containing the protobuf definitions.
The node exposes REST endpoints via gRPC-gateway on port 1317. An OpenAPI specification can be found here
To get the status of the server, run:
curl "http://localhost:1317/cosmos/base/node/v1beta1/status"
The CometBFT layer exposes a RPC server on port 26657. An OpenAPI specification can be found in here.
When sending the transaction, it must be sent serialized with protobuf and encoded in base64, like the following example:
{
"jsonrpc": "2.0",
"id": 2,
"method": "broadcast_tx_sync",
"params": {
"tx": "CloKWAoeL2xhbWJjaGFpbi5sYW1iY2hhaW4uTXNnVmVyaWZ5EjYKLWNvc21vczE1MjR2empjaHkwNjRycjk4ZDJkZTd1NnV2bDRxcjNlZ2ZxNjd4bhIFcHJvb2YSWApQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAn0JsZxYl0K5OPEcDNS6nTDsERXapNMidfDtTtrsjtGwEgQKAggBGA0SBBDAmgwaQIzdKrUQB9oMGpFTbPJgLMbcGDvteJ+KIShE7FlUxcipS9i8FslYSqPoZ0RUg9LAGl4/PMD8s/ooEpzO4N7XqLs="
}
}
This is the format used by the CLI.
Sets up a network of docker containers each with a validator node and a faucet account.
Build docker images:
docker build . -t alignedlayerd_i
docker build . -t alignedlayerd_faucet -f node.Dockerfile
After building the image we need to set up the files for each cosmos validator node. The steps are:
- Creating and initializing each node working directory with cosmos files.
- Add users for each node with sufficient funds.
- Create and distribute inital genesis file.
- Set up addresses between nodes.
- Set up faucet files.
- Build docker compose file.
Run script (replacing node names eg. bash multi_node_setup.sh node0 node1 node2
).
bash multi_node_setup.sh <node1_name> [<node2_name> ...]
The script retrives the password from the PASSWORD env_var. 'password' is set as the default.
Start nodes:
docker-compose --project-name alignedlayer -f ./prod-sim/docker-compose.yml up --detach
This command creates a docker container for each node. Only the first node (<node1_name>
) has the 26657 port open to receive RPC requests.
It also creates an image that runs the faucet frontend in localhost:8088
.
You can verify that it works by running (replacing <node1_name>
by the name of the first node chosen in the bash script):
docker run --rm -it --network alignedlayer_net-public alignedlayerd_i status --node "tcp://<node1_name>:26657"
The dir /faucet
has the files needed to setup the client.
Requirements:
- npm
- node
Instructions:
Include the mnemonic at faucet/.faucet/mnemonic.txt
to reconstruct the address responsible for generating transactions, ensuring that the address belongs to a validator.
Change the parameters defined by the config.js
file as needed, such as:
- The node's endpoint with:
rpc_endpoint
- How much it is given per request:
tx.amount
cd faucet
npm install
node faucet.js
Then the express server is started at localhost:8088
Note: The Tendermint Node(Blockchain) has to be running.
Now the web view can used to request tokens or curl can be used as follows:
curl http://localhost:8088/send/alignedlayer/:address
Validators and delegators can use the following commands to claim their rewards:
The validator-outstanding-rewards command allows users to query all outstanding (un-withdrawn) rewards for a validator and all their delegations.
alignedlayerd query distribution validator-outstanding-rewards [validator] [flags]
Example:
alignedlayerd query distribution validator-outstanding-rewards alignedvaloper1...
Example Output:
rewards:
- amount: "1000000.000000000000000000"
denom: stake
The validator-distribution-info command allows users to query validator commission and self-delegation rewards for validator.
Example:
alignedlayerd query distribution validator-distribution-info alignedvaloper1...
Example output:
commission:
- amount: "100000.000000000000000000"
denom: stake
operator_address: alignedvaloper1...
self_bond_rewards:
- amount: "100000.000000000000000000"
denom: stake
The withdraw-rewards command allows users to withdraw all rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator and the user proves the --commission flag.
alignedlayerd tx distribution withdraw-rewards [validator-addr] [flags]
Example:
alignedlayerd tx distribution withdraw-rewards alignedvaloper1... --from aligned1... --commission
See the Cosmos' documentation to learn about other distribution commands.
You can use the balances command to query account balances by address.
alignedlayerd query bank balances [address] [flags]
Example:
alignedlayerd query bank balances aligned1..
You can use the slashing CLI commands to query slashing state
alignedlayerd query slashing --help
To query genesis parameters for the slashing module:
alignedlayerd query slashing params [flags]
-
To query signing infos of all validators:
alignedlayerd query slashing signing-infos [flags]
Example output:
info: - address: alignedvalcons15gc... index_offset: "147" jailed_until: "1970-01-01T00:00:00Z" - address: alignedvalcons14xa... index_offset: "147" jailed_until: "1970-01-01T00:00:00Z" - address: alignedvalcons14nz... index_offset: "147" jailed_until: "1970-01-01T00:00:00Z" - address: alignedvalcons1a34... index_offset: "147" jailed_until: "1970-01-01T00:00:00Z" pagination: total: "4"
-
To query signing-info of the validator using consensus public key.
Example:
alignedlayerd query slashing signing-info alignedvalcons15gc...
Example output:
val_signing_info: address: alignedvalcons15gc... index_offset: "255" jailed_until: "1970-01-01T00:00:00Z" missed_blocks_counter: "16"
To query all slashes for a given block range:
alignedlayerd query distribution slashes [validator-addr] [start-height] [end-height] [flags]
To send a transaction to unjail yourself, after the JailPeriod, and thus rejoin the validator set:
alignedlayerd tx slashing unjail --from <account_name> --chain-id $CHAIN_ID --fees 20stake
You may stake additional tokens after registering your validator with the following command:
alignedlayerd tx staking delegate <valoperaddr> <amount> --from <account_name> --chain-id $CHAIN_ID --fees 20stake
You can obtain your validator valoperaddr
by doing:
alignedlayerd keys show <account_name> --bech val --address
This error occurs when the node is unable to connect with other peers. If you are having issues with the connection, try adding more peers using the following commands:
peers=$(curl -s https://raw.githubusercontent.com/nodesynctop/Alignedlayer/main/peers.txt)
sed -i.bak -e "s/^persistent_peers *=.*/persistent_peers = \"$peers\"/" $HOME/.alignedlayer/config/config.toml
sudo systemctl restart <your-node-name> # if you are using systemd else restart the node manually
The system will remove the failing peers but not immediately.
We are most grateful to Cosmos SDK, Ignite CLI, CometBFT and Ping.pub for their faucet and explorer.