feat: live mesh peer gossip
When a peer's hello is verified, send a gossip burst of currently connected peers so the new peer can discover and connect to nodes it hasn't met yet — without relying on the anchor for a full peer-join notification for each one. - mesh: add PendingConnect chan for gossip-discovered peer IDs - peer: send gossip burst after hello; push unknown peers from incoming gossip onto PendingConnect (skip self + already-connected) - anchor: drain PendingConnect per runOnce, initiate startOffer for unknown peers using the same lexicographic tiebreak as anchor join; drain goroutine is scoped to the runOnce lifetime to avoid using stale sessions after a reconnect Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -150,6 +150,8 @@ func handleDCMessage(data []byte, from proto.PeerID, localID *crypto.Identity, m
|
||||
PeerID: peerIDPtr(from),
|
||||
Nick: hello.Nick,
|
||||
})
|
||||
// Tell the new peer about everyone we can currently see.
|
||||
go m.sendGossipTo(from)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -224,9 +226,28 @@ func dispatchPeerMessage(msg proto.PeerMessage, from proto.PeerID, m *Mesh) {
|
||||
log.Printf("mesh: file-done from %s xid=%s", from.Short(), msg.Xid)
|
||||
|
||||
case proto.MsgPeerGossip:
|
||||
if msg.Gossip != nil {
|
||||
log.Printf("mesh: gossip from %s: %d peer hints", from.Short(), len(msg.Gossip.Peers))
|
||||
if msg.Gossip == nil {
|
||||
return
|
||||
}
|
||||
// Build set of peers we already know about (connected + self).
|
||||
connected := m.ConnectedPeers()
|
||||
known := make(map[proto.PeerID]bool, len(connected)+1)
|
||||
known[m.Identity.PeerID()] = true
|
||||
for _, p := range connected {
|
||||
known[p.ID] = true
|
||||
}
|
||||
newPeers := 0
|
||||
for _, entry := range msg.Gossip.Peers {
|
||||
if known[entry.Peer.ID] {
|
||||
continue
|
||||
}
|
||||
select {
|
||||
case m.PendingConnect <- entry.Peer.ID:
|
||||
newPeers++
|
||||
default:
|
||||
}
|
||||
}
|
||||
log.Printf("mesh: gossip from %s: %d hints, %d new", from.Short(), len(msg.Gossip.Peers), newPeers)
|
||||
case proto.MsgPing:
|
||||
log.Printf("mesh: ping from %s", from.Short())
|
||||
case proto.MsgPong:
|
||||
@@ -272,4 +293,29 @@ func fingerprintFromSDP(sdp string) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendGossipTo sends the current live peer list to peerID so they can discover
|
||||
// and connect to peers they haven't met yet. Called after hello is verified.
|
||||
func (m *Mesh) sendGossipTo(peerID proto.PeerID) {
|
||||
peers := m.ConnectedPeers()
|
||||
entries := make([]proto.GossipEntry, 0, len(peers))
|
||||
for _, p := range peers {
|
||||
if p.ID == peerID {
|
||||
continue // don't include the recipient in their own gossip
|
||||
}
|
||||
entries = append(entries, proto.GossipEntry{Peer: p})
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
return
|
||||
}
|
||||
wire, err := json.Marshal(proto.PeerMessage{
|
||||
Type: proto.MsgPeerGossip,
|
||||
Gossip: &proto.PeerGossip{Peers: entries},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m.SendTo(peerID, wire)
|
||||
log.Printf("mesh: sent gossip to %s: %d peer(s)", peerID.Short(), len(entries))
|
||||
}
|
||||
|
||||
func peerIDPtr(p proto.PeerID) *proto.PeerID { return &p }
|
||||
|
||||
Reference in New Issue
Block a user