namesys: fixed IPNS republisher to not overwrite IPNS record lifetimes
Adin Schmahmann committed
Aug 24, 2020 at 15:22 UTC
732101a6dfeae06ae5e803822cf874500d27b4bf
2 files changed
+129
-11
namesys/republisher/repub.go
+17
-7
@@ -11,6 +11,7 @@ import (
11
12
proto "github.com/gogo/protobuf/proto"
13
ds "github.com/ipfs/go-datastore"
14
+ ipns "github.com/ipfs/go-ipns"
15
pb "github.com/ipfs/go-ipns/pb"
16
logging "github.com/ipfs/go-log"
17
goprocess "github.com/jbenet/goprocess"
@@ -126,7 +127,7 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro
127
log.Debugf("republishing ipns entry for %s", id)
128
129
// Look for it locally only
129
- p, err := rp.getLastVal(id)
130
+ e, err := rp.getLastIPNSEntry(id)
131
if err != nil {
132
if err == errNoEntry {
133
return nil
@@ -134,25 +135,34 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro
135
return err
136
}
137
138
+ p := path.Path(e.GetValue())
139
+ prevEol, err := ipns.GetEOL(e)
140
+ if err != nil {
141
+ return err
142
+ }
143
+
144
// update record with same sequence number
145
eol := time.Now().Add(rp.RecordLifetime)
146
+ if prevEol.After(eol) {
147
+ eol = prevEol
148
+ }
149
return rp.ns.PublishWithEOL(ctx, priv, p, eol)
150
}
151
142
-func (rp *Republisher) getLastVal(id peer.ID) (path.Path, error) {
152
+func (rp *Republisher) getLastIPNSEntry(id peer.ID) (*pb.IpnsEntry, error) {
153
// Look for it locally only
154
val, err := rp.ds.Get(namesys.IpnsDsKey(id))
155
switch err {
156
case nil:
157
case ds.ErrNotFound:
148
- return "", errNoEntry
158
+ return nil, errNoEntry
159
default:
150
- return "", err
160
+ return nil, err
161
}
162
163
e := new(pb.IpnsEntry)
164
if err := proto.Unmarshal(val, e); err != nil {
155
- return "", err
165
+ return nil, err
166
}
157
- return path.Path(e.Value), nil
158
-}
167
+ return e, nil
168
+}
\ No newline at end of file
namesys/republisher/repub_test.go
+112
-4
@@ -6,16 +6,23 @@ import (
6
"testing"
7
"time"
8
9
+ "github.com/gogo/protobuf/proto"
10
+
11
+ goprocess "github.com/jbenet/goprocess"
12
+ peer "github.com/libp2p/go-libp2p-core/peer"
13
+ mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
14
+
15
+ ds "github.com/ipfs/go-datastore"
16
+ "github.com/ipfs/go-ipns"
17
+ "github.com/ipfs/go-ipns/pb"
18
+ path "github.com/ipfs/go-path"
19
+
20
"github.com/ipfs/go-ipfs/core"
21
"github.com/ipfs/go-ipfs/core/bootstrap"
22
mock "github.com/ipfs/go-ipfs/core/mock"
23
namesys "github.com/ipfs/go-ipfs/namesys"
24
. "github.com/ipfs/go-ipfs/namesys/republisher"
14
- path "github.com/ipfs/go-path"
25
16
- goprocess "github.com/jbenet/goprocess"
17
- peer "github.com/libp2p/go-libp2p-core/peer"
18
- mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
26
)
27
28
func TestRepublish(t *testing.T) {
@@ -109,6 +116,107 @@ func TestRepublish(t *testing.T) {
116
}
117
}
118
119
+func TestLongEOLRepublish(t *testing.T) {
120
+ // set cache life to zero for testing low-period repubs
121
+
122
+ ctx, cancel := context.WithCancel(context.Background())
123
+ defer cancel()
124
+
125
+ // create network
126
+ mn := mocknet.New(ctx)
127
+
128
+ var nodes []*core.IpfsNode
129
+ for i := 0; i < 10; i++ {
130
+ nd, err := mock.MockPublicNode(ctx, mn)
131
+ if err != nil {
132
+ t.Fatal(err)
133
+ }
134
+
135
+ nd.Namesys = namesys.NewNameSystem(nd.Routing, nd.Repo.Datastore(), 0)
136
+
137
+ nodes = append(nodes, nd)
138
+ }
139
+
140
+ if err := mn.LinkAll(); err != nil {
141
+ t.Fatal(err)
142
+ }
143
+
144
+ bsinf := bootstrap.BootstrapConfigWithPeers(
145
+ []peer.AddrInfo{
146
+ nodes[0].Peerstore.PeerInfo(nodes[0].Identity),
147
+ },
148
+ )
149
+
150
+ for _, n := range nodes[1:] {
151
+ if err := n.Bootstrap(bsinf); err != nil {
152
+ t.Fatal(err)
153
+ }
154
+ }
155
+
156
+ // have one node publish a record that is valid for 1 second
157
+ publisher := nodes[3]
158
+ p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid
159
+ rp := namesys.NewIpnsPublisher(publisher.Routing, publisher.Repo.Datastore())
160
+ name := "/ipns/" + publisher.Identity.Pretty()
161
+
162
+ expiration := time.Now().Add(time.Hour)
163
+ err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, expiration)
164
+ if err != nil {
165
+ t.Fatal(err)
166
+ }
167
+
168
+ err = verifyResolution(nodes, name, p)
169
+ if err != nil {
170
+ t.Fatal(err)
171
+ }
172
+
173
+ // The republishers that are contained within the nodes have their timeout set
174
+ // to 12 hours. Instead of trying to tweak those, we're just going to pretend
175
+ // they don't exist and make our own.
176
+ repub := NewRepublisher(rp, publisher.Repo.Datastore(), publisher.PrivateKey, publisher.Repo.Keystore())
177
+ repub.Interval = time.Millisecond * 500
178
+ repub.RecordLifetime = time.Second
179
+
180
+ proc := goprocess.Go(repub.Run)
181
+ defer proc.Close()
182
+
183
+ // now wait a couple seconds for it to fire a few times
184
+ time.Sleep(time.Second * 2)
185
+
186
+ err = verifyResolution(nodes, name, p)
187
+ if err != nil {
188
+ t.Fatal(err)
189
+ }
190
+
191
+ entry, err := getLastIPNSEntry(publisher.Repo.Datastore(), publisher.Identity)
192
+ if err != nil{
193
+ t.Fatal(err)
194
+ }
195
+
196
+ finalEol, err := ipns.GetEOL(entry)
197
+ if err != nil {
198
+ t.Fatal(err)
199
+ }
200
+
201
+ if !finalEol.Equal(expiration) {
202
+ t.Fatal("expiration time modified")
203
+ }
204
+}
205
+
206
+func getLastIPNSEntry(dstore ds.Datastore, id peer.ID) (*ipns_pb.IpnsEntry, error) {
207
+ // Look for it locally only
208
+ val, err := dstore.Get(namesys.IpnsDsKey(id))
209
+ if err != nil {
210
+ return nil, err
211
+ }
212
+
213
+ e := new(ipns_pb.IpnsEntry)
214
+ if err := proto.Unmarshal(val, e); err != nil {
215
+ return nil, err
216
+ }
217
+ return e, nil
218
+}
219
+
220
func verifyResolution(nodes []*core.IpfsNode, key string, exp path.Path) error {
221
ctx, cancel := context.WithCancel(context.Background())
222
defer cancel()