github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/state/bootstrap/bootstrap.go (about)

     1  package bootstrap
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/cockroachdb/pebble"
     9  	"github.com/dgraph-io/badger/v2"
    10  	"github.com/rs/zerolog"
    11  
    12  	"github.com/onflow/flow-go/engine/execution/state"
    13  	"github.com/onflow/flow-go/engine/execution/storehouse"
    14  	"github.com/onflow/flow-go/fvm"
    15  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    16  	"github.com/onflow/flow-go/ledger"
    17  	"github.com/onflow/flow-go/model/flow"
    18  	"github.com/onflow/flow-go/storage"
    19  	"github.com/onflow/flow-go/storage/badger/operation"
    20  	pStorage "github.com/onflow/flow-go/storage/pebble"
    21  )
    22  
    23  // an increased limit for bootstrapping
    24  const ledgerIntractionLimitNeededForBootstrapping = 1_000_000_000
    25  
    26  type Bootstrapper struct {
    27  	logger zerolog.Logger
    28  }
    29  
    30  func NewBootstrapper(logger zerolog.Logger) *Bootstrapper {
    31  	return &Bootstrapper{
    32  		logger: logger,
    33  	}
    34  }
    35  
    36  // BootstrapLedger adds the above root account to the ledger and initializes execution node-only data
    37  func (b *Bootstrapper) BootstrapLedger(
    38  	ledger ledger.Ledger,
    39  	servicePublicKey flow.AccountPublicKey,
    40  	chain flow.Chain,
    41  	opts ...fvm.BootstrapProcedureOption,
    42  ) (flow.StateCommitment, error) {
    43  	startCommit := flow.StateCommitment(ledger.InitialState())
    44  	storageSnapshot := state.NewLedgerStorageSnapshot(
    45  		ledger,
    46  		startCommit)
    47  
    48  	vm := fvm.NewVirtualMachine()
    49  
    50  	ctx := fvm.NewContext(
    51  		fvm.WithLogger(b.logger),
    52  		fvm.WithMaxStateInteractionSize(ledgerIntractionLimitNeededForBootstrapping),
    53  		fvm.WithChain(chain),
    54  	)
    55  
    56  	bootstrap := fvm.Bootstrap(
    57  		servicePublicKey,
    58  		opts...,
    59  	)
    60  
    61  	executionSnapshot, _, err := vm.Run(ctx, bootstrap, storageSnapshot)
    62  	if err != nil {
    63  		return flow.DummyStateCommitment, err
    64  	}
    65  
    66  	newStateCommitment, _, _, err := state.CommitDelta(
    67  		ledger,
    68  		executionSnapshot,
    69  		storehouse.NewExecutingBlockSnapshot(storageSnapshot, startCommit),
    70  	)
    71  	if err != nil {
    72  		return flow.DummyStateCommitment, err
    73  	}
    74  
    75  	return newStateCommitment, nil
    76  }
    77  
    78  // IsBootstrapped returns whether the execution database has been bootstrapped, if yes, returns the
    79  // root statecommitment
    80  func (b *Bootstrapper) IsBootstrapped(db *badger.DB) (flow.StateCommitment, bool, error) {
    81  	var commit flow.StateCommitment
    82  
    83  	err := db.View(func(txn *badger.Txn) error {
    84  		err := operation.LookupStateCommitment(flow.ZeroID, &commit)(txn)
    85  		if err != nil {
    86  			return fmt.Errorf("could not lookup state commitment: %w", err)
    87  		}
    88  
    89  		return nil
    90  	})
    91  
    92  	if errors.Is(err, storage.ErrNotFound) {
    93  		return flow.DummyStateCommitment, false, nil
    94  	}
    95  
    96  	if err != nil {
    97  		return flow.DummyStateCommitment, false, err
    98  	}
    99  
   100  	return commit, true, nil
   101  }
   102  
   103  func (b *Bootstrapper) BootstrapExecutionDatabase(
   104  	db *badger.DB,
   105  	rootSeal *flow.Seal,
   106  ) error {
   107  
   108  	commit := rootSeal.FinalState
   109  	err := operation.RetryOnConflict(db.Update, func(txn *badger.Txn) error {
   110  
   111  		err := operation.InsertExecutedBlock(rootSeal.BlockID)(txn)
   112  		if err != nil {
   113  			return fmt.Errorf("could not index initial genesis execution block: %w", err)
   114  		}
   115  
   116  		err = operation.SkipDuplicates(operation.IndexExecutionResult(rootSeal.BlockID, rootSeal.ResultID))(txn)
   117  		if err != nil {
   118  			return fmt.Errorf("could not index result for root result: %w", err)
   119  		}
   120  
   121  		err = operation.IndexStateCommitment(flow.ZeroID, commit)(txn)
   122  		if err != nil {
   123  			return fmt.Errorf("could not index void state commitment: %w", err)
   124  		}
   125  
   126  		err = operation.IndexStateCommitment(rootSeal.BlockID, commit)(txn)
   127  		if err != nil {
   128  			return fmt.Errorf("could not index genesis state commitment: %w", err)
   129  		}
   130  
   131  		snapshots := make([]*snapshot.ExecutionSnapshot, 0)
   132  		err = operation.InsertExecutionStateInteractions(rootSeal.BlockID, snapshots)(txn)
   133  		if err != nil {
   134  			return fmt.Errorf("could not bootstrap execution state interactions: %w", err)
   135  		}
   136  
   137  		return nil
   138  	})
   139  
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  func ImportRegistersFromCheckpoint(logger zerolog.Logger, checkpointFile string, checkpointHeight uint64, checkpointRootHash ledger.RootHash, pdb *pebble.DB, workerCount int) error {
   148  	logger.Info().Msgf("importing registers from checkpoint file %s at height %d with root hash: %v", checkpointFile, checkpointHeight, checkpointRootHash)
   149  
   150  	bootstrap, err := pStorage.NewRegisterBootstrap(pdb, checkpointFile, checkpointHeight, checkpointRootHash, logger)
   151  	if err != nil {
   152  		return fmt.Errorf("could not create registers bootstrapper: %w", err)
   153  	}
   154  
   155  	// TODO: find a way to hook a context up to this to allow a graceful shutdown
   156  	err = bootstrap.IndexCheckpointFile(context.Background(), workerCount)
   157  	if err != nil {
   158  		return fmt.Errorf("could not load checkpoint file: %w", err)
   159  	}
   160  
   161  	logger.Info().Msgf("finish importing registers from checkpoint file %s at height %d", checkpointFile, checkpointHeight)
   162  	return nil
   163  }