@cryptotaxi247 / kubo / commits / 732101a6d

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()