Help us shape the future of Go! Take the 2026 Go Developer Survey

sensor

package
v0.1.109 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 30, 2026 License: AGPL-3.0 Imports: 36 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var SensorCmd = &cobra.Command{
	Use:   "sensor [nodes file]",
	Short: "Start a devp2p sensor that discovers other peers and will receive blocks and transactions.",
	Long:  sensorUsage,
	Args:  cobra.MinimumNArgs(1),
	PreRunE: func(cmd *cobra.Command, args []string) (err error) {
		inputSensorParams.NodesFile = args[0]
		_, err = p2p.ReadNodeSet(inputSensorParams.NodesFile)
		if err != nil {
			log.Warn().Err(err).Msgf("Creating nodes file %v because it does not exist", inputSensorParams.NodesFile)
		}

		if len(inputSensorParams.StaticNodesFile) > 0 {
			inputSensorParams.staticNodes, err = p2p.ReadNodeSet(inputSensorParams.StaticNodesFile)
			if err != nil {
				log.Warn().Err(err).Msgf("Static nodes file %v not found", inputSensorParams.StaticNodesFile)
			}
		}

		if len(inputSensorParams.TrustedNodesFile) > 0 {
			inputSensorParams.trustedNodes, err = p2p.ReadNodeSet(inputSensorParams.TrustedNodesFile)
			if err != nil {
				log.Warn().Err(err).Msgf("Trusted nodes file %v not found", inputSensorParams.TrustedNodesFile)
			}
		}

		if len(inputSensorParams.Bootnodes) > 0 {
			inputSensorParams.bootnodes, err = p2p.ParseBootnodes(inputSensorParams.Bootnodes)
			if err != nil {
				return fmt.Errorf("unable to parse bootnodes: %w", err)
			}
		}

		if inputSensorParams.NetworkID == 0 {
			return errors.New("network ID must be greater than zero")
		}

		inputSensorParams.privateKey, err = crypto.GenerateKey()
		if err != nil {
			return err
		}

		if len(inputSensorParams.KeyFile) > 0 {
			var privateKey *ecdsa.PrivateKey
			privateKey, err = crypto.LoadECDSA(inputSensorParams.KeyFile)

			if err != nil {
				log.Warn().Err(err).Msg("Key file was not found, generating a new key file")

				err = crypto.SaveECDSA(inputSensorParams.KeyFile, inputSensorParams.privateKey)
				if err != nil {
					return err
				}
			} else {
				inputSensorParams.privateKey = privateKey
			}
		}

		if len(inputSensorParams.PrivateKey) > 0 {
			inputSensorParams.privateKey, err = crypto.HexToECDSA(inputSensorParams.PrivateKey)
			if err != nil {
				log.Error().Err(err).Msg("Failed to parse PrivateKey")
				return err
			}
		}

		inputSensorParams.nat, err = nat.Parse(inputSensorParams.NAT)
		if err != nil {
			log.Error().Err(err).Msg("Failed to parse NAT")
			return err
		}

		return nil
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		ctx, stop := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM)
		defer stop()

		db, err := newDatabase(ctx)
		if err != nil {
			return err
		}

		rpcBlock, err := getLatestBlock(inputSensorParams.RPC)
		if err != nil {
			return err
		}

		head := eth.NewBlockPacket{
			Block: rpcBlock.ToBlock(),
			TD:    rpcBlock.TotalDifficulty.ToBigInt(),
		}

		peersGauge := promauto.NewGauge(prometheus.GaugeOpts{
			Namespace: "sensor",
			Name:      "peers",
			Help:      "The number of peers the sensor is connected to",
		})

		metrics := p2p.NewBlockMetrics(head.Block)

		conns := p2p.NewConns(p2p.ConnsOptions{
			BlocksCache:                inputSensorParams.BlocksCache,
			TxsCache:                   inputSensorParams.TxsCache,
			KnownTxsBloom:              inputSensorParams.KnownTxsBloom,
			KnownBlocksMax:             inputSensorParams.KnownBlocksMax,
			Head:                       head,
			ShouldBroadcastTx:          inputSensorParams.ShouldBroadcastTx,
			ShouldBroadcastTxHashes:    inputSensorParams.ShouldBroadcastTxHashes,
			ShouldBroadcastBlocks:      inputSensorParams.ShouldBroadcastBlocks,
			ShouldBroadcastBlockHashes: inputSensorParams.ShouldBroadcastBlockHashes,
		})

		opts := p2p.EthProtocolOptions{
			Context:                    ctx,
			Database:                   db,
			GenesisHash:                common.HexToHash(inputSensorParams.GenesisHash),
			RPC:                        inputSensorParams.RPC,
			SensorID:                   inputSensorParams.SensorID,
			NetworkID:                  inputSensorParams.NetworkID,
			Conns:                      conns,
			ForkID:                     forkid.ID{Hash: [4]byte(inputSensorParams.ForkID)},
			RequestsCache:              inputSensorParams.RequestsCache,
			ParentsCache:               inputSensorParams.ParentsCache,
			ShouldBroadcastTx:          inputSensorParams.ShouldBroadcastTx,
			ShouldBroadcastTxHashes:    inputSensorParams.ShouldBroadcastTxHashes,
			ShouldBroadcastBlocks:      inputSensorParams.ShouldBroadcastBlocks,
			ShouldBroadcastBlockHashes: inputSensorParams.ShouldBroadcastBlockHashes,
		}

		config := ethp2p.Config{
			PrivateKey:     inputSensorParams.privateKey,
			BootstrapNodes: inputSensorParams.bootnodes,
			StaticNodes:    inputSensorParams.staticNodes,
			TrustedNodes:   inputSensorParams.trustedNodes,
			MaxPeers:       inputSensorParams.MaxPeers,
			ListenAddr:     fmt.Sprintf(":%d", inputSensorParams.Port),
			DiscAddr:       fmt.Sprintf(":%d", inputSensorParams.DiscoveryPort),
			DialRatio:      inputSensorParams.DialRatio,
			NAT:            inputSensorParams.nat,
			DiscoveryV4:    !inputSensorParams.NoDiscovery,
			DiscoveryV5:    !inputSensorParams.NoDiscovery,
			Protocols: []ethp2p.Protocol{
				p2p.NewEthProtocol(66, opts),
				p2p.NewEthProtocol(67, opts),
				p2p.NewEthProtocol(68, opts),
				p2p.NewEthProtocol(69, opts),
			},
		}

		server := ethp2p.Server{Config: config}

		log.Info().Str("enode", server.Self().URLv4()).Msg("Starting sensor")

		if err = server.Start(); err != nil {
			return err
		}
		defer stopServer(&server)

		events := make(chan *ethp2p.PeerEvent)
		sub := server.SubscribeEvents(events)
		defer sub.Unsubscribe()

		ticker := time.NewTicker(2 * time.Second)
		defer ticker.Stop()

		if inputSensorParams.ShouldRunPprof {
			go handlePprof()
		}

		if inputSensorParams.ShouldRunPrometheus {
			go handlePrometheus()
		}

		go handleAPI(&server, conns)
		go handleRPC(conns, inputSensorParams.NetworkID)
		go handleDNSDiscovery(&server)

		for {
			select {
			case <-ticker.C:
				peersGauge.Set(float64(server.PeerCount()))
				db.WritePeers(ctx, server.Peers(), time.Now())
				metrics.Update(conns.HeadBlock().Block, conns.OldestBlock())
				writePeers(server.Peers())
			case <-ctx.Done():
				log.Info().Msg("Stopping sensor...")
				return nil
			case event := <-events:
				log.Debug().Any("event", event).Send()
			case err := <-sub.Err():
				log.Error().Err(err).Send()
			}
		}
	},
}

SensorCmd represents the sensor command. This is responsible for starting a sensor and transmitting blocks and transactions to a database.

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL