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