Skip to content
This repository has been archived by the owner on Apr 15, 2024. It is now read-only.

Commit

Permalink
feat: implement the bootstrapper command (#343)
Browse files Browse the repository at this point in the history
* feat: implement the bootstrapper command

* chore: typos

* feat: also allow to connect to other bootstrappers
  • Loading branch information
rach-id authored May 5, 2023
1 parent b0a58b2 commit 9aa83fc
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 27 deletions.
20 changes: 20 additions & 0 deletions cmd/qgb/base/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"os"
"strings"

"github.com/spf13/cobra"

"github.com/pkg/errors"

"github.com/tendermint/tendermint/libs/cli"
Expand Down Expand Up @@ -39,3 +41,21 @@ func DefaultServicePath(serviceName string) (string, error) {
}
return fmt.Sprintf("%s/.%s", home, strings.ToLower(serviceName)), nil
}

const (
FlagBootstrappers = "p2p-bootstrappers"
FlagP2PListenAddress = "p2p-listen-addr"
FlagP2PNickname = "p2p-nickname"
)

func AddP2PNicknameFlag(cmd *cobra.Command) {
cmd.Flags().StringP(FlagP2PNickname, "p", "", "Nickname of the p2p private key to use (if not provided, an existing one from the p2p store or a newly generated one will be used)")
}

func AddP2PListenAddressFlag(cmd *cobra.Command) {
cmd.Flags().StringP(FlagP2PListenAddress, "q", "/ip4/0.0.0.0/tcp/30000", "MultiAddr for the p2p peer to listen on")
}

func AddBootstrappersFlag(cmd *cobra.Command) {
cmd.Flags().StringP(FlagBootstrappers, "b", "", "Comma-separated multiaddresses of p2p peers to connect to")
}
169 changes: 169 additions & 0 deletions cmd/qgb/bootstrapper/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package bootstrapper

import (
"context"
"os"
"strings"
"time"

p2pcmd "github.com/celestiaorg/orchestrator-relayer/cmd/qgb/keys/p2p"
"github.com/celestiaorg/orchestrator-relayer/helpers"
"github.com/celestiaorg/orchestrator-relayer/p2p"
"github.com/celestiaorg/orchestrator-relayer/store"
ds "github.com/ipfs/go-datastore"
dssync "github.com/ipfs/go-datastore/sync"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/spf13/cobra"
tmlog "github.com/tendermint/tendermint/libs/log"
)

func Command() *cobra.Command {
bsCmd := &cobra.Command{
Use: "bootstrapper",
Aliases: []string{"bs"},
Short: "QGB P2P network bootstrapper command",
SilenceUsage: true,
}

bsCmd.AddCommand(
Start(),
Init(),
p2pcmd.Root(ServiceNameBootstrapper),
)

bsCmd.SetHelpCommand(&cobra.Command{})

return bsCmd
}

func Start() *cobra.Command {
cmd := &cobra.Command{
Use: "start",
Short: "Starts the bootstrapper node using the provided home. " +
"Could be connected to other bootstrappers via the `-b` flag.",
RunE: func(cmd *cobra.Command, args []string) error {
config, err := parseStartFlags(cmd)
if err != nil {
return err
}

// creating the logger
logger := tmlog.NewTMLogger(os.Stdout)
logger.Debug("starting bootstrapper node")

ctx, cancel := context.WithCancel(cmd.Context())
defer cancel()

// checking if the provided home is already initiated
isInit := store.IsInit(logger, config.home, store.InitOptions{
NeedDataStore: false,
NeedEVMKeyStore: false,
NeedP2PKeyStore: true,
})
if !isInit {
return store.ErrNotInited
}

// creating the data store
openOptions := store.OpenOptions{
HasDataStore: false,
HasEVMKeyStore: false,
HasP2PKeyStore: true,
}
s, err := store.OpenStore(logger, config.home, openOptions)
if err != nil {
return err
}

// get the p2p private key or generate a new one
privKey, err := p2pcmd.GetP2PKeyOrGenerateNewOne(s.P2PKeyStore, config.p2pNickname)
if err != nil {
return err
}

// creating the host
h, err := p2p.CreateHost(config.p2pListenAddr, privKey)
if err != nil {
return err
}
logger.Info(
"created host",
"ID",
h.ID().String(),
"Addresses",
h.Addrs(),
)

// creating the data store
dataStore := dssync.MutexWrap(ds.NewMapDatastore())

// get the bootstrappers
var aIBootstrappers []peer.AddrInfo
if config.bootstrappers == "" {
aIBootstrappers = nil
} else {
bs := strings.Split(config.bootstrappers, ",")
aIBootstrappers, err = helpers.ParseAddrInfos(logger, bs)
if err != nil {
return err
}
}

// creating the dht
dht, err := p2p.NewQgbDHT(ctx, h, dataStore, aIBootstrappers, logger)
if err != nil {
return err
}

// Listen for and trap any OS signal to graceful shutdown and exit
go helpers.TrapSignal(logger, cancel)

logger.Info("starting bootstrapper")

ticker := time.NewTicker(time.Minute)
for {
select {
case <-ctx.Done():
return nil
case <-ticker.C:
logger.Info("listening in bootstrapping mode", "peers_connected", dht.RoutingTable().Size())
}
}
},
}
return addStartFlags(cmd)
}

