diff --git a/api/client.go b/api/client.go index d1b864acbf58886d8c4a4fe5605d32787ad2b487..5bbad8f8c76dbcafc87b9f50955339a5decaa5d2 100644 --- a/api/client.go +++ b/api/client.go @@ -219,7 +219,7 @@ func Login(storageDir string, password []byte, parameters params.Network) (*Clie } //get the NDF to pass into permissioning and the network manager - def := c.storage.GetBaseNDF() + def := c.storage.GetNDF() //initialize permissioning if def.Registration.Address != "" { @@ -286,7 +286,7 @@ func LoginWithNewBaseNDF_UNSAFE(storageDir string, password []byte, } //store the updated base NDF - c.storage.SetBaseNDF(def) + c.storage.SetNDF(def) //initialize permissioning if def.Registration.Address != "" { @@ -622,7 +622,7 @@ func checkVersionAndSetupStorage(def *ndf.NetworkDefinition, storageDir string, } // Save NDF to be used in the future - storageSess.SetBaseNDF(def) + storageSess.SetNDF(def) if !isPrecanned { //store the registration code for later use diff --git a/network/follow.go b/network/follow.go index aa505deecbb5cda93f1ebed0124ef070cf234705..3d5d8734b83c5892aae387e55e9e4ab6dba23e38 100644 --- a/network/follow.go +++ b/network/follow.go @@ -159,6 +159,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, // update gateway connections m.GetSender().UpdateNdf(m.GetInstance().GetPartialNdf().Get()) + m.Session.SetNDF(m.GetInstance().GetPartialNdf().Get()) } // Update the address space size diff --git a/network/gateway/hostPool.go b/network/gateway/hostPool.go index 6b27cb9d1e3f3b491efa05fc52f3256b9324f587..1447608bfcc825f5e1269b2941d617718461662d 100644 --- a/network/gateway/hostPool.go +++ b/network/gateway/hostPool.go @@ -13,7 +13,6 @@ package gateway import ( "encoding/binary" - "fmt" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage" @@ -115,8 +114,24 @@ func newHostPool(poolParams PoolParams, rng *fastRNG.StreamGenerator, ndf *ndf.N return nil, err } + // Get the last used list of hosts and use it to seed the host pool list + hostList, err := storage.HostList().Get() + numHostsAdded := 0 + if err == nil { + for _, hid := range hostList { + err := result.replaceHostNoStore(hid, uint32(numHostsAdded)) + if err != nil { + jww.WARN.Printf("Unable to add stored host %s: %s", hid, err.Error()) + } else { + numHostsAdded++ + } + } + } else { + jww.WARN.Printf("Building new HostPool because no HostList stored: %+v", err) + } + // Build the initial HostPool and return - for i := 0; i < len(result.hostList); i++ { + for i := numHostsAdded; i < len(result.hostList); i++ { err := result.forceReplace(uint32(i)) if err != nil { return nil, err @@ -279,8 +294,29 @@ func (h *HostPool) forceReplace(oldPoolIndex uint32) error { } } -// Replace the given slot in the HostPool with a new Gateway with the specified ID +// replaceHost replaces the given slot in the HostPool with a new Gateway with +// the specified ID. The resulting host list is saved to storage. func (h *HostPool) replaceHost(newId *id.ID, oldPoolIndex uint32) error { + err := h.replaceHostNoStore(newId, oldPoolIndex) + if err != nil { + return err + } + + // Convert list of of non-nil and non-zero hosts to ID list + idList := make([]*id.ID, 0, len(h.hostList)) + for _, host := range h.hostList { + if host.GetId() != nil && !host.GetId().Cmp(&id.ID{}) { + idList = append(idList, host.GetId()) + } + } + + // Save the list to storage + return h.storage.HostList().Store(idList) +} + +// replaceHostNoStore replaces the given slot in the HostPool with a new Gateway +// with the specified ID. +func (h *HostPool) replaceHostNoStore(newId *id.ID, oldPoolIndex uint32) error { // Obtain that GwId's Host object newHost, ok := h.manager.GetHost(newId) if !ok { @@ -291,7 +327,8 @@ func (h *HostPool) replaceHost(newId *id.ID, oldPoolIndex uint32) error { // Keep track of oldHost for cleanup oldHost := h.hostList[oldPoolIndex] - // Use the poolIdx to overwrite the random Host in the corresponding index in the hostList + // Use the poolIdx to overwrite the random Host in the corresponding index + // in the hostList h.hostList[oldPoolIndex] = newHost // Use the GwId to keep track of the new random Host's index in the hostList h.hostMap[*newId] = oldPoolIndex @@ -301,7 +338,9 @@ func (h *HostPool) replaceHost(newId *id.ID, oldPoolIndex uint32) error { delete(h.hostMap, *oldHost.GetId()) go oldHost.Disconnect() } - jww.DEBUG.Printf("Replaced Host at %d with new Host %s", oldPoolIndex, newId.String()) + jww.DEBUG.Printf("Replaced Host at %d with new Host %s", oldPoolIndex, + newId.String()) + return nil } @@ -388,7 +427,7 @@ func (h *HostPool) removeGateway(gwId *id.ID) { func (h *HostPool) addGateway(gwId *id.ID, ndfIndex int) { gw := h.ndf.Gateways[ndfIndex] - //check if the host exists + // Check if the host exists host, ok := h.manager.GetHost(gwId) if !ok { @@ -443,7 +482,7 @@ func readUint32(rng io.Reader) uint32 { var rndBytes [4]byte i, err := rng.Read(rndBytes[:]) if i != 4 || err != nil { - panic(fmt.Sprintf("cannot read from rng: %+v", err)) + jww.FATAL.Panicf("cannot read from rng: %+v", err) } return binary.BigEndian.Uint32(rndBytes[:]) } diff --git a/network/gateway/hostpool_test.go b/network/gateway/hostpool_test.go index 49dcacf6b8231647b8c0cbc0ab6e6f84b0c7d126..f88bdf49a5c02e757781f3c452b9eef2759862bb 100644 --- a/network/gateway/hostpool_test.go +++ b/network/gateway/hostpool_test.go @@ -54,6 +54,49 @@ func TestNewHostPool(t *testing.T) { } } +// Tests that the hosts are loaded from storage, if they exist. +func TestNewHostPool_HostListStore(t *testing.T) { + manager := newMockManager() + rng := fastRNG.NewStreamGenerator(1, 1, csprng.NewSystemRNG) + testNdf := getTestNdf(t) + testStorage := storage.InitTestingSession(t) + addGwChan := make(chan network.NodeGateway) + params := DefaultPoolParams() + params.MaxPoolSize = uint32(len(testNdf.Gateways)) + + addedIDs := []*id.ID{ + id.NewIdFromString("testID0", id.Gateway, t), + id.NewIdFromString("testID1", id.Gateway, t), + id.NewIdFromString("testID2", id.Gateway, t), + id.NewIdFromString("testID3", id.Gateway, t), + } + err := testStorage.HostList().Store(addedIDs) + if err != nil { + t.Fatalf("Failed to store host list: %+v", err) + } + + for i, hid := range addedIDs { + testNdf.Gateways[i].ID = hid.Marshal() + } + + // Call the constructor + hp, err := newHostPool(params, rng, testNdf, manager, testStorage, addGwChan) + if err != nil { + t.Fatalf("Failed to create mock host pool: %v", err) + } + + // Check that the host list was saved to storage + hostList, err := hp.storage.HostList().Get() + if err != nil { + t.Errorf("Failed to get host list: %+v", err) + } + + if !reflect.DeepEqual(addedIDs, hostList) { + t.Errorf("Failed to save expected host list to storage."+ + "\nexpected: %+v\nreceived: %+v", addedIDs, hostList) + } +} + // Unit test func TestHostPool_ManageHostPool(t *testing.T) { manager := newMockManager() @@ -115,7 +158,7 @@ func TestHostPool_ManageHostPool(t *testing.T) { for _, ndfGw := range testNdf.Gateways { gwId, err := id.Unmarshal(ndfGw.ID) if err != nil { - t.Errorf("Failed to marshal gateway id for %v", ndfGw) + t.Fatalf("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) @@ -135,6 +178,7 @@ func TestHostPool_ReplaceHost(t *testing.T) { hostList: make([]*connect.Host, newIndex+1), hostMap: make(map[id.ID]uint32), ndf: testNdf, + storage: storage.InitTestingSession(t), } /* "Replace" a host with no entry */ @@ -228,6 +272,18 @@ func TestHostPool_ReplaceHost(t *testing.T) { "\n\tReceived: %d", newIndex, retrievedIndex) } + // Check that the host list was saved to storage + hostList, err := hostPool.storage.HostList().Get() + if err != nil { + t.Errorf("Failed to get host list: %+v", err) + } + + expectedList := []*id.ID{gwIdTwo} + + if !reflect.DeepEqual(expectedList, hostList) { + t.Errorf("Failed to save expected host list to storage."+ + "\nexpected: %+v\nreceived: %+v", expectedList, hostList) + } } // Error path, could not get host @@ -754,7 +810,7 @@ func TestHostPool_UpdateConns_RemoveGateways(t *testing.T) { for _, ndfGw := range testNdf.Gateways { gwId, err := id.Unmarshal(ndfGw.ID) if err != nil { - t.Errorf("Failed to marshal gateway id for %v", ndfGw) + t.Fatalf("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) diff --git a/storage/ndf.go b/storage/ndf.go index 14cb4147b89a4fa53de52e505627c12dbd3abc15..1b081fd0f68c32b140492cc5a61b4f8052eb2134 100644 --- a/storage/ndf.go +++ b/storage/ndf.go @@ -13,25 +13,25 @@ import ( "gitlab.com/xx_network/primitives/ndf" ) -const baseNdfKey = "baseNdf" +const ndfKey = "ndf" -func (s *Session) SetBaseNDF(def *ndf.NetworkDefinition) { - err := utility.SaveNDF(s.kv, baseNdfKey, def) +func (s *Session) SetNDF(def *ndf.NetworkDefinition) { + err := utility.SaveNDF(s.kv, ndfKey, def) if err != nil { - jww.FATAL.Printf("Failed to dave the base NDF: %s", err) + jww.FATAL.Printf("Failed to dave the NDF: %+v", err) } - s.baseNdf = def + s.ndf = def } -func (s *Session) GetBaseNDF() *ndf.NetworkDefinition { - if s.baseNdf != nil { - return s.baseNdf +func (s *Session) GetNDF() *ndf.NetworkDefinition { + if s.ndf != nil { + return s.ndf } - def, err := utility.LoadNDF(s.kv, baseNdfKey) + def, err := utility.LoadNDF(s.kv, ndfKey) if err != nil { - jww.FATAL.Printf("Could not load the base NDF: %s", err) + jww.FATAL.Printf("Could not load the NDF: %+v", err) } - s.baseNdf = def + s.ndf = def return def } diff --git a/storage/session.go b/storage/session.go index c2155214b5dec344da60d9e2f2d831387ccabd1b..3c4c1932baf747b080a5272b3702876abb57c652 100644 --- a/storage/session.go +++ b/storage/session.go @@ -51,7 +51,7 @@ type Session struct { //memoized data regStatus RegistrationStatus - baseNdf *ndf.NetworkDefinition + ndf *ndf.NetworkDefinition //sub-stores e2e *e2e.Store @@ -414,5 +414,7 @@ func InitTestingSession(i interface{}) *Session { jww.FATAL.Panicf("Failed to create uncheckRound store: %v", err) } + s.hostList = hostList.NewStore(s.kv) + return s }