package common import ( "context" "sort" "strings" "github.com/gbrlsnchs/jwt/v3" "github.com/google/uuid" "go.uber.org/fx" "golang.org/x/xerrors" logging "github.com/ipfs/go-log/v2" "github.com/libp2p/go-libp2p-core/host" metrics "github.com/libp2p/go-libp2p-core/metrics" "github.com/libp2p/go-libp2p-core/network" "github.com/libp2p/go-libp2p-core/peer" protocol "github.com/libp2p/go-libp2p-core/protocol" swarm "github.com/libp2p/go-libp2p-swarm" basichost "github.com/libp2p/go-libp2p/p2p/host/basic" "github.com/libp2p/go-libp2p/p2p/net/conngater" ma "github.com/multiformats/go-multiaddr" "github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/lotus/api" apitypes "github.com/filecoin-project/lotus/api/types" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/modules/lp2p" ) var session = uuid.New() type CommonAPI struct { fx.In APISecret *dtypes.APIAlg RawHost lp2p.RawHost Host host.Host Router lp2p.BaseIpfsRouting ConnGater *conngater.BasicConnectionGater Reporter metrics.Reporter Sk *dtypes.ScoreKeeper ShutdownChan dtypes.ShutdownChan } type jwtPayload struct { Allow []auth.Permission } func (a *CommonAPI) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) { var payload jwtPayload if _, err := jwt.Verify([]byte(token), (*jwt.HMACSHA)(a.APISecret), &payload); err != nil { return nil, xerrors.Errorf("JWT Verification failed: %w", err) } return payload.Allow, nil } func (a *CommonAPI) AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error) { p := jwtPayload{ Allow: perms, // TODO: consider checking validity } return jwt.Sign(&p, (*jwt.HMACSHA)(a.APISecret)) } func (a *CommonAPI) NetConnectedness(ctx context.Context, pid peer.ID) (network.Connectedness, error) { return a.Host.Network().Connectedness(pid), nil } func (a *CommonAPI) NetPubsubScores(context.Context) ([]api.PubsubScore, error) { scores := a.Sk.Get() out := make([]api.PubsubScore, len(scores)) i := 0 for k, v := range scores { out[i] = api.PubsubScore{ID: k, Score: v} i++ } sort.Slice(out, func(i, j int) bool { return strings.Compare(string(out[i].ID), string(out[j].ID)) > 0 }) return out, nil } func (a *CommonAPI) NetPeers(context.Context) ([]peer.AddrInfo, error) { conns := a.Host.Network().Conns() out := make([]peer.AddrInfo, len(conns)) for i, conn := range conns { out[i] = peer.AddrInfo{ ID: conn.RemotePeer(), Addrs: []ma.Multiaddr{ conn.RemoteMultiaddr(), }, } } return out, nil } func (a *CommonAPI) NetPeerInfo(_ context.Context, p peer.ID) (*api.ExtendedPeerInfo, error) { info := &api.ExtendedPeerInfo{ID: p} agent, err := a.Host.Peerstore().Get(p, "AgentVersion") if err == nil { info.Agent = agent.(string) } for _, a := range a.Host.Peerstore().Addrs(p) { info.Addrs = append(info.Addrs, a.String()) } sort.Strings(info.Addrs) protocols, err := a.Host.Peerstore().GetProtocols(p) if err == nil { sort.Strings(protocols) info.Protocols = protocols } if cm := a.Host.ConnManager().GetTagInfo(p); cm != nil { info.ConnMgrMeta = &api.ConnMgrInfo{ FirstSeen: cm.FirstSeen, Value: cm.Value, Tags: cm.Tags, Conns: cm.Conns, } } return info, nil } func (a *CommonAPI) NetConnect(ctx context.Context, p peer.AddrInfo) error { if swrm, ok := a.Host.Network().(*swarm.Swarm); ok { swrm.Backoff().Clear(p.ID) } return a.Host.Connect(ctx, p) } func (a *CommonAPI) NetAddrsListen(context.Context) (peer.AddrInfo, error) { return peer.AddrInfo{ ID: a.Host.ID(), Addrs: a.Host.Addrs(), }, nil } func (a *CommonAPI) NetDisconnect(ctx context.Context, p peer.ID) error { return a.Host.Network().ClosePeer(p) } func (a *CommonAPI) NetFindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, error) { return a.Router.FindPeer(ctx, p) } func (a *CommonAPI) NetAutoNatStatus(ctx context.Context) (i api.NatInfo, err error) { autonat := a.RawHost.(*basichost.BasicHost).AutoNat if autonat == nil { return api.NatInfo{ Reachability: network.ReachabilityUnknown, }, nil } var maddr string if autonat.Status() == network.ReachabilityPublic { pa, err := autonat.PublicAddr() if err != nil { return api.NatInfo{}, err } maddr = pa.String() } return api.NatInfo{ Reachability: autonat.Status(), PublicAddr: maddr, }, nil } func (a *CommonAPI) NetAgentVersion(ctx context.Context, p peer.ID) (string, error) { ag, err := a.Host.Peerstore().Get(p, "AgentVersion") if err != nil { return "", err } if ag == nil { return "unknown", nil } return ag.(string), nil } func (a *CommonAPI) NetBandwidthStats(ctx context.Context) (metrics.Stats, error) { return a.Reporter.GetBandwidthTotals(), nil } func (a *CommonAPI) NetBandwidthStatsByPeer(ctx context.Context) (map[string]metrics.Stats, error) { out := make(map[string]metrics.Stats) for p, s := range a.Reporter.GetBandwidthByPeer() { out[p.String()] = s } return out, nil } func (a *CommonAPI) NetBandwidthStatsByProtocol(ctx context.Context) (map[protocol.ID]metrics.Stats, error) { return a.Reporter.GetBandwidthByProtocol(), nil } func (a *CommonAPI) Discover(ctx context.Context) (apitypes.OpenRPCDocument, error) { return build.OpenRPCDiscoverJSON_Full(), nil } func (a *CommonAPI) ID(context.Context) (peer.ID, error) { return a.Host.ID(), nil } func (a *CommonAPI) Version(context.Context) (api.APIVersion, error) { v, err := api.VersionForType(api.RunningNodeType) if err != nil { return api.APIVersion{}, err } return api.APIVersion{ Version: build.UserVersion(), APIVersion: v, BlockDelay: build.BlockDelaySecs, }, nil } func (a *CommonAPI) LogList(context.Context) ([]string, error) { return logging.GetSubsystems(), nil } func (a *CommonAPI) LogSetLevel(ctx context.Context, subsystem, level string) error { return logging.SetLogLevel(subsystem, level) } func (a *CommonAPI) Shutdown(ctx context.Context) error { a.ShutdownChan <- struct{}{} return nil } func (a *CommonAPI) Session(ctx context.Context) (uuid.UUID, error) { return session, nil } func (a *CommonAPI) Closing(ctx context.Context) (<-chan struct{}, error) { return make(chan struct{}), nil // relies on jsonrpc closing } var _ api.Common = &CommonAPI{}