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}