utils.sh
1 #!/bin/bash 2 3 ###################################### 4 # Utility functions for devnet scripts 5 ###################################### 6 7 # Array to store PIDs of all processes 8 declare -a PIDS 9 10 # How many cores should each node use? 11 # (Should be half of the number of (v)CPUs) 12 # NOTE: when you update this, update TASKSET1/2 as well. 13 # shellcheck disable=SC2034 14 CORES_PER_NODE=8 15 16 # Tasksets to pin processes to specific CPUs. 17 # This is a no-op on MacOS. 18 if [[ "$(uname)" == "Darwin" ]]; then 19 # shellcheck disable=SC2034 20 TASKSET1="" 21 # shellcheck disable=SC2034 22 TASKSET2="" 23 else 24 # shellcheck disable=SC2034 25 TASKSET1="taskset -c 0-7" 26 # shellcheck disable=SC2034 27 TASKSET2="taskset -c 8-15" 28 fi 29 30 # Check if any tracked node process has exited. 31 # Returns 0 if a node stopped, 1 otherwise. 32 function check_node_stopped() { 33 for i in "${!PIDS[@]}"; do 34 local pid="${PIDS[i]}" 35 if ! kill -0 "$pid" 2>/dev/null; then 36 echo "Node #${i} (pid=$pid) has exited unexpectedly" 37 return 0 38 fi 39 done 40 return 1 41 } 42 43 # Function checking that each node in the given range [start_index, end_index) 44 # reached a minimum block height. 45 function check_heights() { 46 local start_index=$1 47 local end_index=$2 48 local min_height=$3 49 local network_name=$4 50 local elapsed=$5 51 52 local all_reached=true 53 local highest_height=0 54 55 for node_index in $(seq "$start_index" $((end_index-1))); do 56 port=$((3030 + node_index)) 57 height=$(curl -s "http://127.0.0.1:$port/v2/$network_name/block/height/latest" || echo "0") 58 59 # Track highest height for reporting 60 if (is_integer "$height") && (( height > highest_height )); then 61 highest_height=$height 62 fi 63 64 if ! (is_integer "$height") || (( height < min_height )); then 65 echo "Node #${node_index} (port=$port) only reached height $height, expected at least $min_height" 66 all_reached=false 67 fi 68 done 69 70 if $all_reached; then 71 echo "✅ SUCCESS: All nodes reached minimum height of $min_height" 72 return 0 73 else 74 if (( elapsed > 0 && ((elapsed % 60) == 0) )); then 75 elapsed_mins=$((elapsed / 60)) 76 echo "⏳ WAITING: Not all nodes reached minimum height of $min_height (highest so far: $highest_height, elapsed: $elapsed_mins minutes)" 77 fi 78 79 return 1 80 fi 81 } 82 83 # Function checking that nodes created logs on disk and they contain no errors. 84 function check_logs() { 85 echo "Checking logs exist for all nodes..." 86 local log_dir=$1 87 local total_validators=$2 88 local total_clients=$3 89 90 local all_reached=true 91 local highest_height=0 92 93 for ((validator_index = 0; validator_index < total_validators; validator_index++)); do 94 if [ ! -s "$log_dir/validator-${validator_index}.log" ]; then 95 echo "❌ Test failed! Validator #${validator_index} did not create any logs in \"$log_dir\"." 96 return 1 97 fi 98 99 if grep -q "ERROR" "$log_dir/validator-${validator_index}.log"; then 100 echo "❌ Test failed! Validator #${validator_index} logs contain errors." 101 # Print the errors to the console. 102 grep "ERROR" "$log_dir/validator-${validator_index}.log" 103 return 1 104 fi 105 done 106 107 for ((client_index = 0; client_index < total_clients; client_index++)); do 108 if [ ! -s "$log_dir/client-${client_index}.log" ]; then 109 echo "❌ Test failed! Client #${client_index} did not create any logs in \"$log_dir\"." 110 return 1 111 fi 112 113 if grep -q "ERROR" "$log_dir/client-${client_index}.log"; then 114 echo "❌ Test failed! Client #${client_index} logs contain errors." 115 # Print the errors to the console. 116 grep "ERROR" "$log_dir/client-${client_index}.log" 117 return 1 118 fi 119 120 done 121 122 return 0 123 } 124 125 # Determine network name based on network_id 126 function get_network_name() { 127 local network_id=$1 128 129 case $network_id in 130 0) 131 echo "mainnet" 132 ;; 133 1) 134 echo "testnet" 135 ;; 136 2) 137 echo "canary" 138 ;; 139 *) 140 >&2 echo "Unknown network ID: $network_id, defaulting to mainnet" 141 echo "mainnet" 142 ;; 143 esac 144 } 145 146 # Stops all running processe in the given list. 147 function stop_nodes() { 148 echo "🚨 Cleaning up ${#PIDS[@]} process(es)…" 149 for pid in "${PIDS[@]}"; do 150 if kill -0 "$pid" 2>/dev/null; then 151 kill -9 "$pid" 2>/dev/null || true 152 fi 153 done 154 155 # block until all nodes have shut down 156 wait 157 } 158 159 # Succeeds if all nodes are available. 160 function check_nodes() { 161 local total_validators=$1 162 local total_clients=$2 163 164 for ((node_index = 0; node_index < total_validators + total_clients; node_index++)); do 165 port=$((3030 + node_index)) 166 status=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:3030/v2/$network_name/version") 167 # Fail if the HTTP response is not 2XX. 168 if (( status < 200 || status > 300 )); then 169 return 1 170 fi 171 done 172 173 return 0 174 } 175 176 # Succeeds if the given string is an integer. 177 function is_integer() { 178 if [[ $1 =~ ^[0-9]+$ ]]; then 179 return 0 180 else 181 return 1 182 fi 183 } 184 185 # Succeeds if the given string is a float. 186 function is_float() { 187 if [[ "$1" =~ ^[+-]?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?$ ]]; then 188 return 0 189 else 190 return 1 191 fi 192 } 193 194 # Succeeds if the node with the given index has the specified number of peers (or greater) 195 function wait_for_peers() { 196 local node_index=$1 197 local min_peers=$2 198 199 local total_wait=0 200 local max_wait=300 201 local poll_interval=1 202 203 while (( total_wait < max_wait )); do 204 result=$(curl -s "http://localhost:3030/v2/$network_name/peers/count") 205 206 if (is_integer "$result") && (( result >= min_peers )); then 207 return 0 208 fi 209 210 # Continue waiting 211 sleep $poll_interval 212 total_wait=$((total_wait+poll_interval)) 213 done 214 215 echo "❌ Nodes did not connect within 5 minutes." 216 return 1 217 } 218 219 # Blocks until the node with the given index has at least one peer to sync from (or times out). 220 function wait_for_sync_peers() { 221 local node_index=$1 222 223 local max_wait=300 224 for ((total_wait=0; total_wait < max_wait; ++total_wait)); do 225 port=$((3030+node_index)) 226 result=$(curl -s "http://localhost:${port}/v2/$network_name/sync/peers") 227 echo "$result" 228 num_peers=$(echo "$result" | jq -r '. | length') 229 230 # Height is set to zero without block locators. So wait for until it is greater than 0 for at least one peer. 231 for ((idx=0; idx<num_peers; ++idx)); do 232 count=$(echo "$result" | jq -r ".[keys[$idx]]") 233 if ((count > 0)); then 234 return 0 235 fi 236 done 237 238 # Continue waiting 239 sleep 1 240 done 241 242 return 1 243 } 244 245 # Blocks until the network is ready. 246 function wait_for_nodes() { 247 echo "Waiting for nodes to become ready" 248 249 local total_validators=$1 250 local total_clients=$2 251 252 while true; do 253 if check_node_stopped; then 254 echo "ERROR: one or more nodes stopped unexpectedly" 255 return 1 256 fi 257 258 if check_nodes "$total_validators" "$total_clients"; then 259 echo "All nodes are ready!" 260 return 0 261 fi 262 263 # Pause to give the nodes time to start up. 264 sleep 1 265 done 266 } 267 268 # Compute the throughput for a number of operation over some time. 269 function compute_throughput { 270 local num_ops=$1 271 local duration=$2 272 local decimal_points=2 273 274 # Use floating point division 275 result=$(bc <<< "scale=$decimal_points; $num_ops/$duration") 276 277 echo "$result" 278 } 279 280 # Print the last 20 lines of logs for all nodes. 281 function print_validator_logs() { 282 local log_dir=$1 283 local total_validators=$2 284 local total_clients=$3 285 286 echo "Last 20 lines of node logs:" 287 for ((validator_index = 0; validator_index < total_validators; validator_index++)); do 288 echo "=== Validator $validator_index logs ===" 289 tail -n 20 "$log_dir/validator-$validator_index.log" 290 done 291 } 292 293 function print_client_logs() { 294 local log_dir=$1 295 local total_validators=$2 296 local total_clients=$3 297 298 for ((client_index = 0; client_index < total_clients; client_index++)); do 299 echo "=== Client $client_index logs ===" 300 node_index=$((total_validators + client_index)) 301 tail -n 20 "$log_dir/client-$client_index.log" 302 done 303 }