Skip to content

Commit

Permalink
Merge pull request #1775 from ipfs/fix/ipns-old-record
Browse files Browse the repository at this point in the history
fix publish fail on prexisting bad record
  • Loading branch information
jbenet committed Oct 2, 2015
2 parents 5b2d2eb + b34d41e commit 7e69146
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 61 deletions.
4 changes: 2 additions & 2 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost
n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

// setup name system
n.Namesys = namesys.NewNameSystem(n.Routing)
n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore())

// setup ipns republishing
err = n.setupIpnsRepublisher()
Expand Down Expand Up @@ -456,7 +456,7 @@ func (n *IpfsNode) SetupOfflineRouting() error {

n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)

n.Namesys = namesys.NewNameSystem(n.Routing)
n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore())

return nil
}
Expand Down
2 changes: 1 addition & 1 deletion fuse/ipns/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error {
return err
}

pub := nsys.NewRoutingPublisher(n.Routing)
pub := nsys.NewRoutingPublisher(n.Routing, n.Repo.Datastore())
if err := pub.Publish(ctx, key, path.FromKey(nodek)); err != nil {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions namesys/namesys.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"strings"
"time"

ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
ci "github.com/ipfs/go-ipfs/p2p/crypto"
path "github.com/ipfs/go-ipfs/path"
Expand All @@ -25,15 +26,15 @@ type mpns struct {
}

// NewNameSystem will construct the IPFS naming system based on Routing
func NewNameSystem(r routing.IpfsRouting) NameSystem {
func NewNameSystem(r routing.IpfsRouting, ds ds.Datastore) NameSystem {
return &mpns{
resolvers: map[string]resolver{
"dns": newDNSResolver(),
"proquint": new(ProquintResolver),
"dht": newRoutingResolver(r),
},
publishers: map[string]Publisher{
"/ipns/": NewRoutingPublisher(r),
"/ipns/": NewRoutingPublisher(r, ds),
},
}
}
Expand Down
65 changes: 53 additions & 12 deletions namesys/publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
path "github.com/ipfs/go-ipfs/path"
pin "github.com/ipfs/go-ipfs/pin"
routing "github.com/ipfs/go-ipfs/routing"
dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb"
record "github.com/ipfs/go-ipfs/routing/record"
ft "github.com/ipfs/go-ipfs/unixfs"
u "github.com/ipfs/go-ipfs/util"
Expand All @@ -37,11 +38,15 @@ var PublishPutValTimeout = time.Minute
// routing system.
type ipnsPublisher struct {
routing routing.IpfsRouting
ds ds.Datastore
}

// NewRoutingPublisher constructs a publisher for the IPFS Routing name system.
func NewRoutingPublisher(route routing.IpfsRouting) *ipnsPublisher {
return &ipnsPublisher{routing: route}
func NewRoutingPublisher(route routing.IpfsRouting, ds ds.Datastore) *ipnsPublisher {
if ds == nil {
panic("nil datastore")
}
return &ipnsPublisher{routing: route, ds: ds}
}

// Publish implements Publisher. Accepts a keypair and a value,
Expand All @@ -62,22 +67,58 @@ func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value

_, ipnskey := IpnsKeysForID(id)

// get previous records sequence number, and add one to it
var seqnum uint64
prevrec, err := p.routing.GetValues(ctx, ipnskey, 0)
// get previous records sequence number
seqnum, err := p.getPreviousSeqNo(ctx, ipnskey)
if err != nil {
return err
}

// increment it
seqnum++

return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id)
}

func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey key.Key) (uint64, error) {
prevrec, err := p.ds.Get(ipnskey.DsKey())
if err != nil && err != ds.ErrNotFound {
// None found, lets start at zero!
return 0, err
}
var val []byte
if err == nil {
e := new(pb.IpnsEntry)
err := proto.Unmarshal(prevrec[0].Val, e)
prbytes, ok := prevrec.([]byte)
if !ok {
return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec)
}
dhtrec := new(dhtpb.Record)
err := proto.Unmarshal(prbytes, dhtrec)
if err != nil {
return err
return 0, err
}

seqnum = e.GetSequence() + 1
} else if err != ds.ErrNotFound {
return err
val = dhtrec.GetValue()
} else {
// try and check the dht for a record
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()

rv, err := p.routing.GetValue(ctx, ipnskey)
if err != nil {
// no such record found, start at zero!
return 0, nil
}

val = rv
}

return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id)
e := new(pb.IpnsEntry)
err = proto.Unmarshal(val, e)
if err != nil {
return 0, err
}

return e.GetSequence(), nil
}

func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error {
Expand Down
2 changes: 1 addition & 1 deletion namesys/republisher/repub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func TestRepublish(t *testing.T) {
// have one node publish a record that is valid for 1 second
publisher := nodes[3]
p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid
rp := namesys.NewRoutingPublisher(publisher.Routing)
rp := namesys.NewRoutingPublisher(publisher.Routing, publisher.Repo.Datastore())
err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second))
if err != nil {
t.Fatal(err)
Expand Down
94 changes: 93 additions & 1 deletion namesys/resolve_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package namesys

import (
"errors"
"testing"
"time"

ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
key "github.com/ipfs/go-ipfs/blocks/key"
peer "github.com/ipfs/go-ipfs/p2p/peer"
path "github.com/ipfs/go-ipfs/path"
mockrouting "github.com/ipfs/go-ipfs/routing/mock"
u "github.com/ipfs/go-ipfs/util"
Expand All @@ -13,9 +17,10 @@ import (

func TestRoutingResolve(t *testing.T) {
d := mockrouting.NewServer().Client(testutil.RandIdentityOrFatal(t))
dstore := ds.NewMapDatastore()

resolver := NewRoutingResolver(d)
publisher := NewRoutingPublisher(d)
publisher := NewRoutingPublisher(d, dstore)

privk, pubk, err := testutil.RandTestKeyPair(512)
if err != nil {
Expand Down Expand Up @@ -43,3 +48,90 @@ func TestRoutingResolve(t *testing.T) {
t.Fatal("Got back incorrect value.")
}
}

func TestPrexistingExpiredRecord(t *testing.T) {
dstore := ds.NewMapDatastore()
d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore)

resolver := NewRoutingResolver(d)
publisher := NewRoutingPublisher(d, dstore)

privk, pubk, err := testutil.RandTestKeyPair(512)
if err != nil {
t.Fatal(err)
}

id, err := peer.IDFromPublicKey(pubk)
if err != nil {
t.Fatal(err)
}

// Make an expired record and put it in the datastore
h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN")
eol := time.Now().Add(time.Hour * -1)
err = PutRecordToRouting(context.Background(), privk, h, 0, eol, d, id)
if err != nil {
t.Fatal(err)
}

// Now, with an old record in the system already, try and publish a new one
err = publisher.Publish(context.Background(), privk, h)
if err != nil {
t.Fatal(err)
}

err = verifyCanResolve(resolver, id.Pretty(), h)
if err != nil {
t.Fatal(err)
}
}

func TestPrexistingRecord(t *testing.T) {
dstore := ds.NewMapDatastore()
d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore)

resolver := NewRoutingResolver(d)
publisher := NewRoutingPublisher(d, dstore)

privk, pubk, err := testutil.RandTestKeyPair(512)
if err != nil {
t.Fatal(err)
}

id, err := peer.IDFromPublicKey(pubk)
if err != nil {
t.Fatal(err)
}

// Make a good record and put it in the datastore
h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN")
eol := time.Now().Add(time.Hour)
err = PutRecordToRouting(context.Background(), privk, h, 0, eol, d, id)
if err != nil {
t.Fatal(err)
}

// Now, with an old record in the system already, try and publish a new one
err = publisher.Publish(context.Background(), privk, h)
if err != nil {
t.Fatal(err)
}

err = verifyCanResolve(resolver, id.Pretty(), h)
if err != nil {
t.Fatal(err)
}
}

func verifyCanResolve(r Resolver, name string, exp path.Path) error {
res, err := r.Resolve(context.Background(), name)
if err != nil {
return err
}

if res != exp {
return errors.New("got back wrong record!")
}

return nil
}
Loading

0 comments on commit 7e69146

Please sign in to comment.