package node
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"go-dht/config"
|
|
"go-dht/kademlia"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"net/rpc"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type Node struct {
|
|
port string
|
|
kademlia *kademlia.Kademlia
|
|
}
|
|
|
|
func NewNode() (Node, error) {
|
|
id, err := kademlia.NewID()
|
|
if err != nil {
|
|
return Node{}, err
|
|
}
|
|
|
|
var n Node
|
|
n.kademlia = kademlia.NewKademliaTable(id, config.C.Addr, config.C.Port)
|
|
return n, nil
|
|
}
|
|
|
|
func LoadNode(idStr string) (Node, error) {
|
|
id, err := kademlia.IDFromString(idStr)
|
|
if err != nil {
|
|
return Node{}, err
|
|
}
|
|
var n Node
|
|
n.kademlia = kademlia.NewKademliaTable(id, config.C.Addr, config.C.Port)
|
|
return n, nil
|
|
}
|
|
|
|
func (n Node) ID() kademlia.ID {
|
|
return n.kademlia.N.ID
|
|
}
|
|
|
|
func (n Node) Kademlia() kademlia.Kademlia {
|
|
return *n.kademlia
|
|
}
|
|
|
|
func (n *Node) Start() error {
|
|
// rpc server
|
|
err := rpc.Register(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rpc.HandleHTTP()
|
|
listener, err := net.Listen("tcp", ":"+config.C.Port)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
go func() {
|
|
// TMP in order to print the KBuckets of the node
|
|
for {
|
|
fmt.Println(n.kademlia)
|
|
time.Sleep(8 * time.Second)
|
|
}
|
|
}()
|
|
|
|
go n.pingKnownNodes(config.C.KnownNodes)
|
|
go n.kademlia.Update(kademlia.ListedNode{
|
|
ID: n.ID(),
|
|
Addr: config.C.Addr,
|
|
Port: config.C.Port,
|
|
})
|
|
|
|
err = http.Serve(listener, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *Node) pingKnownNodes(lns []kademlia.ListedNode) error {
|
|
for _, ln := range lns {
|
|
err := n.kademlia.CallPing(ln)
|
|
if err != nil {
|
|
log.Warning("[pingKnownNodes]", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Exposed RPC calls: Ping, Store, FindNode, FindValue
|
|
|
|
func (n *Node) Ping(ln kademlia.ListedNode, thisLn *kademlia.ListedNode) error {
|
|
log.Info("[rpc] PING from ", ln.ID)
|
|
n.kademlia.Update(ln)
|
|
*thisLn = kademlia.ListedNode{
|
|
ID: n.ID(),
|
|
Addr: config.C.Addr,
|
|
Port: config.C.Port,
|
|
}
|
|
// perform PONG call to the requester (maybe ping&pong can be unified)
|
|
err := n.kademlia.CallPong(ln)
|
|
if err != nil {
|
|
log.Warning("[PONG]", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *Node) Pong(ln kademlia.ListedNode, ack *bool) error {
|
|
log.Info("[rpc] PONG")
|
|
n.kademlia.Update(ln)
|
|
return nil
|
|
}
|
|
|
|
func (n *Node) Store(data []byte, ack *bool) error {
|
|
log.Info("[rpc] STORE")
|
|
|
|
h := kademlia.HashData(data)
|
|
if n.kademlia.KBucket(h) != 0 {
|
|
*ack = false
|
|
log.Warning("[STORE] data not for this node")
|
|
return nil
|
|
}
|
|
err := ioutil.WriteFile(config.C.Storage+"/"+hex.EncodeToString(h[:]), data, 0644)
|
|
if err != nil {
|
|
*ack = false
|
|
log.Warning("[STORE]", err)
|
|
return err
|
|
}
|
|
*ack = true
|
|
return nil
|
|
}
|
|
|
|
func (n *Node) FindNode(id kademlia.ID, lns *[]kademlia.ListedNode) error {
|
|
log.Info("[rpc] FIND_NODE")
|
|
// k := n.kademlia.KBucket(ln.ID)
|
|
k, err := n.kademlia.GetClosestKBucket(id)
|
|
if err != nil {
|
|
log.Debug("[rpc] FIND_NODE ERROR: ", err)
|
|
*lns = []kademlia.ListedNode{}
|
|
return nil
|
|
}
|
|
log.Info("[FIND_NODE] k", k)
|
|
bucket := n.kademlia.KBuckets[k]
|
|
*lns = bucket
|
|
return nil
|
|
}
|
|
|
|
type FindValueResp struct {
|
|
Value []byte
|
|
Lns []kademlia.ListedNode
|
|
}
|
|
|
|
func (n *Node) FindValue(id kademlia.ID, resp *FindValueResp) error {
|
|
log.Info("[rpc] FIND_VALUE")
|
|
// first check if value is in this node storage
|
|
f, err := ioutil.ReadFile(config.C.Storage + "/" + id.String())
|
|
if err == nil {
|
|
// value exists, return it
|
|
*resp = FindValueResp{
|
|
Value: f,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// k := n.kademlia.KBucket(id)
|
|
k, err := n.kademlia.GetClosestKBucket(id)
|
|
if err != nil {
|
|
*resp = FindValueResp{}
|
|
return nil
|
|
}
|
|
log.Info("[FIND_VALUE] k", k)
|
|
// bucket := n.kademlia.KBuckets[k]
|
|
*resp = FindValueResp{
|
|
Lns: n.kademlia.KBuckets[k],
|
|
}
|
|
return nil
|
|
|
|
}
|