github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/access/node_builder/access_node_builder.go (about) 1 package node_builder 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math" 8 "os" 9 "path" 10 "path/filepath" 11 "strings" 12 "time" 13 14 "github.com/ipfs/boxo/bitswap" 15 badger "github.com/ipfs/go-ds-badger2" 16 "github.com/libp2p/go-libp2p/core/host" 17 "github.com/libp2p/go-libp2p/core/routing" 18 "github.com/onflow/crypto" 19 "github.com/onflow/flow/protobuf/go/flow/access" 20 "github.com/rs/zerolog" 21 "github.com/spf13/pflag" 22 "google.golang.org/grpc" 23 "google.golang.org/grpc/credentials" 24 "google.golang.org/grpc/credentials/insecure" 25 26 "github.com/onflow/flow-go/admin/commands" 27 stateSyncCommands "github.com/onflow/flow-go/admin/commands/state_synchronization" 28 storageCommands "github.com/onflow/flow-go/admin/commands/storage" 29 "github.com/onflow/flow-go/cmd" 30 "github.com/onflow/flow-go/consensus" 31 "github.com/onflow/flow-go/consensus/hotstuff" 32 "github.com/onflow/flow-go/consensus/hotstuff/committees" 33 "github.com/onflow/flow-go/consensus/hotstuff/notifications" 34 consensuspubsub "github.com/onflow/flow-go/consensus/hotstuff/notifications/pubsub" 35 "github.com/onflow/flow-go/consensus/hotstuff/signature" 36 hotstuffvalidator "github.com/onflow/flow-go/consensus/hotstuff/validator" 37 "github.com/onflow/flow-go/consensus/hotstuff/verification" 38 recovery "github.com/onflow/flow-go/consensus/recovery/protocol" 39 "github.com/onflow/flow-go/engine" 40 "github.com/onflow/flow-go/engine/access/index" 41 "github.com/onflow/flow-go/engine/access/ingestion" 42 pingeng "github.com/onflow/flow-go/engine/access/ping" 43 "github.com/onflow/flow-go/engine/access/rest" 44 "github.com/onflow/flow-go/engine/access/rest/routes" 45 "github.com/onflow/flow-go/engine/access/rpc" 46 "github.com/onflow/flow-go/engine/access/rpc/backend" 47 rpcConnection "github.com/onflow/flow-go/engine/access/rpc/connection" 48 "github.com/onflow/flow-go/engine/access/state_stream" 49 statestreambackend "github.com/onflow/flow-go/engine/access/state_stream/backend" 50 "github.com/onflow/flow-go/engine/access/subscription" 51 followereng "github.com/onflow/flow-go/engine/common/follower" 52 "github.com/onflow/flow-go/engine/common/requester" 53 synceng "github.com/onflow/flow-go/engine/common/synchronization" 54 "github.com/onflow/flow-go/engine/execution/computation/query" 55 "github.com/onflow/flow-go/fvm/storage/derived" 56 "github.com/onflow/flow-go/ledger" 57 "github.com/onflow/flow-go/ledger/complete/wal" 58 "github.com/onflow/flow-go/model/bootstrap" 59 "github.com/onflow/flow-go/model/flow" 60 "github.com/onflow/flow-go/model/flow/filter" 61 "github.com/onflow/flow-go/module" 62 "github.com/onflow/flow-go/module/blobs" 63 "github.com/onflow/flow-go/module/chainsync" 64 "github.com/onflow/flow-go/module/counters" 65 "github.com/onflow/flow-go/module/execution" 66 "github.com/onflow/flow-go/module/executiondatasync/execution_data" 67 execdatacache "github.com/onflow/flow-go/module/executiondatasync/execution_data/cache" 68 finalizer "github.com/onflow/flow-go/module/finalizer/consensus" 69 "github.com/onflow/flow-go/module/grpcserver" 70 "github.com/onflow/flow-go/module/id" 71 "github.com/onflow/flow-go/module/mempool/herocache" 72 "github.com/onflow/flow-go/module/mempool/stdmap" 73 "github.com/onflow/flow-go/module/metrics" 74 "github.com/onflow/flow-go/module/metrics/unstaked" 75 "github.com/onflow/flow-go/module/state_synchronization" 76 "github.com/onflow/flow-go/module/state_synchronization/indexer" 77 edrequester "github.com/onflow/flow-go/module/state_synchronization/requester" 78 "github.com/onflow/flow-go/network" 79 alspmgr "github.com/onflow/flow-go/network/alsp/manager" 80 netcache "github.com/onflow/flow-go/network/cache" 81 "github.com/onflow/flow-go/network/channels" 82 cborcodec "github.com/onflow/flow-go/network/codec/cbor" 83 "github.com/onflow/flow-go/network/p2p" 84 "github.com/onflow/flow-go/network/p2p/blob" 85 p2pbuilder "github.com/onflow/flow-go/network/p2p/builder" 86 p2pbuilderconfig "github.com/onflow/flow-go/network/p2p/builder/config" 87 "github.com/onflow/flow-go/network/p2p/cache" 88 "github.com/onflow/flow-go/network/p2p/conduit" 89 "github.com/onflow/flow-go/network/p2p/connection" 90 "github.com/onflow/flow-go/network/p2p/dht" 91 networkingsubscription "github.com/onflow/flow-go/network/p2p/subscription" 92 "github.com/onflow/flow-go/network/p2p/translator" 93 "github.com/onflow/flow-go/network/p2p/unicast/protocols" 94 relaynet "github.com/onflow/flow-go/network/relay" 95 "github.com/onflow/flow-go/network/slashing" 96 "github.com/onflow/flow-go/network/topology" 97 "github.com/onflow/flow-go/network/underlay" 98 "github.com/onflow/flow-go/network/validator" 99 "github.com/onflow/flow-go/state/protocol" 100 badgerState "github.com/onflow/flow-go/state/protocol/badger" 101 "github.com/onflow/flow-go/state/protocol/blocktimer" 102 "github.com/onflow/flow-go/storage" 103 bstorage "github.com/onflow/flow-go/storage/badger" 104 pStorage "github.com/onflow/flow-go/storage/pebble" 105 "github.com/onflow/flow-go/utils/grpcutils" 106 ) 107 108 // AccessNodeBuilder extends cmd.NodeBuilder and declares additional functions needed to bootstrap an Access node. 109 // The private network allows the staked nodes to communicate among themselves, while the public network allows the 110 // Observers and an Access node to communicate. 111 // 112 // public network private network 113 // +------------------------+ 114 // | Observer 1 |<--------------------------| 115 // +------------------------+ v 116 // +------------------------+ +--------------------+ +------------------------+ 117 // | Observer 2 |<----------------------->| Staked Access Node |<--------------->| All other staked Nodes | 118 // +------------------------+ +--------------------+ +------------------------+ 119 // +------------------------+ ^ 120 // | Observer 3 |<--------------------------| 121 // +------------------------+ 122 123 // AccessNodeConfig defines all the user defined parameters required to bootstrap an access node 124 // For a node running as a standalone process, the config fields will be populated from the command line params, 125 // while for a node running as a library, the config fields are expected to be initialized by the caller. 126 type AccessNodeConfig struct { 127 supportsObserver bool // True if this is an Access node that supports observers and consensus follower engines 128 collectionGRPCPort uint 129 executionGRPCPort uint 130 pingEnabled bool 131 nodeInfoFile string 132 apiRatelimits map[string]int 133 apiBurstlimits map[string]int 134 rpcConf rpc.Config 135 stateStreamConf statestreambackend.Config 136 stateStreamFilterConf map[string]int 137 ExecutionNodeAddress string // deprecated 138 HistoricalAccessRPCs []access.AccessAPIClient 139 logTxTimeToFinalized bool 140 logTxTimeToExecuted bool 141 logTxTimeToFinalizedExecuted bool 142 retryEnabled bool 143 rpcMetricsEnabled bool 144 executionDataSyncEnabled bool 145 publicNetworkExecutionDataEnabled bool 146 executionDataDir string 147 executionDataStartHeight uint64 148 executionDataConfig edrequester.ExecutionDataConfig 149 PublicNetworkConfig PublicNetworkConfig 150 TxResultCacheSize uint 151 TxErrorMessagesCacheSize uint 152 executionDataIndexingEnabled bool 153 registersDBPath string 154 checkpointFile string 155 scriptExecutorConfig query.QueryConfig 156 scriptExecMinBlock uint64 157 scriptExecMaxBlock uint64 158 registerCacheType string 159 registerCacheSize uint 160 programCacheSize uint 161 } 162 163 type PublicNetworkConfig struct { 164 // NetworkKey crypto.PublicKey // TODO: do we need a different key for the public network? 165 BindAddress string 166 Network network.EngineRegistry 167 Metrics module.NetworkMetrics 168 } 169 170 // DefaultAccessNodeConfig defines all the default values for the AccessNodeConfig 171 func DefaultAccessNodeConfig() *AccessNodeConfig { 172 homedir, _ := os.UserHomeDir() 173 return &AccessNodeConfig{ 174 supportsObserver: false, 175 collectionGRPCPort: 9000, 176 executionGRPCPort: 9000, 177 rpcConf: rpc.Config{ 178 UnsecureGRPCListenAddr: "0.0.0.0:9000", 179 SecureGRPCListenAddr: "0.0.0.0:9001", 180 HTTPListenAddr: "0.0.0.0:8000", 181 CollectionAddr: "", 182 HistoricalAccessAddrs: "", 183 BackendConfig: backend.Config{ 184 CollectionClientTimeout: 3 * time.Second, 185 ExecutionClientTimeout: 3 * time.Second, 186 ConnectionPoolSize: backend.DefaultConnectionPoolSize, 187 MaxHeightRange: backend.DefaultMaxHeightRange, 188 PreferredExecutionNodeIDs: nil, 189 FixedExecutionNodeIDs: nil, 190 CircuitBreakerConfig: rpcConnection.CircuitBreakerConfig{ 191 Enabled: false, 192 RestoreTimeout: 60 * time.Second, 193 MaxFailures: 5, 194 MaxRequests: 1, 195 }, 196 ScriptExecutionMode: backend.IndexQueryModeExecutionNodesOnly.String(), // default to ENs only for now 197 EventQueryMode: backend.IndexQueryModeExecutionNodesOnly.String(), // default to ENs only for now 198 TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly.String(), // default to ENs only for now 199 }, 200 RestConfig: rest.Config{ 201 ListenAddress: "", 202 WriteTimeout: rest.DefaultWriteTimeout, 203 ReadTimeout: rest.DefaultReadTimeout, 204 IdleTimeout: rest.DefaultIdleTimeout, 205 }, 206 MaxMsgSize: grpcutils.DefaultMaxMsgSize, 207 CompressorName: grpcutils.NoCompressor, 208 }, 209 stateStreamConf: statestreambackend.Config{ 210 MaxExecutionDataMsgSize: grpcutils.DefaultMaxMsgSize, 211 ExecutionDataCacheSize: subscription.DefaultCacheSize, 212 ClientSendTimeout: subscription.DefaultSendTimeout, 213 ClientSendBufferSize: subscription.DefaultSendBufferSize, 214 MaxGlobalStreams: subscription.DefaultMaxGlobalStreams, 215 EventFilterConfig: state_stream.DefaultEventFilterConfig, 216 RegisterIDsRequestLimit: state_stream.DefaultRegisterIDsRequestLimit, 217 ResponseLimit: subscription.DefaultResponseLimit, 218 HeartbeatInterval: subscription.DefaultHeartbeatInterval, 219 }, 220 stateStreamFilterConf: nil, 221 ExecutionNodeAddress: "localhost:9000", 222 logTxTimeToFinalized: false, 223 logTxTimeToExecuted: false, 224 logTxTimeToFinalizedExecuted: false, 225 pingEnabled: false, 226 retryEnabled: false, 227 rpcMetricsEnabled: false, 228 nodeInfoFile: "", 229 apiRatelimits: nil, 230 apiBurstlimits: nil, 231 TxResultCacheSize: 0, 232 TxErrorMessagesCacheSize: 1000, 233 PublicNetworkConfig: PublicNetworkConfig{ 234 BindAddress: cmd.NotSet, 235 Metrics: metrics.NewNoopCollector(), 236 }, 237 executionDataSyncEnabled: true, 238 publicNetworkExecutionDataEnabled: false, 239 executionDataDir: filepath.Join(homedir, ".flow", "execution_data"), 240 executionDataStartHeight: 0, 241 executionDataConfig: edrequester.ExecutionDataConfig{ 242 InitialBlockHeight: 0, 243 MaxSearchAhead: edrequester.DefaultMaxSearchAhead, 244 FetchTimeout: edrequester.DefaultFetchTimeout, 245 MaxFetchTimeout: edrequester.DefaultMaxFetchTimeout, 246 RetryDelay: edrequester.DefaultRetryDelay, 247 MaxRetryDelay: edrequester.DefaultMaxRetryDelay, 248 }, 249 executionDataIndexingEnabled: false, 250 registersDBPath: filepath.Join(homedir, ".flow", "execution_state"), 251 checkpointFile: cmd.NotSet, 252 scriptExecutorConfig: query.NewDefaultConfig(), 253 scriptExecMinBlock: 0, 254 scriptExecMaxBlock: math.MaxUint64, 255 registerCacheType: pStorage.CacheTypeTwoQueue.String(), 256 registerCacheSize: 0, 257 programCacheSize: 0, 258 } 259 } 260 261 // FlowAccessNodeBuilder provides the common functionality needed to bootstrap a Flow access node 262 // It is composed of the FlowNodeBuilder, the AccessNodeConfig and contains all the components and modules needed for the 263 // access nodes 264 type FlowAccessNodeBuilder struct { 265 *cmd.FlowNodeBuilder 266 *AccessNodeConfig 267 268 // components 269 FollowerState protocol.FollowerState 270 SyncCore *chainsync.Core 271 RpcEng *rpc.Engine 272 FollowerDistributor *consensuspubsub.FollowerDistributor 273 CollectionRPC access.AccessAPIClient 274 TransactionTimings *stdmap.TransactionTimings 275 CollectionsToMarkFinalized *stdmap.Times 276 CollectionsToMarkExecuted *stdmap.Times 277 BlocksToMarkExecuted *stdmap.Times 278 TransactionMetrics *metrics.TransactionCollector 279 RestMetrics *metrics.RestCollector 280 AccessMetrics module.AccessMetrics 281 PingMetrics module.PingMetrics 282 Committee hotstuff.DynamicCommittee 283 Finalized *flow.Header // latest finalized block that the node knows of at startup time 284 Pending []*flow.Header 285 FollowerCore module.HotStuffFollower 286 Validator hotstuff.Validator 287 ExecutionDataDownloader execution_data.Downloader 288 PublicBlobService network.BlobService 289 ExecutionDataRequester state_synchronization.ExecutionDataRequester 290 ExecutionDataStore execution_data.ExecutionDataStore 291 ExecutionDataCache *execdatacache.ExecutionDataCache 292 ExecutionIndexer *indexer.Indexer 293 ExecutionIndexerCore *indexer.IndexerCore 294 ScriptExecutor *backend.ScriptExecutor 295 RegistersAsyncStore *execution.RegistersAsyncStore 296 EventsIndex *index.EventsIndex 297 TxResultsIndex *index.TransactionResultsIndex 298 IndexerDependencies *cmd.DependencyList 299 collectionExecutedMetric module.CollectionExecutedMetric 300 301 // The sync engine participants provider is the libp2p peer store for the access node 302 // which is not available until after the network has started. 303 // Hence, a factory function that needs to be called just before creating the sync engine 304 SyncEngineParticipantsProviderFactory func() module.IdentifierProvider 305 306 // engines 307 IngestEng *ingestion.Engine 308 RequestEng *requester.Engine 309 FollowerEng *followereng.ComplianceEngine 310 SyncEng *synceng.Engine 311 StateStreamEng *statestreambackend.Engine 312 313 // grpc servers 314 secureGrpcServer *grpcserver.GrpcServer 315 unsecureGrpcServer *grpcserver.GrpcServer 316 stateStreamGrpcServer *grpcserver.GrpcServer 317 318 stateStreamBackend *statestreambackend.StateStreamBackend 319 } 320 321 func (builder *FlowAccessNodeBuilder) buildFollowerState() *FlowAccessNodeBuilder { 322 builder.Module("mutable follower state", func(node *cmd.NodeConfig) error { 323 // For now, we only support state implementations from package badger. 324 // If we ever support different implementations, the following can be replaced by a type-aware factory 325 state, ok := node.State.(*badgerState.State) 326 if !ok { 327 return fmt.Errorf("only implementations of type badger.State are currently supported but read-only state has type %T", node.State) 328 } 329 330 followerState, err := badgerState.NewFollowerState( 331 node.Logger, 332 node.Tracer, 333 node.ProtocolEvents, 334 state, 335 node.Storage.Index, 336 node.Storage.Payloads, 337 blocktimer.DefaultBlockTimer, 338 ) 339 builder.FollowerState = followerState 340 341 return err 342 }) 343 344 return builder 345 } 346 347 func (builder *FlowAccessNodeBuilder) buildSyncCore() *FlowAccessNodeBuilder { 348 builder.Module("sync core", func(node *cmd.NodeConfig) error { 349 syncCore, err := chainsync.New(node.Logger, node.SyncCoreConfig, metrics.NewChainSyncCollector(node.RootChainID), node.RootChainID) 350 builder.SyncCore = syncCore 351 352 return err 353 }) 354 355 return builder 356 } 357 358 func (builder *FlowAccessNodeBuilder) buildCommittee() *FlowAccessNodeBuilder { 359 builder.Component("committee", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 360 // initialize consensus committee's membership state 361 // This committee state is for the HotStuff follower, which follows the MAIN CONSENSUS committee 362 // Note: node.Me.NodeID() is not part of the consensus committee 363 committee, err := committees.NewConsensusCommittee(node.State, node.Me.NodeID()) 364 node.ProtocolEvents.AddConsumer(committee) 365 builder.Committee = committee 366 367 return committee, err 368 }) 369 370 return builder 371 } 372 373 func (builder *FlowAccessNodeBuilder) buildLatestHeader() *FlowAccessNodeBuilder { 374 builder.Module("latest header", func(node *cmd.NodeConfig) error { 375 finalized, pending, err := recovery.FindLatest(node.State, node.Storage.Headers) 376 builder.Finalized, builder.Pending = finalized, pending 377 378 return err 379 }) 380 381 return builder 382 } 383 384 func (builder *FlowAccessNodeBuilder) buildFollowerCore() *FlowAccessNodeBuilder { 385 builder.Component("follower core", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 386 // create a finalizer that will handle updating the protocol 387 // state when the follower detects newly finalized blocks 388 final := finalizer.NewFinalizer(node.DB, node.Storage.Headers, builder.FollowerState, node.Tracer) 389 390 packer := signature.NewConsensusSigDataPacker(builder.Committee) 391 // initialize the verifier for the protocol consensus 392 verifier := verification.NewCombinedVerifier(builder.Committee, packer) 393 builder.Validator = hotstuffvalidator.New(builder.Committee, verifier) 394 395 followerCore, err := consensus.NewFollower( 396 node.Logger, 397 node.Metrics.Mempool, 398 node.Storage.Headers, 399 final, 400 builder.FollowerDistributor, 401 node.FinalizedRootBlock.Header, 402 node.RootQC, 403 builder.Finalized, 404 builder.Pending, 405 ) 406 if err != nil { 407 return nil, fmt.Errorf("could not initialize follower core: %w", err) 408 } 409 builder.FollowerCore = followerCore 410 411 return builder.FollowerCore, nil 412 }) 413 414 return builder 415 } 416 417 func (builder *FlowAccessNodeBuilder) buildFollowerEngine() *FlowAccessNodeBuilder { 418 builder.Component("follower engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 419 var heroCacheCollector module.HeroCacheMetrics = metrics.NewNoopCollector() 420 if node.HeroCacheMetricsEnable { 421 heroCacheCollector = metrics.FollowerCacheMetrics(node.MetricsRegisterer) 422 } 423 424 core, err := followereng.NewComplianceCore( 425 node.Logger, 426 node.Metrics.Mempool, 427 heroCacheCollector, 428 builder.FollowerDistributor, 429 builder.FollowerState, 430 builder.FollowerCore, 431 builder.Validator, 432 builder.SyncCore, 433 node.Tracer, 434 ) 435 if err != nil { 436 return nil, fmt.Errorf("could not create follower core: %w", err) 437 } 438 439 builder.FollowerEng, err = followereng.NewComplianceLayer( 440 node.Logger, 441 node.EngineRegistry, 442 node.Me, 443 node.Metrics.Engine, 444 node.Storage.Headers, 445 builder.Finalized, 446 core, 447 node.ComplianceConfig, 448 ) 449 if err != nil { 450 return nil, fmt.Errorf("could not create follower engine: %w", err) 451 } 452 builder.FollowerDistributor.AddOnBlockFinalizedConsumer(builder.FollowerEng.OnFinalizedBlock) 453 454 return builder.FollowerEng, nil 455 }) 456 457 return builder 458 } 459 460 func (builder *FlowAccessNodeBuilder) buildSyncEngine() *FlowAccessNodeBuilder { 461 builder.Component("sync engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 462 spamConfig, err := synceng.NewSpamDetectionConfig() 463 if err != nil { 464 return nil, fmt.Errorf("could not initialize spam detection config: %w", err) 465 } 466 sync, err := synceng.New( 467 node.Logger, 468 node.Metrics.Engine, 469 node.EngineRegistry, 470 node.Me, 471 node.State, 472 node.Storage.Blocks, 473 builder.FollowerEng, 474 builder.SyncCore, 475 builder.SyncEngineParticipantsProviderFactory(), 476 spamConfig, 477 ) 478 if err != nil { 479 return nil, fmt.Errorf("could not create synchronization engine: %w", err) 480 } 481 builder.SyncEng = sync 482 builder.FollowerDistributor.AddFinalizationConsumer(sync) 483 484 return builder.SyncEng, nil 485 }) 486 487 return builder 488 } 489 490 func (builder *FlowAccessNodeBuilder) BuildConsensusFollower() *FlowAccessNodeBuilder { 491 builder. 492 buildFollowerState(). 493 buildSyncCore(). 494 buildCommittee(). 495 buildLatestHeader(). 496 buildFollowerCore(). 497 buildFollowerEngine(). 498 buildSyncEngine() 499 500 return builder 501 } 502 503 func (builder *FlowAccessNodeBuilder) BuildExecutionSyncComponents() *FlowAccessNodeBuilder { 504 var ds *badger.Datastore 505 var bs network.BlobService 506 var processedBlockHeight storage.ConsumerProgress 507 var processedNotifications storage.ConsumerProgress 508 var bsDependable *module.ProxiedReadyDoneAware 509 var execDataDistributor *edrequester.ExecutionDataDistributor 510 var execDataCacheBackend *herocache.BlockExecutionData 511 var executionDataStoreCache *execdatacache.ExecutionDataCache 512 513 // setup dependency chain to ensure indexer starts after the requester 514 requesterDependable := module.NewProxiedReadyDoneAware() 515 builder.IndexerDependencies.Add(requesterDependable) 516 517 builder. 518 AdminCommand("read-execution-data", func(config *cmd.NodeConfig) commands.AdminCommand { 519 return stateSyncCommands.NewReadExecutionDataCommand(builder.ExecutionDataStore) 520 }). 521 Module("execution data datastore and blobstore", func(node *cmd.NodeConfig) error { 522 datastoreDir := filepath.Join(builder.executionDataDir, "blobstore") 523 err := os.MkdirAll(datastoreDir, 0700) 524 if err != nil { 525 return err 526 } 527 528 ds, err = badger.NewDatastore(datastoreDir, &badger.DefaultOptions) 529 if err != nil { 530 return err 531 } 532 533 builder.ShutdownFunc(func() error { 534 if err := ds.Close(); err != nil { 535 return fmt.Errorf("could not close execution data datastore: %w", err) 536 } 537 return nil 538 }) 539 540 return nil 541 }). 542 Module("processed block height consumer progress", func(node *cmd.NodeConfig) error { 543 // Note: progress is stored in the datastore's DB since that is where the jobqueue 544 // writes execution data to. 545 processedBlockHeight = bstorage.NewConsumerProgress(ds.DB, module.ConsumeProgressExecutionDataRequesterBlockHeight) 546 return nil 547 }). 548 Module("processed notifications consumer progress", func(node *cmd.NodeConfig) error { 549 // Note: progress is stored in the datastore's DB since that is where the jobqueue 550 // writes execution data to. 551 processedNotifications = bstorage.NewConsumerProgress(ds.DB, module.ConsumeProgressExecutionDataRequesterNotification) 552 return nil 553 }). 554 Module("blobservice peer manager dependencies", func(node *cmd.NodeConfig) error { 555 bsDependable = module.NewProxiedReadyDoneAware() 556 builder.PeerManagerDependencies.Add(bsDependable) 557 return nil 558 }). 559 Module("execution datastore", func(node *cmd.NodeConfig) error { 560 blobstore := blobs.NewBlobstore(ds) 561 builder.ExecutionDataStore = execution_data.NewExecutionDataStore(blobstore, execution_data.DefaultSerializer) 562 return nil 563 }). 564 Module("execution data cache", func(node *cmd.NodeConfig) error { 565 var heroCacheCollector module.HeroCacheMetrics = metrics.NewNoopCollector() 566 if builder.HeroCacheMetricsEnable { 567 heroCacheCollector = metrics.AccessNodeExecutionDataCacheMetrics(builder.MetricsRegisterer) 568 } 569 570 execDataCacheBackend = herocache.NewBlockExecutionData(builder.stateStreamConf.ExecutionDataCacheSize, builder.Logger, heroCacheCollector) 571 572 // Execution Data cache that uses a blobstore as the backend (instead of a downloader) 573 // This ensures that it simply returns a not found error if the blob doesn't exist 574 // instead of attempting to download it from the network. 575 executionDataStoreCache = execdatacache.NewExecutionDataCache( 576 builder.ExecutionDataStore, 577 builder.Storage.Headers, 578 builder.Storage.Seals, 579 builder.Storage.Results, 580 execDataCacheBackend, 581 ) 582 583 return nil 584 }). 585 Component("execution data service", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 586 opts := []network.BlobServiceOption{ 587 blob.WithBitswapOptions( 588 // Only allow block requests from staked ENs and ANs 589 bitswap.WithPeerBlockRequestFilter( 590 blob.AuthorizedRequester(nil, builder.IdentityProvider, builder.Logger), 591 ), 592 bitswap.WithTracer( 593 blob.NewTracer(node.Logger.With().Str("blob_service", channels.ExecutionDataService.String()).Logger()), 594 ), 595 ), 596 } 597 598 if !builder.BitswapReprovideEnabled { 599 opts = append(opts, blob.WithReprovideInterval(-1)) 600 } 601 602 var err error 603 bs, err = node.EngineRegistry.RegisterBlobService(channels.ExecutionDataService, ds, opts...) 604 if err != nil { 605 return nil, fmt.Errorf("could not register blob service: %w", err) 606 } 607 608 // add blobservice into ReadyDoneAware dependency passed to peer manager 609 // this starts the blob service and configures peer manager to wait for the blobservice 610 // to be ready before starting 611 bsDependable.Init(bs) 612 613 builder.ExecutionDataDownloader = execution_data.NewDownloader(bs) 614 615 return builder.ExecutionDataDownloader, nil 616 }). 617 Component("execution data requester", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 618 // Validation of the start block height needs to be done after loading state 619 if builder.executionDataStartHeight > 0 { 620 if builder.executionDataStartHeight <= builder.FinalizedRootBlock.Header.Height { 621 return nil, fmt.Errorf( 622 "execution data start block height (%d) must be greater than the root block height (%d)", 623 builder.executionDataStartHeight, builder.FinalizedRootBlock.Header.Height) 624 } 625 626 latestSeal, err := builder.State.Sealed().Head() 627 if err != nil { 628 return nil, fmt.Errorf("failed to get latest sealed height") 629 } 630 631 // Note: since the root block of a spork is also sealed in the root protocol state, the 632 // latest sealed height is always equal to the root block height. That means that at the 633 // very beginning of a spork, this check will always fail. Operators should not specify 634 // an InitialBlockHeight when starting from the beginning of a spork. 635 if builder.executionDataStartHeight > latestSeal.Height { 636 return nil, fmt.Errorf( 637 "execution data start block height (%d) must be less than or equal to the latest sealed block height (%d)", 638 builder.executionDataStartHeight, latestSeal.Height) 639 } 640 641 // executionDataStartHeight is provided as the first block to sync, but the 642 // requester expects the initial last processed height, which is the first height - 1 643 builder.executionDataConfig.InitialBlockHeight = builder.executionDataStartHeight - 1 644 } else { 645 builder.executionDataConfig.InitialBlockHeight = builder.SealedRootBlock.Header.Height 646 } 647 648 execDataDistributor = edrequester.NewExecutionDataDistributor() 649 650 // Execution Data cache with a downloader as the backend. This is used by the requester 651 // to download and cache execution data for each block. It shares a cache backend instance 652 // with the datastore implementation. 653 executionDataCache := execdatacache.NewExecutionDataCache( 654 builder.ExecutionDataDownloader, 655 builder.Storage.Headers, 656 builder.Storage.Seals, 657 builder.Storage.Results, 658 execDataCacheBackend, 659 ) 660 661 r, err := edrequester.New( 662 builder.Logger, 663 metrics.NewExecutionDataRequesterCollector(), 664 builder.ExecutionDataDownloader, 665 executionDataCache, 666 processedBlockHeight, 667 processedNotifications, 668 builder.State, 669 builder.Storage.Headers, 670 builder.executionDataConfig, 671 execDataDistributor, 672 ) 673 if err != nil { 674 return nil, fmt.Errorf("failed to create execution data requester: %w", err) 675 } 676 builder.ExecutionDataRequester = r 677 678 builder.FollowerDistributor.AddOnBlockFinalizedConsumer(builder.ExecutionDataRequester.OnBlockFinalized) 679 680 // add requester into ReadyDoneAware dependency passed to indexer. This allows the indexer 681 // to wait for the requester to be ready before starting. 682 requesterDependable.Init(builder.ExecutionDataRequester) 683 684 return builder.ExecutionDataRequester, nil 685 }) 686 687 if builder.publicNetworkExecutionDataEnabled { 688 var publicBsDependable *module.ProxiedReadyDoneAware 689 690 builder.Module("public blobservice peer manager dependencies", func(node *cmd.NodeConfig) error { 691 publicBsDependable = module.NewProxiedReadyDoneAware() 692 builder.PeerManagerDependencies.Add(publicBsDependable) 693 return nil 694 }) 695 builder.Component("public network execution data service", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 696 opts := []network.BlobServiceOption{ 697 blob.WithBitswapOptions( 698 bitswap.WithTracer( 699 blob.NewTracer(node.Logger.With().Str("public_blob_service", channels.PublicExecutionDataService.String()).Logger()), 700 ), 701 ), 702 blob.WithParentBlobService(bs), 703 } 704 705 net := builder.AccessNodeConfig.PublicNetworkConfig.Network 706 707 var err error 708 builder.PublicBlobService, err = net.RegisterBlobService(channels.PublicExecutionDataService, ds, opts...) 709 if err != nil { 710 return nil, fmt.Errorf("could not register blob service: %w", err) 711 } 712 713 // add blobservice into ReadyDoneAware dependency passed to peer manager 714 // this starts the blob service and configures peer manager to wait for the blobservice 715 // to be ready before starting 716 publicBsDependable.Init(builder.PublicBlobService) 717 return &module.NoopReadyDoneAware{}, nil 718 }) 719 } 720 721 if builder.executionDataIndexingEnabled { 722 var indexedBlockHeight storage.ConsumerProgress 723 724 builder. 725 AdminCommand("execute-script", func(config *cmd.NodeConfig) commands.AdminCommand { 726 return stateSyncCommands.NewExecuteScriptCommand(builder.ScriptExecutor) 727 }). 728 Module("indexed block height consumer progress", func(node *cmd.NodeConfig) error { 729 // Note: progress is stored in the MAIN db since that is where indexed execution data is stored. 730 indexedBlockHeight = bstorage.NewConsumerProgress(builder.DB, module.ConsumeProgressExecutionDataIndexerBlockHeight) 731 return nil 732 }). 733 Module("transaction results storage", func(node *cmd.NodeConfig) error { 734 builder.Storage.LightTransactionResults = bstorage.NewLightTransactionResults(node.Metrics.Cache, node.DB, bstorage.DefaultCacheSize) 735 return nil 736 }). 737 DependableComponent("execution data indexer", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 738 // Note: using a DependableComponent here to ensure that the indexer does not block 739 // other components from starting while bootstrapping the register db since it may 740 // take hours to complete. 741 742 pdb, err := pStorage.OpenRegisterPebbleDB(builder.registersDBPath) 743 if err != nil { 744 return nil, fmt.Errorf("could not open registers db: %w", err) 745 } 746 builder.ShutdownFunc(func() error { 747 return pdb.Close() 748 }) 749 750 bootstrapped, err := pStorage.IsBootstrapped(pdb) 751 if err != nil { 752 return nil, fmt.Errorf("could not check if registers db is bootstrapped: %w", err) 753 } 754 755 if !bootstrapped { 756 checkpointFile := builder.checkpointFile 757 if checkpointFile == cmd.NotSet { 758 checkpointFile = path.Join(builder.BootstrapDir, bootstrap.PathRootCheckpoint) 759 } 760 761 // currently, the checkpoint must be from the root block. 762 // read the root hash from the provided checkpoint and verify it matches the 763 // state commitment from the root snapshot. 764 err := wal.CheckpointHasRootHash( 765 node.Logger, 766 "", // checkpoint file already full path 767 checkpointFile, 768 ledger.RootHash(node.RootSeal.FinalState), 769 ) 770 if err != nil { 771 return nil, fmt.Errorf("could not verify checkpoint file: %w", err) 772 } 773 774 checkpointHeight := builder.SealedRootBlock.Header.Height 775 776 if builder.SealedRootBlock.ID() != builder.RootSeal.BlockID { 777 return nil, fmt.Errorf("mismatching sealed root block and root seal: %v != %v", 778 builder.SealedRootBlock.ID(), builder.RootSeal.BlockID) 779 } 780 781 rootHash := ledger.RootHash(builder.RootSeal.FinalState) 782 bootstrap, err := pStorage.NewRegisterBootstrap(pdb, checkpointFile, checkpointHeight, rootHash, builder.Logger) 783 if err != nil { 784 return nil, fmt.Errorf("could not create registers bootstrap: %w", err) 785 } 786 787 // TODO: find a way to hook a context up to this to allow a graceful shutdown 788 workerCount := 10 789 err = bootstrap.IndexCheckpointFile(context.Background(), workerCount) 790 if err != nil { 791 return nil, fmt.Errorf("could not load checkpoint file: %w", err) 792 } 793 } 794 795 registers, err := pStorage.NewRegisters(pdb) 796 if err != nil { 797 return nil, fmt.Errorf("could not create registers storage: %w", err) 798 } 799 800 if builder.registerCacheSize > 0 { 801 cacheType, err := pStorage.ParseCacheType(builder.registerCacheType) 802 if err != nil { 803 return nil, fmt.Errorf("could not parse register cache type: %w", err) 804 } 805 cacheMetrics := metrics.NewCacheCollector(builder.RootChainID) 806 registersCache, err := pStorage.NewRegistersCache(registers, cacheType, builder.registerCacheSize, cacheMetrics) 807 if err != nil { 808 return nil, fmt.Errorf("could not create registers cache: %w", err) 809 } 810 builder.Storage.RegisterIndex = registersCache 811 } else { 812 builder.Storage.RegisterIndex = registers 813 } 814 815 indexerDerivedChainData, queryDerivedChainData, err := builder.buildDerivedChainData() 816 if err != nil { 817 return nil, fmt.Errorf("could not create derived chain data: %w", err) 818 } 819 820 indexerCore, err := indexer.New( 821 builder.Logger, 822 metrics.NewExecutionStateIndexerCollector(), 823 builder.DB, 824 builder.Storage.RegisterIndex, 825 builder.Storage.Headers, 826 builder.Storage.Events, 827 builder.Storage.Collections, 828 builder.Storage.Transactions, 829 builder.Storage.LightTransactionResults, 830 builder.RootChainID.Chain(), 831 indexerDerivedChainData, 832 builder.collectionExecutedMetric, 833 ) 834 if err != nil { 835 return nil, err 836 } 837 builder.ExecutionIndexerCore = indexerCore 838 839 // execution state worker uses a jobqueue to process new execution data and indexes it by using the indexer. 840 builder.ExecutionIndexer, err = indexer.NewIndexer( 841 builder.Logger, 842 registers.FirstHeight(), 843 registers, 844 indexerCore, 845 executionDataStoreCache, 846 builder.ExecutionDataRequester.HighestConsecutiveHeight, 847 indexedBlockHeight, 848 ) 849 if err != nil { 850 return nil, err 851 } 852 853 // setup requester to notify indexer when new execution data is received 854 execDataDistributor.AddOnExecutionDataReceivedConsumer(builder.ExecutionIndexer.OnExecutionData) 855 856 // create script execution module, this depends on the indexer being initialized and the 857 // having the register storage bootstrapped 858 scripts := execution.NewScripts( 859 builder.Logger, 860 metrics.NewExecutionCollector(builder.Tracer), 861 builder.RootChainID, 862 query.NewProtocolStateWrapper(builder.State), 863 builder.Storage.Headers, 864 builder.ExecutionIndexerCore.RegisterValue, 865 builder.scriptExecutorConfig, 866 queryDerivedChainData, 867 builder.programCacheSize > 0, 868 ) 869 870 err = builder.ScriptExecutor.Initialize(builder.ExecutionIndexer, scripts) 871 if err != nil { 872 return nil, err 873 } 874 875 err = builder.EventsIndex.Initialize(builder.ExecutionIndexer) 876 if err != nil { 877 return nil, err 878 } 879 880 err = builder.TxResultsIndex.Initialize(builder.ExecutionIndexer) 881 if err != nil { 882 return nil, err 883 } 884 885 err = builder.RegistersAsyncStore.Initialize(registers) 886 if err != nil { 887 return nil, err 888 } 889 890 return builder.ExecutionIndexer, nil 891 }, builder.IndexerDependencies) 892 } 893 894 if builder.stateStreamConf.ListenAddr != "" { 895 builder.Component("exec state stream engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 896 for key, value := range builder.stateStreamFilterConf { 897 switch key { 898 case "EventTypes": 899 builder.stateStreamConf.MaxEventTypes = value 900 case "Addresses": 901 builder.stateStreamConf.MaxAddresses = value 902 case "Contracts": 903 builder.stateStreamConf.MaxContracts = value 904 case "AccountAddresses": 905 builder.stateStreamConf.MaxAccountAddress = value 906 } 907 } 908 builder.stateStreamConf.RpcMetricsEnabled = builder.rpcMetricsEnabled 909 910 highestAvailableHeight, err := builder.ExecutionDataRequester.HighestConsecutiveHeight() 911 if err != nil { 912 return nil, fmt.Errorf("could not get highest consecutive height: %w", err) 913 } 914 broadcaster := engine.NewBroadcaster() 915 916 eventQueryMode, err := backend.ParseIndexQueryMode(builder.rpcConf.BackendConfig.EventQueryMode) 917 if err != nil { 918 return nil, fmt.Errorf("could not parse event query mode: %w", err) 919 } 920 921 // use the events index for events if enabled and the node is configured to use it for 922 // regular event queries 923 useIndex := builder.executionDataIndexingEnabled && 924 eventQueryMode != backend.IndexQueryModeExecutionNodesOnly 925 926 executionDataTracker := subscription.NewExecutionDataTracker( 927 builder.Logger, 928 node.State, 929 builder.executionDataConfig.InitialBlockHeight, 930 node.Storage.Headers, 931 broadcaster, 932 highestAvailableHeight, 933 builder.EventsIndex, 934 useIndex, 935 ) 936 937 builder.stateStreamBackend, err = statestreambackend.New( 938 node.Logger, 939 node.State, 940 node.Storage.Headers, 941 node.Storage.Seals, 942 node.Storage.Results, 943 builder.ExecutionDataStore, 944 executionDataStoreCache, 945 builder.RegistersAsyncStore, 946 builder.EventsIndex, 947 useIndex, 948 int(builder.stateStreamConf.RegisterIDsRequestLimit), 949 subscription.NewSubscriptionHandler( 950 builder.Logger, 951 broadcaster, 952 builder.stateStreamConf.ClientSendTimeout, 953 builder.stateStreamConf.ResponseLimit, 954 builder.stateStreamConf.ClientSendBufferSize, 955 ), 956 executionDataTracker, 957 ) 958 if err != nil { 959 return nil, fmt.Errorf("could not create state stream backend: %w", err) 960 } 961 962 stateStreamEng, err := statestreambackend.NewEng( 963 node.Logger, 964 builder.stateStreamConf, 965 executionDataStoreCache, 966 node.Storage.Headers, 967 node.RootChainID, 968 builder.stateStreamGrpcServer, 969 builder.stateStreamBackend, 970 ) 971 if err != nil { 972 return nil, fmt.Errorf("could not create state stream engine: %w", err) 973 } 974 builder.StateStreamEng = stateStreamEng 975 976 // setup requester to notify ExecutionDataTracker when new execution data is received 977 execDataDistributor.AddOnExecutionDataReceivedConsumer(builder.stateStreamBackend.OnExecutionData) 978 979 return builder.StateStreamEng, nil 980 }) 981 } 982 983 return builder 984 } 985 986 // buildDerivedChainData creates the derived chain data for the indexer and the query engine 987 // If program caching is disabled, the function will return nil for the indexer cache, and a 988 // derived chain data object for the query engine cache. 989 func (builder *FlowAccessNodeBuilder) buildDerivedChainData() ( 990 indexerCache *derived.DerivedChainData, 991 queryCache *derived.DerivedChainData, 992 err error, 993 ) { 994 cacheSize := builder.programCacheSize 995 996 // the underlying cache requires size > 0. no data will be written so 1 is fine. 997 if cacheSize == 0 { 998 cacheSize = 1 999 } 1000 1001 derivedChainData, err := derived.NewDerivedChainData(cacheSize) 1002 if err != nil { 1003 return nil, nil, err 1004 } 1005 1006 // writes are done by the indexer. using a nil value effectively disables writes to the cache. 1007 if builder.programCacheSize == 0 { 1008 return nil, derivedChainData, nil 1009 } 1010 1011 return derivedChainData, derivedChainData, nil 1012 } 1013 1014 func FlowAccessNode(nodeBuilder *cmd.FlowNodeBuilder) *FlowAccessNodeBuilder { 1015 dist := consensuspubsub.NewFollowerDistributor() 1016 dist.AddProposalViolationConsumer(notifications.NewSlashingViolationsConsumer(nodeBuilder.Logger)) 1017 return &FlowAccessNodeBuilder{ 1018 AccessNodeConfig: DefaultAccessNodeConfig(), 1019 FlowNodeBuilder: nodeBuilder, 1020 FollowerDistributor: dist, 1021 IndexerDependencies: cmd.NewDependencyList(), 1022 } 1023 } 1024 1025 func (builder *FlowAccessNodeBuilder) ParseFlags() error { 1026 builder.BaseFlags() 1027 1028 builder.extraFlags() 1029 1030 return builder.ParseAndPrintFlags() 1031 } 1032 1033 func (builder *FlowAccessNodeBuilder) extraFlags() { 1034 builder.ExtraFlags(func(flags *pflag.FlagSet) { 1035 defaultConfig := DefaultAccessNodeConfig() 1036 1037 flags.UintVar(&builder.collectionGRPCPort, "collection-ingress-port", defaultConfig.collectionGRPCPort, "the grpc ingress port for all collection nodes") 1038 flags.UintVar(&builder.executionGRPCPort, "execution-ingress-port", defaultConfig.executionGRPCPort, "the grpc ingress port for all execution nodes") 1039 flags.StringVarP(&builder.rpcConf.UnsecureGRPCListenAddr, 1040 "rpc-addr", 1041 "r", 1042 defaultConfig.rpcConf.UnsecureGRPCListenAddr, 1043 "the address the unsecured gRPC server listens on") 1044 flags.StringVar(&builder.rpcConf.SecureGRPCListenAddr, 1045 "secure-rpc-addr", 1046 defaultConfig.rpcConf.SecureGRPCListenAddr, 1047 "the address the secure gRPC server listens on") 1048 flags.StringVar(&builder.stateStreamConf.ListenAddr, 1049 "state-stream-addr", 1050 defaultConfig.stateStreamConf.ListenAddr, 1051 "the address the state stream server listens on (if empty the server will not be started)") 1052 flags.StringVarP(&builder.rpcConf.HTTPListenAddr, "http-addr", "h", defaultConfig.rpcConf.HTTPListenAddr, "the address the http proxy server listens on") 1053 flags.StringVar(&builder.rpcConf.RestConfig.ListenAddress, 1054 "rest-addr", 1055 defaultConfig.rpcConf.RestConfig.ListenAddress, 1056 "the address the REST server listens on (if empty the REST server will not be started)") 1057 flags.DurationVar(&builder.rpcConf.RestConfig.WriteTimeout, 1058 "rest-write-timeout", 1059 defaultConfig.rpcConf.RestConfig.WriteTimeout, 1060 "timeout to use when writing REST response") 1061 flags.DurationVar(&builder.rpcConf.RestConfig.ReadTimeout, 1062 "rest-read-timeout", 1063 defaultConfig.rpcConf.RestConfig.ReadTimeout, 1064 "timeout to use when reading REST request headers") 1065 flags.DurationVar(&builder.rpcConf.RestConfig.IdleTimeout, "rest-idle-timeout", defaultConfig.rpcConf.RestConfig.IdleTimeout, "idle timeout for REST connections") 1066 flags.StringVarP(&builder.rpcConf.CollectionAddr, 1067 "static-collection-ingress-addr", 1068 "", 1069 defaultConfig.rpcConf.CollectionAddr, 1070 "the address (of the collection node) to send transactions to") 1071 flags.StringVarP(&builder.ExecutionNodeAddress, 1072 "script-addr", 1073 "s", 1074 defaultConfig.ExecutionNodeAddress, 1075 "the address (of the execution node) forward the script to") 1076 flags.StringVarP(&builder.rpcConf.HistoricalAccessAddrs, 1077 "historical-access-addr", 1078 "", 1079 defaultConfig.rpcConf.HistoricalAccessAddrs, 1080 "comma separated rpc addresses for historical access nodes") 1081 flags.DurationVar(&builder.rpcConf.BackendConfig.CollectionClientTimeout, 1082 "collection-client-timeout", 1083 defaultConfig.rpcConf.BackendConfig.CollectionClientTimeout, 1084 "grpc client timeout for a collection node") 1085 flags.DurationVar(&builder.rpcConf.BackendConfig.ExecutionClientTimeout, 1086 "execution-client-timeout", 1087 defaultConfig.rpcConf.BackendConfig.ExecutionClientTimeout, 1088 "grpc client timeout for an execution node") 1089 flags.UintVar(&builder.rpcConf.BackendConfig.ConnectionPoolSize, 1090 "connection-pool-size", 1091 defaultConfig.rpcConf.BackendConfig.ConnectionPoolSize, 1092 "maximum number of connections allowed in the connection pool, size of 0 disables the connection pooling, and anything less than the default size will be overridden to use the default size") 1093 flags.UintVar(&builder.rpcConf.MaxMsgSize, 1094 "rpc-max-message-size", 1095 grpcutils.DefaultMaxMsgSize, 1096 "the maximum message size in bytes for messages sent or received over grpc") 1097 flags.UintVar(&builder.rpcConf.BackendConfig.MaxHeightRange, 1098 "rpc-max-height-range", 1099 defaultConfig.rpcConf.BackendConfig.MaxHeightRange, 1100 "maximum size for height range requests") 1101 flags.StringSliceVar(&builder.rpcConf.BackendConfig.PreferredExecutionNodeIDs, 1102 "preferred-execution-node-ids", 1103 defaultConfig.rpcConf.BackendConfig.PreferredExecutionNodeIDs, 1104 "comma separated list of execution nodes ids to choose from when making an upstream call e.g. b4a4dbdcd443d...,fb386a6a... etc.") 1105 flags.StringSliceVar(&builder.rpcConf.BackendConfig.FixedExecutionNodeIDs, 1106 "fixed-execution-node-ids", 1107 defaultConfig.rpcConf.BackendConfig.FixedExecutionNodeIDs, 1108 "comma separated list of execution nodes ids to choose from when making an upstream call if no matching preferred execution id is found e.g. b4a4dbdcd443d...,fb386a6a... etc.") 1109 flags.StringVar(&builder.rpcConf.CompressorName, 1110 "grpc-compressor", 1111 defaultConfig.rpcConf.CompressorName, 1112 "name of grpc compressor that will be used for requests to other nodes. One of (gzip, snappy, deflate)") 1113 flags.BoolVar(&builder.logTxTimeToFinalized, "log-tx-time-to-finalized", defaultConfig.logTxTimeToFinalized, "log transaction time to finalized") 1114 flags.BoolVar(&builder.logTxTimeToExecuted, "log-tx-time-to-executed", defaultConfig.logTxTimeToExecuted, "log transaction time to executed") 1115 flags.BoolVar(&builder.logTxTimeToFinalizedExecuted, 1116 "log-tx-time-to-finalized-executed", 1117 defaultConfig.logTxTimeToFinalizedExecuted, 1118 "log transaction time to finalized and executed") 1119 flags.BoolVar(&builder.pingEnabled, 1120 "ping-enabled", 1121 defaultConfig.pingEnabled, 1122 "whether to enable the ping process that pings all other peers and report the connectivity to metrics") 1123 flags.BoolVar(&builder.retryEnabled, "retry-enabled", defaultConfig.retryEnabled, "whether to enable the retry mechanism at the access node level") 1124 flags.BoolVar(&builder.rpcMetricsEnabled, "rpc-metrics-enabled", defaultConfig.rpcMetricsEnabled, "whether to enable the rpc metrics") 1125 flags.UintVar(&builder.TxResultCacheSize, "transaction-result-cache-size", defaultConfig.TxResultCacheSize, "transaction result cache size.(Disabled by default i.e 0)") 1126 flags.UintVar(&builder.TxErrorMessagesCacheSize, "transaction-error-messages-cache-size", defaultConfig.TxErrorMessagesCacheSize, "transaction error messages cache size.(By default 1000)") 1127 flags.StringVarP(&builder.nodeInfoFile, 1128 "node-info-file", 1129 "", 1130 defaultConfig.nodeInfoFile, 1131 "full path to a json file which provides more details about nodes when reporting its reachability metrics") 1132 flags.StringToIntVar(&builder.apiRatelimits, "api-rate-limits", defaultConfig.apiRatelimits, "per second rate limits for Access API methods e.g. Ping=300,GetTransaction=500 etc.") 1133 flags.StringToIntVar(&builder.apiBurstlimits, "api-burst-limits", defaultConfig.apiBurstlimits, "burst limits for Access API methods e.g. Ping=100,GetTransaction=100 etc.") 1134 flags.BoolVar(&builder.supportsObserver, "supports-observer", defaultConfig.supportsObserver, "true if this staked access node supports observer or follower connections") 1135 flags.StringVar(&builder.PublicNetworkConfig.BindAddress, "public-network-address", defaultConfig.PublicNetworkConfig.BindAddress, "staked access node's public network bind address") 1136 flags.BoolVar(&builder.rpcConf.BackendConfig.CircuitBreakerConfig.Enabled, 1137 "circuit-breaker-enabled", 1138 defaultConfig.rpcConf.BackendConfig.CircuitBreakerConfig.Enabled, 1139 "specifies whether the circuit breaker is enabled for collection and execution API clients.") 1140 flags.DurationVar(&builder.rpcConf.BackendConfig.CircuitBreakerConfig.RestoreTimeout, 1141 "circuit-breaker-restore-timeout", 1142 defaultConfig.rpcConf.BackendConfig.CircuitBreakerConfig.RestoreTimeout, 1143 "duration after which the circuit breaker will restore the connection to the client after closing it due to failures. Default value is 60s") 1144 flags.Uint32Var(&builder.rpcConf.BackendConfig.CircuitBreakerConfig.MaxFailures, 1145 "circuit-breaker-max-failures", 1146 defaultConfig.rpcConf.BackendConfig.CircuitBreakerConfig.MaxFailures, 1147 "maximum number of failed calls to the client that will cause the circuit breaker to close the connection. Default value is 5") 1148 flags.Uint32Var(&builder.rpcConf.BackendConfig.CircuitBreakerConfig.MaxRequests, 1149 "circuit-breaker-max-requests", 1150 defaultConfig.rpcConf.BackendConfig.CircuitBreakerConfig.MaxRequests, 1151 "maximum number of requests to check if connection restored after timeout. Default value is 1") 1152 // ExecutionDataRequester config 1153 flags.BoolVar(&builder.executionDataSyncEnabled, 1154 "execution-data-sync-enabled", 1155 defaultConfig.executionDataSyncEnabled, 1156 "whether to enable the execution data sync protocol") 1157 flags.BoolVar(&builder.publicNetworkExecutionDataEnabled, 1158 "public-network-execution-data-sync-enabled", 1159 defaultConfig.publicNetworkExecutionDataEnabled, 1160 "[experimental] whether to enable the execution data sync protocol on public network") 1161 flags.StringVar(&builder.executionDataDir, "execution-data-dir", defaultConfig.executionDataDir, "directory to use for Execution Data database") 1162 flags.Uint64Var(&builder.executionDataStartHeight, 1163 "execution-data-start-height", 1164 defaultConfig.executionDataStartHeight, 1165 "height of first block to sync execution data from when starting with an empty Execution Data database") 1166 flags.Uint64Var(&builder.executionDataConfig.MaxSearchAhead, 1167 "execution-data-max-search-ahead", 1168 defaultConfig.executionDataConfig.MaxSearchAhead, 1169 "max number of heights to search ahead of the lowest outstanding execution data height") 1170 flags.DurationVar(&builder.executionDataConfig.FetchTimeout, 1171 "execution-data-fetch-timeout", 1172 defaultConfig.executionDataConfig.FetchTimeout, 1173 "initial timeout to use when fetching execution data from the network. timeout increases using an incremental backoff until execution-data-max-fetch-timeout. e.g. 30s") 1174 flags.DurationVar(&builder.executionDataConfig.MaxFetchTimeout, 1175 "execution-data-max-fetch-timeout", 1176 defaultConfig.executionDataConfig.MaxFetchTimeout, 1177 "maximum timeout to use when fetching execution data from the network e.g. 300s") 1178 flags.DurationVar(&builder.executionDataConfig.RetryDelay, 1179 "execution-data-retry-delay", 1180 defaultConfig.executionDataConfig.RetryDelay, 1181 "initial delay for exponential backoff when fetching execution data fails e.g. 10s") 1182 flags.DurationVar(&builder.executionDataConfig.MaxRetryDelay, 1183 "execution-data-max-retry-delay", 1184 defaultConfig.executionDataConfig.MaxRetryDelay, 1185 "maximum delay for exponential backoff when fetching execution data fails e.g. 5m") 1186 1187 // Execution State Streaming API 1188 flags.Uint32Var(&builder.stateStreamConf.ExecutionDataCacheSize, "execution-data-cache-size", defaultConfig.stateStreamConf.ExecutionDataCacheSize, "block execution data cache size") 1189 flags.Uint32Var(&builder.stateStreamConf.MaxGlobalStreams, "state-stream-global-max-streams", defaultConfig.stateStreamConf.MaxGlobalStreams, "global maximum number of concurrent streams") 1190 flags.UintVar(&builder.stateStreamConf.MaxExecutionDataMsgSize, 1191 "state-stream-max-message-size", 1192 defaultConfig.stateStreamConf.MaxExecutionDataMsgSize, 1193 "maximum size for a gRPC message containing block execution data") 1194 flags.StringToIntVar(&builder.stateStreamFilterConf, 1195 "state-stream-event-filter-limits", 1196 defaultConfig.stateStreamFilterConf, 1197 "event filter limits for ExecutionData SubscribeEvents API e.g. EventTypes=100,Addresses=100,Contracts=100 etc.") 1198 flags.DurationVar(&builder.stateStreamConf.ClientSendTimeout, 1199 "state-stream-send-timeout", 1200 defaultConfig.stateStreamConf.ClientSendTimeout, 1201 "maximum wait before timing out while sending a response to a streaming client e.g. 30s") 1202 flags.UintVar(&builder.stateStreamConf.ClientSendBufferSize, 1203 "state-stream-send-buffer-size", 1204 defaultConfig.stateStreamConf.ClientSendBufferSize, 1205 "maximum number of responses to buffer within a stream") 1206 flags.Float64Var(&builder.stateStreamConf.ResponseLimit, 1207 "state-stream-response-limit", 1208 defaultConfig.stateStreamConf.ResponseLimit, 1209 "max number of responses per second to send over streaming endpoints. this helps manage resources consumed by each client querying data not in the cache e.g. 3 or 0.5. 0 means no limit") 1210 flags.Uint64Var(&builder.stateStreamConf.HeartbeatInterval, 1211 "state-stream-heartbeat-interval", 1212 defaultConfig.stateStreamConf.HeartbeatInterval, 1213 "default interval in blocks at which heartbeat messages should be sent. applied when client did not specify a value.") 1214 flags.Uint32Var(&builder.stateStreamConf.RegisterIDsRequestLimit, 1215 "state-stream-max-register-values", 1216 defaultConfig.stateStreamConf.RegisterIDsRequestLimit, 1217 "maximum number of register ids to include in a single request to the GetRegisters endpoint") 1218 1219 // Execution Data Indexer 1220 flags.BoolVar(&builder.executionDataIndexingEnabled, 1221 "execution-data-indexing-enabled", 1222 defaultConfig.executionDataIndexingEnabled, 1223 "whether to enable the execution data indexing") 1224 flags.StringVar(&builder.registersDBPath, "execution-state-dir", defaultConfig.registersDBPath, "directory to use for execution-state database") 1225 flags.StringVar(&builder.checkpointFile, "execution-state-checkpoint", defaultConfig.checkpointFile, "execution-state checkpoint file") 1226 1227 flags.StringVar(&builder.rpcConf.BackendConfig.EventQueryMode, 1228 "event-query-mode", 1229 defaultConfig.rpcConf.BackendConfig.EventQueryMode, 1230 "mode to use when querying events. one of [local-only, execution-nodes-only(default), failover]") 1231 1232 flags.StringVar(&builder.rpcConf.BackendConfig.TxResultQueryMode, 1233 "tx-result-query-mode", 1234 defaultConfig.rpcConf.BackendConfig.TxResultQueryMode, 1235 "mode to use when querying transaction results. one of [local-only, execution-nodes-only(default), failover]") 1236 1237 // Script Execution 1238 flags.StringVar(&builder.rpcConf.BackendConfig.ScriptExecutionMode, 1239 "script-execution-mode", 1240 defaultConfig.rpcConf.BackendConfig.ScriptExecutionMode, 1241 "mode to use when executing scripts. one of (local-only, execution-nodes-only, failover, compare)") 1242 flags.Uint64Var(&builder.scriptExecutorConfig.ComputationLimit, 1243 "script-execution-computation-limit", 1244 defaultConfig.scriptExecutorConfig.ComputationLimit, 1245 "maximum number of computation units a locally executed script can use. default: 100000") 1246 flags.IntVar(&builder.scriptExecutorConfig.MaxErrorMessageSize, 1247 "script-execution-max-error-length", 1248 defaultConfig.scriptExecutorConfig.MaxErrorMessageSize, 1249 "maximum number characters to include in error message strings. additional characters are truncated. default: 1000") 1250 flags.DurationVar(&builder.scriptExecutorConfig.LogTimeThreshold, 1251 "script-execution-log-time-threshold", 1252 defaultConfig.scriptExecutorConfig.LogTimeThreshold, 1253 "emit a log for any scripts that take over this threshold. default: 1s") 1254 flags.DurationVar(&builder.scriptExecutorConfig.ExecutionTimeLimit, 1255 "script-execution-timeout", 1256 defaultConfig.scriptExecutorConfig.ExecutionTimeLimit, 1257 "timeout value for locally executed scripts. default: 10s") 1258 flags.Uint64Var(&builder.scriptExecMinBlock, 1259 "script-execution-min-height", 1260 defaultConfig.scriptExecMinBlock, 1261 "lowest block height to allow for script execution. default: no limit") 1262 flags.Uint64Var(&builder.scriptExecMaxBlock, 1263 "script-execution-max-height", 1264 defaultConfig.scriptExecMaxBlock, 1265 "highest block height to allow for script execution. default: no limit") 1266 flags.StringVar(&builder.registerCacheType, 1267 "register-cache-type", 1268 defaultConfig.registerCacheType, 1269 "type of backend cache to use for registers (lru, arc, 2q)") 1270 flags.UintVar(&builder.registerCacheSize, 1271 "register-cache-size", 1272 defaultConfig.registerCacheSize, 1273 "number of registers to cache for script execution. default: 0 (no cache)") 1274 flags.UintVar(&builder.programCacheSize, 1275 "program-cache-size", 1276 defaultConfig.programCacheSize, 1277 "[experimental] number of blocks to cache for cadence programs. use 0 to disable cache. default: 0. Note: this is an experimental feature and may cause nodes to become unstable under certain workloads. Use with caution.") 1278 }).ValidateFlags(func() error { 1279 if builder.supportsObserver && (builder.PublicNetworkConfig.BindAddress == cmd.NotSet || builder.PublicNetworkConfig.BindAddress == "") { 1280 return errors.New("public-network-address must be set if supports-observer is true") 1281 } 1282 if builder.executionDataSyncEnabled { 1283 if builder.executionDataConfig.FetchTimeout <= 0 { 1284 return errors.New("execution-data-fetch-timeout must be greater than 0") 1285 } 1286 if builder.executionDataConfig.MaxFetchTimeout < builder.executionDataConfig.FetchTimeout { 1287 return errors.New("execution-data-max-fetch-timeout must be greater than execution-data-fetch-timeout") 1288 } 1289 if builder.executionDataConfig.RetryDelay <= 0 { 1290 return errors.New("execution-data-retry-delay must be greater than 0") 1291 } 1292 if builder.executionDataConfig.MaxRetryDelay < builder.executionDataConfig.RetryDelay { 1293 return errors.New("execution-data-max-retry-delay must be greater than or equal to execution-data-retry-delay") 1294 } 1295 if builder.executionDataConfig.MaxSearchAhead == 0 { 1296 return errors.New("execution-data-max-search-ahead must be greater than 0") 1297 } 1298 } 1299 if builder.stateStreamConf.ListenAddr != "" { 1300 if builder.stateStreamConf.ExecutionDataCacheSize == 0 { 1301 return errors.New("execution-data-cache-size must be greater than 0") 1302 } 1303 if builder.stateStreamConf.ClientSendBufferSize == 0 { 1304 return errors.New("state-stream-send-buffer-size must be greater than 0") 1305 } 1306 if len(builder.stateStreamFilterConf) > 4 { 1307 return errors.New("state-stream-event-filter-limits must have at most 3 keys (EventTypes, Addresses, Contracts, AccountAddresses)") 1308 } 1309 for key, value := range builder.stateStreamFilterConf { 1310 switch key { 1311 case "EventTypes", "Addresses", "Contracts", "AccountAddresses": 1312 if value <= 0 { 1313 return fmt.Errorf("state-stream-event-filter-limits %s must be greater than 0", key) 1314 } 1315 default: 1316 return errors.New("state-stream-event-filter-limits may only contain the keys EventTypes, Addresses, Contracts, AccountAddresses") 1317 } 1318 } 1319 if builder.stateStreamConf.ResponseLimit < 0 { 1320 return errors.New("state-stream-response-limit must be greater than or equal to 0") 1321 } 1322 if builder.stateStreamConf.RegisterIDsRequestLimit <= 0 { 1323 return errors.New("state-stream-max-register-values must be greater than 0") 1324 } 1325 } 1326 if builder.rpcConf.BackendConfig.CircuitBreakerConfig.Enabled { 1327 if builder.rpcConf.BackendConfig.CircuitBreakerConfig.MaxFailures == 0 { 1328 return errors.New("circuit-breaker-max-failures must be greater than 0") 1329 } 1330 if builder.rpcConf.BackendConfig.CircuitBreakerConfig.MaxRequests == 0 { 1331 return errors.New("circuit-breaker-max-requests must be greater than 0") 1332 } 1333 if builder.rpcConf.BackendConfig.CircuitBreakerConfig.RestoreTimeout <= 0 { 1334 return errors.New("circuit-breaker-restore-timeout must be greater than 0") 1335 } 1336 } 1337 if builder.TxErrorMessagesCacheSize == 0 { 1338 return errors.New("transaction-error-messages-cache-size must be greater than 0") 1339 } 1340 1341 return nil 1342 }) 1343 } 1344 1345 func publicNetworkMsgValidators(log zerolog.Logger, idProvider module.IdentityProvider, selfID flow.Identifier) []network.MessageValidator { 1346 return []network.MessageValidator{ 1347 // filter out messages sent by this node itself 1348 validator.ValidateNotSender(selfID), 1349 validator.NewAnyValidator( 1350 // message should be either from a valid staked node 1351 validator.NewOriginValidator( 1352 id.NewIdentityFilterIdentifierProvider(filter.IsValidCurrentEpochParticipant, idProvider), 1353 ), 1354 // or the message should be specifically targeted for this node 1355 validator.ValidateTarget(log, selfID), 1356 ), 1357 } 1358 } 1359 1360 func (builder *FlowAccessNodeBuilder) InitIDProviders() { 1361 builder.Module("id providers", func(node *cmd.NodeConfig) error { 1362 idCache, err := cache.NewProtocolStateIDCache(node.Logger, node.State, node.ProtocolEvents) 1363 if err != nil { 1364 return fmt.Errorf("could not initialize ProtocolStateIDCache: %w", err) 1365 } 1366 builder.IDTranslator = translator.NewHierarchicalIDTranslator(idCache, translator.NewPublicNetworkIDTranslator()) 1367 1368 // The following wrapper allows to disallow-list byzantine nodes via an admin command: 1369 // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true 1370 disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { 1371 return builder.NetworkUnderlay 1372 }) 1373 if err != nil { 1374 return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) 1375 } 1376 builder.IdentityProvider = disallowListWrapper 1377 1378 // register the wrapper for dynamic configuration via admin command 1379 err = node.ConfigManager.RegisterIdentifierListConfig("network-id-provider-blocklist", 1380 disallowListWrapper.GetDisallowList, disallowListWrapper.Update) 1381 if err != nil { 1382 return fmt.Errorf("failed to register disallow-list wrapper with config manager: %w", err) 1383 } 1384 1385 builder.SyncEngineParticipantsProviderFactory = func() module.IdentifierProvider { 1386 return id.NewIdentityFilterIdentifierProvider( 1387 filter.And( 1388 filter.HasRole[flow.Identity](flow.RoleConsensus), 1389 filter.Not(filter.HasNodeID[flow.Identity](node.Me.NodeID())), 1390 filter.NotEjectedFilter, 1391 ), 1392 builder.IdentityProvider, 1393 ) 1394 } 1395 return nil 1396 }) 1397 } 1398 1399 func (builder *FlowAccessNodeBuilder) Initialize() error { 1400 builder.InitIDProviders() 1401 1402 builder.EnqueueResolver() 1403 1404 // enqueue the regular network 1405 builder.EnqueueNetworkInit() 1406 1407 builder.AdminCommand("get-transactions", func(conf *cmd.NodeConfig) commands.AdminCommand { 1408 return storageCommands.NewGetTransactionsCommand(conf.State, conf.Storage.Payloads, conf.Storage.Collections) 1409 }) 1410 1411 // if this is an access node that supports public followers, enqueue the public network 1412 if builder.supportsObserver { 1413 builder.enqueuePublicNetworkInit() 1414 builder.enqueueRelayNetwork() 1415 } 1416 1417 builder.EnqueuePingService() 1418 1419 builder.EnqueueMetricsServerInit() 1420 1421 if err := builder.RegisterBadgerMetrics(); err != nil { 1422 return err 1423 } 1424 1425 builder.EnqueueTracer() 1426 builder.PreInit(cmd.DynamicStartPreInit) 1427 builder.ValidateRootSnapshot(badgerState.ValidRootSnapshotContainsEntityExpiryRange) 1428 1429 return nil 1430 } 1431 1432 func (builder *FlowAccessNodeBuilder) enqueueRelayNetwork() { 1433 builder.Component("relay network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1434 relayNet := relaynet.NewRelayNetwork( 1435 node.EngineRegistry, 1436 builder.AccessNodeConfig.PublicNetworkConfig.Network, 1437 node.Logger, 1438 map[channels.Channel]channels.Channel{ 1439 channels.ReceiveBlocks: channels.PublicReceiveBlocks, 1440 }, 1441 ) 1442 node.EngineRegistry = relayNet 1443 return relayNet, nil 1444 }) 1445 } 1446 1447 func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { 1448 var processedBlockHeight storage.ConsumerProgress 1449 1450 if builder.executionDataSyncEnabled { 1451 builder.BuildExecutionSyncComponents() 1452 } 1453 1454 ingestionDependable := module.NewProxiedReadyDoneAware() 1455 builder.IndexerDependencies.Add(ingestionDependable) 1456 var lastFullBlockHeight *counters.PersistentStrictMonotonicCounter 1457 1458 builder. 1459 BuildConsensusFollower(). 1460 Module("collection node client", func(node *cmd.NodeConfig) error { 1461 // collection node address is optional (if not specified, collection nodes will be chosen at random) 1462 if strings.TrimSpace(builder.rpcConf.CollectionAddr) == "" { 1463 node.Logger.Info().Msg("using a dynamic collection node address") 1464 return nil 1465 } 1466 1467 node.Logger.Info(). 1468 Str("collection_node", builder.rpcConf.CollectionAddr). 1469 Msg("using the static collection node address") 1470 1471 collectionRPCConn, err := grpc.Dial( 1472 builder.rpcConf.CollectionAddr, 1473 grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(builder.rpcConf.MaxMsgSize))), 1474 grpc.WithTransportCredentials(insecure.NewCredentials()), 1475 rpcConnection.WithClientTimeoutOption(builder.rpcConf.BackendConfig.CollectionClientTimeout)) 1476 if err != nil { 1477 return err 1478 } 1479 builder.CollectionRPC = access.NewAccessAPIClient(collectionRPCConn) 1480 return nil 1481 }). 1482 Module("historical access node clients", func(node *cmd.NodeConfig) error { 1483 addrs := strings.Split(builder.rpcConf.HistoricalAccessAddrs, ",") 1484 for _, addr := range addrs { 1485 if strings.TrimSpace(addr) == "" { 1486 continue 1487 } 1488 node.Logger.Info().Str("access_nodes", addr).Msg("historical access node addresses") 1489 1490 historicalAccessRPCConn, err := grpc.Dial( 1491 addr, 1492 grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(builder.rpcConf.MaxMsgSize))), 1493 grpc.WithTransportCredentials(insecure.NewCredentials())) 1494 if err != nil { 1495 return err 1496 } 1497 builder.HistoricalAccessRPCs = append(builder.HistoricalAccessRPCs, access.NewAccessAPIClient(historicalAccessRPCConn)) 1498 } 1499 return nil 1500 }). 1501 Module("transaction timing mempools", func(node *cmd.NodeConfig) error { 1502 var err error 1503 builder.TransactionTimings, err = stdmap.NewTransactionTimings(1500 * 300) // assume 1500 TPS * 300 seconds 1504 if err != nil { 1505 return err 1506 } 1507 1508 builder.CollectionsToMarkFinalized, err = stdmap.NewTimes(50 * 300) // assume 50 collection nodes * 300 seconds 1509 if err != nil { 1510 return err 1511 } 1512 1513 builder.CollectionsToMarkExecuted, err = stdmap.NewTimes(50 * 300) // assume 50 collection nodes * 300 seconds 1514 if err != nil { 1515 return err 1516 } 1517 1518 builder.BlocksToMarkExecuted, err = stdmap.NewTimes(1 * 300) // assume 1 block per second * 300 seconds 1519 1520 return err 1521 }). 1522 Module("transaction metrics", func(node *cmd.NodeConfig) error { 1523 builder.TransactionMetrics = metrics.NewTransactionCollector( 1524 node.Logger, 1525 builder.TransactionTimings, 1526 builder.logTxTimeToFinalized, 1527 builder.logTxTimeToExecuted, 1528 builder.logTxTimeToFinalizedExecuted, 1529 ) 1530 return nil 1531 }). 1532 Module("rest metrics", func(node *cmd.NodeConfig) error { 1533 m, err := metrics.NewRestCollector(routes.URLToRoute, node.MetricsRegisterer) 1534 if err != nil { 1535 return err 1536 } 1537 builder.RestMetrics = m 1538 return nil 1539 }). 1540 Module("access metrics", func(node *cmd.NodeConfig) error { 1541 builder.AccessMetrics = metrics.NewAccessCollector( 1542 metrics.WithTransactionMetrics(builder.TransactionMetrics), 1543 metrics.WithBackendScriptsMetrics(builder.TransactionMetrics), 1544 metrics.WithRestMetrics(builder.RestMetrics), 1545 ) 1546 return nil 1547 }). 1548 Module("collection metrics", func(node *cmd.NodeConfig) error { 1549 var err error 1550 builder.collectionExecutedMetric, err = indexer.NewCollectionExecutedMetricImpl( 1551 builder.Logger, 1552 builder.AccessMetrics, 1553 builder.CollectionsToMarkFinalized, 1554 builder.CollectionsToMarkExecuted, 1555 builder.BlocksToMarkExecuted, 1556 builder.Storage.Collections, 1557 builder.Storage.Blocks, 1558 ) 1559 if err != nil { 1560 return err 1561 } 1562 1563 return nil 1564 }). 1565 Module("ping metrics", func(node *cmd.NodeConfig) error { 1566 builder.PingMetrics = metrics.NewPingCollector() 1567 return nil 1568 }). 1569 Module("server certificate", func(node *cmd.NodeConfig) error { 1570 // generate the server certificate that will be served by the GRPC server 1571 x509Certificate, err := grpcutils.X509Certificate(node.NetworkKey) 1572 if err != nil { 1573 return err 1574 } 1575 tlsConfig := grpcutils.DefaultServerTLSConfig(x509Certificate) 1576 builder.rpcConf.TransportCredentials = credentials.NewTLS(tlsConfig) 1577 return nil 1578 }). 1579 Module("creating grpc servers", func(node *cmd.NodeConfig) error { 1580 builder.secureGrpcServer = grpcserver.NewGrpcServerBuilder( 1581 node.Logger, 1582 builder.rpcConf.SecureGRPCListenAddr, 1583 builder.rpcConf.MaxMsgSize, 1584 builder.rpcMetricsEnabled, 1585 builder.apiRatelimits, 1586 builder.apiBurstlimits, 1587 grpcserver.WithTransportCredentials(builder.rpcConf.TransportCredentials)).Build() 1588 1589 builder.stateStreamGrpcServer = grpcserver.NewGrpcServerBuilder( 1590 node.Logger, 1591 builder.stateStreamConf.ListenAddr, 1592 builder.stateStreamConf.MaxExecutionDataMsgSize, 1593 builder.rpcMetricsEnabled, 1594 builder.apiRatelimits, 1595 builder.apiBurstlimits, 1596 grpcserver.WithStreamInterceptor()).Build() 1597 1598 if builder.rpcConf.UnsecureGRPCListenAddr != builder.stateStreamConf.ListenAddr { 1599 builder.unsecureGrpcServer = grpcserver.NewGrpcServerBuilder(node.Logger, 1600 builder.rpcConf.UnsecureGRPCListenAddr, 1601 builder.rpcConf.MaxMsgSize, 1602 builder.rpcMetricsEnabled, 1603 builder.apiRatelimits, 1604 builder.apiBurstlimits).Build() 1605 } else { 1606 builder.unsecureGrpcServer = builder.stateStreamGrpcServer 1607 } 1608 1609 return nil 1610 }). 1611 Module("backend script executor", func(node *cmd.NodeConfig) error { 1612 builder.ScriptExecutor = backend.NewScriptExecutor(builder.Logger, builder.scriptExecMinBlock, builder.scriptExecMaxBlock) 1613 return nil 1614 }). 1615 Module("async register store", func(node *cmd.NodeConfig) error { 1616 builder.RegistersAsyncStore = execution.NewRegistersAsyncStore() 1617 return nil 1618 }). 1619 Module("events storage", func(node *cmd.NodeConfig) error { 1620 builder.Storage.Events = bstorage.NewEvents(node.Metrics.Cache, node.DB) 1621 return nil 1622 }). 1623 Module("events index", func(node *cmd.NodeConfig) error { 1624 builder.EventsIndex = index.NewEventsIndex(builder.Storage.Events) 1625 return nil 1626 }). 1627 Module("transaction result index", func(node *cmd.NodeConfig) error { 1628 builder.TxResultsIndex = index.NewTransactionResultsIndex(builder.Storage.LightTransactionResults) 1629 return nil 1630 }). 1631 Module("processed block height consumer progress", func(node *cmd.NodeConfig) error { 1632 processedBlockHeight = bstorage.NewConsumerProgress(builder.DB, module.ConsumeProgressIngestionEngineBlockHeight) 1633 return nil 1634 }). 1635 Module("processed last full block height monotonic consumer progress", func(node *cmd.NodeConfig) error { 1636 rootBlockHeight := node.State.Params().FinalizedRoot().Height 1637 1638 var err error 1639 lastFullBlockHeight, err = counters.NewPersistentStrictMonotonicCounter( 1640 bstorage.NewConsumerProgress(builder.DB, module.ConsumeProgressLastFullBlockHeight), 1641 rootBlockHeight, 1642 ) 1643 if err != nil { 1644 return fmt.Errorf("failed to initialize monotonic consumer progress: %w", err) 1645 } 1646 1647 return nil 1648 }). 1649 Component("RPC engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1650 config := builder.rpcConf 1651 backendConfig := config.BackendConfig 1652 accessMetrics := builder.AccessMetrics 1653 cacheSize := int(backendConfig.ConnectionPoolSize) 1654 1655 var connBackendCache *rpcConnection.Cache 1656 var err error 1657 if cacheSize > 0 { 1658 connBackendCache, err = rpcConnection.NewCache(node.Logger, accessMetrics, cacheSize) 1659 if err != nil { 1660 return nil, fmt.Errorf("could not initialize connection cache: %w", err) 1661 } 1662 } 1663 1664 connFactory := &rpcConnection.ConnectionFactoryImpl{ 1665 CollectionGRPCPort: builder.collectionGRPCPort, 1666 ExecutionGRPCPort: builder.executionGRPCPort, 1667 CollectionNodeGRPCTimeout: backendConfig.CollectionClientTimeout, 1668 ExecutionNodeGRPCTimeout: backendConfig.ExecutionClientTimeout, 1669 AccessMetrics: accessMetrics, 1670 Log: node.Logger, 1671 Manager: rpcConnection.NewManager( 1672 node.Logger, 1673 accessMetrics, 1674 connBackendCache, 1675 config.MaxMsgSize, 1676 backendConfig.CircuitBreakerConfig, 1677 config.CompressorName, 1678 ), 1679 } 1680 1681 scriptExecMode, err := backend.ParseIndexQueryMode(config.BackendConfig.ScriptExecutionMode) 1682 if err != nil { 1683 return nil, fmt.Errorf("could not parse script execution mode: %w", err) 1684 } 1685 1686 eventQueryMode, err := backend.ParseIndexQueryMode(config.BackendConfig.EventQueryMode) 1687 if err != nil { 1688 return nil, fmt.Errorf("could not parse event query mode: %w", err) 1689 } 1690 if eventQueryMode == backend.IndexQueryModeCompare { 1691 return nil, fmt.Errorf("event query mode 'compare' is not supported") 1692 } 1693 1694 broadcaster := engine.NewBroadcaster() 1695 // create BlockTracker that will track for new blocks (finalized and sealed) and 1696 // handles block-related operations. 1697 blockTracker, err := subscription.NewBlockTracker( 1698 node.State, 1699 builder.FinalizedRootBlock.Header.Height, 1700 node.Storage.Headers, 1701 broadcaster, 1702 ) 1703 if err != nil { 1704 return nil, fmt.Errorf("failed to initialize block tracker: %w", err) 1705 } 1706 txResultQueryMode, err := backend.ParseIndexQueryMode(config.BackendConfig.TxResultQueryMode) 1707 if err != nil { 1708 return nil, fmt.Errorf("could not parse transaction result query mode: %w", err) 1709 } 1710 if txResultQueryMode == backend.IndexQueryModeCompare { 1711 return nil, fmt.Errorf("transaction result query mode 'compare' is not supported") 1712 } 1713 1714 nodeBackend, err := backend.New(backend.Params{ 1715 State: node.State, 1716 CollectionRPC: builder.CollectionRPC, 1717 HistoricalAccessNodes: builder.HistoricalAccessRPCs, 1718 Blocks: node.Storage.Blocks, 1719 Headers: node.Storage.Headers, 1720 Collections: node.Storage.Collections, 1721 Transactions: node.Storage.Transactions, 1722 ExecutionReceipts: node.Storage.Receipts, 1723 ExecutionResults: node.Storage.Results, 1724 ChainID: node.RootChainID, 1725 AccessMetrics: builder.AccessMetrics, 1726 ConnFactory: connFactory, 1727 RetryEnabled: builder.retryEnabled, 1728 MaxHeightRange: backendConfig.MaxHeightRange, 1729 PreferredExecutionNodeIDs: backendConfig.PreferredExecutionNodeIDs, 1730 FixedExecutionNodeIDs: backendConfig.FixedExecutionNodeIDs, 1731 Log: node.Logger, 1732 SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, 1733 Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), 1734 TxResultCacheSize: builder.TxResultCacheSize, 1735 TxErrorMessagesCacheSize: builder.TxErrorMessagesCacheSize, 1736 ScriptExecutor: builder.ScriptExecutor, 1737 ScriptExecutionMode: scriptExecMode, 1738 EventQueryMode: eventQueryMode, 1739 BlockTracker: blockTracker, 1740 SubscriptionHandler: subscription.NewSubscriptionHandler( 1741 builder.Logger, 1742 broadcaster, 1743 builder.stateStreamConf.ClientSendTimeout, 1744 builder.stateStreamConf.ResponseLimit, 1745 builder.stateStreamConf.ClientSendBufferSize, 1746 ), 1747 EventsIndex: builder.EventsIndex, 1748 TxResultQueryMode: txResultQueryMode, 1749 TxResultsIndex: builder.TxResultsIndex, 1750 LastFullBlockHeight: lastFullBlockHeight, 1751 }) 1752 if err != nil { 1753 return nil, fmt.Errorf("could not initialize backend: %w", err) 1754 } 1755 1756 engineBuilder, err := rpc.NewBuilder( 1757 node.Logger, 1758 node.State, 1759 config, 1760 node.RootChainID, 1761 builder.AccessMetrics, 1762 builder.rpcMetricsEnabled, 1763 builder.Me, 1764 nodeBackend, 1765 nodeBackend, 1766 builder.secureGrpcServer, 1767 builder.unsecureGrpcServer, 1768 builder.stateStreamBackend, 1769 builder.stateStreamConf, 1770 ) 1771 if err != nil { 1772 return nil, err 1773 } 1774 1775 builder.RpcEng, err = engineBuilder. 1776 WithLegacy(). 1777 WithBlockSignerDecoder(signature.NewBlockSignerDecoder(builder.Committee)). 1778 Build() 1779 if err != nil { 1780 return nil, err 1781 } 1782 builder.FollowerDistributor.AddOnBlockFinalizedConsumer(builder.RpcEng.OnFinalizedBlock) 1783 1784 return builder.RpcEng, nil 1785 }). 1786 Component("ingestion engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1787 var err error 1788 1789 builder.RequestEng, err = requester.New( 1790 node.Logger, 1791 node.Metrics.Engine, 1792 node.EngineRegistry, 1793 node.Me, 1794 node.State, 1795 channels.RequestCollections, 1796 filter.HasRole[flow.Identity](flow.RoleCollection), 1797 func() flow.Entity { return &flow.Collection{} }, 1798 ) 1799 if err != nil { 1800 return nil, fmt.Errorf("could not create requester engine: %w", err) 1801 } 1802 1803 builder.IngestEng, err = ingestion.New( 1804 node.Logger, 1805 node.EngineRegistry, 1806 node.State, 1807 node.Me, 1808 builder.RequestEng, 1809 node.Storage.Blocks, 1810 node.Storage.Headers, 1811 node.Storage.Collections, 1812 node.Storage.Transactions, 1813 node.Storage.Results, 1814 node.Storage.Receipts, 1815 builder.collectionExecutedMetric, 1816 processedBlockHeight, 1817 lastFullBlockHeight, 1818 ) 1819 if err != nil { 1820 return nil, err 1821 } 1822 ingestionDependable.Init(builder.IngestEng) 1823 builder.RequestEng.WithHandle(builder.IngestEng.OnCollection) 1824 builder.FollowerDistributor.AddOnBlockFinalizedConsumer(builder.IngestEng.OnFinalizedBlock) 1825 1826 return builder.IngestEng, nil 1827 }). 1828 Component("requester engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1829 // We initialize the requester engine inside the ingestion engine due to the mutual dependency. However, in 1830 // order for it to properly start and shut down, we should still return it as its own engine here, so it can 1831 // be handled by the scaffold. 1832 return builder.RequestEng, nil 1833 }) 1834 1835 if builder.supportsObserver { 1836 builder.Component("public sync request handler", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1837 syncRequestHandler, err := synceng.NewRequestHandlerEngine( 1838 node.Logger.With().Bool("public", true).Logger(), 1839 unstaked.NewUnstakedEngineCollector(node.Metrics.Engine), 1840 builder.AccessNodeConfig.PublicNetworkConfig.Network, 1841 node.Me, 1842 node.State, 1843 node.Storage.Blocks, 1844 builder.SyncCore, 1845 ) 1846 if err != nil { 1847 return nil, fmt.Errorf("could not create public sync request handler: %w", err) 1848 } 1849 builder.FollowerDistributor.AddFinalizationConsumer(syncRequestHandler) 1850 1851 return syncRequestHandler, nil 1852 }) 1853 } 1854 1855 builder.Component("secure grpc server", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1856 return builder.secureGrpcServer, nil 1857 }) 1858 1859 builder.Component("state stream unsecure grpc server", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1860 return builder.stateStreamGrpcServer, nil 1861 }) 1862 1863 if builder.rpcConf.UnsecureGRPCListenAddr != builder.stateStreamConf.ListenAddr { 1864 builder.Component("unsecure grpc server", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1865 return builder.unsecureGrpcServer, nil 1866 }) 1867 } 1868 1869 if builder.pingEnabled { 1870 builder.Component("ping engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1871 ping, err := pingeng.New( 1872 node.Logger, 1873 node.IdentityProvider, 1874 node.IDTranslator, 1875 node.Me, 1876 builder.PingMetrics, 1877 builder.nodeInfoFile, 1878 node.PingService, 1879 ) 1880 if err != nil { 1881 return nil, fmt.Errorf("could not create ping engine: %w", err) 1882 } 1883 1884 return ping, nil 1885 }) 1886 } 1887 1888 return builder.FlowNodeBuilder.Build() 1889 } 1890 1891 // enqueuePublicNetworkInit enqueues the public network component initialized for the staked node 1892 func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { 1893 var publicLibp2pNode p2p.LibP2PNode 1894 builder. 1895 Module("public network metrics", func(node *cmd.NodeConfig) error { 1896 builder.PublicNetworkConfig.Metrics = metrics.NewNetworkCollector(builder.Logger, metrics.WithNetworkPrefix("public")) 1897 return nil 1898 }). 1899 Component("public libp2p node", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1900 var err error 1901 publicLibp2pNode, err = builder.initPublicLibp2pNode( 1902 builder.NodeConfig.NetworkKey, 1903 builder.PublicNetworkConfig.BindAddress, 1904 builder.PublicNetworkConfig.Metrics) 1905 if err != nil { 1906 return nil, fmt.Errorf("could not create public libp2p node: %w", err) 1907 } 1908 1909 return publicLibp2pNode, nil 1910 }). 1911 Component("public network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1912 msgValidators := publicNetworkMsgValidators(node.Logger.With().Bool("public", true).Logger(), node.IdentityProvider, builder.NodeID) 1913 receiveCache := netcache.NewHeroReceiveCache(builder.FlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, 1914 builder.Logger, 1915 metrics.NetworkReceiveCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork)) 1916 1917 err := node.Metrics.Mempool.Register(metrics.PrependPublicPrefix(metrics.ResourceNetworkingReceiveCache), receiveCache.Size) 1918 if err != nil { 1919 return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) 1920 } 1921 1922 net, err := underlay.NewNetwork(&underlay.NetworkConfig{ 1923 Logger: builder.Logger.With().Str("module", "public-network").Logger(), 1924 Libp2pNode: publicLibp2pNode, 1925 Codec: cborcodec.NewCodec(), 1926 Me: builder.Me, 1927 Topology: topology.EmptyTopology{}, // topology returns empty list since peers are not known upfront 1928 Metrics: builder.PublicNetworkConfig.Metrics, 1929 BitSwapMetrics: builder.Metrics.Bitswap, 1930 IdentityProvider: builder.IdentityProvider, 1931 ReceiveCache: receiveCache, 1932 ConduitFactory: conduit.NewDefaultConduitFactory(), 1933 SporkId: builder.SporkID, 1934 UnicastMessageTimeout: underlay.DefaultUnicastTimeout, 1935 IdentityTranslator: builder.IDTranslator, 1936 AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ 1937 Logger: builder.Logger, 1938 SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, 1939 SpamReportQueueSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamReportQueueSize, 1940 DisablePenalty: builder.FlowConfig.NetworkConfig.AlspConfig.DisablePenalty, 1941 HeartBeatInterval: builder.FlowConfig.NetworkConfig.AlspConfig.HearBeatInterval, 1942 AlspMetrics: builder.Metrics.Network, 1943 NetworkType: network.PublicNetwork, 1944 HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), 1945 }, 1946 SlashingViolationConsumerFactory: func(adapter network.ConduitAdapter) network.ViolationsConsumer { 1947 return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) 1948 }, 1949 }, underlay.WithMessageValidators(msgValidators...)) 1950 if err != nil { 1951 return nil, fmt.Errorf("could not initialize network: %w", err) 1952 } 1953 1954 builder.NetworkUnderlay = net 1955 builder.AccessNodeConfig.PublicNetworkConfig.Network = net 1956 1957 node.Logger.Info().Msgf("network will run on address: %s", builder.PublicNetworkConfig.BindAddress) 1958 return net, nil 1959 }). 1960 Component("public peer manager", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1961 return publicLibp2pNode.PeerManagerComponent(), nil 1962 }) 1963 } 1964 1965 // initPublicLibp2pNode initializes the public libp2p node for the public (unstaked) network. 1966 // The LibP2P host is created with the following options: 1967 // - DHT as server 1968 // - The address from the node config or the specified bind address as the listen address 1969 // - The passed in private key as the libp2p key 1970 // - No connection gater 1971 // - Default Flow libp2p pubsub options 1972 // 1973 // Args: 1974 // - networkKey: The private key to use for the libp2p node 1975 // 1976 // - bindAddress: The address to bind the libp2p node to. 1977 // - networkMetrics: The metrics collector for the network 1978 // Returns: 1979 // - The libp2p node instance for the public network. 1980 // - Any error encountered during initialization. Any error should be considered fatal. 1981 func (builder *FlowAccessNodeBuilder) initPublicLibp2pNode(networkKey crypto.PrivateKey, bindAddress string, networkMetrics module.LibP2PMetrics) (p2p.LibP2PNode, 1982 error, 1983 ) { 1984 connManager, err := connection.NewConnManager(builder.Logger, networkMetrics, &builder.FlowConfig.NetworkConfig.ConnectionManager) 1985 if err != nil { 1986 return nil, fmt.Errorf("could not create connection manager: %w", err) 1987 } 1988 libp2pNode, err := p2pbuilder.NewNodeBuilder(builder.Logger, &builder.FlowConfig.NetworkConfig.GossipSub, &p2pbuilderconfig.MetricsConfig{ 1989 HeroCacheFactory: builder.HeroCacheMetricsFactory(), 1990 Metrics: networkMetrics, 1991 }, 1992 network.PublicNetwork, 1993 bindAddress, 1994 networkKey, 1995 builder.SporkID, 1996 builder.IdentityProvider, 1997 &builder.FlowConfig.NetworkConfig.ResourceManager, 1998 &p2pbuilderconfig.PeerManagerConfig{ 1999 // TODO: eventually, we need pruning enabled even on public network. However, it needs a modified version of 2000 // the peer manager that also operate on the public identities. 2001 ConnectionPruning: connection.PruningDisabled, 2002 UpdateInterval: builder.FlowConfig.NetworkConfig.PeerUpdateInterval, 2003 ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), 2004 }, 2005 &p2p.DisallowListCacheConfig{ 2006 MaxSize: builder.FlowConfig.NetworkConfig.DisallowListNotificationCacheSize, 2007 Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), 2008 }, 2009 &p2pbuilderconfig.UnicastConfig{ 2010 Unicast: builder.FlowConfig.NetworkConfig.Unicast, 2011 }). 2012 SetBasicResolver(builder.Resolver). 2013 SetSubscriptionFilter(networkingsubscription.NewRoleBasedFilter(flow.RoleAccess, builder.IdentityProvider)). 2014 SetConnectionManager(connManager). 2015 SetRoutingSystem(func(ctx context.Context, h host.Host) (routing.Routing, error) { 2016 return dht.NewDHT(ctx, h, protocols.FlowPublicDHTProtocolID(builder.SporkID), builder.Logger, networkMetrics, dht.AsServer()) 2017 }). 2018 Build() 2019 if err != nil { 2020 return nil, fmt.Errorf("could not build libp2p node for staked access node: %w", err) 2021 } 2022 2023 return libp2pNode, nil 2024 }