Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: devnet for regen-ledger #2224

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,8 @@ dist/
.localnet

completions

## Devnet
# Ignore everything in the nodes/ directory
scripts/devnet/shared
/scripts/devnet/.env
20 changes: 20 additions & 0 deletions scripts/devnet/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use Go 1.23 as the base image for building and setting up the environment
FROM golang:1.23 as builder

# Define a build argument for the base path
ARG BASE_PATH=/mnt/nvme

# Set the working directory using the base path
WORKDIR ${BASE_PATH}

# Update package lists and install dependencies (jq)
RUN apt-get update && apt-get install -y jq

# Clone the regen repository and checkout the specific version (this can be parameterized)
ARG REGEN_VERSION=v5.1.4
RUN git clone https://github.com/regen-network/regen-ledger.git && \
cd regen-ledger && \
git checkout ${REGEN_VERSION}

# Build the regen binary using the Makefile
RUN cd ${BASE_PATH}/regen-ledger && make build && cp build/regen /usr/local/bin
94 changes: 94 additions & 0 deletions scripts/devnet/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Regen Network DevNet Setup Guide

This README provides step-by-step instructions for setting up a multi-node Regen Network development network (DevNet) using Docker Compose. This guide aims to help developers quickly spin up a local Regen Network for testing and development purposes.


## Prerequisites
Before starting, ensure you have the following installed on your machine:

* **Docker:** Download and install Docker
* **Docker Compose:** Download and install Docker Compose
* **Git:** Download and install Git
* **jq:** Command-line JSON processor (Install Guide)

## Usage
```shell
chmod +x setup.sh
./setup.sh
```
**Note:** The script will clean up any existing data to ensure a fresh start.

## Interacting with the network
Once the nodes are running, you can interact with them using regen CLI commands.

For example, to query the status of the first node:

```shell
regen status --node http://localhost:26657
```

Replace 26657 with the appropriate RPC port if interacting with other nodes (26659, 26661).


## File Structure
```text
├── Dockerfile # Defines the Docker image used for the Regen nodes.
├── docker-compose.yml # Docker Compose configuration for the three nodes.
├── entrypoint.sh # Entry script executed inside each Docker container to initialize and start the node.
├── setup.sh # Script to generate keys, write environment variables, and start the network.
└── shared/ # Shared directory for nodes to exchange files (e.g., genesis files, gentxs).
```

## Scripts Overview
### `setup.sh`
This script automates the initial setup:

1. **Generates Validator Keys:** Creates keys for each node and extracts their addresses and mnemonics.
2. **Writes .env File:** Stores the addresses and mnemonics as environment variables.
3. **Starts Docker Compose:** Brings up the network with all three nodes.

### `entrypoint.sh`
Executed inside each Docker container, this script:

1. Initializes the node if it's not already initialized.
2. Imports the validator key using the mnemonic.
3. Generates the gentx file.
4. Coordinates with other nodes to collect gentx files and finalize the genesis file.
5. Configures persistent peers and starts the node.

#### Key Features:

**Synchronization Mechanisms:** Ensures nodes wait for each other during initialization.

### `docker-compose.yml`
Defines the Docker services for the three nodes.

Services:
* regen-node1
* regen-node2
* regen-node3


#### Configurations:

* **Environment Variables:** Loaded from the .env file.
* **Ports:** Exposes RPC and P2P ports for each node.
* **Volumes:** Mounts the shared directory and entrypoint.sh script.
* **Networks:** All nodes are on the same Docker network for internal communication.
* **.env File**: Automatically generated by setup.sh, this file contains validator addresses and mnemonics for all nodes.
Example Contents:

```
REGEN_NODE1_VALIDATOR_ADDRESS=regen1...
REGEN_NODE1_VALIDATOR_MNEMONIC="mnemonic words ..."
REGEN_NODE2_VALIDATOR_ADDRESS=regen1...
REGEN_NODE2_VALIDATOR_MNEMONIC="mnemonic words ..."
REGEN_NODE3_VALIDATOR_ADDRESS=regen1...
REGEN_NODE3_VALIDATOR_MNEMONIC="mnemonic words ..."
```

# State Initialization
For state initialization we recommend using a `state_init.sh` script
written by the developer. This script should contain a list of txs using
the regen cli to initialize the desired state before the usage of the tesnet.
190 changes: 190 additions & 0 deletions scripts/devnet/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#!/bin/bash
set -e

