package peer import ( "fmt" ma "github.com/multiformats/go-multiaddr" ) // AddrInfo is a small struct used to pass around a peer with // a set of addresses (and later, keys?). type AddrInfo struct { ID ID Addrs []ma.Multiaddr } var _ fmt.Stringer = AddrInfo{} func (pi AddrInfo) String() string { return fmt.Sprintf("{%v: %v}", pi.ID, pi.Addrs) } var ErrInvalidAddr = fmt.Errorf("invalid p2p multiaddr") // AddrInfosFromP2pAddrs converts a set of Multiaddrs to a set of AddrInfos. func AddrInfosFromP2pAddrs(maddrs ...ma.Multiaddr) ([]AddrInfo, error) { m := make(map[ID][]ma.Multiaddr) for _, maddr := range maddrs { transport, id, err := SplitAddr(maddr) if id == "" || err != nil { return nil, ErrInvalidAddr } if transport == nil { if _, ok := m[id]; !ok { m[id] = nil } } else { m[id] = append(m[id], transport) } } ais := make([]AddrInfo, 0, len(m)) for id, maddrs := range m { ais = append(ais, AddrInfo{ID: id, Addrs: maddrs}) } return ais, nil } // SplitAddr splits a p2p Multiaddr into a transport multiaddr and a peer ID. // // * Returns a nil transport if the address only contains a /p2p part. // * Returns an empty peer ID if the address doesn't contain a /p2p part. func SplitAddr(m ma.Multiaddr) (transport ma.Multiaddr, id ID, err error) { if m == nil { return nil, "", nil } transport, p2ppart, err := ma.SplitLast(m) if err != nil { return nil, "", err } if p2ppart == nil || p2ppart.Protocol().Code != ma.P_P2P { return m, "", nil } id = ID(p2ppart.RawValue()) // already validated by the multiaddr library. return transport, id, nil } // AddrInfoFromString builds an AddrInfo from the string representation of a Multiaddr func AddrInfoFromString(s string) (*AddrInfo, error) { a, err := ma.NewMultiaddr(s) if err != nil { return nil, err } return AddrInfoFromP2pAddr(a) } // AddrInfoFromP2pAddr converts a Multiaddr to an AddrInfo. func AddrInfoFromP2pAddr(m ma.Multiaddr) (*AddrInfo, error) { transport, id, err := SplitAddr(m) if id == "" || err != nil { return nil, ErrInvalidAddr } info := &AddrInfo{ID: id} if transport != nil { info.Addrs = []ma.Multiaddr{transport} } return info, nil } // AddrInfoToP2pAddrs converts an AddrInfo to a list of Multiaddrs. func AddrInfoToP2pAddrs(pi *AddrInfo) ([]ma.Multiaddr, error) { p2ppart, err := ma.NewComponent("p2p", pi.ID.String()) if err != nil { return nil, err } if len(pi.Addrs) == 0 { return []ma.Multiaddr{p2ppart}, nil } addrs := make([]ma.Multiaddr, 0, len(pi.Addrs)) for _, addr := range pi.Addrs { addrs = append(addrs, addr.Encapsulate(p2ppart)) } return addrs, nil } func (pi *AddrInfo) Loggable() map[string]interface{} { return map[string]interface{}{ "peerID": pi.ID.String(), "addrs": pi.Addrs, } } // AddrInfosToIDs extracts the peer IDs from the passed AddrInfos and returns them in-order. func AddrInfosToIDs(pis []AddrInfo) []ID { ps := make([]ID, len(pis)) for i, pi := range pis { ps[i] = pi.ID } return ps }