A very experimental PLC implementation which uses BFT consensus for decentralization
1#!/bin/sh
2
3# Default to 4 nodes if no argument provided
4NUM_NODES="${1:-4}"
5
6# Validate input
7if ! echo "$NUM_NODES" | grep -qE '^[0-9]+$'; then
8 echo "Error: Number of nodes must be a positive integer"
9 echo "Usage: $0 [number_of_nodes]"
10 echo "Example: $0 7"
11 exit 1
12fi
13
14if [ "$NUM_NODES" -lt 1 ]; then
15 echo "Error: Number of nodes must be at least 1"
16 exit 1
17fi
18
19echo "Setting up testnet with $NUM_NODES nodes..."
20
21# Build the binary
22echo "Building didplcbft binary..."
23go build -trimpath
24
25# Clean up existing testnet data
26echo "Cleaning up existing testnet data..."
27rm -r testnet/node*
28
29# Generate testnet with specified number of nodes
30echo "Generating testnet configuration for $NUM_NODES nodes..."
31go run github.com/cometbft/cometbft/cmd/cometbft@v0.38.19 testnet --v "$((NUM_NODES - 1))" --n 1 --starting-ip-address 127.67.67.1 --config ./testnet/baseconfig.toml --o ./testnet
32
33# Adjust RPC and P2P listen addresses for each node
34echo "Configuring RPC and P2P addresses for $NUM_NODES nodes..."
35
36for i in $(seq 0 $((NUM_NODES - 1))); do
37 # Calculate RPC port (starting from 26100)
38 rpc_port=$((26100 + i))
39
40 # Calculate P2P IP address (127.67.67.1 + node_index)
41 p2p_ip="127.67.67.$((1 + i))"
42
43 echo " Configuring node$i (RPC: $rpc_port, P2P: $p2p_ip:26656)"
44
45 # Adjust RPC listen address
46 sed -i "s|^laddr = \"tcp://127.0.0.1:26657\"\$|laddr = \"tcp://127.0.0.1:$rpc_port\"|g" "testnet/node$i/config/config.toml"
47
48 # Adjust P2P listen address
49 sed -i "s|^laddr = \"tcp://0.0.0.0:26656\"\$|laddr = \"tcp://$p2p_ip:26656\"|g" "testnet/node$i/config/config.toml"
50
51 if [ "$i" -ne 0 ]; then
52 echo -e "\n[plc]\nladdr = \"\"" >> "testnet/node$i/config/config.toml"
53 fi
54done
55
56# Configure rpc_servers for the last node (the one that will be started manually)
57last_node=$((NUM_NODES - 1))
58echo "Configuring rpc_servers for node$last_node..."
59
60# Build comma-separated list of RPC addresses for automatically started nodes
61rpc_servers_list=""
62for i in $(seq 0 $((NUM_NODES - 2))); do
63 if [ -n "$rpc_servers_list" ]; then
64 rpc_servers_list="$rpc_servers_list,"
65 fi
66 rpc_port=$((26100 + i))
67 rpc_servers_list="${rpc_servers_list}tcp://127.0.0.1:$rpc_port"
68done
69
70# Replace empty rpc_servers configuration in the last node's config
71echo " Setting rpc_servers = \"$rpc_servers_list\" for node$last_node"
72sed -i "s|^rpc_servers = \"\"\$|rpc_servers = \"$rpc_servers_list\"|g" "testnet/node$last_node/config/config.toml"
73
74# Enable state sync for the last node (the one that will be started manually)
75echo " Enabling state sync for node$last_node"
76sed -i '/\[statesync\]/,/enable = false/s/enable = false/enable = true/' "testnet/node$last_node/config/config.toml"
77
78# Array to store background process IDs
79pids=""
80
81# Cleanup function to kill all background processes
82cleanup() {
83 echo ""
84 echo "Shutting down all nodes..."
85
86 # Kill all background processes
87 for pid in $pids; do
88 if kill -0 "$pid" 2>/dev/null; then
89 echo " Stopping node process $pid..."
90 kill "$pid" 2>/dev/null
91 fi
92 done
93
94 # Clean up temporary fifo files
95 for i in $(seq 0 99); do
96 rm -f "/tmp/didplcbft-node$i-stdout" "/tmp/didplcbft-node$i-stderr" 2>/dev/null
97 done
98
99 # Wait for all processes to terminate
100 wait $pids 2>/dev/null
101
102 echo "All nodes stopped."
103 exit 0
104}
105
106# Set up signal traps
107trap cleanup INT TERM EXIT
108
109# Launch all nodes except the last one (for testing later bringup)
110nodes_to_start=$((NUM_NODES - 1))
111echo "Launching $nodes_to_start nodes in parallel..."
112
113for i in $(seq 0 $((nodes_to_start - 1))); do
114 echo " Starting node$i..."
115 mkfifo "/tmp/didplcbft-node$i-stdout" 2>/dev/null || true
116 mkfifo "/tmp/didplcbft-node$i-stderr" 2>/dev/null || true
117
118 # Start sed processes to prefix output
119 sed "s/^/[node$i-stdout] /" < "/tmp/didplcbft-node$i-stdout" &
120 sed "s/^/[node$i-stderr] /" < "/tmp/didplcbft-node$i-stderr" &
121
122 # Start the didplcbft process with redirected output
123 ./didplcbft --data-dir "testnet/node$i" > "/tmp/didplcbft-node$i-stdout" 2> "/tmp/didplcbft-node$i-stderr" &
124 pid=$!
125 pids="$pids $pid"
126 echo " PID: $pid"
127done
128
129echo ""
130echo "All $nodes_to_start nodes are now running."
131echo "Note: Node $((NUM_NODES - 1)) is not started and can be launched later for testing."
132echo "Press Ctrl+C to stop all running nodes."
133echo ""
134
135# Wait for all background processes
136wait $pids
137
138# If we reach here, all processes have terminated normally
139echo "All nodes have terminated."