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 }