Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Commit

Permalink
feat: use PublishOptions for publishing IPNS records
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Dec 12, 2022
1 parent 8e7bf04 commit 6b648ff
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 75 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/ipfs/go-ipns v0.2.0
github.com/ipfs/go-log v1.0.5
github.com/ipfs/go-path v0.1.1
github.com/ipfs/interface-go-ipfs-core v0.8.0
github.com/ipfs/interface-go-ipfs-core v0.8.1-0.20221212115130-b26fad930d10
github.com/jbenet/goprocess v0.1.4
github.com/libp2p/go-libp2p v0.23.4
github.com/libp2p/go-libp2p-kad-dht v0.19.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ github.com/ipfs/go-peertaskqueue v0.2.0/go.mod h1:5/eNrBEbtSKWCG+kQK8K8fGNixoYUn
github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw=
github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s=
github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0=
github.com/ipfs/interface-go-ipfs-core v0.8.0 h1:pNs34l947fvNOh+XEjXnHW/GV6HXmEzJNeqZFhX4GoQ=
github.com/ipfs/interface-go-ipfs-core v0.8.0/go.mod h1:WYC2H6Mu7aGqhlupi/CVawcs0X1Me4uRvV0rcTlo3zM=
github.com/ipfs/interface-go-ipfs-core v0.8.1-0.20221212115130-b26fad930d10 h1:TtMipqa0EGM3vT/u8MY8g3RILx8jajYUcwAvr+Sc0jI=
github.com/ipfs/interface-go-ipfs-core v0.8.1-0.20221212115130-b26fad930d10/go.mod h1:WYC2H6Mu7aGqhlupi/CVawcs0X1Me4uRvV0rcTlo3zM=
github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9MSJkMLSwnhA=
github.com/ipld/go-ipld-prime v0.11.0 h1:jD/b/22R7CSL+F9xNffcexs+wO0Ji/TfwXO/TWck+70=
github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8=
Expand Down
8 changes: 1 addition & 7 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ package namesys

import (
"errors"
"time"

"context"

Expand Down Expand Up @@ -95,12 +94,7 @@ type Resolver interface {

// Publisher is an object capable of publishing particular names.
type Publisher interface {

// Publish establishes a name-value mapping.
// TODO make this not PrivKey specific.
Publish(ctx context.Context, name ci.PrivKey, value path.Path) error

// TODO: to be replaced by a more generic 'PublishWithValidity' type
// call once the records spec is implemented
PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error
Publish(ctx context.Context, name ci.PrivKey, value path.Path, options ...opts.PublishOption) error
}
24 changes: 13 additions & 11 deletions namesys.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,34 +285,36 @@ func emitOnceResult(ctx context.Context, outCh chan<- onceResult, r onceResult)
}

// Publish implements Publisher
func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path, options ...opts.PublishOption) error {
ctx, span := StartSpan(ctx, "MPNS.Publish")
defer span.End()
return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordEOL))
}

