diff --git a/router/connection.go b/router/connection.go index c5cae87a..99577e57 100644 --- a/router/connection.go +++ b/router/connection.go @@ -255,6 +255,7 @@ func (conn *LocalConnection) makeFeatures() map[string]string { "PeerNameFlavour": PeerNameFlavour, "Name": conn.local.Name.String(), "NickName": conn.local.NickName, + "ShortID": fmt.Sprint(conn.local.ShortID), "UID": fmt.Sprint(conn.local.UID), "ConnID": fmt.Sprint(conn.uid), } @@ -276,7 +277,7 @@ func (features features) Get(key string) string { } func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) { - if err := features.MustHave([]string{"PeerNameFlavour", "Name", "NickName", "UID", "ConnID"}); err != nil { + if err := features.MustHave([]string{"PeerNameFlavour", "Name", "NickName", "ShortID", "UID", "ConnID"}); err != nil { return nil, err } @@ -292,6 +293,12 @@ func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) { nickName := features.Get("NickName") + shortID, err := strconv.ParseUint(features.Get("ShortID"), 10, + PeerShortIDBits) + if err != nil { + return nil, err + } + uid, err := ParsePeerUID(features.Get("UID")) if err != nil { return nil, err @@ -303,7 +310,7 @@ func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) { } conn.uid ^= remoteConnID - return NewPeer(name, nickName, uid, 0), nil + return NewPeer(name, nickName, uid, 0, PeerShortID(shortID)), nil } func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) error { diff --git a/router/local_peer.go b/router/local_peer.go index 15076963..1d159221 100644 --- a/router/local_peer.go +++ b/router/local_peer.go @@ -19,7 +19,8 @@ type LocalPeerAction func() func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer { actionChan := make(chan LocalPeerAction, ChannelSize) peer := &LocalPeer{ - Peer: NewPeer(name, nickName, randomPeerUID(), 0), + Peer: NewPeer(name, nickName, randomPeerUID(), 0, + randomPeerShortID()), router: router, actionChan: actionChan, } @@ -205,7 +206,13 @@ func (peer *LocalPeer) handleDeleteConnection(conn Connection) { // helpers func (peer *LocalPeer) broadcastPeerUpdate(peers ...*Peer) { - peer.router.BroadcastTopologyUpdate(append(peers, peer.Peer)) + // Some tests run without a router. This should be fixed so + // that the relevant part of Router can be easily run in the + // context of a test, but that will involve significant + // reworking of tests. + if peer.router != nil { + peer.router.BroadcastTopologyUpdate(append(peers, peer.Peer)) + } } func (peer *LocalPeer) checkConnectionLimit() error { @@ -241,3 +248,10 @@ func (peer *LocalPeer) connectionCount() int { defer peer.RUnlock() return len(peer.connections) } + +func (peer *LocalPeer) setShortID(shortID PeerShortID) { + peer.Lock() + defer peer.Unlock() + peer.ShortID = shortID + peer.Version++ +} diff --git a/router/overlay.go b/router/overlay.go index b84db7d6..b552e179 100644 --- a/router/overlay.go +++ b/router/overlay.go @@ -15,6 +15,9 @@ type Overlay interface { // The routes have changed, so any cached information should // be discarded. InvalidateRoutes() + + // A mapping of a short id to a peer has changed + InvalidateShortIDs() } type ForwarderParams struct { @@ -90,6 +93,9 @@ func (NullOverlay) MakeForwarder(ForwarderParams) (OverlayForwarder, error) { func (NullOverlay) InvalidateRoutes() { } +func (NullOverlay) InvalidateShortIDs() { +} + func (NullOverlay) SetListener(OverlayForwarderListener) { } diff --git a/router/peer.go b/router/peer.go index 7dfd0857..82ae2201 100644 --- a/router/peer.go +++ b/router/peer.go @@ -17,11 +17,24 @@ func ParsePeerUID(s string) (PeerUID, error) { return PeerUID(uid), err } +// Short IDs exist for the sake of fast datapath. They are 12 bits, +// randomly assigned, but we detect and recover from collisions. This +// does limit us to 4096 peers, but that should be sufficient for a +// while. +type PeerShortID uint16 + +const PeerShortIDBits = 12 + +func randomPeerShortID() PeerShortID { + return PeerShortID(randUint16() & (1< entry.peer.Name) + } + } + + // Check that every peer was seen + for _, n := range counts { + require.Equal(t, 1, n) + } + + // Delete all the peers + shuffle() + for _, p := range ps { + peers.deleteByShortID(p, &pending) + } + + for _, entry := range peers.byShortID { + if entry.peer != peers.ourself.Peer { + require.Nil(t, entry.peer) + } + + require.Empty(t, entry.others) + } +} + +// Test the easy case of short id reassignment, when few short ids are taken +func TestShortIDReassignmentEasy(t *testing.T) { + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + _, peers := newNode(PeerName(0)) + + for i := 1; i <= 10; i++ { + peers.FetchWithDefault(NewPeer(PeerName(i), "", PeerUID(i), 0, + PeerShortID(rng.Intn(1<