diff --git a/internal/kadtest/ids.go b/internal/kadtest/ids.go index 6ce09dd..9fe351f 100644 --- a/internal/kadtest/ids.go +++ b/internal/kadtest/ids.go @@ -2,7 +2,6 @@ package kadtest import ( "crypto/sha256" - "net" "github.com/plprobelab/go-kademlia/kad" "github.com/plprobelab/go-kademlia/key" @@ -20,8 +19,8 @@ var _ kad.NodeID[key.Key8] = (*ID[key.Key8])(nil) // NewID returns a new Kademlia identifier that implements the NodeID interface. // Instead of deriving the Kademlia key from a NodeID, this method directly takes // the Kademlia key. -func NewID[K kad.Key[K]](k K) *ID[K] { - return &ID[K]{key: k} +func NewID[K kad.Key[K]](k K) ID[K] { + return ID[K]{key: k} } // Key returns the Kademlia key that is used by, e.g., the routing table @@ -65,43 +64,3 @@ func (s StringID) Equal(other string) bool { func (s StringID) String() string { return string(s) } - -type Info[K kad.Key[K], A kad.Address[A]] struct { - id *ID[K] - addrs []A -} - -var _ kad.NodeInfo[key.Key8, net.IP] = (*Info[key.Key8, net.IP])(nil) - -func NewInfo[K kad.Key[K], A kad.Address[A]](id *ID[K], addrs []A) *Info[K, A] { - return &Info[K, A]{ - id: id, - addrs: addrs, - } -} - -func (a *Info[K, A]) AddAddr(addr A) { - a.addrs = append(a.addrs, addr) -} - -func (a *Info[K, A]) RemoveAddr(addr A) { - writeIndex := 0 - // remove all occurrences of addr - for _, ad := range a.addrs { - if !ad.Equal(addr) { - a.addrs[writeIndex] = ad - writeIndex++ - } - } - a.addrs = a.addrs[:writeIndex] -} - -func (a *Info[K, A]) ID() kad.NodeID[K] { - return a.id -} - -func (a *Info[K, A]) Addresses() []A { - addresses := make([]A, len(a.addrs)) - copy(addresses, a.addrs) - return addresses -} diff --git a/internal/kadtest/message.go b/internal/kadtest/message.go index 05071ce..c4f7a86 100644 --- a/internal/kadtest/message.go +++ b/internal/kadtest/message.go @@ -5,77 +5,70 @@ import ( "github.com/plprobelab/go-kademlia/key" ) -// StrAddr is a simple implementation of kad.Address that uses a string to represent the address. -type StrAddr string - -var _ kad.Address[StrAddr] = StrAddr("") - -func (a StrAddr) Equal(b StrAddr) bool { return a == b } - -type Request[K kad.Key[K]] struct { +type Request[K kad.Key[K], N kad.NodeID[K]] struct { target K id string } -func NewRequest[K kad.Key[K]](id string, target K) *Request[K] { - return &Request[K]{ +func NewRequest[K kad.Key[K], N kad.NodeID[K]](id string, target K) *Request[K, N] { + return &Request[K, N]{ target: target, id: id, } } -func (r *Request[K]) Target() K { +func (r *Request[K, N]) Target() K { return r.target } -func (r *Request[K]) ID() string { +func (r *Request[K, N]) ID() string { return r.id } -func (r *Request[K]) EmptyResponse() kad.Response[K, StrAddr] { - return &Response[K]{} +func (r *Request[K, N]) EmptyResponse() kad.Response[K, N] { + return &Response[K, N]{} } -type Response[K kad.Key[K]] struct { +type Response[K kad.Key[K], N kad.NodeID[K]] struct { id string - closer []kad.NodeInfo[K, StrAddr] + closer []N } -func NewResponse[K kad.Key[K]](id string, closer []kad.NodeInfo[K, StrAddr]) *Response[K] { - return &Response[K]{ +func NewResponse[K kad.Key[K], N kad.NodeID[K]](id string, closer []N) *Response[K, N] { + return &Response[K, N]{ id: id, closer: closer, } } -func (r *Response[K]) ID() string { +func (r *Response[K, N]) ID() string { return r.id } -func (r *Response[K]) CloserNodes() []kad.NodeInfo[K, StrAddr] { +func (r *Response[K, N]) CloserNodes() []N { return r.closer } type ( // Request8 is a Request message that uses key.Key8 - Request8 = Request[key.Key8] + Request8 = Request[key.Key8, ID[key.Key8]] // Response8 is a Response message that uses key.Key8 - Response8 = Response[key.Key8] + Response8 = Response[key.Key8, ID[key.Key8]] // Request8 is a Request message that uses key.Key256 - Request256 = Request[key.Key256] + Request256 = Request[key.Key256, ID[key.Key256]] // Response256 is a Response message that uses key.Key256 - Response256 = Response[key.Key256] + Response256 = Response[key.Key256, ID[key.Key256]] ) var ( - _ kad.Request[key.Key8, StrAddr] = (*Request8)(nil) - _ kad.Response[key.Key8, StrAddr] = (*Response8)(nil) + _ kad.Request[key.Key8, ID[key.Key8]] = (*Request8)(nil) + _ kad.Response[key.Key8, ID[key.Key8]] = (*Response8)(nil) ) var ( - _ kad.Request[key.Key256, StrAddr] = (*Request256)(nil) - _ kad.Response[key.Key256, StrAddr] = (*Response256)(nil) + _ kad.Request[key.Key256, ID[key.Key256]] = (*Request256)(nil) + _ kad.Response[key.Key256, ID[key.Key256]] = (*Response256)(nil) ) diff --git a/kad/kad.go b/kad/kad.go index 815df3a..93ba724 100644 --- a/kad/kad.go +++ b/kad/kad.go @@ -1,9 +1,5 @@ package kad -import ( - "context" -) - // Key is the interface all Kademlia key types support. // // A Kademlia key is defined as a bit string of arbitrary size. In practice, different Kademlia implementations use @@ -96,24 +92,6 @@ type NodeID[K Key[K]] interface { String() string } -// NodeInfo is a container type that combines node identification information -// and network addresses at which the node is reachable. -type NodeInfo[K Key[K], A Address[A]] interface { - // ID returns the node identifier. - ID() NodeID[K] - - // Addresses returns the network addresses associated with the given node. - Addresses() []A -} - -// Address is an interface that any type must implement that can be used -// to address a node in the DHT network. This can be an IP/Port combination -// or in the case of libp2p a Multiaddress. -type Address[T any] interface { - // Equal re - Equal(T) bool -} - // Equal checks the equality of two NodeIDs. // TODO: move somewhere else. func Equal[K Key[K]](this, that NodeID[K]) bool { @@ -122,7 +100,7 @@ func Equal[K Key[K]](this, that NodeID[K]) bool { type Message interface{} -type Request[K Key[K], A Address[A]] interface { +type Request[K Key[K], N NodeID[K]] interface { Message // Target returns the target key and true, or false if no target key has been specfied. @@ -130,23 +108,13 @@ type Request[K Key[K], A Address[A]] interface { // EmptyResponse returns an empty response struct for this request message // TODO: this is a weird patter, let's try to remove this. - EmptyResponse() Response[K, A] + EmptyResponse() Response[K, N] } -type Response[K Key[K], A Address[A]] interface { +type Response[K Key[K], N NodeID[K]] interface { Message - CloserNodes() []NodeInfo[K, A] -} - -type RoutingProtocol[K Key[K], N NodeID[K], A Address[A]] interface { - FindNode(ctx context.Context, to N, target K) (NodeInfo[K, A], []N, error) - Ping(ctx context.Context, to N) error -} - -type RecordProtocol[K Key[K], N NodeID[K]] interface { - Get(ctx context.Context, to N, target K) ([]Record, []N, error) - Put(ctx context.Context, to N, record Record) error + CloserNodes() []N } type Record any diff --git a/network/endpoint/endpoint.go b/network/endpoint/endpoint.go index 3724bb4..0caa2fe 100644 --- a/network/endpoint/endpoint.go +++ b/network/endpoint/endpoint.go @@ -33,14 +33,14 @@ type RequestHandlerFn[K kad.Key[K]] func(context.Context, kad.NodeID[K], // ResponseHandlerFn defines a function that deals with the response to a // request previously sent to a remote peer. -type ResponseHandlerFn[K kad.Key[K], A kad.Address[A]] func(context.Context, kad.Response[K, A], error) +type ResponseHandlerFn[K kad.Key[K], N kad.NodeID[K]] func(context.Context, kad.Response[K, N], error) // Endpoint defines how Kademlia nodes interacts with each other. -type Endpoint[K kad.Key[K], A kad.Address[A]] interface { +type Endpoint[K kad.Key[K], N kad.NodeID[K]] interface { // MaybeAddToPeerstore adds the given address to the peerstore if it is // valid and if it is not already there. // TODO: consider returning a status of whether the nodeinfo is a new node or contains a new address - MaybeAddToPeerstore(context.Context, kad.NodeInfo[K, A], time.Duration) error + MaybeAddToPeerstore(context.Context, N, time.Duration) error // SendRequestHandleResponse attempts to sends a request to the given peer and handles // the response with the given handler. @@ -48,16 +48,16 @@ type Endpoint[K kad.Key[K], A kad.Address[A]] interface { // any reason. The handler will not be called if an error is returned. SendRequestHandleResponse(context.Context, address.ProtocolID, kad.NodeID[K], kad.Message, kad.Message, time.Duration, - ResponseHandlerFn[K, A]) error + ResponseHandlerFn[K, N]) error // NetworkAddress returns the network address of the given peer (if known). - NetworkAddress(kad.NodeID[K]) (kad.NodeInfo[K, A], error) + NetworkAddress(kad.NodeID[K]) (N, error) } // ServerEndpoint is a Kademlia endpoint that can handle requests from remote // peers. -type ServerEndpoint[K kad.Key[K], A kad.Address[A]] interface { - Endpoint[K, A] +type ServerEndpoint[K kad.Key[K], N kad.NodeID[K]] interface { + Endpoint[K, N] // AddRequestHandler registers a handler for a given protocol ID. AddRequestHandler(address.ProtocolID, kad.Message, RequestHandlerFn[K]) error // RemoveRequestHandler removes a handler for a given protocol ID. @@ -66,8 +66,8 @@ type ServerEndpoint[K kad.Key[K], A kad.Address[A]] interface { // NetworkedEndpoint is an endpoint keeping track of the connectedness with // known remote peers. -type NetworkedEndpoint[K kad.Key[K], A kad.Address[A]] interface { - Endpoint[K, A] +type NetworkedEndpoint[K kad.Key[K], N kad.NodeID[K]] interface { + Endpoint[K, N] // Connectedness returns the connectedness of the given peer. Connectedness(kad.NodeID[K]) (Connectedness, error) } diff --git a/query/iter.go b/query/iter.go index eaa11b9..52960de 100644 --- a/query/iter.go +++ b/query/iter.go @@ -9,47 +9,45 @@ import ( ) // A NodeIter iterates nodes according to some strategy. -type NodeIter[K kad.Key[K]] interface { +type NodeIter[K kad.Key[K], N kad.NodeID[K]] interface { // Add adds node information to the iterator - Add(*NodeStatus[K]) + Add(*NodeStatus[K, N]) // Find returns the node information corresponding to the given Kademlia key - Find(K) (*NodeStatus[K], bool) + Find(K) (*NodeStatus[K, N], bool) // Each applies fn to each entry in the iterator in order. Each stops and returns true if fn returns true. // Otherwise Each returns false when there are no further entries. - Each(ctx context.Context, fn func(context.Context, *NodeStatus[K]) bool) bool + Each(ctx context.Context, fn func(context.Context, *NodeStatus[K, N]) bool) bool } // A ClosestNodesIter iterates nodes in order of ascending distance from a key. -type ClosestNodesIter[K kad.Key[K]] struct { +type ClosestNodesIter[K kad.Key[K], N kad.NodeID[K]] struct { // target is the key whose distance to a node determines the position of that node in the iterator. target K // nodelist holds the nodes discovered so far, ordered by increasing distance from the target. - nodes *trie.Trie[K, *NodeStatus[K]] + nodes *trie.Trie[K, *NodeStatus[K, N]] } -var _ NodeIter[key.Key8] = (*ClosestNodesIter[key.Key8])(nil) - // NewClosestNodesIter creates a new ClosestNodesIter -func NewClosestNodesIter[K kad.Key[K]](target K) *ClosestNodesIter[K] { - return &ClosestNodesIter[K]{ +func NewClosestNodesIter[K kad.Key[K], N kad.NodeID[K]](target K) *ClosestNodesIter[K, N] { + return &ClosestNodesIter[K, N]{ target: target, - nodes: trie.New[K, *NodeStatus[K]](), + nodes: trie.New[K, *NodeStatus[K, N]](), } } -func (iter *ClosestNodesIter[K]) Add(ni *NodeStatus[K]) { +func (iter *ClosestNodesIter[K, N]) Add(ni *NodeStatus[K, N]) { iter.nodes.Add(ni.NodeID.Key(), ni) } -func (iter *ClosestNodesIter[K]) Find(k K) (*NodeStatus[K], bool) { +func (iter *ClosestNodesIter[K, N]) Find(k K) (*NodeStatus[K, N], bool) { found, ni := trie.Find(iter.nodes, k) return ni, found } -func (iter *ClosestNodesIter[K]) Each(ctx context.Context, fn func(context.Context, *NodeStatus[K]) bool) bool { +func (iter *ClosestNodesIter[K, N]) Each(ctx context.Context, fn func(context.Context, *NodeStatus[K, N]) bool) bool { // get all the nodes in order of distance from the target // TODO: turn this into a walk or iterator on trie.Trie entries := trie.Closest(iter.nodes, iter.target, iter.nodes.Size()) @@ -63,27 +61,25 @@ func (iter *ClosestNodesIter[K]) Each(ctx context.Context, fn func(context.Conte } // A SequentialIter iterates nodes in the order they were added to the iterator. -type SequentialIter[K kad.Key[K]] struct { +type SequentialIter[K kad.Key[K], N kad.NodeID[K]] struct { // nodelist holds the nodes discovered so far, ordered by increasing distance from the target. - nodes []*NodeStatus[K] + nodes []*NodeStatus[K, N] } -var _ NodeIter[key.Key8] = (*SequentialIter[key.Key8])(nil) - // NewSequentialIter creates a new SequentialIter -func NewSequentialIter[K kad.Key[K]]() *SequentialIter[K] { - return &SequentialIter[K]{ - nodes: make([]*NodeStatus[K], 0), +func NewSequentialIter[K kad.Key[K], N kad.NodeID[K]]() *SequentialIter[K, N] { + return &SequentialIter[K, N]{ + nodes: make([]*NodeStatus[K, N], 0), } } -func (iter *SequentialIter[K]) Add(ni *NodeStatus[K]) { +func (iter *SequentialIter[K, N]) Add(ni *NodeStatus[K, N]) { iter.nodes = append(iter.nodes, ni) } // Find returns the node information corresponding to the given Kademlia key. It uses a linear // search which makes it unsuitable for large numbers of entries. -func (iter *SequentialIter[K]) Find(k K) (*NodeStatus[K], bool) { +func (iter *SequentialIter[K, N]) Find(k K) (*NodeStatus[K, N], bool) { for i := range iter.nodes { if key.Equal(k, iter.nodes[i].NodeID.Key()) { return iter.nodes[i], true @@ -93,7 +89,7 @@ func (iter *SequentialIter[K]) Find(k K) (*NodeStatus[K], bool) { return nil, false } -func (iter *SequentialIter[K]) Each(ctx context.Context, fn func(context.Context, *NodeStatus[K]) bool) bool { +func (iter *SequentialIter[K, N]) Each(ctx context.Context, fn func(context.Context, *NodeStatus[K, N]) bool) bool { for _, ns := range iter.nodes { if fn(ctx, ns) { return true diff --git a/query/iter_test.go b/query/iter_test.go index 6cecb29..fffce41 100644 --- a/query/iter_test.go +++ b/query/iter_test.go @@ -10,6 +10,11 @@ import ( "github.com/plprobelab/go-kademlia/key" ) +var ( + _ NodeIter[key.Key8, kadtest.ID[key.Key8]] = (*ClosestNodesIter[key.Key8, kadtest.ID[key.Key8]])(nil) + _ NodeIter[key.Key8, kadtest.ID[key.Key8]] = (*SequentialIter[key.Key8, kadtest.ID[key.Key8]])(nil) +) + func TestClosestNodesIter(t *testing.T) { target := key.Key8(0b00000001) a := kadtest.NewID(key.Key8(0b00000100)) // 4 @@ -22,19 +27,19 @@ func TestClosestNodesIter(t *testing.T) { require.True(t, target.Xor(b.Key()).Compare(target.Xor(c.Key())) == -1) require.True(t, target.Xor(c.Key()).Compare(target.Xor(d.Key())) == -1) - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) // add nodes in "random order" - iter.Add(&NodeStatus[key.Key8]{NodeID: b}) - iter.Add(&NodeStatus[key.Key8]{NodeID: d}) - iter.Add(&NodeStatus[key.Key8]{NodeID: a}) - iter.Add(&NodeStatus[key.Key8]{NodeID: c}) + iter.Add(&NodeStatus[key.Key8, kadtest.ID[key.Key8]]{NodeID: b}) + iter.Add(&NodeStatus[key.Key8, kadtest.ID[key.Key8]]{NodeID: d}) + iter.Add(&NodeStatus[key.Key8, kadtest.ID[key.Key8]]{NodeID: a}) + iter.Add(&NodeStatus[key.Key8, kadtest.ID[key.Key8]]{NodeID: c}) // Each should iterate in order of distance from target distances := make([]key.Key8, 0, 4) - iter.Each(context.Background(), func(ctx context.Context, ns *NodeStatus[key.Key8]) bool { + iter.Each(context.Background(), func(ctx context.Context, ns *NodeStatus[key.Key8, kadtest.ID[key.Key8]]) bool { distances = append(distances, target.Xor(ns.NodeID.Key())) return false }) @@ -48,19 +53,19 @@ func TestSequentialIter(t *testing.T) { c := kadtest.NewID(key.Key8(0b00010000)) // 16 d := kadtest.NewID(key.Key8(0b00100000)) // 32 - iter := NewSequentialIter[key.Key8]() + iter := NewSequentialIter[key.Key8, kadtest.ID[key.Key8]]() // add nodes in "random order" - iter.Add(&NodeStatus[key.Key8]{NodeID: b}) - iter.Add(&NodeStatus[key.Key8]{NodeID: d}) - iter.Add(&NodeStatus[key.Key8]{NodeID: a}) - iter.Add(&NodeStatus[key.Key8]{NodeID: c}) + iter.Add(&NodeStatus[key.Key8, kadtest.ID[key.Key8]]{NodeID: b}) + iter.Add(&NodeStatus[key.Key8, kadtest.ID[key.Key8]]{NodeID: d}) + iter.Add(&NodeStatus[key.Key8, kadtest.ID[key.Key8]]{NodeID: a}) + iter.Add(&NodeStatus[key.Key8, kadtest.ID[key.Key8]]{NodeID: c}) // Each should iterate in order the nodes were added to the iiterator order := make([]key.Key8, 0, 4) - iter.Each(context.Background(), func(ctx context.Context, ns *NodeStatus[key.Key8]) bool { + iter.Each(context.Background(), func(ctx context.Context, ns *NodeStatus[key.Key8, kadtest.ID[key.Key8]]) bool { order = append(order, ns.NodeID.Key()) return false }) diff --git a/query/node.go b/query/node.go index 0fd5960..7540ace 100644 --- a/query/node.go +++ b/query/node.go @@ -6,8 +6,8 @@ import ( "github.com/plprobelab/go-kademlia/kad" ) -type NodeStatus[K kad.Key[K]] struct { - NodeID kad.NodeID[K] +type NodeStatus[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N State NodeState } diff --git a/query/pool.go b/query/pool.go index 7e6a469..9e86d0e 100644 --- a/query/pool.go +++ b/query/pool.go @@ -13,11 +13,11 @@ import ( "github.com/plprobelab/go-kademlia/util" ) -type Pool[K kad.Key[K], A kad.Address[A]] struct { +type Pool[K kad.Key[K], N kad.NodeID[K]] struct { // self is the node id of the system the pool is running on - self kad.NodeID[K] - queries []*Query[K, A] - queryIndex map[QueryID]*Query[K, A] + self N + queries []*Query[K, N] + queryIndex map[QueryID]*Query[K, N] // cfg is a copy of the optional configuration supplied to the pool cfg PoolConfig @@ -93,23 +93,23 @@ func DefaultPoolConfig() *PoolConfig { } } -func NewPool[K kad.Key[K], A kad.Address[A]](self kad.NodeID[K], cfg *PoolConfig) (*Pool[K, A], error) { +func NewPool[K kad.Key[K], N kad.NodeID[K]](self N, cfg *PoolConfig) (*Pool[K, N], error) { if cfg == nil { cfg = DefaultPoolConfig() } else if err := cfg.Validate(); err != nil { return nil, err } - return &Pool[K, A]{ + return &Pool[K, N]{ self: self, cfg: *cfg, - queries: make([]*Query[K, A], 0), - queryIndex: make(map[QueryID]*Query[K, A]), + queries: make([]*Query[K, N], 0), + queryIndex: make(map[QueryID]*Query[K, N]), }, nil } // Advance advances the state of the pool by attempting to advance one of its queries -func (p *Pool[K, A]) Advance(ctx context.Context, ev PoolEvent) PoolState { +func (p *Pool[K, N]) Advance(ctx context.Context, ev PoolEvent) PoolState { ctx, span := util.StartSpan(ctx, "Pool.Advance") defer span.End() @@ -121,7 +121,7 @@ func (p *Pool[K, A]) Advance(ctx context.Context, ev PoolEvent) PoolState { eventQueryID := InvalidQueryID switch tev := ev.(type) { - case *EventPoolAddQuery[K, A]: + case *EventPoolAddQuery[K, N]: p.addQuery(ctx, tev.QueryID, tev.Target, tev.ProtocolID, tev.Message, tev.KnownClosestNodes) // TODO: return error as state case *EventPoolStopQuery: @@ -132,9 +132,9 @@ func (p *Pool[K, A]) Advance(ctx context.Context, ev PoolEvent) PoolState { } eventQueryID = qry.id } - case *EventPoolMessageResponse[K, A]: + case *EventPoolMessageResponse[K, N]: if qry, ok := p.queryIndex[tev.QueryID]; ok { - state, terminal := p.advanceQuery(ctx, qry, &EventQueryMessageResponse[K, A]{ + state, terminal := p.advanceQuery(ctx, qry, &EventQueryMessageResponse[K, N]{ NodeID: tev.NodeID, Response: tev.Response, }) @@ -143,9 +143,9 @@ func (p *Pool[K, A]) Advance(ctx context.Context, ev PoolEvent) PoolState { } eventQueryID = qry.id } - case *EventPoolMessageFailure[K]: + case *EventPoolMessageFailure[K, N]: if qry, ok := p.queryIndex[tev.QueryID]; ok { - state, terminal := p.advanceQuery(ctx, qry, &EventQueryMessageFailure[K]{ + state, terminal := p.advanceQuery(ctx, qry, &EventQueryMessageFailure[K, N]{ NodeID: tev.NodeID, Error: tev.Error, }) @@ -189,12 +189,12 @@ func (p *Pool[K, A]) Advance(ctx context.Context, ev PoolEvent) PoolState { return &StatePoolIdle{} } -func (p *Pool[K, A]) advanceQuery(ctx context.Context, qry *Query[K, A], qev QueryEvent) (PoolState, bool) { +func (p *Pool[K, N]) advanceQuery(ctx context.Context, qry *Query[K, N], qev QueryEvent) (PoolState, bool) { state := qry.Advance(ctx, qev) switch st := state.(type) { - case *StateQueryWaitingMessage[K, A]: + case *StateQueryWaitingMessage[K, N]: p.queriesInFlight++ - return &StatePoolQueryMessage[K, A]{ + return &StatePoolQueryMessage[K, N]{ QueryID: st.QueryID, Stats: st.Stats, NodeID: st.NodeID, @@ -231,7 +231,7 @@ func (p *Pool[K, A]) advanceQuery(ctx context.Context, qry *Query[K, A], qev Que return nil, false } -func (p *Pool[K, A]) removeQuery(queryID QueryID) { +func (p *Pool[K, N]) removeQuery(queryID QueryID) { for i := range p.queries { if p.queries[i].id != queryID { continue @@ -247,18 +247,18 @@ func (p *Pool[K, A]) removeQuery(queryID QueryID) { // addQuery adds a query to the pool, returning the new query id // TODO: remove target argument and use msg.Target -func (p *Pool[K, A]) addQuery(ctx context.Context, queryID QueryID, target K, protocolID address.ProtocolID, msg kad.Request[K, A], knownClosestNodes []kad.NodeID[K]) error { +func (p *Pool[K, N]) addQuery(ctx context.Context, queryID QueryID, target K, protocolID address.ProtocolID, msg kad.Request[K, N], knownClosestNodes []N) error { if _, exists := p.queryIndex[queryID]; exists { return fmt.Errorf("query id already in use") } - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[K, N](target) qryCfg := DefaultQueryConfig[K]() qryCfg.Clock = p.cfg.Clock qryCfg.Concurrency = p.cfg.QueryConcurrency qryCfg.RequestTimeout = p.cfg.RequestTimeout - qry, err := NewQuery[K](p.self, queryID, protocolID, msg, iter, knownClosestNodes, qryCfg) + qry, err := NewQuery[K, N](p.self, queryID, protocolID, msg, iter, knownClosestNodes, qryCfg) if err != nil { return fmt.Errorf("new query: %w", err) } @@ -279,11 +279,11 @@ type PoolState interface { type StatePoolIdle struct{} // StatePoolQueryMessage indicates that a pool query is waiting to message a node. -type StatePoolQueryMessage[K kad.Key[K], A kad.Address[A]] struct { +type StatePoolQueryMessage[K kad.Key[K], N kad.NodeID[K]] struct { QueryID QueryID - NodeID kad.NodeID[K] + NodeID N ProtocolID address.ProtocolID - Message kad.Request[K, A] + Message kad.Request[K, N] Stats QueryStats } @@ -309,7 +309,7 @@ type StatePoolQueryTimeout struct { // poolState() ensures that only Pool states can be assigned to the PoolState interface. func (*StatePoolIdle) poolState() {} -func (*StatePoolQueryMessage[K, A]) poolState() {} +func (*StatePoolQueryMessage[K, N]) poolState() {} func (*StatePoolWaitingAtCapacity) poolState() {} func (*StatePoolWaitingWithCapacity) poolState() {} func (*StatePoolQueryFinished) poolState() {} @@ -321,12 +321,12 @@ type PoolEvent interface { } // EventPoolAddQuery is an event that attempts to add a new query -type EventPoolAddQuery[K kad.Key[K], A kad.Address[A]] struct { +type EventPoolAddQuery[K kad.Key[K], N kad.NodeID[K]] struct { QueryID QueryID // the id to use for the new query Target K // the target key for the query ProtocolID address.ProtocolID // the protocol that defines how the message should be interpreted - Message kad.Request[K, A] // the message the query should send to each node it traverses - KnownClosestNodes []kad.NodeID[K] // an initial set of close nodes the query should use + Message kad.Request[K, N] // the message the query should send to each node it traverses + KnownClosestNodes []N // an initial set of close nodes the query should use } // EventPoolStopQuery notifies a pool to stop a query. @@ -335,25 +335,25 @@ type EventPoolStopQuery struct { } // EventPoolMessageResponse notifies a pool that a query that a sent message has received a successful response. -type EventPoolMessageResponse[K kad.Key[K], A kad.Address[A]] struct { +type EventPoolMessageResponse[K kad.Key[K], N kad.NodeID[K]] struct { QueryID QueryID // the id of the query that sent the message - NodeID kad.NodeID[K] // the node the message was sent to - Response kad.Response[K, A] // the message response sent by the node + NodeID N // the node the message was sent to + Response kad.Response[K, N] // the message response sent by the node } // EventPoolMessageFailure notifies a pool that a query that an attempt to send a message has failed. -type EventPoolMessageFailure[K kad.Key[K]] struct { - QueryID QueryID // the id of the query that sent the message - NodeID kad.NodeID[K] // the node the message was sent to - Error error // the error that caused the failure, if any +type EventPoolMessageFailure[K kad.Key[K], N kad.NodeID[K]] struct { + QueryID QueryID // the id of the query that sent the message + NodeID N // the node the message was sent to + Error error // the error that caused the failure, if any } // EventPoolPoll is an event that signals the pool that it can perform housekeeping work such as time out queries. type EventPoolPoll struct{} // poolEvent() ensures that only Pool events can be assigned to the PoolEvent interface. -func (*EventPoolAddQuery[K, A]) poolEvent() {} +func (*EventPoolAddQuery[K, N]) poolEvent() {} func (*EventPoolStopQuery) poolEvent() {} -func (*EventPoolMessageResponse[K, A]) poolEvent() {} -func (*EventPoolMessageFailure[K]) poolEvent() {} +func (*EventPoolMessageResponse[K, N]) poolEvent() {} +func (*EventPoolMessageFailure[K, N]) poolEvent() {} func (*EventPoolPoll) poolEvent() {} diff --git a/query/pool_test.go b/query/pool_test.go index 3e8da96..f72b53f 100644 --- a/query/pool_test.go +++ b/query/pool_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "github.com/plprobelab/go-kademlia/internal/kadtest" - "github.com/plprobelab/go-kademlia/kad" "github.com/plprobelab/go-kademlia/key" "github.com/plprobelab/go-kademlia/network/address" ) @@ -73,7 +72,7 @@ func TestPoolStartsIdle(t *testing.T) { cfg.Clock = clk self := kadtest.NewID(key.Key8(0)) - p, err := NewPool[key.Key8, kadtest.StrAddr](self, cfg) + p, err := NewPool[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) state := p.Advance(ctx, &EventPoolPoll{}) @@ -87,7 +86,7 @@ func TestPoolStopWhenNoQueries(t *testing.T) { cfg.Clock = clk self := kadtest.NewID(key.Key8(0)) - p, err := NewPool[key.Key8, kadtest.StrAddr](self, cfg) + p, err := NewPool[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) state := p.Advance(ctx, &EventPoolPoll{}) @@ -101,28 +100,28 @@ func TestPoolAddQueryStartsIfCapacity(t *testing.T) { cfg.Clock = clk self := kadtest.NewID(key.Key8(0)) - p, err := NewPool[key.Key8, kadtest.StrAddr](self, cfg) + p, err := NewPool[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) target := key.Key8(0b00000001) a := kadtest.NewID(key.Key8(0b00000100)) // 4 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") // first thing the new pool should do is start the query - state := p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.StrAddr]{ + state := p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID, Target: target, ProtocolID: protocolID, Message: msg, - KnownClosestNodes: []kad.NodeID[key.Key8]{a}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a}, }) - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // the query should attempt to contact the node it was given - st := state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) // the query should be the one just added require.Equal(t, queryID, st.QueryID) @@ -148,33 +147,33 @@ func TestPoolMessageResponse(t *testing.T) { cfg.Clock = clk self := kadtest.NewID(key.Key8(0)) - p, err := NewPool[key.Key8, kadtest.StrAddr](self, cfg) + p, err := NewPool[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) target := key.Key8(0b00000001) a := kadtest.NewID(key.Key8(0b00000100)) // 4 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") // first thing the new pool should do is start the query - state := p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.StrAddr]{ + state := p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID, Target: target, ProtocolID: protocolID, Message: msg, - KnownClosestNodes: []kad.NodeID[key.Key8]{a}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a}, }) - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // the query should attempt to contact the node it was given - st := state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID, st.QueryID) require.Equal(t, a, st.NodeID) // notify query that node was contacted successfully, but no closer nodes - state = p.Advance(ctx, &EventPoolMessageResponse[key.Key8, kadtest.StrAddr]{ + state = p.Advance(ctx, &EventPoolMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID, NodeID: a, }) @@ -196,7 +195,7 @@ func TestPoolPrefersRunningQueriesOverNewOnes(t *testing.T) { cfg.Concurrency = 2 // allow two queries to run concurrently self := kadtest.NewID(key.Key8(0)) - p, err := NewPool[key.Key8, kadtest.StrAddr](self, cfg) + p, err := NewPool[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) target := key.Key8(0b00000001) @@ -205,79 +204,79 @@ func TestPoolPrefersRunningQueriesOverNewOnes(t *testing.T) { c := kadtest.NewID(key.Key8(0b00010000)) // 16 d := kadtest.NewID(key.Key8(0b00100000)) // 32 - msg1 := kadtest.NewRequest("1", target) + msg1 := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID1 := QueryID("1") protocolID := address.ProtocolID("testprotocol") // Add the first query - state := p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.StrAddr]{ + state := p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID1, Target: target, ProtocolID: protocolID, Message: msg1, - KnownClosestNodes: []kad.NodeID[key.Key8]{a, b, c, d}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a, b, c, d}, }) - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // the first query should attempt to contact the node it was given - st := state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID1, st.QueryID) require.Equal(t, a, st.NodeID) - msg2 := kadtest.NewRequest("2", target) + msg2 := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("2", target) queryID2 := QueryID("2") // Add the second query - state = p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.StrAddr]{ + state = p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID2, Target: target, ProtocolID: protocolID, Message: msg2, - KnownClosestNodes: []kad.NodeID[key.Key8]{a, b, c, d}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a, b, c, d}, }) // the first query should continue its operation in preference to starting the new query - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID1, st.QueryID) require.Equal(t, b, st.NodeID) // advance the pool again, the first query should continue its operation in preference to starting the new query state = p.Advance(ctx, &EventPoolPoll{}) - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID1, st.QueryID) require.Equal(t, c, st.NodeID) // advance the pool again, the first query is at capacity so the second query can start state = p.Advance(ctx, &EventPoolPoll{}) - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID2, st.QueryID) require.Equal(t, a, st.NodeID) // notify first query that node was contacted successfully, but no closer nodes - state = p.Advance(ctx, &EventPoolMessageResponse[key.Key8, kadtest.StrAddr]{ + state = p.Advance(ctx, &EventPoolMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID1, NodeID: a, }) // first query starts a new message request - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID1, st.QueryID) require.Equal(t, d, st.NodeID) // notify first query that next node was contacted successfully, but no closer nodes - state = p.Advance(ctx, &EventPoolMessageResponse[key.Key8, kadtest.StrAddr]{ + state = p.Advance(ctx, &EventPoolMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID1, NodeID: b, }) // first query is out of nodes to try so second query can proceed - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID2, st.QueryID) require.Equal(t, b, st.NodeID) } @@ -291,67 +290,67 @@ func TestPoolRespectsConcurrency(t *testing.T) { cfg.QueryConcurrency = 1 // allow each query to have a single request in flight self := kadtest.NewID(key.Key8(0)) - p, err := NewPool[key.Key8, kadtest.StrAddr](self, cfg) + p, err := NewPool[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) target := key.Key8(0b00000001) a := kadtest.NewID(key.Key8(0b00000100)) // 4 - msg1 := kadtest.NewRequest("1", target) + msg1 := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID1 := QueryID("1") protocolID := address.ProtocolID("testprotocol") // Add the first query - state := p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.StrAddr]{ + state := p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID1, Target: target, ProtocolID: protocolID, Message: msg1, - KnownClosestNodes: []kad.NodeID[key.Key8]{a}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a}, }) - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // the first query should attempt to contact the node it was given - st := state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID1, st.QueryID) require.Equal(t, a, st.NodeID) - msg2 := kadtest.NewRequest("2", target) + msg2 := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("2", target) queryID2 := QueryID("2") // Add the second query - state = p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.StrAddr]{ + state = p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID2, Target: target, ProtocolID: protocolID, Message: msg2, - KnownClosestNodes: []kad.NodeID[key.Key8]{a}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a}, }) // the second query should start since the first query has a request in flight - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID2, st.QueryID) require.Equal(t, a, st.NodeID) - msg3 := kadtest.NewRequest("3", target) + msg3 := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("3", target) queryID3 := QueryID("3") // Add a third query - state = p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.StrAddr]{ + state = p.Advance(ctx, &EventPoolAddQuery[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID3, Target: target, ProtocolID: protocolID, Message: msg3, - KnownClosestNodes: []kad.NodeID[key.Key8]{a}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a}, }) // the third query should wait since the pool has reached maximum concurrency require.IsType(t, &StatePoolWaitingAtCapacity{}, state) // notify first query that next node was contacted successfully, but no closer nodes - state = p.Advance(ctx, &EventPoolMessageResponse[key.Key8, kadtest.StrAddr]{ + state = p.Advance(ctx, &EventPoolMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ QueryID: queryID1, NodeID: a, }) @@ -363,8 +362,8 @@ func TestPoolRespectsConcurrency(t *testing.T) { // advancing pool again allows query 3 to start state = p.Advance(ctx, &EventPoolPoll{}) - require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StatePoolQueryMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StatePoolQueryMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID3, st.QueryID) require.Equal(t, a, st.NodeID) } diff --git a/query/query.go b/query/query.go index f637f21..d8eb94e 100644 --- a/query/query.go +++ b/query/query.go @@ -37,12 +37,12 @@ type StateQueryFinished struct { } // StateQueryWaitingMessage indicates that the Query is waiting to send a message to a node. -type StateQueryWaitingMessage[K kad.Key[K], A kad.Address[A]] struct { +type StateQueryWaitingMessage[K kad.Key[K], N kad.NodeID[K]] struct { QueryID QueryID Stats QueryStats - NodeID kad.NodeID[K] + NodeID N ProtocolID address.ProtocolID - Message kad.Request[K, A] + Message kad.Request[K, N] } // StateQueryWaitingAtCapacity indicates that the Query is waiting for results and is at capacity. @@ -59,7 +59,7 @@ type StateQueryWaitingWithCapacity struct { // queryState() ensures that only Query states can be assigned to a QueryState. func (*StateQueryFinished) queryState() {} -func (*StateQueryWaitingMessage[K, A]) queryState() {} +func (*StateQueryWaitingMessage[K, N]) queryState() {} func (*StateQueryWaitingAtCapacity) queryState() {} func (*StateQueryWaitingWithCapacity) queryState() {} @@ -71,21 +71,21 @@ type QueryEvent interface { type EventQueryCancel struct{} // EventQueryMessageResponse notifies a query that an attempt to send a message has received a successful response. -type EventQueryMessageResponse[K kad.Key[K], A kad.Address[A]] struct { - NodeID kad.NodeID[K] // the node the message was sent to - Response kad.Response[K, A] // the message response sent by the node +type EventQueryMessageResponse[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node the message was sent to + Response kad.Response[K, N] // the message response sent by the node } // EventQueryMessageFailure notifies a query that an attempt to send a message has failed. -type EventQueryMessageFailure[K kad.Key[K]] struct { - NodeID kad.NodeID[K] // the node the message was sent to - Error error // the error that caused the failure, if any +type EventQueryMessageFailure[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node the message was sent to + Error error // the error that caused the failure, if any } // queryEvent() ensures that only Query events can be assigned to a QueryEvent. func (*EventQueryCancel) queryEvent() {} -func (*EventQueryMessageResponse[K, A]) queryEvent() {} -func (*EventQueryMessageFailure[K]) queryEvent() {} +func (*EventQueryMessageResponse[K, N]) queryEvent() {} +func (*EventQueryMessageFailure[K, N]) queryEvent() {} // QueryConfig specifies optional configuration for a Query type QueryConfig[K kad.Key[K]] struct { @@ -135,16 +135,16 @@ func DefaultQueryConfig[K kad.Key[K]]() *QueryConfig[K] { } } -type Query[K kad.Key[K], A kad.Address[A]] struct { - self kad.NodeID[K] +type Query[K kad.Key[K], N kad.NodeID[K]] struct { + self N id QueryID // cfg is a copy of the optional configuration supplied to the query cfg QueryConfig[K] - iter NodeIter[K] + iter NodeIter[K, N] protocolID address.ProtocolID - msg kad.Request[K, A] + msg kad.Request[K, N] stats QueryStats // finished indicates that that the query has completed its work or has been stopped. @@ -154,7 +154,7 @@ type Query[K kad.Key[K], A kad.Address[A]] struct { inFlight int } -func NewQuery[K kad.Key[K], A kad.Address[A]](self kad.NodeID[K], id QueryID, protocolID address.ProtocolID, msg kad.Request[K, A], iter NodeIter[K], knownClosestNodes []kad.NodeID[K], cfg *QueryConfig[K]) (*Query[K, A], error) { +func NewQuery[K kad.Key[K], N kad.NodeID[K]](self N, id QueryID, protocolID address.ProtocolID, msg kad.Request[K, N], iter NodeIter[K, N], knownClosestNodes []N, cfg *QueryConfig[K]) (*Query[K, N], error) { if cfg == nil { cfg = DefaultQueryConfig[K]() } else if err := cfg.Validate(); err != nil { @@ -166,13 +166,13 @@ func NewQuery[K kad.Key[K], A kad.Address[A]](self kad.NodeID[K], id QueryID, pr if key.Equal(node.Key(), self.Key()) { continue } - iter.Add(&NodeStatus[K]{ + iter.Add(&NodeStatus[K, N]{ NodeID: node, State: &StateNodeNotContacted{}, }) } - return &Query[K, A]{ + return &Query[K, N]{ self: self, id: id, cfg: *cfg, @@ -182,7 +182,7 @@ func NewQuery[K kad.Key[K], A kad.Address[A]](self kad.NodeID[K], id QueryID, pr }, nil } -func (q *Query[K, A]) Advance(ctx context.Context, ev QueryEvent) QueryState { +func (q *Query[K, N]) Advance(ctx context.Context, ev QueryEvent) QueryState { ctx, span := util.StartSpan(ctx, "Query.Advance") defer span.End() if q.finished { @@ -199,9 +199,9 @@ func (q *Query[K, A]) Advance(ctx context.Context, ev QueryEvent) QueryState { QueryID: q.id, Stats: q.stats, } - case *EventQueryMessageResponse[K, A]: + case *EventQueryMessageResponse[K, N]: q.onMessageResponse(ctx, tev.NodeID, tev.Response) - case *EventQueryMessageFailure[K]: + case *EventQueryMessageFailure[K, N]: q.onMessageFailure(ctx, tev.NodeID) case nil: // TEMPORARY: no event to process @@ -225,7 +225,7 @@ func (q *Query[K, A]) Advance(ctx context.Context, ev QueryEvent) QueryState { var returnState QueryState - q.iter.Each(ctx, func(ctx context.Context, ni *NodeStatus[K]) bool { + q.iter.Each(ctx, func(ctx context.Context, ni *NodeStatus[K, N]) bool { switch st := ni.State.(type) { case *StateNodeWaiting: if q.cfg.Clock.Now().After(st.Deadline) { @@ -266,7 +266,7 @@ func (q *Query[K, A]) Advance(ctx context.Context, ev QueryEvent) QueryState { if q.stats.Start.IsZero() { q.stats.Start = q.cfg.Clock.Now() } - returnState = &StateQueryWaitingMessage[K, A]{ + returnState = &StateQueryWaitingMessage[K, N]{ NodeID: ni.NodeID, QueryID: q.id, Stats: q.stats, @@ -313,7 +313,7 @@ func (q *Query[K, A]) Advance(ctx context.Context, ev QueryEvent) QueryState { } } -func (q *Query[K, A]) markFinished() { +func (q *Query[K, N]) markFinished() { q.finished = true if q.stats.End.IsZero() { q.stats.End = q.cfg.Clock.Now() @@ -321,7 +321,7 @@ func (q *Query[K, A]) markFinished() { } // onMessageResponse processes the result of a successful response received from a node. -func (q *Query[K, A]) onMessageResponse(ctx context.Context, node kad.NodeID[K], resp kad.Response[K, A]) { +func (q *Query[K, N]) onMessageResponse(ctx context.Context, node kad.NodeID[K], resp kad.Response[K, N]) { ni, found := q.iter.Find(node.Key()) if !found { // got a rogue message @@ -351,11 +351,11 @@ func (q *Query[K, A]) onMessageResponse(ctx context.Context, node kad.NodeID[K], // add closer nodes to list for _, info := range resp.CloserNodes() { // exclude self from closest nodes - if key.Equal(info.ID().Key(), q.self.Key()) { + if key.Equal(info.Key(), q.self.Key()) { continue } - q.iter.Add(&NodeStatus[K]{ - NodeID: info.ID(), + q.iter.Add(&NodeStatus[K, N]{ + NodeID: info, State: &StateNodeNotContacted{}, }) } @@ -364,7 +364,7 @@ func (q *Query[K, A]) onMessageResponse(ctx context.Context, node kad.NodeID[K], } // onMessageFailure processes the result of a failed attempt to contact a node. -func (q *Query[K, A]) onMessageFailure(ctx context.Context, node kad.NodeID[K]) { +func (q *Query[K, N]) onMessageFailure(ctx context.Context, node kad.NodeID[K]) { ni, found := q.iter.Find(node.Key()) if !found { // got a rogue message diff --git a/query/query_test.go b/query/query_test.go index 25760c1..d4c8324 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/require" "github.com/plprobelab/go-kademlia/internal/kadtest" - "github.com/plprobelab/go-kademlia/kad" "github.com/plprobelab/go-kademlia/key" "github.com/plprobelab/go-kademlia/network/address" ) @@ -58,30 +57,30 @@ func TestQueryMessagesNode(t *testing.T) { a := kadtest.NewID(key.Key8(0b00000100)) // 4 // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{a} + knownNodes := []kadtest.ID[key.Key8]{a} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is request to send a message to the node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // check that we are messaging the correct node with the right message - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, queryID, st.QueryID) require.Equal(t, a, st.NodeID) require.Equal(t, protocolID, st.ProtocolID) @@ -109,32 +108,32 @@ func TestQueryMessagesNearest(t *testing.T) { require.Less(t, target.Xor(near.Key()), target.Xor(far.Key())) // knownNodes are in "random" order with furthest before nearest - knownNodes := []kad.NodeID[key.Key8]{ + knownNodes := []kadtest.ID[key.Key8]{ far, near, } clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is message the nearest node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // check that we are contacting the nearest node first - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, near, st.NodeID) } @@ -145,27 +144,27 @@ func TestQueryCancelFinishesQuery(t *testing.T) { a := kadtest.NewID(key.Key8(0b00000100)) // 4 // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{a} + knownNodes := []kadtest.ID[key.Key8]{a} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is request to send a message to the node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) clk.Add(time.Second) @@ -192,21 +191,21 @@ func TestQueryNoClosest(t *testing.T) { target := key.Key8(0b00000011) // no known nodes to start with - knownNodes := []kad.NodeID[key.Key8]{} + knownNodes := []kadtest.ID[key.Key8]{} - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) clk := clock.NewMock() cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // query is finished because there were no nodes to contat @@ -237,35 +236,35 @@ func TestQueryWaitsAtCapacity(t *testing.T) { c := kadtest.NewID(key.Key8(0b00010000)) // 16 // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{a, b, c} + knownNodes := []kadtest.ID[key.Key8]{a, b, c} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is request to send a message to the node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) require.Equal(t, 1, st.Stats.Requests) // advancing sends the message to the next node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) require.Equal(t, 2, st.Stats.Requests) @@ -292,31 +291,31 @@ func TestQueryTimedOutNodeMakesCapacity(t *testing.T) { require.True(t, target.Xor(c.Key()).Compare(target.Xor(d.Key())) == -1) // knownNodes are in "random" order - knownNodes := []kad.NodeID[key.Key8]{b, c, a, d} + knownNodes := []kadtest.ID[key.Key8]{b, c, a, d} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.RequestTimeout = 3 * time.Minute cfg.Concurrency = len(knownNodes) - 1 // one less than the number of initial nodes - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the nearest node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) - stwm := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 1, stwm.Stats.Requests) require.Equal(t, 0, stwm.Stats.Success) require.Equal(t, 0, stwm.Stats.Failure) @@ -326,10 +325,10 @@ func TestQueryTimedOutNodeMakesCapacity(t *testing.T) { // while the query has capacity the query should contact the next nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) - stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 2, stwm.Stats.Requests) require.Equal(t, 0, stwm.Stats.Success) require.Equal(t, 0, stwm.Stats.Failure) @@ -339,10 +338,10 @@ func TestQueryTimedOutNodeMakesCapacity(t *testing.T) { // while the query has capacity the query should contact the second nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, c, st.NodeID) - stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 3, stwm.Stats.Requests) require.Equal(t, 0, stwm.Stats.Success) require.Equal(t, 0, stwm.Stats.Failure) @@ -363,11 +362,11 @@ func TestQueryTimedOutNodeMakesCapacity(t *testing.T) { // the first node request should have timed out, making capacity for the last node to attempt connection state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, d, st.NodeID) - stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 4, stwm.Stats.Requests) require.Equal(t, 0, stwm.Stats.Success) require.Equal(t, 1, stwm.Stats.Failure) @@ -400,50 +399,50 @@ func TestQueryMessageResponseMakesCapacity(t *testing.T) { require.True(t, target.Xor(c.Key()).Compare(target.Xor(d.Key())) == -1) // knownNodes are in "random" order - knownNodes := []kad.NodeID[key.Key8]{b, c, a, d} + knownNodes := []kadtest.ID[key.Key8]{b, c, a, d} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = len(knownNodes) - 1 // one less than the number of initial nodes - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the nearest node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) - stwm := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 1, stwm.Stats.Requests) require.Equal(t, 0, stwm.Stats.Success) require.Equal(t, 0, stwm.Stats.Failure) // while the query has capacity the query should contact the next nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) - stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 2, stwm.Stats.Requests) require.Equal(t, 0, stwm.Stats.Success) require.Equal(t, 0, stwm.Stats.Failure) // while the query has capacity the query should contact the second nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, c, st.NodeID) - stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 3, stwm.Stats.Requests) require.Equal(t, 0, stwm.Stats.Success) require.Equal(t, 0, stwm.Stats.Failure) @@ -453,11 +452,11 @@ func TestQueryMessageResponseMakesCapacity(t *testing.T) { require.IsType(t, &StateQueryWaitingAtCapacity{}, state) // notify query that first node was contacted successfully, now node d can be contacted - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{NodeID: a}) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{NodeID: a}) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, d, st.NodeID) - stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 4, stwm.Stats.Requests) require.Equal(t, 1, stwm.Stats.Success) require.Equal(t, 0, stwm.Stats.Failure) @@ -486,28 +485,28 @@ func TestQueryCloserNodesAreAddedToIteration(t *testing.T) { require.True(t, target.Xor(c.Key()).Compare(target.Xor(d.Key())) == -1) // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{d} + knownNodes := []kadtest.ID[key.Key8]{d} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the first node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, d, st.NodeID) // advancing reports query has capacity @@ -515,17 +514,16 @@ func TestQueryCloserNodesAreAddedToIteration(t *testing.T) { require.IsType(t, &StateQueryWaitingWithCapacity{}, state) // notify query that first node was contacted successfully, with closer nodes - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: d, - Response: kadtest.NewResponse("resp_d", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(b, []kadtest.StrAddr{"addr_b"}), - kadtest.NewInfo(a, []kadtest.StrAddr{"addr_a"}), + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp_d", []kadtest.ID[key.Key8]{ + b, a, }), }) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // query should contact the next nearest uncontacted node - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) } @@ -544,34 +542,34 @@ func TestQueryCloserNodesIgnoresDuplicates(t *testing.T) { require.True(t, target.Xor(c.Key()).Compare(target.Xor(d.Key())) == -1) // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{d, a} + knownNodes := []kadtest.ID[key.Key8]{d, a} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the first node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) // next the query attempts to contact second nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, d, st.NodeID) // advancing reports query has no capacity @@ -579,17 +577,16 @@ func TestQueryCloserNodesIgnoresDuplicates(t *testing.T) { require.IsType(t, &StateQueryWaitingAtCapacity{}, state) // notify query that second node was contacted successfully, with closer nodes - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: d, - Response: kadtest.NewResponse("resp_d", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(b, []kadtest.StrAddr{"addr_b"}), - kadtest.NewInfo(a, []kadtest.StrAddr{"addr_a"}), + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp_d", []kadtest.ID[key.Key8]{ + b, a, }), }) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // query should contact the next nearest uncontacted node, which is b - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) } @@ -600,28 +597,28 @@ func TestQueryCancelFinishesIteration(t *testing.T) { a := kadtest.NewID(key.Key8(0b00000100)) // 4 // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{a} + knownNodes := []kadtest.ID[key.Key8]{a} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the first node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) // cancel the query so it is now finished @@ -640,28 +637,28 @@ func TestQueryFinishedIgnoresLaterEvents(t *testing.T) { b := kadtest.NewID(key.Key8(0b00001000)) // 8 // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{b} + knownNodes := []kadtest.ID[key.Key8]{b} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the first node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) // cancel the query so it is now finished @@ -675,10 +672,10 @@ func TestQueryFinishedIgnoresLaterEvents(t *testing.T) { require.Equal(t, 0, stf.Stats.Failure) // notify query that second node was contacted successfully, with closer nodes - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: b, - Response: kadtest.NewResponse("resp_b", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(a, []kadtest.StrAddr{"addr_a"}), + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp_b", []kadtest.ID[key.Key8]{ + a, }), }) @@ -701,39 +698,39 @@ func TestQueryWithCloserIterIgnoresMessagesFromUnknownNodes(t *testing.T) { c := kadtest.NewID(key.Key8(0b00010000)) // 16 // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{c} + knownNodes := []kadtest.ID[key.Key8]{c} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the first node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, c, st.NodeID) - stwm := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + stwm := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, 1, stwm.Stats.Requests) require.Equal(t, 0, stwm.Stats.Success) require.Equal(t, 0, stwm.Stats.Failure) // notify query that second node was contacted successfully, with closer nodes - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: b, - Response: kadtest.NewResponse("resp_b", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(a, []kadtest.StrAddr{"addr_a"}), + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp_b", []kadtest.ID[key.Key8]{ + a, }), }) @@ -756,49 +753,49 @@ func TestQueryWithCloserIterFinishesWhenNumResultsReached(t *testing.T) { d := kadtest.NewID(key.Key8(0b00100000)) // 32 // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{a, b, c, d} + knownNodes := []kadtest.ID[key.Key8]{a, b, c, d} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 4 cfg.NumResults = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // contact first node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) // contact second node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) // notify query that first node was contacted successfully - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: a, }) // query attempts to contact third node - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, c, st.NodeID) // notify query that second node was contacted successfully - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: b, }) @@ -815,63 +812,63 @@ func TestQueryWithCloserIterContinuesUntilNumResultsReached(t *testing.T) { c := kadtest.NewID(key.Key8(0b00010000)) // 16 // one known node to start with, the furthesr - knownNodes := []kad.NodeID[key.Key8]{c} + knownNodes := []kadtest.ID[key.Key8]{c} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 4 cfg.NumResults = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // contact first node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, c, st.NodeID) // notify query that node was contacted successfully and tell it about // a closer one - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: c, - Response: kadtest.NewResponse("resp_c", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(b, []kadtest.StrAddr{"addr_b"}), + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp_c", []kadtest.ID[key.Key8]{ + b, }), }) // query attempts to contact second node - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) // notify query that node was contacted successfully and tell it about // a closer one - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: b, - Response: kadtest.NewResponse("resp_b", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(a, []kadtest.StrAddr{"addr_a"}), + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp_b", []kadtest.ID[key.Key8]{ + a, }), }) // query has seen enough successful contacts but there are still // closer nodes that have not been contacted, so query attempts // to contact third node - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) // notify query that second node was contacted successfully - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: a, }) @@ -896,38 +893,38 @@ func TestQueryNotContactedMakesCapacity(t *testing.T) { require.True(t, target.Xor(b.Key()).Compare(target.Xor(c.Key())) == -1) require.True(t, target.Xor(c.Key()).Compare(target.Xor(d.Key())) == -1) - knownNodes := []kad.NodeID[key.Key8]{a, b, c, d} - iter := NewSequentialIter[key.Key8]() + knownNodes := []kadtest.ID[key.Key8]{a, b, c, d} + iter := NewSequentialIter[key.Key8, kadtest.ID[key.Key8]]() clk := clock.NewMock() cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = len(knownNodes) - 1 // one less than the number of initial nodes - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the nearest node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, a, st.NodeID) // while the query has capacity the query should contact the next nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) // while the query has capacity the query should contact the second nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, c, st.NodeID) // the query should be at capacity @@ -935,9 +932,9 @@ func TestQueryNotContactedMakesCapacity(t *testing.T) { require.IsType(t, &StateQueryWaitingAtCapacity{}, state) // notify query that first node was not contacted, now node d can be contacted - state = qry.Advance(ctx, &EventQueryMessageFailure[key.Key8]{NodeID: a}) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + state = qry.Advance(ctx, &EventQueryMessageFailure[key.Key8, kadtest.ID[key.Key8]]{NodeID: a}) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, d, st.NodeID) // the query should be at capacity again @@ -954,50 +951,50 @@ func TestQueryAllNotContactedFinishes(t *testing.T) { c := kadtest.NewID(key.Key8(0b00010000)) // 16 // knownNodes are in "random" order - knownNodes := []kad.NodeID[key.Key8]{a, b, c} + knownNodes := []kadtest.ID[key.Key8]{a, b, c} clk := clock.NewMock() - iter := NewSequentialIter[key.Key8]() + iter := NewSequentialIter[key.Key8, kadtest.ID[key.Key8]]() cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = len(knownNodes) // allow all to be contacted at once - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the nearest node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // while the query has capacity the query should contact the next nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // while the query has capacity the query should contact the third nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // the query should be at capacity state = qry.Advance(ctx, nil) require.IsType(t, &StateQueryWaitingAtCapacity{}, state) // notify query that first node was not contacted - state = qry.Advance(ctx, &EventQueryMessageFailure[key.Key8]{NodeID: a}) + state = qry.Advance(ctx, &EventQueryMessageFailure[key.Key8, kadtest.ID[key.Key8]]{NodeID: a}) require.IsType(t, &StateQueryWaitingWithCapacity{}, state) // notify query that second node was not contacted - state = qry.Advance(ctx, &EventQueryMessageFailure[key.Key8]{NodeID: b}) + state = qry.Advance(ctx, &EventQueryMessageFailure[key.Key8, kadtest.ID[key.Key8]]{NodeID: b}) require.IsType(t, &StateQueryWaitingWithCapacity{}, state) // notify query that third node was not contacted - state = qry.Advance(ctx, &EventQueryMessageFailure[key.Key8]{NodeID: c}) + state = qry.Advance(ctx, &EventQueryMessageFailure[key.Key8, kadtest.ID[key.Key8]]{NodeID: c}) // query has finished since it contacted all possible nodes require.IsType(t, &StateQueryFinished{}, state) @@ -1014,51 +1011,51 @@ func TestQueryAllContactedFinishes(t *testing.T) { b := kadtest.NewID(key.Key8(0b00001000)) // 8 c := kadtest.NewID(key.Key8(0b00010000)) // 16 - knownNodes := []kad.NodeID[key.Key8]{a, b, c} + knownNodes := []kadtest.ID[key.Key8]{a, b, c} clk := clock.NewMock() - iter := NewSequentialIter[key.Key8]() + iter := NewSequentialIter[key.Key8, kadtest.ID[key.Key8]]() cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = len(knownNodes) // allow all to be contacted at once cfg.NumResults = len(knownNodes) + 1 // one more than the size of the network - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := kadtest.NewID(key.Key8(0)) - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the nearest node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // while the query has capacity the query should contact the next nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // while the query has capacity the query should contact the third nearest node state = qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // the query should be at capacity state = qry.Advance(ctx, nil) require.IsType(t, &StateQueryWaitingAtCapacity{}, state) // notify query that first node was contacted successfully, but no closer nodes - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{NodeID: a}) + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{NodeID: a}) require.IsType(t, &StateQueryWaitingWithCapacity{}, state) // notify query that second node was contacted successfully, but no closer nodes - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{NodeID: b}) + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{NodeID: b}) require.IsType(t, &StateQueryWaitingWithCapacity{}, state) // notify query that third node was contacted successfully, but no closer nodes - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{NodeID: c}) + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{NodeID: c}) // query has finished since it contacted all possible nodes, even though it didn't // reach the desired NumResults @@ -1076,35 +1073,35 @@ func TestQueryNeverMessagesSelf(t *testing.T) { b := kadtest.NewID(key.Key8(0b00001000)) // 8 // one known node to start with - knownNodes := []kad.NodeID[key.Key8]{b} + knownNodes := []kadtest.ID[key.Key8]{b} clk := clock.NewMock() - iter := NewClosestNodesIter(target) + iter := NewClosestNodesIter[key.Key8, kadtest.ID[key.Key8]](target) cfg := DefaultQueryConfig[key.Key8]() cfg.Clock = clk cfg.Concurrency = 2 - msg := kadtest.NewRequest("1", target) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", target) queryID := QueryID("test") protocolID := address.ProtocolID("testprotocol") self := a - qry, err := NewQuery[key.Key8, kadtest.StrAddr](self, queryID, protocolID, msg, iter, knownNodes, cfg) + qry, err := NewQuery[key.Key8, kadtest.ID[key.Key8]](self, queryID, protocolID, msg, iter, knownNodes, cfg) require.NoError(t, err) // first thing the new query should do is contact the first node state := qry.Advance(ctx, nil) - require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateQueryWaitingMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) // notify query that first node was contacted successfully, with closer nodes - state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.StrAddr]{ + state = qry.Advance(ctx, &EventQueryMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: b, - Response: kadtest.NewResponse("resp_b", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(a, []kadtest.StrAddr{"addr_a"}), + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp_b", []kadtest.ID[key.Key8]{ + a, }), }) diff --git a/routing/bootstrap.go b/routing/bootstrap.go index 96e0a4d..4d47e09 100644 --- a/routing/bootstrap.go +++ b/routing/bootstrap.go @@ -14,19 +14,19 @@ import ( "github.com/plprobelab/go-kademlia/util" ) -type Bootstrap[K kad.Key[K], A kad.Address[A]] struct { +type Bootstrap[K kad.Key[K], N kad.NodeID[K]] struct { // self is the node id of the system the bootstrap is running on - self kad.NodeID[K] + self N // qry is the query used by the bootstrap process - qry *query.Query[K, A] + qry *query.Query[K, N] // cfg is a copy of the optional configuration supplied to the Bootstrap - cfg BootstrapConfig[K, A] + cfg BootstrapConfig[K, N] } // BootstrapConfig specifies optional configuration for a Bootstrap -type BootstrapConfig[K kad.Key[K], A kad.Address[A]] struct { +type BootstrapConfig[K kad.Key[K], N kad.NodeID[K]] struct { Timeout time.Duration // the time to wait before terminating a query that is not making progress RequestConcurrency int // the maximum number of concurrent requests that each query may have in flight RequestTimeout time.Duration // the timeout queries should use for contacting a single node @@ -34,7 +34,7 @@ type BootstrapConfig[K kad.Key[K], A kad.Address[A]] struct { } // Validate checks the configuration options and returns an error if any have invalid values. -func (cfg *BootstrapConfig[K, A]) Validate() error { +func (cfg *BootstrapConfig[K, N]) Validate() error { if cfg.Clock == nil { return &kaderr.ConfigurationError{ Component: "BootstrapConfig", @@ -68,8 +68,8 @@ func (cfg *BootstrapConfig[K, A]) Validate() error { // DefaultBootstrapConfig returns the default configuration options for a Bootstrap. // Options may be overridden before passing to NewBootstrap -func DefaultBootstrapConfig[K kad.Key[K], A kad.Address[A]]() *BootstrapConfig[K, A] { - return &BootstrapConfig[K, A]{ +func DefaultBootstrapConfig[K kad.Key[K], N kad.NodeID[K]]() *BootstrapConfig[K, N] { + return &BootstrapConfig[K, N]{ Clock: clock.New(), // use standard time Timeout: 5 * time.Minute, RequestConcurrency: 3, @@ -77,29 +77,29 @@ func DefaultBootstrapConfig[K kad.Key[K], A kad.Address[A]]() *BootstrapConfig[K } } -func NewBootstrap[K kad.Key[K], A kad.Address[A]](self kad.NodeID[K], cfg *BootstrapConfig[K, A]) (*Bootstrap[K, A], error) { +func NewBootstrap[K kad.Key[K], N kad.NodeID[K]](self N, cfg *BootstrapConfig[K, N]) (*Bootstrap[K, N], error) { if cfg == nil { - cfg = DefaultBootstrapConfig[K, A]() + cfg = DefaultBootstrapConfig[K, N]() } else if err := cfg.Validate(); err != nil { return nil, err } - return &Bootstrap[K, A]{ + return &Bootstrap[K, N]{ self: self, cfg: *cfg, }, nil } // Advance advances the state of the bootstrap by attempting to advance its query if running. -func (b *Bootstrap[K, A]) Advance(ctx context.Context, ev BootstrapEvent) BootstrapState { +func (b *Bootstrap[K, N]) Advance(ctx context.Context, ev BootstrapEvent) BootstrapState { ctx, span := util.StartSpan(ctx, "Bootstrap.Advance") defer span.End() switch tev := ev.(type) { - case *EventBootstrapStart[K, A]: + case *EventBootstrapStart[K, N]: // TODO: ignore start event if query is already in progress - iter := query.NewClosestNodesIter(b.self.Key()) + iter := query.NewClosestNodesIter[K, N](b.self.Key()) qryCfg := query.DefaultQueryConfig[K]() qryCfg.Clock = b.cfg.Clock @@ -108,7 +108,7 @@ func (b *Bootstrap[K, A]) Advance(ctx context.Context, ev BootstrapEvent) Bootst queryID := query.QueryID("bootstrap") - qry, err := query.NewQuery[K](b.self, queryID, tev.ProtocolID, tev.Message, iter, tev.KnownClosestNodes, qryCfg) + qry, err := query.NewQuery[K, N](b.self, queryID, tev.ProtocolID, tev.Message, iter, tev.KnownClosestNodes, qryCfg) if err != nil { // TODO: don't panic panic(err) @@ -116,13 +116,13 @@ func (b *Bootstrap[K, A]) Advance(ctx context.Context, ev BootstrapEvent) Bootst b.qry = qry return b.advanceQuery(ctx, nil) - case *EventBootstrapMessageResponse[K, A]: - return b.advanceQuery(ctx, &query.EventQueryMessageResponse[K, A]{ + case *EventBootstrapMessageResponse[K, N]: + return b.advanceQuery(ctx, &query.EventQueryMessageResponse[K, N]{ NodeID: tev.NodeID, Response: tev.Response, }) - case *EventBootstrapMessageFailure[K]: - return b.advanceQuery(ctx, &query.EventQueryMessageFailure[K]{ + case *EventBootstrapMessageFailure[K, N]: + return b.advanceQuery(ctx, &query.EventQueryMessageFailure[K, N]{ NodeID: tev.NodeID, Error: tev.Error, }) @@ -140,11 +140,11 @@ func (b *Bootstrap[K, A]) Advance(ctx context.Context, ev BootstrapEvent) Bootst return &StateBootstrapIdle{} } -func (b *Bootstrap[K, A]) advanceQuery(ctx context.Context, qev query.QueryEvent) BootstrapState { +func (b *Bootstrap[K, N]) advanceQuery(ctx context.Context, qev query.QueryEvent) BootstrapState { state := b.qry.Advance(ctx, qev) switch st := state.(type) { - case *query.StateQueryWaitingMessage[K, A]: - return &StateBootstrapMessage[K, A]{ + case *query.StateQueryWaitingMessage[K, N]: + return &StateBootstrapMessage[K, N]{ QueryID: st.QueryID, Stats: st.Stats, NodeID: st.NodeID, @@ -186,11 +186,11 @@ type BootstrapState interface { } // StateBootstrapMessage indicates that the bootstrap query is waiting to message a node. -type StateBootstrapMessage[K kad.Key[K], A kad.Address[A]] struct { +type StateBootstrapMessage[K kad.Key[K], N kad.NodeID[K]] struct { QueryID query.QueryID - NodeID kad.NodeID[K] + NodeID N ProtocolID address.ProtocolID - Message kad.Request[K, A] + Message kad.Request[K, N] Stats query.QueryStats } @@ -213,7 +213,7 @@ type StateBootstrapWaiting struct { } // bootstrapState() ensures that only Bootstrap states can be assigned to a BootstrapState. -func (*StateBootstrapMessage[K, A]) bootstrapState() {} +func (*StateBootstrapMessage[K, N]) bootstrapState() {} func (*StateBootstrapIdle) bootstrapState() {} func (*StateBootstrapFinished) bootstrapState() {} func (*StateBootstrapTimeout) bootstrapState() {} @@ -228,26 +228,26 @@ type BootstrapEvent interface { type EventBootstrapPoll struct{} // EventBootstrapStart is an event that attempts to start a new bootstrap -type EventBootstrapStart[K kad.Key[K], A kad.Address[A]] struct { +type EventBootstrapStart[K kad.Key[K], N kad.NodeID[K]] struct { ProtocolID address.ProtocolID - Message kad.Request[K, A] - KnownClosestNodes []kad.NodeID[K] + Message kad.Request[K, N] + KnownClosestNodes []N } // EventBootstrapMessageResponse notifies a bootstrap that a sent message has received a successful response. -type EventBootstrapMessageResponse[K kad.Key[K], A kad.Address[A]] struct { - NodeID kad.NodeID[K] // the node the message was sent to - Response kad.Response[K, A] // the message response sent by the node +type EventBootstrapMessageResponse[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node the message was sent to + Response kad.Response[K, N] // the message response sent by the node } // EventBootstrapMessageFailure notifiesa bootstrap that an attempt to send a message has failed. -type EventBootstrapMessageFailure[K kad.Key[K]] struct { - NodeID kad.NodeID[K] // the node the message was sent to - Error error // the error that caused the failure, if any +type EventBootstrapMessageFailure[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node the message was sent to + Error error // the error that caused the failure, if any } // bootstrapEvent() ensures that only Bootstrap events can be assigned to the BootstrapEvent interface. func (*EventBootstrapPoll) bootstrapEvent() {} -func (*EventBootstrapStart[K, A]) bootstrapEvent() {} -func (*EventBootstrapMessageResponse[K, A]) bootstrapEvent() {} -func (*EventBootstrapMessageFailure[K]) bootstrapEvent() {} +func (*EventBootstrapStart[K, N]) bootstrapEvent() {} +func (*EventBootstrapMessageResponse[K, N]) bootstrapEvent() {} +func (*EventBootstrapMessageFailure[K, N]) bootstrapEvent() {} diff --git a/routing/bootstrap_test.go b/routing/bootstrap_test.go index 43dddf7..94d74b9 100644 --- a/routing/bootstrap_test.go +++ b/routing/bootstrap_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "github.com/plprobelab/go-kademlia/internal/kadtest" - "github.com/plprobelab/go-kademlia/kad" "github.com/plprobelab/go-kademlia/key" "github.com/plprobelab/go-kademlia/network/address" "github.com/plprobelab/go-kademlia/query" @@ -16,18 +15,18 @@ import ( func TestBootstrapConfigValidate(t *testing.T) { t.Run("default is valid", func(t *testing.T) { - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() require.NoError(t, cfg.Validate()) }) t.Run("clock is not nil", func(t *testing.T) { - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() cfg.Clock = nil require.Error(t, cfg.Validate()) }) t.Run("timeout positive", func(t *testing.T) { - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() cfg.Timeout = 0 require.Error(t, cfg.Validate()) cfg.Timeout = -1 @@ -35,7 +34,7 @@ func TestBootstrapConfigValidate(t *testing.T) { }) t.Run("request concurrency positive", func(t *testing.T) { - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() cfg.RequestConcurrency = 0 require.Error(t, cfg.Validate()) cfg.RequestConcurrency = -1 @@ -43,7 +42,7 @@ func TestBootstrapConfigValidate(t *testing.T) { }) t.Run("request timeout positive", func(t *testing.T) { - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() cfg.RequestTimeout = 0 require.Error(t, cfg.Validate()) cfg.RequestTimeout = -1 @@ -54,11 +53,11 @@ func TestBootstrapConfigValidate(t *testing.T) { func TestBootstrapStartsIdle(t *testing.T) { ctx := context.Background() clk := clock.NewMock() - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() cfg.Clock = clk self := kadtest.NewID(key.Key8(0)) - bs, err := NewBootstrap[key.Key8, kadtest.StrAddr](self, cfg) + bs, err := NewBootstrap[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) state := bs.Advance(ctx, &EventBootstrapPoll{}) @@ -68,28 +67,28 @@ func TestBootstrapStartsIdle(t *testing.T) { func TestBootstrapStart(t *testing.T) { ctx := context.Background() clk := clock.NewMock() - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() cfg.Clock = clk self := kadtest.NewID(key.Key8(0)) - bs, err := NewBootstrap[key.Key8, kadtest.StrAddr](self, cfg) + bs, err := NewBootstrap[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) a := kadtest.NewID(key.Key8(0b00000100)) // 4 - msg := kadtest.NewRequest("1", self.Key()) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", self.Key()) protocolID := address.ProtocolID("testprotocol") // start the bootstrap - state := bs.Advance(ctx, &EventBootstrapStart[key.Key8, kadtest.StrAddr]{ + state := bs.Advance(ctx, &EventBootstrapStart[key.Key8, kadtest.ID[key.Key8]]{ ProtocolID: protocolID, Message: msg, - KnownClosestNodes: []kad.NodeID[key.Key8]{a}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a}, }) - require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // the query should attempt to contact the node it was given - st := state.(*StateBootstrapMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]) // the query should be the one just added require.Equal(t, query.QueryID("bootstrap"), st.QueryID) @@ -111,33 +110,33 @@ func TestBootstrapStart(t *testing.T) { func TestBootstrapMessageResponse(t *testing.T) { ctx := context.Background() clk := clock.NewMock() - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() cfg.Clock = clk self := kadtest.NewID(key.Key8(0)) - bs, err := NewBootstrap[key.Key8, kadtest.StrAddr](self, cfg) + bs, err := NewBootstrap[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) a := kadtest.NewID(key.Key8(0b00000100)) // 4 - msg := kadtest.NewRequest("1", self.Key()) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", self.Key()) protocolID := address.ProtocolID("testprotocol") // start the bootstrap - state := bs.Advance(ctx, &EventBootstrapStart[key.Key8, kadtest.StrAddr]{ + state := bs.Advance(ctx, &EventBootstrapStart[key.Key8, kadtest.ID[key.Key8]]{ ProtocolID: protocolID, Message: msg, - KnownClosestNodes: []kad.NodeID[key.Key8]{a}, + KnownClosestNodes: []kadtest.ID[key.Key8]{a}, }) - require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // the bootstrap should attempt to contact the node it was given - st := state.(*StateBootstrapMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, query.QueryID("bootstrap"), st.QueryID) require.Equal(t, a, st.NodeID) // notify bootstrap that node was contacted successfully, but no closer nodes - state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.StrAddr]{ + state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: a, }) @@ -152,12 +151,12 @@ func TestBootstrapMessageResponse(t *testing.T) { func TestBootstrapProgress(t *testing.T) { ctx := context.Background() clk := clock.NewMock() - cfg := DefaultBootstrapConfig[key.Key8, kadtest.StrAddr]() + cfg := DefaultBootstrapConfig[key.Key8, kadtest.ID[key.Key8]]() cfg.Clock = clk cfg.RequestConcurrency = 3 // 1 less than the 4 nodes to be visited self := kadtest.NewID(key.Key8(0)) - bs, err := NewBootstrap[key.Key8, kadtest.StrAddr](self, cfg) + bs, err := NewBootstrap[key.Key8, kadtest.ID[key.Key8]](self, cfg) require.NoError(t, err) a := kadtest.NewID(key.Key8(0b00000100)) // 4 @@ -170,32 +169,32 @@ func TestBootstrapProgress(t *testing.T) { require.True(t, self.Key().Xor(b.Key()).Compare(self.Key().Xor(c.Key())) == -1) require.True(t, self.Key().Xor(c.Key()).Compare(self.Key().Xor(d.Key())) == -1) - msg := kadtest.NewRequest("1", self.Key()) + msg := kadtest.NewRequest[key.Key8, kadtest.ID[key.Key8]]("1", self.Key()) protocolID := address.ProtocolID("testprotocol") // start the bootstrap - state := bs.Advance(ctx, &EventBootstrapStart[key.Key8, kadtest.StrAddr]{ + state := bs.Advance(ctx, &EventBootstrapStart[key.Key8, kadtest.ID[key.Key8]]{ ProtocolID: protocolID, Message: msg, - KnownClosestNodes: []kad.NodeID[key.Key8]{d, a, b, c}, + KnownClosestNodes: []kadtest.ID[key.Key8]{d, a, b, c}, }) // the bootstrap should attempt to contact the closest node it was given - require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.StrAddr]{}, state) - st := state.(*StateBootstrapMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st := state.(*StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, query.QueryID("bootstrap"), st.QueryID) require.Equal(t, a, st.NodeID) // next the bootstrap attempts to contact second nearest node state = bs.Advance(ctx, &EventBootstrapPoll{}) - require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateBootstrapMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, b, st.NodeID) // next the bootstrap attempts to contact third nearest node state = bs.Advance(ctx, &EventBootstrapPoll{}) - require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateBootstrapMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, c, st.NodeID) // now the bootstrap should be waiting since it is at request capacity @@ -203,17 +202,17 @@ func TestBootstrapProgress(t *testing.T) { require.IsType(t, &StateBootstrapWaiting{}, state) // notify bootstrap that node was contacted successfully, but no closer nodes - state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.StrAddr]{ + state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: a, }) // now the bootstrap has capacity to contact fourth nearest node - require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.StrAddr]{}, state) - st = state.(*StateBootstrapMessage[key.Key8, kadtest.StrAddr]) + require.IsType(t, &StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) + st = state.(*StateBootstrapMessage[key.Key8, kadtest.ID[key.Key8]]) require.Equal(t, d, st.NodeID) // notify bootstrap that a node was contacted successfully - state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.StrAddr]{ + state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: b, }) @@ -221,7 +220,7 @@ func TestBootstrapProgress(t *testing.T) { require.IsType(t, &StateBootstrapWaiting{}, state) // notify bootstrap that a node was contacted successfully - state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.StrAddr]{ + state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: c, }) @@ -229,7 +228,7 @@ func TestBootstrapProgress(t *testing.T) { require.IsType(t, &StateBootstrapWaiting{}, state) // notify bootstrap that the final node was contacted successfully - state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.StrAddr]{ + state = bs.Advance(ctx, &EventBootstrapMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ NodeID: d, }) diff --git a/routing/include.go b/routing/include.go index cab122a..c5728f3 100644 --- a/routing/include.go +++ b/routing/include.go @@ -13,18 +13,18 @@ import ( "github.com/plprobelab/go-kademlia/util" ) -type check[K kad.Key[K], A kad.Address[A]] struct { - NodeInfo kad.NodeInfo[K, A] - Started time.Time +type check[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N + Started time.Time } -type Include[K kad.Key[K], A kad.Address[A]] struct { - rt kad.RoutingTable[K, kad.NodeID[K]] +type Include[K kad.Key[K], N kad.NodeID[K]] struct { + rt kad.RoutingTable[K, N] // checks is an index of checks in progress - checks map[string]check[K, A] + checks map[string]check[K, N] - candidates *nodeQueue[K, A] + candidates *nodeQueue[K, N] // cfg is a copy of the optional configuration supplied to the Include cfg IncludeConfig @@ -82,37 +82,37 @@ func DefaultIncludeConfig() *IncludeConfig { } } -func NewInclude[K kad.Key[K], A kad.Address[A]](rt kad.RoutingTable[K, kad.NodeID[K]], cfg *IncludeConfig) (*Include[K, A], error) { +func NewInclude[K kad.Key[K], N kad.NodeID[K]](rt kad.RoutingTable[K, N], cfg *IncludeConfig) (*Include[K, N], error) { if cfg == nil { cfg = DefaultIncludeConfig() } else if err := cfg.Validate(); err != nil { return nil, err } - return &Include[K, A]{ - candidates: newNodeQueue[K, A](cfg.QueueCapacity), + return &Include[K, N]{ + candidates: newNodeQueue[K, N](cfg.QueueCapacity), cfg: *cfg, rt: rt, - checks: make(map[string]check[K, A], cfg.Concurrency), + checks: make(map[string]check[K, N], cfg.Concurrency), }, nil } // Advance advances the state of the include state machine by attempting to advance its query if running. -func (b *Include[K, A]) Advance(ctx context.Context, ev IncludeEvent) IncludeState { +func (b *Include[K, N]) Advance(ctx context.Context, ev IncludeEvent) IncludeState { ctx, span := util.StartSpan(ctx, "Include.Advance") defer span.End() switch tev := ev.(type) { - case *EventIncludeAddCandidate[K, A]: + case *EventIncludeAddCandidate[K, N]: // Ignore if already running a check - _, checking := b.checks[key.HexString(tev.NodeInfo.ID().Key())] + _, checking := b.checks[key.HexString(tev.NodeID.Key())] if checking { break } // Ignore if node already in routing table - if _, exists := b.rt.GetNode(tev.NodeInfo.ID().Key()); exists { + if _, exists := b.rt.GetNode(tev.NodeID.Key()); exists { break } @@ -120,23 +120,23 @@ func (b *Include[K, A]) Advance(ctx context.Context, ev IncludeEvent) IncludeSta if !b.candidates.HasCapacity() { return &StateIncludeWaitingFull{} } - b.candidates.Enqueue(ctx, tev.NodeInfo) + b.candidates.Enqueue(ctx, tev.NodeID) - case *EventIncludeMessageResponse[K, A]: - ch, ok := b.checks[key.HexString(tev.NodeInfo.ID().Key())] + case *EventIncludeMessageResponse[K, N]: + ch, ok := b.checks[key.HexString(tev.NodeID.Key())] if ok { - delete(b.checks, key.HexString(tev.NodeInfo.ID().Key())) + delete(b.checks, key.HexString(tev.NodeID.Key())) // require that the node responded with at least one closer node if tev.Response != nil && len(tev.Response.CloserNodes()) > 0 { - if b.rt.AddNode(tev.NodeInfo.ID()) { - return &StateIncludeRoutingUpdated[K, A]{ - NodeInfo: ch.NodeInfo, + if b.rt.AddNode(tev.NodeID) { + return &StateIncludeRoutingUpdated[K, N]{ + NodeID: ch.NodeID, } } } } - case *EventIncludeMessageFailure[K, A]: - delete(b.checks, key.HexString(tev.NodeInfo.ID().Key())) + case *EventIncludeMessageFailure[K, N]: + delete(b.checks, key.HexString(tev.NodeID.Key())) case *EventIncludePoll: // ignore, nothing to do @@ -160,64 +160,64 @@ func (b *Include[K, A]) Advance(ctx context.Context, ev IncludeEvent) IncludeSta return &StateIncludeIdle{} } - b.checks[key.HexString(candidate.ID().Key())] = check[K, A]{ - NodeInfo: candidate, - Started: b.cfg.Clock.Now(), + b.checks[key.HexString(candidate.Key())] = check[K, N]{ + NodeID: candidate, + Started: b.cfg.Clock.Now(), } // Ask the node to find itself - return &StateIncludeFindNodeMessage[K, A]{ - NodeInfo: candidate, + return &StateIncludeFindNodeMessage[K, N]{ + NodeID: candidate, } } // nodeQueue is a bounded queue of unique NodeIDs -type nodeQueue[K kad.Key[K], A kad.Address[A]] struct { +type nodeQueue[K kad.Key[K], N kad.NodeID[K]] struct { capacity int - nodes []kad.NodeInfo[K, A] + nodes []N keys map[string]struct{} } -func newNodeQueue[K kad.Key[K], A kad.Address[A]](capacity int) *nodeQueue[K, A] { - return &nodeQueue[K, A]{ +func newNodeQueue[K kad.Key[K], N kad.NodeID[K]](capacity int) *nodeQueue[K, N] { + return &nodeQueue[K, N]{ capacity: capacity, - nodes: make([]kad.NodeInfo[K, A], 0, capacity), + nodes: make([]N, 0, capacity), keys: make(map[string]struct{}, capacity), } } // Enqueue adds a node to the queue. It returns true if the node was // added and false otherwise. -func (q *nodeQueue[K, A]) Enqueue(ctx context.Context, n kad.NodeInfo[K, A]) bool { +func (q *nodeQueue[K, N]) Enqueue(ctx context.Context, n N) bool { if len(q.nodes) == q.capacity { return false } - if _, exists := q.keys[key.HexString(n.ID().Key())]; exists { + if _, exists := q.keys[key.HexString(n.Key())]; exists { return false } q.nodes = append(q.nodes, n) - q.keys[key.HexString(n.ID().Key())] = struct{}{} + q.keys[key.HexString(n.Key())] = struct{}{} return true } // Dequeue reads an node from the queue. It returns the node and a true value // if a node was read or nil and false if no node was read. -func (q *nodeQueue[K, A]) Dequeue(ctx context.Context) (kad.NodeInfo[K, A], bool) { +func (q *nodeQueue[K, N]) Dequeue(ctx context.Context) (N, bool) { if len(q.nodes) == 0 { - var v kad.NodeInfo[K, A] + var v N return v, false } - var n kad.NodeInfo[K, A] + var n N n, q.nodes = q.nodes[0], q.nodes[1:] - delete(q.keys, key.HexString(n.ID().Key())) + delete(q.keys, key.HexString(n.Key())) return n, true } -func (q *nodeQueue[K, A]) HasCapacity() bool { +func (q *nodeQueue[K, N]) HasCapacity() bool { return len(q.nodes) < q.capacity } @@ -228,8 +228,8 @@ type IncludeState interface { // StateIncludeFindNodeMessage indicates that the include subsystem is waiting to send a find node message a node. // A find node message should be sent to the node, with the target being the node's key. -type StateIncludeFindNodeMessage[K kad.Key[K], A kad.Address[A]] struct { - NodeInfo kad.NodeInfo[K, A] // the node to send the mssage to +type StateIncludeFindNodeMessage[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node to send the mssage to } // StateIncludeIdle indicates that the include is not running its query. @@ -248,17 +248,17 @@ type StateIncludeWaitingWithCapacity struct{} type StateIncludeWaitingFull struct{} // StateIncludeRoutingUpdated indicates the routing table has been updated with a new node. -type StateIncludeRoutingUpdated[K kad.Key[K], A kad.Address[A]] struct { - NodeInfo kad.NodeInfo[K, A] +type StateIncludeRoutingUpdated[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N } // includeState() ensures that only Include states can be assigned to an IncludeState. -func (*StateIncludeFindNodeMessage[K, A]) includeState() {} +func (*StateIncludeFindNodeMessage[K, N]) includeState() {} func (*StateIncludeIdle) includeState() {} func (*StateIncludeWaitingAtCapacity) includeState() {} func (*StateIncludeWaitingWithCapacity) includeState() {} func (*StateIncludeWaitingFull) includeState() {} -func (*StateIncludeRoutingUpdated[K, A]) includeState() {} +func (*StateIncludeRoutingUpdated[K, N]) includeState() {} // IncludeEvent is an event intended to advance the state of a include. type IncludeEvent interface { @@ -269,24 +269,24 @@ type IncludeEvent interface { type EventIncludePoll struct{} // EventIncludeAddCandidate notifies that a node should be added to the candidate list -type EventIncludeAddCandidate[K kad.Key[K], A kad.Address[A]] struct { - NodeInfo kad.NodeInfo[K, A] // the candidate node +type EventIncludeAddCandidate[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the candidate node } // EventIncludeMessageResponse notifies a include that a sent message has received a successful response. -type EventIncludeMessageResponse[K kad.Key[K], A kad.Address[A]] struct { - NodeInfo kad.NodeInfo[K, A] // the node the message was sent to - Response kad.Response[K, A] // the message response sent by the node +type EventIncludeMessageResponse[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node the message was sent to + Response kad.Response[K, N] // the message response sent by the node } // EventIncludeMessageFailure notifiesa include that an attempt to send a message has failed. -type EventIncludeMessageFailure[K kad.Key[K], A kad.Address[A]] struct { - NodeInfo kad.NodeInfo[K, A] // the node the message was sent to - Error error // the error that caused the failure, if any +type EventIncludeMessageFailure[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node the message was sent to + Error error // the error that caused the failure, if any } // includeEvent() ensures that only Include events can be assigned to the IncludeEvent interface. func (*EventIncludePoll) includeEvent() {} -func (*EventIncludeAddCandidate[K, A]) includeEvent() {} -func (*EventIncludeMessageResponse[K, A]) includeEvent() {} -func (*EventIncludeMessageFailure[K, A]) includeEvent() {} +func (*EventIncludeAddCandidate[K, N]) includeEvent() {} +func (*EventIncludeMessageResponse[K, N]) includeEvent() {} +func (*EventIncludeMessageFailure[K, N]) includeEvent() {} diff --git a/routing/include_test.go b/routing/include_test.go index dc2c88c..05a326c 100644 --- a/routing/include_test.go +++ b/routing/include_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "github.com/plprobelab/go-kademlia/internal/kadtest" - "github.com/plprobelab/go-kademlia/kad" "github.com/plprobelab/go-kademlia/key" "github.com/plprobelab/go-kademlia/routing/simplert" ) @@ -56,9 +55,9 @@ func TestIncludeStartsIdle(t *testing.T) { cfg := DefaultIncludeConfig() cfg.Clock = clk - rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) + rt := simplert.New[key.Key8, kadtest.ID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - bs, err := NewInclude[key.Key8, kadtest.StrAddr](rt, cfg) + bs, err := NewInclude[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) state := bs.Advance(ctx, &EventIncludePoll{}) @@ -72,30 +71,27 @@ func TestIncludeAddCandidateStartsCheckIfCapacity(t *testing.T) { cfg.Clock = clk cfg.Concurrency = 1 - rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) + rt := simplert.New[key.Key8, kadtest.ID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - p, err := NewInclude[key.Key8, kadtest.StrAddr](rt, cfg) + p, err := NewInclude[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) - candidate := kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ) + candidate := kadtest.NewID(key.Key8(0b00000100)) // add a candidate - state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: candidate, + state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: candidate, }) // the state machine should attempt to send a message - require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) - st := state.(*StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]) + st := state.(*StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]) // the message should be sent to the candidate node - require.Equal(t, candidate, st.NodeInfo) + require.Equal(t, candidate, st.NodeID) // the message should be looking for the candidate node - require.Equal(t, candidate.ID(), st.NodeInfo.ID()) + require.Equal(t, candidate, st.NodeID) // now the include reports that it is waiting since concurrency is 1 state = p.Advance(ctx, &EventIncludePoll{}) @@ -109,20 +105,17 @@ func TestIncludeAddCandidateReportsCapacity(t *testing.T) { cfg.Clock = clk cfg.Concurrency = 2 - rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - p, err := NewInclude[key.Key8, kadtest.StrAddr](rt, cfg) + rt := simplert.New[key.Key8, kadtest.ID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) + p, err := NewInclude[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) - candidate := kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ) + candidate := kadtest.NewID(key.Key8(0b00000100)) // add a candidate - state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: candidate, + state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: candidate, }) - require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // now the state machine reports that it is waiting with capacity since concurrency // is greater than the number of checks in flight @@ -138,33 +131,27 @@ func TestIncludeAddCandidateOverQueueLength(t *testing.T) { cfg.QueueCapacity = 2 // only allow two candidates in the queue cfg.Concurrency = 3 - rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) + rt := simplert.New[key.Key8, kadtest.ID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - p, err := NewInclude[key.Key8, kadtest.StrAddr](rt, cfg) + p, err := NewInclude[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // add a candidate - state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ), + state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000100)), }) - require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // include reports that it is waiting and has capacity for more state = p.Advance(ctx, &EventIncludePoll{}) require.IsType(t, &StateIncludeWaitingWithCapacity{}, state) // add second candidate - state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000010)), - []kadtest.StrAddr{kadtest.StrAddr("2")}, - ), + state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000010)), }) // sends a message to the candidate - require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // include reports that it is waiting and has capacity for more state = p.Advance(ctx, &EventIncludePoll{}) @@ -172,36 +159,27 @@ func TestIncludeAddCandidateOverQueueLength(t *testing.T) { require.IsType(t, &StateIncludeWaitingWithCapacity{}, state) // add third candidate - state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000011)), - []kadtest.StrAddr{kadtest.StrAddr("3")}, - ), + state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000011)), }) // sends a message to the candidate - require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // include reports that it is waiting at capacity since 3 messages are in flight state = p.Advance(ctx, &EventIncludePoll{}) require.IsType(t, &StateIncludeWaitingAtCapacity{}, state) // add fourth candidate - state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000101)), - []kadtest.StrAddr{kadtest.StrAddr("5")}, - ), + state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000101)), }) // include reports that it is waiting at capacity since 3 messages are already in flight require.IsType(t, &StateIncludeWaitingAtCapacity{}, state) // add fifth candidate - state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000110)), - []kadtest.StrAddr{kadtest.StrAddr("6")}, - ), + state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000110)), }) // include reports that it is waiting and the candidate queue is full since it @@ -209,11 +187,8 @@ func TestIncludeAddCandidateOverQueueLength(t *testing.T) { require.IsType(t, &StateIncludeWaitingFull{}, state) // add sixth candidate - state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000111)), - []kadtest.StrAddr{kadtest.StrAddr("7")}, - ), + state = p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000111)), }) // include reports that it is still waiting and the candidate queue is full since it @@ -228,39 +203,34 @@ func TestIncludeMessageResponse(t *testing.T) { cfg.Clock = clk cfg.Concurrency = 2 - rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) + rt := simplert.New[key.Key8, kadtest.ID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - p, err := NewInclude[key.Key8, kadtest.StrAddr](rt, cfg) + p, err := NewInclude[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // add a candidate - state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ), + state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000100)), }) - require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // notify that node was contacted successfully, with no closer nodes - state = p.Advance(ctx, &EventIncludeMessageResponse[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ), - Response: kadtest.NewResponse("resp", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(kadtest.NewID(key.Key8(4)), []kadtest.StrAddr{"addr_4"}), - kadtest.NewInfo(kadtest.NewID(key.Key8(6)), []kadtest.StrAddr{"addr_6"}), + state = p.Advance(ctx, &EventIncludeMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000100)), + + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp", []kadtest.ID[key.Key8]{ + kadtest.NewID(key.Key8(4)), + kadtest.NewID(key.Key8(6)), }), }) // should respond that the routing table was updated - require.IsType(t, &StateIncludeRoutingUpdated[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeRoutingUpdated[key.Key8, kadtest.ID[key.Key8]]{}, state) - st := state.(*StateIncludeRoutingUpdated[key.Key8, kadtest.StrAddr]) + st := state.(*StateIncludeRoutingUpdated[key.Key8, kadtest.ID[key.Key8]]) // the update is for the correct node - require.Equal(t, kadtest.NewID(key.Key8(4)), st.NodeInfo.ID()) + require.Equal(t, kadtest.NewID(key.Key8(4)), st.NodeID) // the routing table should contain the node foundNode, found := rt.GetNode(key.Key8(4)) @@ -281,26 +251,20 @@ func TestIncludeMessageResponseInvalid(t *testing.T) { cfg.Clock = clk cfg.Concurrency = 2 - rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) + rt := simplert.New[key.Key8, kadtest.ID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - p, err := NewInclude[key.Key8, kadtest.StrAddr](rt, cfg) + p, err := NewInclude[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // add a candidate - state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ), + state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000100)), }) - require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // notify that node was contacted successfully, but no closer nodes - state = p.Advance(ctx, &EventIncludeMessageResponse[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ), + state = p.Advance(ctx, &EventIncludeMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000100)), }) // should respond that state machine is idle require.IsType(t, &StateIncludeIdle{}, state) @@ -318,26 +282,20 @@ func TestIncludeMessageFailure(t *testing.T) { cfg.Clock = clk cfg.Concurrency = 2 - rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) + rt := simplert.New[key.Key8, kadtest.ID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - p, err := NewInclude[key.Key8, kadtest.StrAddr](rt, cfg) + p, err := NewInclude[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // add a candidate - state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ), + state := p.Advance(ctx, &EventIncludeAddCandidate[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000100)), }) - require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.StrAddr]{}, state) + require.IsType(t, &StateIncludeFindNodeMessage[key.Key8, kadtest.ID[key.Key8]]{}, state) // notify that node was not contacted successfully - state = p.Advance(ctx, &EventIncludeMessageFailure[key.Key8, kadtest.StrAddr]{ - NodeInfo: kadtest.NewInfo( - kadtest.NewID(key.Key8(0b00000100)), - []kadtest.StrAddr{kadtest.StrAddr("4")}, - ), + state = p.Advance(ctx, &EventIncludeMessageFailure[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: kadtest.NewID(key.Key8(0b00000100)), }) // should respond that state machine is idle diff --git a/routing/probe.go b/routing/probe.go index 572024d..05393ec 100644 --- a/routing/probe.go +++ b/routing/probe.go @@ -55,12 +55,12 @@ type RoutingTableCpl[K kad.Key[K], N kad.NodeID[K]] interface { // The state machine accepts the [EventProbeNotifyConnectivity] event as a notification that an external system has // performed a suitable connectivity check, such as when the node responds to a query. The probe state machine treats // these events as if a successful response had been received from a check by advancing the time of the next check. -type Probe[K kad.Key[K], A kad.Address[A]] struct { - rt RoutingTableCpl[K, kad.NodeID[K]] +type Probe[K kad.Key[K], N kad.NodeID[K]] struct { + rt RoutingTableCpl[K, N] // nvl is a list of nodes with information about their connectivity checks // TODO: this will be expanded with more general scoring information related to their utility - nvl *nodeValueList[K] + nvl *nodeValueList[K, N] // cfg is a copy of the optional configuration supplied to the Probe cfg ProbeConfig @@ -118,22 +118,22 @@ func DefaultProbeConfig() *ProbeConfig { } } -func NewProbe[K kad.Key[K], A kad.Address[A]](rt RoutingTableCpl[K, kad.NodeID[K]], cfg *ProbeConfig) (*Probe[K, A], error) { +func NewProbe[K kad.Key[K], N kad.NodeID[K]](rt RoutingTableCpl[K, N], cfg *ProbeConfig) (*Probe[K, N], error) { if cfg == nil { cfg = DefaultProbeConfig() } else if err := cfg.Validate(); err != nil { return nil, err } - return &Probe[K, A]{ + return &Probe[K, N]{ cfg: *cfg, rt: rt, - nvl: NewNodeValueList[K](), + nvl: NewNodeValueList[K, N](), }, nil } // Advance advances the state of the probe state machine by attempting to advance its query if running. -func (p *Probe[K, A]) Advance(ctx context.Context, ev ProbeEvent) ProbeState { +func (p *Probe[K, N]) Advance(ctx context.Context, ev ProbeEvent) ProbeState { _, span := util.StartSpan(ctx, "Probe.Advance") defer span.End() @@ -141,7 +141,7 @@ func (p *Probe[K, A]) Advance(ctx context.Context, ev ProbeEvent) ProbeState { case *EventProbePoll: // ignore, nothing to do span.SetAttributes(attribute.String("event", "EventProbePoll")) - case *EventProbeAdd[K]: + case *EventProbeAdd[K, N]: // check presence in routing table span.SetAttributes(attribute.String("event", "EventProbeAdd"), attribute.String("nodeid", tev.NodeID.String())) if _, found := p.rt.GetNode(tev.NodeID.Key()); !found { @@ -151,19 +151,19 @@ func (p *Probe[K, A]) Advance(ctx context.Context, ev ProbeEvent) ProbeState { } // add a node to the value list - nv := &nodeValue[K]{ + nv := &nodeValue[K, N]{ NodeID: tev.NodeID, NextCheckDue: p.cfg.Clock.Now().Add(p.cfg.CheckInterval), Cpl: p.rt.Cpl(tev.NodeID.Key()), } // TODO: if node was in ongoing list return a state that can signal the caller to cancel any prior outbound message p.nvl.Put(nv) - case *EventProbeRemove[K]: + case *EventProbeRemove[K, N]: span.SetAttributes(attribute.String("event", "EventProbeRemove"), attribute.String("nodeid", tev.NodeID.String())) p.nvl.Remove(tev.NodeID) - case *EventProbeMessageResponse[K, A]: - span.SetAttributes(attribute.String("event", "EventProbeMessageResponse"), attribute.String("nodeid", tev.NodeInfo.ID().String())) - nv, found := p.nvl.Get(tev.NodeInfo.ID()) + case *EventProbeMessageResponse[K, N]: + span.SetAttributes(attribute.String("event", "EventProbeMessageResponse"), attribute.String("nodeid", tev.NodeID.String())) + nv, found := p.nvl.Get(tev.NodeID) if !found { // ignore message for unknown node, which might have been removed span.RecordError(errors.New("node not in node value list")) @@ -175,14 +175,14 @@ func (p *Probe[K, A]) Advance(ctx context.Context, ev ProbeEvent) ProbeState { // put into list, which will clear any ongoing check too p.nvl.Put(nv) - case *EventProbeMessageFailure[K, A]: + case *EventProbeMessageFailure[K, N]: // probe failed, so remove from routing table and from list - span.SetAttributes(attribute.String("event", "EventProbeMessageFailure"), attribute.String("nodeid", tev.NodeInfo.ID().String())) + span.SetAttributes(attribute.String("event", "EventProbeMessageFailure"), attribute.String("nodeid", tev.NodeID.String())) span.RecordError(tev.Error) - p.rt.RemoveKey(tev.NodeInfo.ID().Key()) - p.nvl.Remove(tev.NodeInfo.ID()) - return &StateProbeNodeFailure[K]{ - NodeID: tev.NodeInfo.ID(), + p.rt.RemoveKey(tev.NodeID.Key()) + p.nvl.Remove(tev.NodeID) + return &StateProbeNodeFailure[K, N]{ + NodeID: tev.NodeID, } case *EventProbeNotifyConnectivity[K]: span.SetAttributes(attribute.String("event", "EventProbeNotifyConnectivity"), attribute.String("nodeid", tev.NodeID.String())) @@ -213,7 +213,7 @@ func (p *Probe[K, A]) Advance(ctx context.Context, ev ProbeEvent) ProbeState { // mark the node as failed since it timed out p.rt.RemoveKey(candidate.Key()) p.nvl.Remove(candidate) - return &StateProbeNodeFailure[K]{ + return &StateProbeNodeFailure[K, N]{ NodeID: candidate, } @@ -233,7 +233,7 @@ func (p *Probe[K, A]) Advance(ctx context.Context, ev ProbeEvent) ProbeState { p.nvl.MarkOngoing(next.NodeID, p.cfg.Clock.Now().Add(p.cfg.Timeout)) // Ask the node to find itself - return &StateProbeConnectivityCheck[K]{ + return &StateProbeConnectivityCheck[K, N]{ NodeID: next.NodeID, } } @@ -245,8 +245,8 @@ type ProbeState interface { // StateProbeConnectivityCheck indicates that the probe subsystem is waiting to send a connectivity check to a node. // A find node message should be sent to the node, with the target being the node's key. -type StateProbeConnectivityCheck[K kad.Key[K]] struct { - NodeID kad.NodeID[K] // the node to send the message to +type StateProbeConnectivityCheck[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node to send the message to } // StateProbeIdle indicates that the probe state machine is not running any checks. @@ -261,16 +261,16 @@ type StateProbeWaitingAtCapacity struct{} type StateProbeWaitingWithCapacity struct{} // StateProbeNodeFailure indicates a node has failed a connectivity check been removed from the routing table and the probe list -type StateProbeNodeFailure[K kad.Key[K]] struct { - NodeID kad.NodeID[K] +type StateProbeNodeFailure[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N } // probeState() ensures that only Probe states can be assigned to the ProbeState interface. -func (*StateProbeConnectivityCheck[K]) probeState() {} -func (*StateProbeIdle) probeState() {} -func (*StateProbeWaitingAtCapacity) probeState() {} -func (*StateProbeWaitingWithCapacity) probeState() {} -func (*StateProbeNodeFailure[K]) probeState() {} +func (*StateProbeConnectivityCheck[K, N]) probeState() {} +func (*StateProbeIdle) probeState() {} +func (*StateProbeWaitingAtCapacity) probeState() {} +func (*StateProbeWaitingWithCapacity) probeState() {} +func (*StateProbeNodeFailure[K, N]) probeState() {} // ProbeEvent is an event intended to advance the state of a probe. type ProbeEvent interface { @@ -281,25 +281,25 @@ type ProbeEvent interface { type EventProbePoll struct{} // EventProbeAdd notifies a probe that a node should be added to its list of nodes. -type EventProbeAdd[K kad.Key[K]] struct { - NodeID kad.NodeID[K] // the node to be probed +type EventProbeAdd[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node to be probed } // EventProbeRemove notifies a probe that a node should be removed from its list of nodes and the routing table. -type EventProbeRemove[K kad.Key[K]] struct { - NodeID kad.NodeID[K] // the node to be removed +type EventProbeRemove[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node to be removed } // EventProbeMessageResponse notifies a probe that a sent message has received a successful response. -type EventProbeMessageResponse[K kad.Key[K], A kad.Address[A]] struct { - NodeInfo kad.NodeInfo[K, A] // the node the message was sent to - Response kad.Response[K, A] // the message response sent by the node +type EventProbeMessageResponse[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node the message was sent to + Response kad.Response[K, N] // the message response sent by the node } // EventProbeMessageFailure notifiesa probe that an attempt to send a message has failed. -type EventProbeMessageFailure[K kad.Key[K], A kad.Address[A]] struct { - NodeInfo kad.NodeInfo[K, A] // the node the message was sent to - Error error // the error that caused the failure, if any +type EventProbeMessageFailure[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N // the node the message was sent to + Error error // the error that caused the failure, if any } // EventProbeNotifyConnectivity notifies a probe that a node has confirmed connectivity from another source such as a query. @@ -309,47 +309,47 @@ type EventProbeNotifyConnectivity[K kad.Key[K]] struct { // probeEvent() ensures that only Probe events can be assigned to the ProbeEvent interface. func (*EventProbePoll) probeEvent() {} -func (*EventProbeAdd[K]) probeEvent() {} -func (*EventProbeRemove[K]) probeEvent() {} -func (*EventProbeMessageResponse[K, A]) probeEvent() {} -func (*EventProbeMessageFailure[K, A]) probeEvent() {} +func (*EventProbeAdd[K, N]) probeEvent() {} +func (*EventProbeRemove[K, N]) probeEvent() {} +func (*EventProbeMessageResponse[K, N]) probeEvent() {} +func (*EventProbeMessageFailure[K, N]) probeEvent() {} func (*EventProbeNotifyConnectivity[K]) probeEvent() {} -type nodeValue[K kad.Key[K]] struct { - NodeID kad.NodeID[K] +type nodeValue[K kad.Key[K], N kad.NodeID[K]] struct { + NodeID N Cpl int // the longest common prefix length the node shares with the routing table's key NextCheckDue time.Time CheckDeadline time.Time Index int // the index of the item in the ordering } -type nodeValueEntry[K kad.Key[K]] struct { - nv *nodeValue[K] +type nodeValueEntry[K kad.Key[K], N kad.NodeID[K]] struct { + nv *nodeValue[K, N] index int // the index of the item in the ordering } -type nodeValueList[K kad.Key[K]] struct { - nodes map[string]*nodeValueEntry[K] - pending *nodeValuePendingList[K] +type nodeValueList[K kad.Key[K], N kad.NodeID[K]] struct { + nodes map[string]*nodeValueEntry[K, N] + pending *nodeValuePendingList[K, N] // ongoing is a list of nodes with ongoing/in-progress probes, loosely ordered earliest to most recent - ongoing []kad.NodeID[K] + ongoing []N } -func NewNodeValueList[K kad.Key[K]]() *nodeValueList[K] { - return &nodeValueList[K]{ - nodes: make(map[string]*nodeValueEntry[K]), - ongoing: make([]kad.NodeID[K], 0), - pending: new(nodeValuePendingList[K]), +func NewNodeValueList[K kad.Key[K], N kad.NodeID[K]]() *nodeValueList[K, N] { + return &nodeValueList[K, N]{ + nodes: make(map[string]*nodeValueEntry[K, N]), + ongoing: make([]N, 0), + pending: new(nodeValuePendingList[K, N]), } } // Put adds a node value to the list, replacing any existing value. // It is added to the pending list and removed from the ongoing list if it was already present there. -func (l *nodeValueList[K]) Put(nv *nodeValue[K]) { +func (l *nodeValueList[K, N]) Put(nv *nodeValue[K, N]) { mk := key.HexString(nv.NodeID.Key()) nve, exists := l.nodes[mk] if !exists { - nve = &nodeValueEntry[K]{ + nve = &nodeValueEntry[K, N]{ nv: nv, } } else { @@ -362,7 +362,7 @@ func (l *nodeValueList[K]) Put(nv *nodeValue[K]) { l.removeFromOngoing(nv.NodeID) } -func (l *nodeValueList[K]) Get(n kad.NodeID[K]) (*nodeValue[K], bool) { +func (l *nodeValueList[K, N]) Get(n kad.NodeID[K]) (*nodeValue[K, N], bool) { mk := key.HexString(n.Key()) nve, found := l.nodes[mk] if !found { @@ -371,21 +371,21 @@ func (l *nodeValueList[K]) Get(n kad.NodeID[K]) (*nodeValue[K], bool) { return nve.nv, true } -func (l *nodeValueList[K]) PendingCount() int { +func (l *nodeValueList[K, N]) PendingCount() int { return len(*l.pending) } -func (l *nodeValueList[K]) OngoingCount() int { +func (l *nodeValueList[K, N]) OngoingCount() int { return len(l.ongoing) } -func (l *nodeValueList[K]) NodeCount() int { +func (l *nodeValueList[K, N]) NodeCount() int { return len(l.nodes) } // Put removes a node value from the list, deleting its information. // It is removed from the pending list andongoing list if it was already present in either. -func (l *nodeValueList[K]) Remove(n kad.NodeID[K]) { +func (l *nodeValueList[K, N]) Remove(n kad.NodeID[K]) { mk := key.HexString(n.Key()) nve, ok := l.nodes[mk] if !ok { @@ -400,7 +400,7 @@ func (l *nodeValueList[K]) Remove(n kad.NodeID[K]) { // FindCheckPastDeadline looks for the first node in the ongoing list whose deadline is // before the supplied timestamp. -func (l *nodeValueList[K]) FindCheckPastDeadline(ts time.Time) (kad.NodeID[K], bool) { +func (l *nodeValueList[K, N]) FindCheckPastDeadline(ts time.Time) (N, bool) { // ongoing is in start time order, oldest first for _, n := range l.ongoing { mk := key.HexString(n.Key()) @@ -413,10 +413,10 @@ func (l *nodeValueList[K]) FindCheckPastDeadline(ts time.Time) (kad.NodeID[K], b return n, true } } - return nil, false + return *new(N), false } -func (l *nodeValueList[K]) removeFromOngoing(n kad.NodeID[K]) { +func (l *nodeValueList[K, N]) removeFromOngoing(n kad.NodeID[K]) { // ongoing list is expected to be small, so linear search is ok for i := range l.ongoing { if key.Equal(n.Key(), l.ongoing[i].Key()) { @@ -425,7 +425,7 @@ func (l *nodeValueList[K]) removeFromOngoing(n kad.NodeID[K]) { l.ongoing[i], l.ongoing[len(l.ongoing)-1] = l.ongoing[len(l.ongoing)-1], l.ongoing[i] } // remove last entry - l.ongoing[len(l.ongoing)-1] = nil + l.ongoing[len(l.ongoing)-1] = *new(N) l.ongoing = l.ongoing[:len(l.ongoing)-1] return } @@ -434,7 +434,7 @@ func (l *nodeValueList[K]) removeFromOngoing(n kad.NodeID[K]) { // PeekNext returns the next node that is due a connectivity check without removing it // from the pending list. -func (l *nodeValueList[K]) PeekNext(ts time.Time) (*nodeValue[K], bool) { +func (l *nodeValueList[K, N]) PeekNext(ts time.Time) (*nodeValue[K, N], bool) { if len(*l.pending) == 0 { return nil, false } @@ -451,7 +451,7 @@ func (l *nodeValueList[K]) PeekNext(ts time.Time) (*nodeValue[K], bool) { // MarkOngoing marks a node as having an ongoing connectivity check. // It has no effect if the node is not already present in the list. -func (l *nodeValueList[K]) MarkOngoing(n kad.NodeID[K], deadline time.Time) { +func (l *nodeValueList[K, N]) MarkOngoing(n kad.NodeID[K], deadline time.Time) { mk := key.HexString(n.Key()) nve, ok := l.nodes[mk] if !ok { @@ -464,10 +464,10 @@ func (l *nodeValueList[K]) MarkOngoing(n kad.NodeID[K], deadline time.Time) { } // nodeValuePendingList is a min-heap of NodeValue ordered by NextCheckDue -type nodeValuePendingList[K kad.Key[K]] []*nodeValueEntry[K] +type nodeValuePendingList[K kad.Key[K], N kad.NodeID[K]] []*nodeValueEntry[K, N] -func (o nodeValuePendingList[K]) Len() int { return len(o) } -func (o nodeValuePendingList[K]) Less(i, j int) bool { +func (o nodeValuePendingList[K, N]) Len() int { return len(o) } +func (o nodeValuePendingList[K, N]) Less(i, j int) bool { // if due times are equal, then sort higher cpls first if o[i].nv.NextCheckDue.Equal(o[j].nv.NextCheckDue) { return o[i].nv.Cpl > o[j].nv.Cpl @@ -476,20 +476,20 @@ func (o nodeValuePendingList[K]) Less(i, j int) bool { return o[i].nv.NextCheckDue.Before(o[j].nv.NextCheckDue) } -func (o nodeValuePendingList[K]) Swap(i, j int) { +func (o nodeValuePendingList[K, N]) Swap(i, j int) { o[i], o[j] = o[j], o[i] o[i].index = i o[j].index = j } -func (o *nodeValuePendingList[K]) Push(x any) { +func (o *nodeValuePendingList[K, N]) Push(x any) { n := len(*o) - v := x.(*nodeValueEntry[K]) + v := x.(*nodeValueEntry[K, N]) v.index = n *o = append(*o, v) } -func (o *nodeValuePendingList[K]) Pop() any { +func (o *nodeValuePendingList[K, N]) Pop() any { if len(*o) == 0 { return nil } diff --git a/routing/probe_test.go b/routing/probe_test.go index a376809..c8a5281 100644 --- a/routing/probe_test.go +++ b/routing/probe_test.go @@ -15,14 +15,7 @@ import ( "github.com/plprobelab/go-kademlia/routing/simplert" ) -var _ heap.Interface = (*nodeValuePendingList[key.Key8])(nil) - -type unaddressedNodeInfo[K kad.Key[K], A kad.Address[A]] struct { - NodeID kad.NodeID[K] -} - -func (u unaddressedNodeInfo[K, A]) ID() kad.NodeID[K] { return u.NodeID } -func (u unaddressedNodeInfo[K, A]) Addresses() []A { return nil } +var _ heap.Interface = (*nodeValuePendingList[key.Key8, kadtest.ID[key.Key8]])(nil) func TestProbeConfigValidate(t *testing.T) { t.Run("default is valid", func(t *testing.T) { @@ -69,7 +62,7 @@ func TestProbeStartsIdle(t *testing.T) { rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - bs, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + bs, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) state := bs.Advance(ctx, &EventProbePoll{}) @@ -88,11 +81,11 @@ func TestProbeAddChecksPresenceInRoutingTable(t *testing.T) { cfg.Concurrency = 1 rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // Add node that isn't in routing table - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) require.IsType(t, &StateProbeIdle{}, state) @@ -119,12 +112,12 @@ func TestProbeAddStartsCheckIfCapacity(t *testing.T) { rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) rt.AddNode(kadtest.NewID(key.Key8(4))) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // after adding first node the probe should be idle since the // connectivity check will be scheduled for the future - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) require.IsType(t, &StateProbeIdle{}, state) @@ -136,10 +129,10 @@ func TestProbeAddStartsCheckIfCapacity(t *testing.T) { // advance time by one revisit interval clk.Add(cfg.CheckInterval) state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) // the probe state machine should attempt to contact the next node - st := state.(*StateProbeConnectivityCheck[key.Key8]) + st := state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) // the connectivity check should be for the right node require.True(t, key.Equal(key.Key8(4), st.NodeID.Key())) @@ -161,26 +154,26 @@ func TestProbeAddManyStartsChecksIfCapacity(t *testing.T) { rt.AddNode(kadtest.NewID(key.Key8(3))) rt.AddNode(kadtest.NewID(key.Key8(2))) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // after adding first node the probe should be idle since the // connectivity check will be scheduled for the future - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) require.IsType(t, &StateProbeIdle{}, state) // after adding second node the probe should still be idle since the // connectivity check will be scheduled for the future - state = sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state = sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(3)), }) require.IsType(t, &StateProbeIdle{}, state) // after adding third node the probe should still be idle since the // connectivity check will be scheduled for the future - state = sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state = sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(2)), }) require.IsType(t, &StateProbeIdle{}, state) @@ -190,18 +183,18 @@ func TestProbeAddManyStartsChecksIfCapacity(t *testing.T) { // Poll the state machine, it should now attempt to contact a node state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) // the connectivity check should be for the right node - st := state.(*StateProbeConnectivityCheck[key.Key8]) + st := state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) require.True(t, key.Equal(key.Key8(4), st.NodeID.Key())) // Poll the state machine, it should now attempt to contact another node state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) // the connectivity check should be for the right node - st = state.(*StateProbeConnectivityCheck[key.Key8]) + st = state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) require.True(t, key.Equal(key.Key8(2), st.NodeID.Key())) // Poll the state machine, it should now be at capacity @@ -223,12 +216,12 @@ func TestProbeAddReportsCapacity(t *testing.T) { rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) rt.AddNode(kadtest.NewID(key.Key8(4))) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // after adding first node the probe should be idle since the // connectivity check will be scheduled for the future - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) require.IsType(t, &StateProbeIdle{}, state) @@ -240,10 +233,10 @@ func TestProbeAddReportsCapacity(t *testing.T) { // advance time by one revisit interval clk.Add(cfg.CheckInterval) state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) // the probe state machine should attempt to contact the next node - st := state.(*StateProbeConnectivityCheck[key.Key8]) + st := state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) // the connectivity check should be for the right node require.True(t, key.Equal(key.Key8(4), st.NodeID.Key())) @@ -266,18 +259,18 @@ func TestProbeRemoveDeletesNodeValue(t *testing.T) { rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) rt.AddNode(kadtest.NewID(key.Key8(4))) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // after adding first node the probe should be idle since the // connectivity check will be scheduled for the future - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) require.IsType(t, &StateProbeIdle{}, state) // remove the node - state = sm.Advance(ctx, &EventProbeRemove[key.Key8]{ + state = sm.Advance(ctx, &EventProbeRemove[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) @@ -297,8 +290,8 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now(), } @@ -314,15 +307,15 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv1 := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv1 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now(), } l.Put(nv1) - nv2 := &nodeValue[key.Key8]{ + nv2 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now().Add(-time.Minute), } @@ -338,15 +331,15 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv1 := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv1 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now(), } l.Put(nv1) - nv2 := &nodeValue[key.Key8]{ + nv2 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now().Add(time.Minute), } @@ -362,8 +355,8 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now(), } @@ -388,8 +381,8 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now(), } @@ -405,7 +398,7 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() got, found := l.PeekNext(clk.Now()) require.False(t, found) require.Nil(t, got) @@ -415,8 +408,8 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now(), } @@ -431,13 +424,13 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv1 := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv1 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(5)), NextCheckDue: clk.Now().Add(-time.Minute), } l.Put(nv1) - nv2 := &nodeValue[key.Key8]{ + nv2 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now().Add(-2 * time.Minute), } @@ -459,14 +452,14 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv1 := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv1 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(5)), Cpl: 1, NextCheckDue: clk.Now().Add(-time.Minute), } l.Put(nv1) - nv2 := &nodeValue[key.Key8]{ + nv2 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), Cpl: 2, NextCheckDue: clk.Now().Add(-time.Minute), @@ -489,13 +482,13 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv1 := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv1 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(5)), NextCheckDue: clk.Now().Add(time.Minute), } l.Put(nv1) - nv2 := &nodeValue[key.Key8]{ + nv2 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now().Add(2 * time.Minute), } @@ -510,8 +503,8 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv1 := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv1 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(5)), NextCheckDue: clk.Now().Add(time.Minute), } @@ -530,14 +523,14 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv1 := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv1 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(5)), NextCheckDue: clk.Now().Add(-2 * time.Minute), } l.Put(nv1) - nv2 := &nodeValue[key.Key8]{ + nv2 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now().Add(-1 * time.Minute), } @@ -567,8 +560,8 @@ func TestNodeValueList(t *testing.T) { t.Parallel() clk := clock.NewMock() - l := NewNodeValueList[key.Key8]() - nv1 := &nodeValue[key.Key8]{ + l := NewNodeValueList[key.Key8, kadtest.ID[key.Key8]]() + nv1 := &nodeValue[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), NextCheckDue: clk.Now(), } @@ -606,12 +599,12 @@ func TestProbeMessageResponse(t *testing.T) { rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) rt.AddNode(kadtest.NewID(key.Key8(4))) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // after adding first node the probe should be idle since the // connectivity check will be scheduled for the future - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) require.IsType(t, &StateProbeIdle{}, state) @@ -619,19 +612,17 @@ func TestProbeMessageResponse(t *testing.T) { // advance time by one revisit interval clk.Add(cfg.CheckInterval) state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) // the probe state machine should attempt to contact the next node - st := state.(*StateProbeConnectivityCheck[key.Key8]) + st := state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) // notify that node was contacted successfully, with no closer nodes - state = sm.Advance(ctx, &EventProbeMessageResponse[key.Key8, kadtest.StrAddr]{ - NodeInfo: unaddressedNodeInfo[key.Key8, kadtest.StrAddr]{ - NodeID: st.NodeID, - }, - Response: kadtest.NewResponse("resp", []kad.NodeInfo[key.Key8, kadtest.StrAddr]{ - kadtest.NewInfo(kadtest.NewID(key.Key8(4)), []kadtest.StrAddr{"addr_4"}), - kadtest.NewInfo(kadtest.NewID(key.Key8(6)), []kadtest.StrAddr{"addr_6"}), + state = sm.Advance(ctx, &EventProbeMessageResponse[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: st.NodeID, + Response: kadtest.NewResponse[key.Key8, kadtest.ID[key.Key8]]("resp", []kadtest.ID[key.Key8]{ + kadtest.NewID(key.Key8(4)), + kadtest.NewID(key.Key8(6)), }), }) @@ -647,7 +638,7 @@ func TestProbeMessageResponse(t *testing.T) { // the probe state machine should attempt to contact node again, now it is time state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) // the connectivity check should be for the right node require.True(t, key.Equal(key.Key8(4), st.NodeID.Key())) @@ -670,12 +661,12 @@ func TestProbeMessageFailure(t *testing.T) { rt := simplert.New[key.Key8, kad.NodeID[key.Key8]](kadtest.NewID(key.Key8(128)), 5) rt.AddNode(kadtest.NewID(key.Key8(4))) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // after adding first node the probe should be idle since the // connectivity check will be scheduled for the future - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) require.IsType(t, &StateProbeIdle{}, state) @@ -683,16 +674,14 @@ func TestProbeMessageFailure(t *testing.T) { // advance time by one revisit interval clk.Add(cfg.CheckInterval) state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) // the probe state machine should attempt to contact the next node - st := state.(*StateProbeConnectivityCheck[key.Key8]) + st := state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) // notify that node was contacted successfully, with no closer nodes - state = sm.Advance(ctx, &EventProbeMessageFailure[key.Key8, kadtest.StrAddr]{ - NodeInfo: unaddressedNodeInfo[key.Key8, kadtest.StrAddr]{ - NodeID: st.NodeID, - }, + state = sm.Advance(ctx, &EventProbeMessageFailure[key.Key8, kadtest.ID[key.Key8]]{ + NodeID: st.NodeID, }) // state machine announces node failure @@ -727,12 +716,12 @@ func TestProbeNotifyConnectivity(t *testing.T) { rt.AddNode(kadtest.NewID(key.Key8(4))) rt.AddNode(kadtest.NewID(key.Key8(3))) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // after adding first node the probe should be idle since the // connectivity check will be scheduled for the future (t0+10) - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) @@ -745,7 +734,7 @@ func TestProbeNotifyConnectivity(t *testing.T) { // add a second node, which will be second in the probe list since it's // time of next check will be later (t0+2+10=t0+12) - state = sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state = sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(3)), }) @@ -771,11 +760,11 @@ func TestProbeNotifyConnectivity(t *testing.T) { // Poll the state machine, it should now attempt to contact a node state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) // the connectivity check should be for the right node, which is the one // that did not get a connectivity notification - st := state.(*StateProbeConnectivityCheck[key.Key8]) + st := state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) require.True(t, key.Equal(key.Key8(3), st.NodeID.Key())) // Poll the state machine, it should now waiting for a response but still have capacity @@ -797,11 +786,11 @@ func TestProbeTimeout(t *testing.T) { rt.AddNode(kadtest.NewID(key.Key8(4))) rt.AddNode(kadtest.NewID(key.Key8(3))) - sm, err := NewProbe[key.Key8, kadtest.StrAddr](rt, cfg) + sm, err := NewProbe[key.Key8, kadtest.ID[key.Key8]](rt, cfg) require.NoError(t, err) // add a node - state := sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state := sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(4)), }) @@ -812,7 +801,7 @@ func TestProbeTimeout(t *testing.T) { clk.Add(time.Minute) // add another node - state = sm.Advance(ctx, &EventProbeAdd[key.Key8]{ + state = sm.Advance(ctx, &EventProbeAdd[key.Key8, kadtest.ID[key.Key8]]{ NodeID: kadtest.NewID(key.Key8(3)), }) @@ -826,8 +815,8 @@ func TestProbeTimeout(t *testing.T) { state = sm.Advance(ctx, &EventProbePoll{}) // the connectivity check should start - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) - stm := state.(*StateProbeConnectivityCheck[key.Key8]) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) + stm := state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) require.True(t, key.Equal(key.Key8(4), stm.NodeID.Key())) // Poll the state machine, it should now waiting for a response with no capacity @@ -851,7 +840,7 @@ func TestProbeTimeout(t *testing.T) { // state machine starts check for next node now there is capacity state = sm.Advance(ctx, &EventProbePoll{}) - require.IsType(t, &StateProbeConnectivityCheck[key.Key8]{}, state) - stm = state.(*StateProbeConnectivityCheck[key.Key8]) + require.IsType(t, &StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]{}, state) + stm = state.(*StateProbeConnectivityCheck[key.Key8, kadtest.ID[key.Key8]]) require.True(t, key.Equal(key.Key8(3), stm.NodeID.Key())) }