id.go 4.96 KB
Newer Older
1 2 3 4 5 6
package commands

import (
	"encoding/base64"
	"encoding/json"
	"errors"
7
	"fmt"
8
	"io"
9
	"strings"
10

Steven Allen's avatar
Steven Allen committed
11
	version "github.com/ipfs/go-ipfs"
12
	core "github.com/ipfs/go-ipfs/core"
13
	cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
Jeromy's avatar
Jeromy committed
14

Jakub Sztandera's avatar
Jakub Sztandera committed
15
	cmds "github.com/ipfs/go-ipfs-cmds"
Raúl Kripalani's avatar
Raúl Kripalani committed
16
	ic "github.com/libp2p/go-libp2p-core/crypto"
Steven Allen's avatar
Steven Allen committed
17
	"github.com/libp2p/go-libp2p-core/host"
Raúl Kripalani's avatar
Raúl Kripalani committed
18 19
	peer "github.com/libp2p/go-libp2p-core/peer"
	pstore "github.com/libp2p/go-libp2p-core/peerstore"
Jakub Sztandera's avatar
Jakub Sztandera committed
20 21
	kb "github.com/libp2p/go-libp2p-kbucket"
	identify "github.com/libp2p/go-libp2p/p2p/protocol/identify"
22 23
)

24 25 26 27 28 29 30 31 32
const offlineIdErrorMessage = `'ipfs id' currently cannot query information on remote
peers without a running daemon; we are working to fix this.
In the meantime, if you want to query remote peers using 'ipfs id',
please run the daemon:

    ipfs daemon &
    ipfs id QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
`

33 34 35 36 37 38 39 40
type IdOutput struct {
	ID              string
	PublicKey       string
	Addresses       []string
	AgentVersion    string
	ProtocolVersion string
}

Kejie Zhang's avatar
Kejie Zhang committed
41 42 43 44
const (
	formatOptionName = "format"
)

45
var IDCmd = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
46
	Helptext: cmds.HelpText{
47
		Tagline: "Show ipfs node id info.",
Jeromy's avatar
Jeromy committed
48
		ShortDescription: `
Richard Littauer's avatar
Richard Littauer committed
49 50
Prints out information about the specified peer.
If no peer is specified, prints out information for local peers.
Jeromy's avatar
Jeromy committed
51

Richard Littauer's avatar
Richard Littauer committed
52 53 54 55 56 57
'ipfs id' supports the format option for output with the following keys:
<id> : The peers id.
<aver>: Agent version.
<pver>: Protocol version.
<pubkey>: Public key.
<addrs>: Addresses (newline delimited).
58 59 60 61

EXAMPLE:

    ipfs id Qmece2RkXhsKe5CRooNisBTh4SK119KrXXGmoK6V3kb8aH -f="<addrs>\n"
Jeromy's avatar
Jeromy committed
62
`,
63
	},
Steven Allen's avatar
Steven Allen committed
64 65
	Arguments: []cmds.Argument{
		cmds.StringArg("peerid", false, false, "Peer.ID of node to look up."),
66
	},
Steven Allen's avatar
Steven Allen committed
67 68
	Options: []cmds.Option{
		cmds.StringOption(formatOptionName, "f", "Optional output format."),
69
	},
70 71
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		n, err := cmdenv.GetNode(env)
72
		if err != nil {
73
			return err
74 75
		}

Jeromy's avatar
Jeromy committed
76
		var id peer.ID
77
		if len(req.Arguments) > 0 {
Steven Allen's avatar
Steven Allen committed
78
			var err error
79
			id, err = peer.Decode(req.Arguments[0])
Steven Allen's avatar
Steven Allen committed
80
			if err != nil {
81
				return fmt.Errorf("invalid peer id")
Jeromy's avatar
Jeromy committed
82 83
			}
		} else {
84
			id = n.Identity
Jeromy's avatar
Jeromy committed
85 86
		}

87 88
		if id == n.Identity {
			output, err := printSelf(n)
89
			if err != nil {
90
				return err
91
			}
92
			return cmds.EmitOnce(res, output)
93 94
		}

