arnaucube
arnaucube/go-dhtPublic Self-hosted repository
go-dht/node/node.go
node/node.goRaw
  1package node
  2
  3import (
  4	"encoding/hex"
  5	"fmt"
  6	"go-dht/config"
  7	"go-dht/kademlia"
  8	"io/ioutil"
  9	"net"
 10	"net/http"
 11	"net/rpc"
 12	"time"
 13
 14	log "github.com/sirupsen/logrus"
 15)
 16
 17type Node struct {
 18	port     string
 19	kademlia *kademlia.Kademlia
 20}
 21
 22func NewNode() (Node, error) {
 23	id, err := kademlia.NewID()
 24	if err != nil {
 25		return Node{}, err
 26	}
 27
 28	var n Node
 29	n.kademlia = kademlia.NewKademliaTable(id, config.C.Addr, config.C.Port)
 30	return n, nil
 31}
 32
 33func LoadNode(idStr string) (Node, error) {
 34	id, err := kademlia.IDFromString(idStr)
 35	if err != nil {
 36		return Node{}, err
 37	}
 38	var n Node
 39	n.kademlia = kademlia.NewKademliaTable(id, config.C.Addr, config.C.Port)
 40	return n, nil
 41}
 42
 43func (n Node) ID() kademlia.ID {
 44	return n.kademlia.N.ID
 45}
 46
 47func (n Node) Kademlia() kademlia.Kademlia {
 48	return *n.kademlia
 49}
 50
 51func (n *Node) Start() error {
 52	// rpc server
 53	err := rpc.Register(n)
 54	if err != nil {
 55		return err
 56	}
 57	rpc.HandleHTTP()
 58	listener, err := net.Listen("tcp", ":"+config.C.Port)
 59	if err != nil {
 60		return err
 61	}
 62
 63	go func() {
 64		// TMP in order to print the KBuckets of the node
 65		for {
 66			fmt.Println(n.kademlia)
 67			time.Sleep(8 * time.Second)
 68		}
 69	}()
 70
 71	go n.pingKnownNodes(config.C.KnownNodes)
 72	go n.kademlia.Update(kademlia.ListedNode{
 73		ID:   n.ID(),
 74		Addr: config.C.Addr,
 75		Port: config.C.Port,
 76	})
 77
 78	err = http.Serve(listener, nil)
 79	if err != nil {
 80		return err
 81	}
 82	return nil
 83}
 84
 85func (n *Node) pingKnownNodes(lns []kademlia.ListedNode) error {
 86	for _, ln := range lns {
 87		err := n.kademlia.CallPing(ln)
 88		if err != nil {
 89			log.Warning("[pingKnownNodes]", err)
 90		}
 91	}
 92	return nil
 93}
 94
 95// Exposed RPC calls: Ping, Store, FindNode, FindValue
 96
 97func (n *Node) Ping(ln kademlia.ListedNode, thisLn *kademlia.ListedNode) error {
 98	log.Info("[rpc] PING from ", ln.ID)
 99	n.kademlia.Update(ln)
100	*thisLn = kademlia.ListedNode{
101		ID:   n.ID(),
102		Addr: config.C.Addr,
103		Port: config.C.Port,
104	}
105	// perform PONG call to the requester (maybe ping&pong can be unified)
106	err := n.kademlia.CallPong(ln)
107	if err != nil {
108		log.Warning("[PONG]", err)
109	}
110	return nil
111}
112
113func (n *Node) Pong(ln kademlia.ListedNode, ack *bool) error {
114	log.Info("[rpc] PONG")
115	n.kademlia.Update(ln)
116	return nil
117}
118
119func (n *Node) Store(data []byte, ack *bool) error {
120	log.Info("[rpc] STORE")
121
122	h := kademlia.HashData(data)
123	if n.kademlia.KBucket(h) != 0 {
124		*ack = false
125		log.Warning("[STORE] data not for this node")
126		return nil
127	}
128	err := ioutil.WriteFile(config.C.Storage+"/"+hex.EncodeToString(h[:]), data, 0644)
129	if err != nil {
130		*ack = false
131		log.Warning("[STORE]", err)
132		return err
133	}
134	*ack = true
135	return nil
136}
137
138func (n *Node) FindNode(id kademlia.ID, lns *[]kademlia.ListedNode) error {
139	log.Info("[rpc] FIND_NODE")
140	// k := n.kademlia.KBucket(ln.ID)
141	k, err := n.kademlia.GetClosestKBucket(id)
142	if err != nil {
143		log.Debug("[rpc] FIND_NODE ERROR: ", err)
144		*lns = []kademlia.ListedNode{}
145		return nil
146	}
147	log.Info("[FIND_NODE] k", k)
148	bucket := n.kademlia.KBuckets[k]
149	*lns = bucket
150	return nil
151}
152
153type FindValueResp struct {
154	Value []byte
155	Lns   []kademlia.ListedNode
156}
157
158func (n *Node) FindValue(id kademlia.ID, resp *FindValueResp) error {
159	log.Info("[rpc] FIND_VALUE")
160	// first check if value is in this node storage
161	f, err := ioutil.ReadFile(config.C.Storage + "/" + id.String())
162	if err == nil {
163		// value exists, return it
164		*resp = FindValueResp{
165			Value: f,
166		}
167		return nil
168	}
169
170	// k := n.kademlia.KBucket(id)
171	k, err := n.kademlia.GetClosestKBucket(id)
172	if err != nil {
173		*resp = FindValueResp{}
174		return nil
175	}
176	log.Info("[FIND_VALUE] k", k)
177	// bucket := n.kademlia.KBuckets[k]
178	*resp = FindValueResp{
179		Lns: n.kademlia.KBuckets[k],
180	}
181	return nil
182
183}