Skip to content

Commit

Permalink
Enhancement/ns dial error (#14)
Browse files Browse the repository at this point in the history
* Include namespace in Vat connection errors to help diagnose misconfigured dial attempts.

* Report dial errors resulting from namespace mismatch.
  • Loading branch information
lthibault authored Apr 21, 2022
1 parent b0456cf commit fe075d4
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 2 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ require (
github.com/multiformats/go-multibase v0.0.3 // indirect
github.com/multiformats/go-multicodec v0.4.1 // indirect
github.com/multiformats/go-multihash v0.1.0 // indirect
github.com/multiformats/go-multistream v0.3.0 // indirect
github.com/multiformats/go-multistream v0.3.0
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
Expand Down
37 changes: 37 additions & 0 deletions pkg/client/dialer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
swarm "github.com/libp2p/go-libp2p-swarm"
inproc "github.com/lthibault/go-libp2p-inproc-transport"
ma "github.com/multiformats/go-multiaddr"
"github.com/multiformats/go-multistream"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -107,10 +108,46 @@ func TestDialer(t *testing.T) {
Boot: boot.StaticAddrs{*host.InfoFromHost(h)},
}.Dial(ctx)

assert.ErrorIs(t, err, multistream.ErrNotSupported)
assert.EqualError(t, err, "protocol not supported")
assert.Nil(t, n, "should return nil client node")
})

t.Run("NamespaceMismatch", func(t *testing.T) {
t.Parallel()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

h, err := libp2p.New(
libp2p.NoListenAddrs,
libp2p.NoTransports,
libp2p.ListenAddrStrings("/inproc/~"),
libp2p.Transport(inproc.New()))
require.NoError(t, err, "must succeed")
defer h.Close()

clt := newVat()
defer clt.Host.Close()

clt.NS = "wrong.namespace"

svr := vat.Network{
NS: "test",
Host: h,
}
svr.Export(pubsub.Capability, mockPubSub{})
svr.Export(cluster.ViewCapability, mockView{})

n, err := client.Dialer{
Vat: clt,
Boot: boot.StaticAddrs{*host.InfoFromHost(h)},
}.Dial(ctx)

assert.ErrorIs(t, err, vat.ErrInvalidNS)
assert.Nil(t, n, "should return nil client node")
})

t.Run("Success", func(t *testing.T) {
t.Parallel()

Expand Down
43 changes: 42 additions & 1 deletion pkg/vat/vat.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ package vat

import (
"context"
"errors"
"strings"

"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/protocol"
"github.com/multiformats/go-multistream"
ww "github.com/wetware/ww/pkg"

"capnproto.org/go/capnp/v3"
"capnproto.org/go/capnp/v3/rpc"
)

var ErrInvalidNS = errors.New("invalid namespace")

type Capability interface {
// Protocols returns the IDs for the given capability.
// Implementations SHOULD order protocol identifiers in decreasing
Expand Down Expand Up @@ -79,7 +84,15 @@ func (n Network) Connect(ctx context.Context, vat peer.AddrInfo, c Capability) (

s, err := n.Host.NewStream(ctx, vat.ID, n.protocolsFor(c)...)
if err != nil {
return nil, err
if err != multistream.ErrNotSupported {
return nil, err
}

if n.isInvalidNS(vat.ID, c) {
return nil, ErrInvalidNS
}

return nil, err // TODO: catch multistream.ErrNotSupported
}

return rpc.NewConn(c.Upgrade(s), &rpc.Options{
Expand Down Expand Up @@ -120,6 +133,34 @@ func (n Network) protocolsFor(c Capability) []protocol.ID {
return ps
}

func (n Network) isInvalidNS(id peer.ID, c Capability) bool {
ps, err := n.Host.Peerstore().GetProtocols(id)
if err != nil {
return false
}

for _, proto := range ps {
if match(c, proto) {
// the remote peer supports the capability, so it
// has to be a namespace mismatch.
return true
}
}

return false // not a ns issue; proto actually unsupported
}

// match the protocol, ignoring namespace
func match(c Capability, proto string) bool {
for _, p := range c.Protocols() {
if strings.HasSuffix(proto, string(p)) {
return true
}
}

return false
}

func bootstrapper(c Capability) *capnp.Client {
if b, ok := c.(Bootstrapper); ok {
return b.Bootstrap()
Expand Down

0 comments on commit fe075d4

Please sign in to comment.