github.com/koko1123/flow-go-1@v0.29.6/cmd/collection/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/spf13/pflag" 8 9 client "github.com/onflow/flow-go-sdk/access/grpc" 10 "github.com/koko1123/flow-go-1/module/mempool/queue" 11 12 sdkcrypto "github.com/onflow/flow-go-sdk/crypto" 13 "github.com/koko1123/flow-go-1/cmd" 14 "github.com/koko1123/flow-go-1/cmd/util/cmd/common" 15 "github.com/koko1123/flow-go-1/consensus" 16 "github.com/koko1123/flow-go-1/consensus/hotstuff/committees" 17 "github.com/koko1123/flow-go-1/consensus/hotstuff/notifications/pubsub" 18 "github.com/koko1123/flow-go-1/consensus/hotstuff/pacemaker/timeout" 19 hotsignature "github.com/koko1123/flow-go-1/consensus/hotstuff/signature" 20 "github.com/koko1123/flow-go-1/consensus/hotstuff/verification" 21 recovery "github.com/koko1123/flow-go-1/consensus/recovery/protocol" 22 "github.com/koko1123/flow-go-1/engine/collection/epochmgr" 23 "github.com/koko1123/flow-go-1/engine/collection/epochmgr/factories" 24 "github.com/koko1123/flow-go-1/engine/collection/ingest" 25 "github.com/koko1123/flow-go-1/engine/collection/pusher" 26 "github.com/koko1123/flow-go-1/engine/collection/rpc" 27 followereng "github.com/koko1123/flow-go-1/engine/common/follower" 28 "github.com/koko1123/flow-go-1/engine/common/provider" 29 consync "github.com/koko1123/flow-go-1/engine/common/synchronization" 30 "github.com/koko1123/flow-go-1/fvm/systemcontracts" 31 "github.com/koko1123/flow-go-1/model/bootstrap" 32 "github.com/koko1123/flow-go-1/model/flow" 33 "github.com/koko1123/flow-go-1/model/flow/filter" 34 "github.com/koko1123/flow-go-1/module" 35 "github.com/koko1123/flow-go-1/module/buffer" 36 builder "github.com/koko1123/flow-go-1/module/builder/collection" 37 "github.com/koko1123/flow-go-1/module/chainsync" 38 modulecompliance "github.com/koko1123/flow-go-1/module/compliance" 39 "github.com/koko1123/flow-go-1/module/epochs" 40 confinalizer "github.com/koko1123/flow-go-1/module/finalizer/consensus" 41 "github.com/koko1123/flow-go-1/module/mempool" 42 epochpool "github.com/koko1123/flow-go-1/module/mempool/epochs" 43 "github.com/koko1123/flow-go-1/module/mempool/herocache" 44 "github.com/koko1123/flow-go-1/module/metrics" 45 "github.com/koko1123/flow-go-1/network/channels" 46 "github.com/koko1123/flow-go-1/state/protocol" 47 badgerState "github.com/koko1123/flow-go-1/state/protocol/badger" 48 "github.com/koko1123/flow-go-1/state/protocol/blocktimer" 49 "github.com/koko1123/flow-go-1/state/protocol/events/gadgets" 50 storagekv "github.com/koko1123/flow-go-1/storage/badger" 51 ) 52 53 func main() { 54 55 var ( 56 txLimit uint 57 maxCollectionSize uint 58 maxCollectionByteSize uint64 59 maxCollectionTotalGas uint64 60 maxCollectionRequestCacheSize uint32 // collection provider engine 61 collectionProviderWorkers uint // collection provider engine 62 builderExpiryBuffer uint 63 builderPayerRateLimitDryRun bool 64 builderPayerRateLimit float64 65 builderUnlimitedPayers []string 66 hotstuffTimeout time.Duration 67 hotstuffMinTimeout time.Duration 68 hotstuffTimeoutIncreaseFactor float64 69 hotstuffTimeoutDecreaseFactor float64 70 hotstuffTimeoutVoteAggregationFraction float64 71 blockRateDelay time.Duration 72 startupTimeString string 73 startupTime time.Time 74 75 followerState protocol.MutableState 76 ingestConf = ingest.DefaultConfig() 77 rpcConf rpc.Config 78 clusterComplianceConfig modulecompliance.Config 79 80 pools *epochpool.TransactionPools // epoch-scoped transaction pools 81 followerBuffer *buffer.PendingBlocks // pending block cache for follower 82 finalizationDistributor *pubsub.FinalizationDistributor 83 finalizedHeader *consync.FinalizedHeaderCache 84 85 push *pusher.Engine 86 ing *ingest.Engine 87 mainChainSyncCore *chainsync.Core 88 followerEng *followereng.Engine 89 colMetrics module.CollectionMetrics 90 err error 91 92 // epoch qc contract client 93 machineAccountInfo *bootstrap.NodeMachineAccountInfo 94 flowClientConfigs []*common.FlowClientConfig 95 insecureAccessAPI bool 96 accessNodeIDS []string 97 apiRatelimits map[string]int 98 apiBurstlimits map[string]int 99 ) 100 101 nodeBuilder := cmd.FlowNode(flow.RoleCollection.String()) 102 nodeBuilder.ExtraFlags(func(flags *pflag.FlagSet) { 103 flags.UintVar(&txLimit, "tx-limit", 50_000, 104 "maximum number of transactions in the memory pool") 105 flags.StringVarP(&rpcConf.ListenAddr, "ingress-addr", "i", "localhost:9000", 106 "the address the ingress server listens on") 107 flags.BoolVar(&rpcConf.RpcMetricsEnabled, "rpc-metrics-enabled", false, 108 "whether to enable the rpc metrics") 109 flags.Uint64Var(&ingestConf.MaxGasLimit, "ingest-max-gas-limit", flow.DefaultMaxTransactionGasLimit, 110 "maximum per-transaction computation limit (gas limit)") 111 flags.Uint64Var(&ingestConf.MaxTransactionByteSize, "ingest-max-tx-byte-size", flow.DefaultMaxTransactionByteSize, 112 "maximum per-transaction byte size") 113 flags.Uint64Var(&ingestConf.MaxCollectionByteSize, "ingest-max-col-byte-size", flow.DefaultMaxCollectionByteSize, 114 "maximum per-collection byte size") 115 flags.BoolVar(&ingestConf.CheckScriptsParse, "ingest-check-scripts-parse", true, 116 "whether we check that inbound transactions are parse-able") 117 flags.UintVar(&ingestConf.ExpiryBuffer, "ingest-expiry-buffer", 30, 118 "expiry buffer for inbound transactions") 119 flags.UintVar(&ingestConf.PropagationRedundancy, "ingest-tx-propagation-redundancy", 10, 120 "how many additional cluster members we propagate transactions to") 121 flags.UintVar(&builderExpiryBuffer, "builder-expiry-buffer", builder.DefaultExpiryBuffer, 122 "expiry buffer for transactions in proposed collections") 123 flags.BoolVar(&builderPayerRateLimitDryRun, "builder-rate-limit-dry-run", false, 124 "determines whether rate limit configuration should be enforced (false), or only logged (true)") 125 flags.Float64Var(&builderPayerRateLimit, "builder-rate-limit", builder.DefaultMaxPayerTransactionRate, // no rate limiting 126 "rate limit for each payer (transactions/collection)") 127 flags.StringSliceVar(&builderUnlimitedPayers, "builder-unlimited-payers", []string{}, // no unlimited payers 128 "set of payer addresses which are omitted from rate limiting") 129 flags.UintVar(&maxCollectionSize, "builder-max-collection-size", flow.DefaultMaxCollectionSize, 130 "maximum number of transactions in proposed collections") 131 flags.Uint64Var(&maxCollectionByteSize, "builder-max-collection-byte-size", flow.DefaultMaxCollectionByteSize, 132 "maximum byte size of the proposed collection") 133 flags.Uint64Var(&maxCollectionTotalGas, "builder-max-collection-total-gas", flow.DefaultMaxCollectionTotalGas, 134 "maximum total amount of maxgas of transactions in proposed collections") 135 flags.DurationVar(&hotstuffTimeout, "hotstuff-timeout", 60*time.Second, 136 "the initial timeout for the hotstuff pacemaker") 137 flags.DurationVar(&hotstuffMinTimeout, "hotstuff-min-timeout", 2500*time.Millisecond, 138 "the lower timeout bound for the hotstuff pacemaker") 139 flags.Float64Var(&hotstuffTimeoutIncreaseFactor, "hotstuff-timeout-increase-factor", 140 timeout.DefaultConfig.TimeoutIncrease, 141 "multiplicative increase of timeout value in case of time out event") 142 flags.Float64Var(&hotstuffTimeoutDecreaseFactor, "hotstuff-timeout-decrease-factor", 143 timeout.DefaultConfig.TimeoutDecrease, 144 "multiplicative decrease of timeout value in case of progress") 145 flags.Float64Var(&hotstuffTimeoutVoteAggregationFraction, "hotstuff-timeout-vote-aggregation-fraction", 146 timeout.DefaultConfig.VoteAggregationTimeoutFraction, 147 "additional fraction of replica timeout that the primary will wait for votes") 148 flags.DurationVar(&blockRateDelay, "block-rate-delay", 250*time.Millisecond, 149 "the delay to broadcast block proposal in order to control block production rate") 150 flags.Uint64Var(&clusterComplianceConfig.SkipNewProposalsThreshold, 151 "cluster-compliance-skip-proposals-threshold", modulecompliance.DefaultConfig().SkipNewProposalsThreshold, "threshold at which new proposals are discarded rather than cached, if their height is this much above local finalized height (cluster compliance engine)") 152 flags.StringVar(&startupTimeString, "hotstuff-startup-time", cmd.NotSet, "specifies date and time (in ISO 8601 format) after which the consensus participant may enter the first view (e.g (e.g 1996-04-24T15:04:05-07:00))") 153 flags.Uint32Var(&maxCollectionRequestCacheSize, "max-collection-provider-cache-size", provider.DefaultEntityRequestCacheSize, "maximum number of collection requests to cache for collection provider") 154 flags.UintVar(&collectionProviderWorkers, "collection-provider-workers", provider.DefaultRequestProviderWorkers, "number of workers to use for collection provider") 155 // epoch qc contract flags 156 flags.BoolVar(&insecureAccessAPI, "insecure-access-api", false, "required if insecure GRPC connection should be used") 157 flags.StringSliceVar(&accessNodeIDS, "access-node-ids", []string{}, fmt.Sprintf("array of access node IDs sorted in priority order where the first ID in this array will get the first connection attempt and each subsequent ID after serves as a fallback. Minimum length %d. Use '*' for all IDs in protocol state.", common.DefaultAccessNodeIDSMinimum)) 158 flags.StringToIntVar(&apiRatelimits, "api-rate-limits", map[string]int{}, "per second rate limits for GRPC API methods e.g. Ping=300,SendTransaction=500 etc. note limits apply globally to all clients.") 159 flags.StringToIntVar(&apiBurstlimits, "api-burst-limits", map[string]int{}, "burst limits for gRPC API methods e.g. Ping=100,SendTransaction=100 etc. note limits apply globally to all clients.") 160 161 }).ValidateFlags(func() error { 162 if startupTimeString != cmd.NotSet { 163 t, err := time.Parse(time.RFC3339, startupTimeString) 164 if err != nil { 165 return fmt.Errorf("invalid start-time value: %w", err) 166 } 167 startupTime = t 168 } 169 return nil 170 }) 171 172 if err = nodeBuilder.Initialize(); err != nil { 173 nodeBuilder.Logger.Fatal().Err(err).Send() 174 } 175 176 nodeBuilder. 177 PreInit(cmd.DynamicStartPreInit). 178 Module("mutable follower state", func(node *cmd.NodeConfig) error { 179 // For now, we only support state implementations from package badger. 180 // If we ever support different implementations, the following can be replaced by a type-aware factory 181 state, ok := node.State.(*badgerState.State) 182 if !ok { 183 return fmt.Errorf("only implementations of type badger.State are currently supported but read-only state has type %T", node.State) 184 } 185 followerState, err = badgerState.NewFollowerState( 186 state, 187 node.Storage.Index, 188 node.Storage.Payloads, 189 node.Tracer, 190 node.ProtocolEvents, 191 blocktimer.DefaultBlockTimer, 192 ) 193 return err 194 }). 195 Module("transactions mempool", func(node *cmd.NodeConfig) error { 196 create := func(epoch uint64) mempool.Transactions { 197 var heroCacheMetricsCollector module.HeroCacheMetrics = metrics.NewNoopCollector() 198 if node.BaseConfig.HeroCacheMetricsEnable { 199 heroCacheMetricsCollector = metrics.CollectionNodeTransactionsCacheMetrics(node.MetricsRegisterer, epoch) 200 } 201 return herocache.NewTransactions( 202 uint32(txLimit), 203 node.Logger, 204 heroCacheMetricsCollector) 205 } 206 207 pools = epochpool.NewTransactionPools(create) 208 err := node.Metrics.Mempool.Register(metrics.ResourceTransaction, pools.CombinedSize) 209 return err 210 }). 211 Module("pending block cache", func(node *cmd.NodeConfig) error { 212 followerBuffer = buffer.NewPendingBlocks() 213 return nil 214 }). 215 Module("metrics", func(node *cmd.NodeConfig) error { 216 colMetrics = metrics.NewCollectionCollector(node.Tracer) 217 return nil 218 }). 219 Module("main chain sync core", func(node *cmd.NodeConfig) error { 220 mainChainSyncCore, err = chainsync.New(node.Logger, node.SyncCoreConfig, metrics.NewChainSyncCollector()) 221 return err 222 }). 223 Module("machine account config", func(node *cmd.NodeConfig) error { 224 machineAccountInfo, err = cmd.LoadNodeMachineAccountInfoFile(node.BootstrapDir, node.NodeID) 225 return err 226 }). 227 Module("sdk client connection options", func(node *cmd.NodeConfig) error { 228 anIDS, err := common.ValidateAccessNodeIDSFlag(accessNodeIDS, node.RootChainID, node.State.Sealed()) 229 if err != nil { 230 return fmt.Errorf("failed to validate flag --access-node-ids %w", err) 231 } 232 233 flowClientConfigs, err = common.FlowClientConfigs(anIDS, insecureAccessAPI, node.State.Sealed()) 234 if err != nil { 235 return fmt.Errorf("failed to prepare flow client connection configs for each access node id %w", err) 236 } 237 238 return nil 239 }). 240 Component("machine account config validator", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 241 //@TODO use fallback logic for flowClient similar to DKG/QC contract clients 242 flowClient, err := common.FlowClient(flowClientConfigs[0]) 243 if err != nil { 244 return nil, fmt.Errorf("failed to get flow client connection option for access node (0): %s %w", flowClientConfigs[0].AccessAddress, err) 245 } 246 247 // disable balance checks for transient networks, which do not have transaction fees 248 var opts []epochs.MachineAccountValidatorConfigOption 249 if node.RootChainID.Transient() { 250 opts = append(opts, epochs.WithoutBalanceChecks) 251 } 252 validator, err := epochs.NewMachineAccountConfigValidator( 253 node.Logger, 254 flowClient, 255 flow.RoleCollection, 256 *machineAccountInfo, 257 opts..., 258 ) 259 260 return validator, err 261 }). 262 Component("follower engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 263 264 // initialize cleaner for DB 265 cleaner := storagekv.NewCleaner(node.Logger, node.DB, node.Metrics.CleanCollector, flow.DefaultValueLogGCFrequency) 266 267 // create a finalizer that will handling updating the protocol 268 // state when the follower detects newly finalized blocks 269 finalizer := confinalizer.NewFinalizer(node.DB, node.Storage.Headers, followerState, node.Tracer) 270 271 // initialize consensus committee's membership state 272 // This committee state is for the HotStuff follower, which follows the MAIN CONSENSUS Committee 273 // Note: node.Me.NodeID() is not part of the consensus committee 274 mainConsensusCommittee, err := committees.NewConsensusCommittee(node.State, node.Me.NodeID()) 275 if err != nil { 276 return nil, fmt.Errorf("could not create Committee state for main consensus: %w", err) 277 } 278 279 packer := hotsignature.NewConsensusSigDataPacker(mainConsensusCommittee) 280 // initialize the verifier for the protocol consensus 281 verifier := verification.NewCombinedVerifier(mainConsensusCommittee, packer) 282 283 finalizationDistributor = pubsub.NewFinalizationDistributor() 284 285 finalized, pending, err := recovery.FindLatest(node.State, node.Storage.Headers) 286 if err != nil { 287 return nil, fmt.Errorf("could not find latest finalized block and pending blocks to recover consensus follower: %w", err) 288 } 289 290 // creates a consensus follower with noop consumer as the notifier 291 followerCore, err := consensus.NewFollower( 292 node.Logger, 293 mainConsensusCommittee, 294 node.Storage.Headers, 295 finalizer, 296 verifier, 297 finalizationDistributor, 298 node.RootBlock.Header, 299 node.RootQC, 300 finalized, 301 pending, 302 ) 303 if err != nil { 304 return nil, fmt.Errorf("could not create follower core logic: %w", err) 305 } 306 307 followerEng, err = followereng.New( 308 node.Logger, 309 node.Network, 310 node.Me, 311 node.Metrics.Engine, 312 node.Metrics.Mempool, 313 cleaner, 314 node.Storage.Headers, 315 node.Storage.Payloads, 316 followerState, 317 followerBuffer, 318 followerCore, 319 mainChainSyncCore, 320 node.Tracer, 321 followereng.WithComplianceOptions(modulecompliance.WithSkipNewProposalsThreshold(node.ComplianceConfig.SkipNewProposalsThreshold)), 322 ) 323 if err != nil { 324 return nil, fmt.Errorf("could not create follower engine: %w", err) 325 } 326 327 return followerEng, nil 328 }). 329 Component("finalized snapshot", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 330 finalizedHeader, err = consync.NewFinalizedHeaderCache(node.Logger, node.State, finalizationDistributor) 331 if err != nil { 332 return nil, fmt.Errorf("could not create finalized snapshot cache: %w", err) 333 } 334 335 return finalizedHeader, nil 336 }). 337 Component("main chain sync engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 338 339 // create a block synchronization engine to handle follower getting out of sync 340 sync, err := consync.New( 341 node.Logger, 342 node.Metrics.Engine, 343 node.Network, 344 node.Me, 345 node.Storage.Blocks, 346 followerEng, 347 mainChainSyncCore, 348 finalizedHeader, 349 node.SyncEngineIdentifierProvider, 350 ) 351 if err != nil { 352 return nil, fmt.Errorf("could not create synchronization engine: %w", err) 353 } 354 355 return sync, nil 356 }). 357 Component("ingestion engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 358 ing, err = ingest.New( 359 node.Logger, 360 node.Network, 361 node.State, 362 node.Metrics.Engine, 363 node.Metrics.Mempool, 364 colMetrics, 365 node.Me, 366 node.RootChainID.Chain(), 367 pools, 368 ingestConf, 369 ) 370 return ing, err 371 }). 372 Component("transaction ingress rpc server", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 373 server := rpc.New( 374 rpcConf, 375 ing, 376 node.Logger, 377 node.RootChainID, 378 apiRatelimits, 379 apiBurstlimits, 380 ) 381 return server, nil 382 }). 383 Component("collection provider engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 384 retrieve := func(collID flow.Identifier) (flow.Entity, error) { 385 coll, err := node.Storage.Collections.ByID(collID) 386 return coll, err 387 } 388 389 var collectionRequestMetrics module.HeroCacheMetrics = metrics.NewNoopCollector() 390 if node.HeroCacheMetricsEnable { 391 collectionRequestMetrics = metrics.CollectionRequestsQueueMetricFactory(node.MetricsRegisterer) 392 } 393 collectionRequestQueue := queue.NewHeroStore(maxCollectionRequestCacheSize, node.Logger, collectionRequestMetrics) 394 395 return provider.New( 396 node.Logger, 397 node.Metrics.Engine, 398 node.Network, 399 node.Me, 400 node.State, 401 collectionRequestQueue, 402 collectionProviderWorkers, 403 channels.ProvideCollections, 404 filter.HasRole(flow.RoleAccess, flow.RoleExecution), 405 retrieve, 406 ) 407 }). 408 Component("pusher engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 409 push, err = pusher.New( 410 node.Logger, 411 node.Network, 412 node.State, 413 node.Metrics.Engine, 414 colMetrics, 415 node.Me, 416 node.Storage.Collections, 417 node.Storage.Transactions, 418 ) 419 return push, err 420 }). 421 // Epoch manager encapsulates and manages epoch-dependent engines as we 422 // transition between epochs 423 Component("epoch manager", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 424 clusterStateFactory, err := factories.NewClusterStateFactory(node.DB, node.Metrics.Cache, node.Tracer) 425 if err != nil { 426 return nil, err 427 } 428 429 // convert hex string flag values to addresses 430 unlimitedPayers := make([]flow.Address, 0, len(builderUnlimitedPayers)) 431 for _, payerStr := range builderUnlimitedPayers { 432 payerAddr := flow.HexToAddress(payerStr) 433 unlimitedPayers = append(unlimitedPayers, payerAddr) 434 } 435 436 builderFactory, err := factories.NewBuilderFactory( 437 node.DB, 438 node.Storage.Headers, 439 node.Tracer, 440 colMetrics, 441 push, 442 node.Logger, 443 builder.WithMaxCollectionSize(maxCollectionSize), 444 builder.WithMaxCollectionByteSize(maxCollectionByteSize), 445 builder.WithMaxCollectionTotalGas(maxCollectionTotalGas), 446 builder.WithExpiryBuffer(builderExpiryBuffer), 447 builder.WithRateLimitDryRun(builderPayerRateLimitDryRun), 448 builder.WithMaxPayerTransactionRate(builderPayerRateLimit), 449 builder.WithUnlimitedPayers(unlimitedPayers...), 450 ) 451 if err != nil { 452 return nil, err 453 } 454 455 proposalFactory, err := factories.NewProposalEngineFactory( 456 node.Logger, 457 node.Network, 458 node.Me, 459 colMetrics, 460 node.Metrics.Engine, 461 node.Metrics.Mempool, 462 node.State, 463 node.Storage.Transactions, 464 modulecompliance.WithSkipNewProposalsThreshold(clusterComplianceConfig.SkipNewProposalsThreshold), 465 ) 466 if err != nil { 467 return nil, err 468 } 469 470 syncFactory, err := factories.NewSyncEngineFactory( 471 node.Logger, 472 node.Metrics.Engine, 473 node.Network, 474 node.Me, 475 node.SyncCoreConfig, 476 ) 477 if err != nil { 478 return nil, err 479 } 480 481 createMetrics := func(chainID flow.ChainID) module.HotstuffMetrics { 482 return metrics.NewHotstuffCollector(chainID) 483 } 484 485 opts := []consensus.Option{ 486 consensus.WithBlockRateDelay(blockRateDelay), 487 consensus.WithInitialTimeout(hotstuffTimeout), 488 consensus.WithMinTimeout(hotstuffMinTimeout), 489 consensus.WithVoteAggregationTimeoutFraction(hotstuffTimeoutVoteAggregationFraction), 490 consensus.WithTimeoutIncreaseFactor(hotstuffTimeoutIncreaseFactor), 491 consensus.WithTimeoutDecreaseFactor(hotstuffTimeoutDecreaseFactor), 492 } 493 494 if !startupTime.IsZero() { 495 opts = append(opts, consensus.WithStartupTime(startupTime)) 496 } 497 498 hotstuffFactory, err := factories.NewHotStuffFactory( 499 node.Logger, 500 node.Me, 501 node.DB, 502 node.State, 503 createMetrics, 504 opts..., 505 ) 506 if err != nil { 507 return nil, err 508 } 509 510 signer := verification.NewStakingSigner(node.Me) 511 512 // construct QC contract client 513 qcContractClients, err := createQCContractClients(node, machineAccountInfo, flowClientConfigs) 514 if err != nil { 515 return nil, fmt.Errorf("could not create qc contract clients %w", err) 516 } 517 518 rootQCVoter := epochs.NewRootQCVoter( 519 node.Logger, 520 node.Me, 521 signer, 522 node.State, 523 qcContractClients, 524 ) 525 526 factory := factories.NewEpochComponentsFactory( 527 node.Me, 528 pools, 529 builderFactory, 530 clusterStateFactory, 531 hotstuffFactory, 532 proposalFactory, 533 syncFactory, 534 ) 535 536 heightEvents := gadgets.NewHeights() 537 node.ProtocolEvents.AddConsumer(heightEvents) 538 539 manager, err := epochmgr.New( 540 node.Logger, 541 node.Me, 542 node.State, 543 pools, 544 rootQCVoter, 545 factory, 546 heightEvents, 547 ) 548 if err != nil { 549 return nil, fmt.Errorf("could not create epoch manager: %w", err) 550 } 551 552 // register the manager for protocol events 553 node.ProtocolEvents.AddConsumer(manager) 554 555 return manager, err 556 }) 557 558 node, err := nodeBuilder.Build() 559 if err != nil { 560 nodeBuilder.Logger.Fatal().Err(err).Send() 561 } 562 node.Run() 563 } 564 565 // createQCContractClient creates QC contract client 566 func createQCContractClient(node *cmd.NodeConfig, machineAccountInfo *bootstrap.NodeMachineAccountInfo, flowClient *client.Client, anID flow.Identifier) (module.QCContractClient, error) { 567 568 var qcContractClient module.QCContractClient 569 570 contracts, err := systemcontracts.SystemContractsForChain(node.RootChainID) 571 if err != nil { 572 return nil, err 573 } 574 qcContractAddress := contracts.ClusterQC.Address.Hex() 575 576 // construct signer from private key 577 sk, err := sdkcrypto.DecodePrivateKey(machineAccountInfo.SigningAlgorithm, machineAccountInfo.EncodedPrivateKey) 578 if err != nil { 579 return nil, fmt.Errorf("could not decode private key from hex: %w", err) 580 } 581 582 txSigner, err := sdkcrypto.NewInMemorySigner(sk, machineAccountInfo.HashAlgorithm) 583 if err != nil { 584 return nil, fmt.Errorf("could not create in-memory signer: %w", err) 585 } 586 587 // create actual qc contract client, all flags and machine account info file found 588 qcContractClient = epochs.NewQCContractClient(node.Logger, flowClient, anID, node.Me.NodeID(), machineAccountInfo.Address, machineAccountInfo.KeyIndex, qcContractAddress, txSigner) 589 590 return qcContractClient, nil 591 } 592 593 // createQCContractClients creates priority ordered array of QCContractClient 594 func createQCContractClients(node *cmd.NodeConfig, machineAccountInfo *bootstrap.NodeMachineAccountInfo, flowClientOpts []*common.FlowClientConfig) ([]module.QCContractClient, error) { 595 qcClients := make([]module.QCContractClient, 0) 596 597 for _, opt := range flowClientOpts { 598 flowClient, err := common.FlowClient(opt) 599 if err != nil { 600 return nil, fmt.Errorf("failed to create flow client for qc contract client with options: %s %w", flowClientOpts, err) 601 } 602 603 qcClient, err := createQCContractClient(node, machineAccountInfo, flowClient, opt.AccessNodeID) 604 if err != nil { 605 return nil, fmt.Errorf("failed to create qc contract client with flow client options: %s %w", flowClientOpts, err) 606 } 607 608 qcClients = append(qcClients, qcClient) 609 } 610 return qcClients, nil 611 }