github.com/koko1123/flow-go-1@v0.29.6/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/filepath" 11 "strings" 12 "time" 13 14 badger "github.com/ipfs/go-ds-badger2" 15 dht "github.com/libp2p/go-libp2p-kad-dht" 16 "github.com/libp2p/go-libp2p/core/host" 17 "github.com/libp2p/go-libp2p/core/peer" 18 "github.com/libp2p/go-libp2p/core/routing" 19 "github.com/onflow/go-bitswap" 20 "github.com/rs/zerolog" 21 "github.com/spf13/pflag" 22 23 "github.com/koko1123/flow-go-1/cmd" 24 "github.com/koko1123/flow-go-1/consensus" 25 "github.com/koko1123/flow-go-1/consensus/hotstuff" 26 "github.com/koko1123/flow-go-1/consensus/hotstuff/committees" 27 "github.com/koko1123/flow-go-1/consensus/hotstuff/notifications/pubsub" 28 hotsignature "github.com/koko1123/flow-go-1/consensus/hotstuff/signature" 29 "github.com/koko1123/flow-go-1/consensus/hotstuff/verification" 30 recovery "github.com/koko1123/flow-go-1/consensus/recovery/protocol" 31 "github.com/onflow/flow-go/crypto" 32 "github.com/koko1123/flow-go-1/engine/access/apiproxy" 33 "github.com/koko1123/flow-go-1/engine/access/rpc" 34 "github.com/koko1123/flow-go-1/engine/access/rpc/backend" 35 "github.com/koko1123/flow-go-1/engine/common/follower" 36 followereng "github.com/koko1123/flow-go-1/engine/common/follower" 37 synceng "github.com/koko1123/flow-go-1/engine/common/synchronization" 38 "github.com/koko1123/flow-go-1/engine/protocol" 39 "github.com/koko1123/flow-go-1/model/encodable" 40 "github.com/koko1123/flow-go-1/model/flow" 41 "github.com/koko1123/flow-go-1/model/flow/filter" 42 "github.com/koko1123/flow-go-1/module" 43 "github.com/koko1123/flow-go-1/module/buffer" 44 "github.com/koko1123/flow-go-1/module/chainsync" 45 "github.com/koko1123/flow-go-1/module/compliance" 46 "github.com/koko1123/flow-go-1/module/executiondatasync/execution_data" 47 finalizer "github.com/koko1123/flow-go-1/module/finalizer/consensus" 48 "github.com/koko1123/flow-go-1/module/id" 49 "github.com/koko1123/flow-go-1/module/local" 50 "github.com/koko1123/flow-go-1/module/metrics" 51 "github.com/koko1123/flow-go-1/module/state_synchronization" 52 edrequester "github.com/koko1123/flow-go-1/module/state_synchronization/requester" 53 consensus_follower "github.com/koko1123/flow-go-1/module/upstream" 54 "github.com/koko1123/flow-go-1/network" 55 netcache "github.com/koko1123/flow-go-1/network/cache" 56 "github.com/koko1123/flow-go-1/network/channels" 57 cborcodec "github.com/koko1123/flow-go-1/network/codec/cbor" 58 "github.com/koko1123/flow-go-1/network/converter" 59 "github.com/koko1123/flow-go-1/network/p2p" 60 "github.com/koko1123/flow-go-1/network/p2p/blob" 61 "github.com/koko1123/flow-go-1/network/p2p/cache" 62 p2pdht "github.com/koko1123/flow-go-1/network/p2p/dht" 63 "github.com/koko1123/flow-go-1/network/p2p/keyutils" 64 "github.com/koko1123/flow-go-1/network/p2p/middleware" 65 "github.com/koko1123/flow-go-1/network/p2p/p2pbuilder" 66 "github.com/koko1123/flow-go-1/network/p2p/subscription" 67 "github.com/koko1123/flow-go-1/network/p2p/translator" 68 "github.com/koko1123/flow-go-1/network/p2p/unicast" 69 "github.com/koko1123/flow-go-1/network/p2p/utils" 70 "github.com/koko1123/flow-go-1/network/slashing" 71 "github.com/koko1123/flow-go-1/network/validator" 72 stateprotocol "github.com/koko1123/flow-go-1/state/protocol" 73 badgerState "github.com/koko1123/flow-go-1/state/protocol/badger" 74 "github.com/koko1123/flow-go-1/state/protocol/blocktimer" 75 "github.com/koko1123/flow-go-1/state/protocol/events/gadgets" 76 "github.com/koko1123/flow-go-1/storage" 77 bstorage "github.com/koko1123/flow-go-1/storage/badger" 78 "github.com/koko1123/flow-go-1/utils/io" 79 ) 80 81 // ObserverBuilder extends cmd.NodeBuilder and declares additional functions needed to bootstrap an Access node 82 // These functions are shared by observer builders. 83 // The Staked network allows the access nodes to communicate among themselves, while the public network allows the 84 // observers and an Access node to communicate. 85 // 86 // public network private network 87 // +------------------------+ 88 // | observer 1 |<--------------------------| 89 // +------------------------+ v 90 // +------------------------+ +----------------------+ +------------------------+ 91 // | observer 2 |<----------------------->| Access Node (staked) |<------------>| All other staked Nodes | 92 // +------------------------+ +----------------------+ +------------------------+ 93 // +------------------------+ ^ 94 // | observer 3 |<--------------------------| 95 // +------------------------+ 96 97 // ObserverServiceConfig defines all the user defined parameters required to bootstrap an access node 98 // For a node running as a standalone process, the config fields will be populated from the command line params, 99 // while for a node running as a library, the config fields are expected to be initialized by the caller. 100 type ObserverServiceConfig struct { 101 bootstrapNodeAddresses []string 102 bootstrapNodePublicKeys []string 103 observerNetworkingKeyPath string 104 bootstrapIdentities flow.IdentityList // the identity list of bootstrap peers the node uses to discover other nodes 105 apiRatelimits map[string]int 106 apiBurstlimits map[string]int 107 rpcConf rpc.Config 108 rpcMetricsEnabled bool 109 executionDataSyncEnabled bool 110 executionDataDir string 111 executionDataStartHeight uint64 112 executionDataConfig edrequester.ExecutionDataConfig 113 apiTimeout time.Duration 114 upstreamNodeAddresses []string 115 upstreamNodePublicKeys []string 116 upstreamIdentities flow.IdentityList // the identity list of upstream peers the node uses to forward API requests to 117 } 118 119 // DefaultObserverServiceConfig defines all the default values for the ObserverServiceConfig 120 func DefaultObserverServiceConfig() *ObserverServiceConfig { 121 homedir, _ := os.UserHomeDir() 122 return &ObserverServiceConfig{ 123 rpcConf: rpc.Config{ 124 UnsecureGRPCListenAddr: "0.0.0.0:9000", 125 SecureGRPCListenAddr: "0.0.0.0:9001", 126 HTTPListenAddr: "0.0.0.0:8000", 127 RESTListenAddr: "", 128 CollectionAddr: "", 129 HistoricalAccessAddrs: "", 130 CollectionClientTimeout: 3 * time.Second, 131 ExecutionClientTimeout: 3 * time.Second, 132 MaxHeightRange: backend.DefaultMaxHeightRange, 133 PreferredExecutionNodeIDs: nil, 134 FixedExecutionNodeIDs: nil, 135 }, 136 rpcMetricsEnabled: false, 137 apiRatelimits: nil, 138 apiBurstlimits: nil, 139 bootstrapNodeAddresses: []string{}, 140 bootstrapNodePublicKeys: []string{}, 141 observerNetworkingKeyPath: cmd.NotSet, 142 executionDataSyncEnabled: false, 143 executionDataDir: filepath.Join(homedir, ".flow", "execution_data"), 144 executionDataStartHeight: 0, 145 executionDataConfig: edrequester.ExecutionDataConfig{ 146 InitialBlockHeight: 0, 147 MaxSearchAhead: edrequester.DefaultMaxSearchAhead, 148 FetchTimeout: edrequester.DefaultFetchTimeout, 149 RetryDelay: edrequester.DefaultRetryDelay, 150 MaxRetryDelay: edrequester.DefaultMaxRetryDelay, 151 }, 152 apiTimeout: 3 * time.Second, 153 upstreamNodeAddresses: []string{}, 154 upstreamNodePublicKeys: []string{}, 155 } 156 } 157 158 // ObserverServiceBuilder provides the common functionality needed to bootstrap a Flow observer service 159 // It is composed of the FlowNodeBuilder, the ObserverServiceConfig and contains all the components and modules needed for the observers 160 type ObserverServiceBuilder struct { 161 *cmd.FlowNodeBuilder 162 *ObserverServiceConfig 163 164 // components 165 LibP2PNode p2p.LibP2PNode 166 FollowerState stateprotocol.MutableState 167 SyncCore *chainsync.Core 168 RpcEng *rpc.Engine 169 FinalizationDistributor *pubsub.FinalizationDistributor 170 FinalizedHeader *synceng.FinalizedHeaderCache 171 Committee hotstuff.Committee 172 Finalized *flow.Header 173 Pending []*flow.Header 174 FollowerCore module.HotStuffFollower 175 ExecutionDataDownloader execution_data.Downloader 176 ExecutionDataRequester state_synchronization.ExecutionDataRequester // for the observer, the sync engine participants provider is the libp2p peer store which is not 177 // available until after the network has started. Hence, a factory function that needs to be called just before 178 // creating the sync engine 179 SyncEngineParticipantsProviderFactory func() module.IdentifierProvider 180 181 // engines 182 FollowerEng *followereng.Engine 183 SyncEng *synceng.Engine 184 185 // Public network 186 peerID peer.ID 187 } 188 189 // deriveBootstrapPeerIdentities derives the Flow Identity of the bootstrap peers from the parameters. 190 // These are the identities of the observers also acting as the DHT bootstrap server 191 func (builder *ObserverServiceBuilder) deriveBootstrapPeerIdentities() error { 192 // if bootstrap identities already provided (as part of alternate initialization as a library the skip reading command 193 // line params) 194 if builder.bootstrapIdentities != nil { 195 return nil 196 } 197 198 ids, err := BootstrapIdentities(builder.bootstrapNodeAddresses, builder.bootstrapNodePublicKeys) 199 if err != nil { 200 return fmt.Errorf("failed to derive bootstrap peer identities: %w", err) 201 } 202 203 builder.bootstrapIdentities = ids 204 205 return nil 206 } 207 208 // deriveBootstrapPeerIdentities derives the Flow Identity of the bootstrap peers from the parameters. 209 // These are the identities of the observers also acting as the DHT bootstrap server 210 func (builder *ObserverServiceBuilder) deriveUpstreamIdentities() error { 211 // if bootstrap identities already provided (as part of alternate initialization as a library the skip reading command 212 // line params) 213 if builder.upstreamIdentities != nil { 214 return nil 215 } 216 217 // BootstrapIdentities converts the bootstrap node addresses and keys to a Flow Identity list where 218 // each Flow Identity is initialized with the passed address, the networking key 219 // and the Node ID set to ZeroID, role set to Access, 0 stake and no staking key. 220 addresses := builder.upstreamNodeAddresses 221 keys := builder.upstreamNodePublicKeys 222 if len(addresses) != len(keys) { 223 return fmt.Errorf("number of addresses and keys provided for the boostrap nodes don't match") 224 } 225 226 ids := make([]*flow.Identity, len(addresses)) 227 for i, address := range addresses { 228 key := keys[i] 229 230 // json unmarshaller needs a quotes before and after the string 231 // the pflags.StringSliceVar does not retain quotes for the command line arg even if escaped with \" 232 // hence this additional check to ensure the key is indeed quoted 233 if !strings.HasPrefix(key, "\"") { 234 key = fmt.Sprintf("\"%s\"", key) 235 } 236 237 // create the identity of the peer by setting only the relevant fields 238 ids[i] = &flow.Identity{ 239 NodeID: flow.ZeroID, // the NodeID is the hash of the staking key and for the public network it does not apply 240 Address: address, 241 Role: flow.RoleAccess, // the upstream node has to be an access node 242 NetworkPubKey: nil, 243 } 244 245 // networking public key 246 var networkKey encodable.NetworkPubKey 247 err := json.Unmarshal([]byte(key), &networkKey) 248 if err == nil { 249 ids[i].NetworkPubKey = networkKey 250 } 251 } 252 253 builder.upstreamIdentities = ids 254 255 return nil 256 } 257 258 func (builder *ObserverServiceBuilder) buildFollowerState() *ObserverServiceBuilder { 259 builder.Module("mutable follower state", func(node *cmd.NodeConfig) error { 260 // For now, we only support state implementations from package badger. 261 // If we ever support different implementations, the following can be replaced by a type-aware factory 262 state, ok := node.State.(*badgerState.State) 263 if !ok { 264 return fmt.Errorf("only implementations of type badger.State are currently supported but read-only state has type %T", node.State) 265 } 266 267 followerState, err := badgerState.NewFollowerState( 268 state, 269 node.Storage.Index, 270 node.Storage.Payloads, 271 node.Tracer, 272 node.ProtocolEvents, 273 blocktimer.DefaultBlockTimer, 274 ) 275 builder.FollowerState = followerState 276 277 return err 278 }) 279 280 return builder 281 } 282 283 func (builder *ObserverServiceBuilder) buildSyncCore() *ObserverServiceBuilder { 284 builder.Module("sync core", func(node *cmd.NodeConfig) error { 285 syncCore, err := chainsync.New(node.Logger, node.SyncCoreConfig, metrics.NewChainSyncCollector()) 286 builder.SyncCore = syncCore 287 288 return err 289 }) 290 291 return builder 292 } 293 294 func (builder *ObserverServiceBuilder) buildCommittee() *ObserverServiceBuilder { 295 builder.Module("committee", func(node *cmd.NodeConfig) error { 296 // initialize consensus committee's membership state 297 // This committee state is for the HotStuff follower, which follows the MAIN CONSENSUS Committee 298 // Note: node.Me.NodeID() is not part of the consensus committee 299 committee, err := committees.NewConsensusCommittee(node.State, node.Me.NodeID()) 300 builder.Committee = committee 301 302 return err 303 }) 304 305 return builder 306 } 307 308 func (builder *ObserverServiceBuilder) buildLatestHeader() *ObserverServiceBuilder { 309 builder.Module("latest header", func(node *cmd.NodeConfig) error { 310 finalized, pending, err := recovery.FindLatest(node.State, node.Storage.Headers) 311 builder.Finalized, builder.Pending = finalized, pending 312 313 return err 314 }) 315 316 return builder 317 } 318 319 func (builder *ObserverServiceBuilder) buildFollowerCore() *ObserverServiceBuilder { 320 builder.Component("follower core", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 321 // create a finalizer that will handle updating the protocol 322 // state when the follower detects newly finalized blocks 323 final := finalizer.NewFinalizer(node.DB, node.Storage.Headers, builder.FollowerState, node.Tracer) 324 325 packer := hotsignature.NewConsensusSigDataPacker(builder.Committee) 326 // initialize the verifier for the protocol consensus 327 verifier := verification.NewCombinedVerifier(builder.Committee, packer) 328 329 followerCore, err := consensus.NewFollower( 330 node.Logger, 331 builder.Committee, 332 node.Storage.Headers, 333 final, 334 verifier, 335 builder.FinalizationDistributor, 336 node.RootBlock.Header, 337 node.RootQC, 338 builder.Finalized, 339 builder.Pending, 340 ) 341 if err != nil { 342 return nil, fmt.Errorf("could not initialize follower core: %w", err) 343 } 344 builder.FollowerCore = followerCore 345 346 return builder.FollowerCore, nil 347 }) 348 349 return builder 350 } 351 352 func (builder *ObserverServiceBuilder) buildFollowerEngine() *ObserverServiceBuilder { 353 builder.Component("follower engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 354 // initialize cleaner for DB 355 cleaner := bstorage.NewCleaner(node.Logger, node.DB, builder.Metrics.CleanCollector, flow.DefaultValueLogGCFrequency) 356 conCache := buffer.NewPendingBlocks() 357 358 followerEng, err := follower.New( 359 node.Logger, 360 node.Network, 361 node.Me, 362 node.Metrics.Engine, 363 node.Metrics.Mempool, 364 cleaner, 365 node.Storage.Headers, 366 node.Storage.Payloads, 367 builder.FollowerState, 368 conCache, 369 builder.FollowerCore, 370 builder.SyncCore, 371 node.Tracer, 372 follower.WithComplianceOptions(compliance.WithSkipNewProposalsThreshold(builder.ComplianceConfig.SkipNewProposalsThreshold)), 373 follower.WithChannel(channels.PublicReceiveBlocks), 374 ) 375 if err != nil { 376 return nil, fmt.Errorf("could not create follower engine: %w", err) 377 } 378 builder.FollowerEng = followerEng 379 380 return builder.FollowerEng, nil 381 }) 382 383 return builder 384 } 385 386 func (builder *ObserverServiceBuilder) buildFinalizedHeader() *ObserverServiceBuilder { 387 builder.Component("finalized snapshot", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 388 finalizedHeader, err := synceng.NewFinalizedHeaderCache(node.Logger, node.State, builder.FinalizationDistributor) 389 if err != nil { 390 return nil, fmt.Errorf("could not create finalized snapshot cache: %w", err) 391 } 392 builder.FinalizedHeader = finalizedHeader 393 394 return builder.FinalizedHeader, nil 395 }) 396 397 return builder 398 } 399 400 func (builder *ObserverServiceBuilder) buildSyncEngine() *ObserverServiceBuilder { 401 builder.Component("sync engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 402 sync, err := synceng.New( 403 node.Logger, 404 node.Metrics.Engine, 405 node.Network, 406 node.Me, 407 node.Storage.Blocks, 408 builder.FollowerEng, 409 builder.SyncCore, 410 builder.FinalizedHeader, 411 builder.SyncEngineParticipantsProviderFactory(), 412 ) 413 if err != nil { 414 return nil, fmt.Errorf("could not create synchronization engine: %w", err) 415 } 416 builder.SyncEng = sync 417 418 return builder.SyncEng, nil 419 }) 420 421 return builder 422 } 423 424 func (builder *ObserverServiceBuilder) BuildConsensusFollower() cmd.NodeBuilder { 425 builder. 426 buildFollowerState(). 427 buildSyncCore(). 428 buildCommittee(). 429 buildLatestHeader(). 430 buildFollowerCore(). 431 buildFollowerEngine(). 432 buildFinalizedHeader(). 433 buildSyncEngine() 434 435 return builder 436 } 437 438 func (builder *ObserverServiceBuilder) BuildExecutionDataRequester() *ObserverServiceBuilder { 439 var ds *badger.Datastore 440 var bs network.BlobService 441 var processedBlockHeight storage.ConsumerProgress 442 var processedNotifications storage.ConsumerProgress 443 444 builder. 445 Module("execution data datastore and blobstore", func(node *cmd.NodeConfig) error { 446 err := os.MkdirAll(builder.executionDataDir, 0700) 447 if err != nil { 448 return err 449 } 450 451 ds, err = badger.NewDatastore(builder.executionDataDir, &badger.DefaultOptions) 452 if err != nil { 453 return err 454 } 455 456 builder.ShutdownFunc(func() error { 457 if err := ds.Close(); err != nil { 458 return fmt.Errorf("could not close execution data datastore: %w", err) 459 } 460 return nil 461 }) 462 463 return nil 464 }). 465 Module("processed block height consumer progress", func(node *cmd.NodeConfig) error { 466 // uses the datastore's DB 467 processedBlockHeight = bstorage.NewConsumerProgress(ds.DB, module.ConsumeProgressExecutionDataRequesterBlockHeight) 468 return nil 469 }). 470 Module("processed notifications consumer progress", func(node *cmd.NodeConfig) error { 471 // uses the datastore's DB 472 processedNotifications = bstorage.NewConsumerProgress(ds.DB, module.ConsumeProgressExecutionDataRequesterNotification) 473 return nil 474 }). 475 Component("execution data service", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 476 var err error 477 bs, err = node.Network.RegisterBlobService(channels.ExecutionDataService, ds, 478 blob.WithBitswapOptions( 479 bitswap.WithTracer( 480 blob.NewTracer(node.Logger.With().Str("blob_service", channels.ExecutionDataService.String()).Logger()), 481 ), 482 ), 483 ) 484 if err != nil { 485 return nil, fmt.Errorf("could not register blob service: %w", err) 486 } 487 488 builder.ExecutionDataDownloader = execution_data.NewDownloader(bs) 489 490 return builder.ExecutionDataDownloader, nil 491 }). 492 Component("execution data requester", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 493 // Validation of the start block height needs to be done after loading state 494 if builder.executionDataStartHeight > 0 { 495 if builder.executionDataStartHeight <= builder.RootBlock.Header.Height { 496 return nil, fmt.Errorf( 497 "execution data start block height (%d) must be greater than the root block height (%d)", 498 builder.executionDataStartHeight, builder.RootBlock.Header.Height) 499 } 500 501 latestSeal, err := builder.State.Sealed().Head() 502 if err != nil { 503 return nil, fmt.Errorf("failed to get latest sealed height") 504 } 505 506 // Note: since the root block of a spork is also sealed in the root protocol state, the 507 // latest sealed height is always equal to the root block height. That means that at the 508 // very beginning of a spork, this check will always fail. Operators should not specify 509 // an InitialBlockHeight when starting from the beginning of a spork. 510 if builder.executionDataStartHeight > latestSeal.Height { 511 return nil, fmt.Errorf( 512 "execution data start block height (%d) must be less than or equal to the latest sealed block height (%d)", 513 builder.executionDataStartHeight, latestSeal.Height) 514 } 515 516 // executionDataStartHeight is provided as the first block to sync, but the 517 // requester expects the initial last processed height, which is the first height - 1 518 builder.executionDataConfig.InitialBlockHeight = builder.executionDataStartHeight - 1 519 } else { 520 builder.executionDataConfig.InitialBlockHeight = builder.RootBlock.Header.Height 521 } 522 523 builder.ExecutionDataRequester = edrequester.New( 524 builder.Logger, 525 metrics.NewExecutionDataRequesterCollector(), 526 builder.ExecutionDataDownloader, 527 processedBlockHeight, 528 processedNotifications, 529 builder.State, 530 builder.Storage.Headers, 531 builder.Storage.Results, 532 builder.Storage.Seals, 533 builder.executionDataConfig, 534 ) 535 536 builder.FinalizationDistributor.AddOnBlockFinalizedConsumer(builder.ExecutionDataRequester.OnBlockFinalized) 537 538 return builder.ExecutionDataRequester, nil 539 }) 540 541 return builder 542 } 543 544 type Option func(*ObserverServiceConfig) 545 546 func NewFlowObserverServiceBuilder(opts ...Option) *ObserverServiceBuilder { 547 config := DefaultObserverServiceConfig() 548 for _, opt := range opts { 549 opt(config) 550 } 551 anb := &ObserverServiceBuilder{ 552 ObserverServiceConfig: config, 553 FlowNodeBuilder: cmd.FlowNode(flow.RoleAccess.String()), 554 FinalizationDistributor: pubsub.NewFinalizationDistributor(), 555 } 556 // the observer gets a version of the root snapshot file that does not contain any node addresses 557 // hence skip all the root snapshot validations that involved an identity address 558 anb.FlowNodeBuilder.SkipNwAddressBasedValidations = true 559 return anb 560 } 561 562 func (builder *ObserverServiceBuilder) ParseFlags() error { 563 564 builder.BaseFlags() 565 566 builder.extraFlags() 567 568 return builder.ParseAndPrintFlags() 569 } 570 571 func (builder *ObserverServiceBuilder) extraFlags() { 572 builder.ExtraFlags(func(flags *pflag.FlagSet) { 573 defaultConfig := DefaultObserverServiceConfig() 574 575 flags.StringVarP(&builder.rpcConf.UnsecureGRPCListenAddr, "rpc-addr", "r", defaultConfig.rpcConf.UnsecureGRPCListenAddr, "the address the unsecured gRPC server listens on") 576 flags.StringVar(&builder.rpcConf.SecureGRPCListenAddr, "secure-rpc-addr", defaultConfig.rpcConf.SecureGRPCListenAddr, "the address the secure gRPC server listens on") 577 flags.StringVarP(&builder.rpcConf.HTTPListenAddr, "http-addr", "h", defaultConfig.rpcConf.HTTPListenAddr, "the address the http proxy server listens on") 578 flags.StringVar(&builder.rpcConf.RESTListenAddr, "rest-addr", defaultConfig.rpcConf.RESTListenAddr, "the address the REST server listens on (if empty the REST server will not be started)") 579 flags.UintVar(&builder.rpcConf.MaxHeightRange, "rpc-max-height-range", defaultConfig.rpcConf.MaxHeightRange, "maximum size for height range requests") 580 flags.StringToIntVar(&builder.apiRatelimits, "api-rate-limits", defaultConfig.apiRatelimits, "per second rate limits for Access API methods e.g. Ping=300,GetTransaction=500 etc.") 581 flags.StringToIntVar(&builder.apiBurstlimits, "api-burst-limits", defaultConfig.apiBurstlimits, "burst limits for Access API methods e.g. Ping=100,GetTransaction=100 etc.") 582 flags.StringVar(&builder.observerNetworkingKeyPath, "observer-networking-key-path", defaultConfig.observerNetworkingKeyPath, "path to the networking key for observer") 583 flags.StringSliceVar(&builder.bootstrapNodeAddresses, "bootstrap-node-addresses", defaultConfig.bootstrapNodeAddresses, "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") 584 flags.StringSliceVar(&builder.bootstrapNodePublicKeys, "bootstrap-node-public-keys", defaultConfig.bootstrapNodePublicKeys, "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....\"") 585 flags.DurationVar(&builder.apiTimeout, "upstream-api-timeout", defaultConfig.apiTimeout, "tcp timeout for Flow API gRPC sockets to upstrem nodes") 586 flags.StringSliceVar(&builder.upstreamNodeAddresses, "upstream-node-addresses", defaultConfig.upstreamNodeAddresses, "the gRPC network addresses of the upstream access node. e.g. access-001.mainnet.flow.org:9000,access-002.mainnet.flow.org:9000") 587 flags.StringSliceVar(&builder.upstreamNodePublicKeys, "upstream-node-public-keys", defaultConfig.upstreamNodePublicKeys, "the networking public key of the upstream access node (in the same order as the upstream node addresses) e.g. \"d57a5e9c5.....\",\"44ded42d....\"") 588 flags.BoolVar(&builder.rpcMetricsEnabled, "rpc-metrics-enabled", defaultConfig.rpcMetricsEnabled, "whether to enable the rpc metrics") 589 590 // ExecutionDataRequester config 591 flags.BoolVar(&builder.executionDataSyncEnabled, "execution-data-sync-enabled", defaultConfig.executionDataSyncEnabled, "whether to enable the execution data sync protocol") 592 flags.StringVar(&builder.executionDataDir, "execution-data-dir", defaultConfig.executionDataDir, "directory to use for Execution Data database") 593 flags.Uint64Var(&builder.executionDataStartHeight, "execution-data-start-height", defaultConfig.executionDataStartHeight, "height of first block to sync execution data from when starting with an empty Execution Data database") 594 flags.Uint64Var(&builder.executionDataConfig.MaxSearchAhead, "execution-data-max-search-ahead", defaultConfig.executionDataConfig.MaxSearchAhead, "max number of heights to search ahead of the lowest outstanding execution data height") 595 flags.DurationVar(&builder.executionDataConfig.FetchTimeout, "execution-data-fetch-timeout", defaultConfig.executionDataConfig.FetchTimeout, "timeout to use when fetching execution data from the network e.g. 300s") 596 flags.DurationVar(&builder.executionDataConfig.RetryDelay, "execution-data-retry-delay", defaultConfig.executionDataConfig.RetryDelay, "initial delay for exponential backoff when fetching execution data fails e.g. 10s") 597 flags.DurationVar(&builder.executionDataConfig.MaxRetryDelay, "execution-data-max-retry-delay", defaultConfig.executionDataConfig.MaxRetryDelay, "maximum delay for exponential backoff when fetching execution data fails e.g. 5m") 598 }).ValidateFlags(func() error { 599 if builder.executionDataSyncEnabled { 600 if builder.executionDataConfig.FetchTimeout <= 0 { 601 return errors.New("execution-data-fetch-timeout must be greater than 0") 602 } 603 if builder.executionDataConfig.RetryDelay <= 0 { 604 return errors.New("execution-data-retry-delay must be greater than 0") 605 } 606 if builder.executionDataConfig.MaxRetryDelay < builder.executionDataConfig.RetryDelay { 607 return errors.New("execution-data-max-retry-delay must be greater than or equal to execution-data-retry-delay") 608 } 609 if builder.executionDataConfig.MaxSearchAhead == 0 { 610 return errors.New("execution-data-max-search-ahead must be greater than 0") 611 } 612 } 613 return nil 614 }) 615 } 616 617 // initNetwork creates the network.Network implementation with the given metrics, middleware, initial list of network 618 // participants and topology used to choose peers from the list of participants. The list of participants can later be 619 // updated by calling network.SetIDs. 620 func (builder *ObserverServiceBuilder) initNetwork(nodeID module.Local, 621 networkMetrics module.NetworkCoreMetrics, 622 middleware network.Middleware, 623 topology network.Topology, 624 receiveCache *netcache.ReceiveCache, 625 ) (*p2p.Network, error) { 626 627 // creates network instance 628 net, err := p2p.NewNetwork(&p2p.NetworkParameters{ 629 Logger: builder.Logger, 630 Codec: cborcodec.NewCodec(), 631 Me: nodeID, 632 MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, 633 Topology: topology, 634 SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), 635 Metrics: networkMetrics, 636 IdentityProvider: builder.IdentityProvider, 637 ReceiveCache: receiveCache, 638 }) 639 if err != nil { 640 return nil, fmt.Errorf("could not initialize network: %w", err) 641 } 642 643 return net, nil 644 } 645 646 func publicNetworkMsgValidators(log zerolog.Logger, idProvider module.IdentityProvider, selfID flow.Identifier) []network.MessageValidator { 647 return []network.MessageValidator{ 648 // filter out messages sent by this node itself 649 validator.ValidateNotSender(selfID), 650 validator.NewAnyValidator( 651 // message should be either from a valid staked node 652 validator.NewOriginValidator( 653 id.NewIdentityFilterIdentifierProvider(filter.IsValidCurrentEpochParticipant, idProvider), 654 ), 655 // or the message should be specifically targeted for this node 656 validator.ValidateTarget(log, selfID), 657 ), 658 } 659 } 660 661 // BootstrapIdentities converts the bootstrap node addresses and keys to a Flow Identity list where 662 // each Flow Identity is initialized with the passed address, the networking key 663 // and the Node ID set to ZeroID, role set to Access, 0 stake and no staking key. 664 func BootstrapIdentities(addresses []string, keys []string) (flow.IdentityList, error) { 665 if len(addresses) != len(keys) { 666 return nil, fmt.Errorf("number of addresses and keys provided for the boostrap nodes don't match") 667 } 668 669 ids := make([]*flow.Identity, len(addresses)) 670 for i, address := range addresses { 671 bytes, err := hex.DecodeString(keys[i]) 672 if err != nil { 673 return nil, fmt.Errorf("failed to decode secured GRPC server public key hex %w", err) 674 } 675 676 publicFlowNetworkingKey, err := crypto.DecodePublicKey(crypto.ECDSAP256, bytes) 677 if err != nil { 678 return nil, fmt.Errorf("failed to get public flow networking key could not decode public key bytes %w", err) 679 } 680 681 // create the identity of the peer by setting only the relevant fields 682 ids[i] = &flow.Identity{ 683 NodeID: flow.ZeroID, // the NodeID is the hash of the staking key and for the public network it does not apply 684 Address: address, 685 Role: flow.RoleAccess, // the upstream node has to be an access node 686 NetworkPubKey: publicFlowNetworkingKey, 687 } 688 } 689 return ids, nil 690 } 691 692 func (builder *ObserverServiceBuilder) initNodeInfo() error { 693 // use the networking key that was loaded from the configured file 694 networkingKey, err := loadNetworkingKey(builder.observerNetworkingKeyPath) 695 if err != nil { 696 return fmt.Errorf("could not load networking private key: %w", err) 697 } 698 699 pubKey, err := keyutils.LibP2PPublicKeyFromFlow(networkingKey.PublicKey()) 700 if err != nil { 701 return fmt.Errorf("could not load networking public key: %w", err) 702 } 703 704 builder.peerID, err = peer.IDFromPublicKey(pubKey) 705 if err != nil { 706 return fmt.Errorf("could not get peer ID from public key: %w", err) 707 } 708 709 builder.NodeID, err = translator.NewPublicNetworkIDTranslator().GetFlowID(builder.peerID) 710 if err != nil { 711 return fmt.Errorf("could not get flow node ID: %w", err) 712 } 713 714 builder.NodeConfig.NetworkKey = networkingKey // copy the key to NodeConfig 715 builder.NodeConfig.StakingKey = nil // no staking key for the observer 716 717 return nil 718 } 719 720 func (builder *ObserverServiceBuilder) InitIDProviders() { 721 builder.Module("id providers", func(node *cmd.NodeConfig) error { 722 idCache, err := cache.NewProtocolStateIDCache(node.Logger, node.State, builder.ProtocolEvents) 723 if err != nil { 724 return fmt.Errorf("could not initialize ProtocolStateIDCache: %w", err) 725 } 726 builder.IDTranslator = translator.NewHierarchicalIDTranslator(idCache, translator.NewPublicNetworkIDTranslator()) 727 728 // The following wrapper allows to black-list byzantine nodes via an admin command: 729 // the wrapper overrides the 'Ejected' flag of blocked nodes to true 730 builder.IdentityProvider, err = cache.NewNodeBlocklistWrapper(idCache, node.DB) 731 if err != nil { 732 return fmt.Errorf("could not initialize NodeBlocklistWrapper: %w", err) 733 } 734 735 // use the default identifier provider 736 builder.SyncEngineParticipantsProviderFactory = func() module.IdentifierProvider { 737 return id.NewCustomIdentifierProvider(func() flow.IdentifierList { 738 pids := builder.LibP2PNode.GetPeersForProtocol(unicast.FlowProtocolID(builder.SporkID)) 739 result := make(flow.IdentifierList, 0, len(pids)) 740 741 for _, pid := range pids { 742 // exclude own Identifier 743 if pid == builder.peerID { 744 continue 745 } 746 747 if flowID, err := builder.IDTranslator.GetFlowID(pid); err != nil { 748 // TODO: this is an instance of "log error and continue with best effort" anti-pattern 749 builder.Logger.Err(err).Str("peer", pid.String()).Msg("failed to translate to Flow ID") 750 } else { 751 result = append(result, flowID) 752 } 753 } 754 755 return result 756 }) 757 } 758 759 return nil 760 }) 761 } 762 763 func (builder *ObserverServiceBuilder) Initialize() error { 764 if err := builder.deriveBootstrapPeerIdentities(); err != nil { 765 return err 766 } 767 768 if err := builder.deriveUpstreamIdentities(); err != nil { 769 return err 770 } 771 772 if err := builder.validateParams(); err != nil { 773 return err 774 } 775 776 if err := builder.initNodeInfo(); err != nil { 777 return err 778 } 779 780 builder.InitIDProviders() 781 782 builder.enqueuePublicNetworkInit() 783 784 builder.enqueueConnectWithStakedAN() 785 786 builder.enqueueRPCServer() 787 788 if builder.BaseConfig.MetricsEnabled { 789 builder.EnqueueMetricsServerInit() 790 if err := builder.RegisterBadgerMetrics(); err != nil { 791 return err 792 } 793 } 794 795 builder.PreInit(builder.initObserverLocal()) 796 797 return nil 798 } 799 800 func (builder *ObserverServiceBuilder) validateParams() error { 801 if builder.BaseConfig.BindAddr == cmd.NotSet || builder.BaseConfig.BindAddr == "" { 802 return errors.New("bind address not specified") 803 } 804 if builder.ObserverServiceConfig.observerNetworkingKeyPath == cmd.NotSet { 805 return errors.New("networking key not provided") 806 } 807 if len(builder.bootstrapIdentities) > 0 { 808 return nil 809 } 810 if len(builder.bootstrapNodeAddresses) == 0 { 811 return errors.New("no bootstrap node address provided") 812 } 813 if len(builder.bootstrapNodeAddresses) != len(builder.bootstrapNodePublicKeys) { 814 return errors.New("number of bootstrap node addresses and public keys should match") 815 } 816 if len(builder.upstreamNodePublicKeys) > 0 && len(builder.upstreamNodeAddresses) != len(builder.upstreamNodePublicKeys) { 817 return errors.New("number of upstream node addresses and public keys must match if public keys given") 818 } 819 return nil 820 } 821 822 // initLibP2PFactory creates the LibP2P factory function for the given node ID and network key for the observer. 823 // The factory function is later passed into the initMiddleware function to eventually instantiate the p2p.LibP2PNode instance 824 // The LibP2P host is created with the following options: 825 // * DHT as client and seeded with the given bootstrap peers 826 // * The specified bind address as the listen address 827 // * The passed in private key as the libp2p key 828 // * No connection gater 829 // * No connection manager 830 // * No peer manager 831 // * Default libp2p pubsub options 832 func (builder *ObserverServiceBuilder) initLibP2PFactory(networkKey crypto.PrivateKey) p2pbuilder.LibP2PFactoryFunc { 833 return func() (p2p.LibP2PNode, error) { 834 var pis []peer.AddrInfo 835 836 for _, b := range builder.bootstrapIdentities { 837 pi, err := utils.PeerAddressInfo(*b) 838 839 if err != nil { 840 return nil, fmt.Errorf("could not extract peer address info from bootstrap identity %v: %w", b, err) 841 } 842 843 pis = append(pis, pi) 844 } 845 846 node, err := p2pbuilder.NewNodeBuilder( 847 builder.Logger, 848 builder.Metrics.Network, 849 builder.BaseConfig.BindAddr, 850 networkKey, 851 builder.SporkID, 852 builder.LibP2PResourceManagerConfig). 853 SetSubscriptionFilter( 854 subscription.NewRoleBasedFilter( 855 subscription.UnstakedRole, builder.IdentityProvider, 856 ), 857 ). 858 SetRoutingSystem(func(ctx context.Context, h host.Host) (routing.Routing, error) { 859 return p2pdht.NewDHT(ctx, h, unicast.FlowPublicDHTProtocolID(builder.SporkID), 860 builder.Logger, 861 builder.Metrics.Network, 862 p2pdht.AsClient(), 863 dht.BootstrapPeers(pis...), 864 ) 865 }). 866 Build() 867 868 if err != nil { 869 return nil, err 870 } 871 872 builder.LibP2PNode = node 873 874 return builder.LibP2PNode, nil 875 } 876 } 877 878 // initObserverLocal initializes the observer's ID, network key and network address 879 // Currently, it reads a node-info.priv.json like any other node. 880 // TODO: read the node ID from the special bootstrap files 881 func (builder *ObserverServiceBuilder) initObserverLocal() func(node *cmd.NodeConfig) error { 882 return func(node *cmd.NodeConfig) error { 883 // for an observer, set the identity here explicitly since it will not be found in the protocol state 884 self := &flow.Identity{ 885 NodeID: node.NodeID, 886 NetworkPubKey: node.NetworkKey.PublicKey(), 887 StakingPubKey: nil, // no staking key needed for the observer 888 Role: flow.RoleAccess, // observer can only run as an access node 889 Address: builder.BindAddr, 890 } 891 892 var err error 893 node.Me, err = local.NewNoKey(self) 894 if err != nil { 895 return fmt.Errorf("could not initialize local: %w", err) 896 } 897 return nil 898 } 899 } 900 901 // Build enqueues the sync engine and the follower engine for the observer. 902 // Currently, the observer only runs the follower engine. 903 func (builder *ObserverServiceBuilder) Build() (cmd.Node, error) { 904 builder.BuildConsensusFollower() 905 if builder.executionDataSyncEnabled { 906 builder.BuildExecutionDataRequester() 907 } 908 return builder.FlowNodeBuilder.Build() 909 } 910 911 // enqueuePublicNetworkInit enqueues the observer network component initialized for the observer 912 func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { 913 var libp2pNode p2p.LibP2PNode 914 builder. 915 Component("public libp2p node", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 916 libP2PFactory := builder.initLibP2PFactory(node.NetworkKey) 917 918 var err error 919 libp2pNode, err = libP2PFactory() 920 if err != nil { 921 return nil, fmt.Errorf("could not create public libp2p node: %w", err) 922 } 923 924 return libp2pNode, nil 925 }). 926 Component("public network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 927 var heroCacheCollector module.HeroCacheMetrics = metrics.NewNoopCollector() 928 if builder.HeroCacheMetricsEnable { 929 heroCacheCollector = metrics.NetworkReceiveCacheMetricsFactory(builder.MetricsRegisterer) 930 } 931 receiveCache := netcache.NewHeroReceiveCache(builder.NetworkReceivedMessageCacheSize, 932 builder.Logger, 933 heroCacheCollector) 934 935 err := node.Metrics.Mempool.Register(metrics.ResourceNetworkingReceiveCache, receiveCache.Size) 936 if err != nil { 937 return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) 938 } 939 940 msgValidators := publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID) 941 942 builder.initMiddleware(node.NodeID, libp2pNode, msgValidators...) 943 944 // topology is nil since it is automatically managed by libp2p 945 net, err := builder.initNetwork(builder.Me, builder.Metrics.Network, builder.Middleware, nil, receiveCache) 946 if err != nil { 947 return nil, err 948 } 949 950 builder.Network = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) 951 952 builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr) 953 954 idEvents := gadgets.NewIdentityDeltas(builder.Middleware.UpdateNodeAddresses) 955 builder.ProtocolEvents.AddConsumer(idEvents) 956 957 return builder.Network, nil 958 }) 959 } 960 961 // enqueueConnectWithStakedAN enqueues the upstream connector component which connects the libp2p host of the observer 962 // service with the AN. 963 // Currently, there is an issue with LibP2P stopping advertisements of subscribed topics if no peers are connected 964 // (https://github.com/libp2p/go-libp2p-pubsub/issues/442). This means that an observer could end up not being 965 // discovered by other observers if it subscribes to a topic before connecting to the AN. Hence, the need 966 // of an explicit connect to the AN before the node attempts to subscribe to topics. 967 func (builder *ObserverServiceBuilder) enqueueConnectWithStakedAN() { 968 builder.Component("upstream connector", func(_ *cmd.NodeConfig) (module.ReadyDoneAware, error) { 969 return consensus_follower.NewUpstreamConnector(builder.bootstrapIdentities, builder.LibP2PNode, builder.Logger), nil 970 }) 971 } 972 973 func (builder *ObserverServiceBuilder) enqueueRPCServer() { 974 builder.Component("RPC engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { 975 engineBuilder, err := rpc.NewBuilder( 976 node.Logger, 977 node.State, 978 builder.rpcConf, 979 nil, 980 nil, 981 node.Storage.Blocks, 982 node.Storage.Headers, 983 node.Storage.Collections, 984 node.Storage.Transactions, 985 node.Storage.Receipts, 986 node.Storage.Results, 987 node.RootChainID, 988 nil, 989 nil, 990 0, 991 0, 992 false, 993 builder.rpcMetricsEnabled, 994 builder.apiRatelimits, 995 builder.apiBurstlimits, 996 ) 997 if err != nil { 998 return nil, err 999 } 1000 1001 // upstream access node forwarder 1002 forwarder, err := apiproxy.NewFlowAccessAPIForwarder(builder.upstreamIdentities, builder.apiTimeout) 1003 if err != nil { 1004 return nil, err 1005 } 1006 1007 proxy := &apiproxy.FlowAccessAPIRouter{ 1008 Logger: builder.Logger, 1009 Metrics: metrics.NewObserverCollector(), 1010 Upstream: forwarder, 1011 Observer: protocol.NewHandler(protocol.New( 1012 node.State, 1013 node.Storage.Blocks, 1014 node.Storage.Headers, 1015 backend.NewNetworkAPI(node.State, node.RootChainID, backend.DefaultSnapshotHistoryLimit), 1016 )), 1017 } 1018 1019 // build the rpc engine 1020 builder.RpcEng, err = engineBuilder. 1021 WithNewHandler(proxy). 1022 WithLegacy(). 1023 Build() 1024 if err != nil { 1025 return nil, err 1026 } 1027 return builder.RpcEng, nil 1028 }) 1029 } 1030 1031 // initMiddleware creates the network.Middleware implementation with the libp2p factory function, metrics, peer update 1032 // interval, and validators. The network.Middleware is then passed into the initNetwork function. 1033 func (builder *ObserverServiceBuilder) initMiddleware(nodeID flow.Identifier, 1034 libp2pNode p2p.LibP2PNode, 1035 validators ...network.MessageValidator) network.Middleware { 1036 slashingViolationsConsumer := slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network) 1037 builder.Middleware = middleware.NewMiddleware( 1038 builder.Logger, 1039 libp2pNode, nodeID, 1040 builder.Metrics.Bitswap, 1041 builder.SporkID, 1042 middleware.DefaultUnicastTimeout, 1043 builder.IDTranslator, 1044 builder.CodecFactory(), 1045 slashingViolationsConsumer, 1046 middleware.WithMessageValidators(validators...)) 1047 1048 return builder.Middleware 1049 } 1050 1051 func loadNetworkingKey(path string) (crypto.PrivateKey, error) { 1052 data, err := io.ReadFile(path) 1053 if err != nil { 1054 return nil, fmt.Errorf("could not read networking key (path=%s): %w", path, err) 1055 } 1056 1057 keyBytes, err := hex.DecodeString(strings.Trim(string(data), "\n ")) 1058 if err != nil { 1059 return nil, fmt.Errorf("could not hex decode networking key (path=%s): %w", path, err) 1060 } 1061 1062 networkingKey, err := crypto.DecodePrivateKey(crypto.ECDSASecp256k1, keyBytes) 1063 if err != nil { 1064 return nil, fmt.Errorf("could not decode networking key (path=%s): %w", path, err) 1065 } 1066 1067 return networkingKey, nil 1068 }