github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/dev/scripts/boot-cluster.sh (about) 1 #!/bin/bash 2 # 3 # A script to boot a dev swarm cluster on a Linux host (typically in a Docker 4 # container started with swarm/dev/run.sh). 5 # 6 # The cluster contains a bootnode, a geth node and multiple swarm nodes, with 7 # each node having its own data directory in a base directory passed with the 8 # --dir flag (default is swarm/dev/cluster). 9 # 10 # To avoid using different ports for each node and to make networking more 11 # realistic, each node gets its own network namespace with IPs assigned from 12 # the 192.168.33.0/24 subnet: 13 # 14 # bootnode: 192.168.33.2 15 # geth: 192.168.33.3 16 # swarm: 192.168.33.10{1,2,...,n} 17 18 set -e 19 20 ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" 21 source "${ROOT}/swarm/dev/scripts/util.sh" 22 23 # DEFAULT_BASE_DIR is the default base directory to store node data 24 DEFAULT_BASE_DIR="${ROOT}/swarm/dev/cluster" 25 26 # DEFAULT_CLUSTER_SIZE is the default swarm cluster size 27 DEFAULT_CLUSTER_SIZE=3 28 29 # Linux bridge configuration for connecting the node network namespaces 30 BRIDGE_NAME="swarmbr0" 31 BRIDGE_IP="192.168.33.1" 32 33 # static bootnode configuration 34 BOOTNODE_IP="192.168.33.2" 35 BOOTNODE_PORT="30301" 36 BOOTNODE_KEY="32078f313bea771848db70745225c52c00981589ad6b5b49163f0f5ee852617d" 37 BOOTNODE_PUBKEY="760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d" 38 BOOTNODE_URL="enode://${BOOTNODE_PUBKEY}@${BOOTNODE_IP}:${BOOTNODE_PORT}" 39 40 # static geth configuration 41 GETH_IP="192.168.33.3" 42 GETH_RPC_PORT="8545" 43 GETH_RPC_URL="http://${GETH_IP}:${GETH_RPC_PORT}" 44 45 usage() { 46 cat >&2 <<USAGE 47 usage: $0 [options] 48 49 Boot a dev swarm cluster. 50 51 OPTIONS: 52 -d, --dir DIR Base directory to store node data [default: ${DEFAULT_BASE_DIR}] 53 -s, --size SIZE Size of swarm cluster [default: ${DEFAULT_CLUSTER_SIZE}] 54 -h, --help Show this message 55 USAGE 56 } 57 58 main() { 59 local base_dir="${DEFAULT_BASE_DIR}" 60 local cluster_size="${DEFAULT_CLUSTER_SIZE}" 61 62 parse_args "$@" 63 64 local pid_dir="${base_dir}/pids" 65 local log_dir="${base_dir}/logs" 66 mkdir -p "${base_dir}" "${pid_dir}" "${log_dir}" 67 68 stop_cluster 69 create_network 70 start_bootnode 71 start_geth_node 72 start_swarm_nodes 73 } 74 75 parse_args() { 76 while true; do 77 case "$1" in 78 -h | --help) 79 usage 80 exit 0 81 ;; 82 -d | --dir) 83 if [[ -z "$2" ]]; then 84 fail "--dir flag requires an argument" 85 fi 86 base_dir="$2" 87 shift 2 88 ;; 89 -s | --size) 90 if [[ -z "$2" ]]; then 91 fail "--size flag requires an argument" 92 fi 93 cluster_size="$2" 94 shift 2 95 ;; 96 *) 97 break 98 ;; 99 esac 100 done 101 102 if [[ $# -ne 0 ]]; then 103 usage 104 fail "ERROR: invalid arguments: $@" 105 fi 106 } 107 108 stop_cluster() { 109 info "stopping existing cluster" 110 "${ROOT}/swarm/dev/scripts/stop-cluster.sh" --dir "${base_dir}" 111 } 112 113 # create_network creates a Linux bridge which is used to connect the node 114 # network namespaces together 115 create_network() { 116 local subnet="${BRIDGE_IP}/24" 117 118 info "creating ${subnet} network on ${BRIDGE_NAME}" 119 ip link add name "${BRIDGE_NAME}" type bridge 120 ip link set dev "${BRIDGE_NAME}" up 121 ip address add "${subnet}" dev "${BRIDGE_NAME}" 122 } 123 124 # start_bootnode starts a bootnode which is used to bootstrap the geth and 125 # swarm nodes 126 start_bootnode() { 127 local key_file="${base_dir}/bootnode.key" 128 echo -n "${BOOTNODE_KEY}" > "${key_file}" 129 130 local args=( 131 --addr "${BOOTNODE_IP}:${BOOTNODE_PORT}" 132 --nodekey "${key_file}" 133 --verbosity "6" 134 ) 135 136 start_node "bootnode" "${BOOTNODE_IP}" "$(which bootnode)" ${args[@]} 137 } 138 139 # start_geth_node starts a geth node with --datadir pointing at <base-dir>/geth 140 # and a single, unlocked account with password "geth" 141 start_geth_node() { 142 local dir="${base_dir}/geth" 143 mkdir -p "${dir}" 144 145 local password="geth" 146 echo "${password}" > "${dir}/password" 147 148 # create an account if necessary 149 if [[ ! -e "${dir}/keystore" ]]; then 150 info "creating geth account" 151 create_account "${dir}" "${password}" 152 fi 153 154 # get the account address 155 local address="$(jq --raw-output '.address' ${dir}/keystore/*)" 156 if [[ -z "${address}" ]]; then 157 fail "failed to get geth account address" 158 fi 159 160 local args=( 161 --datadir "${dir}" 162 --networkid "321" 163 --bootnodes "${BOOTNODE_URL}" 164 --unlock "${address}" 165 --password "${dir}/password" 166 --rpc 167 --rpcaddr "${GETH_IP}" 168 --rpcport "${GETH_RPC_PORT}" 169 --verbosity "6" 170 ) 171 172 start_node "geth" "${GETH_IP}" "$(which geth)" ${args[@]} 173 } 174 175 start_swarm_nodes() { 176 for i in $(seq 1 ${cluster_size}); do 177 start_swarm_node "${i}" 178 done 179 } 180 181 # start_swarm_node starts a swarm node with a name like "swarmNN" (where NN is 182 # a zero-padded integer like "07"), --datadir pointing at <base-dir>/<name> 183 # (e.g. <base-dir>/swarm07) and a single account with <name> as the password 184 start_swarm_node() { 185 local num=$1 186 local name="swarm$(printf '%02d' ${num})" 187 local ip="192.168.33.1$(printf '%02d' ${num})" 188 189 local dir="${base_dir}/${name}" 190 mkdir -p "${dir}" 191 192 local password="${name}" 193 echo "${password}" > "${dir}/password" 194 195 # create an account if necessary 196 if [[ ! -e "${dir}/keystore" ]]; then 197 info "creating account for ${name}" 198 create_account "${dir}" "${password}" 199 fi 200 201 # get the account address 202 local address="$(jq --raw-output '.address' ${dir}/keystore/*)" 203 if [[ -z "${address}" ]]; then 204 fail "failed to get swarm account address" 205 fi 206 207 local args=( 208 --bootnodes "${BOOTNODE_URL}" 209 --datadir "${dir}" 210 --identity "${name}" 211 --ens-api "${GETH_RPC_URL}" 212 --bzznetworkid "321" 213 --bzzaccount "${address}" 214 --password "${dir}/password" 215 --verbosity "6" 216 ) 217 218 start_node "${name}" "${ip}" "$(which swarm)" ${args[@]} 219 } 220 221 # start_node runs the node command as a daemon in a network namespace 222 start_node() { 223 local name="$1" 224 local ip="$2" 225 local path="$3" 226 local cmd_args=${@:4} 227 228 info "starting ${name} with IP ${ip}" 229 230 create_node_network "${name}" "${ip}" 231 232 # add a marker to the log file 233 cat >> "${log_dir}/${name}.log" <<EOF 234 235 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 236 Starting ${name} node - $(date) 237 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 238 239 EOF 240 241 # run the command in the network namespace using start-stop-daemon to 242 # daemonise the process, sending all output to the log file 243 local daemon_args=( 244 --start 245 --background 246 --no-close 247 --make-pidfile 248 --pidfile "${pid_dir}/${name}.pid" 249 --exec "${path}" 250 ) 251 if ! ip netns exec "${name}" start-stop-daemon ${daemon_args[@]} -- $cmd_args &>> "${log_dir}/${name}.log"; then 252 fail "could not start ${name}, check ${log_dir}/${name}.log" 253 fi 254 } 255 256 # create_node_network creates a network namespace and connects it to the Linux 257 # bridge using a veth pair 258 create_node_network() { 259 local name="$1" 260 local ip="$2" 261 262 # create the namespace 263 ip netns add "${name}" 264 265 # create the veth pair 266 local veth0="veth${name}0" 267 local veth1="veth${name}1" 268 ip link add name "${veth0}" type veth peer name "${veth1}" 269 270 # add one end to the bridge 271 ip link set dev "${veth0}" master "${BRIDGE_NAME}" 272 ip link set dev "${veth0}" up 273 274 # add the other end to the namespace, rename it eth0 and give it the ip 275 ip link set dev "${veth1}" netns "${name}" 276 ip netns exec "${name}" ip link set dev "${veth1}" name "eth0" 277 ip netns exec "${name}" ip link set dev "eth0" up 278 ip netns exec "${name}" ip address add "${ip}/24" dev "eth0" 279 } 280 281 create_account() { 282 local dir=$1 283 local password=$2 284 285 geth --datadir "${dir}" --password /dev/stdin account new <<< "${password}" 286 } 287 288 main "$@"