routed.go 4.17 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1
2
3
package routedhost

import (
4
	"context"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
6
7
	"fmt"
	"time"

Jeromy's avatar
Jeromy committed
8
	host "github.com/libp2p/go-libp2p-host"
Jeromy's avatar
Jeromy committed
9

Jeromy's avatar
Jeromy committed
10
	logging "github.com/ipfs/go-log"
11
	ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
Jeromy's avatar
Jeromy committed
12
	lgbl "github.com/libp2p/go-libp2p-loggables"
Jeromy's avatar
Jeromy committed
13
	inet "github.com/libp2p/go-libp2p-net"
Jeromy's avatar
Jeromy committed
14
15
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
Jeromy's avatar
Jeromy committed
16
	protocol "github.com/libp2p/go-libp2p-protocol"
Jeromy's avatar
Jeromy committed
17
	ma "github.com/multiformats/go-multiaddr"
Jeromy's avatar
Jeromy committed
18
	msmux "github.com/multiformats/go-multistream"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
19
20
)

Jeromy's avatar
Jeromy committed
21
var log = logging.Logger("routedhost")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22
23
24
25
26
27
28
29
30
31

// AddressTTL is the expiry time for our addresses.
// We expire them quickly.
const AddressTTL = time.Second * 10

// RoutedHost is a p2p Host that includes a routing system.
// This allows the Host to find the addresses for peers when
// it does not have them.
type RoutedHost struct {
	host  host.Host // embedded other host.
Jeromy's avatar
Jeromy committed
32
33
34
35
	route Routing
}

type Routing interface {
Jeromy's avatar
Jeromy committed
36
	FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
37
38
}

Jeromy's avatar
Jeromy committed
39
func Wrap(h host.Host, r Routing) *RoutedHost {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
40
41
42
43
44
45
46
47
	return &RoutedHost{h, r}
}

// Connect ensures there is a connection between this host and the peer with
// given peer.ID. See (host.Host).Connect for more information.
//
// RoutedHost's Connect differs in that if the host has no addresses for a
// given peer, it will use its routing system to try to find some.
Jeromy's avatar
Jeromy committed
48
func (rh *RoutedHost) Connect(ctx context.Context, pi pstore.PeerInfo) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49
	// first, check if we're already connected.
50
	if rh.Network().Connectedness(pi.ID) == inet.Connected {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
51
52
53
54
55
		return nil
	}

	// if we were given some addresses, keep + use them.
	if len(pi.Addrs) > 0 {
Jeromy's avatar
Jeromy committed
56
		rh.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57
58
59
60
61
62
	}

	// Check if we have some addresses in our recent memory.
	addrs := rh.Peerstore().Addrs(pi.ID)
	if len(addrs) < 1 {
		// no addrs? find some with the routing system.
63
64
		var err error
		addrs, err = rh.findPeerAddrs(ctx, pi.ID)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
65
66
67
68
69
70
71
72
73
74
		if err != nil {
			return err
		}
	}

	// if we're here, we got some addrs. let's use our wrapped host to connect.
	pi.Addrs = addrs
	return rh.host.Connect(ctx, pi)
}

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
func (rh *RoutedHost) findPeerAddrs(ctx context.Context, id peer.ID) ([]ma.Multiaddr, error) {
	pi, err := rh.route.FindPeer(ctx, id)
	if err != nil {
		return nil, err // couldnt find any :(
	}

	if pi.ID != id {
		err = fmt.Errorf("routing failure: provided addrs for different peer")
		logRoutingErrDifferentPeers(ctx, id, pi.ID, err)
		return nil, err
	}

	return pi.Addrs, nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
90
91
92
93
94
95
96
97
98
99
100
func logRoutingErrDifferentPeers(ctx context.Context, wanted, got peer.ID, err error) {
	lm := make(lgbl.DeferredMap)
	lm["error"] = err
	lm["wantedPeer"] = func() interface{} { return wanted.Pretty() }
	lm["gotPeer"] = func() interface{} { return got.Pretty() }
	log.Event(ctx, "routingError", lm)
}

func (rh *RoutedHost) ID() peer.ID {
	return rh.host.ID()
}
Jeromy's avatar
Jeromy committed
101

Jeromy's avatar
Jeromy committed
102
func (rh *RoutedHost) Peerstore() pstore.Peerstore {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103
104
	return rh.host.Peerstore()
}
Jeromy's avatar
Jeromy committed
105

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
106
107
108
func (rh *RoutedHost) Addrs() []ma.Multiaddr {
	return rh.host.Addrs()
}
Jeromy's avatar
Jeromy committed
109

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
110
111
112
func (rh *RoutedHost) Network() inet.Network {
	return rh.host.Network()
}
Jeromy's avatar
Jeromy committed
113

Jeromy's avatar
Jeromy committed
114
func (rh *RoutedHost) Mux() *msmux.MultistreamMuxer {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
115
116
	return rh.host.Mux()
}
Jeromy's avatar
Jeromy committed
117

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118
119
120
func (rh *RoutedHost) SetStreamHandler(pid protocol.ID, handler inet.StreamHandler) {
	rh.host.SetStreamHandler(pid, handler)
}
Jeromy's avatar
Jeromy committed
121

122
123
124
125
func (rh *RoutedHost) SetStreamHandlerMatch(pid protocol.ID, m func(string) bool, handler inet.StreamHandler) {
	rh.host.SetStreamHandlerMatch(pid, m, handler)
}

Jeromy's avatar
Jeromy committed
126
127
128
129
func (rh *RoutedHost) RemoveStreamHandler(pid protocol.ID) {
	rh.host.RemoveStreamHandler(pid)
}

130
func (rh *RoutedHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.ID) (inet.Stream, error) {
131
132
133
134
135
136
	// Ensure we have a connection, with peer addresses resolved by the routing system (#207)
	// It is not sufficient to let the underlying host connect, it will most likely not have
	// any addresses for the peer without any prior connections.
	err := rh.Connect(ctx, pstore.PeerInfo{ID: p})
	if err != nil {
		return nil, err
137
138
	}

139
	return rh.host.NewStream(ctx, p, pids...)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
140
141
142
143
144
}
func (rh *RoutedHost) Close() error {
	// no need to close IpfsRouting. we dont own it.
	return rh.host.Close()
}
145
func (rh *RoutedHost) ConnManager() ifconnmgr.ConnManager {
Jeromy's avatar
Jeromy committed
146
147
	return rh.host.ConnManager()
}
Jeromy's avatar
Jeromy committed
148

149
var _ (host.Host) = (*RoutedHost)(nil)