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 }