Skip to content

Commit 7e69146

Browse files
committed
Merge pull request #1775 from ipfs/fix/ipns-old-record
fix publish fail on prexisting bad record
2 parents 5b2d2eb + b34d41e commit 7e69146

File tree

12 files changed

+275
-61
lines changed

12 files changed

+275
-61
lines changed

core/core.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost
227227
n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)
228228

229229
// setup name system
230-
n.Namesys = namesys.NewNameSystem(n.Routing)
230+
n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore())
231231

232232
// setup ipns republishing
233233
err = n.setupIpnsRepublisher()
@@ -456,7 +456,7 @@ func (n *IpfsNode) SetupOfflineRouting() error {
456456

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

459-
n.Namesys = namesys.NewNameSystem(n.Routing)
459+
n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore())
460460

461461
return nil
462462
}

fuse/ipns/common.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error {
3333
return err
3434
}
3535

36-
pub := nsys.NewRoutingPublisher(n.Routing)
36+
pub := nsys.NewRoutingPublisher(n.Routing, n.Repo.Datastore())
3737
if err := pub.Publish(ctx, key, path.FromKey(nodek)); err != nil {
3838
return err
3939
}

namesys/namesys.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"strings"
55
"time"
66

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

2728
// NewNameSystem will construct the IPFS naming system based on Routing
28-
func NewNameSystem(r routing.IpfsRouting) NameSystem {
29+
func NewNameSystem(r routing.IpfsRouting, ds ds.Datastore) NameSystem {
2930
return &mpns{
3031
resolvers: map[string]resolver{
3132
"dns": newDNSResolver(),
3233
"proquint": new(ProquintResolver),
3334
"dht": newRoutingResolver(r),
3435
},
3536
publishers: map[string]Publisher{
36-
"/ipns/": NewRoutingPublisher(r),
37+
"/ipns/": NewRoutingPublisher(r, ds),
3738
},
3839
}
3940
}

namesys/publisher.go

+53-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
path "github.com/ipfs/go-ipfs/path"
1919
pin "github.com/ipfs/go-ipfs/pin"
2020
routing "github.com/ipfs/go-ipfs/routing"
21+
dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb"
2122
record "github.com/ipfs/go-ipfs/routing/record"
2223
ft "github.com/ipfs/go-ipfs/unixfs"
2324
u "github.com/ipfs/go-ipfs/util"
@@ -37,11 +38,15 @@ var PublishPutValTimeout = time.Minute
3738
// routing system.
3839
type ipnsPublisher struct {
3940
routing routing.IpfsRouting
41+
ds ds.Datastore
4042
}
4143

4244
// NewRoutingPublisher constructs a publisher for the IPFS Routing name system.
43-
func NewRoutingPublisher(route routing.IpfsRouting) *ipnsPublisher {
44-
return &ipnsPublisher{routing: route}
45+
func NewRoutingPublisher(route routing.IpfsRouting, ds ds.Datastore) *ipnsPublisher {
46+
if ds == nil {
47+
panic("nil datastore")
48+
}
49+
return &ipnsPublisher{routing: route, ds: ds}
4550
}
4651

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

6368
_, ipnskey := IpnsKeysForID(id)
6469

65-
// get previous records sequence number, and add one to it
66-
var seqnum uint64
67-
prevrec, err := p.routing.GetValues(ctx, ipnskey, 0)
70+
// get previous records sequence number
71+
seqnum, err := p.getPreviousSeqNo(ctx, ipnskey)
72+
if err != nil {
73+
return err
74+
}
75+
76+
// increment it
77+
seqnum++
78+
79+
return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id)
80+
}
81+
82+
func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey key.Key) (uint64, error) {
83+
prevrec, err := p.ds.Get(ipnskey.DsKey())
84+
if err != nil && err != ds.ErrNotFound {
85+
// None found, lets start at zero!
86+
return 0, err
87+
}
88+
var val []byte
6889
if err == nil {
69-
e := new(pb.IpnsEntry)
70-
err := proto.Unmarshal(prevrec[0].Val, e)
90+
prbytes, ok := prevrec.([]byte)
91+
if !ok {
92+
return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec)
93+
}
94+
dhtrec := new(dhtpb.Record)
95+
err := proto.Unmarshal(prbytes, dhtrec)
7196
if err != nil {
72-
return err
97+
return 0, err
7398
}
7499

75-
seqnum = e.GetSequence() + 1
76-
} else if err != ds.ErrNotFound {
77-
return err
100+
val = dhtrec.GetValue()
101+
} else {
102+
// try and check the dht for a record
103+
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
104+
defer cancel()
105+
106+
rv, err := p.routing.GetValue(ctx, ipnskey)
107+
if err != nil {
108+
// no such record found, start at zero!
109+
return 0, nil
110+
}
111+
112+
val = rv
78113
}
79114

