Discovery Neo Oclib
This commit is contained in:
@@ -39,6 +39,8 @@ type PeerRecordPayload struct {
|
||||
PubKey []byte `json:"public_key"`
|
||||
ExpiryDate time.Time `json:"expiry_date"`
|
||||
IsNano bool `json:"is_nano"`
|
||||
// MasterID is the libp2p PeerID of this peer's MASTER, self-attested and signed.
|
||||
MasterID string `json:"master_id,omitempty"`
|
||||
// TTLSeconds is the publisher's declared lifetime for this record in seconds.
|
||||
// 0 means "use the default (120 s)". Included in the signed payload so it
|
||||
// cannot be altered by an intermediary.
|
||||
@@ -105,6 +107,7 @@ func (pr *PeerRecord) ExtractPeer(ourkey string, key string, pubKey crypto.PubKe
|
||||
NATSAddress: pr.NATSAddress,
|
||||
WalletAddress: pr.WalletAddress,
|
||||
Location: pr.Location,
|
||||
MasterID: pr.MasterID,
|
||||
}
|
||||
if time.Now().UTC().After(pr.ExpiryDate) {
|
||||
return pp.SELF == p.Relation, nil, errors.New("peer " + key + " is offline")
|
||||
@@ -285,6 +288,20 @@ func (ix *IndexerService) initNodeHandler() {
|
||||
}
|
||||
cancel2()
|
||||
}
|
||||
// PendingContact: update inverted index — for each target peer in the list,
|
||||
// record that hb.PeerID wants to contact it. Entries expire after 3 heartbeat
|
||||
// intervals so stale callers are cleaned up automatically if they stop advertising.
|
||||
if len(hb.PendingContact) > 0 {
|
||||
expiry := time.Now().Add(3 * 20 * time.Second)
|
||||
ix.pendingContactIndexMu.Lock()
|
||||
for _, targetID := range hb.PendingContact {
|
||||
if ix.pendingContactIndex[targetID] == nil {
|
||||
ix.pendingContactIndex[targetID] = map[string]time.Time{}
|
||||
}
|
||||
ix.pendingContactIndex[targetID][hb.PeerID] = expiry
|
||||
}
|
||||
ix.pendingContactIndexMu.Unlock()
|
||||
}
|
||||
}
|
||||
ix.Host.SetStreamHandler(common.ProtocolHeartbeat, ix.HandleHeartbeat)
|
||||
ix.Host.SetStreamHandler(common.ProtocolPublish, ix.handleNodePublish)
|
||||
@@ -351,7 +368,8 @@ func (ix *IndexerService) handleNodePublish(s network.Stream) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, err := rec.Verify(); err != nil {
|
||||
pubKey, err := rec.Verify()
|
||||
if err != nil {
|
||||
ix.behavior.RecordBadSignature(remotePeer)
|
||||
logger.Warn().Err(err).Str("peer", remotePeer.String()).Msg("bad signature on publish")
|
||||
return
|
||||
@@ -369,6 +387,26 @@ func (ix *IndexerService) handleNodePublish(s network.Stream) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Chain of trust: PubKey → PeerID (libp2p invariant), then transport identity.
|
||||
// This prevents a peer from publishing a record on behalf of someone else.
|
||||
if derivedID, err := lpp.IDFromPublicKey(pubKey); err != nil || derivedID != pid {
|
||||
ix.behavior.RecordBadSignature(remotePeer)
|
||||
logger.Warn().Str("peer", remotePeer.String()).Msg("PubKey/PeerID mismatch on publish")
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
if remotePeer != pid {
|
||||
ix.behavior.RecordBadSignature(remotePeer)
|
||||
logger.Warn().Str("remote", remotePeer.String()).Str("claimed", pid.String()).Msg("transport identity mismatch on publish")
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
if rec.StreamAddress != "" && !strings.HasSuffix(rec.StreamAddress, "/p2p/"+rec.PeerID) {
|
||||
ix.behavior.RecordBadSignature(remotePeer)
|
||||
logger.Warn().Str("peer", remotePeer.String()).Msg("StreamAddress/PeerID mismatch on publish")
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
ix.StreamMU.Lock()
|
||||
defer ix.StreamMU.Unlock()
|
||||
@@ -566,7 +604,7 @@ func (ix *IndexerService) handleIndirectProbe(s network.Stream) {
|
||||
// Connect to target if not already connected.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
|
||||
defer cancel()
|
||||
if ix.Host.Network().Connectedness(req.Target.ID) != network.Connected {
|
||||
if len(ix.Host.Network().ConnsToPeer(req.Target.ID)) == 0 {
|
||||
if err := ix.Host.Connect(ctx, req.Target); err != nil {
|
||||
respond(false, 0)
|
||||
return
|
||||
|
||||
@@ -79,6 +79,11 @@ type IndexerService struct {
|
||||
// eventQueue holds SWIM membership events to be piggybacked on responses
|
||||
// (infection-style dissemination toward connected nodes).
|
||||
eventQueue *common.MembershipEventQueue
|
||||
// pendingContactIndex is an inverted index built from Heartbeat.PendingContact.
|
||||
// Maps target peer ID → { caller peer ID → expiry time }.
|
||||
// Returned in HeartbeatResponse.PendingCallers when the target reconnects.
|
||||
pendingContactIndex map[string]map[string]time.Time
|
||||
pendingContactIndexMu sync.Mutex
|
||||
}
|
||||
|
||||
// NewIndexerService creates an IndexerService.
|
||||
@@ -95,6 +100,7 @@ func NewIndexerService(h host.Host, ps *pubsub.PubSub, maxNode int) *IndexerServ
|
||||
behavior: newNodeBehaviorTracker(),
|
||||
deletedDIDs: make(map[string]time.Time),
|
||||
eventQueue: &common.MembershipEventQueue{},
|
||||
pendingContactIndex: map[string]map[string]time.Time{},
|
||||
}
|
||||
if ps == nil {
|
||||
ps, err = pubsub.NewGossipSub(context.Background(), ix.Host)
|
||||
@@ -408,6 +414,24 @@ func NewIndexerService(h host.Host, ps *pubsub.PubSub, maxNode int) *IndexerServ
|
||||
resp.Incarnation = ix.incarnation.Load()
|
||||
resp.MembershipEvents = ix.eventQueue.Drain(5)
|
||||
|
||||
// PendingCallers: look up who has undelivered messages for this node.
|
||||
// Clean up expired entries at the same time.
|
||||
ix.pendingContactIndexMu.Lock()
|
||||
if callers, ok := ix.pendingContactIndex[remotePeer.String()]; ok {
|
||||
now := time.Now()
|
||||
for callerID, exp := range callers {
|
||||
if now.Before(exp) {
|
||||
resp.PendingCallers = append(resp.PendingCallers, callerID)
|
||||
} else {
|
||||
delete(callers, callerID)
|
||||
}
|
||||
}
|
||||
if len(callers) == 0 {
|
||||
delete(ix.pendingContactIndex, remotePeer.String())
|
||||
}
|
||||
}
|
||||
ix.pendingContactIndexMu.Unlock()
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user