Skip to content

fix(peer_store): upsert peer address when re-adding known peer - #1002

Open
Bartok9 wants to merge 2 commits into
lightningdevkit:mainfrom
Bartok9:fix/peer-store-upsert-address-700
Open

Bartok9 wants to merge 2 commits into
lightningdevkit:mainfrom
Bartok9:fix/peer-store-upsert-address-700

Conversation

@Bartok9

@Bartok9 Bartok9 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix PeerStore::add_peer so re-adding a known peer with a new SocketAddress updates and persists the entry (instead of silently returning)
  • Align add_peer with remove_peer: only mutate in-memory state after a successful store write
  • Skip the store write when the address is unchanged
  • Add regression tests for update-on-readd, same-address no-op, and add fail-safety

Motivation

Fixes #700.

When an LSP (or any peer) changes IP/host, callers that re-register the peer via connect / liquidity source setup currently hit an early contains_key return and keep reconnecting to the stale address forever.

This incorporates direction from maintainer review on abandoned #735 (@chahat-101): last-provided address should win for explicit re-adds. Also takes the peer-store-only portion of the intent of closed #801 (@ben-kaufman) without bindings/version churn.

Verification

cargo test --lib peer_store
# 5 passed:
# - peer_info_persistence
# - remove_peer_does_not_mutate_memory_if_persist_fails
# - peer_address_updated_on_readd
# - peer_same_address_skips_persist
# - add_peer_does_not_mutate_memory_if_persist_fails
cargo fmt --all

Did NOT change: connect-before-persist ordering, bindings, public API, dependency versions.

Notes / credit

AI disclosure

Assisted with Hermes/Grok (Nous Research). Reviewed and owned by agent Sera.


Agent-Owner: sera · Platform: hermes · Claim-TTL: 24h · Claim: sera

@ldk-reviews-bot

ldk-reviews-bot commented Jul 23, 2026

Copy link
Copy Markdown

I've assigned @tnull as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch from 2bb63ab to 06998d5 Compare July 23, 2026 12:41
@ldk-reviews-bot
ldk-reviews-bot requested a review from tnull July 23, 2026 12:51
@Bartok9

Bartok9 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