80-
return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id)
115+
e := new(pb.IpnsEntry)
116+
err = proto.Unmarshal(val, e)
117+
if err != nil {
118+
return 0, err
119+
}
120+
121+
return e.GetSequence(), nil
81122
}
82123

83124
func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error {

namesys/republisher/repub_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func TestRepublish(t *testing.T) {
5454
// have one node publish a record that is valid for 1 second
5555
publisher := nodes[3]
5656
p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid
57-
rp := namesys.NewRoutingPublisher(publisher.Routing)
57+
rp := namesys.NewRoutingPublisher(publisher.Routing, publisher.Repo.Datastore())
5858
err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second))
5959
if err != nil {
6060
t.Fatal(err)

namesys/resolve_test.go

+93-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package namesys
22

33
import (
4+
"errors"
45
"testing"
6+
"time"
57

8+
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
69
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
710
key "github.com/ipfs/go-ipfs/blocks/key"
11+
peer "github.com/ipfs/go-ipfs/p2p/peer"
812
path "github.com/ipfs/go-ipfs/path"
913
mockrouting "github.com/ipfs/go-ipfs/routing/mock"
1014
u "github.com/ipfs/go-ipfs/util"
@@ -13,9 +17,10 @@ import (
1317

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

1722
resolver := NewRoutingResolver(d)
18-
publisher := NewRoutingPublisher(d)
23+
publisher := NewRoutingPublisher(d, dstore)
1924

2025
privk, pubk, err := testutil.RandTestKeyPair(512)
2126
if err != nil {
@@ -43,3 +48,90 @@ func TestRoutingResolve(t *testing.T) {
4348
t.Fatal("Got back incorrect value.")
4449
}
4550
}
51+
52+
func TestPrexistingExpiredRecord(t *testing.T) {
53+
dstore := ds.NewMapDatastore()
54+
d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore)
55+
56+
resolver := NewRoutingResolver(d)
57+
publisher := NewRoutingPublisher(d, dstore)
58+
59+
privk, pubk, err := testutil.RandTestKeyPair(512)
60+
if err != nil {
61+
t.Fatal(err)
62+
}
63+
64+
id, err := peer.IDFromPublicKey(pubk)
65+
if err != nil {
66+
t.Fatal(err)
67+
}
68+
69+
// Make an expired record and put it in the datastore
70+
h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN")
71+
eol := time.Now().Add(time.Hour * -1)
72+
err = PutRecordToRouting(context.Background(), privk, h, 0, eol, d, id)
73+
if err != nil {
74+
t.Fatal(err)
75+
}
76+
77+
// Now, with an old record in the system already, try and publish a new one
78+
err = publisher.Publish(context.Background(), privk, h)
79+
if err != nil {
80+
t.Fatal(err)
81+
}
82+
83+
err = verifyCanResolve(resolver, id.Pretty(), h)
84+
if err != nil {
85+
t.Fatal(err)
86+
}
87+
}
88+
89+
func TestPrexistingRecord(t *testing.T) {
90+
dstore := ds.NewMapDatastore()
91+
d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore)
92+
93+
resolver := NewRoutingResolver(d)
94+
publisher := NewRoutingPublisher(d, dstore)
95+
96+
privk, pubk, err := testutil.RandTestKeyPair(512)
97+
if err != nil {
98+
t.Fatal(err)
99+
}
100+
101+
id, err := peer.IDFromPublicKey(pubk)
102+
if err != nil {
103+
t.Fatal(err)
104+
}
105+
106+
// Make a good record and put it in the datastore
107+
h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN")
108+
eol := time.Now().Add(time.Hour)
109+
err = PutRecordToRouting(context.Background(), privk, h, 0, eol, d, id)
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
114+
// Now, with an old record in the system already, try and publish a new one
115+
err = publisher.Publish(context.Background(), privk, h)
116+
if err != nil {
117+
t.Fatal(err)
118+
}
119+
120+
err = verifyCanResolve(resolver, id.Pretty(), h)
121+
if err != nil {
122+
t.Fatal(err)
123+
}
124+
}
125+
126+
func verifyCanResolve(r Resolver, name string, exp path.Path) error {
127+
res, err := r.Resolve(context.Background(), name)
128+
if err != nil {
129+
return err
130+
}
131+
132+
if res != exp {
133+
return errors.New("got back wrong record!")
134+
}
135+
136+
return nil
137+
}

0 commit comments

Comments
 (0)