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