diff --git a/go.mod b/go.mod index ba38a93cc58d70a70c57c5042ea1ec37b4f938b2..299d72471662e37fa39f19b44dff11034514d28a 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( gitlab.com/elixxir/crypto v0.0.7-0.20210319231554-b73b6e62ddbc gitlab.com/elixxir/ekv v0.1.4 gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b - gitlab.com/xx_network/comms v0.0.4-0.20210323205910-f01316c830dd + gitlab.com/xx_network/comms v0.0.4-0.20210329190058-55f9bdf16249 gitlab.com/xx_network/crypto v0.0.5-0.20210319231335-249c6b1aa323 gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad diff --git a/go.sum b/go.sum index 8fd108a05a47213c580b49cf8fbf0fa768f7d653..7177981b103b8110d33c9592f09d26cef649976a 100644 --- a/go.sum +++ b/go.sum @@ -284,6 +284,8 @@ gitlab.com/xx_network/comms v0.0.4-0.20210323140408-2b2613abb5a3 h1:Mh+YiOX89vG9 gitlab.com/xx_network/comms v0.0.4-0.20210323140408-2b2613abb5a3/go.mod h1:0Hx+zO3Pr4uYw4RZXFnPM3ocjY6bPIKDiHCjWTZLOSI= gitlab.com/xx_network/comms v0.0.4-0.20210323205910-f01316c830dd h1:WZn6y52gqigTXBAdsDRM3KWNBwnPEnYoGcBrsSAuphI= gitlab.com/xx_network/comms v0.0.4-0.20210323205910-f01316c830dd/go.mod h1:0Hx+zO3Pr4uYw4RZXFnPM3ocjY6bPIKDiHCjWTZLOSI= +gitlab.com/xx_network/comms v0.0.4-0.20210329190058-55f9bdf16249 h1:dDcesEGcAzN1C6PP7Y8T0Ky5c4mpRfhVFMZJKBrbQzk= +gitlab.com/xx_network/comms v0.0.4-0.20210329190058-55f9bdf16249/go.mod h1:0Hx+zO3Pr4uYw4RZXFnPM3ocjY6bPIKDiHCjWTZLOSI= gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE= gitlab.com/xx_network/crypto v0.0.4 h1:lpKOL5mTJ2awWMfgBy30oD/UvJVrWZzUimSHlOdZZxo= gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk= diff --git a/network/gateway/gateway.go b/network/gateway/gateway.go index 1dda3483f0a8976aba0e7f9de68b8fb55b61ad25..a33c50ba00ea3a072a5b93b9566b6eaa2f499833 100644 --- a/network/gateway/gateway.go +++ b/network/gateway/gateway.go @@ -205,9 +205,8 @@ func (h *HostPool) pruneHostPool() error { for poolIdx := uint32(0); poolIdx < h.poolParams.poolSize; { host := h.hostList[poolIdx] - // Check the Host for errors - if host == nil || *host.GetMetrics().ErrCounter >= h.poolParams.errThreshold { + if host == nil || host.GetMetrics().GetErrorCounter() >= h.poolParams.errThreshold { // If errors occurred, randomly select a new Gw by index in the NDF ndfIdx := readRangeUint32(0, uint32(len(h.ndf.Gateways)), h.rng) @@ -220,6 +219,7 @@ func (h *HostPool) pruneHostPool() error { // Verify the GwId is not already in the hostMap if _, ok := h.hostMap[*gwId]; !ok { + // If it is a new GwId, replace the old Host with the new Host err = h.replaceHost(gwId, poolIdx) if err != nil { diff --git a/network/gateway/gateway_test.go b/network/gateway/gateway_test.go new file mode 100644 index 0000000000000000000000000000000000000000..586a5cfce5ce4fb579926490f2b349142c59b0d5 --- /dev/null +++ b/network/gateway/gateway_test.go @@ -0,0 +1,231 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package gateway + +import ( + "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/primitives/id" + "testing" +) + +// Full happy path test +func TestReplaceHost(t *testing.T) { + manager := newHappyManager() + testNdf := getTestNdf(t) + newIndex := uint32(20) + + // Construct a manager (bypass business logic in constructor) + hostPool := &HostPool{ + manager: manager, + hostList: make([]*connect.Host, newIndex+1), + hostMap: make(map[id.ID]uint32), + ndf: testNdf, + } + + /* "Replace" a host with no entry */ + + // Pull a gateway ID from the ndf + gwIdOne, err := id.Unmarshal(testNdf.Gateways[0].ID) + if err != nil { + t.Errorf("Failed to unmarshal ID in mock ndf: %v", err) + } + + // Add mock gateway to manager + _, err = manager.AddHost(gwIdOne, "", nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + } + + // "Replace" (insert) the host + err = hostPool.replaceHost(gwIdOne, newIndex) + if err != nil { + t.Errorf("Could not replace host: %v", err) + } + + // Check the state of the map has been correctly updated + retrievedIndex, ok := hostPool.hostMap[*gwIdOne] + if !ok { + t.Errorf("Expected insertion of gateway ID into map") + } + if retrievedIndex != newIndex { + t.Errorf("Index pulled from map not expected value." + + "\n\tExpected: %d" + + "\n\tReceived: %d", newIndex, retrievedIndex) + } + + // Check that the state of the list list been correctly updated + retrievedHost := hostPool.hostList[newIndex] + if !gwIdOne.Cmp(retrievedHost.GetId()) { + t.Errorf("Id pulled from list is not expected." + + "\n\tExpected: %s" + + "\n\tReceived: %s", gwIdOne, retrievedHost.GetId()) + } + + /* Replace the initial host with a new host */ + + // Pull a different gateway ID from the ndf + gwIdTwo, err := id.Unmarshal(testNdf.Gateways[1].ID) + if err != nil { + t.Errorf("Failed to unmarshal ID in mock ndf: %v", err) + } + + // Add second mock gateway to manager + _, err = manager.AddHost(gwIdTwo, "", nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + } + + + // Replace the old host + err = hostPool.replaceHost(gwIdTwo, newIndex) + if err != nil { + t.Errorf("Could not replace host: %v", err) + } + + // Check that the state of the list been correctly updated for new host + retrievedHost = hostPool.hostList[newIndex] + if !gwIdTwo.Cmp(retrievedHost.GetId()) { + t.Errorf("Id pulled from list is not expected." + + "\n\tExpected: %s" + + "\n\tReceived: %s", gwIdTwo, retrievedHost.GetId()) + } + + // Check the state of the map has been correctly removed for the old gateway + retrievedOldIndex, ok := hostPool.hostMap[*gwIdOne] + if ok { + t.Errorf("Exoected old gateway to be cleared from map") + } + if retrievedOldIndex != 0 { + t.Errorf("Index pulled from map with old gateway as the key " + + "was not cleared." + + "\n\tExpected: %d" + + "\n\tReceived: %d", 0, retrievedOldIndex) + } + + + // Check the state of the map has been correctly updated for the old gateway + retrievedIndex, ok = hostPool.hostMap[*gwIdTwo] + if !ok { + t.Errorf("Expected insertion of gateway ID into map") + } + if retrievedIndex != newIndex { + t.Errorf("Index pulled from map using new gateway as the key " + + "was not updated." + + "\n\tExpected: %d" + + "\n\tReceived: %d", newIndex, retrievedIndex) + } +} + +// Error path, could not get host +func TestReplaceHost_Error(t *testing.T) { + manager := newHappyManager() + + // Construct a manager (bypass business logic in constructor) + hostPool := &HostPool{ + manager: manager, + hostList: make([]*connect.Host, 1), + hostMap: make(map[id.ID]uint32), + } + + // Construct an unknown gateway ID to the manager + gatewayId := id.NewIdFromString("BadGateway", id.Gateway, t) + + err := hostPool.replaceHost(gatewayId, 0) + if err == nil { + t.Errorf("Expected error in happy path: Should not be able to find a host") + } + +} + +// Happy path +func TestPruneHostPool(t *testing.T) { + manager := newHappyManager() + testNdf := getTestNdf(t) + newIndex := uint32(20) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + rng := csprng.NewSystemRNG() + + // Construct a manager (bypass business logic in constructor) + hostPool := &HostPool{ + manager: manager, + hostList: make([]*connect.Host, newIndex+1), + hostMap: make(map[id.ID]uint32), + ndf: testNdf, + poolParams: params, + rng: rng, + } + + // Pull all gateways from ndf into host manager + hostList := make([]*connect.Host, 0) + for _, gw := range testNdf.Gateways { + + gwId, err := id.Unmarshal(gw.ID) + if err != nil { + t.Errorf("Failed to unmarshal ID in mock ndf: %v", err) + } + // Add mock gateway to manager + h, err := manager.AddHost(gwId, "", nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + + + hostList = append(hostList, h) + + } + // fixme: have a case where host's error threshold is met? + + // Call prune host pool + err := hostPool.pruneHostPool() + if err != nil { + t.Errorf("Unexpected error in happy path: %v",err) + } + + // Check that the host map has been properly updated + for _, h := range hostList { + _, ok := hostPool.hostMap[*h.GetId()] + if !ok { + t.Errorf("Gateway %s was not placed in host map after pruning", h.GetId().String()) + } + } + +} + +// Error path: not enough gateways in ndf compared to +// required pool size +func TestPruneHostPool_Error(t *testing.T) { + manager := newHappyManager() + testNdf := getTestNdf(t) + newIndex := uint32(20) + params := DefaultPoolParams() + + // Trigger the case where the Ndf doesn't have enough gateways + params.poolSize = uint32(len(testNdf.Gateways)) + 1 + rng := csprng.NewSystemRNG() + + // Construct a manager (bypass business logic in constructor) + hostPool := &HostPool{ + manager: manager, + hostList: make([]*connect.Host, newIndex+1), + hostMap: make(map[id.ID]uint32), + ndf: testNdf, + poolParams: params, + rng: rng, + } + + // Call prune + err := hostPool.pruneHostPool() + if err == nil { + t.Errorf("Gateways should not be available: " + + "not enough gateways in ndf compared to param's pool size") + } + +} \ No newline at end of file diff --git a/network/gateway/utils_test.go b/network/gateway/utils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6f7d89bf16d177e4fd29910d9cf6c4a60c53e4fe --- /dev/null +++ b/network/gateway/utils_test.go @@ -0,0 +1,105 @@ +package gateway + +import ( + "errors" + "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" +) + +// Mock structure adhering to HostManager which returns the error +// path for all it's methods +type MangerErrorPath struct {} + +// Constructor for MangerErrorPath +func newErrorManager() *MangerErrorPath { + return &MangerErrorPath{} +} + + +func (mep *MangerErrorPath) GetHost(hostId *id.ID) (*connect.Host, bool) { + return nil, false +} +func (mep *MangerErrorPath) AddHost(hid *id.ID, address string, + cert []byte, params connect.HostParams) (host *connect.Host, err error) { + return nil, errors.New("Failed to add host") +} + +func (mep *MangerErrorPath) RemoveHost(hid *id.ID) {} + + +// Mock structure adhering to HostManager to be used for happy path +type ManagerHappyPath struct { + hosts map[string]*connect.Host +} + +// Constructor for ManagerHappyPath +func newHappyManager() *ManagerHappyPath { + return &ManagerHappyPath{ + hosts: make(map[string]*connect.Host), + } +} + +func (mhp *ManagerHappyPath) GetHost(hostId *id.ID) (*connect.Host, bool) { + h, ok := mhp.hosts[hostId.String()] + return h, ok +} + +func (mhp *ManagerHappyPath) AddHost(hid *id.ID, address string, + cert []byte, params connect.HostParams) (host *connect.Host, err error) { + host, err = connect.NewHost(hid, address, cert, params) + if err != nil { + return nil, err + } + + mhp.hosts[hid.String()] = host + + return +} + +func (mhp *ManagerHappyPath) RemoveHost(hid *id.ID) { + delete(mhp.hosts, hid.String()) +} + +// Returns a mock +func getTestNdf(face interface{}) *ndf.NetworkDefinition { + return &ndf.NetworkDefinition{ + Gateways: []ndf.Gateway{{ + ID: id.NewIdFromUInt(0, id.Gateway, face)[:], + Address: "0.0.0.1", + }, { + ID: id.NewIdFromUInt(1, id.Gateway, face)[:], + Address: "0.0.0.2", + }, { + ID: id.NewIdFromUInt(2, id.Gateway, face)[:], + Address: "0.0.0.3", + }, { + ID: id.NewIdFromUInt(3, id.Gateway, face)[:], + Address: "0.0.0.1", + }, { + ID: id.NewIdFromUInt(4, id.Gateway, face)[:], + Address: "0.0.0.2", + }, { + ID: id.NewIdFromUInt(5, id.Gateway, face)[:], + Address: "0.0.0.3", + }, { + ID: id.NewIdFromUInt(6, id.Gateway, face)[:], + Address: "0.0.0.1", + }, { + ID: id.NewIdFromUInt(7, id.Gateway, face)[:], + Address: "0.0.0.2", + }, { + ID: id.NewIdFromUInt(8, id.Gateway, face)[:], + Address: "0.0.0.3", + }, { + ID: id.NewIdFromUInt(9, id.Gateway, face)[:], + Address: "0.0.0.1", + }, { + ID: id.NewIdFromUInt(10, id.Gateway, face)[:], + Address: "0.0.0.2", + }, { + ID: id.NewIdFromUInt(11, id.Gateway, face)[:], + Address: "0.0.0.3", + }}, + } +}