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