github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/observer/node_builder/observer_builder.go (about)

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