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}