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) Start() error {
 48	// rpc server
 49	err := rpc.Register(n)
 50	if err != nil {
 51		return err
 52	}
 53	rpc.HandleHTTP()
 54	listener, err := net.Listen("tcp", ":"+config.C.Port)
 55	if err != nil {
 56		return err
 57	}
 58
 59	go func() {
 60		// TMP in order to print the KBuckets of the node
 61		for {
 62			fmt.Println(n.kademlia)
 63			time.Sleep(5 * time.Second)
 64		}
 65	}()
 66
 67	go n.pingKnownNodes(config.C.KnownNodes)
 68
 69	err = http.Serve(listener, nil)
 70	if err != nil {
 71		return err
 72	}
 73	return nil
 74}
 75
 76func (n *Node) pingKnownNodes(lns []kademlia.ListedNode) error {
 77	for _, ln := range lns {
 78		err := n.kademlia.CallPing(ln)
 79		if err != nil {
 80			log.Warning("[pingKnownNodes]", err)
 81		}
 82	}
 83	return nil
 84}
 85
 86// Exposed RPC calls: Ping, Store, FindNode, FindValue
 87
 88func (n *Node) Ping(ln kademlia.ListedNode, thisLn *kademlia.ListedNode) error {
 89	log.Info("[rpc] PING from ", ln.ID)
 90	n.kademlia.Update(ln)
 91	*thisLn = kademlia.ListedNode{
 92		ID:   n.ID(),
 93		Addr: config.C.Addr,
 94		Port: config.C.Port,
 95	}
 96	// perform PONG call to the requester (maybe ping&pong can be unified)
 97	err := n.kademlia.CallPong(ln)
 98	if err != nil {
 99		log.Warning("[PONG]", err)
100	}
101	return nil
102}
103
104func (n *Node) Pong(ln kademlia.ListedNode, ack *bool) error {
105	log.Info("[rpc] PONG")
106	n.kademlia.Update(ln)
107	return nil
108}
109
110func (n *Node) Store(data []byte, ack *bool) error {
111	log.Info("[rpc] STORE")
112
113	h := kademlia.HashData(data)
114	if n.kademlia.KBucket(h) != 0 {
115		*ack = false
116		log.Warning("[STORE] data not for this node")
117		return nil
118	}
119	err := ioutil.WriteFile(config.C.Storage+"/"+hex.EncodeToString(h[:]), data, 0644)
120	if err != nil {
121		*ack = false
122		log.Warning("[STORE]", err)
123		return err
124	}
125	*ack = true
126	return nil
127}
128
129func (n *Node) FindNode(ln kademlia.ListedNode, lns *[]kademlia.ListedNode) error {
130	log.Info("[rpc] FIND_NODE")
131	// k := n.kademlia.KBucket(ln.ID)
132	k, err := n.kademlia.FindClosestKBucket(ln.ID)
133	if err != nil {
134		*lns = []kademlia.ListedNode{}
135		return nil
136	}
137	log.Info("[FIND_NODE] k", k)
138	bucket := n.kademlia.KBuckets[k]
139	*lns = bucket
140	return nil
141}
142
143type FindValueResp struct {
144	Value []byte
145	Lns   []kademlia.ListedNode
146}
147
148func (n *Node) FindValue(id kademlia.ID, resp *FindValueResp) error {
149	log.Info("[rpc] FIND_VALUE")
150	// first check if value is in this node storage
151	f, err := ioutil.ReadFile(config.C.Storage + "/" + id.String())
152	if err == nil {
153		// value exists, return it
154		*resp = FindValueResp{
155			Value: f,
156		}
157		return nil
158	}
159
160	// k := n.kademlia.KBucket(id)
161	k, err := n.kademlia.FindClosestKBucket(id)
162	if err != nil {
163		*resp = FindValueResp{}
164		return nil
165	}
166	log.Info("[FIND_VALUE] k", k)
167	// bucket := n.kademlia.KBuckets[k]
168	*resp = FindValueResp{
169		Lns: n.kademlia.KBuckets[k],
170	}
171	return nil
172
173}