mirror of
https://github.com/arnaucube/go-dht.git
synced 2026-02-06 19:06:44 +01:00
node rpc PING & PONG methods and calls
This commit is contained in:
15
README.md
15
README.md
@@ -2,4 +2,17 @@
|
|||||||
|
|
||||||
Kademlia DHT Go implementation.
|
Kademlia DHT Go implementation.
|
||||||
|
|
||||||
Following the specification from https://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf and http://xlattice.sourceforge.net/components/protocol/kademlia/specs.html
|
Following the specification from
|
||||||
|
- https://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf
|
||||||
|
- http://xlattice.sourceforge.net/components/protocol/kademlia/specs.html
|
||||||
|
|
||||||
|
## Run
|
||||||
|
To run a node:
|
||||||
|
```
|
||||||
|
go run main.go --config config.test0.yaml --debug start
|
||||||
|
```
|
||||||
|
|
||||||
|
To run 3 test nodes inside a tmux session:
|
||||||
|
```
|
||||||
|
bash run-dev-nodes.sh
|
||||||
|
```
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ var NodeCommands = []cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdStart(c *cli.Context) error {
|
func cmdStart(c *cli.Context) error {
|
||||||
|
if c.GlobalBool("debug") {
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
log.SetReportCaller(true)
|
||||||
|
}
|
||||||
|
|
||||||
if err := config.MustRead(c); err != nil {
|
if err := config.MustRead(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
3
config.test0.yaml
Normal file
3
config.test0.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id: "0fd85ddddf15aeec2d5d8b01b013dbca030a18d7"
|
||||||
|
addr: 127.0.0.1
|
||||||
|
port: 5000
|
||||||
7
config.test1.yaml
Normal file
7
config.test1.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
id: "0fd85ddddf15aeec2d5d8b01b013dbca030a18d0"
|
||||||
|
addr: 127.0.0.1
|
||||||
|
port: 5001
|
||||||
|
knownNodes:
|
||||||
|
- id: "0fd85ddddf15aeec2d5d8b01b013dbca030a18d7"
|
||||||
|
addr: 127.0.0.1
|
||||||
|
port: 5000
|
||||||
7
config.test2.yaml
Normal file
7
config.test2.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
id: "c48d8b53dbefb609ed4e94d386dd5b22efcb2c5b"
|
||||||
|
addr: 127.0.0.1
|
||||||
|
port: 5002
|
||||||
|
knownNodes:
|
||||||
|
- id: "0fd85ddddf15aeec2d5d8b01b013dbca030a18d7"
|
||||||
|
addr: 127.0.0.1
|
||||||
|
port: 5000
|
||||||
10
config.yaml
10
config.yaml
@@ -1,10 +0,0 @@
|
|||||||
id: "0fd85ddddf15aeec2d5d8b01b013dbca030a18d7"
|
|
||||||
addr: 127.0.0.1
|
|
||||||
port: 5000
|
|
||||||
knownNodes:
|
|
||||||
- id: "0fd85ddddf15aeec2d5d8b01b013dbca030a18d7"
|
|
||||||
addr: 127.0.0.1
|
|
||||||
port: 5001
|
|
||||||
- id: "c48d8b53dbefb609ed4e94d386dd5b22efcb2c5b"
|
|
||||||
addr: 127.0.0.1
|
|
||||||
port: 5002
|
|
||||||
@@ -2,6 +2,7 @@ package kademlia
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
"net/rpc"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -20,18 +21,23 @@ type ListedNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Kademlia struct {
|
type Kademlia struct {
|
||||||
ID ID
|
// N is this node data
|
||||||
|
N ListedNode
|
||||||
KBuckets [B * 8][]ListedNode
|
KBuckets [B * 8][]ListedNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKademliaTable(id ID) *Kademlia {
|
func NewKademliaTable(id ID, addr, port string) *Kademlia {
|
||||||
return &Kademlia{
|
return &Kademlia{
|
||||||
ID: id,
|
N: ListedNode{
|
||||||
|
ID: id,
|
||||||
|
Addr: addr,
|
||||||
|
Port: port,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kad Kademlia) String() string {
|
func (kad Kademlia) String() string {
|
||||||
r := "Node ID: " + kad.ID.String() + ", KBuckets:\n"
|
r := "Node ID: " + kad.N.ID.String() + ", KBuckets:\n"
|
||||||
for i, kb := range kad.KBuckets {
|
for i, kb := range kad.KBuckets {
|
||||||
if len(kb) > 0 {
|
if len(kb) > 0 {
|
||||||
r += " KBucket " + strconv.Itoa(i) + "\n"
|
r += " KBucket " + strconv.Itoa(i) + "\n"
|
||||||
@@ -44,7 +50,7 @@ func (kad Kademlia) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (kad Kademlia) KBucket(o ID) int {
|
func (kad Kademlia) KBucket(o ID) int {
|
||||||
d := kad.ID.Distance(o)
|
d := kad.N.ID.Distance(o)
|
||||||
return kBucketByDistance(d[:])
|
return kBucketByDistance(d[:])
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -63,8 +69,8 @@ func (kad *Kademlia) Update(o ListedNode) {
|
|||||||
kb := kad.KBuckets[k]
|
kb := kad.KBuckets[k]
|
||||||
if len(kb) >= KBucketSize {
|
if len(kb) >= KBucketSize {
|
||||||
// if n.KBuckets[k] is alrady full, perform ping of the first element
|
// if n.KBuckets[k] is alrady full, perform ping of the first element
|
||||||
log.Debug("node.KBuckets[k] already full, performing ping to node.KBuckets[0]")
|
log.Info("node.KBuckets[k] already full, performing ping to node.KBuckets[0]")
|
||||||
kad.Ping(k, o)
|
kad.PingOldNode(k, o)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// check that is not already in the list
|
// check that is not already in the list
|
||||||
@@ -72,22 +78,59 @@ func (kad *Kademlia) Update(o ListedNode) {
|
|||||||
if exist {
|
if exist {
|
||||||
// update position of o to the bottom
|
// update position of o to the bottom
|
||||||
kad.KBuckets[k] = moveToBottom(kad.KBuckets[k], pos)
|
kad.KBuckets[k] = moveToBottom(kad.KBuckets[k], pos)
|
||||||
log.Debug("ListedNode already exists, moved to bottom")
|
log.Info("ListedNode already exists, moved to bottom")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// not exists, add it to the kBucket
|
// not exists, add it to the kBucket
|
||||||
kad.KBuckets[k] = append(kad.KBuckets[k], o)
|
kad.KBuckets[k] = append(kad.KBuckets[k], o)
|
||||||
log.Debug("ListedNode not exists, added to the bottom")
|
log.Info("ListedNode not exists, added to the bottom")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kad *Kademlia) Ping(k int, o ListedNode) {
|
func (kad *Kademlia) PingOldNode(k int, o ListedNode) {
|
||||||
// TODO when rpc layer is done
|
// TODO when rpc layer is done
|
||||||
// ping the n.KBuckets[k][0] (using goroutine)
|
// ping the n.KBuckets[k][0] (using goroutine)
|
||||||
// if no response (timeout), delete it and add 'o'
|
// if no response (timeout), delete it and add 'o'
|
||||||
// n.KBuckets[k][0] = o
|
// n.KBuckets[k][0] = o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kad *Kademlia) CallPing(o ListedNode) error {
|
||||||
|
client, err := rpc.DialHTTP("tcp", o.Addr+":"+o.Port)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ln := ListedNode{
|
||||||
|
ID: kad.N.ID,
|
||||||
|
Addr: kad.N.Addr,
|
||||||
|
Port: kad.N.Port,
|
||||||
|
}
|
||||||
|
var reply ListedNode
|
||||||
|
err = client.Call("Node.Ping", ln, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kad *Kademlia) CallPong(o ListedNode) error {
|
||||||
|
client, err := rpc.DialHTTP("tcp", o.Addr+":"+o.Port)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ln := ListedNode{
|
||||||
|
ID: kad.N.ID,
|
||||||
|
Addr: kad.N.Addr,
|
||||||
|
Port: kad.N.Port,
|
||||||
|
}
|
||||||
|
var reply bool
|
||||||
|
err = client.Call("Node.Pong", ln, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func existsInListedNodes(lns []ListedNode, ln ListedNode) (bool, int) {
|
func existsInListedNodes(lns []ListedNode, ln ListedNode) (bool, int) {
|
||||||
for i, v := range lns {
|
for i, v := range lns {
|
||||||
if v.ID.Equal(ln.ID) {
|
if v.ID.Equal(ln.ID) {
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ func TestCountZeros(t *testing.T) {
|
|||||||
func TestKBucket(t *testing.T) {
|
func TestKBucket(t *testing.T) {
|
||||||
idA, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
idA, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
kademlia := NewKademliaTable(idA)
|
kademlia := NewKademliaTable(idA, "127.0.0.1", "5000")
|
||||||
|
|
||||||
d := kademlia.KBucket(kademlia.ID)
|
d := kademlia.KBucket(kademlia.N.ID)
|
||||||
assert.Equal(t, 0, d) // same node should have distance 0
|
assert.Equal(t, 0, d) // same node should have distance 0
|
||||||
|
|
||||||
idB, err := IDFromString("c48d8b53dbefb609ed4e94d386dd5b22efcb2c5b")
|
idB, err := IDFromString("c48d8b53dbefb609ed4e94d386dd5b22efcb2c5b")
|
||||||
@@ -100,7 +100,7 @@ func TestMoveToBottom(t *testing.T) {
|
|||||||
func TestUpdate(t *testing.T) {
|
func TestUpdate(t *testing.T) {
|
||||||
idA, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
idA, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
kademlia := NewKademliaTable(idA)
|
kademlia := NewKademliaTable(idA, "127.0.0.1", "5000")
|
||||||
|
|
||||||
lns := prepareTestListedNodes()
|
lns := prepareTestListedNodes()
|
||||||
for _, lnI := range lns {
|
for _, lnI := range lns {
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -10,13 +10,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
|
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "go-dht"
|
app.Name = "go-dht"
|
||||||
app.Version = "0.0.1-alpha"
|
app.Version = "0.0.1-alpha"
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
cli.StringFlag{Name: "config"},
|
cli.StringFlag{Name: "config"},
|
||||||
|
cli.BoolFlag{Name: "debug"},
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Commands = []cli.Command{}
|
app.Commands = []cli.Command{}
|
||||||
|
|||||||
37
node/node.go
37
node/node.go
@@ -1,11 +1,13 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go-dht/config"
|
"go-dht/config"
|
||||||
"go-dht/kademlia"
|
"go-dht/kademlia"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -22,7 +24,7 @@ func NewNode() (Node, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var n Node
|
var n Node
|
||||||
n.kademlia = kademlia.NewKademliaTable(id)
|
n.kademlia = kademlia.NewKademliaTable(id, config.C.Addr, config.C.Port)
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,15 +34,16 @@ func LoadNode(idStr string) (Node, error) {
|
|||||||
return Node{}, err
|
return Node{}, err
|
||||||
}
|
}
|
||||||
var n Node
|
var n Node
|
||||||
n.kademlia = kademlia.NewKademliaTable(id)
|
n.kademlia = kademlia.NewKademliaTable(id, config.C.Addr, config.C.Port)
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n Node) ID() kademlia.ID {
|
func (n Node) ID() kademlia.ID {
|
||||||
return n.kademlia.ID
|
return n.kademlia.N.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) Start() error {
|
func (n *Node) Start() error {
|
||||||
|
// rpc server
|
||||||
err := rpc.Register(n)
|
err := rpc.Register(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -50,11 +53,31 @@ func (n *Node) Start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// TMP in order to print the KBuckets of the node
|
||||||
|
for {
|
||||||
|
fmt.Println(n.kademlia)
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go n.pingKnownNodes(config.C.KnownNodes)
|
||||||
|
|
||||||
err = http.Serve(listener, nil)
|
err = http.Serve(listener, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO ping config.C.KnownNodes
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +91,11 @@ func (n *Node) Ping(ln kademlia.ListedNode, thisLn *kademlia.ListedNode) error {
|
|||||||
Addr: config.C.Addr,
|
Addr: config.C.Addr,
|
||||||
Port: config.C.Port,
|
Port: config.C.Port,
|
||||||
}
|
}
|
||||||
// TODO perform PONG call to the requester (maybe ping&pong can be unified)
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
run-dev-nodes.sh
Normal file
13
run-dev-nodes.sh
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
SESSION='go-dht'
|
||||||
|
|
||||||
|
tmux new-session -d -s $SESSION
|
||||||
|
tmux split-window -d -t 0 -v
|
||||||
|
tmux split-window -d -t 0 -h
|
||||||
|
|
||||||
|
|
||||||
|
tmux send-keys -t 0 'go run main.go --config config.test0.yaml --debug start' enter
|
||||||
|
sleep 2
|
||||||
|
tmux send-keys -t 1 'go run main.go --config config.test1.yaml --debug start' enter
|
||||||
|
tmux send-keys -t 2 'go run main.go --config config.test2.yaml --debug start' enter
|
||||||
|
|
||||||
|
tmux attach
|
||||||
Reference in New Issue
Block a user