2018-11-10 17:32:45 +00:00
|
|
|
/**
|
|
|
|
* Bootnode
|
|
|
|
*
|
2019-08-18 19:54:20 +00:00
|
|
|
* A node which implements the DiscoveryV5 protocol for peer
|
2018-11-10 17:32:45 +00:00
|
|
|
* discovery. The purpose of this service is to provide a starting point for
|
|
|
|
* newly connected services to find other peers outside of their network.
|
|
|
|
*
|
|
|
|
* Usage: Run bootnode --help for flag options.
|
|
|
|
*/
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2019-09-23 17:24:16 +00:00
|
|
|
"context"
|
2019-08-18 19:54:20 +00:00
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/rand"
|
2019-09-12 00:04:35 +00:00
|
|
|
"encoding/hex"
|
2018-11-10 17:32:45 +00:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
2020-04-13 04:11:09 +00:00
|
|
|
"io"
|
2019-08-18 19:54:20 +00:00
|
|
|
"net"
|
2019-09-23 15:18:58 +00:00
|
|
|
"net/http"
|
2019-09-17 23:24:08 +00:00
|
|
|
"os"
|
2019-08-18 19:54:20 +00:00
|
|
|
|
2019-08-21 15:30:22 +00:00
|
|
|
"github.com/btcsuite/btcd/btcec"
|
2019-09-18 08:44:25 +00:00
|
|
|
gethlog "github.com/ethereum/go-ethereum/log"
|
2019-09-06 19:20:20 +00:00
|
|
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
2019-09-23 17:24:16 +00:00
|
|
|
ds "github.com/ipfs/go-datastore"
|
|
|
|
dsync "github.com/ipfs/go-datastore/sync"
|
|
|
|
logging "github.com/ipfs/go-log"
|
|
|
|
libp2p "github.com/libp2p/go-libp2p"
|
2019-08-21 15:30:22 +00:00
|
|
|
"github.com/libp2p/go-libp2p-core/crypto"
|
2019-09-23 17:24:16 +00:00
|
|
|
kaddht "github.com/libp2p/go-libp2p-kad-dht"
|
|
|
|
dhtopts "github.com/libp2p/go-libp2p-kad-dht/opts"
|
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
2019-09-06 19:20:20 +00:00
|
|
|
"github.com/pkg/errors"
|
2020-04-14 20:27:03 +00:00
|
|
|
"github.com/prysmaticlabs/go-bitfield"
|
|
|
|
"github.com/prysmaticlabs/go-ssz"
|
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
2020-04-11 20:12:48 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/logutil"
|
2020-04-14 20:27:03 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2019-01-10 04:19:33 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/version"
|
2019-09-01 03:05:36 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-09-01 22:29:58 +00:00
|
|
|
_ "go.uber.org/automaxprocs"
|
2018-11-10 17:32:45 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2019-09-23 17:24:16 +00:00
|
|
|
debug = flag.Bool("debug", false, "Enable debug logging")
|
2020-04-11 20:12:48 +00:00
|
|
|
logFileName = flag.String("log-file", "", "Specify log filename, relative or absolute")
|
2019-09-23 17:24:16 +00:00
|
|
|
privateKey = flag.String("private", "", "Private key to use for peer ID")
|
|
|
|
discv5port = flag.Int("discv5-port", 4000, "Port to listen for discv5 connections")
|
|
|
|
kademliaPort = flag.Int("kad-port", 4500, "Port to listen for connections to kad DHT")
|
|
|
|
metricsPort = flag.Int("metrics-port", 5000, "Port to listen for connections")
|
2020-04-17 05:40:14 +00:00
|
|
|
externalIP = flag.String("external-ip", "", "External IP for the bootnode")
|
|
|
|
disableKad = flag.Bool("disable-kad", false, "Disables the bootnode from running kademlia dht")
|
|
|
|
log = logrus.WithField("prefix", "bootnode")
|
2018-11-10 17:32:45 +00:00
|
|
|
)
|
|
|
|
|
2019-09-23 17:24:16 +00:00
|
|
|
const dhtProtocol = "/prysm/0.0.0/dht"
|
2020-04-17 05:40:14 +00:00
|
|
|
const defaultIP = "127.0.0.1"
|
2019-09-23 17:24:16 +00:00
|
|
|
|
2019-09-23 15:18:58 +00:00
|
|
|
type handler struct {
|
|
|
|
listener *discover.UDPv5
|
|
|
|
}
|
|
|
|
|
2018-11-10 17:32:45 +00:00
|
|
|
func main() {
|
|
|
|
flag.Parse()
|
2019-01-10 04:19:33 +00:00
|
|
|
|
2020-04-11 20:12:48 +00:00
|
|
|
if *logFileName != "" {
|
|
|
|
if err := logutil.ConfigurePersistentLogging(*logFileName); err != nil {
|
|
|
|
log.WithError(err).Error("Failed to configuring logging to disk.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-10 04:19:33 +00:00
|
|
|
fmt.Printf("Starting bootnode. Version: %s\n", version.GetVersion())
|
|
|
|
|
2018-11-10 17:32:45 +00:00
|
|
|
if *debug {
|
2019-09-01 03:05:36 +00:00
|
|
|
logrus.SetLevel(logrus.DebugLevel)
|
2019-09-17 23:24:08 +00:00
|
|
|
|
|
|
|
// Geth specific logging.
|
|
|
|
glogger := gethlog.NewGlogHandler(gethlog.StreamHandler(os.Stderr, gethlog.TerminalFormat(false)))
|
|
|
|
glogger.Verbosity(gethlog.LvlTrace)
|
|
|
|
gethlog.Root().SetHandler(glogger)
|
|
|
|
|
|
|
|
log.Debug("Debug logging enabled.")
|
2018-11-10 17:32:45 +00:00
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
privKey, interfacePrivKey := extractPrivateKey()
|
2019-09-06 19:20:20 +00:00
|
|
|
cfg := discover.Config{
|
2019-09-23 17:24:16 +00:00
|
|
|
PrivateKey: privKey,
|
2019-09-06 19:20:20 +00:00
|
|
|
}
|
2020-04-17 05:40:14 +00:00
|
|
|
listener := createListener(defaultIP, *discv5port, cfg)
|
2018-11-10 17:32:45 +00:00
|
|
|
|
2019-08-18 19:54:20 +00:00
|
|
|
node := listener.Self()
|
2019-09-06 19:20:20 +00:00
|
|
|
log.Infof("Running bootnode: %s", node.String())
|
2019-05-10 15:56:30 +00:00
|
|
|
|
2020-04-17 05:40:14 +00:00
|
|
|
if !*disableKad {
|
|
|
|
startKademliaDHT(interfacePrivKey)
|
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
|
2019-09-23 15:18:58 +00:00
|
|
|
handler := &handler{
|
|
|
|
listener: listener,
|
|
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("/p2p", handler.httpHandler)
|
|
|
|
|
|
|
|
if err := http.ListenAndServe(fmt.Sprintf(":%d", *metricsPort), mux); err != nil {
|
|
|
|
log.Fatalf("Failed to start server %v", err)
|
|
|
|
}
|
|
|
|
|
2019-08-18 19:54:20 +00:00
|
|
|
select {}
|
|
|
|
}
|
2018-11-10 17:32:45 +00:00
|
|
|
|
2019-09-23 17:24:16 +00:00
|
|
|
func startKademliaDHT(privKey crypto.PrivKey) {
|
|
|
|
|
|
|
|
if *debug {
|
|
|
|
logging.SetDebugLogging()
|
|
|
|
}
|
2020-04-17 05:40:14 +00:00
|
|
|
ipAddr := defaultIP
|
|
|
|
if *externalIP != "" {
|
|
|
|
ipAddr = *externalIP
|
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
|
2020-04-17 05:40:14 +00:00
|
|
|
listen, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d", ipAddr, *kademliaPort))
|
2019-09-23 17:24:16 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Failed to construct new multiaddress. %v", err)
|
|
|
|
}
|
|
|
|
opts := []libp2p.Option{
|
|
|
|
libp2p.ListenAddrs(listen),
|
|
|
|
}
|
|
|
|
opts = append(opts, libp2p.Identity(privKey))
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
host, err := libp2p.New(ctx, opts...)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Failed to create new host. %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dopts := []dhtopts.Option{
|
|
|
|
dhtopts.Datastore(dsync.MutexWrap(ds.NewMapDatastore())),
|
|
|
|
dhtopts.Protocols(
|
2020-01-08 02:36:55 +00:00
|
|
|
dhtProtocol,
|
2019-09-23 17:24:16 +00:00
|
|
|
),
|
|
|
|
}
|
|
|
|
|
|
|
|
dht, err := kaddht.New(ctx, host, dopts...)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Failed to create new dht: %v", err)
|
|
|
|
}
|
|
|
|
if err := dht.Bootstrap(context.Background()); err != nil {
|
|
|
|
log.Fatalf("Failed to bootstrap DHT. %v", err)
|
|
|
|
}
|
|
|
|
|
2020-04-17 05:40:14 +00:00
|
|
|
fmt.Printf("Running Kademlia DHT bootnode: /ip4/%s/tcp/%d/p2p/%s\n", ipAddr, *kademliaPort, host.ID().Pretty())
|
2019-09-23 17:24:16 +00:00
|
|
|
}
|
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
func createListener(ipAddr string, port int, cfg discover.Config) *discover.UDPv5 {
|
2019-09-02 18:23:07 +00:00
|
|
|
ip := net.ParseIP(ipAddr)
|
|
|
|
if ip.To4() == nil {
|
2020-04-17 05:40:14 +00:00
|
|
|
log.Fatalf("IPV4 address not provided instead %s was provided", defaultIP)
|
2019-09-02 18:23:07 +00:00
|
|
|
}
|
2019-08-18 19:54:20 +00:00
|
|
|
udpAddr := &net.UDPAddr{
|
2019-09-06 19:20:20 +00:00
|
|
|
IP: ip,
|
2019-08-18 19:54:20 +00:00
|
|
|
Port: port,
|
2019-05-10 15:56:30 +00:00
|
|
|
}
|
2019-08-18 19:54:20 +00:00
|
|
|
conn, err := net.ListenUDP("udp4", udpAddr)
|
2019-05-10 15:56:30 +00:00
|
|
|
if err != nil {
|
2019-08-18 19:54:20 +00:00
|
|
|
log.Fatal(err)
|
2019-05-10 15:56:30 +00:00
|
|
|
}
|
2020-04-17 05:40:14 +00:00
|
|
|
if *externalIP != "" {
|
|
|
|
ip = net.ParseIP(*externalIP)
|
|
|
|
}
|
2019-09-06 19:20:20 +00:00
|
|
|
localNode, err := createLocalNode(cfg.PrivateKey, ip, port)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2018-11-10 17:32:45 +00:00
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
network, err := discover.ListenV5(conn, localNode, cfg)
|
2019-08-18 19:54:20 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
return network
|
2018-11-10 17:32:45 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 15:18:58 +00:00
|
|
|
func (h *handler) httpHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.WriteHeader(http.StatusOK)
|
2020-04-13 04:11:09 +00:00
|
|
|
write := func(w io.Writer, b []byte) {
|
|
|
|
if _, err := w.Write(b); err != nil {
|
|
|
|
log.WithError(err).Error("Failed to write to http response")
|
|
|
|
}
|
|
|
|
}
|
2019-09-23 15:18:58 +00:00
|
|
|
allNodes := h.listener.AllNodes()
|
2020-04-13 04:11:09 +00:00
|
|
|
write(w, []byte("Nodes stored in the table:\n"))
|
2019-09-23 15:18:58 +00:00
|
|
|
for i, n := range allNodes {
|
2020-04-13 04:11:09 +00:00
|
|
|
write(w, []byte(fmt.Sprintf("Node %d\n", i)))
|
|
|
|
write(w, []byte(n.String()+"\n"))
|
|
|
|
write(w, []byte("Node ID: "+n.ID().String()+"\n"))
|
|
|
|
write(w, []byte("IP: "+n.IP().String()+"\n"))
|
|
|
|
write(w, []byte(fmt.Sprintf("UDP Port: %d", n.UDP())+"\n"))
|
|
|
|
write(w, []byte(fmt.Sprintf("TCP Port: %d", n.UDP())+"\n\n"))
|
2019-09-23 15:18:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
func createLocalNode(privKey *ecdsa.PrivateKey, ipAddr net.IP, port int) (*enode.LocalNode, error) {
|
|
|
|
db, err := enode.OpenDB("")
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "Could not open node's peer database")
|
|
|
|
}
|
|
|
|
|
2020-04-14 20:27:03 +00:00
|
|
|
forkID := &pb.ENRForkID{
|
|
|
|
CurrentForkDigest: []byte{0, 0, 0, 0},
|
|
|
|
NextForkVersion: params.BeaconConfig().NextForkVersion,
|
|
|
|
NextForkEpoch: params.BeaconConfig().NextForkEpoch,
|
|
|
|
}
|
|
|
|
forkEntry, err := ssz.Marshal(forkID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "Could not marshal fork id")
|
|
|
|
}
|
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
localNode := enode.NewLocalNode(db, privKey)
|
|
|
|
ipEntry := enr.IP(ipAddr)
|
|
|
|
udpEntry := enr.UDP(port)
|
2019-12-10 05:35:16 +00:00
|
|
|
localNode.SetFallbackIP(ipAddr)
|
|
|
|
localNode.SetFallbackUDP(port)
|
2019-09-06 19:20:20 +00:00
|
|
|
localNode.Set(ipEntry)
|
|
|
|
localNode.Set(udpEntry)
|
2020-04-14 20:27:03 +00:00
|
|
|
localNode.Set(enr.WithEntry("eth2", forkEntry))
|
|
|
|
localNode.Set(enr.WithEntry("attnets", bitfield.NewBitvector64()))
|
2019-09-06 19:20:20 +00:00
|
|
|
|
|
|
|
return localNode, nil
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:24:16 +00:00
|
|
|
func extractPrivateKey() (*ecdsa.PrivateKey, crypto.PrivKey) {
|
2019-08-18 19:54:20 +00:00
|
|
|
var privKey *ecdsa.PrivateKey
|
2019-09-23 17:24:16 +00:00
|
|
|
var interfaceKey crypto.PrivKey
|
2018-11-10 17:32:45 +00:00
|
|
|
if *privateKey != "" {
|
2019-09-12 00:04:35 +00:00
|
|
|
dst, err := hex.DecodeString(*privateKey)
|
2018-11-10 17:32:45 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-09-12 00:04:35 +00:00
|
|
|
unmarshalledKey, err := crypto.UnmarshalSecp256k1PrivateKey(dst)
|
2018-11-10 17:32:45 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
interfaceKey = unmarshalledKey
|
2019-08-21 15:30:22 +00:00
|
|
|
privKey = (*ecdsa.PrivateKey)((*btcec.PrivateKey)(unmarshalledKey.(*crypto.Secp256k1PrivateKey)))
|
2019-08-18 19:54:20 +00:00
|
|
|
|
2018-11-10 17:32:45 +00:00
|
|
|
} else {
|
2019-08-21 15:30:22 +00:00
|
|
|
privInterfaceKey, _, err := crypto.GenerateSecp256k1Key(rand.Reader)
|
2019-08-18 19:54:20 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
interfaceKey = privInterfaceKey
|
2019-08-21 15:30:22 +00:00
|
|
|
privKey = (*ecdsa.PrivateKey)((*btcec.PrivateKey)(privInterfaceKey.(*crypto.Secp256k1PrivateKey)))
|
2018-11-10 17:32:45 +00:00
|
|
|
log.Warning("No private key was provided. Using default/random private key")
|
2019-09-12 00:04:35 +00:00
|
|
|
b, err := privInterfaceKey.Raw()
|
2019-09-01 03:05:36 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-09-12 00:04:35 +00:00
|
|
|
log.Debugf("Private key %x", b)
|
2018-11-10 17:32:45 +00:00
|
|
|
}
|
2019-09-01 03:05:36 +00:00
|
|
|
|
2019-09-23 17:24:16 +00:00
|
|
|
return privKey, interfaceKey
|
2018-11-10 17:32:45 +00:00
|
|
|
}
|