forked from ipfs/rainbow
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup_bitswap.go
More file actions
201 lines (177 loc) · 7.58 KB
/
Copy pathsetup_bitswap.go
File metadata and controls
201 lines (177 loc) · 7.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package main
import (
"context"
"slices"
"time"
"github.com/ipfs/boxo/routing/providerquerymanager"
"github.com/ipfs/boxo/bitswap"
bsclient "github.com/ipfs/boxo/bitswap/client"
"github.com/ipfs/boxo/bitswap/network"
bsnet "github.com/ipfs/boxo/bitswap/network/bsnet"
"github.com/ipfs/boxo/bitswap/network/httpnet"
bsserver "github.com/ipfs/boxo/bitswap/server"
"github.com/ipfs/boxo/blockstore"
"github.com/ipfs/boxo/exchange"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
metri "github.com/ipfs/go-metrics-interface"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
"github.com/libp2p/go-libp2p/core/routing"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
// peerstoreMergingHost wraps the bitswap host so Connect sees addresses
// the DHT host has learned for a peer.
//
// BasicHost.Connect runs AddAddrs(pi.Addrs, TempAddrTTL) and then dials
// every address in the host's peerstore for that peer. Sharing a libp2p
// host (kubo, ipfs-check) means identify exchanges, DHT response messages,
// and DCUtR coordination all enrich the same peerstore that bitswap reads
// at dial time. Rainbow's --dht-shared-host=false default runs the DHT on
// a separate libp2p host, so that enrichment lands on a peerstore the
// bitswap host never reads. The wrapper bridges that gap.
//
// IP-based addresses are filtered down to publicly routable ones. The DHT
// host's peerstore can hold loopback or RFC1918 entries that misconfigured
// peers stored in their routing tables; forwarding those would just waste
// resource-manager budget on dials that can never connect. Non-IP addresses
// (relay-only /p2p-circuit hops, DNS-based transports) cannot be classified
// without resolving them, so they pass through and let the swarm decide.
type peerstoreMergingHost struct {
host.Host
dhtAddrs peerstore.AddrBook
}
// Connect copies dialable DHT-known addresses for pi.ID into the main host's
// peerstore at TempAddrTTL (the same TTL BasicHost.Connect uses for the
// AddrInfo it receives), then delegates to the embedded host. Identify on
// the resulting connection refreshes the durable set on its own.
func (h *peerstoreMergingHost) Connect(ctx context.Context, pi peer.AddrInfo) error {
dialable := slices.DeleteFunc(h.dhtAddrs.Addrs(pi.ID), isUndialableMergedAddr)
if len(dialable) > 0 {
h.Host.Peerstore().AddAddrs(pi.ID, dialable, peerstore.TempAddrTTL)
}
return h.Host.Connect(ctx, pi)
}
// isUndialableMergedAddr reports whether a DHT-learned address should be
// dropped before forwarding into the bitswap host's peerstore. IP- and
// DNS-rooted addresses are filtered through manet.IsPublicAddr (which
// rejects RFC1918, loopback, link-local, and special-use DNS names like
// .local, .localhost, .invalid, .test). Everything else (relay-only
// /p2p-circuit hops, unknown transports) cannot be classified locally
// and is kept so the swarm can attempt it.
//
// Mirrors the hasIPOrDNSComponent + IsPublicAddr guard go-libp2p uses
// in BasicHost.filterPublicAddrs (p2p/host/basic/addrs_manager.go).
// Phrased as the predicate slices.DeleteFunc wants (true means drop).
func isUndialableMergedAddr(a ma.Multiaddr) bool {
if len(a) == 0 {
return true
}
switch a[0].Protocol().Code {
case ma.P_IP4, ma.P_IP6, ma.P_IP6ZONE, ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR:
return !manet.IsPublicAddr(a)
}
return false
}
// setupBitswapExchange wires bitswap onto h, the main libp2p host. In the
// split-host setup (dhtAddrs non-nil), h is wrapped so each bitswap Connect
// copies DHT-known public addresses into the peerstore before dialing.
func setupBitswapExchange(ctx context.Context, cfg Config, h host.Host, dhtAddrs peerstore.AddrBook, cr routing.ContentRouting, bstore blockstore.Blockstore) exchange.Interface {
bsctx := metri.CtxScope(ctx, "ipfs_bitswap")
connEvtMgr := network.NewConnectEventManager()
bitswapHost := h
if dhtAddrs != nil {
bitswapHost = &peerstoreMergingHost{Host: h, dhtAddrs: dhtAddrs}
}
var exnet network.BitSwapNetwork
bn := bsnet.NewFromIpfsHost(bitswapHost, bsnet.WithConnectEventManager(connEvtMgr))
if cfg.HTTPRetrievalEnable {
htnet := httpnet.New(h,
httpnet.WithHTTPWorkers(cfg.HTTPRetrievalWorkers),
httpnet.WithMaxDontHaveErrors(cfg.HTTPRetrievalMaxDontHaveErrors),
httpnet.WithAllowlist(cfg.HTTPRetrievalAllowlist),
httpnet.WithDenylist(cfg.HTTPRetrievalDenylist),
httpnet.WithUserAgent("rainbow/"+buildVersion()),
httpnet.WithMetricsLabelsForEndpoints(cfg.HTTPRetrievalMetricsLabelsForEndpoints),
httpnet.WithConnectEventManager(connEvtMgr),
)
exnet = network.New(h.Peerstore(), bn, htnet)
} else {
exnet = bn
}
// Custom query manager with the content router and the host
// and our custom options to overwrite the default.
pqm, err := providerquerymanager.New(exnet, cr,
providerquerymanager.WithMaxInProcessRequests(cfg.RoutingMaxRequests),
providerquerymanager.WithMaxProviders(cfg.RoutingMaxProviders),
providerquerymanager.WithMaxTimeout(cfg.RoutingMaxTimeout),
providerquerymanager.WithIgnoreProviders(cfg.RoutingIgnoreProviders...),
)
if err != nil {
panic(err)
}
context.AfterFunc(ctx, func() {
pqm.Close()
})
// --- Client Options
// bitswap.RebroadcastDelay: default is 1 minute to search for a random
// live-want (1 CID). I think we want to search for random live-wants more
// often although probably it overlaps with general rebroadcasts.
const rebroadcastDelay = 10 * time.Second
// bitswap.ProviderSearchDelay: default is 1 second.
const providerSearchDelay = 1 * time.Second
// --- Bitswap Client Options
clientOpts := []bsclient.Option{
bsclient.RebroadcastDelay(rebroadcastDelay),
bsclient.ProviderSearchDelay(providerSearchDelay),
bsclient.WithDefaultProviderQueryManager(false), // we pass it in manually
}
if !cfg.BitswapEnableDuplicateBlockStats {
clientOpts = append(clientOpts, bsclient.WithoutDuplicatedBlockStats())
}
// If peering and shared cache are both enabled, we initialize both a
// Client and a Server with custom request filter and custom options.
// client+server is more expensive but necessary when deployment requires
// serving cached blocks to safelisted peerids
if cfg.PeeringSharedCache && len(cfg.Peering) > 0 {
var peerBlockRequestFilter bsserver.PeerBlockRequestFilter
// Set up request filter to only respond to request for safelisted (peered) nodes
peers := make(map[peer.ID]struct{}, len(cfg.Peering))
for _, a := range cfg.Peering {
peers[a.ID] = struct{}{}
}
peerBlockRequestFilter = func(p peer.ID, c cid.Cid) bool {
_, ok := peers[p]
return ok
}
// turn bitswap clients option into bitswap options
var opts []bitswap.Option
for _, o := range clientOpts {
opts = append(opts, bitswap.WithClientOption(o))
}
// ---- Server Options
opts = append(opts,
bitswap.WithPeerBlockRequestFilter(peerBlockRequestFilter),
// When we don't have a block, don't reply. This reduces processment.
bitswap.SetSendDontHaves(false),
bitswap.WithWantHaveReplaceSize(cfg.BitswapWantHaveReplaceSize),
)
// Initialize client+server
bswap := bitswap.New(bsctx, exnet, pqm, bstore, opts...)
exnet.Start(bswap)
return &noNotifyExchange{bswap}
}
// By default, rainbow runs with bitswap client alone
bswap := bsclient.New(bsctx, exnet, pqm, bstore, clientOpts...)
exnet.Start(bswap)
return bswap
}
type noNotifyExchange struct {
exchange.Interface
}
func (e *noNotifyExchange) NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error {
// Rainbow does not notify when we get new blocks in our Blockservice.
return nil
}