diff --git a/Dockerfile b/Dockerfile index 84acbee8b4..c99d372ebb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM docker.io/golang:1.23-alpine3.20 as builder +FROM --platform=$BUILDPLATFORM docker.io/golang:1.23-alpine3.20 AS builder ARG TARGETPLATFORM ARG BUILDPLATFORM @@ -21,9 +21,11 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN uname -a &&\ - CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ - make build && make cel-key +RUN uname -a && \ + export CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} && \ + make build && \ + make cel-key && \ + make cel-shed FROM docker.io/alpine:3.20.2 @@ -34,8 +36,8 @@ ARG USER_NAME=celestia ENV CELESTIA_HOME=/home/${USER_NAME} # Default node type can be overwritten in deployment manifest -ENV NODE_TYPE bridge -ENV P2P_NETWORK mocha +ENV NODE_TYPE=bridge +ENV P2P_NETWORK=mocha # hadolint ignore=DL3018 RUN uname -a &&\ @@ -54,6 +56,7 @@ RUN uname -a &&\ # Copy in the binary COPY --from=builder /src/build/celestia /bin/celestia COPY --from=builder /src/./cel-key /bin/cel-key +COPY --from=builder /src/./cel-shed /bin/cel-shed COPY --chown=${USER_NAME}:${USER_NAME} docker/entrypoint.sh /opt/entrypoint.sh diff --git a/cmd/cel-shed/p2p.go b/cmd/cel-shed/p2p.go index 957582c8bc..df213145db 100644 --- a/cmd/cel-shed/p2p.go +++ b/cmd/cel-shed/p2p.go @@ -1,17 +1,28 @@ package main import ( + "context" "crypto/rand" "encoding/hex" "fmt" + "os" + "sync" + "sync/atomic" + "time" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" "github.com/spf13/cobra" + "go.uber.org/fx" + + "github.com/celestiaorg/celestia-node/nodebuilder" + "github.com/celestiaorg/celestia-node/nodebuilder/fraud" + "github.com/celestiaorg/celestia-node/nodebuilder/node" + "github.com/celestiaorg/celestia-node/nodebuilder/p2p" ) func init() { - p2pCmd.AddCommand(p2pNewKeyCmd, p2pPeerIDCmd) + p2pCmd.AddCommand(p2pNewKeyCmd, p2pPeerIDCmd, p2pConnectBootstrappersCmd) } var p2pCmd = &cobra.Command{ @@ -75,3 +86,119 @@ var p2pPeerIDCmd = &cobra.Command{ }, Args: cobra.ExactArgs(1), } + +var ( + errorOnAnyFailure bool + errorOnAllFailure bool + connectionTimeout time.Duration +) + +var p2pConnectBootstrappersCmd = &cobra.Command{ + Use: "connect-bootstrappers [network]", + Short: "Connect to bootstrappers of a certain network", + RunE: func(cmd *cobra.Command, args []string) error { + if errorOnAnyFailure && errorOnAllFailure { + return fmt.Errorf("only one of --err-any and --err-all can be specified") + } + + ctx, cancel := context.WithTimeout(cmd.Context(), connectionTimeout) + defer cancel() + + network := p2p.GetNetwork(args[0]) + bootstrappers, err := p2p.BootstrappersFor(network) + if err != nil { + return fmt.Errorf("failed to get bootstrappers: %w", err) + } + + store := nodebuilder.NewMemStore() + cfg := p2p.DefaultConfig(node.Light) + modp2p := p2p.ConstructModule(node.Light, &cfg) + + var mod p2p.Module + app := fx.New( + fx.NopLogger, + modp2p, + fx.Provide(fraud.Unmarshaler), + fx.Provide(cmd.Context), + fx.Provide(store.Keystore), + fx.Provide(store.Datastore), + fx.Supply(bootstrappers), + fx.Supply(network), + fx.Supply(node.Light), + fx.Invoke(func(modprov p2p.Module) { + mod = modprov + }), + ) + + if err := app.Start(ctx); err != nil { + return fmt.Errorf("failed to start app: %w", err) + } + defer func() { + if err := app.Stop(ctx); err != nil { + fmt.Printf("failed to stop application: %v\n", err) + } + }() + + p2pInfo, err := mod.Info(ctx) + if err != nil { + return fmt.Errorf("failed to get p2p info: %w", err) + } + + fmt.Printf("PeerID: %s\n", p2pInfo.ID) + for _, addr := range p2pInfo.Addrs { + fmt.Printf("Listening on: %s\n", addr.String()) + } + fmt.Println() + + var successfulConnections atomic.Int32 + var failedConnections atomic.Int32 + var wg sync.WaitGroup + + for _, bootstrapper := range bootstrappers { + wg.Add(1) + go func(bootstrapper peer.AddrInfo) { + defer wg.Done() + fmt.Printf("Attempting to connect to bootstrapper: %s\n", bootstrapper) + if err := mod.Connect(ctx, bootstrapper); err != nil { + fmt.Printf("Error: Failed to connect to bootstrapper %s. Reason: %v\n", bootstrapper, err) + failedConnections.Add(1) + return + } + fmt.Printf("Success: Connected to bootstrapper: %s\n", bootstrapper) + successfulConnections.Add(1) + }(bootstrapper) + } + + wg.Wait() + + if failedConnections.Load() == int32(len(bootstrappers)) && errorOnAllFailure { + fmt.Println() + fmt.Println("failed to connect to all bootstrappers") + os.Exit(1) + return nil + } else if failedConnections.Load() > 0 && errorOnAnyFailure { + fmt.Println() + fmt.Println("failed to connect to some bootstrappers") + os.Exit(1) + return nil + } + + return nil + }, + Args: cobra.ExactArgs(1), +} + +func init() { + p2pConnectBootstrappersCmd.Flags().BoolVar( + &errorOnAnyFailure, "err-any", false, + "Return error if at least one bootstrapper is not reachable", + ) + p2pConnectBootstrappersCmd.Flags().BoolVar( + &errorOnAllFailure, "err-all", false, + "Return error if no bootstrapper is reachable", + ) + p2pConnectBootstrappersCmd.Flags().DurationVar( + &connectionTimeout, "timeout", 10*time.Second, + "Timeout duration for the entire bootstrapper connection process", + ) +} diff --git a/nodebuilder/fraud/constructors.go b/nodebuilder/fraud/constructors.go index 037abb2e51..14593f3fc8 100644 --- a/nodebuilder/fraud/constructors.go +++ b/nodebuilder/fraud/constructors.go @@ -17,7 +17,7 @@ import ( "github.com/celestiaorg/celestia-node/nodebuilder/p2p" ) -func fraudUnmarshaler() fraud.ProofUnmarshaler[*header.ExtendedHeader] { +func Unmarshaler() fraud.ProofUnmarshaler[*header.ExtendedHeader] { return defaultProofUnmarshaler } diff --git a/nodebuilder/fraud/module.go b/nodebuilder/fraud/module.go index bf353f63c6..62903226ba 100644 --- a/nodebuilder/fraud/module.go +++ b/nodebuilder/fraud/module.go @@ -14,7 +14,7 @@ var log = logging.Logger("module/fraud") func ConstructModule(tp node.Type) fx.Option { baseComponent := fx.Options( - fx.Provide(fraudUnmarshaler), + fx.Provide(Unmarshaler), fx.Provide(func(serv fraud.Service[*header.ExtendedHeader]) fraud.Getter[*header.ExtendedHeader] { return serv }), diff --git a/nodebuilder/p2p/network.go b/nodebuilder/p2p/network.go index 4f83eea213..09b9221bb6 100644 --- a/nodebuilder/p2p/network.go +++ b/nodebuilder/p2p/network.go @@ -69,6 +69,11 @@ var networkAliases = map[string]Network{ "private": Private, } +// GetNetwork returns the Network for the given string representation. +func GetNetwork(networkStr string) Network { + return networkAliases[networkStr] +} + // orderedNetworks is a list of all known networks in order of priority. var orderedNetworks = []Network{Mainnet, Mocha, Arabica, Private}