# Constants
BASE_PATH=${BASE_PATH:-/mnt/nvme}
HOME_DIR=${BASE_PATH}/.regen
SHARED_DIR=${BASE_PATH}/shared
GENTX_DIR="$SHARED_DIR/gentxs"
INITIAL_GENESIS_READY="$SHARED_DIR/initial_genesis_ready"
FINAL_GENESIS_READY="$SHARED_DIR/final_genesis_ready"
CHAIN_ID="regen-devnet"

# Colors and Emojis
GREEN='\033[0;32m'
NC='\033[0m'
INFO="${GREEN}ℹ️${NC}"
SUCCESS="${GREEN}${NC}"
WAIT="${GREEN}${NC}"

# Helper: Print message with emoji
log() { echo -e "${1} ${2}"; }

# Ensure shared directories exist
mkdir -p "$GENTX_DIR"

# Determine the number of nodes
NODE_COUNT="${NODE_COUNT:-3}"
NODE_NAMES=($(for i in $(seq 1 "$NODE_COUNT"); do echo "regen-node$i"; done))

# Fetch ports from environment variables
P2P_PORT=${P2P_PORT:-26656}
RPC_PORT=${RPC_PORT:-26657}

configure_rpc_and_p2p() {
CONFIG_FILE="$HOME_DIR/config/config.toml"
APP_FILE="$HOME_DIR/config/app.toml"

log "$INFO" "🔍 Verifying configuration of RPC, P2P, and gRPC for ${NODE_NAME}..."

# Configure RPC in the [rpc] section of config.toml
if [ -f "$CONFIG_FILE" ]; then
log "$INFO" "⚙️ Configuring RPC and P2P in $CONFIG_FILE..."

# Update RPC address only in the [rpc] section
sed -i "/\[rpc\]/,/^\[.*\]/ s|^laddr *=.*|laddr = \"tcp://0.0.0.0:$RPC_PORT\"|" "$CONFIG_FILE"

# Update P2P address only in the [p2p] section
sed -i "/\[p2p\]/,/^\[.*\]/ s|^laddr *=.*|laddr = \"tcp://0.0.0.0:$P2P_PORT\"|" "$CONFIG_FILE"
sed -i "/\[p2p\]/,/^\[.*\]/ s|^external_address *=.*|external_address = \"tcp://0.0.0.0:$P2P_PORT\"|" "$CONFIG_FILE"

log "$SUCCESS" "✅ Configured RPC on $RPC_PORT and P2P on $P2P_PORT in $CONFIG_FILE."
else
log "$INFO" "⚠️ $CONFIG_FILE not found. Skipping P2P and RPC configuration."
fi

# Configure gRPC and API in app.toml
if [ -f "$APP_FILE" ]; then
log "$INFO" "⚙️ Configuring gRPC and API in $APP_FILE..."

# Update gRPC address in the [grpc] section
sed -i "/\[grpc\]/,/^\[.*\]/ s|^address *=.*|address = \"localhost:$GRPC_PORT\"|" "$APP_FILE"


log "$SUCCESS" "✅ Configured gRPC on $GRPC_PORT and API enabled in $APP_FILE."
else
log "$INFO" "⚠️ $APP_FILE not found. Skipping gRPC and API configuration."
fi
}


fetch_environment_variables() {
NODE_ENV_NAME=$(echo "${NODE_NAME^^}" | tr '-' '_')
VALIDATOR_MNEMONIC_VAR="${NODE_ENV_NAME}_VALIDATOR_MNEMONIC"
VALIDATOR_ADDRESS_VAR="${NODE_ENV_NAME}_VALIDATOR_ADDRESS"

VALIDATOR_MNEMONIC="${!VALIDATOR_MNEMONIC_VAR}"
VALIDATOR_ADDRESS="${!VALIDATOR_ADDRESS_VAR}"

if [ -z "$VALIDATOR_MNEMONIC" ] || [ -z "$VALIDATOR_ADDRESS" ]; then
log "$WAIT" "Mnemonic or address not found for ${NODE_NAME}!"
exit 1
fi
log "$SUCCESS" "Fetched mnemonic and address for ${NODE_NAME}."
}

initialize_node() {
if [ ! -f "$HOME_DIR/config/node_key.json" ]; then
regen init "$NODE_NAME" --chain-id "$CHAIN_ID" --home "$HOME_DIR"
log "$SUCCESS" "Initialized ${NODE_NAME}."
fi
}

save_node_id() {
NODE_ID=$(regen tendermint show-node-id --home "$HOME_DIR")
echo "$NODE_ID" > "$SHARED_DIR/${NODE_NAME}_id"
log "$SUCCESS" "Saved Node ID for ${NODE_NAME}: $NODE_ID"
}

add_validator_accounts_to_genesis() {
log "$INFO" "Adding validator accounts to genesis..."
for NODE in "${NODE_NAMES[@]}"; do
NODE_ENV=$(echo "${NODE^^}" | tr '-' '_')
ADDR_VAR="${NODE_ENV}_VALIDATOR_ADDRESS"
ADDRESS="${!ADDR_VAR}"
log "$INFO" "Adding ${NODE}'s address: ${ADDRESS}"
regen add-genesis-account "$ADDRESS" 100000000uregen --home "$HOME_DIR"
done
log "$SUCCESS" "All validator accounts added to genesis."
}

generate_gentx() {
log "$INFO" "Generating gentx for ${NODE_NAME}..."
echo "$VALIDATOR_MNEMONIC" | regen keys add my_validator --recover --keyring-backend test --home "$HOME_DIR"
regen gentx my_validator 50000000uregen --keyring-backend test --chain-id "$CHAIN_ID" --home "$HOME_DIR"
cp "$HOME_DIR/config/gentx/"*.json "$GENTX_DIR/${NODE_NAME}_gentx.json"
log "$SUCCESS" "Generated gentx for ${NODE_NAME}."
}

wait_for_gentx_files() {
log "$INFO" "Waiting for all gentx files..."
for NODE in "${NODE_NAMES[@]}"; do
if [ "$NODE" != "$NODE_NAME" ]; then
while [ ! -f "$GENTX_DIR/${NODE}_gentx.json" ]; do
log "$WAIT" "Waiting for gentx from ${NODE}..."
sleep 2
done
log "$SUCCESS" "Received gentx from ${NODE}."
fi
done
}

wait_for_initial_genesis() {
log "$INFO" "${NODE_NAME} waiting for initial genesis.json..."
while [ ! -f "$INITIAL_GENESIS_READY" ]; do
sleep 2
done
cp "$SHARED_DIR/genesis.json" "$HOME_DIR/config/genesis.json"
log "$SUCCESS" "Initial genesis.json received for ${NODE_NAME}."
}

wait_for_final_genesis() {
log "$INFO" "Waiting for finalized genesis.json..."
while [ ! -f "$FINAL_GENESIS_READY" ]; do
sleep 2
done
cp "$SHARED_DIR/genesis.json" "$HOME_DIR/config/genesis.json"
log "$SUCCESS" "Finalized genesis.json received for ${NODE_NAME}."
}

collect_and_finalize_genesis() {
log "$INFO" "Collecting and validating gentx files..."
regen collect-gentxs --gentx-dir "$GENTX_DIR" --home "$HOME_DIR"
regen validate-genesis --home "$HOME_DIR"
cp "$HOME_DIR/config/genesis.json" "$SHARED_DIR/genesis.json"
touch "$FINAL_GENESIS_READY"
log "$SUCCESS" "Finalized genesis.json saved."
}

# Main Setup Logic
log "$INFO" "Starting setup for ${NODE_NAME}..."


fetch_environment_variables
initialize_node
save_node_id
configure_rpc_and_p2p

if [ "$NODE_NAME" == "regen-node1" ]; then
jq '.app_state.staking.params.bond_denom = "uregen"' "$HOME_DIR/config/genesis.json" > "$HOME_DIR/config/genesis_tmp.json"
mv "$HOME_DIR/config/genesis_tmp.json" "$HOME_DIR/config/genesis.json"
log "$SUCCESS" "Modified genesis.json for ${NODE_NAME}."

add_validator_accounts_to_genesis

# Save initial genesis.json and notify other nodes
cp "$HOME_DIR/config/genesis.json" "$SHARED_DIR/genesis.json"
touch "$INITIAL_GENESIS_READY"
log "$SUCCESS" "Initial genesis.json saved."

wait_for_gentx_files
collect_and_finalize_genesis
else
wait_for_initial_genesis
generate_gentx
wait_for_final_genesis
fi


log "$INFO" "Starting ${NODE_NAME}..."
exec regen start --home "$HOME_DIR" --minimum-gas-prices="0.025uregen"
Loading
Loading