1+ #! /bin/bash
2+
3+ # Sequencer Migration Script
4+ # Migrates sequencing from l2geth -> l2reth -> l2geth
5+ # Usage: ./migrate-sequencer.sh <blocks_to_produce>
6+
7+ set -euo pipefail
8+
9+ # Colors for output
10+ RED=' \033[0;31m'
11+ GREEN=' \033[0;32m'
12+ YELLOW=' \033[1;33m'
13+ BLUE=' \033[0;34m'
14+ NC=' \033[0m' # No Color
15+
16+ # Configuration
17+ SYNC_TIMEOUT=5 # 5 seconds timeout for sync operations
18+ POLL_INTERVAL=0.1 # Poll every 0.1 seconds
19+
20+ # Global variables to track state
21+ START_TIME=$( date +%s)
22+ INITIAL_L2GETH_BLOCK=" "
23+ INITIAL_L2RETH_BLOCK=" "
24+ L2GETH_STOP_BLOCK=" "
25+ L2RETH_FINAL_BLOCK=" "
26+
27+ # Helper function to print colored output
28+ log () {
29+ echo -e " ${2:- $NC } [$( date ' +%H:%M:%S' ) ] $1 ${NC} "
30+ }
31+
32+ log_info () { log " $1 " " $BLUE " ; }
33+ log_success () { log " $1 " " $GREEN " ; }
34+ log_warning () { log " $1 " " $YELLOW " ; }
35+ log_error () { log " $1 " " $RED " ; }
36+
37+ # Check if required environment variables are set
38+ check_env_vars () {
39+ log_info " Checking environment variables..."
40+
41+ if [[ -z " ${L2GETH_RPC_URL:- } " ]]; then
42+ log_error " L2GETH_RPC_URL environment variable is required"
43+ exit 1
44+ fi
45+
46+ if [[ -z " ${L2RETH_RPC_URL:- } " ]]; then
47+ log_error " L2RETH_RPC_URL environment variable is required"
48+ exit 1
49+ fi
50+
51+ log_success " Environment variables configured"
52+ log_info " L2GETH_RPC_URL: $L2GETH_RPC_URL "
53+ log_info " L2RETH_RPC_URL: $L2RETH_RPC_URL "
54+ }
55+
56+ # Get block number and hash for a given RPC URL
57+ get_block_info () {
58+ local rpc_url=" $1 "
59+ local temp_file=$( mktemp)
60+
61+ if ! cast block latest --rpc-url " $rpc_url " > " $temp_file " 2> /dev/null; then
62+ rm -f " $temp_file "
63+ return 1
64+ fi
65+
66+ local block_number=$( grep " ^number" " $temp_file " | awk ' {print $2}' )
67+ local block_hash=$( grep " ^hash" " $temp_file " | awk ' {print $2}' )
68+
69+ rm -f " $temp_file "
70+ echo " $block_number $block_hash "
71+ }
72+
73+ # Get only block number for a given RPC URL
74+ get_block_number () {
75+ local rpc_url=" $1 "
76+ cast block latest --rpc-url " $rpc_url " 2> /dev/null | grep " ^number" | awk ' {print $2}'
77+ }
78+
79+ is_l2geth_mining () {
80+ local result=$( cast rpc eth_mining --rpc-url " $L2GETH_RPC_URL " 2> /dev/null | tr -d ' "' )
81+ [[ " $result " == " true" ]]
82+ }
83+
84+ start_l2geth_mining () {
85+ log_info " Starting l2geth mining..."
86+ if cast rpc miner_start --rpc-url " $L2GETH_RPC_URL " > /dev/null 2>&1 ; then
87+ log_success " L2GETH mining started"
88+ return 0
89+ else
90+ log_error " Failed to start l2geth mining"
91+ return 1
92+ fi
93+ }
94+
95+ stop_l2geth_mining () {
96+ log_info " Stopping l2geth mining..."
97+ if cast rpc miner_stop --rpc-url " $L2GETH_RPC_URL " > /dev/null 2>&1 ; then
98+ log_success " L2GETH mining stopped"
99+ return 0
100+ else
101+ log_error " Failed to stop l2geth mining"
102+ return 1
103+ fi
104+ }
105+
106+ enable_l2reth_sequencing () {
107+ log_info " Enabling L2RETH automatic sequencing..."
108+ if cast rpc rollupNode_enableAutomaticSequencing --rpc-url " $L2RETH_RPC_URL " > /dev/null 2>&1 ; then
109+ log_success " L2RETH automatic sequencing enabled"
110+ return 0
111+ else
112+ log_error " Failed to enable L2RETH automatic sequencing"
113+ return 1
114+ fi
115+ }
116+
117+ disable_l2reth_sequencing () {
118+ log_info " Disabling L2RETH automatic sequencing..."
119+ if cast rpc rollupNode_disableAutomaticSequencing --rpc-url " $L2RETH_RPC_URL " > /dev/null 2>&1 ; then
120+ log_success " L2RETH automatic sequencing disabled"
121+ return 0
122+ else
123+ log_error " Failed to disable L2RETH automatic sequencing"
124+ return 1
125+ fi
126+ }
127+
128+ wait_for_block () {
129+ local rpc_url=" $1 "
130+ local target_block=" $2 "
131+ local node_name=" $3 "
132+ local target_hash=" $4 "
133+
134+ log_info " Waiting for $node_name to reach block #$target_block (hash: $target_hash )..."
135+
136+ local start_time=$( date +%s)
137+ while true ; do
138+ local current_time=$( date +%s)
139+ local elapsed=$(( current_time - start_time))
140+
141+ if [[ $elapsed -gt $SYNC_TIMEOUT ]]; then
142+ log_error " Timeout waiting for $node_name to reach block #$target_block "
143+ return 1
144+ fi
145+
146+ local block_info
147+ if block_info=$( get_block_info " $rpc_url " ) ; then
148+ local current_block=$( echo " $block_info " | awk ' {print $1}' )
149+ local current_hash=$( echo " $block_info " | awk ' {print $2}' )
150+
151+ if [[ " $current_block " -ge " $target_block " ]]; then
152+ if [[ " $current_block " -eq " $target_block " && " $current_hash " == " $target_hash " ]]; then
153+ log_success " $node_name reached target block #$target_block (hash: $target_hash )"
154+ return 0
155+ elif [[ " $current_block " -gt " $target_block " ]]; then
156+ log_success " $node_name surpassed target, now at block #$current_block (hash: $current_hash )"
157+ return 0
158+ else
159+ log_warning " $node_name at block #$current_block but hash mismatch: expected $target_hash , got $current_hash "
160+ fi
161+ fi
162+ fi
163+
164+ sleep $POLL_INTERVAL
165+ done
166+ }
167+
168+ check_rpc_connectivity () {
169+ log_info " Checking RPC connectivity..."
170+
171+ if ! get_block_info " $L2GETH_RPC_URL " > /dev/null; then
172+ log_error " Cannot connect to L2GETH at $L2GETH_RPC_URL "
173+ exit 1
174+ fi
175+
176+ if ! get_block_info " $L2RETH_RPC_URL " > /dev/null; then
177+ log_error " Cannot connect to L2RETH at $L2RETH_RPC_URL "
178+ exit 1
179+ fi
180+
181+ log_success " Both nodes are accessible"
182+ }
183+
184+ pre_flight_checks () {
185+ log_info " === PRE-FLIGHT CHECKS ==="
186+
187+ check_rpc_connectivity
188+
189+ # Get initial block states
190+ local l2geth_info=$( get_block_info " $L2GETH_RPC_URL " )
191+ local l2reth_info=$( get_block_info " $L2RETH_RPC_URL " )
192+
193+ INITIAL_L2GETH_BLOCK=$( echo " $l2geth_info " | awk ' {print $1}' )
194+ local l2geth_hash=$( echo " $l2geth_info " | awk ' {print $2}' )
195+ INITIAL_L2RETH_BLOCK=$( echo " $l2reth_info " | awk ' {print $1}' )
196+ local l2reth_hash=$( echo " $l2reth_info " | awk ' {print $2}' )
197+
198+ log_info " L2GETH current block: #$INITIAL_L2GETH_BLOCK (hash: $l2geth_hash )"
199+ log_info " L2RETH current block: #$INITIAL_L2RETH_BLOCK (hash: $l2reth_hash )"
200+
201+ # Check if l2geth is mining
202+ if ! is_l2geth_mining; then
203+ log_error " L2GETH is not currently mining. Please start mining first."
204+ exit 1
205+ fi
206+ log_success " L2GETH is currently mining"
207+
208+ # Verify nodes are on the same chain by comparing a recent block
209+ local compare_block=$(( INITIAL_L2 RETH_BLOCK < INITIAL_L2 GETH_BLOCK ? INITIAL_L2 RETH_BLOCK : INITIAL_L2 GETH_BLOCK))
210+ if [[ $compare_block -gt 0 ]]; then
211+ local l2geth_compare_hash=$( cast block " $compare_block " --rpc-url " $L2GETH_RPC_URL " 2> /dev/null | grep " ^hash" | awk ' {print $2}' )
212+ local l2reth_compare_hash=$( cast block " $compare_block " --rpc-url " $L2RETH_RPC_URL " 2> /dev/null | grep " ^hash" | awk ' {print $2}' )
213+
214+ if [[ " $l2geth_compare_hash " != " $l2reth_compare_hash " ]]; then
215+ log_error " Nodes are on different chains! Block #$compare_block hashes differ:"
216+ log_error " L2GETH: $l2geth_compare_hash "
217+ log_error " L2RETH: $l2reth_compare_hash "
218+ exit 1
219+ fi
220+ log_success " Nodes are on the same chain (verified at block #$compare_block )"
221+ fi
222+
223+ log_success " Pre-flight checks completed"
224+ }
225+
226+ print_summary () {
227+ local end_time=$( date +%s)
228+ local total_time=$(( end_time - START_TIME))
229+
230+ log_info " === MIGRATION SUMMARY ==="
231+ log_info " Migration completed in ${total_time} s"
232+ log_info " Initial L2GETH block: #$INITIAL_L2GETH_BLOCK "
233+ log_info " Initial L2RETH block: #$INITIAL_L2RETH_BLOCK "
234+ log_info " L2GETH stopped at block: #$L2GETH_STOP_BLOCK "
235+ log_info " L2RETH final block: #$L2RETH_FINAL_BLOCK "
236+
237+ local final_l2geth_info=$( get_block_info " $L2GETH_RPC_URL " )
238+ local final_l2geth_block=$( echo " $final_l2geth_info " | awk ' {print $1}' )
239+ local final_l2geth_hash=$( echo " $final_l2geth_info " | awk ' {print $2}' )
240+ log_info " Final L2GETH block: #$final_l2geth_block (hash: $final_l2geth_hash )"
241+
242+ log_success " Sequencer migration completed successfully!"
243+ }
244+
245+ main () {
246+ # Check arguments
247+ if [[ $# -ne 1 ]]; then
248+ echo " Usage: $0 <blocks_to_produce>"
249+ echo " blocks_to_produce: Number of blocks for L2RETH to produce during migration"
250+ exit 1
251+ fi
252+
253+ local blocks_to_produce=" $1 "
254+
255+ # Validate blocks_to_produce is a positive integer
256+ if ! [[ " $blocks_to_produce " =~ ^[1-9][0-9]* $ ]]; then
257+ log_error " blocks_to_produce must be a positive integer, got: $blocks_to_produce "
258+ exit 1
259+ fi
260+
261+ log_info " Starting sequencer migration: L2GETH -> L2RETH -> L2GETH"
262+ log_info " L2RETH will produce $blocks_to_produce blocks"
263+
264+ check_env_vars
265+ pre_flight_checks
266+
267+ # Double check if user wants to proceed
268+ read -p " Proceed with migration? (y/N): " confirm
269+ if [[ " $confirm " != " y" && " $confirm " != " Y" ]]; then
270+ log_warning " Migration aborted by user"
271+ exit 0
272+ fi
273+
274+ # Phase 1: Stop L2GETH sequencing
275+ log_info " === PHASE 1: STOPPING L2GETH SEQUENCING ==="
276+ stop_l2geth_mining
277+
278+ # Record where L2GETH stopped
279+ local stop_info=$( get_block_info " $L2GETH_RPC_URL " )
280+ L2GETH_STOP_BLOCK=$( echo " $stop_info " | awk ' {print $1}' )
281+ local stop_hash=$( echo " $stop_info " | awk ' {print $2}' )
282+ log_success " L2GETH sequencing stopped at block #$L2GETH_STOP_BLOCK (hash: $stop_hash )"
283+
284+ # Phase 2: Wait for L2RETH to sync
285+ log_info " === PHASE 2: WAITING FOR L2RETH SYNC ==="
286+ wait_for_block " $L2RETH_RPC_URL " " $L2GETH_STOP_BLOCK " " L2RETH" " $stop_hash "
287+
288+ # Phase 3: Enable L2RETH sequencing and wait for blocks
289+ log_info " === PHASE 3: L2RETH SEQUENCING ($blocks_to_produce blocks) ==="
290+ enable_l2reth_sequencing
291+
292+ local target_block=$(( L2 GETH_STOP_BLOCK + blocks_to_produce))
293+ log_info " Waiting for L2RETH to produce $blocks_to_produce blocks (target: #$target_block )..."
294+
295+ # Monitor block production
296+ local current_block=$L2GETH_STOP_BLOCK
297+ while [[ $current_block -lt $target_block ]]; do
298+ sleep $POLL_INTERVAL
299+ local new_block=$( get_block_number " $L2RETH_RPC_URL " )
300+ if [[ $new_block -gt $current_block ]]; then
301+ local block_info=$( get_block_info " $L2RETH_RPC_URL " )
302+ local block_hash=$( echo " $block_info " | awk ' {print $2}' )
303+ log_success " L2RETH produced block #$new_block (hash: $block_hash )"
304+ current_block=$new_block
305+ fi
306+ done
307+
308+ # Phase 4: Stop L2RETH sequencing
309+ log_info " === PHASE 4: STOPPING L2RETH SEQUENCING ==="
310+ disable_l2reth_sequencing
311+
312+ # Record final L2RETH block
313+ local final_info=$( get_block_info " $L2RETH_RPC_URL " )
314+ L2RETH_FINAL_BLOCK=$( echo " $final_info " | awk ' {print $1}' )
315+ local final_hash=$( echo " $final_info " | awk ' {print $2}' )
316+ log_success " L2RETH sequencing stopped at block #$L2RETH_FINAL_BLOCK (hash: $final_hash )"
317+
318+ # Phase 5: Wait for L2GETH to sync
319+ log_info " === PHASE 5: WAITING FOR L2GETH SYNC ==="
320+ wait_for_block " $L2GETH_RPC_URL " " $L2RETH_FINAL_BLOCK " " L2GETH" " $final_hash "
321+
322+ # Phase 6: Resume L2GETH sequencing
323+ log_info " === PHASE 6: RESUMING L2GETH SEQUENCING ==="
324+ start_l2geth_mining
325+
326+ # TODO: this could be done with wait for function?
327+ # Wait for at least one new block to confirm
328+ log_info " Waiting for L2GETH to produce at least one new block..."
329+ local confirmation_target=$(( L2 RETH_FINAL_BLOCK + 1 ))
330+ local start_time=$( date +%s)
331+ while true ; do
332+ local current_time=$( date +%s)
333+ local elapsed=$(( current_time - start_time))
334+
335+ if [[ $elapsed -gt 60 ]]; then # 1 minute timeout for first block
336+ log_error " Timeout waiting for L2GETH to produce new block"
337+ exit 1
338+ fi
339+
340+ local current_block=$( get_block_number " $L2GETH_RPC_URL " )
341+ if [[ $current_block -ge $confirmation_target ]]; then
342+ local confirm_info=$( get_block_info " $L2GETH_RPC_URL " )
343+ local confirm_hash=$( echo " $confirm_info " | awk ' {print $2}' )
344+ log_success " L2GETH sequencing resumed, produced block #$current_block (hash: $confirm_hash )"
345+ break
346+ fi
347+
348+ sleep $POLL_INTERVAL
349+ done
350+
351+ print_summary
352+ }
353+
354+ # Run main function
355+ main " $@ "
0 commit comments