diff --git a/go.mod b/go.mod index 5b2d27eab8195cbb24839d4614c6571d5aa883bd..20aa686ea726c7a04ebe1b861dd8f0171c3933b9 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/golang/protobuf v1.4.3 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect - github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea // indirect + github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea github.com/magiconair/properties v1.8.4 // indirect github.com/mitchellh/mapstructure v1.4.0 // indirect github.com/pelletier/go-toml v1.8.1 // indirect @@ -23,7 +23,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.20210331153942-daf17c8b6b47 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 b6e88d3cda43ab0ff4237f50ff7d3315075d9d9a..773bc4faf55be9478d3257fb88146c846b19c918 100644 --- a/go.sum +++ b/go.sum @@ -292,6 +292,12 @@ 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/comms v0.0.4-0.20210330224545-2d0e07fb64c6 h1:LsxlArrT5mIM5rRbMuNp+PQPWrCbPKhunHO2MfznbsE= +gitlab.com/xx_network/comms v0.0.4-0.20210330224545-2d0e07fb64c6/go.mod h1:0Hx+zO3Pr4uYw4RZXFnPM3ocjY6bPIKDiHCjWTZLOSI= +gitlab.com/xx_network/comms v0.0.4-0.20210331153942-daf17c8b6b47 h1:r8qBdMtuQouVylWl5RPctlXzPldcbhI2bvYkeUB1Ia0= +gitlab.com/xx_network/comms v0.0.4-0.20210331153942-daf17c8b6b47/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/follow.go b/network/follow.go index 88857b59846ed601b33226d59bc690cbcddc7867..3c05519620d7dce05140d7958f779ab8940f6ade 100644 --- a/network/follow.go +++ b/network/follow.go @@ -189,8 +189,8 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, update.State = uint32(states.FAILED) rnd, err := m.Instance.GetWrappedRound(id.Round(update.ID)) if err != nil { - jww.ERROR.Printf("Failed to report client error: " + - "Could not get round for event triggering: " + + jww.ERROR.Printf("Failed to report client error: "+ + "Could not get round for event triggering: "+ "Unable to get round %d from instance: %+v", id.Round(update.ID), err) break diff --git a/network/gateway/hostPool.go b/network/gateway/hostPool.go index 3e520daba3ecd68e99af1eb8a84f0807ff73f99d..e8ab9f17102eaca390a2e013fe31f7495cdc8e54 100644 --- a/network/gateway/hostPool.go +++ b/network/gateway/hostPool.go @@ -115,7 +115,6 @@ func (h *HostPool) GetAny(length int) []*connect.Host { length = int(h.poolParams.poolSize) } result := make([]*connect.Host, length) - h.hostMux.RLock() for i := 0; i < length; { gwIdx := readRangeUint32(0, h.poolParams.poolSize, h.rng) @@ -212,9 +211,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) @@ -227,6 +225,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/hostpool_test.go b/network/gateway/hostpool_test.go new file mode 100644 index 0000000000000000000000000000000000000000..83be2608fa2226140e2dbc1bb5453d1a7608c42d --- /dev/null +++ b/network/gateway/hostpool_test.go @@ -0,0 +1,811 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/comms/network" + "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" + "reflect" + "testing" + "time" +) + +// Unit test +func TestNewHostPool(t *testing.T) { + manager := newMockManager() + rng := csprng.NewSystemRNG() + testNdf := getTestNdf(t) + testStorage := storage.InitTestingSession(t) + addGwChan := make(chan network.NodeGateway) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + + // Pull all gateways from ndf into host manager + 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 + _, err = manager.AddHost(gwId, "", nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + + } + + // Call the constructor + _, err := NewHostPool(params, rng, testNdf, manager, + testStorage, addGwChan) + if err != nil { + t.Errorf("Failed to create mock host pool: %v", err) + } +} + +// Unit test +func TestHostPool_ManageHostPool(t *testing.T) { + manager := newMockManager() + rng := csprng.NewSystemRNG() + testNdf := getTestNdf(t) + testStorage := storage.InitTestingSession(t) + addGwChan := make(chan network.NodeGateway) + + // Construct custom params + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + params.pruneInterval = 1 * time.Second + + // Pull all gateways from ndf into host manager + 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 + _, err = manager.AddHost(gwId, gw.Address, nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + + } + + // Call the constructor + testPool, err := NewHostPool(params, rng, testNdf, manager, + testStorage, addGwChan) + if err != nil { + t.Errorf("Failed to create mock host pool: %v", err) + } + + stopper := testPool.StartHostPool() + stopper.Close(3 * time.Second) + + // Construct a list of new gateways/nodes to add to ndf + newGatewayLen := len(testNdf.Gateways) + newGateways := make([]ndf.Gateway, newGatewayLen) + newNodes := make([]ndf.Node, newGatewayLen) + for i := 0; i < newGatewayLen; i++ { + // Construct gateways + gwId := id.NewIdFromUInt(uint64(100+i), id.Gateway, t) + newGateways[i] = ndf.Gateway{ID: gwId.Bytes()} + // Construct nodes + nodeId := gwId.DeepCopy() + nodeId.SetType(id.Node) + newNodes[i] = ndf.Node{ID: nodeId.Bytes()} + + } + + newNdf := getTestNdf(t) + // Update the ndf, removing some gateways at a cutoff + newNdf.Gateways = newGateways + newNdf.Nodes = newNodes + + testPool.UpdateNdf(newNdf) + + time.Sleep(2 * time.Second) + + // Check that old gateways are not in pool + for _, ndfGw := range testNdf.Gateways { + gwId, err := id.Unmarshal(ndfGw.ID) + if err != nil { + t.Errorf("Failed to marshal gateway id for %v", ndfGw) + } + if _, ok := testPool.hostMap[*gwId]; ok { + t.Errorf("Expected gateway %v to be removed from pool", gwId) + } + } +} + +// Full happy path test +func TestHostPool_ReplaceHost(t *testing.T) { + manager := newMockManager() + 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 TestHostPool_ReplaceHost_Error(t *testing.T) { + manager := newMockManager() + + // 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 TestHostPool_PruneHostPool(t *testing.T) { + manager := newMockManager() + 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) + + } + + // Construct a host past the error threshold + errorThresholdIndex := 0 + overThreshold := params.errThreshold + 25 + hostList[errorThresholdIndex].SetMetricsTesting(connect.NewMetricTesting(overThreshold, t), t) + oldHost := hostList[0] + + // 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()) + } + } + + // Check that the host list has been has been properly updated + // at the index with a host past the error threshold + retrievedHost := hostPool.hostList[errorThresholdIndex] + if reflect.DeepEqual(oldHost, retrievedHost) { + t.Errorf("Expected host list to have it's bad host replaced. " + + "Contains old host information after pruning") + } + +} + +// Error path: not enough gateways in ndf compared to +// required pool size +func TestHostPool_PruneHostPool_Error(t *testing.T) { + manager := newMockManager() + 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") + } + +} + +// Unit test +func TestHostPool_UpdateNdf(t *testing.T) { + manager := newMockManager() + 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, + } + + // Construct a new Ndf different from original one above + newNdf := getTestNdf(t) + newGateway := ndf.Gateway{ + ID: id.NewIdFromUInt(27, id.Gateway, t).Bytes(), + } + newNdf.Gateways = append(newNdf.Gateways, newGateway) + + // Update pool with the new Ndf + hostPool.UpdateNdf(newNdf) + + // Check that the ndf update flag has been set + if !hostPool.isNdfUpdated { + t.Errorf("Expected ndf updated flag to be set after updateNdf call") + } + + // Check that the host pool's ndf has been modified properly + if !reflect.DeepEqual(newNdf, hostPool.ndf) { + t.Errorf("Host pool ndf not updated to new ndf.") + } +} + +// Full test +func TestHostPool_GetPreferred(t *testing.T) { + manager := newMockManager() + rng := csprng.NewSystemRNG() + testNdf := getTestNdf(t) + testStorage := storage.InitTestingSession(t) + addGwChan := make(chan network.NodeGateway) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + + // Pull all gateways from ndf into host manager + hostMap := make(map[id.ID]bool, 0) + targets := make([]*id.ID, 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 + _, err = manager.AddHost(gwId, gw.Address, nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + + hostMap[*gwId] = true + targets = append(targets, gwId) + + } + + // Call the constructor + testPool, err := NewHostPool(params, rng, testNdf, manager, + testStorage, addGwChan) + if err != nil { + t.Errorf("Failed to create mock host pool: %v", err) + } + + retrievedList := testPool.GetPreferred(targets) + if len(retrievedList) != len(targets) { + t.Errorf("Requested list did not output requested length."+ + "\n\tExpected: %d"+ + "\n\tReceived: %v", len(targets), len(retrievedList)) + } + + // In case where all requested gateways are present + // ensure requested hosts were returned + for _, h := range retrievedList { + if !hostMap[*h.GetId()] { + t.Errorf("A target gateways which should have been returned was not."+ + "\n\tExpected: %v", h.GetId()) + } + } + + // Replace a request with a gateway not in pool + targets[3] = id.NewIdFromUInt(74, id.Gateway, t) + retrievedList = testPool.GetPreferred(targets) + if len(retrievedList) != len(targets) { + t.Errorf("Requested list did not output requested length."+ + "\n\tExpected: %d"+ + "\n\tReceived: %v", len(targets), len(retrievedList)) + } + + // In case where a requested gateway is not present + for _, h := range retrievedList { + if h.GetId().Cmp(targets[3]) { + t.Errorf("Should not have returned ID not in pool") + } + } + +} + +// Unit test +func TestHostPool_GetAny(t *testing.T) { + manager := newMockManager() + rng := csprng.NewSystemRNG() + testNdf := getTestNdf(t) + testStorage := storage.InitTestingSession(t) + addGwChan := make(chan network.NodeGateway) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + + // Pull all gateways from ndf into host manager + 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 + _, err = manager.AddHost(gwId, gw.Address, nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + + } + + // Call the constructor + testPool, err := NewHostPool(params, rng, testNdf, manager, + testStorage, addGwChan) + if err != nil { + t.Errorf("Failed to create mock host pool: %v", err) + } + + requested := 3 + anyList := testPool.GetAny(requested) + if len(anyList) != requested { + t.Errorf("GetAnyList did not get requested length."+ + "\n\tExpected: %v"+ + "\n\tReceived: %v", requested, len(anyList)) + } + + for _, h := range anyList { + _, ok := manager.GetHost(h.GetId()) + if !ok { + t.Errorf("Host %s in retrieved list not in manager", h) + } + } + + // Request more than are in host list + largeRequest := requested * 1000 + largeRetrieved := testPool.GetAny(largeRequest) + if len(largeRetrieved) != len(testPool.hostList) { + t.Errorf("Large request should result in a list of all in host list") + } + +} + +// Unit test +func TestHostPool_ForceAdd(t *testing.T) { + manager := newMockManager() + rng := csprng.NewSystemRNG() + testNdf := getTestNdf(t) + testStorage := storage.InitTestingSession(t) + addGwChan := make(chan network.NodeGateway) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + + // Pull all gateways from ndf into host manager + 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 + _, err = manager.AddHost(gwId, gw.Address, nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + + } + + // Call the constructor + testPool, err := NewHostPool(params, rng, testNdf, manager, + testStorage, addGwChan) + if err != nil { + t.Errorf("Failed to create mock host pool: %v", err) + } + + // Construct a list of new gateways to add + newGatewayLen := 10 + newGateways := make([]*id.ID, newGatewayLen) + for i := 0; i < newGatewayLen; i++ { + gwId := id.NewIdFromUInt(uint64(100+i), id.Gateway, t) + // Add mock gateway to manager + _, err = manager.AddHost(gwId, "", nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + newGateways[i] = gwId + } + + // ForceAdd list of gateways + err = testPool.ForceAdd(newGateways) + if err != nil { + t.Errorf("Could not add gateways: %v", err) + } + + for _, gw := range newGateways { + if _, ok := testPool.hostMap[*gw]; !ok { + t.Errorf("Failed to forcefully add new gateway ID: %v", gw) + } + } + +} + +// Unit test which only adds information to ndf +func TestHostPool_UpdateConns_AddGateways(t *testing.T) { + manager := newMockManager() + rng := csprng.NewSystemRNG() + testNdf := getTestNdf(t) + testStorage := storage.InitTestingSession(t) + addGwChan := make(chan network.NodeGateway) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + + // Pull all gateways from ndf into host manager + 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 + _, err = manager.AddHost(gwId, gw.Address, nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + + } + + // Call the constructor + testPool, err := NewHostPool(params, rng, testNdf, manager, + testStorage, addGwChan) + if err != nil { + t.Errorf("Failed to create mock host pool: %v", err) + } + + // Construct a list of new gateways/nodes to add to ndf + newGatewayLen := 10 + newGateways := make([]ndf.Gateway, newGatewayLen) + newNodes := make([]ndf.Node, newGatewayLen) + for i := 0; i < newGatewayLen; i++ { + // Construct gateways + gwId := id.NewIdFromUInt(uint64(100+i), id.Gateway, t) + newGateways[i] = ndf.Gateway{ID: gwId.Bytes()} + // Construct nodes + nodeId := gwId.DeepCopy() + nodeId.SetType(id.Node) + newNodes[i] = ndf.Node{ID: nodeId.Bytes()} + + } + + // Update the ndf + newNdf := getTestNdf(t) + newNdf.Gateways = append(newNdf.Gateways, newGateways...) + newNdf.Nodes = append(newNdf.Nodes, newNodes...) + + testPool.UpdateNdf(newNdf) + + // Update the connections + err = testPool.updateConns() + if err != nil { + t.Errorf("Failed to update connections: %v", err) + } + + // Check that new gateways are in manager + for _, ndfGw := range newGateways { + gwId, err := id.Unmarshal(ndfGw.ID) + if err != nil { + t.Errorf("Failed to marshal gateway id for %v", ndfGw) + } + _, ok := testPool.GetSpecific(gwId) + if !ok { + t.Errorf("Failed to find gateway %v in manager", gwId) + } + } + +} + +// Unit test which only adds information to ndf +func TestHostPool_UpdateConns_RemoveGateways(t *testing.T) { + manager := newMockManager() + rng := csprng.NewSystemRNG() + testNdf := getTestNdf(t) + testStorage := storage.InitTestingSession(t) + addGwChan := make(chan network.NodeGateway) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + + // Pull all gateways from ndf into host manager + 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 + _, err = manager.AddHost(gwId, gw.Address, nil, connect.GetDefaultHostParams()) + if err != nil { + t.Errorf("Could not add mock host to manager: %v", err) + t.FailNow() + } + + } + + // Call the constructor + testPool, err := NewHostPool(params, rng, testNdf, manager, + testStorage, addGwChan) + if err != nil { + t.Errorf("Failed to create mock host pool: %v", err) + } + + // Construct a list of new gateways/nodes to add to ndf + newGatewayLen := len(testNdf.Gateways) + newGateways := make([]ndf.Gateway, newGatewayLen) + newNodes := make([]ndf.Node, newGatewayLen) + for i := 0; i < newGatewayLen; i++ { + // Construct gateways + gwId := id.NewIdFromUInt(uint64(100+i), id.Gateway, t) + newGateways[i] = ndf.Gateway{ID: gwId.Bytes()} + // Construct nodes + nodeId := gwId.DeepCopy() + nodeId.SetType(id.Node) + newNodes[i] = ndf.Node{ID: nodeId.Bytes()} + + } + + // Update the ndf, replacing old data entirely + newNdf := getTestNdf(t) + newNdf.Gateways = newGateways + newNdf.Nodes = newNodes + + testPool.UpdateNdf(newNdf) + + // Update the connections + err = testPool.updateConns() + if err != nil { + t.Errorf("Failed to update connections: %v", err) + } + + // Check that old gateways are not in pool + for _, ndfGw := range testNdf.Gateways { + gwId, err := id.Unmarshal(ndfGw.ID) + if err != nil { + t.Errorf("Failed to marshal gateway id for %v", ndfGw) + } + if _, ok := testPool.hostMap[*gwId]; ok { + t.Errorf("Expected gateway %v to be removed from pool", gwId) + } + } +} + +// Unit test +func TestHostPool_AddGateway(t *testing.T) { + manager := newMockManager() + testNdf := getTestNdf(t) + newIndex := uint32(20) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + + // 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, + addGatewayChan: make(chan network.NodeGateway), + storage: storage.InitTestingSession(t), + } + + ndfIndex := 0 + + gwId, err := id.Unmarshal(testNdf.Gateways[ndfIndex].ID) + if err != nil { + t.Errorf("Failed to unmarshal ID in mock ndf: %v", err) + } + + hostPool.addGateway(gwId, ndfIndex) + + _, ok := manager.GetHost(gwId) + if !ok { + t.Errorf("Unsuccessfully added host to manager") + } +} + +// Unit test +func TestHostPool_RemoveGateway(t *testing.T) { + manager := newMockManager() + testNdf := getTestNdf(t) + newIndex := uint32(20) + params := DefaultPoolParams() + params.poolSize = uint32(len(testNdf.Gateways)) + + // 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, + addGatewayChan: make(chan network.NodeGateway), + storage: storage.InitTestingSession(t), + } + + ndfIndex := 0 + + gwId, err := id.Unmarshal(testNdf.Gateways[ndfIndex].ID) + if err != nil { + t.Errorf("Failed to unmarshal ID in mock ndf: %v", err) + } + + // Add the new gateway host + h, err := hostPool.manager.AddHost(gwId, "", nil, params.hostParams) + if err != nil { + jww.ERROR.Printf("Could not add gateway host %s: %+v", gwId, err) + } + + // Manually add host information + hostPool.hostMap[*gwId] = uint32(ndfIndex) + hostPool.hostList[ndfIndex] = h + + // Call the removal + hostPool.removeGateway(gwId) + + // Check that the map and list have been updated + if hostPool.hostList[ndfIndex] != nil { + t.Errorf("Host list index was not set to nil after removal") + } + + if _, ok := hostPool.hostMap[*gwId]; ok { + t.Errorf("Host map did not delete host entry") + } +} diff --git a/network/gateway/utils_test.go b/network/gateway/utils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9cb76c7834e9c8f51bde01f50e3e42e209ef412e --- /dev/null +++ b/network/gateway/utils_test.go @@ -0,0 +1,120 @@ +package gateway + +import ( + "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" +) + +// Mock structure adhering to HostManager to be used for happy path +type mockManager struct { + hosts map[string]*connect.Host +} + +// Constructor for mockManager +func newMockManager() *mockManager { + return &mockManager{ + hosts: make(map[string]*connect.Host), + } +} + +func (mhp *mockManager) GetHost(hostId *id.ID) (*connect.Host, bool) { + h, ok := mhp.hosts[hostId.String()] + return h, ok +} + +func (mhp *mockManager) 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 *mockManager) 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", + }}, + Nodes: []ndf.Node{{ + ID: id.NewIdFromUInt(0, id.Node, face)[:], + Address: "0.0.0.1", + }, { + ID: id.NewIdFromUInt(1, id.Node, face)[:], + Address: "0.0.0.2", + }, { + ID: id.NewIdFromUInt(2, id.Node, face)[:], + Address: "0.0.0.3", + }, { + ID: id.NewIdFromUInt(3, id.Node, face)[:], + Address: "0.0.0.1", + }, { + ID: id.NewIdFromUInt(4, id.Node, face)[:], + Address: "0.0.0.2", + }, { + ID: id.NewIdFromUInt(5, id.Node, face)[:], + Address: "0.0.0.3", + }, { + ID: id.NewIdFromUInt(6, id.Node, face)[:], + Address: "0.0.0.1", + }, { + ID: id.NewIdFromUInt(7, id.Node, face)[:], + Address: "0.0.0.2", + }, { + ID: id.NewIdFromUInt(8, id.Node, face)[:], + Address: "0.0.0.3", + }, { + ID: id.NewIdFromUInt(9, id.Node, face)[:], + Address: "0.0.0.1", + }, { + ID: id.NewIdFromUInt(10, id.Node, face)[:], + Address: "0.0.0.2", + }, { + ID: id.NewIdFromUInt(11, id.Node, face)[:], + Address: "0.0.0.3", + }}, + } +} diff --git a/network/node/register.go b/network/node/register.go index 2b59d1e7ffd23853705541c46eb14242e8c3ca76..a27529b6eb612e9c3ae49a4e07393dbe67d8c347 100644 --- a/network/node/register.go +++ b/network/node/register.go @@ -45,7 +45,7 @@ func StartRegistration(instance *network.Instance, session *storage.Session, rng multi := stoppable.NewMulti("NodeRegistrations") - for i:=uint(0);i<numParallel;i++{ + for i := uint(0); i < numParallel; i++ { stop := stoppable.NewSingle(fmt.Sprintf("NodeRegistration %d", i)) go registerNodes(session, rngGen, comms, stop, c) diff --git a/network/rounds/utils_test.go b/network/rounds/utils_test.go index f8f14e32778574850519b02fcd73da6d2171e2e5..6a5a6b2cadcc80436c55023e657bc7be1aefee34 100644 --- a/network/rounds/utils_test.go +++ b/network/rounds/utils_test.go @@ -39,6 +39,7 @@ const ReturningGateway = "GetMessageRequest" const FalsePositive = "FalsePositive" const PayloadMessage = "Payload" const ErrorGateway = "Error" + type mockMessageRetrievalComms struct { testingSignature *testing.T }