26
26
"node" : "Only bootstrap and run the Tezos node." ,
27
27
}
28
28
29
- snapshot_import_modes = {
30
- "download rolling" : "Import rolling snapshot from xtz-shots.io (recommended)" ,
31
- "download full" : "Import full snapshot from xtz-shots.io" ,
32
- "file" : "Import snapshot from a file" ,
33
- "url" : "Import snapshot from a url" ,
34
- "skip" : "Skip snapshot import and synchronize with the network from scratch" ,
35
- }
36
-
37
29
systemd_enable = {
38
30
"yes" : "Enable the services, running them both now and on every boot" ,
39
31
"no" : "Start the services this time only" ,
51
43
"on" : "Request to continue or restart the subsidy" ,
52
44
}
53
45
46
+ default_providers = {
47
+ "xtz-shots.io" : "https://xtz-shots.io/tezos-snapshots.json" ,
48
+ "marigold.dev" : "https://snapshots.tezos.marigold.dev/api/tezos-snapshots.json" ,
49
+ }
50
+
51
+ recommended_provider = list (default_providers .keys ())[0 ]
54
52
55
53
TMP_SNAPSHOT_LOCATION = "/tmp/octez_node.snapshot.d/"
56
54
@@ -146,6 +144,10 @@ def __init__(self, actual_sha256=None, expected_sha256=None):
146
144
self .expected_sha256 = expected_sha256
147
145
148
146
147
+ class InterruptStep (Exception ):
148
+ "Raised when there is need to interrupt step handling flow."
149
+
150
+
149
151
def check_file_contents_integrity (filename , sha256 ):
150
152
import hashlib
151
153
@@ -220,9 +222,32 @@ def get_node_version_hash():
220
222
)
221
223
222
224
# We define this step as a function to better tailor snapshot options to the chosen history mode
223
- def get_snapshot_mode_query (modes ):
225
+ def get_snapshot_mode_query (config ):
226
+
227
+ static_import_modes = {
228
+ "file" : "Import snapshot from a file" ,
229
+ "direct url" : "Import snapshot from a direct url" ,
230
+ "provider url" : "Import snapshot from a provider" ,
231
+ "skip" : "Skip snapshot import and synchronize with the network from scratch" ,
232
+ }
233
+
234
+ history_mode = config ["history_mode" ]
235
+
236
+ mk_option = lambda pr , hm = history_mode : f"download { hm } ({ pr } )"
237
+ mk_desc = lambda pr , hm = history_mode : f"Import { hm } snapshot from { pr } " + (
238
+ " (recommended)" if pr == recommended_provider else ""
239
+ )
240
+
241
+ dynamic_import_modes = {}
242
+
243
+ for name in default_providers .keys ():
244
+ if config ["snapshots" ].get (name , None ):
245
+ dynamic_import_modes [mk_option (name )] = mk_desc (name )
246
+
247
+ import_modes = {** dynamic_import_modes , ** static_import_modes }
248
+
224
249
return Step (
225
- id = "snapshot " ,
250
+ id = "snapshot_mode " ,
226
251
prompt = "The Tezos node can take a significant time to bootstrap from scratch.\n "
227
252
"Bootstrapping from a snapshot is suggested instead.\n "
228
253
"How would you like to proceed?" ,
@@ -231,13 +256,13 @@ def get_snapshot_mode_query(modes):
231
256
"which will take a significant amount of time.\n In order to avoid this, we suggest "
232
257
"bootstrapping from a snapshot instead.\n \n "
233
258
"Snapshots can be downloaded from the following websites:\n "
234
- "Tezos Giganode Snapshots - https://snapshots- tezos.giganode.io / \n "
259
+ "Marigold - https://snapshots. tezos.marigold.dev / \n "
235
260
"XTZ-Shots - https://xtz-shots.io/ \n \n "
236
261
"We recommend to use rolling snapshots. This is the smallest and the fastest mode\n "
237
262
"that is sufficient for baking. You can read more about other Tezos node history modes here:\n "
238
263
"https://tezos.gitlab.io/user/history_modes.html#history-modes" ,
239
- options = modes ,
240
- validator = Validator (enum_range_validator (modes )),
264
+ options = import_modes ,
265
+ validator = Validator (enum_range_validator (import_modes )),
241
266
)
242
267
243
268
@@ -250,11 +275,19 @@ def get_snapshot_mode_query(modes):
250
275
validator = Validator ([required_field_validator , filepath_validator ]),
251
276
)
252
277
278
+ provider_url_query = Step (
279
+ id = "provider_url" ,
280
+ prompt = "Provide the url of the snapshot provider." ,
281
+ help = "You have indicated wanting to fetch the snapshot from a custom provider.\n " ,
282
+ default = None ,
283
+ validator = Validator ([required_field_validator , reachable_url_validator ()]),
284
+ )
285
+
253
286
snapshot_url_query = Step (
254
287
id = "snapshot_url" ,
255
288
prompt = "Provide the url of the node snapshot file." ,
256
289
help = "You have indicated wanting to import the snapshot from a custom url.\n "
257
- "You can use e.g. links to XTZ-Shots or Tezos Giganode Snapshots resources." ,
290
+ "You can use e.g. links to XTZ-Shots or Marigold resources." ,
258
291
default = None ,
259
292
validator = Validator ([required_field_validator , reachable_url_validator ()]),
260
293
)
@@ -368,13 +401,12 @@ def check_blockchain_data(self):
368
401
return False
369
402
return True
370
403
371
- # Check https://xtz-shots.io/tezos-snapshots.json and collect the most recent snapshot
404
+ # Check the provider url and collect the most recent snapshot
372
405
# that is suited for the chosen history mode and network
373
- def get_snapshot_link (self ):
406
+ def get_snapshot_metadata (self , name , json_url ):
374
407
def hashes_comply (s1 , s2 ):
375
408
return s1 .startswith (s2 ) or s2 .startswith (s1 )
376
409
377
- json_url = "https://xtz-shots.io/tezos-snapshots.json"
378
410
try :
379
411
snapshot_array = None
380
412
with urllib .request .urlopen (json_url ) as url :
@@ -403,12 +435,23 @@ def hashes_comply(s1, s2):
403
435
{"url" : None , "block_hash" : None },
404
436
)
405
437
406
- self .config ["snapshot_metadata" ] = snapshot_metadata
407
-
408
- except (urllib .error .URLError , ValueError ):
409
- print (f"Couldn't collect snapshot metadata from { json_url } " )
438
+ self .config ["snapshots" ][name ] = snapshot_metadata
439
+ except urllib .error .URLError :
440
+ print (
441
+ color (
442
+ f"\n Couldn't collect snapshot metadata from { json_url } due to networking issues.\n " ,
443
+ color_red ,
444
+ )
445
+ )
446
+ except ValueError :
447
+ print (
448
+ color (
449
+ f"\n Couldn't collect snapshot metadata from { json_url } due to format mismatch.\n " ,
450
+ color_red ,
451
+ )
452
+ )
410
453
except Exception as e :
411
- print (f"Unexpected error handling snapshot metadata:\n { e } \n " )
454
+ print (f"\n Unexpected error handling snapshot metadata:\n { e } \n " )
412
455
413
456
def output_snapshot_metadata (self ):
414
457
from datetime import datetime
@@ -444,6 +487,70 @@ def output_snapshot_metadata(self):
444
487
)
445
488
)
446
489
490
+ def fetch_snapshot_from_provider (self , name ):
491
+ try :
492
+ url = self .config ["snapshots" ][name ]["url" ]
493
+ sha256 = self .config ["snapshots" ][name ]["sha256" ]
494
+ return fetch_snapshot (url , sha256 )
495
+ except KeyError :
496
+ raise InterruptStep
497
+ except (ValueError , urllib .error .URLError ):
498
+ print ()
499
+ print ("The snapshot download option you chose is unavailable," )
500
+ print ("which normally shouldn't happen. Please check your" )
501
+ print ("internet connection or choose another option." )
502
+ print ()
503
+ raise InterruptStep
504
+
505
+ def get_snapshot_from_provider (self , name , url ):
506
+ try :
507
+ self .config ["snapshots" ][name ]
508
+ except KeyError :
509
+ self .get_snapshot_metadata (name , url )
510
+ snapshot_file = self .fetch_snapshot_from_provider (name )
511
+ snapshot_block_hash = self .config ["snapshots" ][name ]["block_hash" ]
512
+ return (snapshot_file , snapshot_block_hash )
513
+
514
+ def get_snapshot_from_direct_url (self , url ):
515
+ try :
516
+ self .query_step (snapshot_sha256_query )
517
+ sha256 = self .config ["snapshot_sha256" ]
518
+ snapshot_file = fetch_snapshot (url , sha256 )
519
+ if sha256 :
520
+ print ("Checking the snapshot integrity..." )
521
+ check_file_contents_integrity (snapshot_file , sha256 )
522
+ print ("Integrity verified." )
523
+ return (snapshot_file , None )
524
+ except (ValueError , urllib .error .URLError ):
525
+ print ()
526
+ print ("The snapshot URL you provided is unavailable." )
527
+ print ("Please check the URL again or choose another option." )
528
+ print ()
529
+ raise InterruptStep
530
+ except Sha256Mismatch as e :
531
+ print ()
532
+ print ("SHA256 mismatch." )
533
+ print (f"Expected sha256: { e .expected_sha256 } " )
534
+ print (f"Actual sha256: { e .actual_sha256 } " )
535
+ print ()
536
+ self .query_step (ignore_hash_mismatch_query )
537
+ if self .config ["ignore_hash_mismatch" ] == "no" :
538
+ raise InterruptStep
539
+ else :
540
+ return (snapshot_file , None )
541
+
542
+ def get_snapshot_from_provider_url (self , url ):
543
+ name = "custom"
544
+ if os .path .basename (url ) == "tezos-snapshots.json" :
545
+ return self .get_snapshot_from_provider (name , url )
546
+ else :
547
+ try :
548
+ return self .get_snapshot_from_provider (name , url )
549
+ except InterruptStep :
550
+ return self .get_snapshot_from_provider (
551
+ name , os .path .join (url , "tezos-snapshots.json" )
552
+ )
553
+
447
554
# Importing the snapshot for Node bootstrapping
448
555
def import_snapshot (self ):
449
556
do_import = self .check_blockchain_data ()
@@ -457,76 +564,57 @@ def import_snapshot(self):
457
564
f"--history-mode { self .config ['history_mode' ]} "
458
565
)
459
566
460
- self .get_snapshot_link ()
567
+ self .config [ "snapshots" ] = {}
461
568
462
- if self .config ["snapshot_metadata" ] is None :
463
- snapshot_import_modes .pop ("download rolling" , None )
464
- snapshot_import_modes .pop ("download full" , None )
465
- elif self .config ["history_mode" ] == "rolling" :
466
- snapshot_import_modes .pop ("download full" , None )
467
- else :
468
- snapshot_import_modes .pop ("download rolling" , None )
569
+ print ("Getting snapshots' metadata from providers..." )
570
+ for name , url in default_providers .items ():
571
+ self .get_snapshot_metadata (name , url )
469
572
470
573
else :
471
574
return
472
575
473
576
while not valid_choice :
474
577
475
- self .query_step (get_snapshot_mode_query (snapshot_import_modes ))
578
+ self .query_step (get_snapshot_mode_query (self . config ))
476
579
477
580
snapshot_file = TMP_SNAPSHOT_LOCATION
478
581
snapshot_block_hash = None
479
582
480
- if self .config ["snapshot" ] == "skip" :
481
- return
482
- elif self .config ["snapshot" ] == "file" :
483
- self .query_step (snapshot_file_query )
484
- snapshot_file = self .config ["snapshot_file" ]
485
- elif self .config ["snapshot" ] == "url" :
486
- self .query_step (snapshot_url_query )
487
- try :
488
- self .query_step (snapshot_sha256_query )
489
- url = self .config ["snapshot_metadata" ]["url" ]
490
- sha256 = self .config ["snapshot_metadata" ]["sha256" ]
491
- snapshot_file = fetch_snapshot (url , sha256 )
492
- if sha256 :
493
- print ("Checking the snapshot integrity..." )
494
- check_file_contents_integrity (snapshot_file , sha256 )
495
- print ("Integrity verified." )
496
- except (ValueError , urllib .error .URLError ):
497
- print ()
498
- print ("The snapshot URL you provided is unavailable." )
499
- print ("Please check the URL again or choose another option." )
500
- print ()
501
- continue
502
- except Sha256Mismatch as e :
503
- print ()
504
- print ("SHA256 mismatch." )
505
- print (f"Expected sha256: { e .expected_sha256 } " )
506
- print (f"Actual sha256: { e .actual_sha256 } " )
507
- print ()
508
- self .query_step (ignore_hash_mismatch_query )
509
- if self .config ["ignore_hash_mismatch" ] == "no" :
510
- continue
511
- else :
512
- url = self .config ["snapshot_metadata" ]["url" ]
513
- sha256 = self .config ["snapshot_metadata" ]["sha256" ]
514
- snapshot_block_hash = self .config ["snapshot_metadata" ]["block_hash" ]
515
- try :
516
- self .output_snapshot_metadata ()
517
- snapshot_file = fetch_snapshot (url , sha256 )
518
- except (ValueError , urllib .error .URLError ):
519
- print ()
520
- print ("The snapshot download option you chose is unavailable," )
521
- print ("which normally shouldn't happen. Please check your" )
522
- print ("internet connection or choose another option." )
523
- print ()
524
- continue
583
+ try :
584
+ if self .config ["snapshot_mode" ] == "skip" :
585
+ return
586
+ elif self .config ["snapshot_mode" ] == "file" :
587
+ self .query_step (snapshot_file_query )
588
+ snapshot_file = self .config ["snapshot_file" ]
589
+ elif self .config ["snapshot_mode" ] == "direct url" :
590
+ self .query_step (snapshot_url_query )
591
+ url = self .config ["snapshot_url" ]
592
+ (
593
+ snapshot_file ,
594
+ snapshot_block_hash ,
595
+ ) = self .get_snapshot_from_direct_url (url )
596
+ elif self .config ["snapshot_mode" ] == "provider url" :
597
+ self .query_step (provider_url_query )
598
+ name , url = "custom" , self .config ["provider_url" ]
599
+ (
600
+ snapshot_file ,
601
+ snapshot_block_hash ,
602
+ ) = self .get_snapshot_from_provider_url (url )
603
+ else :
604
+ for name , url in default_providers .items ():
605
+ if name in self .config ["snapshot_mode" ]:
606
+ (
607
+ snapshot_file ,
608
+ snapshot_block_hash ,
609
+ ) = self .get_snapshot_from_provider (name , url )
610
+ except InterruptStep :
611
+ print ("Getting back to the snapshot import mode step." )
612
+ continue
525
613
526
614
valid_choice = True
527
615
528
616
import_flag = ""
529
- if is_full_snapshot (snapshot_file , self .config ["snapshot " ]):
617
+ if is_full_snapshot (snapshot_file , self .config ["snapshot_mode " ]):
530
618
if self .config ["history_mode" ] == "archive" :
531
619
import_flag = "--reconstruct "
532
620
0 commit comments