Skip to content

fix(peerstore): replace stale addrs on newer signed peer record#3487

Open
lidel wants to merge 1 commit intomasterfrom
feat/pstore-replace-signed-addrs
Open

fix(peerstore): replace stale addrs on newer signed peer record#3487
lidel wants to merge 1 commit intomasterfrom
feat/pstore-replace-signed-addrs

Conversation

@lidel
Copy link
Copy Markdown
Member

@lidel lidel commented Apr 15, 2026

Note

This is another fix that aims to reduce stale addr accumulation (defensive, on Consumer side).

By looking at commented code, tt seems there was intention to do it in the past.

This PR

When we deal with signed peer records that have monotonic Seq, treat ConsumePeerRecord as replace-semantics rather than merge.

When a peer publishes a newer signed peer record, addrs that were present in the previously stored signed record but omitted from the new one are evicted, so the peerstore reflects the peer's current self-advertised set instead of the accumulated union. Unsigned addrs (e.g. DHT gossip or identify exchanges without a signed record) are untouched, and addrs held by a live connection (TTL >= ConnectedAddrTTL) are kept so active sessions are not torn down.

Both backends recover the prior addr set by decoding the stored envelope: pstoremem reuses the Envelope already kept on peerRecordState; pstoreds fetches it via GetPeerRecord and drops superseded addrs with deleteAddrs. Envelope.Record() caches on first access, so the diff is cheap. The shared CertifiedAddresses assertion is updated to match the new behavior.

@lidel lidel requested a review from MarcoPolo April 15, 2026 00:42
@lidel lidel changed the title feat(peerstore): replace stale addrs on newer signed peer record fix(peerstore): replace stale addrs on newer signed peer record Apr 15, 2026
Treat ConsumePeerRecord as replace-semantics rather than merge: when a
peer publishes a newer signed peer record, addrs that were present in
the previously stored signed record but omitted from the new one are
evicted, so the peerstore reflects the peer's current self-advertised
set instead of the accumulated union. Unsigned addrs (e.g. DHT gossip
or identify exchanges without a signed record) are untouched, and
addrs held by a live connection (TTL >= ConnectedAddrTTL) are kept so
active sessions are not torn down.

Both backends recover the prior addr set by decoding the stored
envelope: pstoremem reuses the Envelope already kept on
peerRecordState; pstoreds fetches it via GetPeerRecord and drops
superseded addrs with deleteAddrs. Envelope.Record() caches on first
access, so the diff is cheap. The shared CertifiedAddresses assertion
is updated to match the new behavior.
@lidel lidel force-pushed the feat/pstore-replace-signed-addrs branch from 7385b32 to b8b5f55 Compare April 15, 2026 00:50
Comment on lines -478 to -479
// Adding a new envelope should clear existing certified addresses.
// Only the newly-added ones should remain
Copy link
Copy Markdown
Member Author

@lidel lidel Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ intention was documented, but never implemented. this PR aims to implement it

this PR does not purge all addrs, only one matching previous signed record, but we could also pivot it to do full clean so only signed addrs remain

(not feeling strongly, whatever feels better)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replacing it makes sense. The full purge is a bigger riskier change I think

@lidel lidel marked this pull request as ready for review April 16, 2026 18:19
}
pr.RUnlock()

superseded := make([]ma.Multiaddr, 0, len(prevRec.Addrs))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very minor nit: if you return an iter.Seq you avoid this allocation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd lean toward keeping the slice here: caller gates on len(superseded) > 0 and deleteInPlace iterates the set per surviving entry, so iter.Seq would just push the alloc into deleteAddrs or force a signature change.. feels like not worth the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants