1package peer
2
3import (
4	"fmt"
5
6	ma "github.com/multiformats/go-multiaddr"
7)
8
9// AddrInfo is a small struct used to pass around a peer with
10// a set of addresses (and later, keys?).
11type AddrInfo struct {
12	ID    ID
13	Addrs []ma.Multiaddr
14}
15
16var _ fmt.Stringer = AddrInfo{}
17
18func (pi AddrInfo) String() string {
19	return fmt.Sprintf("{%v: %v}", pi.ID, pi.Addrs)
20}
21
22var ErrInvalidAddr = fmt.Errorf("invalid p2p multiaddr")
23
24// AddrInfosFromP2pAddrs converts a set of Multiaddrs to a set of AddrInfos.
25func AddrInfosFromP2pAddrs(maddrs ...ma.Multiaddr) ([]AddrInfo, error) {
26	m := make(map[ID][]ma.Multiaddr)
27	for _, maddr := range maddrs {
28		transport, id := SplitAddr(maddr)
29		if id == "" {
30			return nil, ErrInvalidAddr
31		}
32		if transport == nil {
33			if _, ok := m[id]; !ok {
34				m[id] = nil
35			}
36		} else {
37			m[id] = append(m[id], transport)
38		}
39	}
40	ais := make([]AddrInfo, 0, len(m))
41	for id, maddrs := range m {
42		ais = append(ais, AddrInfo{ID: id, Addrs: maddrs})
43	}
44	return ais, nil
45}
46
47// SplitAddr splits a p2p Multiaddr into a transport multiaddr and a peer ID.
48//
49// * Returns a nil transport if the address only contains a /p2p part.
50// * Returns a empty peer ID if the address doesn't contain a /p2p part.
51func SplitAddr(m ma.Multiaddr) (transport ma.Multiaddr, id ID) {
52	if m == nil {
53		return nil, ""
54	}
55
56	transport, p2ppart := ma.SplitLast(m)
57	if p2ppart == nil || p2ppart.Protocol().Code != ma.P_P2P {
58		return m, ""
59	}
60	id = ID(p2ppart.RawValue()) // already validated by the multiaddr library.
61	return transport, id
62}
63
64// AddrInfoFromP2pAddr converts a Multiaddr to an AddrInfo.
65func AddrInfoFromP2pAddr(m ma.Multiaddr) (*AddrInfo, error) {
66	transport, id := SplitAddr(m)
67	if id == "" {
68		return nil, ErrInvalidAddr
69	}
70	info := &AddrInfo{ID: id}
71	if transport != nil {
72		info.Addrs = []ma.Multiaddr{transport}
73	}
74	return info, nil
75}
76
77// AddrInfoToP2pAddrs converts an AddrInfo to a list of Multiaddrs.
78func AddrInfoToP2pAddrs(pi *AddrInfo) ([]ma.Multiaddr, error) {
79	var addrs []ma.Multiaddr
80	p2ppart, err := ma.NewComponent("p2p", IDB58Encode(pi.ID))
81	if err != nil {
82		return nil, err
83	}
84	if len(pi.Addrs) == 0 {
85		return []ma.Multiaddr{p2ppart}, nil
86	}
87	for _, addr := range pi.Addrs {
88		addrs = append(addrs, addr.Encapsulate(p2ppart))
89	}
90	return addrs, nil
91}
92
93func (pi *AddrInfo) Loggable() map[string]interface{} {
94	return map[string]interface{}{
95		"peerID": pi.ID.Pretty(),
96		"addrs":  pi.Addrs,
97	}
98}
99