package indexer import ( "encoding/base64" "encoding/json" "errors" "oc-discovery/daemons/node/common" "time" pp "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" ) type PeerRecord struct { Name string `json:"name"` DID string `json:"did"` // real PEER ID PeerID string `json:"peer_id"` PubKey []byte `json:"pub_key"` APIUrl string `json:"api_url"` StreamAddress string `json:"stream_address"` NATSAddress string `json:"nats_address"` WalletAddress string `json:"wallet_address"` Signature []byte `json:"signature"` ExpiryDate time.Time `json:"expiry_date"` } func (p *PeerRecord) Sign() error { priv, err := common.LoadKeyFromFile(false) if err != nil { return err } dht := PeerRecord{ Name: p.Name, DID: p.DID, PubKey: p.PubKey, ExpiryDate: p.ExpiryDate, } payload, _ := json.Marshal(dht) b, err := common.Sign(priv, payload) p.Signature = b return err } func (p *PeerRecord) Verify() (crypto.PubKey, error) { pubKey, err := crypto.UnmarshalPublicKey(p.PubKey) // retrieve pub key in message if err != nil { return pubKey, err } dht := PeerRecord{ Name: p.Name, DID: p.DID, PubKey: p.PubKey, ExpiryDate: p.ExpiryDate, } payload, _ := json.Marshal(dht) if ok, _ := common.Verify(pubKey, payload, p.Signature); !ok { // verify minimal message was sign per pubKey return pubKey, errors.New("invalid signature") } return pubKey, nil } func (pr *PeerRecord) ExtractPeer(ourkey string, key string, pubKey crypto.PubKey) (bool, *pp.Peer, error) { pubBytes, _ := pubKey.Raw() rel := pp.NONE if ourkey == key { // at this point is PeerID is same as our... we are... thats our peer INFO rel = pp.SELF } p := &pp.Peer{ AbstractObject: utils.AbstractObject{ UUID: pr.DID, Name: pr.Name, }, State: pp.ONLINE, Relation: rel, // VERIFY.... it crush nothing PeerID: pr.PeerID, PublicKey: base64.StdEncoding.EncodeToString(pubBytes), APIUrl: pr.APIUrl, StreamAddress: pr.StreamAddress, NATSAddress: pr.NATSAddress, WalletAddress: pr.WalletAddress, } if time.Now().After(pr.ExpiryDate) { // is expired p.State = pp.OFFLINE // then is considers OFFLINE } b, err := json.Marshal(p) if err != nil { return pp.SELF == p.Relation, nil, err } tools.NewNATSCaller().SetNATSPub(tools.CREATE_RESOURCE, tools.NATSResponse{ FromApp: "oc-discovery", Datatype: tools.PEER, Method: int(tools.CREATE_PEER), Payload: b, }) if p.State == pp.OFFLINE { return pp.SELF == p.Relation, nil, errors.New("peer " + key + " is offline") } return pp.SELF == p.Relation, p, nil } type GetValue struct { Key string `json:"key"` } type GetResponse struct { Found bool `json:"found"` Record PeerRecord `json:"record,omitempty"` } func (ix *IndexerService) initNodeHandler() { ix.Host.SetStreamHandler(common.ProtocolHeartbeat, ix.HandleNodeHeartbeat) ix.Host.SetStreamHandler(common.ProtocolPublish, ix.handleNodePublish) ix.Host.SetStreamHandler(common.ProtocolGet, ix.handleNodeGet) } func (ix *IndexerService) handleNodePublish(s network.Stream) { defer s.Close() var rec PeerRecord if err := json.NewDecoder(s).Decode(&rec); err != nil { return } if rec.PeerID == "" || rec.ExpiryDate.Before(time.Now()) { // already expired return } pid, err := peer.Decode(rec.PeerID) if err != nil { return } ix.StreamMU.Lock() defer ix.StreamMU.Unlock() streams := ix.StreamRecords[common.ProtocolPublish] if streams == nil { ix.StreamRecords[common.ProtocolPublish] = map[peer.ID]*common.StreamRecord[PeerRecord]{} return } if srec, ok := streams[pid]; ok { srec.DID = rec.DID srec.Record = rec srec.LastSeen = time.Now() } else { streams[pid] = &common.StreamRecord[PeerRecord]{ // HeartBeat wil DID: rec.DID, Record: rec, LastSeen: time.Now(), } } } func (ix *IndexerService) handleNodeGet(s network.Stream) { defer s.Close() var req GetValue if err := json.NewDecoder(s).Decode(&req); err != nil { return } ix.StreamMU.Lock() defer ix.StreamMU.Unlock() streams := ix.StreamRecords[common.ProtocolGet] if streams == nil { ix.StreamRecords[common.ProtocolGet] = map[peer.ID]*common.StreamRecord[PeerRecord]{} return } // simple lookup by PeerID (or DID) for _, rec := range streams { if rec.Record.DID == req.Key || rec.Record.PeerID == req.Key { // OK resp := GetResponse{ Found: true, Record: rec.Record, } _ = json.NewEncoder(s).Encode(resp) return } } // Not found _ = json.NewEncoder(s).Encode(GetResponse{Found: false}) }