Skip to content

Commit

Permalink
feat: contentrouter implements routing.ValueStore
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Aug 9, 2023
1 parent 0f70f92 commit ecc6fb4
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 0 deletions.
73 changes: 73 additions & 0 deletions routing/http/contentrouter/contentrouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package contentrouter
import (
"context"
"reflect"
"strings"

"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/routing/http/types"
"github.com/ipfs/boxo/routing/http/types/iter"
"github.com/ipfs/go-cid"
Expand All @@ -20,6 +22,8 @@ var logger = logging.Logger("routing/http/contentrouter")
type Client interface {
GetProviders(ctx context.Context, key cid.Cid) (iter.ResultIter[types.Record], error)
GetPeers(ctx context.Context, pid peer.ID) (peers iter.ResultIter[types.Record], err error)
GetIPNSRecord(ctx context.Context, name ipns.Name) (*ipns.Record, error)
PutIPNSRecord(ctx context.Context, name ipns.Name, record *ipns.Record) error
}

type contentRouter struct {
Expand All @@ -28,6 +32,7 @@ type contentRouter struct {

var _ routing.ContentRouting = (*contentRouter)(nil)
var _ routing.PeerRouting = (*contentRouter)(nil)
var _ routing.ValueStore = (*contentRouter)(nil)
var _ routinghelpers.ProvideManyRouter = (*contentRouter)(nil)
var _ routinghelpers.ReadyAbleRouter = (*contentRouter)(nil)

Expand Down Expand Up @@ -143,3 +148,71 @@ func (c *contentRouter) FindPeer(ctx context.Context, pid peer.ID) (peer.AddrInf

return peer.AddrInfo{}, err

Check warning on line 149 in routing/http/contentrouter/contentrouter.go

View check run for this annotation

Codecov / codecov/patch

routing/http/contentrouter/contentrouter.go#L149

Added line #L149 was not covered by tests
}

func (c *contentRouter) PutValue(ctx context.Context, key string, data []byte, opts ...routing.Option) error {
if !strings.HasPrefix(key, "/ipns/") {
return routing.ErrNotSupported
}

name, err := ipns.NameFromRoutingKey([]byte(key))
if err != nil {
return err
}

record, err := ipns.UnmarshalRecord(data)
if err != nil {
return err
}

return c.client.PutIPNSRecord(ctx, name, record)
}

func (c *contentRouter) GetValue(ctx context.Context, key string, opts ...routing.Option) ([]byte, error) {
if !strings.HasPrefix(key, "/ipns/") {
return nil, routing.ErrNotSupported
}

name, err := ipns.NameFromRoutingKey([]byte(key))
if err != nil {
return nil, err
}

record, err := c.client.GetIPNSRecord(ctx, name)
if err != nil {
return nil, err
}

Check warning on line 183 in routing/http/contentrouter/contentrouter.go

View check run for this annotation

Codecov / codecov/patch

routing/http/contentrouter/contentrouter.go#L182-L183

Added lines #L182 - L183 were not covered by tests

return ipns.MarshalRecord(record)
}

func (c *contentRouter) SearchValue(ctx context.Context, key string, opts ...routing.Option) (<-chan []byte, error) {
if !strings.HasPrefix(key, "/ipns/") {
return nil, routing.ErrNotSupported
}

Check warning on line 191 in routing/http/contentrouter/contentrouter.go

View check run for this annotation

Codecov / codecov/patch

routing/http/contentrouter/contentrouter.go#L188-L191

Added lines #L188 - L191 were not covered by tests

name, err := ipns.NameFromRoutingKey([]byte(key))
if err != nil {
return nil, err
}

Check warning on line 196 in routing/http/contentrouter/contentrouter.go

View check run for this annotation

Codecov / codecov/patch

routing/http/contentrouter/contentrouter.go#L193-L196

Added lines #L193 - L196 were not covered by tests

ch := make(chan []byte)

go func() {
record, err := c.client.GetIPNSRecord(ctx, name)
if err != nil {
close(ch)
return
}

Check warning on line 205 in routing/http/contentrouter/contentrouter.go

View check run for this annotation

Codecov / codecov/patch

routing/http/contentrouter/contentrouter.go#L198-L205

Added lines #L198 - L205 were not covered by tests

raw, err := ipns.MarshalRecord(record)
if err != nil {
close(ch)
return
}

Check warning on line 211 in routing/http/contentrouter/contentrouter.go

View check run for this annotation

Codecov / codecov/patch

routing/http/contentrouter/contentrouter.go#L207-L211

Added lines #L207 - L211 were not covered by tests

ch <- raw
close(ch)

Check warning on line 214 in routing/http/contentrouter/contentrouter.go

View check run for this annotation

Codecov / codecov/patch

routing/http/contentrouter/contentrouter.go#L213-L214

Added lines #L213 - L214 were not covered by tests
}()

return ch, nil

Check warning on line 217 in routing/http/contentrouter/contentrouter.go

View check run for this annotation

Codecov / codecov/patch

routing/http/contentrouter/contentrouter.go#L217

Added line #L217 was not covered by tests
}
99 changes: 99 additions & 0 deletions routing/http/contentrouter/contentrouter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import (
"context"
"crypto/rand"
"testing"
"time"

"github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/ipns"
ipfspath "github.com/ipfs/boxo/path"
"github.com/ipfs/boxo/routing/http/types"
"github.com/ipfs/boxo/routing/http/types/iter"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/routing"
"github.com/multiformats/go-multihash"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
Expand All @@ -28,6 +34,15 @@ func (m *mockClient) Ready(ctx context.Context) (bool, error) {
args := m.Called(ctx)
return args.Bool(0), args.Error(1)
}
func (m *mockClient) GetIPNSRecord(ctx context.Context, name ipns.Name) (*ipns.Record, error) {
args := m.Called(ctx, name)
return args.Get(0).(*ipns.Record), args.Error(1)
}
func (m *mockClient) PutIPNSRecord(ctx context.Context, name ipns.Name, record *ipns.Record) error {
args := m.Called(ctx, name, record)
return args.Error(0)
}

func makeCID() cid.Cid {
buf := make([]byte, 63)
_, err := rand.Read(buf)
Expand Down Expand Up @@ -108,3 +123,87 @@ func TestFindPeer(t *testing.T) {
require.NoError(t, err)
require.Equal(t, peer.ID, p1)
}

func makeName(t *testing.T) (crypto.PrivKey, ipns.Name) {
sk, _, err := crypto.GenerateEd25519Key(rand.Reader)
require.NoError(t, err)

pid, err := peer.IDFromPrivateKey(sk)
require.NoError(t, err)

return sk, ipns.NameFromPeer(pid)
}

func makeIPNSRecord(t *testing.T, sk crypto.PrivKey, opts ...ipns.Option) (*ipns.Record, []byte) {
cid, err := cid.Decode("bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4")
require.NoError(t, err)

path := path.IpfsPath(cid)
eol := time.Now().Add(time.Hour * 48)
ttl := time.Second * 20

record, err := ipns.NewRecord(sk, ipfspath.FromString(path.String()), 1, eol, ttl, opts...)
require.NoError(t, err)

rawRecord, err := ipns.MarshalRecord(record)
require.NoError(t, err)

return record, rawRecord
}

func TestGetValue(t *testing.T) {
ctx := context.Background()
client := &mockClient{}
crc := NewContentRoutingClient(client)

t.Run("Fail On Unsupported Key", func(t *testing.T) {
v, err := crc.GetValue(ctx, "/something/unsupported")
require.Nil(t, v)
require.ErrorIs(t, err, routing.ErrNotSupported)
})

t.Run("Fail On Invalid IPNS Name", func(t *testing.T) {
v, err := crc.GetValue(ctx, "/ipns/invalid")
require.Nil(t, v)
require.Error(t, err)
})

t.Run("Succeeds On Valid IPNS Name", func(t *testing.T) {
sk, name := makeName(t)
rec, rawRec := makeIPNSRecord(t, sk)
client.On("GetIPNSRecord", ctx, name).Return(rec, nil)
v, err := crc.GetValue(ctx, string(name.RoutingKey()))
require.NoError(t, err)
require.Equal(t, rawRec, v)
})
}

func TestPutValue(t *testing.T) {
ctx := context.Background()
client := &mockClient{}
crc := NewContentRoutingClient(client)

sk, name := makeName(t)
_, rawRec := makeIPNSRecord(t, sk)

t.Run("Fail On Unsupported Key", func(t *testing.T) {
err := crc.PutValue(ctx, "/something/unsupported", rawRec)
require.ErrorIs(t, err, routing.ErrNotSupported)
})

t.Run("Fail On Invalid IPNS Name", func(t *testing.T) {
err := crc.PutValue(ctx, "/ipns/invalid", rawRec)
require.Error(t, err)
})

t.Run("Fail On Invalid IPNS Record", func(t *testing.T) {
err := crc.PutValue(ctx, string(name.RoutingKey()), []byte("gibberish"))
require.Error(t, err)
})

t.Run("Succeeds On Valid IPNS Name & Record", func(t *testing.T) {
client.On("PutIPNSRecord", ctx, name, mock.Anything).Return(nil)
err := crc.PutValue(ctx, string(name.RoutingKey()), rawRec)
require.NoError(t, err)
})
}

0 comments on commit ecc6fb4

Please sign in to comment.