github.com/onflow/flow-go@v0.33.17/cmd/observer/node_builder/observer_builder.go (about) 1 package node_builder 2 3 import ( 4 "context" 5 "encoding/hex" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "os" 10 "path" 11 "path/filepath" 12 "strings" 13 "time" 14 15 "github.com/ipfs/boxo/bitswap" 16 badger "github.com/ipfs/go-ds-badger2" 17 dht "github.com/libp2p/go-libp2p-kad-dht" 18 "github.com/libp2p/go-libp2p/core/host" 19 "github.com/libp2p/go-libp2p/core/peer" 20 "github.com/libp2p/go-libp2p/core/routing" 21 "github.com/rs/zerolog" 22 "github.com/spf13/pflag" 23 24 "google.golang.org/grpc/credentials" 25 26 "github.com/onflow/flow-go/admin/commands" 27 stateSyncCommands "github.com/onflow/flow-go/admin/commands/state_synchronization" 28 "github.com/onflow/flow-go/cmd" 29 "github.com/onflow/flow-go/consensus" 30 "github.com/onflow/flow-go/consensus/hotstuff" 31 "github.com/onflow/flow-go/consensus/hotstuff/committees" 32 "github.com/onflow/flow-go/consensus/hotstuff/notifications" 33 "github.com/onflow/flow-go/consensus/hotstuff/notifications/pubsub" 34 hotsignature "github.com/onflow/flow-go/consensus/hotstuff/signature" 35 hotstuffvalidator "github.com/onflow/flow-go/consensus/hotstuff/validator" 36 "github.com/onflow/flow-go/consensus/hotstuff/verification" 37 recovery "github.com/onflow/flow-go/consensus/recovery/protocol" 38 "github.com/onflow/flow-go/crypto" 39 "github.com/onflow/flow-go/engine" 40 "github.com/onflow/flow-go/engine/access/apiproxy" 41 "github.com/onflow/flow-go/engine/access/rest" 42 restapiproxy "github.com/onflow/flow-go/engine/access/rest/apiproxy" 43 "github.com/onflow/flow-go/engine/access/rest/routes" 44 "github.com/onflow/flow-go/engine/access/rpc" 45 "github.com/onflow/flow-go/engine/access/rpc/backend" 46 rpcConnection "github.com/onflow/flow-go/engine/access/rpc/connection" 47 "github.com/onflow/flow-go/engine/access/state_stream" 48 statestreambackend "github.com/onflow/flow-go/engine/access/state_stream/backend" 49 "github.com/onflow/flow-go/engine/common/follower" 50 synceng "github.com/onflow/flow-go/engine/common/synchronization" 51 "github.com/onflow/flow-go/engine/execution/computation/query" 52 "github.com/onflow/flow-go/engine/protocol" 53 "github.com/onflow/flow-go/ledger" 54 "github.com/onflow/flow-go/ledger/complete/wal" 55 "github.com/onflow/flow-go/model/bootstrap" 56 "github.com/onflow/flow-go/model/encodable" 57 "github.com/onflow/flow-go/model/flow" 58 "github.com/onflow/flow-go/model/flow/filter" 59 "github.com/onflow/flow-go/module" 60 "github.com/onflow/flow-go/module/blobs" 61 "github.com/onflow/flow-go/module/chainsync" 62 "github.com/onflow/flow-go/module/execution" 63 "github.com/onflow/flow-go/module/executiondatasync/execution_data" 64 execdatacache "github.com/onflow/flow-go/module/executiondatasync/execution_data/cache" 65 finalizer "github.com/onflow/flow-go/module/finalizer/consensus" 66 "github.com/onflow/flow-go/module/grpcserver" 67 "github.com/onflow/flow-go/module/id" 68 "github.com/onflow/flow-go/module/local" 69 "github.com/onflow/flow-go/module/mempool/herocache" 70 "github.com/onflow/flow-go/module/metrics" 71 "github.com/onflow/flow-go/module/state_synchronization" 72 "github.com/onflow/flow-go/module/state_synchronization/indexer" 73 edrequester "github.com/onflow/flow-go/module/state_synchronization/requester" 74 consensus_follower "github.com/onflow/flow-go/module/upstream" 75 "github.com/onflow/flow-go/network" 76 alspmgr "github.com/onflow/flow-go/network/alsp/manager" 77 netcache "github.com/onflow/flow-go/network/cache" 78 "github.com/onflow/flow-go/network/channels" 79 "github.com/onflow/flow-go/network/converter" 80 "github.com/onflow/flow-go/network/p2p" 81 "github.com/onflow/flow-go/network/p2p/blob" 82 p2pbuilder "github.com/onflow/flow-go/network/p2p/builder" 83 p2pbuilderconfig "github.com/onflow/flow-go/network/p2p/builder/config" 84 "github.com/onflow/flow-go/network/p2p/cache" 85 "github.com/onflow/flow-go/network/p2p/conduit" 86 p2pdht "github.com/onflow/flow-go/network/p2p/dht" 87 "github.com/onflow/flow-go/network/p2p/keyutils" 88 p2plogging "github.com/onflow/flow-go/network/p2p/logging" 89 "github.com/onflow/flow-go/network/p2p/subscription" 90 "github.com/onflow/flow-go/network/p2p/translator" 91 "github.com/onflow/flow-go/network/p2p/unicast/protocols" 92 "github.com/onflow/flow-go/network/p2p/utils" 93 "github.com/onflow/flow-go/network/slashing" 94 "github.com/onflow/flow-go/network/underlay" 95 "github.com/onflow/flow-go/network/validator" 96 stateprotocol "github.com/onflow/flow-go/state/protocol" 97 badgerState "github.com/onflow/flow-go/state/protocol/badger" 98 "github.com/onflow/flow-go/state/protocol/blocktimer" 99 "github.com/onflow/flow-go/state/protocol/events/gadgets" 100 "github.com/onflow/flow-go/storage" 101 bstorage "github.com/onflow/flow-go/storage/badger" 102 pStorage "github.com/onflow/flow-go/storage/pebble" 103 "github.com/onflow/flow-go/utils/grpcutils" 104 "github.com/onflow/flow-go/utils/io" 105 ) 106 107 // ObserverBuilder extends cmd.NodeBuilder and declares additional functions needed to bootstrap an Access node 108 // These functions are shared by observer builders. 109 // The Staked network allows the access 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 |<----------------------->| Access Node (staked) |<------------>| All other staked Nodes | 118 // +------------------------+ +----------------------+ +------------------------+ 119 // +------------------------+ ^ 120 // | observer 3 |<--------------------------| 121 // +------------------------+ 122 123 // ObserverServiceConfig 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 ObserverServiceConfig struct { 127 bootstrapNodeAddresses []string 128 bootstrapNodePublicKeys []string 129 observerNetworkingKeyPath string 130 bootstrapIdentities flow.IdentityList // the identity list of bootstrap peers the node uses to discover other nodes 131 apiRatelimits map[string]int 132 apiBurstlimits map[string]int 133 rpcConf rpc.Config 134 rpcMetricsEnabled bool 135 registersDBPath string 136 checkpointFile string 137 apiTimeout time.Duration 138 stateStreamConf statestreambackend.Config 139 stateStreamFilterConf map[string]int 140 upstreamNodeAddresses []string 141 upstreamNodePublicKeys []string 142 upstreamIdentities flow.IdentityList // the identity list of upstream peers the node uses to forward API requests to 143 scriptExecutorConfig query.QueryConfig 144 executionDataSyncEnabled bool 145 executionDataIndexingEnabled bool 146 executionDataDir string 147 executionDataStartHeight uint64 148 executionDataConfig edrequester.ExecutionDataConfig 149 } 150 151 // DefaultObserverServiceConfig defines all the default values for the ObserverServiceConfig 152 func DefaultObserverServiceConfig() *ObserverServiceConfig { 153 homedir, _ := os.UserHomeDir() 154 return &ObserverServiceConfig{ 155 rpcConf: rpc.Config{ 156 UnsecureGRPCListenAddr: "0.0.0.0:9000", 157 SecureGRPCListenAddr: "0.0.0.0:9001", 158 HTTPListenAddr: "0.0.0.0:8000", 159 CollectionAddr: "", 160 HistoricalAccessAddrs: "", 161 BackendConfig: backend.Config{ 162 CollectionClientTimeout: 3 * time.Second, 163 ExecutionClientTimeout: 3 * time.Second, 164 ConnectionPoolSize: backend.DefaultConnectionPoolSize, 165 MaxHeightRange: backend.DefaultMaxHeightRange, 166 PreferredExecutionNodeIDs: nil, 167 FixedExecutionNodeIDs: nil, 168 }, 169 RestConfig: rest.Config{ 170 ListenAddress: "", 171 WriteTimeout: rest.DefaultWriteTimeout, 172 ReadTimeout: rest.DefaultReadTimeout, 173 IdleTimeout: rest.DefaultIdleTimeout, 174 }, 175 MaxMsgSize: grpcutils.DefaultMaxMsgSize, 176 CompressorName: grpcutils.NoCompressor, 177 }, 178 stateStreamConf: statestreambackend.Config{ 179 MaxExecutionDataMsgSize: grpcutils.DefaultMaxMsgSize, 180 ExecutionDataCacheSize: state_stream.DefaultCacheSize, 181 ClientSendTimeout: state_stream.DefaultSendTimeout, 182 ClientSendBufferSize: state_stream.DefaultSendBufferSize, 183 MaxGlobalStreams: state_stream.DefaultMaxGlobalStreams, 184 EventFilterConfig: state_stream.DefaultEventFilterConfig, 185 ResponseLimit: state_stream.DefaultResponseLimit, 186 HeartbeatInterval: state_stream.DefaultHeartbeatInterval, 187 RegisterIDsRequestLimit: state_stream.DefaultRegisterIDsRequestLimit, 188 }, 189 stateStreamFilterConf: nil, 190 rpcMetricsEnabled: false, 191 apiRatelimits: nil, 192 apiBurstlimits: nil, 193 bootstrapNodeAddresses: []string{}, 194 bootstrapNodePublicKeys: []string{}, 195 observerNetworkingKeyPath: cmd.NotSet, 196 apiTimeout: 3 * time.Second, 197 upstreamNodeAddresses: []string{}, 198 upstreamNodePublicKeys: []string{}, 199 registersDBPath: filepath.Join(homedir, ".flow", "execution_state"), 200 checkpointFile: cmd.NotSet, 201 scriptExecutorConfig: query.NewDefaultConfig(), 202 executionDataSyncEnabled: false, 203 executionDataIndexingEnabled: false, 204 executionDataDir: filepath.Join(homedir, ".flow", "execution_data"), 205 executionDataStartHeight: 0, 206 executionDataConfig: edrequester.ExecutionDataConfig{ 207 InitialBlockHeight: 0, 208 MaxSearchAhead: edrequester.DefaultMaxSearchAhead, 209 FetchTimeout: edrequester.DefaultFetchTimeout, 210 MaxFetchTimeout: edrequester.DefaultMaxFetchTimeout, 211 RetryDelay: edrequester.DefaultRetryDelay, 212 MaxRetryDelay: edrequester.DefaultMaxRetryDelay, 213 }, 214 } 215 } 216 217 // ObserverServiceBuilder provides the common functionality needed to bootstrap a Flow observer service 218 // It is composed of the FlowNodeBuilder, the ObserverServiceConfig and contains all the components and modules needed for the observers 219 type ObserverServiceBuilder struct { 220 *cmd.FlowNodeBuilder 221 *ObserverServiceConfig 222 223 // components 224 225 LibP2PNode p2p.LibP2PNode 226 FollowerState stateprotocol.FollowerState 227 SyncCore *chainsync.Core 228 RpcEng *rpc.Engine 229 FollowerDistributor *pubsub.FollowerDistributor 230 Committee hotstuff.DynamicCommittee 231 Finalized *flow.Header 232 Pending []*flow.Header 233 FollowerCore module.HotStuffFollower 234 ExecutionIndexer *indexer.Indexer 235 ExecutionIndexerCore *indexer.IndexerCore 236 IndexerDependencies *cmd.DependencyList 237 238 ExecutionDataDownloader execution_data.Downloader 239 ExecutionDataRequester state_synchronization.ExecutionDataRequester 240 ExecutionDataStore execution_data.ExecutionDataStore 241 242 RegistersAsyncStore *execution.RegistersAsyncStore 243 EventsIndex *backend.EventsIndex 244 245 // available until after the network has started. Hence, a factory function that needs to be called just before 246 // creating the sync engine 247 SyncEngineParticipantsProviderFactory func() module.IdentifierProvider 248 249 // engines 250 FollowerEng *follower.ComplianceEngine 251 SyncEng *synceng.Engine 252 StateStreamEng *statestreambackend.Engine 253 254 // Public network 255 peerID peer.ID 256 257 RestMetrics *metrics.RestCollector 258 AccessMetrics module.AccessMetrics 259 260 // grpc servers 261 secureGrpcServer *grpcserver.GrpcServer 262 unsecureGrpcServer *grpcserver.GrpcServer 263 stateStreamGrpcServer *grpcserver.GrpcServer 264 265 stateStreamBackend *statestreambackend.StateStreamBackend 266 } 267 268 // deriveBootstrapPeerIdentities derives the Flow Identity of the bootstrap peers from the parameters. 269 // These are the identities of the observers also acting as the DHT bootstrap server 270 func (builder *ObserverServiceBuilder) deriveBootstrapPeerIdentities() error { 271 // if bootstrap identities already provided (as part of alternate initialization as a library the skip reading command 272 // line params) 273 if builder.bootstrapIdentities != nil { 274 return nil 275 } 276 277 ids, err := cmd.BootstrapIdentities(builder.bootstrapNodeAddresses, builder.bootstrapNodePublicKeys) 278 if err != nil { 279 return fmt.Errorf("failed to derive bootstrap peer identities: %w", err) 280 } 281 282 builder.bootstrapIdentities = ids 283 284 return nil 285 } 286 287 // deriveBootstrapPeerIdentities derives the Flow Identity of the bootstrap peers from the parameters. 288 // These are the identities of the observers also acting as the DHT bootstrap server 289 func (builder *ObserverServiceBuilder) deriveUpstreamIdentities() error { 290 // if bootstrap identities already provided (as part of alternate initialization as a library the skip reading command 291 // line params) 292 if builder.upstreamIdentities != nil { 293 return nil 294 } 295 296 // BootstrapIdentities converts the bootstrap node addresses and keys to a Flow Identity list where 297 // each Flow Identity is initialized with the passed address, the networking key 298 // and the Node ID set to ZeroID, role set to Access, 0 stake and no staking key. 299 addresses := builder.upstreamNodeAddresses 300 keys := builder.upstreamNodePublicKeys 301 if len(addresses) != len(keys) { 302 return fmt.Errorf("number of addresses and keys provided for the boostrap nodes don't match") 303 } 304 305 ids := make([]*flow.Identity, len(addresses)) 306 for i, address := range addresses { 307 key := keys[i] 308 309 // json unmarshaller needs a quotes before and after the string 310 // the pflags.StringSliceVar does not retain quotes for the command line arg even if escaped with \" 311 // hence this additional check to ensure the key is indeed quoted 312 if !strings.HasPrefix(key, "\"") { 313 key = fmt.Sprintf("\"%s\"", key) 314 } 315 316 // create the identity of the peer by setting only the relevant fields 317 ids[i] = &flow.Identity{ 318 NodeID: flow.ZeroID, // the NodeID is the hash of the staking key and for the public network it does not apply 319 Address: address, 320 Role: flow.RoleAccess, // the upstream node has to be an access node 321 NetworkPubKey: nil, 322 } 323 324 // networking public key 325 var networkKey encodable.NetworkPubKey 326 err := json.Unmarshal([]byte(key), &networkKey) 327 if err == nil { 328 ids[i].NetworkPubKey = networkKey 329 } 330 } 331 332 builder.upstreamIdentities = ids 333 334 return nil 335 } 336 337 func (builder *ObserverServiceBuilder) buildFollowerState() *ObserverServiceBuilder { 338 builder.Module("mutable follower state", func(node *cmd.NodeConfig) error { 339 // For now, we only support state implementations from package badger. 340 // If we ever support different implementations, the following can be replaced by a type-aware factory 341 state, ok := node.State.(*badgerState.State) 342 if !ok { 343 return fmt.Errorf("only implementations of type badger.State are currently supported but read-only state has type %T", node.State) 344 } 345 346 followerState, err := badgerState.NewFollowerState( 347 node.Logger, 348 node.Tracer, 349 node.ProtocolEvents, 350 state, 351 node.Storage.Index, 352 node.Storage.Payloads, 353 blocktimer.DefaultBlockTimer, 354 ) 355 builder.FollowerState = followerState 356 357 return err 358 }) 359 360 return builder 361 } 362 363 func (builder *ObserverServiceBuilder) buildSyncCore() *ObserverServiceBuilder { 364 builder.Module("sync core", func(node *cmd.NodeConfig) error { 365 syncCore, err := chainsync.New(node.Logger, node.SyncCoreConfig, metrics.NewChainSyncCollector(node.RootChainID), node.RootChainID) 366 builder.SyncCore = syncCore 367 368 return err 369 }) 370 371 return builder 372 } 373 374 func (builder *ObserverServiceBuilder) buildCommittee() *ObserverServiceBuilder { 375 builder.Component("committee", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 376 // initialize consensus committee's membership state 377 // This committee state is for the HotStuff follower, which follows the MAIN CONSENSUS committee 378 // Note: node.Me.NodeID() is not part of the consensus committee 379 committee, err := committees.NewConsensusCommittee(node.State, node.Me.NodeID()) 380 node.ProtocolEvents.AddConsumer(committee) 381 builder.Committee = committee 382 383 return committee, err 384 }) 385 386 return builder 387 } 388 389 func (builder *ObserverServiceBuilder) buildLatestHeader() *ObserverServiceBuilder { 390 builder.Module("latest header", func(node *cmd.NodeConfig) error { 391 finalized, pending, err := recovery.FindLatest(node.State, node.Storage.Headers) 392 builder.Finalized, builder.Pending = finalized, pending 393 394 return err 395 }) 396 397 return builder 398 } 399 400 func (builder *ObserverServiceBuilder) buildFollowerCore() *ObserverServiceBuilder { 401 builder.Component("follower core", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 402 // create a finalizer that will handle updating the protocol 403 // state when the follower detects newly finalized blocks 404 final := finalizer.NewFinalizer(node.DB, node.Storage.Headers, builder.FollowerState, node.Tracer) 405 406 followerCore, err := consensus.NewFollower( 407 node.Logger, 408 node.Metrics.Mempool, 409 node.Storage.Headers, 410 final, 411 builder.FollowerDistributor, 412 node.FinalizedRootBlock.Header, 413 node.RootQC, 414 builder.Finalized, 415 builder.Pending, 416 ) 417 if err != nil { 418 return nil, fmt.Errorf("could not initialize follower core: %w", err) 419 } 420 builder.FollowerCore = followerCore 421 422 return builder.FollowerCore, nil 423 }) 424 425 return builder 426 } 427 428 func (builder *ObserverServiceBuilder) buildFollowerEngine() *ObserverServiceBuilder { 429 builder.Component("follower engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 430 var heroCacheCollector module.HeroCacheMetrics = metrics.NewNoopCollector() 431 if node.HeroCacheMetricsEnable { 432 heroCacheCollector = metrics.FollowerCacheMetrics(node.MetricsRegisterer) 433 } 434 packer := hotsignature.NewConsensusSigDataPacker(builder.Committee) 435 verifier := verification.NewCombinedVerifier(builder.Committee, packer) // verifier for HotStuff signature constructs (QCs, TCs, votes) 436 val := hotstuffvalidator.New(builder.Committee, verifier) 437 438 core, err := follower.NewComplianceCore( 439 node.Logger, 440 node.Metrics.Mempool, 441 heroCacheCollector, 442 builder.FollowerDistributor, 443 builder.FollowerState, 444 builder.FollowerCore, 445 val, 446 builder.SyncCore, 447 node.Tracer, 448 ) 449 if err != nil { 450 return nil, fmt.Errorf("could not create follower core: %w", err) 451 } 452 453 builder.FollowerEng, err = follower.NewComplianceLayer( 454 node.Logger, 455 node.EngineRegistry, 456 node.Me, 457 node.Metrics.Engine, 458 node.Storage.Headers, 459 builder.Finalized, 460 core, 461 builder.ComplianceConfig, 462 follower.WithChannel(channels.PublicReceiveBlocks), 463 ) 464 if err != nil { 465 return nil, fmt.Errorf("could not create follower engine: %w", err) 466 } 467 builder.FollowerDistributor.AddOnBlockFinalizedConsumer(builder.FollowerEng.OnFinalizedBlock) 468 469 return builder.FollowerEng, nil 470 }) 471 472 return builder 473 } 474 475 func (builder *ObserverServiceBuilder) buildSyncEngine() *ObserverServiceBuilder { 476 builder.Component("sync engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 477 spamConfig, err := synceng.NewSpamDetectionConfig() 478 if err != nil { 479 return nil, fmt.Errorf("could not initialize spam detection config: %w", err) 480 } 481 482 sync, err := synceng.New( 483 node.Logger, 484 node.Metrics.Engine, 485 node.EngineRegistry, 486 node.Me, 487 node.State, 488 node.Storage.Blocks, 489 builder.FollowerEng, 490 builder.SyncCore, 491 builder.SyncEngineParticipantsProviderFactory(), 492 spamConfig, 493 ) 494 if err != nil { 495 return nil, fmt.Errorf("could not create synchronization engine: %w", err) 496 } 497 builder.SyncEng = sync 498 builder.FollowerDistributor.AddFinalizationConsumer(sync) 499 500 return builder.SyncEng, nil 501 }) 502 503 return builder 504 } 505 506 func (builder *ObserverServiceBuilder) BuildConsensusFollower() cmd.NodeBuilder { 507 builder. 508 buildFollowerState(). 509 buildSyncCore(). 510 buildCommittee(). 511 buildLatestHeader(). 512 buildFollowerCore(). 513 buildFollowerEngine(). 514 buildSyncEngine() 515 516 return builder 517 } 518 519 type Option func(*ObserverServiceConfig) 520 521 func NewFlowObserverServiceBuilder(opts ...Option) *ObserverServiceBuilder { 522 config := DefaultObserverServiceConfig() 523 for _, opt := range opts { 524 opt(config) 525 } 526 anb := &ObserverServiceBuilder{ 527 ObserverServiceConfig: config, 528 FlowNodeBuilder: cmd.FlowNode("observer"), 529 FollowerDistributor: pubsub.NewFollowerDistributor(), 530 IndexerDependencies: cmd.NewDependencyList(), 531 } 532 anb.FollowerDistributor.AddProposalViolationConsumer(notifications.NewSlashingViolationsConsumer(anb.Logger)) 533 // the observer gets a version of the root snapshot file that does not contain any node addresses 534 // hence skip all the root snapshot validations that involved an identity address 535 anb.FlowNodeBuilder.SkipNwAddressBasedValidations = true 536 return anb 537 } 538 539 func (builder *ObserverServiceBuilder) ParseFlags() error { 540 541 builder.BaseFlags() 542 543 builder.extraFlags() 544 545 return builder.ParseAndPrintFlags() 546 } 547 548 func (builder *ObserverServiceBuilder) extraFlags() { 549 builder.ExtraFlags(func(flags *pflag.FlagSet) { 550 defaultConfig := DefaultObserverServiceConfig() 551 552 flags.StringVarP(&builder.rpcConf.UnsecureGRPCListenAddr, 553 "rpc-addr", 554 "r", 555 defaultConfig.rpcConf.UnsecureGRPCListenAddr, 556 "the address the unsecured gRPC server listens on") 557 flags.StringVar(&builder.rpcConf.SecureGRPCListenAddr, 558 "secure-rpc-addr", 559 defaultConfig.rpcConf.SecureGRPCListenAddr, 560 "the address the secure gRPC server listens on") 561 flags.StringVarP(&builder.rpcConf.HTTPListenAddr, "http-addr", "h", defaultConfig.rpcConf.HTTPListenAddr, "the address the http proxy server listens on") 562 flags.StringVar(&builder.rpcConf.RestConfig.ListenAddress, 563 "rest-addr", 564 defaultConfig.rpcConf.RestConfig.ListenAddress, 565 "the address the REST server listens on (if empty the REST server will not be started)") 566 flags.DurationVar(&builder.rpcConf.RestConfig.WriteTimeout, 567 "rest-write-timeout", 568 defaultConfig.rpcConf.RestConfig.WriteTimeout, 569 "timeout to use when writing REST response") 570 flags.DurationVar(&builder.rpcConf.RestConfig.ReadTimeout, 571 "rest-read-timeout", 572 defaultConfig.rpcConf.RestConfig.ReadTimeout, 573 "timeout to use when reading REST request headers") 574 flags.DurationVar(&builder.rpcConf.RestConfig.IdleTimeout, "rest-idle-timeout", defaultConfig.rpcConf.RestConfig.IdleTimeout, "idle timeout for REST connections") 575 flags.UintVar(&builder.rpcConf.MaxMsgSize, 576 "rpc-max-message-size", 577 defaultConfig.rpcConf.MaxMsgSize, 578 "the maximum message size in bytes for messages sent or received over grpc") 579 flags.UintVar(&builder.rpcConf.BackendConfig.ConnectionPoolSize, 580 "connection-pool-size", 581 defaultConfig.rpcConf.BackendConfig.ConnectionPoolSize, 582 "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") 583 flags.UintVar(&builder.rpcConf.BackendConfig.MaxHeightRange, 584 "rpc-max-height-range", 585 defaultConfig.rpcConf.BackendConfig.MaxHeightRange, 586 "maximum size for height range requests") 587 flags.StringToIntVar(&builder.apiRatelimits, 588 "api-rate-limits", 589 defaultConfig.apiRatelimits, 590 "per second rate limits for Access API methods e.g. Ping=300,GetTransaction=500 etc.") 591 flags.StringToIntVar(&builder.apiBurstlimits, 592 "api-burst-limits", 593 defaultConfig.apiBurstlimits, 594 "burst limits for Access API methods e.g. Ping=100,GetTransaction=100 etc.") 595 flags.StringVar(&builder.observerNetworkingKeyPath, 596 "observer-networking-key-path", 597 defaultConfig.observerNetworkingKeyPath, 598 "path to the networking key for observer") 599 flags.StringSliceVar(&builder.bootstrapNodeAddresses, 600 "bootstrap-node-addresses", 601 defaultConfig.bootstrapNodeAddresses, 602 "the network addresses of the bootstrap access node if this is an observer e.g. access-001.mainnet.flow.org:9653,access-002.mainnet.flow.org:9653") 603 flags.StringSliceVar(&builder.bootstrapNodePublicKeys, 604 "bootstrap-node-public-keys", 605 defaultConfig.bootstrapNodePublicKeys, 606 "the networking public key of the bootstrap access node if this is an observer (in the same order as the bootstrap node addresses) e.g. \"d57a5e9c5.....\",\"44ded42d....\"") 607 flags.DurationVar(&builder.apiTimeout, "upstream-api-timeout", defaultConfig.apiTimeout, "tcp timeout for Flow API gRPC sockets to upstrem nodes") 608 flags.StringSliceVar(&builder.upstreamNodeAddresses, 609 "upstream-node-addresses", 610 defaultConfig.upstreamNodeAddresses, 611 "the gRPC network addresses of the upstream access node. e.g. access-001.mainnet.flow.org:9000,access-002.mainnet.flow.org:9000") 612 flags.StringSliceVar(&builder.upstreamNodePublicKeys, 613 "upstream-node-public-keys", 614 defaultConfig.upstreamNodePublicKeys, 615 "the networking public key of the upstream access node (in the same order as the upstream node addresses) e.g. \"d57a5e9c5.....\",\"44ded42d....\"") 616 flags.BoolVar(&builder.rpcMetricsEnabled, "rpc-metrics-enabled", defaultConfig.rpcMetricsEnabled, "whether to enable the rpc metrics") 617 flags.BoolVar(&builder.executionDataIndexingEnabled, 618 "execution-data-indexing-enabled", 619 defaultConfig.executionDataIndexingEnabled, 620 "whether to enable the execution data indexing") 621 flags.StringVar(&builder.registersDBPath, "execution-state-dir", defaultConfig.registersDBPath, "directory to use for execution-state database") 622 flags.StringVar(&builder.checkpointFile, "execution-state-checkpoint", defaultConfig.checkpointFile, "execution-state checkpoint file") 623 624 // ExecutionDataRequester config 625 flags.BoolVar(&builder.executionDataSyncEnabled, 626 "execution-data-sync-enabled", 627 defaultConfig.executionDataSyncEnabled, 628 "whether to enable the execution data sync protocol") 629 flags.StringVar(&builder.executionDataDir, 630 "execution-data-dir", 631 defaultConfig.executionDataDir, 632 "directory to use for Execution Data database") 633 flags.Uint64Var(&builder.executionDataStartHeight, 634 "execution-data-start-height", 635 defaultConfig.executionDataStartHeight, 636 "height of first block to sync execution data from when starting with an empty Execution Data database") 637 flags.Uint64Var(&builder.executionDataConfig.MaxSearchAhead, 638 "execution-data-max-search-ahead", 639 defaultConfig.executionDataConfig.MaxSearchAhead, 640 "max number of heights to search ahead of the lowest outstanding execution data height") 641 flags.DurationVar(&builder.executionDataConfig.FetchTimeout, 642 "execution-data-fetch-timeout", 643 defaultConfig.executionDataConfig.FetchTimeout, 644 "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") 645 flags.DurationVar(&builder.executionDataConfig.MaxFetchTimeout, 646 "execution-data-max-fetch-timeout", 647 defaultConfig.executionDataConfig.MaxFetchTimeout, 648 "maximum timeout to use when fetching execution data from the network e.g. 300s") 649 flags.DurationVar(&builder.executionDataConfig.RetryDelay, 650 "execution-data-retry-delay", 651 defaultConfig.executionDataConfig.RetryDelay, 652 "initial delay for exponential backoff when fetching execution data fails e.g. 10s") 653 flags.DurationVar(&builder.executionDataConfig.MaxRetryDelay, 654 "execution-data-max-retry-delay", 655 defaultConfig.executionDataConfig.MaxRetryDelay, 656 "maximum delay for exponential backoff when fetching execution data fails e.g. 5m") 657 658 // Streaming API 659 flags.StringVar(&builder.stateStreamConf.ListenAddr, 660 "state-stream-addr", 661 defaultConfig.stateStreamConf.ListenAddr, 662 "the address the state stream server listens on (if empty the server will not be started)") 663 flags.Uint32Var(&builder.stateStreamConf.ExecutionDataCacheSize, 664 "execution-data-cache-size", 665 defaultConfig.stateStreamConf.ExecutionDataCacheSize, 666 "block execution data cache size") 667 flags.Uint32Var(&builder.stateStreamConf.MaxGlobalStreams, 668 "state-stream-global-max-streams", defaultConfig.stateStreamConf.MaxGlobalStreams, 669 "global maximum number of concurrent streams") 670 flags.UintVar(&builder.stateStreamConf.MaxExecutionDataMsgSize, 671 "state-stream-max-message-size", 672 defaultConfig.stateStreamConf.MaxExecutionDataMsgSize, 673 "maximum size for a gRPC message containing block execution data") 674 flags.StringToIntVar(&builder.stateStreamFilterConf, 675 "state-stream-event-filter-limits", 676 defaultConfig.stateStreamFilterConf, 677 "event filter limits for ExecutionData SubscribeEvents API e.g. EventTypes=100,Addresses=100,Contracts=100 etc.") 678 flags.DurationVar(&builder.stateStreamConf.ClientSendTimeout, 679 "state-stream-send-timeout", 680 defaultConfig.stateStreamConf.ClientSendTimeout, 681 "maximum wait before timing out while sending a response to a streaming client e.g. 30s") 682 flags.UintVar(&builder.stateStreamConf.ClientSendBufferSize, 683 "state-stream-send-buffer-size", 684 defaultConfig.stateStreamConf.ClientSendBufferSize, 685 "maximum number of responses to buffer within a stream") 686 flags.Float64Var(&builder.stateStreamConf.ResponseLimit, 687 "state-stream-response-limit", 688 defaultConfig.stateStreamConf.ResponseLimit, 689 "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") 690 flags.Uint64Var(&builder.stateStreamConf.HeartbeatInterval, 691 "state-stream-heartbeat-interval", 692 defaultConfig.stateStreamConf.HeartbeatInterval, 693 "default interval in blocks at which heartbeat messages should be sent. applied when client did not specify a value.") 694 flags.Uint32Var(&builder.stateStreamConf.RegisterIDsRequestLimit, 695 "state-stream-max-register-values", 696 defaultConfig.stateStreamConf.RegisterIDsRequestLimit, 697 "maximum number of register ids to include in a single request to the GetRegisters endpoint") 698 699 flags.StringVar(&builder.rpcConf.BackendConfig.EventQueryMode, 700 "event-query-mode", 701 defaultConfig.rpcConf.BackendConfig.EventQueryMode, 702 "mode to use when querying events. one of [local-only, execution-nodes-only(default), failover]") 703 704 }).ValidateFlags(func() error { 705 if builder.executionDataSyncEnabled { 706 if builder.executionDataConfig.FetchTimeout <= 0 { 707 return errors.New("execution-data-fetch-timeout must be greater than 0") 708 } 709 if builder.executionDataConfig.MaxFetchTimeout < builder.executionDataConfig.FetchTimeout { 710 return errors.New("execution-data-max-fetch-timeout must be greater than execution-data-fetch-timeout") 711 } 712 if builder.executionDataConfig.RetryDelay <= 0 { 713 return errors.New("execution-data-retry-delay must be greater than 0") 714 } 715 if builder.executionDataConfig.MaxRetryDelay < builder.executionDataConfig.RetryDelay { 716 return errors.New("execution-data-max-retry-delay must be greater than or equal to execution-data-retry-delay") 717 } 718 if builder.executionDataConfig.MaxSearchAhead == 0 { 719 return errors.New("execution-data-max-search-ahead must be greater than 0") 720 } 721 } 722 if builder.stateStreamConf.ListenAddr != "" { 723 if builder.stateStreamConf.ExecutionDataCacheSize == 0 { 724 return errors.New("execution-data-cache-size must be greater than 0") 725 } 726 if builder.stateStreamConf.ClientSendBufferSize == 0 { 727 return errors.New("state-stream-send-buffer-size must be greater than 0") 728 } 729 if len(builder.stateStreamFilterConf) > 3 { 730 return errors.New("state-stream-event-filter-limits must have at most 3 keys (EventTypes, Addresses, Contracts)") 731 } 732 for key, value := range builder.stateStreamFilterConf { 733 switch key { 734 case "EventTypes", "Addresses", "Contracts": 735 if value <= 0 { 736 return fmt.Errorf("state-stream-event-filter-limits %s must be greater than 0", key) 737 } 738 default: 739 return errors.New("state-stream-event-filter-limits may only contain the keys EventTypes, Addresses, Contracts") 740 } 741 } 742 if builder.stateStreamConf.ResponseLimit < 0 { 743 return errors.New("state-stream-response-limit must be greater than or equal to 0") 744 } 745 if builder.stateStreamConf.RegisterIDsRequestLimit <= 0 { 746 return errors.New("state-stream-max-register-values must be greater than 0") 747 } 748 } 749 750 return nil 751 }) 752 } 753 754 func publicNetworkMsgValidators(log zerolog.Logger, idProvider module.IdentityProvider, selfID flow.Identifier) []network.MessageValidator { 755 return []network.MessageValidator{ 756 // filter out messages sent by this node itself 757 validator.ValidateNotSender(selfID), 758 validator.NewAnyValidator( 759 // message should be either from a valid staked node 760 validator.NewOriginValidator( 761 id.NewIdentityFilterIdentifierProvider(filter.IsValidCurrentEpochParticipant, idProvider), 762 ), 763 // or the message should be specifically targeted for this node 764 validator.ValidateTarget(log, selfID), 765 ), 766 } 767 } 768 769 func (builder *ObserverServiceBuilder) initNodeInfo() error { 770 // use the networking key that was loaded from the configured file 771 networkingKey, err := loadNetworkingKey(builder.observerNetworkingKeyPath) 772 if err != nil { 773 return fmt.Errorf("could not load networking private key: %w", err) 774 } 775 776 pubKey, err := keyutils.LibP2PPublicKeyFromFlow(networkingKey.PublicKey()) 777 if err != nil { 778 return fmt.Errorf("could not load networking public key: %w", err) 779 } 780 781 builder.peerID, err = peer.IDFromPublicKey(pubKey) 782 if err != nil { 783 return fmt.Errorf("could not get peer ID from public key: %w", err) 784 } 785 786 builder.NodeID, err = translator.NewPublicNetworkIDTranslator().GetFlowID(builder.peerID) 787 if err != nil { 788 return fmt.Errorf("could not get flow node ID: %w", err) 789 } 790 791 builder.NodeConfig.NetworkKey = networkingKey // copy the key to NodeConfig 792 builder.NodeConfig.StakingKey = nil // no staking key for the observer 793 794 return nil 795 } 796 797 func (builder *ObserverServiceBuilder) InitIDProviders() { 798 builder.Module("id providers", func(node *cmd.NodeConfig) error { 799 idCache, err := cache.NewProtocolStateIDCache(node.Logger, node.State, builder.ProtocolEvents) 800 if err != nil { 801 return fmt.Errorf("could not initialize ProtocolStateIDCache: %w", err) 802 } 803 builder.IDTranslator = translator.NewHierarchicalIDTranslator(idCache, translator.NewPublicNetworkIDTranslator()) 804 805 // The following wrapper allows to black-list byzantine nodes via an admin command: 806 // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true 807 builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { 808 return builder.NetworkUnderlay 809 }) 810 if err != nil { 811 return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) 812 } 813 814 // use the default identifier provider 815 builder.SyncEngineParticipantsProviderFactory = func() module.IdentifierProvider { 816 return id.NewCustomIdentifierProvider(func() flow.IdentifierList { 817 pids := builder.LibP2PNode.GetPeersForProtocol(protocols.FlowProtocolID(builder.SporkID)) 818 result := make(flow.IdentifierList, 0, len(pids)) 819 820 for _, pid := range pids { 821 // exclude own Identifier 822 if pid == builder.peerID { 823 continue 824 } 825 826 if flowID, err := builder.IDTranslator.GetFlowID(pid); err != nil { 827 // TODO: this is an instance of "log error and continue with best effort" anti-pattern 828 builder.Logger.Err(err).Str("peer", p2plogging.PeerId(pid)).Msg("failed to translate to Flow ID") 829 } else { 830 result = append(result, flowID) 831 } 832 } 833 834 return result 835 }) 836 } 837 838 return nil 839 }) 840 } 841 842 func (builder *ObserverServiceBuilder) Initialize() error { 843 if err := builder.deriveBootstrapPeerIdentities(); err != nil { 844 return err 845 } 846 847 if err := builder.deriveUpstreamIdentities(); err != nil { 848 return err 849 } 850 851 if err := builder.validateParams(); err != nil { 852 return err 853 } 854 855 if err := builder.initNodeInfo(); err != nil { 856 return err 857 } 858 859 builder.InitIDProviders() 860 861 builder.enqueuePublicNetworkInit() 862 863 builder.enqueueConnectWithStakedAN() 864 865 if builder.executionDataSyncEnabled { 866 builder.BuildExecutionSyncComponents() 867 } 868 869 builder.enqueueRPCServer() 870 871 if builder.BaseConfig.MetricsEnabled { 872 builder.EnqueueMetricsServerInit() 873 if err := builder.RegisterBadgerMetrics(); err != nil { 874 return err 875 } 876 } 877 878 builder.PreInit(builder.initObserverLocal()) 879 880 return nil 881 } 882 883 func (builder *ObserverServiceBuilder) validateParams() error { 884 if builder.BaseConfig.BindAddr == cmd.NotSet || builder.BaseConfig.BindAddr == "" { 885 return errors.New("bind address not specified") 886 } 887 if builder.ObserverServiceConfig.observerNetworkingKeyPath == cmd.NotSet { 888 return errors.New("networking key not provided") 889 } 890 if len(builder.bootstrapIdentities) > 0 { 891 return nil 892 } 893 if len(builder.bootstrapNodeAddresses) == 0 { 894 return errors.New("no bootstrap node address provided") 895 } 896 if len(builder.bootstrapNodeAddresses) != len(builder.bootstrapNodePublicKeys) { 897 return errors.New("number of bootstrap node addresses and public keys should match") 898 } 899 if len(builder.upstreamNodePublicKeys) > 0 && len(builder.upstreamNodeAddresses) != len(builder.upstreamNodePublicKeys) { 900 return errors.New("number of upstream node addresses and public keys must match if public keys given") 901 } 902 return nil 903 } 904 905 // initPublicLibp2pNode creates a libp2p node for the observer service in the public (unstaked) network. 906 // The factory function is later passed into the initMiddleware function to eventually instantiate the p2p.LibP2PNode instance 907 // The LibP2P host is created with the following options: 908 // * DHT as client and seeded with the given bootstrap peers 909 // * The specified bind address as the listen address 910 // * The passed in private key as the libp2p key 911 // * No connection gater 912 // * No connection manager 913 // * No peer manager 914 // * Default libp2p pubsub options. 915 // Args: 916 // - networkKey: the private key to use for the libp2p node 917 // Returns: 918 // - p2p.LibP2PNode: the libp2p node 919 // - error: if any error occurs. Any error returned is considered irrecoverable. 920 func (builder *ObserverServiceBuilder) initPublicLibp2pNode(networkKey crypto.PrivateKey) (p2p.LibP2PNode, error) { 921 var pis []peer.AddrInfo 922 923 for _, b := range builder.bootstrapIdentities { 924 pi, err := utils.PeerAddressInfo(*b) 925 if err != nil { 926 return nil, fmt.Errorf("could not extract peer address info from bootstrap identity %v: %w", b, err) 927 } 928 929 pis = append(pis, pi) 930 } 931 932 node, err := p2pbuilder.NewNodeBuilder( 933 builder.Logger, 934 &builder.FlowConfig.NetworkConfig.GossipSub, 935 &p2pbuilderconfig.MetricsConfig{ 936 HeroCacheFactory: builder.HeroCacheMetricsFactory(), 937 Metrics: builder.Metrics.Network, 938 }, 939 network.PublicNetwork, 940 builder.BaseConfig.BindAddr, 941 networkKey, 942 builder.SporkID, 943 builder.IdentityProvider, 944 &builder.FlowConfig.NetworkConfig.ResourceManager, 945 p2pbuilderconfig.PeerManagerDisableConfig(), // disable peer manager for observer node. 946 &p2p.DisallowListCacheConfig{ 947 MaxSize: builder.FlowConfig.NetworkConfig.DisallowListNotificationCacheSize, 948 Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), 949 }, 950 &p2pbuilderconfig.UnicastConfig{ 951 Unicast: builder.FlowConfig.NetworkConfig.Unicast, 952 }). 953 SetSubscriptionFilter( 954 subscription.NewRoleBasedFilter( 955 subscription.UnstakedRole, builder.IdentityProvider, 956 ), 957 ). 958 SetRoutingSystem(func(ctx context.Context, h host.Host) (routing.Routing, error) { 959 return p2pdht.NewDHT(ctx, h, protocols.FlowPublicDHTProtocolID(builder.SporkID), 960 builder.Logger, 961 builder.Metrics.Network, 962 p2pdht.AsClient(), 963 dht.BootstrapPeers(pis...), 964 ) 965 }). 966 Build() 967 968 if err != nil { 969 return nil, fmt.Errorf("could not initialize libp2p node for observer: %w", err) 970 } 971 972 builder.LibP2PNode = node 973 974 return builder.LibP2PNode, nil 975 } 976 977 // initObserverLocal initializes the observer's ID, network key and network address 978 // Currently, it reads a node-info.priv.json like any other node. 979 // TODO: read the node ID from the special bootstrap files 980 func (builder *ObserverServiceBuilder) initObserverLocal() func(node *cmd.NodeConfig) error { 981 return func(node *cmd.NodeConfig) error { 982 // for an observer, set the identity here explicitly since it will not be found in the protocol state 983 self := &flow.Identity{ 984 NodeID: node.NodeID, 985 NetworkPubKey: node.NetworkKey.PublicKey(), 986 StakingPubKey: nil, // no staking key needed for the observer 987 Role: flow.RoleAccess, // observer can only run as an access node 988 Address: builder.BindAddr, 989 } 990 991 var err error 992 node.Me, err = local.NewNoKey(self) 993 if err != nil { 994 return fmt.Errorf("could not initialize local: %w", err) 995 } 996 return nil 997 } 998 } 999 1000 // Build enqueues the sync engine and the follower engine for the observer. 1001 // Currently, the observer only runs the follower engine. 1002 func (builder *ObserverServiceBuilder) Build() (cmd.Node, error) { 1003 builder.BuildConsensusFollower() 1004 return builder.FlowNodeBuilder.Build() 1005 } 1006 1007 func (builder *ObserverServiceBuilder) BuildExecutionSyncComponents() *ObserverServiceBuilder { 1008 var ds *badger.Datastore 1009 var bs network.BlobService 1010 var processedBlockHeight storage.ConsumerProgress 1011 var processedNotifications storage.ConsumerProgress 1012 var publicBsDependable *module.ProxiedReadyDoneAware 1013 var execDataDistributor *edrequester.ExecutionDataDistributor 1014 var execDataCacheBackend *herocache.BlockExecutionData 1015 var executionDataStoreCache *execdatacache.ExecutionDataCache 1016 1017 // setup dependency chain to ensure indexer starts after the requester 1018 requesterDependable := module.NewProxiedReadyDoneAware() 1019 builder.IndexerDependencies.Add(requesterDependable) 1020 1021 builder. 1022 AdminCommand("read-execution-data", func(config *cmd.NodeConfig) commands.AdminCommand { 1023 return stateSyncCommands.NewReadExecutionDataCommand(builder.ExecutionDataStore) 1024 }). 1025 Module("execution data datastore and blobstore", func(node *cmd.NodeConfig) error { 1026 datastoreDir := filepath.Join(builder.executionDataDir, "blobstore") 1027 err := os.MkdirAll(datastoreDir, 0700) 1028 if err != nil { 1029 return err 1030 } 1031 1032 ds, err = badger.NewDatastore(datastoreDir, &badger.DefaultOptions) 1033 if err != nil { 1034 return err 1035 } 1036 1037 builder.ShutdownFunc(func() error { 1038 if err := ds.Close(); err != nil { 1039 return fmt.Errorf("could not close execution data datastore: %w", err) 1040 } 1041 return nil 1042 }) 1043 1044 return nil 1045 }). 1046 Module("processed block height consumer progress", func(node *cmd.NodeConfig) error { 1047 // Note: progress is stored in the datastore's DB since that is where the jobqueue 1048 // writes execution data to. 1049 processedBlockHeight = bstorage.NewConsumerProgress(ds.DB, module.ConsumeProgressExecutionDataRequesterBlockHeight) 1050 return nil 1051 }). 1052 Module("processed notifications consumer progress", func(node *cmd.NodeConfig) error { 1053 // Note: progress is stored in the datastore's DB since that is where the jobqueue 1054 // writes execution data to. 1055 processedNotifications = bstorage.NewConsumerProgress(ds.DB, module.ConsumeProgressExecutionDataRequesterNotification) 1056 return nil 1057 }). 1058 Module("blobservice peer manager dependencies", func(node *cmd.NodeConfig) error { 1059 publicBsDependable = module.NewProxiedReadyDoneAware() 1060 builder.PeerManagerDependencies.Add(publicBsDependable) 1061 return nil 1062 }). 1063 Module("execution datastore", func(node *cmd.NodeConfig) error { 1064 blobstore := blobs.NewBlobstore(ds) 1065 builder.ExecutionDataStore = execution_data.NewExecutionDataStore(blobstore, execution_data.DefaultSerializer) 1066 return nil 1067 }). 1068 Module("execution data cache", func(node *cmd.NodeConfig) error { 1069 var heroCacheCollector module.HeroCacheMetrics = metrics.NewNoopCollector() 1070 if builder.HeroCacheMetricsEnable { 1071 heroCacheCollector = metrics.AccessNodeExecutionDataCacheMetrics(builder.MetricsRegisterer) 1072 } 1073 1074 execDataCacheBackend = herocache.NewBlockExecutionData(builder.stateStreamConf.ExecutionDataCacheSize, builder.Logger, heroCacheCollector) 1075 1076 // Execution Data cache that uses a blobstore as the backend (instead of a downloader) 1077 // This ensures that it simply returns a not found error if the blob doesn't exist 1078 // instead of attempting to download it from the network. 1079 executionDataStoreCache = execdatacache.NewExecutionDataCache( 1080 builder.ExecutionDataStore, 1081 builder.Storage.Headers, 1082 builder.Storage.Seals, 1083 builder.Storage.Results, 1084 execDataCacheBackend, 1085 ) 1086 1087 return nil 1088 }). 1089 Component("public execution data service", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1090 opts := []network.BlobServiceOption{ 1091 blob.WithBitswapOptions( 1092 bitswap.WithTracer( 1093 blob.NewTracer(node.Logger.With().Str("public_blob_service", channels.PublicExecutionDataService.String()).Logger()), 1094 ), 1095 ), 1096 } 1097 1098 var err error 1099 bs, err = node.EngineRegistry.RegisterBlobService(channels.PublicExecutionDataService, ds, opts...) 1100 if err != nil { 1101 return nil, fmt.Errorf("could not register blob service: %w", err) 1102 } 1103 1104 // add blobservice into ReadyDoneAware dependency passed to peer manager 1105 // this starts the blob service and configures peer manager to wait for the blobservice 1106 // to be ready before starting 1107 publicBsDependable.Init(bs) 1108 1109 builder.ExecutionDataDownloader = execution_data.NewDownloader(bs) 1110 1111 return builder.ExecutionDataDownloader, nil 1112 }). 1113 Component("execution data requester", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1114 // Validation of the start block height needs to be done after loading state 1115 if builder.executionDataStartHeight > 0 { 1116 if builder.executionDataStartHeight <= builder.FinalizedRootBlock.Header.Height { 1117 return nil, fmt.Errorf( 1118 "execution data start block height (%d) must be greater than the root block height (%d)", 1119 builder.executionDataStartHeight, builder.FinalizedRootBlock.Header.Height) 1120 } 1121 1122 latestSeal, err := builder.State.Sealed().Head() 1123 if err != nil { 1124 return nil, fmt.Errorf("failed to get latest sealed height") 1125 } 1126 1127 // Note: since the root block of a spork is also sealed in the root protocol state, the 1128 // latest sealed height is always equal to the root block height. That means that at the 1129 // very beginning of a spork, this check will always fail. Operators should not specify 1130 // an InitialBlockHeight when starting from the beginning of a spork. 1131 if builder.executionDataStartHeight > latestSeal.Height { 1132 return nil, fmt.Errorf( 1133 "execution data start block height (%d) must be less than or equal to the latest sealed block height (%d)", 1134 builder.executionDataStartHeight, latestSeal.Height) 1135 } 1136 1137 // executionDataStartHeight is provided as the first block to sync, but the 1138 // requester expects the initial last processed height, which is the first height - 1 1139 builder.executionDataConfig.InitialBlockHeight = builder.executionDataStartHeight - 1 1140 } else { 1141 builder.executionDataConfig.InitialBlockHeight = builder.FinalizedRootBlock.Header.Height 1142 } 1143 1144 execDataDistributor = edrequester.NewExecutionDataDistributor() 1145 1146 // Execution Data cache with a downloader as the backend. This is used by the requester 1147 // to download and cache execution data for each block. It shares a cache backend instance 1148 // with the datastore implementation. 1149 executionDataCache := execdatacache.NewExecutionDataCache( 1150 builder.ExecutionDataDownloader, 1151 builder.Storage.Headers, 1152 builder.Storage.Seals, 1153 builder.Storage.Results, 1154 execDataCacheBackend, 1155 ) 1156 1157 r, err := edrequester.New( 1158 builder.Logger, 1159 metrics.NewExecutionDataRequesterCollector(), 1160 builder.ExecutionDataDownloader, 1161 executionDataCache, 1162 processedBlockHeight, 1163 processedNotifications, 1164 builder.State, 1165 builder.Storage.Headers, 1166 builder.executionDataConfig, 1167 execDataDistributor, 1168 ) 1169 if err != nil { 1170 return nil, fmt.Errorf("failed to create execution data requester: %w", err) 1171 } 1172 builder.ExecutionDataRequester = r 1173 1174 builder.FollowerDistributor.AddOnBlockFinalizedConsumer(builder.ExecutionDataRequester.OnBlockFinalized) 1175 1176 // add requester into ReadyDoneAware dependency passed to indexer. This allows the indexer 1177 // to wait for the requester to be ready before starting. 1178 requesterDependable.Init(builder.ExecutionDataRequester) 1179 1180 return builder.ExecutionDataRequester, nil 1181 }) 1182 1183 if builder.executionDataIndexingEnabled { 1184 var indexedBlockHeight storage.ConsumerProgress 1185 1186 builder.Module("indexed block height consumer progress", func(node *cmd.NodeConfig) error { 1187 // Note: progress is stored in the MAIN db since that is where indexed execution data is stored. 1188 indexedBlockHeight = bstorage.NewConsumerProgress(builder.DB, module.ConsumeProgressExecutionDataIndexerBlockHeight) 1189 return nil 1190 }).Module("transaction results storage", func(node *cmd.NodeConfig) error { 1191 builder.Storage.LightTransactionResults = bstorage.NewLightTransactionResults(node.Metrics.Cache, node.DB, bstorage.DefaultCacheSize) 1192 return nil 1193 }).DependableComponent("execution data indexer", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1194 // Note: using a DependableComponent here to ensure that the indexer does not block 1195 // other components from starting while bootstrapping the register db since it may 1196 // take hours to complete. 1197 1198 pdb, err := pStorage.OpenRegisterPebbleDB(builder.registersDBPath) 1199 if err != nil { 1200 return nil, fmt.Errorf("could not open registers db: %w", err) 1201 } 1202 builder.ShutdownFunc(func() error { 1203 return pdb.Close() 1204 }) 1205 1206 bootstrapped, err := pStorage.IsBootstrapped(pdb) 1207 if err != nil { 1208 return nil, fmt.Errorf("could not check if registers db is bootstrapped: %w", err) 1209 } 1210 1211 if !bootstrapped { 1212 checkpointFile := builder.checkpointFile 1213 if checkpointFile == cmd.NotSet { 1214 checkpointFile = path.Join(builder.BootstrapDir, bootstrap.PathRootCheckpoint) 1215 } 1216 1217 // currently, the checkpoint must be from the root block. 1218 // read the root hash from the provided checkpoint and verify it matches the 1219 // state commitment from the root snapshot. 1220 err := wal.CheckpointHasRootHash( 1221 node.Logger, 1222 "", // checkpoint file already full path 1223 checkpointFile, 1224 ledger.RootHash(node.RootSeal.FinalState), 1225 ) 1226 if err != nil { 1227 return nil, fmt.Errorf("could not verify checkpoint file: %w", err) 1228 } 1229 1230 checkpointHeight := builder.SealedRootBlock.Header.Height 1231 1232 if builder.SealedRootBlock.ID() != builder.RootSeal.BlockID { 1233 return nil, fmt.Errorf("mismatching sealed root block and root seal: %v != %v", 1234 builder.SealedRootBlock.ID(), builder.RootSeal.BlockID) 1235 } 1236 1237 rootHash := ledger.RootHash(builder.RootSeal.FinalState) 1238 bootstrap, err := pStorage.NewRegisterBootstrap(pdb, checkpointFile, checkpointHeight, rootHash, builder.Logger) 1239 if err != nil { 1240 return nil, fmt.Errorf("could not create registers bootstrap: %w", err) 1241 } 1242 1243 // TODO: find a way to hook a context up to this to allow a graceful shutdown 1244 workerCount := 10 1245 err = bootstrap.IndexCheckpointFile(context.Background(), workerCount) 1246 if err != nil { 1247 return nil, fmt.Errorf("could not load checkpoint file: %w", err) 1248 } 1249 } 1250 1251 registers, err := pStorage.NewRegisters(pdb) 1252 if err != nil { 1253 return nil, fmt.Errorf("could not create registers storage: %w", err) 1254 } 1255 1256 builder.Storage.RegisterIndex = registers 1257 1258 var collectionExecutedMetric module.CollectionExecutedMetric = metrics.NewNoopCollector() 1259 indexerCore, err := indexer.New( 1260 builder.Logger, 1261 metrics.NewExecutionStateIndexerCollector(), 1262 builder.DB, 1263 builder.Storage.RegisterIndex, 1264 builder.Storage.Headers, 1265 builder.Storage.Events, 1266 builder.Storage.Collections, 1267 builder.Storage.Transactions, 1268 builder.Storage.LightTransactionResults, 1269 collectionExecutedMetric, 1270 ) 1271 if err != nil { 1272 return nil, err 1273 } 1274 builder.ExecutionIndexerCore = indexerCore 1275 1276 // execution state worker uses a jobqueue to process new execution data and indexes it by using the indexer. 1277 builder.ExecutionIndexer, err = indexer.NewIndexer( 1278 builder.Logger, 1279 registers.FirstHeight(), 1280 registers, 1281 indexerCore, 1282 executionDataStoreCache, 1283 builder.ExecutionDataRequester.HighestConsecutiveHeight, 1284 indexedBlockHeight, 1285 ) 1286 if err != nil { 1287 return nil, err 1288 } 1289 1290 // setup requester to notify indexer when new execution data is received 1291 execDataDistributor.AddOnExecutionDataReceivedConsumer(builder.ExecutionIndexer.OnExecutionData) 1292 1293 err = builder.EventsIndex.Initialize(builder.ExecutionIndexer) 1294 if err != nil { 1295 return nil, err 1296 } 1297 1298 err = builder.RegistersAsyncStore.Initialize(registers) 1299 if err != nil { 1300 return nil, err 1301 } 1302 1303 return builder.ExecutionIndexer, nil 1304 }, builder.IndexerDependencies) 1305 } 1306 1307 if builder.stateStreamConf.ListenAddr != "" { 1308 builder.Component("exec state stream engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1309 for key, value := range builder.stateStreamFilterConf { 1310 switch key { 1311 case "EventTypes": 1312 builder.stateStreamConf.MaxEventTypes = value 1313 case "Addresses": 1314 builder.stateStreamConf.MaxAddresses = value 1315 case "Contracts": 1316 builder.stateStreamConf.MaxContracts = value 1317 } 1318 } 1319 builder.stateStreamConf.RpcMetricsEnabled = builder.rpcMetricsEnabled 1320 1321 highestAvailableHeight, err := builder.ExecutionDataRequester.HighestConsecutiveHeight() 1322 if err != nil { 1323 return nil, fmt.Errorf("could not get highest consecutive height: %w", err) 1324 } 1325 broadcaster := engine.NewBroadcaster() 1326 1327 eventQueryMode, err := backend.ParseIndexQueryMode(builder.rpcConf.BackendConfig.EventQueryMode) 1328 if err != nil { 1329 return nil, fmt.Errorf("could not parse event query mode: %w", err) 1330 } 1331 1332 // use the events index for events if enabled and the node is configured to use it for 1333 // regular event queries 1334 useIndex := builder.executionDataIndexingEnabled && 1335 eventQueryMode != backend.IndexQueryModeExecutionNodesOnly 1336 1337 builder.stateStreamBackend, err = statestreambackend.New( 1338 node.Logger, 1339 builder.stateStreamConf, 1340 node.State, 1341 node.Storage.Headers, 1342 node.Storage.Seals, 1343 node.Storage.Results, 1344 builder.ExecutionDataStore, 1345 executionDataStoreCache, 1346 broadcaster, 1347 builder.executionDataConfig.InitialBlockHeight, 1348 highestAvailableHeight, 1349 builder.RegistersAsyncStore, 1350 builder.EventsIndex, 1351 useIndex, 1352 ) 1353 if err != nil { 1354 return nil, fmt.Errorf("could not create state stream backend: %w", err) 1355 } 1356 1357 stateStreamEng, err := statestreambackend.NewEng( 1358 node.Logger, 1359 builder.stateStreamConf, 1360 executionDataStoreCache, 1361 node.Storage.Headers, 1362 node.RootChainID, 1363 builder.stateStreamGrpcServer, 1364 builder.stateStreamBackend, 1365 broadcaster, 1366 ) 1367 if err != nil { 1368 return nil, fmt.Errorf("could not create state stream engine: %w", err) 1369 } 1370 builder.StateStreamEng = stateStreamEng 1371 1372 execDataDistributor.AddOnExecutionDataReceivedConsumer(builder.StateStreamEng.OnExecutionData) 1373 1374 return builder.StateStreamEng, nil 1375 }) 1376 } 1377 return builder 1378 } 1379 1380 // enqueuePublicNetworkInit enqueues the observer network component initialized for the observer 1381 func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { 1382 var publicLibp2pNode p2p.LibP2PNode 1383 builder. 1384 Component("public libp2p node", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1385 var err error 1386 publicLibp2pNode, err = builder.initPublicLibp2pNode(node.NetworkKey) 1387 if err != nil { 1388 return nil, fmt.Errorf("could not create public libp2p node: %w", err) 1389 } 1390 1391 return publicLibp2pNode, nil 1392 }). 1393 Component("public network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1394 receiveCache := netcache.NewHeroReceiveCache(builder.FlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, 1395 builder.Logger, 1396 metrics.NetworkReceiveCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork)) 1397 1398 err := node.Metrics.Mempool.Register(metrics.PrependPublicPrefix(metrics.ResourceNetworkingReceiveCache), receiveCache.Size) 1399 if err != nil { 1400 return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) 1401 } 1402 1403 net, err := underlay.NewNetwork(&underlay.NetworkConfig{ 1404 Logger: builder.Logger.With().Str("component", "public-network").Logger(), 1405 Codec: builder.CodecFactory(), 1406 Me: builder.Me, 1407 Topology: nil, // topology is nil since it is managed by libp2p; //TODO: can we set empty topology? 1408 Libp2pNode: publicLibp2pNode, 1409 Metrics: builder.Metrics.Network, 1410 BitSwapMetrics: builder.Metrics.Bitswap, 1411 IdentityProvider: builder.IdentityProvider, 1412 ReceiveCache: receiveCache, 1413 ConduitFactory: conduit.NewDefaultConduitFactory(), 1414 SporkId: builder.SporkID, 1415 UnicastMessageTimeout: underlay.DefaultUnicastTimeout, 1416 IdentityTranslator: builder.IDTranslator, 1417 AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ 1418 Logger: builder.Logger, 1419 SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, 1420 SpamReportQueueSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamReportQueueSize, 1421 DisablePenalty: builder.FlowConfig.NetworkConfig.AlspConfig.DisablePenalty, 1422 HeartBeatInterval: builder.FlowConfig.NetworkConfig.AlspConfig.HearBeatInterval, 1423 AlspMetrics: builder.Metrics.Network, 1424 HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), 1425 NetworkType: network.PublicNetwork, 1426 }, 1427 SlashingViolationConsumerFactory: func(adapter network.ConduitAdapter) network.ViolationsConsumer { 1428 return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) 1429 }, 1430 }, underlay.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) 1431 if err != nil { 1432 return nil, fmt.Errorf("could not initialize network: %w", err) 1433 } 1434 1435 builder.NetworkUnderlay = net 1436 builder.EngineRegistry = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) 1437 1438 builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr) 1439 1440 idEvents := gadgets.NewIdentityDeltas(builder.NetworkUnderlay.UpdateNodeAddresses) 1441 builder.ProtocolEvents.AddConsumer(idEvents) 1442 1443 return builder.EngineRegistry, nil 1444 }) 1445 } 1446 1447 // enqueueConnectWithStakedAN enqueues the upstream connector component which connects the libp2p host of the observer 1448 // service with the AN. 1449 // Currently, there is an issue with LibP2P stopping advertisements of subscribed topics if no peers are connected 1450 // (https://github.com/libp2p/go-libp2p-pubsub/issues/442). This means that an observer could end up not being 1451 // discovered by other observers if it subscribes to a topic before connecting to the AN. Hence, the need 1452 // of an explicit connect to the AN before the node attempts to subscribe to topics. 1453 func (builder *ObserverServiceBuilder) enqueueConnectWithStakedAN() { 1454 builder.Component("upstream connector", func(_ *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1455 return consensus_follower.NewUpstreamConnector(builder.bootstrapIdentities, builder.LibP2PNode, builder.Logger), nil 1456 }) 1457 } 1458 1459 func (builder *ObserverServiceBuilder) enqueueRPCServer() { 1460 builder.Module("rest metrics", func(node *cmd.NodeConfig) error { 1461 m, err := metrics.NewRestCollector(routes.URLToRoute, node.MetricsRegisterer) 1462 if err != nil { 1463 return err 1464 } 1465 builder.RestMetrics = m 1466 return nil 1467 }) 1468 builder.Module("access metrics", func(node *cmd.NodeConfig) error { 1469 builder.AccessMetrics = metrics.NewAccessCollector( 1470 metrics.WithRestMetrics(builder.RestMetrics), 1471 ) 1472 return nil 1473 }) 1474 builder.Module("server certificate", func(node *cmd.NodeConfig) error { 1475 // generate the server certificate that will be served by the GRPC server 1476 x509Certificate, err := grpcutils.X509Certificate(node.NetworkKey) 1477 if err != nil { 1478 return err 1479 } 1480 tlsConfig := grpcutils.DefaultServerTLSConfig(x509Certificate) 1481 builder.rpcConf.TransportCredentials = credentials.NewTLS(tlsConfig) 1482 return nil 1483 }) 1484 builder.Module("creating grpc servers", func(node *cmd.NodeConfig) error { 1485 builder.secureGrpcServer = grpcserver.NewGrpcServerBuilder(node.Logger, 1486 builder.rpcConf.SecureGRPCListenAddr, 1487 builder.rpcConf.MaxMsgSize, 1488 builder.rpcMetricsEnabled, 1489 builder.apiRatelimits, 1490 builder.apiBurstlimits, 1491 grpcserver.WithTransportCredentials(builder.rpcConf.TransportCredentials)).Build() 1492 1493 builder.stateStreamGrpcServer = grpcserver.NewGrpcServerBuilder( 1494 node.Logger, 1495 builder.stateStreamConf.ListenAddr, 1496 builder.stateStreamConf.MaxExecutionDataMsgSize, 1497 builder.rpcMetricsEnabled, 1498 builder.apiRatelimits, 1499 builder.apiBurstlimits, 1500 grpcserver.WithStreamInterceptor()).Build() 1501 1502 if builder.rpcConf.UnsecureGRPCListenAddr != builder.stateStreamConf.ListenAddr { 1503 builder.unsecureGrpcServer = grpcserver.NewGrpcServerBuilder(node.Logger, 1504 builder.rpcConf.UnsecureGRPCListenAddr, 1505 builder.rpcConf.MaxMsgSize, 1506 builder.rpcMetricsEnabled, 1507 builder.apiRatelimits, 1508 builder.apiBurstlimits).Build() 1509 } else { 1510 builder.unsecureGrpcServer = builder.stateStreamGrpcServer 1511 } 1512 1513 return nil 1514 }) 1515 builder.Module("async register store", func(node *cmd.NodeConfig) error { 1516 builder.RegistersAsyncStore = execution.NewRegistersAsyncStore() 1517 return nil 1518 }) 1519 builder.Module("events storage", func(node *cmd.NodeConfig) error { 1520 builder.Storage.Events = bstorage.NewEvents(node.Metrics.Cache, node.DB) 1521 return nil 1522 }) 1523 builder.Module("events index", func(node *cmd.NodeConfig) error { 1524 builder.EventsIndex = backend.NewEventsIndex(builder.Storage.Events) 1525 return nil 1526 }) 1527 builder.Component("RPC engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1528 accessMetrics := builder.AccessMetrics 1529 config := builder.rpcConf 1530 backendConfig := config.BackendConfig 1531 cacheSize := int(backendConfig.ConnectionPoolSize) 1532 1533 var connBackendCache *rpcConnection.Cache 1534 var err error 1535 if cacheSize > 0 { 1536 connBackendCache, err = rpcConnection.NewCache(node.Logger, accessMetrics, cacheSize) 1537 if err != nil { 1538 return nil, fmt.Errorf("could not initialize connection cache: %w", err) 1539 } 1540 } 1541 1542 connFactory := &rpcConnection.ConnectionFactoryImpl{ 1543 CollectionGRPCPort: 0, 1544 ExecutionGRPCPort: 0, 1545 CollectionNodeGRPCTimeout: builder.apiTimeout, 1546 ExecutionNodeGRPCTimeout: builder.apiTimeout, 1547 AccessMetrics: accessMetrics, 1548 Log: node.Logger, 1549 Manager: rpcConnection.NewManager( 1550 node.Logger, 1551 accessMetrics, 1552 connBackendCache, 1553 config.MaxMsgSize, 1554 backendConfig.CircuitBreakerConfig, 1555 config.CompressorName, 1556 ), 1557 } 1558 1559 accessBackend, err := backend.New(backend.Params{ 1560 State: node.State, 1561 Blocks: node.Storage.Blocks, 1562 Headers: node.Storage.Headers, 1563 Collections: node.Storage.Collections, 1564 Transactions: node.Storage.Transactions, 1565 ExecutionReceipts: node.Storage.Receipts, 1566 ExecutionResults: node.Storage.Results, 1567 ChainID: node.RootChainID, 1568 AccessMetrics: accessMetrics, 1569 ConnFactory: connFactory, 1570 RetryEnabled: false, 1571 MaxHeightRange: backendConfig.MaxHeightRange, 1572 PreferredExecutionNodeIDs: backendConfig.PreferredExecutionNodeIDs, 1573 FixedExecutionNodeIDs: backendConfig.FixedExecutionNodeIDs, 1574 Log: node.Logger, 1575 SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, 1576 Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), 1577 }) 1578 if err != nil { 1579 return nil, fmt.Errorf("could not initialize backend: %w", err) 1580 } 1581 1582 observerCollector := metrics.NewObserverCollector() 1583 restHandler, err := restapiproxy.NewRestProxyHandler( 1584 accessBackend, 1585 builder.upstreamIdentities, 1586 connFactory, 1587 builder.Logger, 1588 observerCollector, 1589 node.RootChainID.Chain()) 1590 if err != nil { 1591 return nil, err 1592 } 1593 1594 engineBuilder, err := rpc.NewBuilder( 1595 node.Logger, 1596 node.State, 1597 config, 1598 node.RootChainID, 1599 accessMetrics, 1600 builder.rpcMetricsEnabled, 1601 builder.Me, 1602 accessBackend, 1603 restHandler, 1604 builder.secureGrpcServer, 1605 builder.unsecureGrpcServer, 1606 builder.stateStreamBackend, 1607 builder.stateStreamConf, 1608 ) 1609 if err != nil { 1610 return nil, err 1611 } 1612 1613 // upstream access node forwarder 1614 forwarder, err := apiproxy.NewFlowAccessAPIForwarder(builder.upstreamIdentities, connFactory) 1615 if err != nil { 1616 return nil, err 1617 } 1618 1619 rpcHandler := &apiproxy.FlowAccessAPIRouter{ 1620 Logger: builder.Logger, 1621 Metrics: observerCollector, 1622 Upstream: forwarder, 1623 Observer: protocol.NewHandler(protocol.New( 1624 node.State, 1625 node.Storage.Blocks, 1626 node.Storage.Headers, 1627 backend.NewNetworkAPI( 1628 node.State, 1629 node.RootChainID, 1630 node.Storage.Headers, 1631 backend.DefaultSnapshotHistoryLimit, 1632 ), 1633 )), 1634 } 1635 1636 // build the rpc engine 1637 builder.RpcEng, err = engineBuilder. 1638 WithRpcHandler(rpcHandler). 1639 WithLegacy(). 1640 Build() 1641 if err != nil { 1642 return nil, err 1643 } 1644 builder.FollowerDistributor.AddOnBlockFinalizedConsumer(builder.RpcEng.OnFinalizedBlock) 1645 return builder.RpcEng, nil 1646 }) 1647 1648 // build secure grpc server 1649 builder.Component("secure grpc server", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1650 return builder.secureGrpcServer, nil 1651 }) 1652 1653 builder.Component("state stream unsecure grpc server", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1654 return builder.stateStreamGrpcServer, nil 1655 }) 1656 1657 if builder.rpcConf.UnsecureGRPCListenAddr != builder.stateStreamConf.ListenAddr { 1658 builder.Component("unsecure grpc server", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 1659 return builder.unsecureGrpcServer, nil 1660 }) 1661 } 1662 } 1663 1664 func loadNetworkingKey(path string) (crypto.PrivateKey, error) { 1665 data, err := io.ReadFile(path) 1666 if err != nil { 1667 return nil, fmt.Errorf("could not read networking key (path=%s): %w", path, err) 1668 } 1669 1670 keyBytes, err := hex.DecodeString(strings.Trim(string(data), "\n ")) 1671 if err != nil { 1672 return nil, fmt.Errorf("could not hex decode networking key (path=%s): %w", path, err) 1673 } 1674 1675 networkingKey, err := crypto.DecodePrivateKey(crypto.ECDSASecp256k1, keyBytes) 1676 if err != nil { 1677 return nil, fmt.Errorf("could not decode networking key (path=%s): %w", path, err) 1678 } 1679 1680 return networkingKey, nil 1681 }