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 7, 2022
1 parent 1bf7d3d commit 6436cc9
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 150 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ require (
github.com/hashicorp/golang-lru v0.5.4
github.com/ipfs/go-cid v0.0.7
github.com/ipfs/go-datastore v0.5.0
github.com/ipfs/go-ipfs-ds-help v0.1.1
github.com/ipfs/go-ipfs-ds-help v1.1.0
github.com/ipfs/go-ipfs-keystore v0.0.2
github.com/ipfs/go-ipfs-routing v0.2.1
github.com/ipfs/go-ipns v0.1.2
github.com/ipfs/go-log v1.0.5
github.com/ipfs/go-path v0.0.9
github.com/ipfs/interface-go-ipfs-core v0.4.0
github.com/ipfs/go-path v0.1.1
github.com/ipfs/interface-go-ipfs-core v0.7.1-0.20221207140113-4f1c5845bf21
github.com/jbenet/goprocess v0.1.4
github.com/libp2p/go-libp2p v0.16.0
github.com/libp2p/go-libp2p-core v0.11.0
Expand Down
135 changes: 70 additions & 65 deletions go.sum

Large diffs are not rendered by default.

26 changes: 10 additions & 16 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,30 @@ That works well for many use cases, but doesn't allow you to answer
questions like "what is Alice's current homepage?". The mutable name
system allows Alice to publish information like:
The current homepage for alice.example.com is
/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
The current homepage for alice.example.com is
/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
or:
The current homepage for node
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
is
/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
The current homepage for node
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
is
/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
The mutable name system also allows users to resolve those references
to find the immutable IPFS object currently referenced by a given
mutable name.
For command-line bindings to this functionality, see:
ipfs name
ipfs dns
ipfs resolve
ipfs name
ipfs dns
ipfs resolve
*/
package namesys

import (
"errors"
"time"

context "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
}
25 changes: 13 additions & 12 deletions namesys.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import (
// (b) dns domains: resolves using links in DNS TXT records
//
// It can only publish to: (a) IPFS routing naming.
//
type mpns struct {
ds ds.Datastore

Expand Down Expand Up @@ -286,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 (
ipns "github.com/ipfs/go-ipns"
pb "github.com/ipfs/go-ipns/pb"
path "github.com/ipfs/go-path"
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
"github.com/libp2p/go-libp2p-core/crypto"
peer "github.com/libp2p/go-libp2p-core/peer"
routing "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 (
ipns "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"
goprocess "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"
path "github.com/ipfs/go-path"
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"

keystore "github.com/ipfs/go-ipfs-keystore"
namesys "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 6436cc9

Please sign in to comment.