github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/verification_builder.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/spf13/pflag"
     8  
     9  	flowconsensus "github.com/onflow/flow-go/consensus"
    10  	"github.com/onflow/flow-go/consensus/hotstuff"
    11  	"github.com/onflow/flow-go/consensus/hotstuff/committees"
    12  	"github.com/onflow/flow-go/consensus/hotstuff/notifications"
    13  	"github.com/onflow/flow-go/consensus/hotstuff/notifications/pubsub"
    14  	hotsignature "github.com/onflow/flow-go/consensus/hotstuff/signature"
    15  	"github.com/onflow/flow-go/consensus/hotstuff/validator"
    16  	"github.com/onflow/flow-go/consensus/hotstuff/verification"
    17  	recoveryprotocol "github.com/onflow/flow-go/consensus/recovery/protocol"
    18  	"github.com/onflow/flow-go/engine/common/follower"
    19  	followereng "github.com/onflow/flow-go/engine/common/follower"
    20  	commonsync "github.com/onflow/flow-go/engine/common/synchronization"
    21  	"github.com/onflow/flow-go/engine/execution/computation"
    22  	"github.com/onflow/flow-go/engine/verification/assigner"
    23  	"github.com/onflow/flow-go/engine/verification/assigner/blockconsumer"
    24  	"github.com/onflow/flow-go/engine/verification/fetcher"
    25  	"github.com/onflow/flow-go/engine/verification/fetcher/chunkconsumer"
    26  	"github.com/onflow/flow-go/engine/verification/requester"
    27  	"github.com/onflow/flow-go/engine/verification/verifier"
    28  	"github.com/onflow/flow-go/fvm"
    29  	"github.com/onflow/flow-go/model/flow"
    30  	"github.com/onflow/flow-go/module"
    31  	"github.com/onflow/flow-go/module/chainsync"
    32  	"github.com/onflow/flow-go/module/chunks"
    33  	finalizer "github.com/onflow/flow-go/module/finalizer/consensus"
    34  	"github.com/onflow/flow-go/module/mempool"
    35  	"github.com/onflow/flow-go/module/mempool/stdmap"
    36  	"github.com/onflow/flow-go/module/metrics"
    37  	"github.com/onflow/flow-go/state/protocol"
    38  	badgerState "github.com/onflow/flow-go/state/protocol/badger"
    39  	"github.com/onflow/flow-go/state/protocol/blocktimer"
    40  	"github.com/onflow/flow-go/storage/badger"
    41  )
    42  
    43  type VerificationConfig struct {
    44  	chunkAlpha uint // number of verifiers assigned per chunk.
    45  	chunkLimit uint // size of chunk-related memory pools.
    46  
    47  	requestInterval    time.Duration // time interval that requester engine tries requesting chunk data packs.
    48  	backoffMinInterval time.Duration // minimum time interval a chunk data pack request waits before dispatching.
    49  	backoffMaxInterval time.Duration // maximum time interval a chunk data pack request waits before dispatching.
    50  	backoffMultiplier  float64       // base of exponent in exponential backoff multiplier for backing off requests for chunk data packs.
    51  	requestTargets     uint64        // maximum number of execution nodes a chunk data pack request is dispatched to.
    52  
    53  	blockWorkers uint64 // number of blocks processed in parallel.
    54  	chunkWorkers uint64 // number of chunks processed in parallel.
    55  
    56  	stopAtHeight uint64 // height to stop the node on
    57  }
    58  
    59  type VerificationNodeBuilder struct {
    60  	*FlowNodeBuilder
    61  	verConf VerificationConfig
    62  }
    63  
    64  func NewVerificationNodeBuilder(nodeBuilder *FlowNodeBuilder) *VerificationNodeBuilder {
    65  	return &VerificationNodeBuilder{
    66  		FlowNodeBuilder: nodeBuilder,
    67  		verConf:         VerificationConfig{},
    68  	}
    69  }
    70  
    71  func (v *VerificationNodeBuilder) LoadFlags() {
    72  	v.FlowNodeBuilder.
    73  		ExtraFlags(func(flags *pflag.FlagSet) {
    74  			flags.UintVar(&v.verConf.chunkLimit, "chunk-limit", 10000, "maximum number of chunk states in the memory pool")
    75  			flags.UintVar(&v.verConf.chunkAlpha, "chunk-alpha", flow.DefaultChunkAssignmentAlpha, "number of verifiers should be assigned to each chunk")
    76  			flags.DurationVar(&v.verConf.requestInterval, "chunk-request-interval", requester.DefaultRequestInterval, "time interval chunk data pack request is processed")
    77  			flags.DurationVar(&v.verConf.backoffMinInterval, "backoff-min-interval", requester.DefaultBackoffMinInterval, "min time interval a chunk data pack request waits before dispatching")
    78  			flags.DurationVar(&v.verConf.backoffMaxInterval, "backoff-max-interval", requester.DefaultBackoffMaxInterval, "min time interval a chunk data pack request waits before dispatching")
    79  			flags.Float64Var(&v.verConf.backoffMultiplier, "backoff-multiplier", requester.DefaultBackoffMultiplier, "base of exponent in exponential backoff requesting mechanism")
    80  			flags.Uint64Var(&v.verConf.requestTargets, "request-targets", requester.DefaultRequestTargets, "maximum number of execution nodes a chunk data pack request is dispatched to")
    81  			flags.Uint64Var(&v.verConf.blockWorkers, "block-workers", blockconsumer.DefaultBlockWorkers, "maximum number of blocks being processed in parallel")
    82  			flags.Uint64Var(&v.verConf.chunkWorkers, "chunk-workers", chunkconsumer.DefaultChunkWorkers, "maximum number of execution nodes a chunk data pack request is dispatched to")
    83  			flags.Uint64Var(&v.verConf.stopAtHeight, "stop-at-height", 0, "height to stop the node at (0 to disable)")
    84  		})
    85  }
    86  
    87  func (v *VerificationNodeBuilder) LoadComponentsAndModules() {
    88  	var (
    89  		followerState protocol.FollowerState
    90  
    91  		chunkStatuses        *stdmap.ChunkStatuses    // used in fetcher engine
    92  		chunkRequests        *stdmap.ChunkRequests    // used in requester engine
    93  		processedChunkIndex  *badger.ConsumerProgress // used in chunk consumer
    94  		processedBlockHeight *badger.ConsumerProgress // used in block consumer
    95  		chunkQueue           *badger.ChunksQueue      // used in chunk consumer
    96  
    97  		syncCore            *chainsync.Core   // used in follower engine
    98  		assignerEngine      *assigner.Engine  // the assigner engine
    99  		fetcherEngine       *fetcher.Engine   // the fetcher engine
   100  		requesterEngine     *requester.Engine // the requester engine
   101  		verifierEng         *verifier.Engine  // the verifier engine
   102  		chunkConsumer       *chunkconsumer.ChunkConsumer
   103  		blockConsumer       *blockconsumer.BlockConsumer
   104  		followerDistributor *pubsub.FollowerDistributor
   105  
   106  		committee    *committees.Consensus
   107  		followerCore *hotstuff.FollowerLoop     // follower hotstuff logic
   108  		followerEng  *follower.ComplianceEngine // the follower engine
   109  		collector    module.VerificationMetrics // used to collect metrics of all engines
   110  	)
   111  
   112  	v.FlowNodeBuilder.
   113  		PreInit(DynamicStartPreInit).
   114  		Module("mutable follower state", func(node *NodeConfig) error {
   115  			var err error
   116  			// For now, we only support state implementations from package badger.
   117  			// If we ever support different implementations, the following can be replaced by a type-aware factory
   118  			state, ok := node.State.(*badgerState.State)
   119  			if !ok {
   120  				return fmt.Errorf("only implementations of type badger.State are currently supported but read-only state has type %T", node.State)
   121  			}
   122  			followerState, err = badgerState.NewFollowerState(
   123  				node.Logger,
   124  				node.Tracer,
   125  				node.ProtocolEvents,
   126  				state,
   127  				node.Storage.Index,
   128  				node.Storage.Payloads,
   129  				blocktimer.DefaultBlockTimer,
   130  			)
   131  			return err
   132  		}).
   133  		Module("verification metrics", func(node *NodeConfig) error {
   134  			collector = metrics.NewVerificationCollector(node.Tracer, node.MetricsRegisterer)
   135  			return nil
   136  		}).
   137  		Module("chunk status memory pool", func(node *NodeConfig) error {
   138  			var err error
   139  
   140  			chunkStatuses = stdmap.NewChunkStatuses(v.verConf.chunkLimit)
   141  			err = node.Metrics.Mempool.Register(metrics.ResourceChunkStatus, chunkStatuses.Size)
   142  			if err != nil {
   143  				return fmt.Errorf("could not register backend metric: %w", err)
   144  			}
   145  			return nil
   146  		}).
   147  		Module("chunk requests memory pool", func(node *NodeConfig) error {
   148  			var err error
   149  
   150  			chunkRequests = stdmap.NewChunkRequests(v.verConf.chunkLimit)
   151  			err = node.Metrics.Mempool.Register(metrics.ResourceChunkRequest, chunkRequests.Size)
   152  			if err != nil {
   153  				return fmt.Errorf("could not register backend metric: %w", err)
   154  			}
   155  			return nil
   156  		}).
   157  		Module("processed chunk index consumer progress", func(node *NodeConfig) error {
   158  			processedChunkIndex = badger.NewConsumerProgress(node.DB, module.ConsumeProgressVerificationChunkIndex)
   159  			return nil
   160  		}).
   161  		Module("processed block height consumer progress", func(node *NodeConfig) error {
   162  			processedBlockHeight = badger.NewConsumerProgress(node.DB, module.ConsumeProgressVerificationBlockHeight)
   163  			return nil
   164  		}).
   165  		Module("chunks queue", func(node *NodeConfig) error {
   166  			chunkQueue = badger.NewChunkQueue(node.DB)
   167  			ok, err := chunkQueue.Init(chunkconsumer.DefaultJobIndex)
   168  			if err != nil {
   169  				return fmt.Errorf("could not initialize default index in chunks queue: %w", err)
   170  			}
   171  
   172  			node.Logger.Info().
   173  				Str("component", "node-builder").
   174  				Bool("init_to_default", ok).
   175  				Msg("chunks queue index has been initialized")
   176  
   177  			return nil
   178  		}).
   179  		Module("follower distributor", func(node *NodeConfig) error {
   180  			followerDistributor = pubsub.NewFollowerDistributor()
   181  			followerDistributor.AddProposalViolationConsumer(notifications.NewSlashingViolationsConsumer(node.Logger))
   182  			return nil
   183  		}).
   184  		Module("sync core", func(node *NodeConfig) error {
   185  			var err error
   186  
   187  			syncCore, err = chainsync.New(node.Logger, node.SyncCoreConfig, metrics.NewChainSyncCollector(node.RootChainID), node.RootChainID)
   188  			return err
   189  		}).
   190  		Component("verifier engine", func(node *NodeConfig) (module.ReadyDoneAware, error) {
   191  			var err error
   192  
   193  			vm := fvm.NewVirtualMachine()
   194  			fvmOptions := append(
   195  				[]fvm.Option{fvm.WithLogger(node.Logger)},
   196  				node.FvmOptions...,
   197  			)
   198  
   199  			// TODO(JanezP): cleanup creation of fvm context github.com/onflow/flow-go/issues/5249
   200  			fvmOptions = append(fvmOptions, computation.DefaultFVMOptions(node.RootChainID, false, false)...)
   201  			vmCtx := fvm.NewContext(fvmOptions...)
   202  
   203  			chunkVerifier := chunks.NewChunkVerifier(vm, vmCtx, node.Logger)
   204  			approvalStorage := badger.NewResultApprovals(node.Metrics.Cache, node.DB)
   205  			verifierEng, err = verifier.New(
   206  				node.Logger,
   207  				collector,
   208  				node.Tracer,
   209  				node.EngineRegistry,
   210  				node.State,
   211  				node.Me,
   212  				chunkVerifier,
   213  				approvalStorage)
   214  			return verifierEng, err
   215  		}).
   216  		Component("chunk consumer, requester, and fetcher engines", func(node *NodeConfig) (module.ReadyDoneAware, error) {
   217  			var err error
   218  
   219  			requesterEngine, err = requester.New(
   220  				node.Logger,
   221  				node.State,
   222  				node.EngineRegistry,
   223  				node.Tracer,
   224  				collector,
   225  				chunkRequests,
   226  				v.verConf.requestInterval,
   227  				requester.RetryAfterQualifier,
   228  				mempool.ExponentialUpdater(
   229  					v.verConf.backoffMultiplier,
   230  					v.verConf.backoffMaxInterval,
   231  					v.verConf.backoffMinInterval,
   232  				),
   233  				v.verConf.requestTargets)
   234  			if err != nil {
   235  				return nil, fmt.Errorf("could not create requester engine: %w", err)
   236  			}
   237  
   238  			fetcherEngine = fetcher.New(
   239  				node.Logger,
   240  				collector,
   241  				node.Tracer,
   242  				verifierEng,
   243  				node.State,
   244  				chunkStatuses,
   245  				node.Storage.Headers,
   246  				node.Storage.Blocks,
   247  				node.Storage.Results,
   248  				node.Storage.Receipts,
   249  				requesterEngine,
   250  				v.verConf.stopAtHeight)
   251  
   252  			// requester and fetcher engines are started by chunk consumer
   253  			chunkConsumer, err = chunkconsumer.NewChunkConsumer(
   254  				node.Logger,
   255  				collector,
   256  				processedChunkIndex,
   257  				chunkQueue,
   258  				fetcherEngine,
   259  				v.verConf.chunkWorkers)
   260  
   261  			if err != nil {
   262  				return nil, fmt.Errorf("could not create chunk consumer: %w", err)
   263  			}
   264  
   265  			err = node.Metrics.Mempool.Register(metrics.ResourceChunkConsumer, chunkConsumer.Size)
   266  			if err != nil {
   267  				return nil, fmt.Errorf("could not register backend metric: %w", err)
   268  			}
   269  
   270  			return chunkConsumer, nil
   271  		}).
   272  		Component("assigner engine", func(node *NodeConfig) (module.ReadyDoneAware, error) {
   273  			var chunkAssigner module.ChunkAssigner
   274  			var err error
   275  
   276  			chunkAssigner, err = chunks.NewChunkAssigner(v.verConf.chunkAlpha, node.State)
   277  			if err != nil {
   278  				return nil, fmt.Errorf("could not initialize chunk assigner: %w", err)
   279  			}
   280  
   281  			assignerEngine = assigner.New(
   282  				node.Logger,
   283  				collector,
   284  				node.Tracer,
   285  				node.Me,
   286  				node.State,
   287  				chunkAssigner,
   288  				chunkQueue,
   289  				chunkConsumer,
   290  				v.verConf.stopAtHeight)
   291  
   292  			return assignerEngine, nil
   293  		}).
   294  		Component("block consumer", func(node *NodeConfig) (module.ReadyDoneAware, error) {
   295  			var initBlockHeight uint64
   296  			var err error
   297  
   298  			blockConsumer, initBlockHeight, err = blockconsumer.NewBlockConsumer(
   299  				node.Logger,
   300  				collector,
   301  				processedBlockHeight,
   302  				node.Storage.Blocks,
   303  				node.State,
   304  				assignerEngine,
   305  				v.verConf.blockWorkers)
   306  
   307  			if err != nil {
   308  				return nil, fmt.Errorf("could not initialize block consumer: %w", err)
   309  			}
   310  
   311  			err = node.Metrics.Mempool.Register(metrics.ResourceBlockConsumer, blockConsumer.Size)
   312  			if err != nil {
   313  				return nil, fmt.Errorf("could not register backend metric: %w", err)
   314  			}
   315  
   316  			node.Logger.Info().
   317  				Str("component", "node-builder").
   318  				Uint64("init_height", initBlockHeight).
   319  				Msg("block consumer initialized")
   320  
   321  			return blockConsumer, nil
   322  		}).
   323  		Component("consensus committee", func(node *NodeConfig) (module.ReadyDoneAware, error) {
   324  			// initialize consensus committee's membership state
   325  			// This committee state is for the HotStuff follower, which follows the MAIN CONSENSUS Committee
   326  			// Note: node.Me.NodeID() is not part of the consensus committee
   327  			var err error
   328  			committee, err = committees.NewConsensusCommittee(node.State, node.Me.NodeID())
   329  			node.ProtocolEvents.AddConsumer(committee)
   330  			return committee, err
   331  		}).
   332  		Component("follower core", func(node *NodeConfig) (module.ReadyDoneAware, error) {
   333  			// create a finalizer that handles updating the protocol
   334  			// state when the follower detects newly finalized blocks
   335  			final := finalizer.NewFinalizer(node.DB, node.Storage.Headers, followerState, node.Tracer)
   336  
   337  			finalized, pending, err := recoveryprotocol.FindLatest(node.State, node.Storage.Headers)
   338  			if err != nil {
   339  				return nil, fmt.Errorf("could not find latest finalized block and pending blocks to recover consensus follower: %w", err)
   340  			}
   341  
   342  			followerDistributor.AddOnBlockFinalizedConsumer(blockConsumer.OnFinalizedBlock)
   343  
   344  			// creates a consensus follower with ingestEngine as the notifier
   345  			// so that it gets notified upon each new finalized block
   346  			followerCore, err = flowconsensus.NewFollower(
   347  				node.Logger,
   348  				node.Metrics.Mempool,
   349  				node.Storage.Headers,
   350  				final,
   351  				followerDistributor,
   352  				node.FinalizedRootBlock.Header,
   353  				node.RootQC,
   354  				finalized,
   355  				pending,
   356  			)
   357  			if err != nil {
   358  				return nil, fmt.Errorf("could not create follower core logic: %w", err)
   359  			}
   360  
   361  			return followerCore, nil
   362  		}).
   363  		Component("follower engine", func(node *NodeConfig) (module.ReadyDoneAware, error) {
   364  			packer := hotsignature.NewConsensusSigDataPacker(committee)
   365  			// initialize the verifier for the protocol consensus
   366  			verifier := verification.NewCombinedVerifier(committee, packer)
   367  			validator := validator.New(committee, verifier)
   368  
   369  			var heroCacheCollector module.HeroCacheMetrics = metrics.NewNoopCollector()
   370  			if node.HeroCacheMetricsEnable {
   371  				heroCacheCollector = metrics.FollowerCacheMetrics(node.MetricsRegisterer)
   372  			}
   373  
   374  			core, err := followereng.NewComplianceCore(
   375  				node.Logger,
   376  				node.Metrics.Mempool,
   377  				heroCacheCollector,
   378  				followerDistributor,
   379  				followerState,
   380  				followerCore,
   381  				validator,
   382  				syncCore,
   383  				node.Tracer,
   384  			)
   385  			if err != nil {
   386  				return nil, fmt.Errorf("could not create follower core: %w", err)
   387  			}
   388  
   389  			followerEng, err = followereng.NewComplianceLayer(
   390  				node.Logger,
   391  				node.EngineRegistry,
   392  				node.Me,
   393  				node.Metrics.Engine,
   394  				node.Storage.Headers,
   395  				node.LastFinalizedHeader,
   396  				core,
   397  				node.ComplianceConfig,
   398  			)
   399  			if err != nil {
   400  				return nil, fmt.Errorf("could not create follower engine: %w", err)
   401  			}
   402  			followerDistributor.AddOnBlockFinalizedConsumer(followerEng.OnFinalizedBlock)
   403  
   404  			return followerEng, nil
   405  		}).
   406  		Component("sync engine", func(node *NodeConfig) (module.ReadyDoneAware, error) {
   407  			spamConfig, err := commonsync.NewSpamDetectionConfig()
   408  			if err != nil {
   409  				return nil, fmt.Errorf("could not initialize spam detection config: %w", err)
   410  			}
   411  
   412  			sync, err := commonsync.New(
   413  				node.Logger,
   414  				node.Metrics.Engine,
   415  				node.EngineRegistry,
   416  				node.Me,
   417  				node.State,
   418  				node.Storage.Blocks,
   419  				followerEng,
   420  				syncCore,
   421  				node.SyncEngineIdentifierProvider,
   422  				spamConfig,
   423  			)
   424  			if err != nil {
   425  				return nil, fmt.Errorf("could not create synchronization engine: %w", err)
   426  			}
   427  			followerDistributor.AddFinalizationConsumer(sync)
   428  
   429  			return sync, nil
   430  		})
   431  }