Skip to content

Commit

Permalink
feat: Routing.Type=auto (DHT+IPNI)
Browse files Browse the repository at this point in the history
This changes the default routing to to be implicit DHT+IPNI

Full context:
#9422 (comment)
  • Loading branch information
lidel committed Dec 7, 2022
1 parent 7a8639e commit 57b98fe
Show file tree
Hide file tree
Showing 13 changed files with 152 additions and 69 deletions.
29 changes: 12 additions & 17 deletions cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const (
routingOptionNoneKwd = "none"
routingOptionCustomKwd = "custom"
routingOptionDefaultKwd = "default"
routingOptionAutoKwd = "auto"
unencryptTransportKwd = "disable-transport-encryption"
unrestrictedAPIAccessKwd = "unrestricted-api"
writableKwd = "writable"
Expand Down Expand Up @@ -89,7 +90,7 @@ For example, to change the 'Gateway' port:
ipfs config Addresses.Gateway /ip4/127.0.0.1/tcp/8082
The API address can be changed the same way:
The RPC API address can be changed the same way:
ipfs config Addresses.API /ip4/127.0.0.1/tcp/5002
Expand All @@ -100,14 +101,14 @@ other computers in the network, use 0.0.0.0 as the ip address:
ipfs config Addresses.Gateway /ip4/0.0.0.0/tcp/8080
Be careful if you expose the API. It is a security risk, as anyone could
Be careful if you expose the RPC API. It is a security risk, as anyone could
control your node remotely. If you need to control the node remotely,
make sure to protect the port as you would other services or database
(firewall, authenticated proxy, etc).
HTTP Headers
ipfs supports passing arbitrary headers to the API and Gateway. You can
ipfs supports passing arbitrary headers to the RPC API and Gateway. You can
do this by setting headers on the API.HTTPHeaders and Gateway.HTTPHeaders
keys:
Expand Down Expand Up @@ -141,18 +142,6 @@ environment variable:
export IPFS_PATH=/path/to/ipfsrepo
Routing
IPFS by default will use a DHT for content routing. There is an alternative
that operates the DHT in a 'client only' mode that can be enabled by
running the daemon as:
ipfs daemon --routing=dhtclient
Or you can set routing to dhtclient in the config:
ipfs config Routing.Type dhtclient
DEPRECATION NOTICE
Previously, ipfs used an environment variable as seen below:
Expand Down Expand Up @@ -402,14 +391,20 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment

routingOption, _ := req.Options[routingOptionKwd].(string)
if routingOption == routingOptionDefaultKwd {
routingOption = cfg.Routing.Type
routingOption = cfg.Routing.Type.WithDefault(routingOptionAutoKwd)
if routingOption == "" {
routingOption = routingOptionDHTKwd
routingOption = routingOptionAutoKwd
}
}
switch routingOption {
case routingOptionSupernodeKwd:
return errors.New("supernode routing was never fully implemented and has been removed")
case routingOptionDefaultKwd, routingOptionAutoKwd:
ncfg.Routing = libp2p.ConstructDefaultRouting(
cfg.Identity.PeerID,
cfg.Addresses.Swarm,
cfg.Identity.PrivKey,
)
case routingOptionDHTClientKwd:
ncfg.Routing = libp2p.DHTClientOption
case routingOptionDHTKwd:
Expand Down
2 changes: 1 addition & 1 deletion config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func InitWithIdentity(identity Identity) (*Config, error) {
},

Routing: Routing{
Type: "dht",
Type: nil,
Methods: nil,
Routers: nil,
},
Expand Down
2 changes: 1 addition & 1 deletion config/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ functionality - performance of content discovery and data
fetching may be degraded.
`,
Transform: func(c *Config) error {
c.Routing.Type = "dhtclient"
c.Routing.Type = NewOptionalString("dhtclient") // TODO: replace 'dhtclient' with 'autoclient' that prioritizes HTTP and uses dhtclient only after some delay as a fallback
c.AutoNAT.ServiceMode = AutoNATServiceDisabled
c.Reprovider.Interval = "0"

Expand Down
7 changes: 4 additions & 3 deletions config/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
type Routing struct {
// Type sets default daemon routing mode.
//
// Can be one of "dht", "dhtclient", "dhtserver", "none", or "custom".
// When "custom" is set, you can specify a list of Routers.
Type string
// Can be one of "auto", "dht", "dhtclient", "dhtserver", "none", or "custom".
// When unset or set to "auto", DHT and implicit routers are used.
// When "custom" is set, user-provided Routing.Routers is used.
Type *OptionalString `json:",omitempty"`

Routers Routers

Expand Down
4 changes: 2 additions & 2 deletions config/routing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestRouterParameters(t *testing.T) {
sec := time.Second
min := time.Minute
r := Routing{
Type: "custom",
Type: NewOptionalString("custom"),
Routers: map[string]RouterParser{
"router-dht": {Router{
Type: RouterTypeDHT,
Expand Down Expand Up @@ -113,7 +113,7 @@ func TestRouterMissingParameters(t *testing.T) {
require := require.New(t)

r := Routing{
Type: "custom",
Type: NewOptionalString("custom"),
Routers: map[string]RouterParser{
"router-wrong-reframe": {Router{
Type: RouterTypeReframe,
Expand Down
2 changes: 1 addition & 1 deletion core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func GetNode(t *testing.T, reframeURLs ...string) *IpfsNode {
API: []string{"/ip4/127.0.0.1/tcp/0"},
},
Routing: config.Routing{
Type: "custom",
Type: config.NewOptionalString("custom"),
Routers: routers,
Methods: config.Methods{
config.MethodNameFindPeers: config.Method{
Expand Down
60 changes: 60 additions & 0 deletions core/node/libp2p/routingopt.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package libp2p

import (
"context"
"time"

"github.com/ipfs/go-datastore"
"github.com/ipfs/kubo/config"
Expand All @@ -23,6 +24,64 @@ type RoutingOption func(
...peer.AddrInfo,
) (routing.Routing, error)

// Default HTTP routers used in parallel to DHT when Routing.Type = "auto"
var defaultHTTPRouters = []string{
"https://dev.cid.contact/routing/v1", // TODO: remove when cid.contact is ready
"https://cid.contact/routing/v1", // https://github.com/ipfs/kubo/issues/9422#issuecomment-1338142084
// TODO: add an independent router from Cloudflare
}

// ConstructDefaultRouting returns routers used when Routing.Type is unset or set to "auto"
func ConstructDefaultRouting(peerID string, addrs []string, privKey string) func(
ctx context.Context,
host host.Host,
dstore datastore.Batching,
validator record.Validator,
bootstrapPeers ...peer.AddrInfo,
) (routing.Routing, error) {
return func(
ctx context.Context,
host host.Host,
dstore datastore.Batching,
validator record.Validator,
bootstrapPeers ...peer.AddrInfo,
) (routing.Routing, error) {
// Defined routers will be queried in parallel (optimizing for response speed)
// Different trade-offs can be made by setting Routing.Type = "custom" with own Routing.Routers
var routers []*routinghelpers.ParallelRouter

// Run the default DHT routing (same as Routing.Type = "dht")
dhtRouting, err := DHTOption(ctx, host, dstore, validator, bootstrapPeers...)
if err != nil {
return nil, err
}
routers = append(routers, &routinghelpers.ParallelRouter{
Router: dhtRouting,
IgnoreError: false, // TODO: confirm this is what we want
Timeout: 1 * time.Minute, // TODO: what value makes sense here?
ExecuteAfter: 0,
})

// Append HTTP routers for additional speed
for _, endpoint := range defaultHTTPRouters {
httpRouter, err := irouting.ConstructHTTPRouter(endpoint, peerID, addrs, privKey)
if err != nil {
return nil, err
}
routers = append(routers, &routinghelpers.ParallelRouter{
Router: httpRouter,
IgnoreError: true, // TODO: confirm this is what we want
Timeout: 1 * time.Minute, // TODO: what value makes sense here?
ExecuteAfter: 0,
})
}

routing := routinghelpers.NewComposableParallel(routers)
return routing, nil
}
}

// constructDHTRouting is used when Routing.Type = "dht"
func constructDHTRouting(mode dht.ModeOpt) func(
ctx context.Context,
host host.Host,
Expand All @@ -49,6 +108,7 @@ func constructDHTRouting(mode dht.ModeOpt) func(
}
}

// ConstructDelegatedRouting is used when Routing.Type = "custom"
func ConstructDelegatedRouting(routers config.Routers, methods config.Methods, peerID string, addrs []string, privKey string) func(
ctx context.Context,
host host.Host,
Expand Down
85 changes: 44 additions & 41 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ config file at runtime.
- [`Reprovider.Interval`](#reproviderinterval)
- [`Reprovider.Strategy`](#reproviderstrategy)
- [`Routing`](#routing)
- [`Routing.Type`](#routingtype)
- [`Routing.Routers`](#routingrouters)
- [`Routing.Routers: Type`](#routingrouters-type)
- [`Routing.Routers: Parameters`](#routingrouters-parameters)
- [`Routing: Methods`](#routing-methods)
- [`Routing.Type`](#routingtype)
- [`Swarm`](#swarm)
- [`Swarm.AddrFilters`](#swarmaddrfilters)
- [`Swarm.DisableBandwidthMetrics`](#swarmdisablebandwidthmetrics)
Expand Down Expand Up @@ -1322,6 +1322,49 @@ Type: `string` (or unset for the default, which is "all")

Contains options for content, peer, and IPNS routing mechanisms.

### `Routing.Type`

There are multiple routing options: "auto", "none", "dht" and "custom".

* **DEFAULT:** If unset, or set to "auto", your node will use the IPFS DHT
and parallel HTTP routers listed below for additional speed.

* If set to "none", your node will use _no_ routing system. You'll have to
explicitly connect to peers that have the content you're looking for.

* If set to "dht" (or "dhtclient"/"dhtserver"), your node will ONLY use the IPFS DHT (no HTTP routers).

* If set to "custom", all default routers are disabled, and only onles defined in `Routing.Routers` will be used.

When the DHT is enabled, it can operate in two modes: client and server.

* In server mode, your node will query other peers for DHT records, and will
respond to requests from other peers (both requests to store records and
requests to retrieve records).

* In client mode, your node will query the DHT as a client but will not respond
to requests from other peers. This mode is less resource-intensive than server
mode.

When `Routing.Type` is set to `auto` or `dht`, your node will start as a DHT client, and
switch to a DHT server when and if it determines that it's reachable from the
public internet (e.g., it's not behind a firewall).

To force a specific DHT-only mode, client or server, set `Routing.Type` to
`dhtclient` or `dhtserver` respectively. Please do not set this to `dhtserver`
unless you're sure your node is reachable from the public network.

When `Routing.Type` is set to `auto` your node will accelerate some types of routing
by leveraging HTTP endpoints compatible with [IPIP-337](https://github.com/ipfs/specs/pull/337)
in addition to the IPFS DHT.
By default, an instance of [IPNI](https://github.com/ipni/specs/blob/main/IPNI.md#readme)
at https://cid.contact is used.
Alternative routing rules can be configured in `Routing.Routers` after setting `Routing.Type` to `custom`.

Default: `auto` (DHT + IPNI)

Type: `optionalString` (`null`/missing means the default)

### `Routing.Routers`

**EXPERIMENTAL: `Routing.Routers` configuration may change in future release**
Expand Down Expand Up @@ -1463,46 +1506,6 @@ ipfs config Routing.Methods --json '{
```

### `Routing.Type`

There are three core routing options: "none", "dht" (default) and "custom".

* If set to "none", your node will use _no_ routing system. You'll have to
explicitly connect to peers that have the content you're looking for.
* If set to "dht" (or "dhtclient"/"dhtserver"), your node will use the IPFS DHT.
* If set to "custom", `Routing.Routers` will be used.

When the DHT is enabled, it can operate in two modes: client and server.

* In server mode, your node will query other peers for DHT records, and will
respond to requests from other peers (both requests to store records and
requests to retrieve records).
* In client mode, your node will query the DHT as a client but will not respond
to requests from other peers. This mode is less resource-intensive than server
mode.

When `Routing.Type` is set to `dht`, your node will start as a DHT client, and
switch to a DHT server when and if it determines that it's reachable from the
public internet (e.g., it's not behind a firewall).

To force a specific DHT mode, client or server, set `Routing.Type` to
`dhtclient` or `dhtserver` respectively. Please do not set this to `dhtserver`
unless you're sure your node is reachable from the public network.

**Example:**

```json
{
"Routing": {
"Type": "dhtclient"
}
}
```

Default: `dht`

Type: `optionalString` (`null`/missing means the default)

## `Swarm`

Options for configuring the swarm.
Expand Down
2 changes: 1 addition & 1 deletion repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func initTempNode(ctx context.Context, bootstrap []string, peers []peer.AddrInfo
}

// configure the temporary node
cfg.Routing.Type = "dhtclient"
cfg.Routing.Type = config.NewOptionalString("dhtclient")

// Disable listening for inbound connections
cfg.Addresses.Gateway = []string{}
Expand Down
16 changes: 16 additions & 0 deletions routing/delegated.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,22 @@ type ExtraHTTPParams struct {
PrivKeyB64 string
}

func ConstructHTTPRouter(endpoint string, peerID string, addrs []string, privKey string) (routing.Routing, error) {
return httpRoutingFromConfig(
config.Router{
Type: "http",
Parameters: &config.HTTPRouterParams{
Endpoint: endpoint,
},
},
&ExtraHTTPParams{
PeerID: peerID,
Addrs: addrs,
PrivKeyB64: privKey,
},
)
}

func httpRoutingFromConfig(conf config.Router, extraHTTP *ExtraHTTPParams) (routing.Routing, error) {
params := conf.Parameters.(*config.HTTPRouterParams)
if params.Endpoint == "" {
Expand Down
2 changes: 1 addition & 1 deletion test/sharness/t0041-ping.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ test_expect_success "test ping other" '

test_expect_success "test ping unreachable peer" '
printf "Looking up peer %s\n" "$BAD_PEER" > bad_ping_exp &&
printf "PING QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJx.\nPing error: routing: not found\nError: ping failed\n" >> bad_ping_exp &&
printf "PING QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJx.\nPing error: no addresses\nError: ping failed\n" >> bad_ping_exp &&
! ipfsi 0 ping -n2 -- "$BAD_PEER" > bad_ping_actual 2>&1 &&
test_cmp bad_ping_exp bad_ping_actual
'
Expand Down
4 changes: 4 additions & 0 deletions test/sharness/t0170-legacy-dht.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ test_dht() {
iptb testbed create -type localipfs -count $NUM_NODES -init
'

test_expect_success 'DHT-only routing' '
iptb run -- ipfs config Routing.Type dht
'

startup_cluster $NUM_NODES $@

test_expect_success 'peer ids' '
Expand Down
6 changes: 5 additions & 1 deletion test/sharness/t0170-routing-dht.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This file does the same tests as t0170-dht.sh but uses 'routing' commands instead
# (only exception is query, which lives only under dht)
test_description="Test routing command"
test_description="Test routing command for DHT queries"

. lib/test-lib.sh

Expand All @@ -14,6 +14,10 @@ test_dht() {
iptb testbed create -type localipfs -count $NUM_NODES -init
'

test_expect_success 'DHT-only routing' '
iptb run -- ipfs config Routing.Type dht
'

startup_cluster $NUM_NODES $@

test_expect_success 'peer ids' '
Expand Down

0 comments on commit 57b98fe

Please sign in to comment.