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  }