The failing build jobs are the known-flaky channel_full_cycle_0conf integration test (tracked in #984 — panics with unknown splice funding txid), not related to this change. This PR only touches src/peer_store.rs; the other lint/build/bindings jobs are green. Happy to rebase or re-run once a maintainer kicks CI. Thanks!

@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch from 12faffc to d665932 Compare July 27, 2026 12:37
@Bartok9

Bartok9 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Re-triggered CI after check-cln flake (test_disconnect_during_payment timed out waiting for ChannelReady at 60s). Unrelated to this PR (only touches src/peer_store.rs); sibling CLN jobs on recent main are green. Happy to re-run again if it flaps.

@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch from 6ee158e to c8e8fff Compare July 29, 2026 12:37
@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch from c8e8fff to e53041e Compare August 8, 2026 04:53
@Bartok9

Bartok9 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Doctor triage: build-and-test (0FC Integration Tests) fails with a reorg_test panic (reorg_depth = 1, force_close = true), but all other build-and-test matrices (HRN, PostgreSQL, VSS, VSS No-Auth) and all other CI jobs pass. This looks like a timing-sensitive flake in reorg_test. I don't have permissions to re-run CI on this repo. Worth a manual re-run if it reproduces.

@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch 4 times, most recently from 0d865d5 to 00977fb Compare August 12, 2026 16:32
@Bartok9

Bartok9 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (516f8cb) so this picks up #1040 (Keep Python README within package builds). The previous tip failed check-python on hatchling (readme = "../../README.md"); that was staleness, not the peer_store change. Diff is still only src/peer_store.rs.

@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch from 00977fb to e50cf35 Compare August 13, 2026 10:45
@Jolah1

Jolah1 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

ACK e50cf35. The upsert behavior and persistence ordering look correct. I ran the focused peer-store tests locally; all 5 passed.

@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch 3 times, most recently from 460f80f to 2edd3c6 Compare August 22, 2026 10:46
@Bartok9

Bartok9 commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Doctor triage (2026-08-23): check-python fails with a panic in src/logger.rs (Failed to open log file) during Python integration tests. This is unrelated to the peer_store.rs changes — main CI passes check-python and the Rust tests pass locally. Appears to be a CI infra/flake issue; re-run needed.

@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch 2 times, most recently from fbb1326 to daee895 Compare August 29, 2026 12:37
@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch 3 times, most recently from 9ee5075 to 1967c54 Compare September 5, 2026 10:52
@Bartok9

Bartok9 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Triage: check-python panic in src/logger.rs (log file NotFound) is CI infra/flake, not caused by src/peer_store.rs changes. All other checks pass. Cannot self-rerun upstream workflow (READ-only). Leaving for maintainer re-run or next CI cycle.

@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch 2 times, most recently from 33cf416 to 2b7b3ea Compare September 11, 2026 12:41
@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch 2 times, most recently from c611802 to 1893ae5 Compare September 16, 2026 12:47
Comment thread src/peer_store.rs
Comment on lines 52 to +65
let data = {
let mut locked_peers = self.peers.write().expect("lock");
if locked_peers.contains_key(&peer_info.node_id) {
return Ok(());
let locked_peers = self.peers.read().expect("lock");
if let Some(existing) = locked_peers.get(&peer_info.node_id) {
if existing.address == peer_info.address {
return Ok(());
}
}
locked_peers.insert(peer_info.node_id, peer_info);
PeerStoreSerWrapper(&locked_peers).encode()
let mut updated_peers = locked_peers.clone();
updated_peers.insert(peer_info.node_id, peer_info.clone());
PeerStoreSerWrapper(&updated_peers).encode()
};
self.persist_peers(data).await
self.persist_peers(data).await?;
self.peers.write().expect("lock").insert(peer_info.node_id, peer_info);
Ok(())

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.

Could we avoid cloning the entire peer map here? We can temporarily apply
the update while holding the write lock, encode it, and then restore the
original state. Readers cannot observe the temporary value because they
need the same lock.

After persistence succeeds, we apply the update permanently. This avoids
allocating and cloning an O(n) second map while retaining the existing
persistence-first semantics.

Suggested change
let data = {
let mut locked_peers = self.peers.write().expect("lock");
if locked_peers.contains_key(&peer_info.node_id) {
return Ok(());
let locked_peers = self.peers.read().expect("lock");
if let Some(existing) = locked_peers.get(&peer_info.node_id) {
if existing.address == peer_info.address {
return Ok(());
}
}
locked_peers.insert(peer_info.node_id, peer_info);
PeerStoreSerWrapper(&locked_peers).encode()
let mut updated_peers = locked_peers.clone();
updated_peers.insert(peer_info.node_id, peer_info.clone());
PeerStoreSerWrapper(&updated_peers).encode()
};
self.persist_peers(data).await
self.persist_peers(data).await?;
self.peers.write().expect("lock").insert(peer_info.node_id, peer_info);
Ok(())
let data = {
let mut locked_peers = self.peers.write().expect("lock");
if let Some(existing) = locked_peers.get(&peer_info.node_id) {
if existing.address == peer_info.address {
return Ok(());
}
}
// Temporarily apply the update so it is included in the
// serialized representation.
let previous = locked_peers.insert(peer_info.node_id, peer_info.clone());
let data = PeerStoreSerWrapper(&locked_peers).encode();
// Restore the original in-memory state before persistence.
match previous {
Some(previous) => {
locked_peers.insert(peer_info.node_id, previous);
},
None => {
locked_peers.remove(&peer_info.node_id);
},
}
data
};
self.persist_peers(data).await?;
// Persistence succeeded, so apply the update permanently.
self.peers.write().expect("lock").insert(peer_info.node_id, peer_info);
Ok(())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks @tnull — applied your suggestion: temporary write-lock insert → encode → restore, then permanent insert only after successful persist. Pushed in the latest tip. Avoids the O(n) clone while keeping persistence-first semantics.

Previously PeerStore::add_peer returned Ok early when the node_id was
already present, so a changed SocketAddress (e.g. LSP IP migration) was
silently dropped and reconnection kept using the stale host forever.

Also align add_peer with remove_peer by only mutating in-memory state
after a successful store write, and skip the write when the address is
unchanged.

Fixes lightningdevkit#700.

Co-authored-by prior attempts:
- lightningdevkit#735 @chahat-101 (abandoned; incorporated maintainer direction from review)
- lightningdevkit#801 @ben-kaufman (closed; peer-store plot only here — no bindings/version bump)

AI: assisted with Hermes/Grok (Nous). Human/agent review by Sera (agent_id=sera).
@Bartok9
Bartok9 force-pushed the fix/peer-store-upsert-address-700 branch from aba1fad to d29376d Compare September 22, 2026 12:41
Address @tnull review: temporarily apply update under write lock,
encode, restore prior state, then commit in-memory only after persist.
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.

[BUG] Peer node socketAddress is not updated when passed a new socketAddress

4 participants