Skip to content

Instantly share code, notes, and snippets.

@cordt-sei
Last active March 7, 2025 03:46
Show Gist options
  • Save cordt-sei/69b0886c94575663aa6b521a495bfdcc to your computer and use it in GitHub Desktop.
Save cordt-sei/69b0886c94575663aa6b521a495bfdcc to your computer and use it in GitHub Desktop.
#!/bin/bash
# Enhanced initialize_local_chain.sh with multiple setup options
# This script provides various options for setting up Sei local environments
set -e
# Color codes for better output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Print with timestamp
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
}
warn() {
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1"
}
error() {
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1"
exit 1
}
# Get the project root directory
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
# Default configuration
DISABLE_ORACLE=false
USE_DOCKER=false
MULTI_NODE=false
NO_RUN=0
NODE_COUNT=4
USE_EXISTING_SCRIPT=false
keyname=admin
# Display menu and get user choice
show_menu() {
clear
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ SEI LOCAL SETUP OPTIONS ║${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo
echo "Please select a setup option:"
echo
echo " 1) Standard single-node setup (existing behavior)"
echo " 2) Single-node setup with oracle requirement disabled"
echo " 3) Multi-node local test cluster (4 validators)"
echo " 4) Multi-node local test cluster with oracle requirement disabled"
echo " 5) Custom configuration"
echo " 0) Exit"
echo
read -p "Enter your choice [0-5]: " choice
case $choice in
1)
USE_EXISTING_SCRIPT=true
log "Selected standard single-node setup"
;;
2)
DISABLE_ORACLE=true
log "Selected single-node setup with oracle requirement disabled"
;;
3)
MULTI_NODE=true
log "Selected multi-node local test cluster"
;;
4)
MULTI_NODE=true
DISABLE_ORACLE=true
log "Selected multi-node local test cluster with oracle requirement disabled"
;;
5)
custom_config
;;
0)
log "Exiting..."
exit 0
;;
*)
error "Invalid choice. Please enter a number between 0 and 5."
;;
esac
}
# Allow custom configuration
custom_config() {
echo
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ CUSTOM CONFIGURATION ║${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo
# Oracle requirement
read -p "Disable oracle requirement? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
DISABLE_ORACLE=true
log "Oracle requirement will be disabled"
else
log "Oracle requirement will remain enabled"
fi
# Single or multi-node
read -p "Set up multiple validator nodes (cluster)? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
MULTI_NODE=true
read -p "Number of validator nodes (2-10) [default: 4]: " NODE_COUNT
NODE_COUNT=${NODE_COUNT:-4}
if ! [[ "$NODE_COUNT" =~ ^[2-9]$|^10$ ]]; then
NODE_COUNT=4
warn "Invalid number. Using default: 4 nodes"
fi
log "Will set up a ${NODE_COUNT}-node validator cluster"
# Docker option (only for multi-node)
if [ -f "${PROJECT_ROOT}/docker/docker-compose.yml" ]; then
read -p "Use Docker for multi-node setup? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
USE_DOCKER=true
log "Will use Docker for multi-node setup"
else
log "Will use direct execution for multi-node setup"
fi
fi
else
log "Will set up a single validator node"
fi
# No run option
read -p "Set up only without starting? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
NO_RUN=1
log "Will set up without starting the node(s)"
else
log "Will set up and start the node(s)"
fi
}
# Function to run the original initialize_local_chain.sh script
run_original_script() {
log "Running original initialize_local_chain.sh script..."
# Use python3 as default, but fall back to python if python3 doesn't exist
PYTHON_CMD=python3
if ! command -v $PYTHON_CMD &> /dev/null
then
PYTHON_CMD=python
fi
# clean up old sei directory
rm -rf ~/.sei
log "Building..."
#install seid
make install
# initialize chain with chain ID and add the first key
~/go/bin/seid init demo --chain-id sei-chain
~/go/bin/seid keys add $keyname --keyring-backend test
# add the key as a genesis account with massive balances of several different tokens
~/go/bin/seid add-genesis-account $(~/go/bin/seid keys show $keyname -a --keyring-backend test) 100000000000000000000usei,100000000000000000000uusdc,100000000000000000000uatom --keyring-backend test
# gentx for account
~/go/bin/seid gentx $keyname 7000000000000000usei --chain-id sei-chain --keyring-backend test
# add validator information to genesis file
KEY=$(jq '.pub_key' ~/.sei/config/priv_validator_key.json -c)
jq '.validators = [{}]' ~/.sei/config/genesis.json > ~/.sei/config/tmp_genesis.json
jq '.validators[0] += {"power":"7000000000"}' ~/.sei/config/tmp_genesis.json > ~/.sei/config/tmp_genesis_2.json
jq '.validators[0] += {"pub_key":'$KEY'}' ~/.sei/config/tmp_genesis_2.json > ~/.sei/config/tmp_genesis_3.json
mv ~/.sei/config/tmp_genesis_3.json ~/.sei/config/genesis.json && rm ~/.sei/config/tmp_genesis.json && rm ~/.sei/config/tmp_genesis_2.json
log "Creating Accounts"
# create test accounts + fund them
python3 loadtest/scripts/populate_genesis_accounts.py 20 loc
~/go/bin/seid collect-gentxs
# update some params in genesis file for easier use of the chain localls (make gov props faster)
cat ~/.sei/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["max_deposit_period"]="60s"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["gov"]["voting_params"]["voting_period"]="30s"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["gov"]["voting_params"]["expedited_voting_period"]="10s"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["vote_period"]="2"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["whitelist"]=[{"name": "ueth"},{"name": "ubtc"},{"name": "uusdc"},{"name": "uusdt"},{"name": "uosmo"},{"name": "uatom"},{"name": "usei"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["distribution"]["params"]["community_tax"]="0.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["max_gas"]="35000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["min_txs_in_block"]="2"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["staking"]["params"]["max_voting_power_ratio"]="1.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["bank"]["denom_metadata"]=[{"denom_units":[{"denom":"usei","exponent":0,"aliases":["USEI"]}],"base":"usei","display":"usei","name":"USEI","symbol":"USEI"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
# Use the Python command to get the dates
START_DATE=$($PYTHON_CMD -c "from datetime import datetime; print(datetime.now().strftime('%Y-%m-%d'))")
END_DATE_3DAYS=$($PYTHON_CMD -c "from datetime import datetime, timedelta; print((datetime.now() + timedelta(days=3)).strftime('%Y-%m-%d'))")
END_DATE_5DAYS=$($PYTHON_CMD -c "from datetime import datetime, timedelta; print((datetime.now() + timedelta(days=5)).strftime('%Y-%m-%d'))")
cat ~/.sei/config/genesis.json | jq --arg start_date "$START_DATE" --arg end_date "$END_DATE_3DAYS" '.app_state["mint"]["params"]["token_release_schedule"]=[{"start_date": $start_date, "end_date": $end_date, "token_release_amount": "999999999999"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq --arg start_date "$END_DATE_3DAYS" --arg end_date "$END_DATE_5DAYS" '.app_state["mint"]["params"]["token_release_schedule"] += [{"start_date": $start_date, "end_date": $end_date, "token_release_amount": "999999999999"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
if [ ! -z "$2" ]; then
APP_TOML_PATH="$2"
else
APP_TOML_PATH="$HOME/.sei/config/app.toml"
fi
# Enable OCC and SeiDB
sed -i.bak -e 's/# concurrency-workers = .*/concurrency-workers = 500/' $APP_TOML_PATH
sed -i.bak -e 's/occ-enabled = .*/occ-enabled = true/' $APP_TOML_PATH
sed -i.bak -e 's/sc-enable = .*/sc-enable = true/' $APP_TOML_PATH
sed -i.bak -e 's/ss-enable = .*/ss-enable = true/' $APP_TOML_PATH
# set block time to 2s
if [ ! -z "$1" ]; then
CONFIG_PATH="$1"
else
CONFIG_PATH="$HOME/.sei/config/config.toml"
fi
if [ ! -z "$2" ]; then
APP_PATH="$2"
else
APP_PATH="$HOME/.sei/config/app.toml"
fi
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
sed -i 's/mode = "full"/mode = "validator"/g' $CONFIG_PATH
sed -i 's/indexer = \["null"\]/indexer = \["kv"\]/g' $CONFIG_PATH
sed -i 's/timeout_prevote =.*/timeout_prevote = "2000ms"/g' $CONFIG_PATH
sed -i 's/timeout_precommit =.*/timeout_precommit = "2000ms"/g' $CONFIG_PATH
sed -i 's/timeout_commit =.*/timeout_commit = "2000ms"/g' $CONFIG_PATH
sed -i 's/skip_timeout_commit =.*/skip_timeout_commit = false/g' $CONFIG_PATH
elif [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' 's/mode = "full"/mode = "validator"/g' $CONFIG_PATH
sed -i '' 's/indexer = \["null"\]/indexer = \["kv"\]/g' $CONFIG_PATH
sed -i '' 's/unsafe-propose-timeout-override =.*/unsafe-propose-timeout-override = "2s"/g' $CONFIG_PATH
sed -i '' 's/unsafe-propose-timeout-delta-override =.*/unsafe-propose-timeout-delta-override = "2s"/g' $CONFIG_PATH
sed -i '' 's/unsafe-vote-timeout-override =.*/unsafe-vote-timeout-override = "2s"/g' $CONFIG_PATH
sed -i '' 's/unsafe-vote-timeout-delta-override =.*/unsafe-vote-timeout-delta-override = "2s"/g' $CONFIG_PATH
sed -i '' 's/unsafe-commit-timeout-override =.*/unsafe-commit-timeout-override = "2s"/g' $CONFIG_PATH
else
printf "Platform not supported, please ensure that the following values are set in your config.toml:\n"
printf "### Consensus Configuration Options ###\n"
printf "\t timeout_prevote = \"2000ms\"\n"
printf "\t timeout_precommit = \"2000ms\"\n"
printf "\t timeout_commit = \"2000ms\"\n"
printf "\t skip_timeout_commit = false\n"
exit 1
fi
~/go/bin/seid config keyring-backend test
if [ $NO_RUN = 1 ]; then
log "No run flag set, exiting without starting the chain"
exit 0
fi
# start the chain with log tracing
log "Starting the Sei chain..."
GORACE="log_path=/tmp/race/seid_race" ~/go/bin/seid start --trace --chain-id sei-chain
}
# Function to run script with oracle requirement disabled (single node)
run_with_oracle_disabled() {
log "Running Sei setup with oracle requirement disabled..."
# This function is similar to run_original_script but adds the oracle disabling commands
# Use python3 as default, but fall back to python if python3 doesn't exist
PYTHON_CMD=python3
if ! command -v $PYTHON_CMD &> /dev/null
then
PYTHON_CMD=python
fi
# clean up old sei directory
rm -rf ~/.sei
log "Building..."
#install seid
make install
# initialize chain with chain ID and add the first key
~/go/bin/seid init demo --chain-id sei-chain
~/go/bin/seid keys add $keyname --keyring-backend test
# add the key as a genesis account with massive balances of several different tokens
~/go/bin/seid add-genesis-account $(~/go/bin/seid keys show $keyname -a --keyring-backend test) 100000000000000000000usei,100000000000000000000uusdc,100000000000000000000uatom --keyring-backend test
# gentx for account
~/go/bin/seid gentx $keyname 7000000000000000usei --chain-id sei-chain --keyring-backend test
# add validator information to genesis file
KEY=$(jq '.pub_key' ~/.sei/config/priv_validator_key.json -c)
jq '.validators = [{}]' ~/.sei/config/genesis.json > ~/.sei/config/tmp_genesis.json
jq '.validators[0] += {"power":"7000000000"}' ~/.sei/config/tmp_genesis.json > ~/.sei/config/tmp_genesis_2.json
jq '.validators[0] += {"pub_key":'$KEY'}' ~/.sei/config/tmp_genesis_2.json > ~/.sei/config/tmp_genesis_3.json
mv ~/.sei/config/tmp_genesis_3.json ~/.sei/config/genesis.json && rm ~/.sei/config/tmp_genesis.json && rm ~/.sei/config/tmp_genesis_2.json
log "Creating Accounts"
# create test accounts + fund them
python3 loadtest/scripts/populate_genesis_accounts.py 20 loc
~/go/bin/seid collect-gentxs
# MODIFICATION: Disable oracle voting requirement by setting critical parameters to 0
log "Disabling oracle voting requirements..."
cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["min_valid_per_window"]="0.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["vote_threshold"]="0.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["slash_fraction"]="0.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
# update some params in genesis file for easier use of the chain localls (make gov props faster)
cat ~/.sei/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["max_deposit_period"]="60s"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["gov"]["voting_params"]["voting_period"]="30s"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["gov"]["voting_params"]["expedited_voting_period"]="10s"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["vote_period"]="2"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["whitelist"]=[{"name": "ueth"},{"name": "ubtc"},{"name": "uusdc"},{"name": "uusdt"},{"name": "uosmo"},{"name": "uatom"},{"name": "usei"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["distribution"]["params"]["community_tax"]="0.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["max_gas"]="35000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["min_txs_in_block"]="2"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["staking"]["params"]["max_voting_power_ratio"]="1.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq '.app_state["bank"]["denom_metadata"]=[{"denom_units":[{"denom":"usei","exponent":0,"aliases":["USEI"]}],"base":"usei","display":"usei","name":"USEI","symbol":"USEI"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
# Use the Python command to get the dates
START_DATE=$($PYTHON_CMD -c "from datetime import datetime; print(datetime.now().strftime('%Y-%m-%d'))")
END_DATE_3DAYS=$($PYTHON_CMD -c "from datetime import datetime, timedelta; print((datetime.now() + timedelta(days=3)).strftime('%Y-%m-%d'))")
END_DATE_5DAYS=$($PYTHON_CMD -c "from datetime import datetime, timedelta; print((datetime.now() + timedelta(days=5)).strftime('%Y-%m-%d'))")
cat ~/.sei/config/genesis.json | jq --arg start_date "$START_DATE" --arg end_date "$END_DATE_3DAYS" '.app_state["mint"]["params"]["token_release_schedule"]=[{"start_date": $start_date, "end_date": $end_date, "token_release_amount": "999999999999"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
cat ~/.sei/config/genesis.json | jq --arg start_date "$END_DATE_3DAYS" --arg end_date "$END_DATE_5DAYS" '.app_state["mint"]["params"]["token_release_schedule"] += [{"start_date": $start_date, "end_date": $end_date, "token_release_amount": "999999999999"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json
if [ ! -z "$2" ]; then
APP_TOML_PATH="$2"
else
APP_TOML_PATH="$HOME/.sei/config/app.toml"
fi
# Enable OCC and SeiDB
sed -i.bak -e 's/# concurrency-workers = .*/concurrency-workers = 500/' $APP_TOML_PATH
sed -i.bak -e 's/occ-enabled = .*/occ-enabled = true/' $APP_TOML_PATH
sed -i.bak -e 's/sc-enable = .*/sc-enable = true/' $APP_TOML_PATH
sed -i.bak -e 's/ss-enable = .*/ss-enable = true/' $APP_TOML_PATH
# set block time to 2s
if [ ! -z "$1" ]; then
CONFIG_PATH="$1"
else
CONFIG_PATH="$HOME/.sei/config/config.toml"
fi
if [ ! -z "$2" ]; then
APP_PATH="$2"
else
APP_PATH="$HOME/.sei/config/app.toml"
fi
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
sed -i 's/mode = "full"/mode = "validator"/g' $CONFIG_PATH
sed -i 's/indexer = \["null"\]/indexer = \["kv"\]/g' $CONFIG_PATH
sed -i 's/timeout_prevote =.*/timeout_prevote = "2000ms"/g' $CONFIG_PATH
sed -i 's/timeout_precommit =.*/timeout_precommit = "2000ms"/g' $CONFIG_PATH
sed -i 's/timeout_commit =.*/timeout_commit = "2000ms"/g' $CONFIG_PATH
sed -i 's/skip_timeout_commit =.*/skip_timeout_commit = false/g' $CONFIG_PATH
elif [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' 's/mode = "full"/mode = "validator"/g' $CONFIG_PATH
sed -i '' 's/indexer = \["null"\]/indexer = \["kv"\]/g' $CONFIG_PATH
sed -i '' 's/unsafe-propose-timeout-override =.*/unsafe-propose-timeout-override = "2s"/g' $CONFIG_PATH
sed -i '' 's/unsafe-propose-timeout-delta-override =.*/unsafe-propose-timeout-delta-override = "2s"/g' $CONFIG_PATH
sed -i '' 's/unsafe-vote-timeout-override =.*/unsafe-vote-timeout-override = "2s"/g' $CONFIG_PATH
sed -i '' 's/unsafe-vote-timeout-delta-override =.*/unsafe-vote-timeout-delta-override = "2s"/g' $CONFIG_PATH
sed -i '' 's/unsafe-commit-timeout-override =.*/unsafe-commit-timeout-override = "2s"/g' $CONFIG_PATH
else
printf "Platform not supported, please ensure that the following values are set in your config.toml:\n"
printf "### Consensus Configuration Options ###\n"
printf "\t timeout_prevote = \"2000ms\"\n"
printf "\t timeout_precommit = \"2000ms\"\n"
printf "\t timeout_commit = \"2000ms\"\n"
printf "\t skip_timeout_commit = false\n"
exit 1
fi
~/go/bin/seid config keyring-backend test
if [ $NO_RUN = 1 ]; then
log "No run flag set, exiting without starting the chain"
exit 0
fi
# start the chain with log tracing
log "Starting the Sei chain with oracle requirement disabled..."
GORACE="log_path=/tmp/race/seid_race" ~/go/bin/seid start --trace --chain-id sei-chain
}
# Function to create oracle voter script (for fallback)
create_oracle_voter_script() {
local ORACLE_SCRIPT="${PROJECT_ROOT}/scripts/oracle_voter.py"
log "Creating oracle voter script at ${ORACLE_SCRIPT}..."
cat > "${ORACLE_SCRIPT}" << 'EOF'
#!/usr/bin/env python3
import time
import subprocess
import requests
import sys
import logging
# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Default configuration
KEYNAME = "admin"
PASSWORD = "12345678"
CHAIN_ID = "sei-chain"
VOTE_PERIOD = 10
BINARY = "~/go/bin/seid"
NODE = "http://localhost:26657"
def get_current_vote_period():
try:
res = requests.get(f"{NODE}/blockchain")
body = res.json()
return int(body["result"]["last_height"]) // VOTE_PERIOD
except Exception as e:
logging.error(f"Error getting current vote period: {e}")
return -1
def execute_cmd(cmd):
try:
result = subprocess.run(cmd, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
logging.info(f"Command executed successfully: {cmd}")
logging.debug(f"Output: {result.stdout}")
return result.stdout
except subprocess.CalledProcessError as e:
logging.error(f"Command failed: {cmd}")
logging.error(f"Error: {e.stderr}")
return None
def get_validator_address():
cmd = f"printf '{PASSWORD}\\n' | {BINARY} keys show {KEYNAME} --bech=val | grep address | cut -d':' -f2 | xargs"
result = execute_cmd(cmd)
if result:
val_addr = result.strip()
logging.info(f"Validator address: {val_addr}")
return val_addr
return None
def vote():
val_addr = get_validator_address()
if not val_addr:
logging.error("Could not get validator address")
return False
# Construct fake prices (1 for each whitelisted token)
prices = "1usei,1ueth,1ubtc,1uusdc,1uusdt,1uosmo,1uatom"
# Submit vote
cmd = f"printf '{PASSWORD}\\n' | {BINARY} tx oracle aggregate-vote {prices} {val_addr} --from {KEYNAME} --chain-id={CHAIN_ID} -y --broadcast-mode=sync"
result = execute_cmd(cmd)
return result is not None
def main():
logging.info("Starting automatic oracle voter")
logging.info(f"Chain ID: {CHAIN_ID}")
logging.info(f"Node: {NODE}")
last_voted_period = -1
while True:
try:
current_period = get_current_vote_period()
if current_period > last_voted_period:
logging.info(f"Submitting oracle vote for period {current_period}")
if vote():
last_voted_period = current_period
logging.info(f"Vote submitted successfully for period {current_period}")
else:
logging.error(f"Failed to submit vote for period {current_period}")
time.sleep(1) # Check every second
except Exception as e:
logging.error(f"Unexpected error: {e}")
time.sleep(5) # Wait a bit longer on error
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
logging.info("Oracle voter stopped by user")
EOF
chmod +x "${ORACLE_SCRIPT}"
log "Oracle voter script created successfully"
}
# Function to create the patch genesis script for multi-node setup
create_patch_genesis_script() {
local PATCH_SCRIPT="${PROJECT_ROOT}/docker/patch_genesis.sh"
log "Creating genesis patch script at ${PATCH_SCRIPT}..."
cat > "${PATCH_SCRIPT}" << 'EOF'
#!/bin/bash
# This script patches the genesis file to disable oracle requirements
# It will be executed in the Docker container
set -e
echo "Patching genesis files to disable oracle requirements..."
# Function to patch a single genesis file
patch_genesis() {
local genesis_file=$1
echo "Patching $genesis_file"
# Create a backup
cp "$genesis_file" "${genesis_file}.bak"
# Disable oracle voting requirement
cat "$genesis_file" | jq '.app_state["oracle"]["params"]["min_valid_per_window"]="0.000000000000000000"' > "${genesis_file}.tmp"
mv "${genesis_file}.tmp" "$genesis_file"
cat "$genesis_file" | jq '.app_state["oracle"]["params"]["vote_threshold"]="0.000000000000000000"' > "${genesis_file}.tmp"
mv "${genesis_file}.tmp" "$genesis_file"
cat "$genesis_file" | jq '.app_state["oracle"]["params"]["slash_fraction"]="0.000000000000000000"' > "${genesis_file}.tmp"
mv "${genesis_file}.tmp" "$genesis_file"
echo "Successfully patched $genesis_file"
}
# Wait for the genesis file to be created
MAX_ATTEMPTS=30
ATTEMPT=0
GENESIS_FILE="/sei-protocol/sei-chain/build/generated/validator-0/sei/config/genesis.json"
while [ ! -f "$GENESIS_FILE" ] && [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
echo "Waiting for genesis file to be created... (attempt $ATTEMPT/$MAX_ATTEMPTS)"
sleep 5
ATTEMPT=$((ATTEMPT + 1))
done
if [ ! -f "$GENESIS_FILE" ]; then
echo "Genesis file not found after waiting. Cannot patch."
exit 1
fi
# Patch the main genesis file
patch_genesis "$GENESIS_FILE"
# Find and patch all other genesis files
find /sei-protocol/sei-chain/build/generated -name "genesis.json" | while read -r genesis_file; do
if [ "$genesis_file" != "$GENESIS_FILE" ]; then
patch_genesis "$genesis_file"
fi
done
echo "All genesis files have been patched successfully!"
EOF
chmod +x "${PATCH_SCRIPT}"
log "Genesis patch script created successfully"
}
# Function to run multi-node setup with Docker
run_multinode_docker() {
log "Setting up multi-node cluster using Docker..."
# Ensure the docker directory exists
if [ ! -f "${PROJECT_ROOT}/docker/docker-compose.yml" ]; then
error "Docker configuration not found at ${PROJECT_ROOT}/docker/docker-compose.yml"
fi
# Create the patch script if oracle requirement should be disabled
if [ "$DISABLE_ORACLE" = true ]; then
create_patch_genesis_script
fi
# Create a wrapper script for the Docker setup
local WRAPPER_SCRIPT="${PROJECT_ROOT}/run_docker_cluster.sh"
cat > "${WRAPPER_SCRIPT}" << 'EOF'
#!/bin/bash
set -e
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
cd "${PROJECT_ROOT}"
# Make directory for generated files
mkdir -p build/generated
# Build Docker image
echo "Building Docker node image..."
make build-docker-node
# Run directly using make command which properly sets up environment variables
cd "${PROJECT_ROOT}"
# Check if we need to run or just setup
if [ "${NO_RUN}" = "1" ]; then
echo "Setting up without starting..."
make docker-cluster-stop
else
echo "Starting docker cluster..."
# Apply patch_genesis.sh if needed
if [ -f "${PROJECT_ROOT}/docker/patch_genesis.sh" ]; then
# First run a single validator to generate the genesis file
echo "Starting validator-0 to generate initial genesis..."
cd "${PROJECT_ROOT}" && make docker-cluster-start-skipbuild
# Wait a bit for the genesis to be created
sleep 5
# Now stop and restart with our modified configuration
make docker-cluster-stop
sleep 3
echo "Restarting with patched genesis..."
fi
# Start the cluster
cd "${PROJECT_ROOT}" && make docker-cluster-start-skipbuild
fi
EOF
chmod +x "${WRAPPER_SCRIPT}"
# Run the wrapper script
log "Running Docker cluster setup..."
export NO_RUN=${NO_RUN}
bash "${WRAPPER_SCRIPT}"
}
# Function to run multi-node setup with direct execution
run_multinode_direct() {
log "Setting up multi-node cluster using direct execution..."
if [ "$DISABLE_ORACLE" = true ]; then
log "You've selected to disable oracle requirements."
fi
log "Direct execution of multi-node setup is not fully implemented yet."
log "Would you like to use Docker for the multi-node setup instead? (y/n)"
read -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
USE_DOCKER=true
run_multinode_docker
else
# Create a simple way to launch multiple nodes
log "Creating a simple multi-node direct execution script..."
local MULTINODE_SCRIPT="${PROJECT_ROOT}/run_multinode_direct.sh"
cat > "${MULTINODE_SCRIPT}" << 'EOF'
#!/bin/bash
# Script to run multiple Sei validator nodes directly (without Docker)
set -e
NODE_COUNT=${1:-4}
DISABLE_ORACLE=${2:-false}
NO_RUN=${3:-0}
echo "Setting up $NODE_COUNT Sei validator nodes..."
# Create and configure the nodes
for i in $(seq 0 $((NODE_COUNT-1))); do
echo "Setting up validator-$i..."
# Create separate data directory for each node
NODE_HOME="$HOME/.sei-validator-$i"
rm -rf "$NODE_HOME"
mkdir -p "$NODE_HOME"
# Initialize the node
seid --home "$NODE_HOME" init "validator-$i" --chain-id sei-chain
# Create account
seid --home "$NODE_HOME" keys add validator --keyring-backend test
# Add test funds
seid --home "$NODE_HOME" add-genesis-account $(seid --home "$NODE_HOME" keys show validator -a --keyring-backend test) 100000000000000000000usei,100000000000000000000uusdc,100000000000000000000uatom --keyring-backend test
# Create genesis transaction
seid --home "$NODE_HOME" gentx validator 7000000000000000usei --chain-id sei-chain --keyring-backend test
# Disable oracle requirement if requested
if [ "$DISABLE_ORACLE" = true ]; then
echo "Disabling oracle requirements for validator-$i..."
cat "$NODE_HOME/config/genesis.json" | jq '.app_state["oracle"]["params"]["min_valid_per_window"]="0.000000000000000000"' > "$NODE_HOME/config/tmp_genesis.json" && mv "$NODE_HOME/config/tmp_genesis.json" "$NODE_HOME/config/genesis.json"
cat "$NODE_HOME/config/genesis.json" | jq '.app_state["oracle"]["params"]["vote_threshold"]="0.000000000000000000"' > "$NODE_HOME/config/tmp_genesis.json" && mv "$NODE_HOME/config/tmp_genesis.json" "$NODE_HOME/config/genesis.json"
cat "$NODE_HOME/config/genesis.json" | jq '.app_state["oracle"]["params"]["slash_fraction"]="0.000000000000000000"' > "$NODE_HOME/config/tmp_genesis.json" && mv "$NODE_HOME/config/tmp_genesis.json" "$NODE_HOME/config/genesis.json"
fi
# Configure P2P for each node
sed -i.bak "s/26656/2${i}656/g" "$NODE_HOME/config/config.toml"
sed -i.bak "s/26657/2${i}657/g" "$NODE_HOME/config/config.toml"
sed -i.bak "s/26658/2${i}658/g" "$NODE_HOME/config/config.toml"
sed -i.bak "s/6060/6${i}60/g" "$NODE_HOME/config/config.toml"
# Configure API for each node
sed -i.bak "s/1317/1${i}17/g" "$NODE_HOME/config/app.toml"
sed -i.bak "s/9090/9${i}90/g" "$NODE_HOME/config/app.toml"
sed -i.bak "s/9091/9${i}91/g" "$NODE_HOME/config/app.toml"
done
echo "Basic node setup completed!"
echo "Note: This is a simplified multi-node setup that creates separate node directories."
echo "Further configuration is needed to make them connect as a network."
if [ "$NO_RUN" = "1" ]; then
echo "No-run flag set. Exiting without starting nodes."
exit 0
fi
echo "Starting validator-0 (primary node)..."
seid --home "$HOME/.sei-validator-0" start --trace --chain-id sei-chain
EOF
chmod +x "${MULTINODE_SCRIPT}"
log "Created multi-node direct execution script at ${MULTINODE_SCRIPT}"
log "Note: Direct multi-node execution is experimental and needs further development."
# Offer to run the script
read -p "Would you like to attempt running the multi-node direct execution? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
bash "${MULTINODE_SCRIPT}" "${NODE_COUNT}" "${DISABLE_ORACLE}" "${NO_RUN}"
else
log "Okay. You can run it later with: ${MULTINODE_SCRIPT}"
fi
fi
}
# Cleanup of previous deployment
perform_cleanup() {
log "Starting comprehensive cleanup..."
# Stop any running Docker containers related to Sei
if command -v docker &> /dev/null; then
log "Stopping and removing Docker containers..."
cd "${PROJECT_ROOT}" && make docker-cluster-stop 2>/dev/null || true
docker ps -a | grep sei | awk '{print $1}' | xargs -r docker rm -f 2>/dev/null || true
fi
# Kill any running seid processes
log "Stopping any running seid processes..."
pkill -f "seid" 2>/dev/null || true
# Remove Sei data directories
log "Removing Sei data directories..."
rm -rf ~/.sei
# Remove all validator directories
for i in {0..10}; do
rm -rf ~/.sei-validator-$i 2>/dev/null || true
done
# Remove generated files in project directory
log "Removing generated files..."
rm -rf "${PROJECT_ROOT}/build/generated" 2>/dev/null || true
# Remove any temporary scripts we created
log "Removing temporary scripts..."
rm -f "${PROJECT_ROOT}/docker/patch_genesis.sh" 2>/dev/null || true
rm -f "${PROJECT_ROOT}/run_docker_cluster.sh" 2>/dev/null || true
rm -f "${PROJECT_ROOT}/run_multinode_direct.sh" 2>/dev/null || true
# Kill any oracle voter processes
log "Stopping any running oracle voter processes..."
pkill -f "oracle_voter.py" 2>/dev/null || true
# Remove oracle voter logs
rm -f oracle_voter.log 2>/dev/null || true
log "Cleanup completed successfully!"
}
# Main function
main() {
# Ask if user wants to clean up first
read -p "Do you want to perform a complete cleanup of previous executions first? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
perform_cleanup
fi
# Display the menu and get user choice
show_menu
# Run the selected setup
if [ "$USE_EXISTING_SCRIPT" = true ]; then
run_original_script
elif [ "$MULTI_NODE" = true ]; then
if [ "$USE_DOCKER" = true ]; then
run_multinode_docker
else
# Check if Docker is available and suggest it
if [ -f "${PROJECT_ROOT}/docker/docker-compose.yml" ]; then
log "Multi-node setup is easier with Docker. Would you like to use Docker? (y/n)"
read -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
USE_DOCKER=true
run_multinode_docker
else
run_multinode_direct
fi
else
run_multinode_direct
fi
fi
else
if [ "$DISABLE_ORACLE" = true ]; then
# If oracle requirement is disabled, create the voter script (just in case)
create_oracle_voter_script
run_with_oracle_disabled
else
run_original_script
fi
fi
}
# Execute main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment