Indexer Quality Score TrustLess
This commit is contained in:
30
README.md
30
README.md
@@ -15,3 +15,33 @@ If default Swagger page is displayed instead of tyour api, change url in swagger
|
|||||||
url: "swagger.json"
|
url: "swagger.json"
|
||||||
|
|
||||||
|
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant Dev as Développeur / Owner
|
||||||
|
participant IPFS as Réseau IPFS
|
||||||
|
participant CID as CID (hash du fichier)
|
||||||
|
participant Argo as Orchestrateur Argo
|
||||||
|
participant CU as Compute Unit
|
||||||
|
participant MinIO as Storage MinIO
|
||||||
|
|
||||||
|
%% 1. Ajout du fichier sur IPFS
|
||||||
|
Dev->>IPFS: Chiffre et ajoute fichier (algo/dataset)
|
||||||
|
IPFS-->>CID: Génère CID unique (hash du fichier)
|
||||||
|
Dev->>Dev: Stocke CID pour référence future
|
||||||
|
|
||||||
|
%% 2. Orchestration par Argo
|
||||||
|
Argo->>CID: Requête CID pour job
|
||||||
|
CID-->>Argo: Fournit le fichier (vérifié via hash)
|
||||||
|
|
||||||
|
%% 3. Execution sur la Compute Unit
|
||||||
|
Argo->>CU: Déploie job avec fichier récupéré
|
||||||
|
CU->>CU: Vérifie hash (CID) pour intégrité
|
||||||
|
CU->>CU: Exécute l'algo sur le dataset
|
||||||
|
|
||||||
|
%% 4. Stockage des résultats
|
||||||
|
CU->>MinIO: Stocke output (résultats) ou logs
|
||||||
|
CU->>IPFS: Optionnel : ajoute output sur IPFS (nouveau CID)
|
||||||
|
|
||||||
|
%% 5. Vérification et traçabilité
|
||||||
|
Dev->>IPFS: Vérifie CID output si nécessaire
|
||||||
|
CU->>Dev: Fournit résultat et log de hash
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
cr "crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
"oc-discovery/conf"
|
"oc-discovery/conf"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -60,7 +66,7 @@ func (ix *LongLivedStreamRecordedService[T]) gc() {
|
|||||||
streams := ix.StreamRecords[ProtocolHeartbeat]
|
streams := ix.StreamRecords[ProtocolHeartbeat]
|
||||||
|
|
||||||
for pid, rec := range streams {
|
for pid, rec := range streams {
|
||||||
if now.After(rec.HeartbeatStream.Expiry) || now.Sub(rec.LastSeen) > 2*rec.HeartbeatStream.Expiry.Sub(now) {
|
if now.After(rec.HeartbeatStream.Expiry) || now.Sub(rec.HeartbeatStream.UptimeTracker.LastSeen) > 2*rec.HeartbeatStream.Expiry.Sub(now) {
|
||||||
for _, sstreams := range ix.StreamRecords {
|
for _, sstreams := range ix.StreamRecords {
|
||||||
if sstreams[pid] != nil {
|
if sstreams[pid] != nil {
|
||||||
delete(sstreams, pid)
|
delete(sstreams, pid)
|
||||||
@@ -115,34 +121,44 @@ func (ix *LongLivedStreamRecordedService[T]) snapshot() []*StreamRecord[T] {
|
|||||||
func (ix *LongLivedStreamRecordedService[T]) HandleNodeHeartbeat(s network.Stream) {
|
func (ix *LongLivedStreamRecordedService[T]) HandleNodeHeartbeat(s network.Stream) {
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
for {
|
for {
|
||||||
pid, hb, err := CheckHeartbeat(ix.Host, s, ix.maxNodesConn)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ix.StreamMU.Lock()
|
ix.StreamMU.Lock()
|
||||||
if ix.StreamRecords[ProtocolHeartbeat] == nil {
|
if ix.StreamRecords[ProtocolHeartbeat] == nil {
|
||||||
ix.StreamRecords[ProtocolHeartbeat] = map[pp.ID]*StreamRecord[T]{}
|
ix.StreamRecords[ProtocolHeartbeat] = map[pp.ID]*StreamRecord[T]{}
|
||||||
}
|
}
|
||||||
streams := ix.StreamRecords[ProtocolHeartbeat]
|
streams := ix.StreamRecords[ProtocolHeartbeat]
|
||||||
|
streamsAnonym := map[pp.ID]HeartBeatStreamed{}
|
||||||
|
for k, v := range streams {
|
||||||
|
streamsAnonym[k] = v
|
||||||
|
}
|
||||||
|
ix.StreamMU.Unlock()
|
||||||
|
|
||||||
|
pid, hb, err := CheckHeartbeat(ix.Host, s, streamsAnonym, &ix.StreamMU, ix.maxNodesConn)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ix.StreamMU.Lock()
|
||||||
// if record already seen update last seen
|
// if record already seen update last seen
|
||||||
if rec, ok := streams[*pid]; ok {
|
if rec, ok := streams[*pid]; ok {
|
||||||
rec.DID = hb.DID
|
rec.DID = hb.DID
|
||||||
rec.Stream = s
|
rec.Stream = s
|
||||||
rec.HeartbeatStream = hb.Stream
|
rec.HeartbeatStream = hb.Stream
|
||||||
rec.LastSeen = time.Now().UTC()
|
rec.HeartbeatStream.UptimeTracker.LastSeen = time.Now().UTC()
|
||||||
} else {
|
} else {
|
||||||
|
hb.Stream.UptimeTracker = &UptimeTracker{
|
||||||
|
FirstSeen: time.Now().UTC(),
|
||||||
|
LastSeen: time.Now().UTC(),
|
||||||
|
}
|
||||||
streams[*pid] = &StreamRecord[T]{
|
streams[*pid] = &StreamRecord[T]{
|
||||||
DID: hb.DID,
|
DID: hb.DID,
|
||||||
HeartbeatStream: hb.Stream,
|
HeartbeatStream: hb.Stream,
|
||||||
Stream: s,
|
Stream: s,
|
||||||
LastSeen: time.Now().UTC(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ix.StreamMU.Unlock()
|
ix.StreamMU.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckHeartbeat(h host.Host, s network.Stream, maxNodes int) (*pp.ID, *Heartbeat, error) {
|
func CheckHeartbeat(h host.Host, s network.Stream, streams map[pp.ID]HeartBeatStreamed, lock *sync.RWMutex, maxNodes int) (*pp.ID, *Heartbeat, error) {
|
||||||
if len(h.Network().Peers()) >= maxNodes {
|
if len(h.Network().Peers()) >= maxNodes {
|
||||||
return nil, nil, fmt.Errorf("too many connections, try another indexer")
|
return nil, nil, fmt.Errorf("too many connections, try another indexer")
|
||||||
}
|
}
|
||||||
@@ -150,7 +166,26 @@ func CheckHeartbeat(h host.Host, s network.Stream, maxNodes int) (*pp.ID, *Heart
|
|||||||
if err := json.NewDecoder(s).Decode(&hb); err != nil {
|
if err := json.NewDecoder(s).Decode(&hb); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
if ok, bpms, err := getBandwidthChallengeRate(MinPayloadChallenge+int(rand.Float64()*(MaxPayloadChallenge-MinPayloadChallenge)), s); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
} else if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("Not a proper peer")
|
||||||
|
} else {
|
||||||
pid, err := pp.Decode(hb.PeerID)
|
pid, err := pp.Decode(hb.PeerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
upTime := float64(0)
|
||||||
|
lock.Lock()
|
||||||
|
if rec, ok := streams[pid]; ok && rec.GetUptimeTracker() != nil {
|
||||||
|
upTime = rec.GetUptimeTracker().Uptime().Hours() / float64(time.Since(TimeWatcher).Hours())
|
||||||
|
}
|
||||||
|
lock.Unlock()
|
||||||
|
diversity := getDiversityRate(h, hb.IndexersBinded)
|
||||||
|
hb.ComputeIndexerScore(upTime, bpms, diversity)
|
||||||
|
if hb.Score < 75 {
|
||||||
|
return nil, nil, errors.New("not enough trusting value")
|
||||||
|
}
|
||||||
hb.Stream = &Stream{
|
hb.Stream = &Stream{
|
||||||
Name: hb.Name,
|
Name: hb.Name,
|
||||||
DID: hb.DID,
|
DID: hb.DID,
|
||||||
@@ -158,6 +193,91 @@ func CheckHeartbeat(h host.Host, s network.Stream, maxNodes int) (*pp.ID, *Heart
|
|||||||
Expiry: time.Now().UTC().Add(2 * time.Minute),
|
Expiry: time.Now().UTC().Add(2 * time.Minute),
|
||||||
} // here is the long-lived bidirectionnal heart bit.
|
} // here is the long-lived bidirectionnal heart bit.
|
||||||
return &pid, &hb, err
|
return &pid, &hb, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDiversityRate(h host.Host, peers []string) float64 {
|
||||||
|
peers, _ = checkPeers(h, peers)
|
||||||
|
diverse := []string{}
|
||||||
|
for _, p := range peers {
|
||||||
|
ip, err := ExtractIP(p)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
div := ip.Mask(net.CIDRMask(24, 32)).String()
|
||||||
|
if !slices.Contains(diverse, div) {
|
||||||
|
diverse = append(diverse, div)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return float64(len(diverse) / len(peers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPeers(h host.Host, peers []string) ([]string, []string) {
|
||||||
|
concretePeer := []string{}
|
||||||
|
ips := []string{}
|
||||||
|
for _, p := range peers {
|
||||||
|
ad, err := pp.AddrInfoFromString(p)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if PeerIsAlive(h, *ad) {
|
||||||
|
concretePeer = append(concretePeer, p)
|
||||||
|
if ip, err := ExtractIP(p); err == nil {
|
||||||
|
ips = append(ips, ip.Mask(net.CIDRMask(24, 32)).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return concretePeer, ips
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaxExpectedMbps = 50.0
|
||||||
|
const MinPayloadChallenge = 512
|
||||||
|
const MaxPayloadChallenge = 2048
|
||||||
|
const BaseRoundTrip = 400 * time.Millisecond
|
||||||
|
|
||||||
|
func getBandwidthChallengeRate(payloadSize int, s network.Stream) (bool, float64, error) {
|
||||||
|
// Génération payload aléatoire
|
||||||
|
payload := make([]byte, payloadSize)
|
||||||
|
_, err := cr.Read(payload)
|
||||||
|
if err != nil {
|
||||||
|
return false, 0, err
|
||||||
|
}
|
||||||
|
start := time.Now()
|
||||||
|
// send on heartbeat stream the challenge
|
||||||
|
if _, err = s.Write(payload); err != nil {
|
||||||
|
return false, 0, err
|
||||||
|
}
|
||||||
|
// read back
|
||||||
|
response := make([]byte, payloadSize)
|
||||||
|
_, err = io.ReadFull(s, response)
|
||||||
|
if err != nil {
|
||||||
|
return false, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := time.Since(start)
|
||||||
|
// Verify content
|
||||||
|
if !bytes.Equal(payload, response) {
|
||||||
|
return false, 0, nil // pb or a sadge peer.
|
||||||
|
}
|
||||||
|
maxRoundTrip := BaseRoundTrip + (time.Duration(payloadSize) * (100 * time.Millisecond))
|
||||||
|
mbps := float64(payloadSize*8) / duration.Seconds() / 1e6
|
||||||
|
if duration > maxRoundTrip || mbps < 5.0 {
|
||||||
|
return false, float64(mbps / MaxExpectedMbps), nil
|
||||||
|
}
|
||||||
|
return true, float64(mbps / MaxExpectedMbps), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UptimeTracker struct {
|
||||||
|
FirstSeen time.Time
|
||||||
|
LastSeen time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UptimeTracker) Uptime() time.Duration {
|
||||||
|
return time.Since(u.FirstSeen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UptimeTracker) IsEligible(min time.Duration) bool {
|
||||||
|
return u.Uptime() >= min
|
||||||
}
|
}
|
||||||
|
|
||||||
type StreamRecord[T interface{}] struct {
|
type StreamRecord[T interface{}] struct {
|
||||||
@@ -165,7 +285,13 @@ type StreamRecord[T interface{}] struct {
|
|||||||
HeartbeatStream *Stream
|
HeartbeatStream *Stream
|
||||||
Stream network.Stream
|
Stream network.Stream
|
||||||
Record T
|
Record T
|
||||||
LastSeen time.Time // to check expiry
|
}
|
||||||
|
|
||||||
|
func (s *StreamRecord[T]) GetUptimeTracker() *UptimeTracker {
|
||||||
|
if s.HeartbeatStream == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return s.HeartbeatStream.UptimeTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
@@ -173,6 +299,11 @@ type Stream struct {
|
|||||||
DID string `json:"did"`
|
DID string `json:"did"`
|
||||||
Stream network.Stream
|
Stream network.Stream
|
||||||
Expiry time.Time `json:"expiry"`
|
Expiry time.Time `json:"expiry"`
|
||||||
|
UptimeTracker *UptimeTracker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) GetUptimeTracker() *UptimeTracker {
|
||||||
|
return s.UptimeTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStream[T interface{}](s network.Stream, did string, record T) *Stream {
|
func NewStream[T interface{}](s network.Stream, did string, record T) *Stream {
|
||||||
@@ -228,10 +359,13 @@ const (
|
|||||||
ProtocolGet = "/opencloud/record/get/1.0"
|
ProtocolGet = "/opencloud/record/get/1.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
var StaticIndexers []*pp.AddrInfo = []*pp.AddrInfo{}
|
var TimeWatcher time.Time
|
||||||
|
|
||||||
|
var StaticIndexers map[string]*pp.AddrInfo = map[string]*pp.AddrInfo{}
|
||||||
var StreamIndexers ProtocolStream = ProtocolStream{}
|
var StreamIndexers ProtocolStream = ProtocolStream{}
|
||||||
|
|
||||||
func ConnectToIndexers(h host.Host, minIndexer int, maxIndexer int, myPID pp.ID) {
|
func ConnectToIndexers(h host.Host, minIndexer int, maxIndexer int, myPID pp.ID) {
|
||||||
|
TimeWatcher = time.Now().UTC()
|
||||||
logger := oclib.GetLogger()
|
logger := oclib.GetLogger()
|
||||||
addresses := strings.Split(conf.GetConfig().IndexerAddresses, ",")
|
addresses := strings.Split(conf.GetConfig().IndexerAddresses, ",")
|
||||||
|
|
||||||
@@ -243,7 +377,6 @@ func ConnectToIndexers(h host.Host, minIndexer int, maxIndexer int, myPID pp.ID)
|
|||||||
fmt.Println("GENERATE ADDR", indexerAddr)
|
fmt.Println("GENERATE ADDR", indexerAddr)
|
||||||
ad, err := pp.AddrInfoFromString(indexerAddr)
|
ad, err := pp.AddrInfoFromString(indexerAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("ADDR ERR", err)
|
|
||||||
logger.Err(err)
|
logger.Err(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -256,7 +389,7 @@ func ConnectToIndexers(h host.Host, minIndexer int, maxIndexer int, myPID pp.ID)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StaticIndexers = append(StaticIndexers, ad)
|
StaticIndexers[indexerAddr] = ad
|
||||||
// make a privilege streams with indexer.
|
// make a privilege streams with indexer.
|
||||||
for _, proto := range []protocol.ID{ProtocolPublish, ProtocolGet, ProtocolHeartbeat} {
|
for _, proto := range []protocol.ID{ProtocolPublish, ProtocolGet, ProtocolHeartbeat} {
|
||||||
AddStreamProtocol(nil, StreamIndexers, h, proto, ad.ID, myPID, force, nil)
|
AddStreamProtocol(nil, StreamIndexers, h, proto, ad.ID, myPID, force, nil)
|
||||||
@@ -312,6 +445,14 @@ type Heartbeat struct {
|
|||||||
DID string `json:"did"`
|
DID string `json:"did"`
|
||||||
PeerID string `json:"peer_id"`
|
PeerID string `json:"peer_id"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
IndexersBinded []string `json:"indexers_binded"`
|
||||||
|
Score float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hb *Heartbeat) ComputeIndexerScore(uptimeHours float64, bpms float64, diversity float64) {
|
||||||
|
hb.Score = (0.4 * uptimeHours) +
|
||||||
|
(0.4 * bpms) +
|
||||||
|
(0.2 * diversity)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HeartbeatInfo []struct {
|
type HeartbeatInfo []struct {
|
||||||
@@ -320,7 +461,7 @@ type HeartbeatInfo []struct {
|
|||||||
|
|
||||||
const ProtocolHeartbeat = "/opencloud/heartbeat/1.0"
|
const ProtocolHeartbeat = "/opencloud/heartbeat/1.0"
|
||||||
|
|
||||||
func SendHeartbeat(ctx context.Context, proto protocol.ID, name string, h host.Host, ps ProtocolStream, peers []*pp.AddrInfo, interval time.Duration) {
|
func SendHeartbeat(ctx context.Context, proto protocol.ID, name string, h host.Host, ps ProtocolStream, peers map[string]*pp.AddrInfo, interval time.Duration) {
|
||||||
peerID, err := oclib.GenerateNodeID()
|
peerID, err := oclib.GenerateNodeID()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
panic("can't heartbeat daemon failed to start")
|
panic("can't heartbeat daemon failed to start")
|
||||||
@@ -331,11 +472,16 @@ func SendHeartbeat(ctx context.Context, proto protocol.ID, name string, h host.H
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
|
addrs := []string{}
|
||||||
|
for addr := range StaticIndexers {
|
||||||
|
addrs = append(addrs, addr)
|
||||||
|
}
|
||||||
hb := Heartbeat{
|
hb := Heartbeat{
|
||||||
Name: name,
|
Name: name,
|
||||||
DID: peerID,
|
DID: peerID,
|
||||||
PeerID: h.ID().String(),
|
PeerID: h.ID().String(),
|
||||||
Timestamp: time.Now().UTC().Unix(),
|
Timestamp: time.Now().UTC().Unix(),
|
||||||
|
IndexersBinded: addrs,
|
||||||
}
|
}
|
||||||
for _, ix := range peers {
|
for _, ix := range peers {
|
||||||
_ = sendHeartbeat(ctx, h, proto, ix, hb, ps, interval*time.Second)
|
_ = sendHeartbeat(ctx, h, proto, ix, hb, ps, interval*time.Second)
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import (
|
|||||||
"cloud.o-forge.io/core/oc-lib/models/peer"
|
"cloud.o-forge.io/core/oc-lib/models/peer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type HeartBeatStreamed interface {
|
||||||
|
GetUptimeTracker() *UptimeTracker
|
||||||
|
}
|
||||||
|
|
||||||
type DiscoveryPeer interface {
|
type DiscoveryPeer interface {
|
||||||
GetPeerRecord(ctx context.Context, key string) ([]*peer.Peer, error)
|
GetPeerRecord(ctx context.Context, key string) ([]*peer.Peer, error)
|
||||||
}
|
}
|
||||||
|
|||||||
40
daemons/node/common/utils.go
Normal file
40
daemons/node/common/utils.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/host"
|
||||||
|
pp "github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PeerIsAlive(h host.Host, ad pp.AddrInfo) bool {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err := h.Connect(ctx, ad)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractIP(addr string) (net.IP, error) {
|
||||||
|
ma, err := multiaddr.NewMultiaddr(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ips, err := ma.ValueForProtocol(multiaddr.P_IP4) // or P_IP6
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
host, _, err := net.SplitHostPort(ips)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(host)
|
||||||
|
if ip == nil {
|
||||||
|
return nil, fmt.Errorf("invalid IP: %s", host)
|
||||||
|
}
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
@@ -171,15 +171,13 @@ func (ix *IndexerService) handleNodePublish(s network.Stream) {
|
|||||||
fmt.Println("UPDATE PUBLISH", pid)
|
fmt.Println("UPDATE PUBLISH", pid)
|
||||||
srec.DID = rec.DID
|
srec.DID = rec.DID
|
||||||
srec.Record = rec
|
srec.Record = rec
|
||||||
srec.LastSeen = time.Now().UTC()
|
srec.HeartbeatStream.UptimeTracker.LastSeen = time.Now().UTC()
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("CREATE PUBLISH", pid)
|
ix.StreamMU.Unlock()
|
||||||
streams[pid] = &common.StreamRecord[PeerRecord]{ // HeartBeat wil
|
logger.Err(errors.New("no heartbeat"))
|
||||||
DID: rec.DID,
|
continue
|
||||||
Record: rec,
|
|
||||||
LastSeen: time.Now().UTC(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ix.StreamMU.Unlock()
|
||||||
|
|
||||||
if ix.LongLivedPubSubs[common.TopicPubSubNodeActivity] != nil && !rec.NoPub {
|
if ix.LongLivedPubSubs[common.TopicPubSubNodeActivity] != nil && !rec.NoPub {
|
||||||
|
|
||||||
@@ -211,7 +209,6 @@ func (ix *IndexerService) handleNodePublish(s network.Stream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ix.StreamMU.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func ListenNATS(n *Node) {
|
|||||||
p := &peer.Peer{}
|
p := &peer.Peer{}
|
||||||
p = p.Deserialize(m, p).(*peer.Peer)
|
p = p.Deserialize(m, p).(*peer.Peer)
|
||||||
|
|
||||||
ad, err := pp.AddrInfoFromString(p.PeerID)
|
ad, err := pp.AddrInfoFromString(p.StreamAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ func ListenNATS(n *Node) {
|
|||||||
n.Mu.Unlock()
|
n.Mu.Unlock()
|
||||||
|
|
||||||
if p.Relation == peer.PARTNER {
|
if p.Relation == peer.PARTNER {
|
||||||
n.StreamService.ConnectToPartner(ad.ID, ad)
|
n.StreamService.ConnectToPartner(p.StreamAddress)
|
||||||
} else {
|
} else {
|
||||||
ps := common.ProtocolStream{}
|
ps := common.ProtocolStream{}
|
||||||
for p, s := range n.StreamService.Streams {
|
for p, s := range n.StreamService.Streams {
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func (ps *StreamService) ToPartnerPublishEvent(
|
|||||||
if err := json.Unmarshal(payload, &p); err != nil {
|
if err := json.Unmarshal(payload, &p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ad, err := pp.AddrInfoFromString(p.StreamAddress)
|
pid, err := pp.Decode(p.PeerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -111,11 +111,11 @@ func (ps *StreamService) ToPartnerPublishEvent(
|
|||||||
if ps.Streams[ProtocolHeartbeatPartner] == nil {
|
if ps.Streams[ProtocolHeartbeatPartner] == nil {
|
||||||
ps.Streams[ProtocolHeartbeatPartner] = map[pp.ID]*common.Stream{}
|
ps.Streams[ProtocolHeartbeatPartner] = map[pp.ID]*common.Stream{}
|
||||||
}
|
}
|
||||||
ps.ConnectToPartner(ad.ID, ad)
|
ps.ConnectToPartner(p.StreamAddress)
|
||||||
} else if ps.Streams[ProtocolHeartbeatPartner] != nil && ps.Streams[ProtocolHeartbeatPartner][ad.ID] != nil {
|
} else if ps.Streams[ProtocolHeartbeatPartner] != nil && ps.Streams[ProtocolHeartbeatPartner][pid] != nil {
|
||||||
for _, pids := range ps.Streams {
|
for _, pids := range ps.Streams {
|
||||||
if pids[ad.ID] != nil {
|
if pids[pid] != nil {
|
||||||
delete(pids, ad.ID)
|
delete(pids, pid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/libp2p/go-libp2p/core/network"
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
pp "github.com/libp2p/go-libp2p/core/peer"
|
pp "github.com/libp2p/go-libp2p/core/peer"
|
||||||
"github.com/libp2p/go-libp2p/core/protocol"
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ProtocolSearchResource = "/opencloud/resource/search/1.0"
|
const ProtocolSearchResource = "/opencloud/resource/search/1.0"
|
||||||
@@ -44,7 +45,7 @@ type StreamService struct {
|
|||||||
Node common.DiscoveryPeer
|
Node common.DiscoveryPeer
|
||||||
Streams common.ProtocolStream
|
Streams common.ProtocolStream
|
||||||
maxNodesConn int
|
maxNodesConn int
|
||||||
Mu sync.Mutex
|
Mu sync.RWMutex
|
||||||
// Stream map[protocol.ID]map[pp.ID]*daemons.Stream
|
// Stream map[protocol.ID]map[pp.ID]*daemons.Stream
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,26 +67,30 @@ func InitStream(ctx context.Context, h host.Host, key pp.ID, maxNode int, node c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *StreamService) HandlePartnerHeartbeat(stream network.Stream) {
|
func (s *StreamService) HandlePartnerHeartbeat(stream network.Stream) {
|
||||||
pid, hb, err := common.CheckHeartbeat(s.Host, stream, s.maxNodesConn)
|
s.Mu.Lock()
|
||||||
|
if s.Streams[ProtocolHeartbeatPartner] == nil {
|
||||||
|
s.Streams[ProtocolHeartbeatPartner] = map[pp.ID]*common.Stream{}
|
||||||
|
}
|
||||||
|
streams := s.Streams[ProtocolHeartbeatPartner]
|
||||||
|
streamsAnonym := map[pp.ID]common.HeartBeatStreamed{}
|
||||||
|
for k, v := range streams {
|
||||||
|
streamsAnonym[k] = v
|
||||||
|
}
|
||||||
|
s.Mu.Unlock()
|
||||||
|
pid, hb, err := common.CheckHeartbeat(s.Host, stream, streamsAnonym, &s.Mu, s.maxNodesConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Mu.Lock()
|
s.Mu.Lock()
|
||||||
defer s.Mu.Unlock()
|
defer s.Mu.Unlock()
|
||||||
|
|
||||||
if s.Streams[ProtocolHeartbeatPartner] == nil {
|
|
||||||
s.Streams[ProtocolHeartbeatPartner] = map[pp.ID]*common.Stream{}
|
|
||||||
}
|
|
||||||
streams := s.Streams[ProtocolHeartbeatPartner]
|
|
||||||
// if record already seen update last seen
|
// if record already seen update last seen
|
||||||
if rec, ok := streams[*pid]; ok {
|
if rec, ok := streams[*pid]; ok {
|
||||||
rec.DID = hb.DID
|
rec.DID = hb.DID
|
||||||
rec.Expiry = time.Now().UTC().Add(2 * time.Minute)
|
rec.Expiry = time.Now().UTC().Add(2 * time.Minute)
|
||||||
} else { // if not in stream ?
|
} else { // if not in stream ?
|
||||||
pid := stream.Conn().RemotePeer()
|
val, err := stream.Conn().RemoteMultiaddr().ValueForProtocol(ma.P_IP4)
|
||||||
ai, err := pp.AddrInfoFromP2pAddr(stream.Conn().RemoteMultiaddr())
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
s.ConnectToPartner(pid, ai)
|
s.ConnectToPartner(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
go s.StartGC(30 * time.Second)
|
go s.StartGC(30 * time.Second)
|
||||||
@@ -111,22 +116,18 @@ func (s *StreamService) connectToPartners() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
ad, err := pp.AddrInfoFromString(p.StreamAddress)
|
s.ConnectToPartner(p.StreamAddress)
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pid, err := pp.Decode(p.PeerID)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s.ConnectToPartner(pid, ad)
|
|
||||||
// heartbeat your partner.
|
// heartbeat your partner.
|
||||||
}
|
}
|
||||||
// TODO if handle... from partner then HeartBeat back
|
// TODO if handle... from partner then HeartBeat back
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StreamService) ConnectToPartner(pid pp.ID, ad *pp.AddrInfo) {
|
func (s *StreamService) ConnectToPartner(address string) {
|
||||||
|
ad, err := pp.AddrInfoFromString(address)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
logger := oclib.GetLogger()
|
logger := oclib.GetLogger()
|
||||||
force := false
|
force := false
|
||||||
for _, proto := range protocols {
|
for _, proto := range protocols {
|
||||||
@@ -134,11 +135,11 @@ func (s *StreamService) ConnectToPartner(pid pp.ID, ad *pp.AddrInfo) {
|
|||||||
if s.Streams[proto] == nil {
|
if s.Streams[proto] == nil {
|
||||||
s.Streams[proto] = map[pp.ID]*common.Stream{}
|
s.Streams[proto] = map[pp.ID]*common.Stream{}
|
||||||
}
|
}
|
||||||
s.Streams[proto][pid] = &common.Stream{
|
s.Streams[proto][ad.ID] = &common.Stream{
|
||||||
Stream: ss,
|
Stream: ss,
|
||||||
Expiry: time.Now().UTC().Add(2 * time.Minute),
|
Expiry: time.Now().UTC().Add(2 * time.Minute),
|
||||||
}
|
}
|
||||||
go s.readLoop(s.Streams[proto][pid])
|
go s.readLoop(s.Streams[proto][ad.ID])
|
||||||
}
|
}
|
||||||
if s.Host.Network().Connectedness(ad.ID) != network.Connected {
|
if s.Host.Network().Connectedness(ad.ID) != network.Connected {
|
||||||
force = true
|
force = true
|
||||||
@@ -147,10 +148,10 @@ func (s *StreamService) ConnectToPartner(pid pp.ID, ad *pp.AddrInfo) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.Streams = common.AddStreamProtocol(nil, s.Streams, s.Host, proto, pid, s.Key, force, &f)
|
s.Streams = common.AddStreamProtocol(nil, s.Streams, s.Host, proto, ad.ID, s.Key, force, &f)
|
||||||
}
|
}
|
||||||
common.SendHeartbeat(context.Background(), ProtocolHeartbeatPartner, conf.GetConfig().Name,
|
common.SendHeartbeat(context.Background(), ProtocolHeartbeatPartner, conf.GetConfig().Name,
|
||||||
s.Host, s.Streams, []*pp.AddrInfo{ad}, 20*time.Second)
|
s.Host, s.Streams, map[string]*pp.AddrInfo{address: ad}, 20*time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StreamService) searchPeer(search string) ([]*peer.Peer, error) {
|
func (s *StreamService) searchPeer(search string) ([]*peer.Peer, error) {
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -3,7 +3,7 @@ module oc-discovery
|
|||||||
go 1.24.6
|
go 1.24.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20260209113703-b9c9b6678099
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260212123952-403913d8cf13
|
||||||
github.com/beego/beego v1.12.13
|
github.com/beego/beego v1.12.13
|
||||||
github.com/beego/beego/v2 v2.3.8
|
github.com/beego/beego/v2 v2.3.8
|
||||||
github.com/go-redis/redis v6.15.9+incompatible
|
github.com/go-redis/redis v6.15.9+incompatible
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -46,6 +46,8 @@ cloud.o-forge.io/core/oc-lib v0.0.0-20260209095536-b767afb30168 h1:HHmfg0ktsJ5aT
|
|||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20260209095536-b767afb30168/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks=
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260209095536-b767afb30168/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks=
|
||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20260209113703-b9c9b6678099 h1:HczicbRtjiU51McjpDkmCsrQVs406bHybbLd+ZkqTo0=
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260209113703-b9c9b6678099 h1:HczicbRtjiU51McjpDkmCsrQVs406bHybbLd+ZkqTo0=
|
||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20260209113703-b9c9b6678099/go.mod h1:jmyBwmsac/4V7XPL347qawF60JsBCDmNAMfn/ySXKYo=
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260209113703-b9c9b6678099/go.mod h1:jmyBwmsac/4V7XPL347qawF60JsBCDmNAMfn/ySXKYo=
|
||||||
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260212123952-403913d8cf13 h1:DNIPQ7C+7wjbj5RUx29wLxuIe/wiSOcuUMlLRIv6Fvs=
|
||||||
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260212123952-403913d8cf13/go.mod h1:jmyBwmsac/4V7XPL347qawF60JsBCDmNAMfn/ySXKYo=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
|||||||
Reference in New Issue
Block a user