|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Exit on error |
| 4 | +set -e |
| 5 | + |
| 6 | +# Check arguments |
| 7 | +if [ "$#" -lt 4 ]; then |
| 8 | + echo "Usage: $0 <validator0_ip> <validator1_ip> <validator2_ip> <validator3_ip>" |
| 9 | + echo "Example: $0 1.2.3.4 5.6.7.8 9.10.11.12 13.14.15.16" |
| 10 | + exit 1 |
| 11 | +fi |
| 12 | + |
| 13 | +# validate dependencies are installed |
| 14 | +command -v jq >/dev/null 2>&1 || { |
| 15 | + echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/" |
| 16 | + exit 1 |
| 17 | +} |
| 18 | + |
| 19 | +# Set number of validators |
| 20 | +NUM_VALIDATORS=4 |
| 21 | + |
| 22 | +# Store validator IPs in array |
| 23 | +declare -a VALIDATOR_IPS=($1 $2 $3 $4) |
| 24 | +echo "All validator IPs: ${VALIDATOR_IPS[@]}" |
| 25 | +echo "Number of validators: $NUM_VALIDATORS" |
| 26 | + |
| 27 | +# Configuration |
| 28 | +CHAIN_ID="hhub_9000-1" |
| 29 | +KEYRING="test" |
| 30 | +KEYALGO="eth_secp256k1" |
| 31 | +DENOM="ahhub" |
| 32 | +HOME_PREFIX="$HOME/.hhubd" |
| 33 | +# Set balance and stake amounts (matching local_node.sh exactly) |
| 34 | +GENESIS_BALANCE="1000000000000000000000000000" # 1 million hhub |
| 35 | +GENTX_STAKE="1000000000000000000000000" # 1 million hhub (1000000000000000000000000 = 10^24) |
| 36 | +BASEFEE=1000000000 |
| 37 | + |
| 38 | +# Port configuration |
| 39 | +P2P_PORT=26656 |
| 40 | +RPC_PORT=26657 # Must be different from P2P_PORT |
| 41 | +API_PORT=1317 |
| 42 | +GRPC_PORT=9090 |
| 43 | +GRPC_WEB_PORT=9092 |
| 44 | +JSON_RPC_PORT=8545 |
| 45 | +WS_PORT=8546 |
| 46 | + |
| 47 | +# Clean up all existing data locally and remotely |
| 48 | +echo "Cleaning up all existing data..." |
| 49 | + |
| 50 | +# Clean local node data |
| 51 | +rm -rf "${HOME_PREFIX}"/* |
| 52 | +for i in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 53 | + echo "Cleaning up local validator $i data..." |
| 54 | + rm -rf "${HOME}/.hhubd$i" |
| 55 | +done |
| 56 | + |
| 57 | +# Clean remote node data (skip any IP matching primary) |
| 58 | +PRIMARY_IP=${VALIDATOR_IPS[0]} |
| 59 | +for i in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 60 | + TARGET_IP=${VALIDATOR_IPS[$i]} |
| 61 | + if [ "$TARGET_IP" = "$PRIMARY_IP" ]; then |
| 62 | + echo "Skipping IP $TARGET_IP since it matches primary node" |
| 63 | + continue |
| 64 | + fi |
| 65 | + echo "Cleaning up data on $TARGET_IP..." |
| 66 | + ssh root@${TARGET_IP} 'rm -rf ~/.hhubd ~/.hhubd* 2>/dev/null || true' |
| 67 | +done |
| 68 | + |
| 69 | +# Initialize primary node |
| 70 | +echo "Initializing primary node..." |
| 71 | +hhubd init "node0" -o --chain-id="${CHAIN_ID}" --home "${HOME_PREFIX}" |
| 72 | + |
| 73 | +# Path variables |
| 74 | +GENESIS="${HOME_PREFIX}/config/genesis.json" |
| 75 | +TMP_GENESIS="${HOME_PREFIX}/config/tmp_genesis.json" |
| 76 | + |
| 77 | +# Change parameter token denominations to ahhub |
| 78 | +jq --arg denom "$DENOM" '.app_state["staking"]["params"]["bond_denom"]=$denom' "$GENESIS" > "$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" |
| 79 | +jq --arg denom "$DENOM" '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]=$denom' "$GENESIS" > "$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" |
| 80 | +jq --arg denom "$DENOM" '.app_state["gov"]["params"]["min_deposit"][0]["denom"]=$denom' "$GENESIS" > "$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" |
| 81 | +jq --arg denom "$DENOM" '.app_state["inflation"]["params"]["mint_denom"]=$denom' "$GENESIS" > "$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" |
| 82 | + |
| 83 | +# Set gas limit in genesis |
| 84 | +jq '.consensus_params["block"]["max_gas"]="10000000"' "$GENESIS" > "$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" |
| 85 | + |
| 86 | +# Set base fee in genesis |
| 87 | +jq --arg fee "$BASEFEE" '.app_state["feemarket"]["params"]["base_fee"]=$fee' "$GENESIS" > "$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" |
| 88 | + |
| 89 | +# Change proposal periods to pass within a reasonable time |
| 90 | +sed -i.bak 's/"max_deposit_period": "172800s"/"max_deposit_period": "30s"/g' "$GENESIS" |
| 91 | +sed -i.bak 's/"voting_period": "172800s"/"voting_period": "30s"/g' "$GENESIS" |
| 92 | +sed -i.bak 's/"expedited_voting_period": "86400s"/"expedited_voting_period": "15s"/g' "$GENESIS" |
| 93 | + |
| 94 | +# Create gentx directory in primary node |
| 95 | +mkdir -p "${HOME_PREFIX}/config/gentx" |
| 96 | + |
| 97 | +# Create validator keys and add genesis accounts |
| 98 | +for i in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 99 | + echo "Creating validator $i key..." |
| 100 | + hhubd keys add "validator$i" \ |
| 101 | + --keyring-backend="${KEYRING}" \ |
| 102 | + --algo="${KEYALGO}" \ |
| 103 | + --home "${HOME_PREFIX}" |
| 104 | + |
| 105 | + echo "Adding genesis account for validator $i..." |
| 106 | + hhubd add-genesis-account "validator$i" "${GENESIS_BALANCE}${DENOM}" \ |
| 107 | + --keyring-backend="${KEYRING}" \ |
| 108 | + --home "${HOME_PREFIX}" |
| 109 | +done |
| 110 | + |
| 111 | +# Create clone directories, gentx, and get node IDs |
| 112 | +declare -a NODE_IDS |
| 113 | +for i in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 114 | + CLONE_HOME="${HOME}/.hhubd$i" |
| 115 | + echo "Creating gentx for validator $i in ${CLONE_HOME}..." |
| 116 | + |
| 117 | + # Initialize fresh node |
| 118 | + rm -rf "${CLONE_HOME}" |
| 119 | + hhubd init "node$i" --chain-id="${CHAIN_ID}" --home "${CLONE_HOME}" >/dev/null 2>&1 |
| 120 | + |
| 121 | + # Get and store node ID early |
| 122 | + NODE_IDS[$i]=$(hhubd tendermint show-node-id --home "${CLONE_HOME}") |
| 123 | + echo "Node $i ID: ${NODE_IDS[$i]}" |
| 124 | + |
| 125 | + # Copy necessary files from primary node |
| 126 | + cp "${HOME_PREFIX}/config/genesis.json" "${CLONE_HOME}/config/" |
| 127 | + cp -r "${HOME_PREFIX}/keyring-test" "${CLONE_HOME}/" 2>/dev/null || true |
| 128 | + mkdir -p "${CLONE_HOME}/config/gentx" |
| 129 | + |
| 130 | + # Set pruning to nothing for archive mode and configure settings |
| 131 | + APP_TOML="${CLONE_HOME}/config/app.toml" |
| 132 | + CONFIG_TOML="${CLONE_HOME}/config/config.toml" |
| 133 | + |
| 134 | + # Archive mode settings |
| 135 | + sed -i.bak 's/^pruning = "default"/pruning = "nothing"/' "$APP_TOML" |
| 136 | + |
| 137 | + # Configure external access in config.toml |
| 138 | + # Update RPC and P2P ports |
| 139 | + sed -i.bak -e '/^\[rpc\]/,/^\[/s|^laddr *= *.*|laddr = "tcp://0.0.0.0:26657"|' "$CONFIG_TOML" |
| 140 | + sed -i.bak -e '/^\[p2p\]/,/^\[/s|^laddr *= *.*|laddr = "tcp://0.0.0.0:26656"|' "$CONFIG_TOML" |
| 141 | + |
| 142 | + # Update other settings |
| 143 | + sed -i.bak \ |
| 144 | + -e "s/^moniker *=.*/moniker = \"node${i}\"/" \ |
| 145 | + -e "s/^proxy_app *=.*/proxy_app = \"tcp:\/\/127.0.0.1:26658\"/" \ |
| 146 | + -e "s/^allow_duplicate_ip *=.*/allow_duplicate_ip = true/" \ |
| 147 | + -e "s/^#allow_duplicate_ip *=.*/allow_duplicate_ip = true/" \ |
| 148 | + "$CONFIG_TOML" |
| 149 | + |
| 150 | + # Set minimum gas price to 0 |
| 151 | + sed -i.bak "s/^minimum-gas-prices *=.*/minimum-gas-prices = \"0${DENOM}\"/g" "$APP_TOML" |
| 152 | + |
| 153 | + # Configure API and EVM settings in app.toml |
| 154 | + sed -i.bak \ |
| 155 | + -e "/^\[api\]/,/^\[/s|^address *= *.*|address = \"tcp://0.0.0.0:${API_PORT}\"|" \ |
| 156 | + -e "/^\[grpc\]/,/^\[/s|^address *= *.*|address = \"0.0.0.0:${GRPC_PORT}\"|" \ |
| 157 | + -e "/^\[grpc-web\]/,/^\[/s|^address *= *.*|address = \"0.0.0.0:${GRPC_WEB_PORT}\"|" \ |
| 158 | + -e "/^\[json-rpc\]/,/^\[/s|^address *= *.*|address = \"0.0.0.0:${JSON_RPC_PORT}\"|" \ |
| 159 | + -e "/^\[json-rpc\]/,/^\[/s|^ws-address *= *.*|ws-address = \"0.0.0.0:${WS_PORT}\"|" \ |
| 160 | + -e "/^\[json-rpc\]/,/^\[/s|^enable *= *.*|enable = true|" \ |
| 161 | + -e "/^\[json-rpc\]/,/^\[/s|^api *= *.*|api = \"eth,net,web3,txpool,debug\"|" \ |
| 162 | + -e 's/^json-rpc.enable-indexer = .*$/json-rpc.enable-indexer = true/' \ |
| 163 | + -e 's/^evm.tracer = .*$/evm.tracer = ""/' \ |
| 164 | + "$APP_TOML" |
| 165 | + |
| 166 | + # Set timeouts for better network stability |
| 167 | + sed -i.bak 's/timeout_propose = "3s"/timeout_propose = "30s"/g' "$CONFIG_TOML" |
| 168 | + sed -i.bak 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' "$CONFIG_TOML" |
| 169 | + sed -i.bak 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' "$CONFIG_TOML" |
| 170 | + sed -i.bak 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' "$CONFIG_TOML" |
| 171 | + sed -i.bak 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' "$CONFIG_TOML" |
| 172 | + sed -i.bak 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' "$CONFIG_TOML" |
| 173 | + sed -i.bak 's/timeout_commit = "5s"/timeout_commit = "150s"/g' "$CONFIG_TOML" |
| 174 | + sed -i.bak 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' "$CONFIG_TOML" |
| 175 | + |
| 176 | + # Use the corresponding validator IP |
| 177 | + PUBLIC_IP=${VALIDATOR_IPS[$i]} |
| 178 | + |
| 179 | + # Create gentx |
| 180 | + hhubd gentx "validator$i" \ |
| 181 | + "${GENTX_STAKE}${DENOM}" \ |
| 182 | + --chain-id="${CHAIN_ID}" \ |
| 183 | + --moniker="node$i" \ |
| 184 | + --commission-rate="0.05" \ |
| 185 | + --commission-max-rate="0.20" \ |
| 186 | + --commission-max-change-rate="0.01" \ |
| 187 | + --min-self-delegation="1" \ |
| 188 | + --ip="${PUBLIC_IP}" \ |
| 189 | + --home "${CLONE_HOME}" \ |
| 190 | + --keyring-backend="${KEYRING}" |
| 191 | + |
| 192 | + # Copy gentx back to primary node |
| 193 | + if [ -d "${CLONE_HOME}/config/gentx" ] && [ "$(ls -A "${CLONE_HOME}/config/gentx")" ]; then |
| 194 | + cp "${CLONE_HOME}/config/gentx/"* "${HOME_PREFIX}/config/gentx/" |
| 195 | + else |
| 196 | + echo "Warning: No gentx files found in ${CLONE_HOME}/config/gentx" |
| 197 | + fi |
| 198 | + |
| 199 | + echo "Gentx created for node $i" |
| 200 | +done |
| 201 | + |
| 202 | +# Collect gentxs |
| 203 | +echo "Collecting gentxs..." |
| 204 | +hhubd collect-gentxs --home "${HOME_PREFIX}" |
| 205 | + |
| 206 | +# Validate genesis |
| 207 | +echo "Validating genesis..." |
| 208 | +hhubd validate-genesis --home "${HOME_PREFIX}" |
| 209 | + |
| 210 | +# Configure peers for each validator |
| 211 | +for i in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 212 | + CLONE_HOME="${HOME}/.hhubd$i" |
| 213 | + PEERS="" |
| 214 | + |
| 215 | + # Build peers string excluding self |
| 216 | + for j in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 217 | + if [ $i -ne $j ]; then |
| 218 | + if [ ! -z "$PEERS" ]; then |
| 219 | + PEERS="${PEERS}," |
| 220 | + fi |
| 221 | + PEERS="${PEERS}${NODE_IDS[$j]}@${VALIDATOR_IPS[$j]}:${P2P_PORT}" |
| 222 | + fi |
| 223 | + done |
| 224 | + |
| 225 | + # Configure peers |
| 226 | + echo "Configuring peers for node $i..." |
| 227 | + sed -i.bak -e "s/^persistent_peers *=.*/persistent_peers = \"$PEERS\"/" "${CLONE_HOME}/config/config.toml" |
| 228 | +done |
| 229 | + |
| 230 | +# Copy genesis to all validators |
| 231 | +for i in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 232 | + CLONE_HOME="${HOME}/.hhubd$i" |
| 233 | + cp "${HOME_PREFIX}/config/genesis.json" "${CLONE_HOME}/config/" |
| 234 | +done |
| 235 | + |
| 236 | +# Copy validator data to target machines (skip any IP matching primary) |
| 237 | +PRIMARY_IP=${VALIDATOR_IPS[0]} |
| 238 | +for i in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 239 | + TARGET_IP=${VALIDATOR_IPS[$i]} |
| 240 | + if [ "$TARGET_IP" = "$PRIMARY_IP" ]; then |
| 241 | + echo "Skipping IP $TARGET_IP since it matches primary node" |
| 242 | + continue |
| 243 | + fi |
| 244 | + echo "Copying validator $i data to $TARGET_IP..." |
| 245 | + # First remove the old directory on remote |
| 246 | + ssh root@${TARGET_IP} "rm -rf ${HOME}/.hhubd${i}" |
| 247 | + # Then copy the new data |
| 248 | + rsync -av "${HOME}/.hhubd${i}/" "root@${TARGET_IP}:${HOME}/.hhubd${i}/" |
| 249 | +done |
| 250 | + |
| 251 | +echo "All validators initialized successfully!" |
| 252 | +echo "Genesis file location: ${HOME_PREFIX}/config/genesis.json" |
| 253 | +echo "Validator information:" |
| 254 | +for i in $(seq 0 $((NUM_VALIDATORS-1))); do |
| 255 | + CLONE_HOME="${HOME}/.hhubd$i" |
| 256 | + echo "Validator $i:" |
| 257 | + echo " Directory: ${CLONE_HOME}" |
| 258 | + echo " Node ID: ${NODE_IDS[$i]}" |
| 259 | + echo " IP: ${VALIDATOR_IPS[$i]}" |
| 260 | +done |
0 commit comments