func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error {
ctx, span := StartSpan(ctx, "MPNS.PublishWithEOL", trace.WithAttributes(attribute.String("Value", value.String())))
defer span.End()
// This is a bit hacky. We do this because the EOL is based on the current
// time, but also needed in the end of the function. Therefore, we parse
// the options immediately and add an option PublishWithEOL with the EOL
// calculated in this moment.
publishOpts := opts.ProcessPublishOptions(options)
options = append(options, opts.PublishWithEOL(publishOpts.EOL))

id, err := peer.IDFromPrivateKey(name)
if err != nil {
span.RecordError(err)
return err
}
span.SetAttributes(attribute.String("ID", id.String()))
if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil {
if err := ns.ipnsPublisher.Publish(ctx, name, value, options...); err != nil {
// Invalidate the cache. Publishing may _partially_ succeed but
// still return an error.
ns.cacheInvalidate(string(id))
span.RecordError(err)
return err
}
ttl := DefaultResolverCacheTTL
if setTTL, ok := checkCtxTTL(ctx); ok {
ttl = setTTL
if publishOpts.TTL >= 0 {
ttl = publishOpts.TTL
}
if ttEol := time.Until(eol); ttEol < ttl {
ttl = ttEol
if ttEOL := time.Until(publishOpts.EOL); ttEOL < ttl {
ttl = ttEOL
}
ns.cacheSet(string(id), value, ttl)
return nil
Expand Down
3 changes: 1 addition & 2 deletions namesys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,7 @@ func TestPublishWithTTL(t *testing.T) {
ttl := 1 * time.Second
eol := time.Now().Add(2 * time.Second)

ctx := ContextWithTTL(context.Background(), ttl)
err = nsys.Publish(ctx, priv, p)
err = nsys.Publish(context.Background(), priv, p, opts.PublishWithEOL(eol), opts.PublishWithTTL(ttl))
if err != nil {
t.Fatal(err)
}
Expand Down
64 changes: 15 additions & 49 deletions publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/ipfs/go-ipns"
pb "github.com/ipfs/go-ipns/pb"
"github.com/ipfs/go-path"
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/routing"
Expand All @@ -22,11 +23,6 @@ import (

const ipnsPrefix = "/ipns/"

// DefaultRecordEOL specifies the time that the network will cache IPNS
// records after being publihsed. Records should be re-published before this
// interval expires.
const DefaultRecordEOL = 24 * time.Hour

// IpnsPublisher is capable of publishing and resolving names to the IPFS
// routing system.
type IpnsPublisher struct {
Expand All @@ -47,9 +43,18 @@ func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher

// Publish implements Publisher. Accepts a keypair and a value,
// and publishes it out to the routing system
func (p *IpnsPublisher) Publish(ctx context.Context, k crypto.PrivKey, value path.Path) error {
func (p *IpnsPublisher) Publish(ctx context.Context, k crypto.PrivKey, value path.Path, options ...opts.PublishOption) error {
log.Debugf("Publish %s", value)
return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordEOL))

ctx, span := StartSpan(ctx, "IpnsPublisher.Publish", trace.WithAttributes(attribute.String("Value", value.String())))
defer span.End()

record, err := p.updateRecord(ctx, k, value, options...)
if err != nil {
return err
}

return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record)
}

// IpnsDsKey returns a datastore key given an IPNS identifier (peer
Expand Down Expand Up @@ -142,7 +147,7 @@ func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti
return e, nil
}

func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) {
func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, value path.Path, options ...opts.PublishOption) (*pb.IpnsEntry, error) {
id, err := peer.IDFromPrivateKey(k)
if err != nil {
return nil, err
Expand All @@ -164,12 +169,10 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, valu
seqno++
}

// Set the TTL
// TODO: Make this less hacky.
ttl, _ := checkCtxTTL(ctx)
opts := opts.ProcessPublishOptions(options)

// Create record
entry, err := ipns.Create(k, []byte(value), seqno, eol, ttl)
entry, err := ipns.Create(k, []byte(value), seqno, opts.EOL, opts.TTL)
if err != nil {
return nil, err
}
Expand All @@ -190,33 +193,6 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, valu
return entry, nil
}

// PublishWithEOL is a temporary stand in for the ipns records implementation
// see here for more details: https://github.com/ipfs/specs/tree/master/records
func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k crypto.PrivKey, value path.Path, eol time.Time) error {
ctx, span := StartSpan(ctx, "IpnsPublisher.PublishWithEOL", trace.WithAttributes(attribute.String("Value", value.String())))
defer span.End()

record, err := p.updateRecord(ctx, k, value, eol)
if err != nil {
return err
}

return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record)
}

// setting the TTL on published records is an experimental feature.
// as such, i'm using the context to wire it through to avoid changing too
// much code along the way.
func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
v := ctx.Value(ttlContextKey)
if v == nil {
return 0, false
}

d, ok := v.(time.Duration)
return d, ok
}

// PutRecordToRouting publishes the given entry using the provided ValueStore,
// keyed on the ID associated with the provided public key. The public key is
// also made available to the routing system so that entries can be verified.
Expand Down Expand Up @@ -307,13 +283,3 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec
func PkKeyForID(id peer.ID) string {
return "/pk/" + string(id)
}

// contextKey is a private comparable type used to hold value keys in contexts
type contextKey string

var ttlContextKey contextKey = "ipns-publish-ttl"

// ContextWithTTL returns a copy of the parent context with an added value representing the TTL
func ContextWithTTL(ctx context.Context, ttl time.Duration) context.Context {
return context.WithValue(context.Background(), ttlContextKey, ttl)
}
3 changes: 2 additions & 1 deletion republisher/repub.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/ipfs/go-ipns"
pb "github.com/ipfs/go-ipns/pb"
logging "github.com/ipfs/go-log"
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
"github.com/jbenet/goprocess"
gpctx "github.com/jbenet/goprocess/context"
ic "github.com/libp2p/go-libp2p/core/crypto"
Expand Down Expand Up @@ -161,7 +162,7 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro
if prevEol.After(eol) {
eol = prevEol
}
err = rp.ns.PublishWithEOL(ctx, priv, p, eol)
err = rp.ns.Publish(ctx, priv, p, opts.PublishWithEOL(eol))
span.RecordError(err)
return err
}
Expand Down
5 changes: 3 additions & 2 deletions republisher/repub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/ipfs/go-ipns"
ipns_pb "github.com/ipfs/go-ipns/pb"
"github.com/ipfs/go-path"
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"

keystore "github.com/ipfs/go-ipfs-keystore"
"github.com/ipfs/go-namesys"
Expand Down Expand Up @@ -103,7 +104,7 @@ func TestRepublish(t *testing.T) {
timeout := time.Second
for {
expiration = time.Now().Add(time.Second)
err := rp.PublishWithEOL(ctx, publisher.privKey, p, expiration)
err := rp.Publish(ctx, publisher.privKey, p, opts.PublishWithEOL(expiration))
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -179,7 +180,7 @@ func TestLongEOLRepublish(t *testing.T) {
name := "/ipns/" + publisher.id

expiration := time.Now().Add(time.Hour)
err := rp.PublishWithEOL(ctx, publisher.privKey, p, expiration)
err := rp.Publish(ctx, publisher.privKey, p, opts.PublishWithEOL(expiration))
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 6b648ff

Please sign in to comment.