Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Network CLI #9

Merged
merged 16 commits into from
Jul 9, 2019
5 changes: 3 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ type API interface {

// network

// // peers
NetPeers(context.Context) ([]peer.AddrInfo, error) // TODO: check serialization
NetConnect(context.Context, peer.AddrInfo) error
NetAddrsListen(context.Context) (MultiaddrSlice, error)
// // ping
// // connect

// Struct

Expand Down
16 changes: 16 additions & 0 deletions api/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,25 @@ type Struct struct {
Internal struct {
ID func(context.Context) (peer.ID, error)
Version func(context.Context) (Version, error)

NetPeers func(context.Context) ([]peer.AddrInfo, error)
NetConnect func(context.Context, peer.AddrInfo) error
NetAddrsListen func(context.Context) (MultiaddrSlice, error)
}
}

func (c *Struct) NetPeers(ctx context.Context) ([]peer.AddrInfo, error) {
return c.Internal.NetPeers(ctx)
}

func (c *Struct) NetConnect(ctx context.Context, p peer.AddrInfo) error {
return c.Internal.NetConnect(ctx, p)
}

func (c *Struct) NetAddrsListen(ctx context.Context) (MultiaddrSlice, error) {
return c.Internal.NetAddrsListen(ctx)
}

// ID implements API.ID
func (c *Struct) ID(ctx context.Context) (peer.ID, error) {
return c.Internal.ID(ctx)
Expand Down
28 changes: 28 additions & 0 deletions api/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package api

import (
"encoding/json"
ma "github.com/multiformats/go-multiaddr"
)

// TODO: check if this exists anywhere else
type MultiaddrSlice []ma.Multiaddr

func (m *MultiaddrSlice) UnmarshalJSON(raw []byte) (err error) {
var temp []string
if err := json.Unmarshal(raw, &temp); err != nil {
return err
}

res := make([]ma.Multiaddr, len(temp))
for i, str := range temp {
res[i], err = ma.NewMultiaddr(str)
if err != nil {
return err
}
}
*m = res
return nil
}

var _ json.Unmarshaler = new(MultiaddrSlice)
40 changes: 39 additions & 1 deletion cli/cmd.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
package cli

import (
"context"
"os"
"os/signal"
"syscall"

"github.com/filecoin-project/go-lotus/api"
"gopkg.in/urfave/cli.v2"
)

// Commands is the root group of CLI commands
const (
metadataContext = "context"
metadataAPI = "api"
)

// ApiConnector returns API instance
type ApiConnector func() api.API

func getApi(ctx *cli.Context) api.API {
return ctx.App.Metadata[metadataAPI].(ApiConnector)()
}

// reqContext returns context for cli execution. Calling it for the first time
// installs SIGTERM handler that will close returned context.
// Not safe for concurrent execution.
func reqContext(cctx *cli.Context) context.Context {
if uctx, ok := cctx.App.Metadata[metadataContext]; ok {
// unchecked cast as if something else is in there
// it is crash worthy either way
return uctx.(context.Context)
}
ctx, done := context.WithCancel(context.Background())
sigChan := make(chan os.Signal, 2)
go func() {
<-sigChan
done()
}()
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)

return ctx
}

var Commands = []*cli.Command{
netCmd,
versionCmd,
}
164 changes: 164 additions & 0 deletions cli/net.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package cli

import (
"context"
"fmt"
"sync"
"time"

"github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
"gopkg.in/urfave/cli.v2"
)

var netCmd = &cli.Command{
Name: "net",
Usage: "Manage P2P Network",
Subcommands: []*cli.Command{
netPeers,
netConnect,
netListen,
},
}

var netPeers = &cli.Command{
Name: "peers",
Usage: "Print peers",
Action: func(cctx *cli.Context) error {
api := getApi(cctx)
ctx := reqContext(cctx)
peers, err := api.NetPeers(ctx)
if err != nil {
return err
}

for _, peer := range peers {
fmt.Println(peer)
}

return nil
},
}

var netListen = &cli.Command{
Name: "listen",
Usage: "List listen addresses",
Action: func(cctx *cli.Context) error {
api := getApi(cctx)
ctx := reqContext(cctx)

addrs, err := api.NetAddrsListen(ctx)
if err != nil {
return err
}

for _, peer := range addrs {
fmt.Println(peer)
}
return nil
},
}

var netConnect = &cli.Command{
Name: "connect",
Usage: "Connect to a peer",
Action: func(cctx *cli.Context) error {
api := getApi(cctx)
ctx := reqContext(cctx)

pis, err := parseAddresses(ctx, cctx.Args().Slice())
if err != nil {
return err
}

for _, pi := range pis {
fmt.Printf("connect %s: ", pi.ID.Pretty())
err := api.NetConnect(ctx, pi)
if err != nil {
fmt.Println("failure")
return err
}
fmt.Println("success")
}

return nil
},
}

// parseAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns a slice of properly constructed peers
func parseAddresses(ctx context.Context, addrs []string) ([]peer.AddrInfo, error) {
// resolve addresses
maddrs, err := resolveAddresses(ctx, addrs)
if err != nil {
return nil, err
}

return peer.AddrInfosFromP2pAddrs(maddrs...)
}

const (
dnsResolveTimeout = 10 * time.Second
)

// resolveAddresses resolves addresses parallelly
func resolveAddresses(ctx context.Context, addrs []string) ([]ma.Multiaddr, error) {
ctx, cancel := context.WithTimeout(ctx, dnsResolveTimeout)
defer cancel()

var maddrs []ma.Multiaddr
var wg sync.WaitGroup
resolveErrC := make(chan error, len(addrs))

maddrC := make(chan ma.Multiaddr)

for _, addr := range addrs {
maddr, err := ma.NewMultiaddr(addr)
if err != nil {
return nil, err
}

// check whether address ends in `ipfs/Qm...`
if _, last := ma.SplitLast(maddr); last.Protocol().Code == ma.P_IPFS {
maddrs = append(maddrs, maddr)
continue
}
wg.Add(1)
go func(maddr ma.Multiaddr) {
defer wg.Done()
raddrs, err := madns.Resolve(ctx, maddr)
if err != nil {
resolveErrC <- err
return
}
// filter out addresses that still doesn't end in `ipfs/Qm...`
found := 0
for _, raddr := range raddrs {
if _, last := ma.SplitLast(raddr); last != nil && last.Protocol().Code == ma.P_IPFS {
maddrC <- raddr
found++
}
}
if found == 0 {
resolveErrC <- fmt.Errorf("found no ipfs peers at %s", maddr)
}
}(maddr)
}
go func() {
wg.Wait()
close(maddrC)
}()

for maddr := range maddrC {
maddrs = append(maddrs, maddr)
}

select {
case err := <-resolveErrC:
return nil, err
default:
}

return maddrs, nil
}
8 changes: 8 additions & 0 deletions cmd/lotus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

"gopkg.in/urfave/cli.v2"

"github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/api/client"
"github.com/filecoin-project/go-lotus/build"
lcli "github.com/filecoin-project/go-lotus/cli"
"github.com/filecoin-project/go-lotus/daemon"
Expand All @@ -20,6 +22,12 @@ func main() {
Name: "lotus",
Usage: "Filecoin decentralized storage network client",
Version: build.Version,
Metadata: map[string]interface{}{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a slightly weird way to wire this through

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed (this is just the first thing I tried)

"api": lcli.ApiConnector(func() api.API {
// TODO: get this from repo
return client.NewRPC("http://127.0.0.1:1234/rpc/v0")
}),
},

Commands: append(local, lcli.Commands...),
}
Expand Down
10 changes: 9 additions & 1 deletion daemon/cmd.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build !nodaemon

package daemon

import (
Expand All @@ -13,6 +15,12 @@ import (
var Cmd = &cli.Command{
Name: "daemon",
Usage: "Start a lotus daemon process",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "api",
Value: ":1234",
},
},
Action: func(cctx *cli.Context) error {
ctx := context.Background()

Expand All @@ -26,6 +34,6 @@ var Cmd = &cli.Command{
return err
}

return serveRPC(api)
return serveRPC(api, cctx.String("api"))
},
}
24 changes: 24 additions & 0 deletions daemon/cmd_nodaemon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// +build nodaemon

package daemon

import (
"errors"

"gopkg.in/urfave/cli.v2"
)

// Cmd is the `go-lotus daemon` command
var Cmd = &cli.Command{
Name: "daemon",
Usage: "Start a lotus daemon process",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "api",
Value: ":1234",
},
},
Action: func(cctx *cli.Context) error {
return errors.New("daemon support not included in this binary")
},
}
4 changes: 2 additions & 2 deletions daemon/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"github.com/filecoin-project/go-lotus/lib/jsonrpc"
)

func serveRPC(api api.API) error {
func serveRPC(api api.API, addr string) error {
rpcServer := jsonrpc.NewServer()
rpcServer.Register("Filecoin", api)
http.Handle("/rpc/v0", rpcServer)
return http.ListenAndServe(":1234", http.DefaultServeMux)
return http.ListenAndServe(addr, http.DefaultServeMux)
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/ipfs/go-ipfs-routing v0.1.0
github.com/ipfs/go-ipld-cbor v0.0.2
github.com/ipfs/go-ipld-format v0.0.2
github.com/ipfs/go-log v0.0.2-0.20190703113630-0c3cfb1eccc4
github.com/ipfs/go-log v0.0.2-0.20190708183747-9c9fd6111bea
github.com/ipfs/go-merkledag v0.0.2
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52
github.com/libp2p/go-libp2p v0.2.0
Expand All @@ -38,6 +38,7 @@ require (
github.com/libp2p/go-maddr-filter v0.0.4
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
github.com/multiformats/go-multiaddr v0.0.4
github.com/multiformats/go-multiaddr-dns v0.0.2
github.com/multiformats/go-multihash v0.0.5
github.com/pkg/errors v0.8.1
github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14
Expand Down
Loading