Files
core/dht/Node.go
Kleissner 891e621e8a DHT: Add Search Client which runs the search in concurrent levels. This means faster results.
Add function FilterSearchStatus to get notified on the status of searches for debugging.
FindNode uses the new search client.
2021-07-28 01:28:31 +02:00

134 lines
2.9 KiB
Go

/*
File Name: Node.go
Copyright: 2021 Peernet s.r.o.
Author: Peter Kleissner
*/
package dht
import (
"bytes"
"math/big"
"time"
)
// Node is the over-the-wire representation of a node
type Node struct {
// ID is the unique identifier
ID []byte
// LastSeen when was this node last considered seen by the DHT
LastSeen time.Time
// Info is an arbitrary pointer specified by the caller
Info interface{}
}
// shortList is used in order to sort a list of arbitrary nodes against a comparator. These nodes are sorted by xor distance
type shortList struct {
// Nodes are a list of nodes to be compared
Nodes []*Node
// Comparator is the ID to compare to
Comparator []byte
// Contacted is a list of nodes that are considered contacted
Contacted map[string]bool
}
func newShortList() *shortList {
return &shortList{
Contacted: make(map[string]bool),
}
}
func areNodesEqual(n1 *Node, n2 *Node, allowNilID bool) bool {
if n1 == nil || n2 == nil {
return false
}
if !allowNilID {
if n1.ID == nil || n2.ID == nil {
return false
}
if bytes.Compare(n1.ID, n2.ID) != 0 {
return false
}
}
return true
}
func (n *shortList) RemoveNode(ID []byte) {
for i := 0; i < n.Len(); i++ {
if bytes.Compare(n.Nodes[i].ID, ID) == 0 {
n.Nodes = append(n.Nodes[:i], n.Nodes[i+1:]...)
return
}
}
}
func (n *shortList) AppendUniqueNodes(nodes ...*Node) {
nodesLoop:
for _, vv := range nodes {
for _, v := range n.Nodes {
if bytes.Compare(v.ID, vv.ID) == 0 {
continue nodesLoop
}
}
n.Nodes = append(n.Nodes, vv)
}
}
func (n *shortList) Len() int {
return len(n.Nodes)
}
func (n *shortList) Swap(i, j int) {
n.Nodes[i], n.Nodes[j] = n.Nodes[j], n.Nodes[i]
}
func (n *shortList) Less(i, j int) bool {
iDist := getDistance(n.Nodes[i].ID, n.Comparator)
jDist := getDistance(n.Nodes[j].ID, n.Comparator)
return iDist.Cmp(jDist) == -1
}
func getDistance(id1 []byte, id2 []byte) *big.Int {
buf1 := new(big.Int).SetBytes(id1)
buf2 := new(big.Int).SetBytes(id2)
result := new(big.Int).Xor(buf1, buf2)
return result
}
// GetUncontacted returns a list of uncontacted nodes. Each returned node will be marked as contacted.
func (n *shortList) GetUncontacted(count int, useCount bool) (Nodes []*Node) {
for _, node := range n.Nodes {
if useCount && count <= 0 {
break
}
// Don't contact nodes already contacted
if n.Contacted[string(node.ID)] == true {
continue
}
n.Contacted[string(node.ID)] = true
Nodes = append(Nodes, node)
count--
}
return Nodes
}
// NodeMessage is a message sent by a node
type NodeMessage struct {
SenderID []byte // Sender of the message
Data []byte // FIND_VALUE: Actual data
Closest []*Node // FIND_VALUE, FIND_NODE: Closest nodes to the requested key
Storing []*Node // FIND_VALUE: Nodes known to store the value
}
// NodeFilterFunc is called to filter nodes based on the callers choice
type NodeFilterFunc func(node *Node) (accept bool)