@@ -282,7 +282,21 @@ func (ab *dsAddrBook) AddAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duratio
282282
283283// ConsumePeerRecord adds addresses from a signed peer.PeerRecord (contained in
284284// a record.Envelope), which will expire after the given TTL.
285- // See https://godoc.org/github.com/libp2p/go-libp2p/core/peerstore#CertifiedAddrBook for more details.
285+ // See https://godoc.org/github.com/libp2p/go-libp2p/core/peerstore#CertifiedAddrBook
286+ // for more details.
287+ //
288+ // The signed peer record's Seq is treated as monotonic per peer: a record
289+ // with a Seq lower than the last accepted one is rejected. Equal Seq is
290+ // accepted as a TTL refresh.
291+ //
292+ // When a newer signed record is accepted, addrs that were present in the
293+ // previously stored signed record but absent in the new one are evicted, so
294+ // the peerstore reflects the peer's current self-advertised set instead of
295+ // the union of every record we have ever seen. Unsigned addrs (added via
296+ // AddAddr / SetAddr from sources like DHT gossip, or from an identify
297+ // exchange where the peer did not send a signed record) are not touched, and
298+ // addrs held by a live connection (TTL >= ConnectedAddrTTL) are also kept so
299+ // active sessions are not dropped.
286300func (ab * dsAddrBook ) ConsumePeerRecord (recordEnvelope * record.Envelope , ttl time.Duration ) (bool , error ) {
287301 r , err := recordEnvelope .Record ()
288302 if err != nil {
@@ -303,6 +317,15 @@ func (ab *dsAddrBook) ConsumePeerRecord(recordEnvelope *record.Envelope, ttl tim
303317 }
304318
305319 addrs := cleanAddrs (rec .Addrs , rec .PeerID )
320+
321+ // Diff against the previously stored signed record so we can drop addrs
322+ // the peer no longer advertises before adding the new ones.
323+ if superseded := ab .supersededSignedAddrs (rec .PeerID , addrs ); len (superseded ) > 0 {
324+ if err := ab .deleteAddrs (rec .PeerID , superseded ); err != nil {
325+ return false , err
326+ }
327+ }
328+
306329 err = ab .setAddrs (rec .PeerID , addrs , ttl , ttlExtend , true )
307330 if err != nil {
308331 return false , err
@@ -315,6 +338,62 @@ func (ab *dsAddrBook) ConsumePeerRecord(recordEnvelope *record.Envelope, ttl tim
315338 return true , nil
316339}
317340
341+ // supersededSignedAddrs returns addrs that were present in the previously
342+ // stored signed peer record for p but are absent in newAddrs. Addrs held by
343+ // a live connection (TTL >= ConnectedAddrTTL) are excluded so an active
344+ // session is not torn down when the peer rotates its advertised set.
345+ func (ab * dsAddrBook ) supersededSignedAddrs (p peer.ID , newAddrs []ma.Multiaddr ) []ma.Multiaddr {
346+ prevEnv := ab .GetPeerRecord (p )
347+ if prevEnv == nil {
348+ return nil
349+ }
350+ prev , err := prevEnv .Record ()
351+ if err != nil {
352+ return nil
353+ }
354+ prevRec , ok := prev .(* peer.PeerRecord )
355+ if ! ok {
356+ return nil
357+ }
358+
359+ newSet := make (map [string ]struct {}, len (newAddrs ))
360+ for _ , a := range newAddrs {
361+ newSet [string (a .Bytes ())] = struct {}{}
362+ }
363+
364+ pr , err := ab .loadRecord (p , true , false )
365+ if err != nil {
366+ return nil
367+ }
368+ pr .RLock ()
369+ connected := make (map [string ]struct {})
370+ for _ , a := range pr .Addrs {
371+ if ttlIsConnected (time .Duration (a .Ttl )) {
372+ connected [string (a .Addr )] = struct {}{}
373+ }
374+ }
375+ pr .RUnlock ()
376+
377+ superseded := make ([]ma.Multiaddr , 0 , len (prevRec .Addrs ))
378+ for _ , a := range prevRec .Addrs {
379+ key := string (a .Bytes ())
380+ if _ , still := newSet [key ]; still {
381+ continue
382+ }
383+ if _ , isConn := connected [key ]; isConn {
384+ continue
385+ }
386+ superseded = append (superseded , a )
387+ }
388+ return superseded
389+ }
390+
391+ // ttlIsConnected reports whether the given TTL marks the address as held by
392+ // a live connection.
393+ func ttlIsConnected (ttl time.Duration ) bool {
394+ return ttl >= pstore .ConnectedAddrTTL
395+ }
396+
318397func (ab * dsAddrBook ) latestPeerRecordSeq (p peer.ID ) uint64 {
319398 pr , err := ab .loadRecord (p , true , false )
320399 if err != nil {
0 commit comments