From 813b59fc38e9ea9a8149f4dd5ad8531408b0441e Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 15 Feb 2024 10:36:28 +0100 Subject: [PATCH] routing/http: add ProvideRecords, ProvidePeerRecords for direct publish --- routing/http/client/client.go | 57 +++++++++++++++++++---- routing/http/client/client_test.go | 3 +- routing/http/types/record_announcement.go | 2 +- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/routing/http/client/client.go b/routing/http/client/client.go index 0e3fb4931..c66525daa 100644 --- a/routing/http/client/client.go +++ b/routing/http/client/client.go @@ -72,12 +72,6 @@ type httpClient interface { type Option func(*Client) -func WithIdentity(identity crypto.PrivKey) Option { - return func(c *Client) { - c.identity = identity - } -} - func WithHTTPClient(h httpClient) Option { return func(c *Client) { c.httpClient = h @@ -101,8 +95,15 @@ func WithUserAgent(ua string) Option { } } -func WithProviderInfo(peerID peer.ID, addrs []multiaddr.Multiaddr, protocols []string) Option { +// WithProviderInfo configures the [Client] with the given provider information. +// This is used by the methods [Client.Provide] and [Client.ProvidePeer] in order +// to create and sign announcement records. +// +// You can still use [Client.ProvideRecords] and [Client.ProvidePeerRecords] +// without this configuration. Then, you must provide already signed-records. +func WithProviderInfo(identity crypto.PrivKey, peerID peer.ID, addrs []multiaddr.Multiaddr, protocols []string) Option { return func(c *Client) { + c.identity = identity c.peerID = peerID c.protocols = protocols for _, a := range addrs { @@ -236,6 +237,9 @@ func (c *Client) FindProviders(ctx context.Context, key cid.Cid) (providers iter return &measuringIter[iter.Result[types.Record]]{Iter: it, ctx: ctx, m: m}, nil } +// Provide publishes [types.AnnouncementRecord]s based on the given [types.AnnouncementRequests]. +// This records will be signed by your provided. Therefore, the [Client] must have been configured +// with [WithProviderInfo]. func (c *Client) Provide(ctx context.Context, announcements ...types.AnnouncementRequest) (iter.ResultIter[*types.AnnouncementRecord], error) { if err := c.canProvide(); err != nil { return nil, err @@ -282,7 +286,24 @@ func (c *Client) Provide(ctx context.Context, announcements ...types.Announcemen req := jsontypes.AnnounceProvidersRequest{ Providers: records, } + return c.provide(ctx, url, req) +} + +// ProvideRecords publishes the given [types.AnnouncementRecord]. An error will +// be returned if the records aren't signed or valid. +func (c *Client) ProvideRecords(ctx context.Context, records ...*types.AnnouncementRecord) (iter.ResultIter[*types.AnnouncementRecord], error) { + providerRecords := make([]types.Record, len(records)) + for i, record := range records { + if err := record.Verify(); err != nil { + return nil, err + } + providerRecords[i] = records[i] + } + url := c.baseURL + "/routing/v1/providers" + req := jsontypes.AnnounceProvidersRequest{ + Providers: providerRecords, + } return c.provide(ctx, url, req) } @@ -429,7 +450,8 @@ func (c *Client) FindPeers(ctx context.Context, pid peer.ID) (peers iter.ResultI return &measuringIter[iter.Result[*types.PeerRecord]]{Iter: it, ctx: ctx, m: m}, nil } -// ProvidePeer provides information regarding your own peer, setup with [WithProviderInfo]. +// ProvidePeer publishes an [types.AnnouncementRecord] with the provider +// information from your peer, configured with [WithProviderInfo]. func (c *Client) ProvidePeer(ctx context.Context, ttl time.Duration, metadata []byte) (iter.ResultIter[*types.AnnouncementRecord], error) { if err := c.canProvide(); err != nil { return nil, err @@ -438,7 +460,6 @@ func (c *Client) ProvidePeer(ctx context.Context, ttl time.Duration, metadata [] record := &types.AnnouncementRecord{ Schema: types.SchemaAnnouncement, Payload: types.AnnouncementPayload{ - // TODO: CID, Scope not present for /routing/v1/peers, right? Timestamp: time.Now(), TTL: ttl, ID: &c.peerID, @@ -472,6 +493,24 @@ func (c *Client) ProvidePeer(ctx context.Context, ttl time.Duration, metadata [] return c.provide(ctx, url, req) } +// ProvidePeerRecords publishes the given [types.AnnouncementRecord]. An error will +// be returned if the records aren't signed or valid. +func (c *Client) ProvidePeerRecords(ctx context.Context, records ...*types.AnnouncementRecord) (iter.ResultIter[*types.AnnouncementRecord], error) { + providerRecords := make([]types.Record, len(records)) + for i, record := range records { + if err := record.Verify(); err != nil { + return nil, err + } + providerRecords[i] = records[i] + } + + url := c.baseURL + "/routing/v1/peers" + req := jsontypes.AnnouncePeersRequest{ + Peers: providerRecords, + } + return c.provide(ctx, url, req) +} + // GetIPNS tries to retrieve the [ipns.Record] for the given [ipns.Name]. The record is // validated against the given name. If validation fails, an error is returned, but no // record. diff --git a/routing/http/client/client_test.go b/routing/http/client/client_test.go index 2efa49311..2e6e43b0d 100644 --- a/routing/http/client/client_test.go +++ b/routing/http/client/client_test.go @@ -135,8 +135,7 @@ func makeTestDeps(t *testing.T, clientsOpts []Option, serverOpts []server.Option serverAddr := "http://" + server.Listener.Addr().String() recordingHTTPClient := &recordingHTTPClient{httpClient: defaultHTTPClient} defaultClientOpts := []Option{ - WithProviderInfo(peerID, addrs, nil), - WithIdentity(identity), + WithProviderInfo(identity, peerID, addrs, nil), WithUserAgent(testUserAgent), WithHTTPClient(recordingHTTPClient), } diff --git a/routing/http/types/record_announcement.go b/routing/http/types/record_announcement.go index 8871743fa..2d8e8d9c4 100644 --- a/routing/http/types/record_announcement.go +++ b/routing/http/types/record_announcement.go @@ -210,7 +210,7 @@ func (ar AnnouncementRecord) IsSigned() bool { func (ar *AnnouncementRecord) Sign(peerID peer.ID, key crypto.PrivKey) error { if ar.IsSigned() { - return errors.New("already signed") + return ar.Verify() } if key == nil {