func Init() *cobra.Command {
cmd := cobra.Command{
Use: "init",
Short: "Initialize the QGB bootstrapper store. Passed flags have persisted effect.",
RunE: func(cmd *cobra.Command, args []string) error {
config, err := parseInitFlags(cmd)
if err != nil {
return err
}

logger := tmlog.NewTMLogger(os.Stdout)

initOptions := store.InitOptions{
NeedDataStore: false,
NeedEVMKeyStore: false,
NeedP2PKeyStore: true,
}
isInit := store.IsInit(logger, config.home, initOptions)
if isInit {
logger.Info("provided path is already initiated", "path", config.home)
return nil
}

err = store.Init(logger, config.home, initOptions)
if err != nil {
return err
}

return nil
},
}
return addInitFlags(&cmd)
}
92 changes: 92 additions & 0 deletions cmd/qgb/bootstrapper/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package bootstrapper

import (
"github.com/celestiaorg/orchestrator-relayer/cmd/qgb/base"
"github.com/spf13/cobra"
)

const (
ServiceNameBootstrapper = "bootstrapper"
)

func addStartFlags(cmd *cobra.Command) *cobra.Command {
homeDir, err := base.DefaultServicePath(ServiceNameBootstrapper)
if err != nil {
panic(err)
}
cmd.Flags().String(base.FlagHome, homeDir, "The qgb bootstrappers home directory")
base.AddP2PNicknameFlag(cmd)
base.AddP2PListenAddressFlag(cmd)
base.AddBootstrappersFlag(cmd)
return cmd
}

type StartConfig struct {
home string
p2pListenAddr, p2pNickname string
bootstrappers string
}

func parseStartFlags(cmd *cobra.Command) (StartConfig, error) {
p2pListenAddress, err := cmd.Flags().GetString(base.FlagP2PListenAddress)
if err != nil {
return StartConfig{}, err
}
p2pNickname, err := cmd.Flags().GetString(base.FlagP2PNickname)
if err != nil {
return StartConfig{}, err
}
homeDir, err := cmd.Flags().GetString(base.FlagHome)
if err != nil {
return StartConfig{}, err
}
if homeDir == "" {
var err error
homeDir, err = base.DefaultServicePath(ServiceNameBootstrapper)
if err != nil {
return StartConfig{}, err
}
}
bootstrappers, err := cmd.Flags().GetString(base.FlagBootstrappers)
if err != nil {
return StartConfig{}, err
}

return StartConfig{
p2pNickname: p2pNickname,
p2pListenAddr: p2pListenAddress,
home: homeDir,
bootstrappers: bootstrappers,
}, nil
}

func addInitFlags(cmd *cobra.Command) *cobra.Command {
homeDir, err := base.DefaultServicePath(ServiceNameBootstrapper)
if err != nil {
panic(err)
}
cmd.Flags().String(base.FlagHome, homeDir, "The qgb bootstrappers home directory")
return cmd
}

type InitConfig struct {
home string
}

func parseInitFlags(cmd *cobra.Command) (InitConfig, error) {
homeDir, err := cmd.Flags().GetString(base.FlagHome)
if err != nil {
return InitConfig{}, err
}
if homeDir == "" {
var err error
homeDir, err = base.DefaultServicePath(ServiceNameBootstrapper)
if err != nil {
return InitConfig{}, err
}
}

return InitConfig{
home: homeDir,
}, nil
}
16 changes: 6 additions & 10 deletions cmd/qgb/orchestrator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ const (
FlagCelestiaGRPC = "celes-grpc"
FlagEVMAccAddress = "evm-address"
FlagTendermintRPC = "celes-rpc"
FlagBootstrappers = "p2p-bootstrappers"
FlagP2PListenAddress = "p2p-listen-addr"
FlagP2PNickname = "p2p-nickname"
ServiceNameOrchestrator = "orchestrator"
)

Expand All @@ -27,16 +24,15 @@ func addOrchestratorFlags(cmd *cobra.Command) *cobra.Command {
"",
"Specify the EVM account address to use for signing (Note: the private key should be in the keystore)",
)
cmd.Flags().StringP(FlagBootstrappers, "b", "", "Comma-separated multiaddresses of p2p peers to connect to")
cmd.Flags().StringP(FlagP2PNickname, "p", "", "Nickname of the p2p private key to use (if not provided, an existing one from the p2p store or a newly generated one will be used)")
cmd.Flags().StringP(FlagP2PListenAddress, "q", "/ip4/0.0.0.0/tcp/30000", "MultiAddr for the p2p peer to listen on")
homeDir, err := base.DefaultServicePath(ServiceNameOrchestrator)
if err != nil {
panic(err)
}
cmd.Flags().String(base.FlagHome, homeDir, "The qgb orchestrator home directory")
cmd.Flags().String(base.FlagEVMPassphrase, "", "the evm account passphrase (if not specified as a flag, it will be asked interactively)")

base.AddP2PNicknameFlag(cmd)
base.AddP2PListenAddressFlag(cmd)
base.AddBootstrappersFlag(cmd)
return cmd
}

Expand Down Expand Up @@ -64,15 +60,15 @@ func parseOrchestratorFlags(cmd *cobra.Command) (StartConfig, error) {
if err != nil {
return StartConfig{}, err
}
bootstrappers, err := cmd.Flags().GetString(FlagBootstrappers)
bootstrappers, err := cmd.Flags().GetString(base.FlagBootstrappers)
if err != nil {
return StartConfig{}, err
}
p2pListenAddress, err := cmd.Flags().GetString(FlagP2PListenAddress)
p2pListenAddress, err := cmd.Flags().GetString(base.FlagP2PListenAddress)
if err != nil {
return StartConfig{}, err
}
p2pNickname, err := cmd.Flags().GetString(FlagP2PNickname)
p2pNickname, err := cmd.Flags().GetString(base.FlagP2PNickname)
if err != nil {
return StartConfig{}, err
}
Expand Down
Loading

0 comments on commit 9aa83fc

Please sign in to comment.