@@ -120,6 +120,10 @@ func Bootstrap(
120120 if err != nil {
121121 return nil , err
122122 }
123+ err = lctx .AcquireLock (storage .LockBootstrapping )
124+ if err != nil {
125+ return nil , err
126+ }
123127
124128 config := defaultBootstrapConfig ()
125129 for _ , opt := range options {
@@ -155,10 +159,6 @@ func Bootstrap(
155159 // (The lowest block in sealing segment is the last sealed block, but we don't use that here.)
156160 lastFinalized := segment .Finalized () // highest block in sealing segment; finalized by protocol convention
157161
158- // The spork root block is always provided by a sealing segment separately. This is because the spork root block
159- // may or may not be part of [SealingSegment.Blocks] depending on how much history the sealing segment covers.
160- sporkRootBlock := segment .SporkRootBlock
161-
162162 // bootstrap the sealing segment
163163 // creating sealed root block with the rootResult
164164 // creating finalized root block with lastFinalized
@@ -168,12 +168,6 @@ func Bootstrap(
168168 }
169169
170170 err = db .WithReaderBatchWriter (func (rw storage.ReaderBatchWriter ) error {
171- // initialize spork params
172- err = bootstrapSporkInfo (lctx , rw , blocks , sporkRootBlock )
173- if err != nil {
174- return fmt .Errorf ("could not bootstrap spork info: %w" , err )
175- }
176-
177171 // bootstrap dynamic protocol state
178172 err = bootstrapProtocolState (lctx , rw , segment , root .Params (), epochProtocolStateSnapshots , protocolKVStoreSnapshots , setups , commits , ! config .SkipNetworkAddressValidation )
179173 if err != nil {
@@ -287,6 +281,10 @@ func bootstrapProtocolState(
287281// 4. For the highest seal (`rootSeal`), we index the sealed result ID in the database.
288282// This is necessary for the execution node to confirm that it is starting to execute from the
289283// correct state.
284+ // 5. persist the spork root block. This block is always provided separately in the sealing
285+ // segment, as it may or may not be included in SealingSegment.Blocks depending on how much
286+ // history is covered. The spork root block is persisted as a root proposal without proposer
287+ // signature (by convention).
290288func bootstrapSealingSegment (
291289 lctx lockctx.Proof ,
292290 db storage.DB ,
@@ -475,6 +473,36 @@ func bootstrapSealingSegment(
475473 return err
476474 }
477475
476+ // STEP 5: PERSIST spork root block
477+ // The spork root block is always provided by a sealing segment separately. This is because the spork root block
478+ // may or may not be part of [SealingSegment.Blocks] depending on how much history the sealing segment covers.
479+ sporkRootBlock := segment .SporkRootBlock
480+
481+ // create the spork root proposal
482+ proposal , err := flow .NewRootProposal (
483+ flow.UntrustedProposal {
484+ Block : * sporkRootBlock ,
485+ ProposerSigData : nil , // by protocol convention, the spork root block (or genesis block) don't have a proposer signature
486+ },
487+ )
488+ if err != nil {
489+ return fmt .Errorf ("could not create root proposal for spork root block: %w" , err )
490+ }
491+
492+ err = db .WithReaderBatchWriter (func (rw storage.ReaderBatchWriter ) error {
493+ err = blocks .BatchStore (lctx , rw , proposal )
494+ if err != nil {
495+ // the spork root block may or may not have already been persisted, depending
496+ // on whether the root snapshot sealing segment contained it.
497+ if errors .Is (err , storage .ErrAlreadyExists ) {
498+ return nil
499+ }
500+ return fmt .Errorf ("could not store spork root block: %w" , err )
501+ }
502+
503+ return nil
504+ })
505+
478506 return nil
479507}
480508
@@ -485,6 +513,7 @@ func bootstrapSealingSegment(
485513// - Sealed Root Block Height (block height sealed as of the Root Block)
486514// - Latest Finalized Height (initialized to height of Root Block)
487515// - Latest Sealed Block Height (initialized to block height sealed as of the Root Block)
516+ // - Spork root block ID (spork root block in sealing segment)
488517// - initial entry in map:
489518// Finalized Block ID -> ID of latest seal in fork with this block as head
490519func bootstrapStatePointers (lctx lockctx.Proof , rw storage.ReaderBatchWriter , root protocol.Snapshot ) error {
@@ -498,6 +527,21 @@ func bootstrapStatePointers(lctx lockctx.Proof, rw storage.ReaderBatchWriter, ro
498527 lastFinalized := segment .Finalized () // the lastFinalized block in sealing segment is the latest known finalized block
499528 lastSealed := segment .Sealed () // the lastSealed block in sealing segment is the latest known sealed block
500529
530+ enc , err := datastore .NewVersionedInstanceParams (
531+ datastore .DefaultInstanceParamsVersion ,
532+ lastFinalized .ID (),
533+ lastSealed .ID (),
534+ root .Params ().SporkID (),
535+ )
536+ if err != nil {
537+ return fmt .Errorf ("could not create versioned instance params: %w" , err )
538+ }
539+
540+ err = operation .InsertInstanceParams (lctx , rw , * enc )
541+ if err != nil {
542+ return fmt .Errorf ("could not store instance params: %w" , err )
543+ }
544+
501545 // find the finalized seal that seals the lastSealed block, meaning seal.BlockID == lastSealed.ID()
502546 seal , err := segment .FinalizedSeal ()
503547 if err != nil {
@@ -558,15 +602,6 @@ func bootstrapStatePointers(lctx lockctx.Proof, rw storage.ReaderBatchWriter, ro
558602 }
559603
560604 // insert height pointers
561- err = operation .InsertRootHeight (w , lastFinalized .Height )
562- if err != nil {
563- return fmt .Errorf ("could not insert finalized root height: %w" , err )
564- }
565- // the sealed root height is the lastSealed block in sealing segment
566- err = operation .InsertSealedRootHeight (w , lastSealed .Height )
567- if err != nil {
568- return fmt .Errorf ("could not insert sealed root height: %w" , err )
569- }
570605 err = operation .UpsertFinalizedHeight (lctx , w , lastFinalized .Height )
571606 if err != nil {
572607 return fmt .Errorf ("could not insert finalized height: %w" , err )
@@ -683,44 +718,6 @@ func bootstrapEpochForProtocolStateEntry(
683718 return nil
684719}
685720
686- // bootstrapSporkInfo bootstraps the protocol state with information about the
687- // spork which is used to disambiguate Flow networks.
688- func bootstrapSporkInfo (
689- lctx lockctx.Proof ,
690- rw storage.ReaderBatchWriter ,
691- blocks storage.Blocks ,
692- sporkRootBlock * flow.Block ,
693- ) error {
694- // persist the ID of the spork root block
695- sporkRootBlockID := sporkRootBlock .ID ()
696- err := operation .IndexSporkRootBlock (rw .Writer (), sporkRootBlockID )
697- if err != nil {
698- return fmt .Errorf ("could not insert spork ID: %w" , err )
699- }
700-
701- // store the spork root block
702- proposal , err := flow .NewRootProposal (
703- flow.UntrustedProposal {
704- Block : * sporkRootBlock ,
705- ProposerSigData : nil , // by protocol convention, the spork root block (or genesis block) don't have a proposer signature
706- },
707- )
708- if err != nil {
709- return fmt .Errorf ("could not create root proposal for spork root block: %w" , err )
710- }
711-
712- err = blocks .BatchStore (lctx , rw , proposal )
713- if err != nil {
714- // the spork root block may or may not have already been persisted, depending
715- // on whether the root snapshot sealing segment contained it.
716- if errors .Is (err , storage .ErrAlreadyExists ) {
717- return nil
718- }
719- return fmt .Errorf ("could not store spork root block: %w" , err )
720- }
721- return nil
722- }
723-
724721// indexEpochHeights populates the epoch height index from the root snapshot.
725722// We index the FirstHeight for every epoch where the transition occurs within the sealing segment of the root snapshot,
726723// or for the first epoch of a spork if the snapshot is a spork root snapshot (1 block sealing segment).
@@ -781,10 +778,12 @@ func OpenState(
781778 if ! isBootstrapped {
782779 return nil , fmt .Errorf ("expected database to contain bootstrapped state" )
783780 }
784- sporkRootBlock , err := ReadSporkRootBlock (db , blocks )
781+ instanceParams , err := datastore . ReadInstanceParams (db . Reader (), headers , seals , blocks )
785782 if err != nil {
786- return nil , fmt .Errorf ("could not read spork root block : %w" , err )
783+ return nil , fmt .Errorf ("could not read instance params : %w" , err )
787784 }
785+ sporkRootBlock := instanceParams .SporkRootBlock ()
786+
788787 globalParams := inmem .NewParams (
789788 inmem.EncodableParams {
790789 ChainID : sporkRootBlock .ChainID ,
@@ -793,10 +792,6 @@ func OpenState(
793792 SporkRootBlockView : sporkRootBlock .View ,
794793 },
795794 )
796- instanceParams , err := ReadInstanceParams (db .Reader (), headers , seals )
797- if err != nil {
798- return nil , fmt .Errorf ("could not read instance params: %w" , err )
799- }
800795 params := & datastore.Params {
801796 GlobalParams : globalParams ,
802797 InstanceParams : instanceParams ,
0 commit comments