p2p/discover: WIP

This commit is contained in:
Felix Lange 2016-05-06 10:21:37 +02:00
parent 80450e984b
commit 655bc19209
3 changed files with 116 additions and 60 deletions

View file

@ -63,6 +63,14 @@ type Network struct {
nursery []*Node nursery []*Node
nodes map[NodeID]*Node // tracks active nodes with state != known nodes map[NodeID]*Node // tracks active nodes with state != known
timeoutTimers map[timeoutEvent]*time.Timer timeoutTimers map[timeoutEvent]*time.Timer
// Revalidation queues.
// Nodes put on these queues will be pinged eventually.
slowRevalidateQueue []*Node
fastRevalidateQueue []*Node
// Buffers for state transition.
sendBuf []*ingressPacket
} }
// transport is implemented by the UDP transport. // transport is implemented by the UDP transport.
@ -73,6 +81,7 @@ type transport interface {
sendPong(remote *Node, pingHash []byte) sendPong(remote *Node, pingHash []byte)
sendFindnode(remote *Node, target NodeID) sendFindnode(remote *Node, target NodeID)
sendNeighbours(remote *Node, nodes []*Node) sendNeighbours(remote *Node, nodes []*Node)
localAddr() *net.UDPAddr localAddr() *net.UDPAddr
Close() Close()
} }
@ -272,15 +281,15 @@ loop:
// Ingress packet handling. // Ingress packet handling.
case pkt := <-net.read: case pkt := <-net.read:
node := net.getNode(pkt.remoteID, nil) n := net.internNode(&pkt)
prestate := node.state prestate := n.state
status := "ok" status := "ok"
if err := net.handle(node, pkt.ev, &pkt); err != nil { if err := net.handle(n, pkt.ev, &pkt); err != nil {
status = err.Error() status = err.Error()
} }
if glog.V(logger.Detail) { if glog.V(logger.Detail) {
glog.Infof("<<< (%d) %v from %x@%v: %v -> %v (%v)", glog.Infof("<<< (%d) %v from %x@%v: %v -> %v (%v)",
net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, node.state, status) net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status)
} }
// TODO: persist state if n.state goes >= known, delete if it goes <= known // TODO: persist state if n.state goes >= known, delete if it goes <= known
@ -354,37 +363,6 @@ loop:
// Everything below runs on the Network.loop goroutine // Everything below runs on the Network.loop goroutine
// and can modify Node, Table and Network at any time without locking. // and can modify Node, Table and Network at any time without locking.
func (net *Network) getNode(id NodeID, fromDB *Node) *Node {
if n := net.nodes[id]; n != nil {
return n
}
if fromDB == nil {
fromDB = &Node{ID: id}
}
if fromDB.state == nil {
fromDB.state = unknown
}
net.nodes[id] = fromDB
return net.nodes[id]
}
func (net *Network) nodeFromRPC(rn rpcNode) (n *Node, err error) {
n = net.nodes[rn.ID]
if n == nil {
// We haven't seen this node before.
n, err = nodeFromRPC(rn)
n.state = unknown
if err == nil {
net.nodes[n.ID] = n
}
return n, err
}
if !bytes.Equal(n.IP, rn.IP) || n.UDP != rn.UDP || n.TCP != rn.TCP {
err = fmt.Errorf("metadata mismatch: got %v, want %v", rn, n)
}
return n, err
}
func (net *Network) refresh(done chan<- struct{}) { func (net *Network) refresh(done chan<- struct{}) {
seeds := net.db.querySeeds(seedCount, seedMaxAge) seeds := net.db.querySeeds(seedCount, seedMaxAge)
if len(seeds) == 0 { if len(seeds) == 0 {
@ -400,7 +378,7 @@ func (net *Network) refresh(done chan<- struct{}) {
age := time.Since(net.db.lastPong(n.ID)) age := time.Since(net.db.lastPong(n.ID))
glog.Infof("seed node (age %v): %v", age, n) glog.Infof("seed node (age %v): %v", age, n)
} }
n = net.getNode(n.ID, n) n = net.internNodeFromDB(n)
if n.state == unknown { if n.state == unknown {
net.transition(n, verifyinit) net.transition(n, verifyinit)
} }
@ -415,6 +393,48 @@ func (net *Network) refresh(done chan<- struct{}) {
}() }()
} }
// Node Interning.
func (net *Network) internNode(pkt *ingressPacket) *Node {
if n := net.nodes[pkt.remoteID]; n != nil {
return n
}
n := NewNode(pkt.remoteID, pkt.remoteAddr.IP, uint16(pkt.remoteAddr.Port), uint16(pkt.remoteAddr.Port))
n.state = unknown
net.nodes[pkt.remoteID] = n
return n
}
func (net *Network) internNodeFromDB(dbn *Node) *Node {
if n := net.nodes[dbn.ID]; n != nil {
return n
}
n := NewNode(dbn.ID, dbn.IP, dbn.UDP, dbn.TCP)
n.state = unknown
net.nodes[n.ID] = n
return n
}
func (net *Network) internNodeFromNeighbours(rn rpcNode) (n *Node, err error) {
if rn.ID == net.tab.self.ID {
return nil, errors.New("is self")
}
n = net.nodes[rn.ID]
if n == nil {
// We haven't seen this node before.
n, err = nodeFromRPC(rn)
n.state = unknown
if err == nil {
net.nodes[n.ID] = n
}
return n, err
}
if !bytes.Equal(n.IP, rn.IP) || n.UDP != rn.UDP || n.TCP != rn.TCP {
err = fmt.Errorf("metadata mismatch: got %v, want %v", rn, n)
}
return n, err
}
// nodeNetGuts is embedded in Node and contains fields. // nodeNetGuts is embedded in Node and contains fields.
type nodeNetGuts struct { type nodeNetGuts struct {
// This is a cached copy of sha3(ID) which is used for node // This is a cached copy of sha3(ID) which is used for node
@ -430,6 +450,7 @@ type nodeNetGuts struct {
pingEcho []byte // hash of last ping sent by us pingEcho []byte // hash of last ping sent by us
deferredQueries []*findnodeQuery // queries that can't be sent yet deferredQueries []*findnodeQuery // queries that can't be sent yet
pendingNeighbours *findnodeQuery // current query, waiting for reply pendingNeighbours *findnodeQuery // current query, waiting for reply
queryTimeouts int
} }
func (n *nodeNetGuts) deferQuery(q *findnodeQuery) { func (n *nodeNetGuts) deferQuery(q *findnodeQuery) {
@ -475,7 +496,7 @@ type nodeEvent uint
//go:generate stringer -type=nodeEvent //go:generate stringer -type=nodeEvent
const ( const (
invalidEvent nodeEvent = iota // zero is 'reserved' invalidEvent nodeEvent = iota // zero is reserved
// Packet type events. // Packet type events.
// These correspond to packet types in the UDP protocol. // These correspond to packet types in the UDP protocol.
@ -530,13 +551,14 @@ func init() {
n.pendingNeighbours.reply <- nil n.pendingNeighbours.reply <- nil
n.pendingNeighbours = nil n.pendingNeighbours = nil
} }
n.queryTimeouts = 0
}, },
handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) {
switch ev { switch ev {
case pingPacket: case pingPacket:
net.conn.sendPong(n, pkt.hash) net.handlePing(n, pkt)
net.ping(n, pkt.remoteAddr) net.ping(n, pkt.remoteAddr)
return remoteverifywait, nil return verifywait, nil
default: default:
return unknown, errInvalidEvent return unknown, errInvalidEvent
} }
@ -551,7 +573,7 @@ func init() {
handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) {
switch ev { switch ev {
case pingPacket: case pingPacket:
net.conn.sendPong(n, pkt.hash) net.handlePing(n, pkt)
return verifywait, nil return verifywait, nil
case pongPacket: case pongPacket:
net.abortTimedEvent(n, pongTimeout) net.abortTimedEvent(n, pongTimeout)
@ -559,7 +581,7 @@ func init() {
case pongTimeout: case pongTimeout:
return unknown, nil return unknown, nil
default: default:
return unknown, errInvalidEvent return verifyinit, errInvalidEvent
} }
}, },
} }
@ -574,7 +596,7 @@ func init() {
case pongTimeout: case pongTimeout:
return unknown, nil return unknown, nil
default: default:
return unknown, errInvalidEvent return verifywait, errInvalidEvent
} }
}, },
} }
@ -601,6 +623,7 @@ func init() {
name: "known", name: "known",
canQuery: true, canQuery: true,
enter: func(net *Network, n *Node) { enter: func(net *Network, n *Node) {
n.queryTimeouts = 0
n.startNextQuery(net) n.startNextQuery(net)
// Insert into the table and start revalidation of the last node // Insert into the table and start revalidation of the last node
// in the bucket if it is full. // in the bucket if it is full.
@ -613,13 +636,10 @@ func init() {
handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) {
switch ev { switch ev {
case pingPacket: case pingPacket:
net.conn.sendPong(n, pkt.hash) net.handlePing(n, pkt)
return known, nil
case pingTimeout:
return known, nil return known, nil
case findnodePacket, neighborsPacket, neighboursTimeout: case findnodePacket, neighborsPacket, neighboursTimeout:
err := net.handleQueryEvent(n, ev, pkt) return net.handleQueryEvent(n, ev, pkt)
return known, err
default: default:
return known, errInvalidEvent return known, errInvalidEvent
} }
@ -639,13 +659,12 @@ func init() {
return known, nil return known, nil
case pongTimeout: case pongTimeout:
net.tab.deleteReplace(n) net.tab.deleteReplace(n)
return known, nil return unresponsive, nil
case pingPacket: case pingPacket:
net.conn.sendPong(n, pkt.hash) net.handlePing(n, pkt)
return contested, nil return contested, nil
case findnodePacket, neighborsPacket, neighboursTimeout: case findnodePacket, neighborsPacket, neighboursTimeout:
err := net.handleQueryEvent(n, ev, pkt) return net.handleQueryEvent(n, ev, pkt)
return contested, err
default: default:
return contested, errInvalidEvent return contested, errInvalidEvent
} }
@ -658,11 +677,10 @@ func init() {
handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) {
switch ev { switch ev {
case pingPacket: case pingPacket:
net.conn.sendPong(n, pkt.hash) net.handlePing(n, pkt)
return known, nil return known, nil
case findnodePacket, neighborsPacket, neighboursTimeout: case findnodePacket, neighborsPacket, neighboursTimeout:
err := net.handleQueryEvent(n, ev, pkt) return net.handleQueryEvent(n, ev, pkt)
return unresponsive, err
default: default:
return unresponsive, errInvalidEvent return unresponsive, errInvalidEvent
} }
@ -743,21 +761,31 @@ func (net *Network) ping(n *Node, addr *net.UDPAddr) {
net.timedEvent(respTimeout, n, pongTimeout) net.timedEvent(respTimeout, n, pongTimeout)
} }
func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) error { func (net *Network) handlePing(n *Node, pkt *ingressPacket) {
n.TCP = pkt.data.(*ping).From.TCP
net.conn.sendPong(n, pkt.hash)
}
func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) {
switch ev { switch ev {
case findnodePacket: case findnodePacket:
target := crypto.Keccak256Hash(pkt.data.(*findnode).Target[:]) target := crypto.Keccak256Hash(pkt.data.(*findnode).Target[:])
results := net.tab.closest(target, bucketSize).entries results := net.tab.closest(target, bucketSize).entries
net.conn.sendNeighbours(n, results) net.conn.sendNeighbours(n, results)
return nil return n.state, nil
case neighborsPacket: case neighborsPacket:
return net.handleNeighboursPacket(n, pkt.data.(*neighbors)) err := net.handleNeighboursPacket(n, pkt.data.(*neighbors))
return n.state, err
case neighboursTimeout: case neighboursTimeout:
if n.pendingNeighbours != nil { if n.pendingNeighbours != nil {
n.pendingNeighbours.reply <- nil n.pendingNeighbours.reply <- nil
n.pendingNeighbours = nil n.pendingNeighbours = nil
} }
return nil n.queryTimeouts++
if n.queryTimeouts > maxFindnodeFailures && n.state == known {
return contested, errors.New("too many timeouts")
}
return n.state, nil
default: default:
panic("oops") panic("oops")
} }
@ -771,7 +799,7 @@ func (net *Network) handleNeighboursPacket(n *Node, req *neighbors) error {
nodes := make([]*Node, len(req.Nodes)) nodes := make([]*Node, len(req.Nodes))
for i, rn := range req.Nodes { for i, rn := range req.Nodes {
nn, err := net.nodeFromRPC(rn) nn, err := net.internNodeFromNeighbours(rn)
if err != nil { if err != nil {
glog.V(logger.Debug).Infof("invalid neighbour from %x: %v", n.ID[:8], err) glog.V(logger.Debug).Infof("invalid neighbour from %x: %v", n.ID[:8], err)
continue continue

View file

@ -217,6 +217,34 @@ func MustParseNode(rawurl string) *Node {
return n return n
} }
// type nodeQueue []*Node
//
// // pushNew adds n to the end if it is not present.
// func (nl *nodeList) appendNew(n *Node) {
// for _, entry := range n {
// if entry == n {
// return
// }
// }
// *nq = append(*nq, n)
// }
//
// // popRandom removes a random node. Nodes closer to
// // to the head of the beginning of the have a slightly higher probability.
// func (nl *nodeList) popRandom() *Node {
// ix := rand.Intn(len(*nq))
// //TODO: probability as mentioned above.
// nl.removeIndex(ix)
// }
//
// func (nl *nodeList) removeIndex(i int) *Node {
// slice = *nl
// if len(*slice) <= i {
// return nil
// }
// *nl = append(slice[:i], slice[i+1:]...)
// }
const nodeIDBits = 512 const nodeIDBits = 512
// NodeID is a unique identifier for each node. // NodeID is a unique identifier for each node.

View file

@ -112,6 +112,7 @@ func (p *Peer) LocalAddr() net.Addr {
func (p *Peer) Disconnect(reason DiscReason) { func (p *Peer) Disconnect(reason DiscReason) {
select { select {
case p.disc <- reason: case p.disc <- reason:
glog.V(logger.Debug).Infof("%v: locally requested disconnect: %v\n", p, reason)
case <-p.closed: case <-p.closed:
} }
} }
@ -177,7 +178,6 @@ loop:
glog.V(logger.Debug).Infof("%v: protocol error: %v (%v)\n", p, err, reason) glog.V(logger.Debug).Infof("%v: protocol error: %v (%v)\n", p, err, reason)
break loop break loop
case reason = <-p.disc: case reason = <-p.disc:
glog.V(logger.Debug).Infof("%v: locally requested disconnect: %v\n", p, reason)
break loop break loop
} }
} }