95
		// TODO handle offline mode with polymorphism instead of conditionals
96
		if !n.IsOnline {
97
			return errors.New(offlineIdErrorMessage)
98 99
		}

100
		p, err := n.Routing.FindPeer(req.Context, id)
101
		if err == kb.ErrLookupFailure {
102
			return errors.New(offlineIdErrorMessage)
103 104
		}
		if err != nil {
105
			return err
106
		}
107

108
		output, err := printPeer(n.Peerstore, p.ID)
109
		if err != nil {
110
			return err
111
		}
112
		return cmds.EmitOnce(res, output)
113
	},
114
	Encoders: cmds.EncoderMap{
115
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *IdOutput) error {
116
			format, found := req.Options[formatOptionName].(string)
117 118
			if found {
				output := format
119 120 121 122 123
				output = strings.Replace(output, "<id>", out.ID, -1)
				output = strings.Replace(output, "<aver>", out.AgentVersion, -1)
				output = strings.Replace(output, "<pver>", out.ProtocolVersion, -1)
				output = strings.Replace(output, "<pubkey>", out.PublicKey, -1)
				output = strings.Replace(output, "<addrs>", strings.Join(out.Addresses, "\n"), -1)
124 125
				output = strings.Replace(output, "\\n", "\n", -1)
				output = strings.Replace(output, "\\t", "\t", -1)
126
				fmt.Fprint(w, output)
127
			} else {
128
				marshaled, err := json.MarshalIndent(out, "", "\t")
129
				if err != nil {
130
					return err
131
				}
132
				marshaled = append(marshaled, byte('\n'))
133
				fmt.Fprintln(w, string(marshaled))
134
			}
135 136
			return nil
		}),
137
	},
138
	Type: IdOutput{},
139 140
}

Jeromy's avatar
Jeromy committed
141
func printPeer(ps pstore.Peerstore, p peer.ID) (interface{}, error) {
142
	if p == "" {
Łukasz Magiera's avatar
Łukasz Magiera committed
143
		return nil, errors.New("attempted to print nil peer")
144
	}
145

146
	info := new(IdOutput)
147
	info.ID = p.Pretty()
148

149 150
	if pk := ps.PubKey(p); pk != nil {
		pkb, err := ic.MarshalPublicKey(pk)
Jeromy's avatar
Jeromy committed
151 152 153 154
		if err != nil {
			return nil, err
		}
		info.PublicKey = base64.StdEncoding.EncodeToString(pkb)
155
	}
156

157
	for _, a := range ps.Addrs(p) {
158 159 160
		info.Addresses = append(info.Addresses, a.String())
	}

161 162
	if v, err := ps.Get(p, "ProtocolVersion"); err == nil {
		if vs, ok := v.(string); ok {
163
			info.ProtocolVersion = vs
164 165 166 167
		}
	}
	if v, err := ps.Get(p, "AgentVersion"); err == nil {
		if vs, ok := v.(string); ok {
168
			info.AgentVersion = vs
169 170
		}
	}
171 172 173

	return info, nil
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
174 175 176 177 178 179 180 181 182 183 184 185 186 187

// printing self is special cased as we get values differently.
func printSelf(node *core.IpfsNode) (interface{}, error) {
	info := new(IdOutput)
	info.ID = node.Identity.Pretty()

	pk := node.PrivateKey.GetPublic()
	pkb, err := ic.MarshalPublicKey(pk)
	if err != nil {
		return nil, err
	}
	info.PublicKey = base64.StdEncoding.EncodeToString(pkb)

	if node.PeerHost != nil {
Steven Allen's avatar
Steven Allen committed
188 189 190 191 192 193
		addrs, err := peer.AddrInfoToP2pAddrs(host.InfoFromHost(node.PeerHost))
		if err != nil {
			return nil, err
		}
		for _, a := range addrs {
			info.Addresses = append(info.Addresses, a.String())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
194 195
		}
	}
196
	info.ProtocolVersion = identify.LibP2PVersion
Steven Allen's avatar
Steven Allen committed
197
	info.AgentVersion = version.UserAgent
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
198 199
	return info, nil
}