github.com/koko1123/flow-go-1@v0.29.6/follower/follower_builder.go (about)

     1  package follower
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"strings"
     9  
    10  	dht "github.com/libp2p/go-libp2p-kad-dht"
    11  	"github.com/libp2p/go-libp2p/core/host"
    12  	"github.com/libp2p/go-libp2p/core/peer"
    13  	"github.com/libp2p/go-libp2p/core/routing"
    14  	"github.com/rs/zerolog"
    15  
    16  	"github.com/koko1123/flow-go-1/cmd"
    17  	"github.com/koko1123/flow-go-1/consensus"
    18  	"github.com/koko1123/flow-go-1/consensus/hotstuff"
    19  	"github.com/koko1123/flow-go-1/consensus/hotstuff/committees"
    20  	"github.com/koko1123/flow-go-1/consensus/hotstuff/notifications/pubsub"
    21  	hotsignature "github.com/koko1123/flow-go-1/consensus/hotstuff/signature"
    22  	"github.com/koko1123/flow-go-1/consensus/hotstuff/verification"
    23  	recovery "github.com/koko1123/flow-go-1/consensus/recovery/protocol"
    24  	"github.com/onflow/flow-go/crypto"
    25  	"github.com/koko1123/flow-go-1/engine/common/follower"
    26  	followereng "github.com/koko1123/flow-go-1/engine/common/follower"
    27  	synceng "github.com/koko1123/flow-go-1/engine/common/synchronization"
    28  	"github.com/koko1123/flow-go-1/model/encodable"
    29  	"github.com/koko1123/flow-go-1/model/flow"
    30  	"github.com/koko1123/flow-go-1/model/flow/filter"
    31  	"github.com/koko1123/flow-go-1/module"
    32  	"github.com/koko1123/flow-go-1/module/buffer"
    33  	synchronization "github.com/koko1123/flow-go-1/module/chainsync"
    34  	"github.com/koko1123/flow-go-1/module/compliance"
    35  	finalizer "github.com/koko1123/flow-go-1/module/finalizer/consensus"
    36  	"github.com/koko1123/flow-go-1/module/id"
    37  	"github.com/koko1123/flow-go-1/module/local"
    38  	"github.com/koko1123/flow-go-1/module/metrics"
    39  	"github.com/koko1123/flow-go-1/module/upstream"
    40  	"github.com/koko1123/flow-go-1/network"
    41  	netcache "github.com/koko1123/flow-go-1/network/cache"
    42  	"github.com/koko1123/flow-go-1/network/channels"
    43  	cborcodec "github.com/koko1123/flow-go-1/network/codec/cbor"
    44  	"github.com/koko1123/flow-go-1/network/converter"
    45  	"github.com/koko1123/flow-go-1/network/p2p"
    46  	"github.com/koko1123/flow-go-1/network/p2p/cache"
    47  	p2pdht "github.com/koko1123/flow-go-1/network/p2p/dht"
    48  	"github.com/koko1123/flow-go-1/network/p2p/keyutils"
    49  	"github.com/koko1123/flow-go-1/network/p2p/middleware"
    50  	"github.com/koko1123/flow-go-1/network/p2p/p2pbuilder"
    51  	"github.com/koko1123/flow-go-1/network/p2p/subscription"
    52  	"github.com/koko1123/flow-go-1/network/p2p/translator"
    53  	"github.com/koko1123/flow-go-1/network/p2p/unicast"
    54  	"github.com/koko1123/flow-go-1/network/p2p/utils"
    55  	"github.com/koko1123/flow-go-1/network/slashing"
    56  	"github.com/koko1123/flow-go-1/network/validator"
    57  	"github.com/koko1123/flow-go-1/state/protocol"
    58  	badgerState "github.com/koko1123/flow-go-1/state/protocol/badger"
    59  	"github.com/koko1123/flow-go-1/state/protocol/blocktimer"
    60  	"github.com/koko1123/flow-go-1/state/protocol/events/gadgets"
    61  	storage "github.com/koko1123/flow-go-1/storage/badger"
    62  )
    63  
    64  // FlowBuilder extends cmd.NodeBuilder and declares additional functions needed to bootstrap an Access node
    65  // These functions are shared by staked and observer builders.
    66  // The Staked network allows the staked nodes to communicate among themselves, while the public network allows the
    67  // observers and an Access node to communicate.
    68  //
    69  //                                 public network                           staked network
    70  //  +------------------------+
    71  //  | observer 1             |<--------------------------|
    72  //  +------------------------+                           v
    73  //  +------------------------+                         +----------------------+              +------------------------+
    74  //  | observer 2             |<----------------------->| Access Node (staked) |<------------>| All other staked Nodes |
    75  //  +------------------------+                         +----------------------+              +------------------------+
    76  //  +------------------------+                           ^
    77  //  | observer 3             |<--------------------------|
    78  //  +------------------------+
    79  
    80  // FollowerServiceConfig defines all the user defined parameters required to bootstrap an access node
    81  // For a node running as a standalone process, the config fields will be populated from the command line params,
    82  // while for a node running as a library, the config fields are expected to be initialized by the caller.
    83  type FollowerServiceConfig struct {
    84  	bootstrapNodeAddresses  []string
    85  	bootstrapNodePublicKeys []string
    86  	bootstrapIdentities     flow.IdentityList // the identity list of bootstrap peers the node uses to discover other nodes
    87  	NetworkKey              crypto.PrivateKey // the networking key passed in by the caller when being used as a library
    88  	baseOptions             []cmd.Option
    89  }
    90  
    91  // DefaultFollowerServiceConfig defines all the default values for the FollowerServiceConfig
    92  func DefaultFollowerServiceConfig() *FollowerServiceConfig {
    93  	return &FollowerServiceConfig{
    94  		bootstrapNodeAddresses:  []string{},
    95  		bootstrapNodePublicKeys: []string{},
    96  	}
    97  }
    98  
    99  // FollowerServiceBuilder provides the common functionality needed to bootstrap a Flow staked and observer
   100  // It is composed of the FlowNodeBuilder, the FollowerServiceConfig and contains all the components and modules needed for the
   101  // staked and observers
   102  type FollowerServiceBuilder struct {
   103  	*cmd.FlowNodeBuilder
   104  	*FollowerServiceConfig
   105  
   106  	// components
   107  	LibP2PNode              p2p.LibP2PNode
   108  	FollowerState           protocol.MutableState
   109  	SyncCore                *synchronization.Core
   110  	FinalizationDistributor *pubsub.FinalizationDistributor
   111  	FinalizedHeader         *synceng.FinalizedHeaderCache
   112  	Committee               hotstuff.Committee
   113  	Finalized               *flow.Header
   114  	Pending                 []*flow.Header
   115  	FollowerCore            module.HotStuffFollower
   116  	// for the observer, the sync engine participants provider is the libp2p peer store which is not
   117  	// available until after the network has started. Hence, a factory function that needs to be called just before
   118  	// creating the sync engine
   119  	SyncEngineParticipantsProviderFactory func() module.IdentifierProvider
   120  
   121  	// engines
   122  	FollowerEng *followereng.Engine
   123  	SyncEng     *synceng.Engine
   124  
   125  	peerID peer.ID
   126  }
   127  
   128  // deriveBootstrapPeerIdentities derives the Flow Identity of the bootstrap peers from the parameters.
   129  // These are the identities of the staked and observers also acting as the DHT bootstrap server
   130  func (builder *FollowerServiceBuilder) deriveBootstrapPeerIdentities() error {
   131  	// if bootstrap identities already provided (as part of alternate initialization as a library the skip reading command
   132  	// line params)
   133  	if builder.bootstrapIdentities != nil {
   134  		return nil
   135  	}
   136  
   137  	ids, err := BootstrapIdentities(builder.bootstrapNodeAddresses, builder.bootstrapNodePublicKeys)
   138  	if err != nil {
   139  		return fmt.Errorf("failed to derive bootstrap peer identities: %w", err)
   140  	}
   141  
   142  	builder.bootstrapIdentities = ids
   143  
   144  	return nil
   145  }
   146  
   147  func (builder *FollowerServiceBuilder) buildFollowerState() *FollowerServiceBuilder {
   148  	builder.Module("mutable follower state", func(node *cmd.NodeConfig) error {
   149  		// For now, we only support state implementations from package badger.
   150  		// If we ever support different implementations, the following can be replaced by a type-aware factory
   151  		state, ok := node.State.(*badgerState.State)
   152  		if !ok {
   153  			return fmt.Errorf("only implementations of type badger.State are currently supported but read-only state has type %T", node.State)
   154  		}
   155  
   156  		followerState, err := badgerState.NewFollowerState(
   157  			state,
   158  			node.Storage.Index,
   159  			node.Storage.Payloads,
   160  			node.Tracer,
   161  			node.ProtocolEvents,
   162  			blocktimer.DefaultBlockTimer,
   163  		)
   164  		builder.FollowerState = followerState
   165  
   166  		return err
   167  	})
   168  
   169  	return builder
   170  }
   171  
   172  func (builder *FollowerServiceBuilder) buildSyncCore() *FollowerServiceBuilder {
   173  	builder.Module("sync core", func(node *cmd.NodeConfig) error {
   174  		syncCore, err := synchronization.New(node.Logger, node.SyncCoreConfig, metrics.NewChainSyncCollector())
   175  		builder.SyncCore = syncCore
   176  
   177  		return err
   178  	})
   179  
   180  	return builder
   181  }
   182  
   183  func (builder *FollowerServiceBuilder) buildCommittee() *FollowerServiceBuilder {
   184  	builder.Module("committee", func(node *cmd.NodeConfig) error {
   185  		// initialize consensus committee's membership state
   186  		// This committee state is for the HotStuff follower, which follows the MAIN CONSENSUS Committee
   187  		// Note: node.Me.NodeID() is not part of the consensus committee
   188  		committee, err := committees.NewConsensusCommittee(node.State, node.Me.NodeID())
   189  		builder.Committee = committee
   190  
   191  		return err
   192  	})
   193  
   194  	return builder
   195  }
   196  
   197  func (builder *FollowerServiceBuilder) buildLatestHeader() *FollowerServiceBuilder {
   198  	builder.Module("latest header", func(node *cmd.NodeConfig) error {
   199  		finalized, pending, err := recovery.FindLatest(node.State, node.Storage.Headers)
   200  		builder.Finalized, builder.Pending = finalized, pending
   201  
   202  		return err
   203  	})
   204  
   205  	return builder
   206  }
   207  
   208  func (builder *FollowerServiceBuilder) buildFollowerCore() *FollowerServiceBuilder {
   209  	builder.Component("follower core", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) {
   210  		// create a finalizer that will handle updating the protocol
   211  		// state when the follower detects newly finalized blocks
   212  		final := finalizer.NewFinalizer(node.DB, node.Storage.Headers, builder.FollowerState, node.Tracer)
   213  
   214  		packer := hotsignature.NewConsensusSigDataPacker(builder.Committee)
   215  		// initialize the verifier for the protocol consensus
   216  		verifier := verification.NewCombinedVerifier(builder.Committee, packer)
   217  
   218  		followerCore, err := consensus.NewFollower(node.Logger, builder.Committee, node.Storage.Headers, final, verifier,
   219  			builder.FinalizationDistributor, node.RootBlock.Header, node.RootQC, builder.Finalized, builder.Pending)
   220  		if err != nil {
   221  			return nil, fmt.Errorf("could not initialize follower core: %w", err)
   222  		}
   223  		builder.FollowerCore = followerCore
   224  
   225  		return builder.FollowerCore, nil
   226  	})
   227  
   228  	return builder
   229  }
   230  
   231  func (builder *FollowerServiceBuilder) buildFollowerEngine() *FollowerServiceBuilder {
   232  	builder.Component("follower engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) {
   233  		// initialize cleaner for DB
   234  		cleaner := storage.NewCleaner(node.Logger, node.DB, builder.Metrics.CleanCollector, flow.DefaultValueLogGCFrequency)
   235  		conCache := buffer.NewPendingBlocks()
   236  
   237  		followerEng, err := follower.New(
   238  			node.Logger,
   239  			node.Network,
   240  			node.Me,
   241  			node.Metrics.Engine,
   242  			node.Metrics.Mempool,
   243  			cleaner,
   244  			node.Storage.Headers,
   245  			node.Storage.Payloads,
   246  			builder.FollowerState,
   247  			conCache,
   248  			builder.FollowerCore,
   249  			builder.SyncCore,
   250  			node.Tracer,
   251  			follower.WithComplianceOptions(compliance.WithSkipNewProposalsThreshold(builder.ComplianceConfig.SkipNewProposalsThreshold)),
   252  			follower.WithChannel(channels.PublicReceiveBlocks),
   253  		)
   254  		if err != nil {
   255  			return nil, fmt.Errorf("could not create follower engine: %w", err)
   256  		}
   257  		builder.FollowerEng = followerEng
   258  
   259  		return builder.FollowerEng, nil
   260  	})
   261  
   262  	return builder
   263  }
   264  
   265  func (builder *FollowerServiceBuilder) buildFinalizedHeader() *FollowerServiceBuilder {
   266  	builder.Component("finalized snapshot", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) {
   267  		finalizedHeader, err := synceng.NewFinalizedHeaderCache(node.Logger, node.State, builder.FinalizationDistributor)
   268  		if err != nil {
   269  			return nil, fmt.Errorf("could not create finalized snapshot cache: %w", err)
   270  		}
   271  		builder.FinalizedHeader = finalizedHeader
   272  
   273  		return builder.FinalizedHeader, nil
   274  	})
   275  
   276  	return builder
   277  }
   278  
   279  func (builder *FollowerServiceBuilder) buildSyncEngine() *FollowerServiceBuilder {
   280  	builder.Component("sync engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) {
   281  		sync, err := synceng.New(
   282  			node.Logger,
   283  			node.Metrics.Engine,
   284  			node.Network,
   285  			node.Me,
   286  			node.Storage.Blocks,
   287  			builder.FollowerEng,
   288  			builder.SyncCore,
   289  			builder.FinalizedHeader,
   290  			builder.SyncEngineParticipantsProviderFactory(),
   291  		)
   292  		if err != nil {
   293  			return nil, fmt.Errorf("could not create synchronization engine: %w", err)
   294  		}
   295  		builder.SyncEng = sync
   296  
   297  		return builder.SyncEng, nil
   298  	})
   299  
   300  	return builder
   301  }
   302  
   303  func (builder *FollowerServiceBuilder) BuildConsensusFollower() cmd.NodeBuilder {
   304  	builder.
   305  		buildFollowerState().
   306  		buildSyncCore().
   307  		buildCommittee().
   308  		buildLatestHeader().
   309  		buildFollowerCore().
   310  		buildFollowerEngine().
   311  		buildFinalizedHeader().
   312  		buildSyncEngine()
   313  
   314  	return builder
   315  }
   316  
   317  type FollowerOption func(*FollowerServiceConfig)
   318  
   319  func WithBootStrapPeers(bootstrapNodes ...*flow.Identity) FollowerOption {
   320  	return func(config *FollowerServiceConfig) {
   321  		config.bootstrapIdentities = bootstrapNodes
   322  	}
   323  }
   324  
   325  func WithNetworkKey(key crypto.PrivateKey) FollowerOption {
   326  	return func(config *FollowerServiceConfig) {
   327  		config.NetworkKey = key
   328  	}
   329  }
   330  
   331  func WithBaseOptions(baseOptions []cmd.Option) FollowerOption {
   332  	return func(config *FollowerServiceConfig) {
   333  		config.baseOptions = baseOptions
   334  	}
   335  }
   336  
   337  func FlowConsensusFollowerService(opts ...FollowerOption) *FollowerServiceBuilder {
   338  	config := DefaultFollowerServiceConfig()
   339  	for _, opt := range opts {
   340  		opt(config)
   341  	}
   342  	ret := &FollowerServiceBuilder{
   343  		FollowerServiceConfig: config,
   344  		// TODO: using RoleAccess here for now. This should be refactored eventually to have its own role type
   345  		FlowNodeBuilder:         cmd.FlowNode(flow.RoleAccess.String(), config.baseOptions...),
   346  		FinalizationDistributor: pubsub.NewFinalizationDistributor(),
   347  	}
   348  	// the observer gets a version of the root snapshot file that does not contain any node addresses
   349  	// hence skip all the root snapshot validations that involved an identity address
   350  	ret.FlowNodeBuilder.SkipNwAddressBasedValidations = true
   351  	return ret
   352  }
   353  
   354  // initNetwork creates the network.Network implementation with the given metrics, middleware, initial list of network
   355  // participants and topology used to choose peers from the list of participants. The list of participants can later be
   356  // updated by calling network.SetIDs.
   357  func (builder *FollowerServiceBuilder) initNetwork(nodeID module.Local,
   358  	networkMetrics module.NetworkMetrics,
   359  	middleware network.Middleware,
   360  	topology network.Topology,
   361  	receiveCache *netcache.ReceiveCache,
   362  ) (*p2p.Network, error) {
   363  
   364  	codec := cborcodec.NewCodec()
   365  
   366  	// creates network instance
   367  	net, err := p2p.NewNetwork(&p2p.NetworkParameters{
   368  		Logger:              builder.Logger,
   369  		Codec:               codec,
   370  		Me:                  nodeID,
   371  		MiddlewareFactory:   func() (network.Middleware, error) { return builder.Middleware, nil },
   372  		Topology:            topology,
   373  		SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware),
   374  		Metrics:             networkMetrics,
   375  		IdentityProvider:    builder.IdentityProvider,
   376  		ReceiveCache:        receiveCache,
   377  	})
   378  	if err != nil {
   379  		return nil, fmt.Errorf("could not initialize network: %w", err)
   380  	}
   381  
   382  	return net, nil
   383  }
   384  
   385  func publicNetworkMsgValidators(log zerolog.Logger, idProvider module.IdentityProvider, selfID flow.Identifier) []network.MessageValidator {
   386  	return []network.MessageValidator{
   387  		// filter out messages sent by this node itself
   388  		validator.ValidateNotSender(selfID),
   389  		validator.NewAnyValidator(
   390  			// message should be either from a valid staked node
   391  			validator.NewOriginValidator(
   392  				id.NewIdentityFilterIdentifierProvider(filter.IsValidCurrentEpochParticipant, idProvider),
   393  			),
   394  			// or the message should be specifically targeted for this node
   395  			validator.ValidateTarget(log, selfID),
   396  		),
   397  	}
   398  }
   399  
   400  // BootstrapIdentities converts the bootstrap node addresses and keys to a Flow Identity list where
   401  // each Flow Identity is initialized with the passed address, the networking key
   402  // and the Node ID set to ZeroID, role set to Access, 0 stake and no staking key.
   403  func BootstrapIdentities(addresses []string, keys []string) (flow.IdentityList, error) {
   404  
   405  	if len(addresses) != len(keys) {
   406  		return nil, fmt.Errorf("number of addresses and keys provided for the boostrap nodes don't match")
   407  	}
   408  
   409  	ids := make([]*flow.Identity, len(addresses))
   410  	for i, address := range addresses {
   411  		key := keys[i]
   412  
   413  		// json unmarshaller needs a quotes before and after the string
   414  		// the pflags.StringSliceVar does not retain quotes for the command line arg even if escaped with \"
   415  		// hence this additional check to ensure the key is indeed quoted
   416  		if !strings.HasPrefix(key, "\"") {
   417  			key = fmt.Sprintf("\"%s\"", key)
   418  		}
   419  		// networking public key
   420  		var networkKey encodable.NetworkPubKey
   421  		err := json.Unmarshal([]byte(key), &networkKey)
   422  		if err != nil {
   423  			return nil, err
   424  		}
   425  
   426  		// create the identity of the peer by setting only the relevant fields
   427  		ids[i] = &flow.Identity{
   428  			NodeID:        flow.ZeroID, // the NodeID is the hash of the staking key and for the public network it does not apply
   429  			Address:       address,
   430  			Role:          flow.RoleAccess, // the upstream node has to be an access node
   431  			NetworkPubKey: networkKey,
   432  		}
   433  	}
   434  	return ids, nil
   435  }
   436  
   437  func (builder *FollowerServiceBuilder) initNodeInfo() error {
   438  	// use the networking key that has been passed in the config, or load from the configured file
   439  	networkingKey := builder.FollowerServiceConfig.NetworkKey
   440  
   441  	pubKey, err := keyutils.LibP2PPublicKeyFromFlow(networkingKey.PublicKey())
   442  	if err != nil {
   443  		return fmt.Errorf("could not load networking public key: %w", err)
   444  	}
   445  
   446  	builder.peerID, err = peer.IDFromPublicKey(pubKey)
   447  	if err != nil {
   448  		return fmt.Errorf("could not get peer ID from public key: %w", err)
   449  	}
   450  
   451  	builder.NodeID, err = translator.NewPublicNetworkIDTranslator().GetFlowID(builder.peerID)
   452  	if err != nil {
   453  		return fmt.Errorf("could not get flow node ID: %w", err)
   454  	}
   455  
   456  	builder.NodeConfig.NetworkKey = networkingKey // copy the key to NodeConfig
   457  	builder.NodeConfig.StakingKey = nil           // no staking key for the observer
   458  
   459  	return nil
   460  }
   461  
   462  func (builder *FollowerServiceBuilder) InitIDProviders() {
   463  	builder.Module("id providers", func(node *cmd.NodeConfig) error {
   464  		idCache, err := cache.NewProtocolStateIDCache(node.Logger, node.State, builder.ProtocolEvents)
   465  		if err != nil {
   466  			return fmt.Errorf("could not initialize ProtocolStateIDCache: %w", err)
   467  		}
   468  		builder.IDTranslator = translator.NewHierarchicalIDTranslator(idCache, translator.NewPublicNetworkIDTranslator())
   469  
   470  		// The following wrapper allows to black-list byzantine nodes via an admin command:
   471  		// the wrapper overrides the 'Ejected' flag of blocked nodes to true
   472  		builder.IdentityProvider, err = cache.NewNodeBlocklistWrapper(idCache, node.DB)
   473  		if err != nil {
   474  			return fmt.Errorf("could not initialize NodeBlocklistWrapper: %w", err)
   475  		}
   476  
   477  		// use the default identifier provider
   478  		builder.SyncEngineParticipantsProviderFactory = func() module.IdentifierProvider {
   479  			return id.NewCustomIdentifierProvider(func() flow.IdentifierList {
   480  				pids := builder.LibP2PNode.GetPeersForProtocol(unicast.FlowProtocolID(builder.SporkID))
   481  				result := make(flow.IdentifierList, 0, len(pids))
   482  
   483  				for _, pid := range pids {
   484  					// exclude own Identifier
   485  					if pid == builder.peerID {
   486  						continue
   487  					}
   488  
   489  					if flowID, err := builder.IDTranslator.GetFlowID(pid); err != nil {
   490  						// TODO: this is an instance of "log error and continue with best effort" anti-pattern
   491  						builder.Logger.Err(err).Str("peer", pid.String()).Msg("failed to translate to Flow ID")
   492  					} else {
   493  						result = append(result, flowID)
   494  					}
   495  				}
   496  
   497  				return result
   498  			})
   499  		}
   500  
   501  		return nil
   502  	})
   503  }
   504  
   505  func (builder *FollowerServiceBuilder) Initialize() error {
   506  	if err := builder.deriveBootstrapPeerIdentities(); err != nil {
   507  		return err
   508  	}
   509  
   510  	if err := builder.validateParams(); err != nil {
   511  		return err
   512  	}
   513  
   514  	if err := builder.initNodeInfo(); err != nil {
   515  		return err
   516  	}
   517  
   518  	builder.InitIDProviders()
   519  
   520  	builder.enqueuePublicNetworkInit()
   521  
   522  	builder.enqueueConnectWithStakedAN()
   523  
   524  	if builder.BaseConfig.MetricsEnabled {
   525  		builder.EnqueueMetricsServerInit()
   526  		if err := builder.RegisterBadgerMetrics(); err != nil {
   527  			return err
   528  		}
   529  	}
   530  
   531  	builder.PreInit(builder.initObserverLocal())
   532  
   533  	return nil
   534  }
   535  
   536  func (builder *FollowerServiceBuilder) validateParams() error {
   537  	if builder.BaseConfig.BindAddr == cmd.NotSet || builder.BaseConfig.BindAddr == "" {
   538  		return errors.New("bind address not specified")
   539  	}
   540  	if builder.FollowerServiceConfig.NetworkKey == nil {
   541  		return errors.New("networking key not provided")
   542  	}
   543  	if len(builder.bootstrapIdentities) > 0 {
   544  		return nil
   545  	}
   546  	if len(builder.bootstrapNodeAddresses) == 0 {
   547  		return errors.New("no bootstrap node address provided")
   548  	}
   549  	if len(builder.bootstrapNodeAddresses) != len(builder.bootstrapNodePublicKeys) {
   550  		return errors.New("number of bootstrap node addresses and public keys should match")
   551  	}
   552  	return nil
   553  }
   554  
   555  // initLibP2PFactory creates the LibP2P factory function for the given node ID and network key for the observer.
   556  // The factory function is later passed into the initMiddleware function to eventually instantiate the p2p.LibP2PNode instance
   557  // The LibP2P host is created with the following options:
   558  //   - DHT as client and seeded with the given bootstrap peers
   559  //   - The specified bind address as the listen address
   560  //   - The passed in private key as the libp2p key
   561  //   - No connection gater
   562  //   - No connection manager
   563  //   - No peer manager
   564  //   - Default libp2p pubsub options
   565  func (builder *FollowerServiceBuilder) initLibP2PFactory(networkKey crypto.PrivateKey) p2pbuilder.LibP2PFactoryFunc {
   566  	return func() (p2p.LibP2PNode, error) {
   567  		var pis []peer.AddrInfo
   568  
   569  		for _, b := range builder.bootstrapIdentities {
   570  			pi, err := utils.PeerAddressInfo(*b)
   571  
   572  			if err != nil {
   573  				return nil, fmt.Errorf("could not extract peer address info from bootstrap identity %v: %w", b, err)
   574  			}
   575  
   576  			pis = append(pis, pi)
   577  		}
   578  
   579  		node, err := p2pbuilder.NewNodeBuilder(
   580  			builder.Logger,
   581  			builder.Metrics.Network,
   582  			builder.BaseConfig.BindAddr,
   583  			networkKey,
   584  			builder.SporkID,
   585  			builder.LibP2PResourceManagerConfig).
   586  			SetSubscriptionFilter(
   587  				subscription.NewRoleBasedFilter(
   588  					subscription.UnstakedRole, builder.IdentityProvider,
   589  				),
   590  			).
   591  			SetRoutingSystem(func(ctx context.Context, h host.Host) (routing.Routing, error) {
   592  				return p2pdht.NewDHT(ctx, h, unicast.FlowPublicDHTProtocolID(builder.SporkID),
   593  					builder.Logger,
   594  					builder.Metrics.Network,
   595  					p2pdht.AsClient(),
   596  					dht.BootstrapPeers(pis...),
   597  				)
   598  			}).
   599  			Build()
   600  
   601  		if err != nil {
   602  			return nil, fmt.Errorf("could not build libp2p node: %w", err)
   603  		}
   604  
   605  		builder.LibP2PNode = node
   606  
   607  		return builder.LibP2PNode, nil
   608  	}
   609  }
   610  
   611  // initObserverLocal initializes the observer's ID, network key and network address
   612  // Currently, it reads a node-info.priv.json like any other node.
   613  // TODO: read the node ID from the special bootstrap files
   614  func (builder *FollowerServiceBuilder) initObserverLocal() func(node *cmd.NodeConfig) error {
   615  	return func(node *cmd.NodeConfig) error {
   616  		// for an observer, set the identity here explicitly since it will not be found in the protocol state
   617  		self := &flow.Identity{
   618  			NodeID:        node.NodeID,
   619  			NetworkPubKey: node.NetworkKey.PublicKey(),
   620  			StakingPubKey: nil,             // no staking key needed for the observer
   621  			Role:          flow.RoleAccess, // observer can only run as an access node
   622  			Address:       builder.BindAddr,
   623  		}
   624  
   625  		var err error
   626  		node.Me, err = local.NewNoKey(self)
   627  		if err != nil {
   628  			return fmt.Errorf("could not initialize local: %w", err)
   629  		}
   630  		return nil
   631  	}
   632  }
   633  
   634  // Build enqueues the sync engine and the follower engine for the observer.
   635  // Currently, the observer only runs the follower engine.
   636  func (builder *FollowerServiceBuilder) Build() (cmd.Node, error) {
   637  	builder.BuildConsensusFollower()
   638  	return builder.FlowNodeBuilder.Build()
   639  }
   640  
   641  // enqueuePublicNetworkInit enqueues the observer network component initialized for the observer
   642  func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() {
   643  	var libp2pNode p2p.LibP2PNode
   644  	builder.
   645  		Component("public libp2p node", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) {
   646  			libP2PFactory := builder.initLibP2PFactory(node.NetworkKey)
   647  
   648  			var err error
   649  			libp2pNode, err = libP2PFactory()
   650  			if err != nil {
   651  				return nil, fmt.Errorf("could not create public libp2p node: %w", err)
   652  			}
   653  
   654  			return libp2pNode, nil
   655  		}).
   656  		Component("public network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) {
   657  			var heroCacheCollector module.HeroCacheMetrics = metrics.NewNoopCollector()
   658  			if builder.HeroCacheMetricsEnable {
   659  				heroCacheCollector = metrics.NetworkReceiveCacheMetricsFactory(builder.MetricsRegisterer)
   660  			}
   661  			receiveCache := netcache.NewHeroReceiveCache(builder.NetworkReceivedMessageCacheSize,
   662  				builder.Logger,
   663  				heroCacheCollector)
   664  
   665  			err := node.Metrics.Mempool.Register(metrics.ResourceNetworkingReceiveCache, receiveCache.Size)
   666  			if err != nil {
   667  				return nil, fmt.Errorf("could not register networking receive cache metric: %w", err)
   668  			}
   669  
   670  			msgValidators := publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)
   671  
   672  			builder.initMiddleware(node.NodeID, libp2pNode, msgValidators...)
   673  
   674  			// topology is nil since it is automatically managed by libp2p
   675  			net, err := builder.initNetwork(builder.Me, builder.Metrics.Network, builder.Middleware, nil, receiveCache)
   676  			if err != nil {
   677  				return nil, err
   678  			}
   679  
   680  			builder.Network = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee)
   681  
   682  			builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr)
   683  
   684  			idEvents := gadgets.NewIdentityDeltas(builder.Middleware.UpdateNodeAddresses)
   685  			builder.ProtocolEvents.AddConsumer(idEvents)
   686  
   687  			return builder.Network, nil
   688  		})
   689  }
   690  
   691  // enqueueConnectWithStakedAN enqueues the upstream connector component which connects the libp2p host of the observer
   692  // AN with the staked AN.
   693  // Currently, there is an issue with LibP2P stopping advertisements of subscribed topics if no peers are connected
   694  // (https://github.com/libp2p/go-libp2p-pubsub/issues/442). This means that an observer could end up not being
   695  // discovered by other observers if it subscribes to a topic before connecting to the staked AN. Hence, the need
   696  // of an explicit connect to the staked AN before the node attempts to subscribe to topics.
   697  func (builder *FollowerServiceBuilder) enqueueConnectWithStakedAN() {
   698  	builder.Component("upstream connector", func(_ *cmd.NodeConfig) (module.ReadyDoneAware, error) {
   699  		return upstream.NewUpstreamConnector(builder.bootstrapIdentities, builder.LibP2PNode, builder.Logger), nil
   700  	})
   701  }
   702  
   703  // initMiddleware creates the network.Middleware implementation with the libp2p factory function, metrics, peer update
   704  // interval, and validators. The network.Middleware is then passed into the initNetwork function.
   705  func (builder *FollowerServiceBuilder) initMiddleware(nodeID flow.Identifier,
   706  	libp2pNode p2p.LibP2PNode,
   707  	validators ...network.MessageValidator) network.Middleware {
   708  	slashingViolationsConsumer := slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network)
   709  	builder.Middleware = middleware.NewMiddleware(
   710  		builder.Logger,
   711  		libp2pNode,
   712  		nodeID,
   713  		builder.Metrics.Bitswap,
   714  		builder.SporkID,
   715  		middleware.DefaultUnicastTimeout,
   716  		builder.IDTranslator,
   717  		builder.CodecFactory(),
   718  		slashingViolationsConsumer,
   719  		middleware.WithMessageValidators(validators...))
   720  
   721  	return builder.Middleware
   722  }