diff --git a/Makefile b/Makefile
index 4e24281ae9bafbb7b0c5ea639f2ca85f7d9d502b..700a0b2f4a2b4bdfe2ca541d9b78a1dcbdfefce3 100644
--- a/Makefile
+++ b/Makefile
@@ -5,8 +5,8 @@ setup:
 
 version:
 	go run main.go generate
-	sed -i.bak 's/package\ cmd/package\ globals/g' version_vars.go
-	mv version_vars.go globals/version_vars.go
+	sed -i.bak 's/package\ cmd/package\ api/g' version_vars.go
+	mv version_vars.go api/version_vars.go
 
 clean:
 	rm -rf vendor/
diff --git a/api/results.go b/api/results.go
index 2c87dd27a4a98ceecfbe8e5704bd9984d4a7814b..885458fe2974d22b16592ba9e3f3ce2f5d4154a2 100644
--- a/api/results.go
+++ b/api/results.go
@@ -8,8 +8,9 @@ package api
 
 import (
 	"fmt"
+	"time"
+
 	jww "github.com/spf13/jwalterweatherman"
-	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/network/gateway"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/comms/network"
@@ -17,7 +18,6 @@ import (
 	"gitlab.com/elixxir/primitives/states"
 	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/primitives/id"
-	"time"
 )
 
 // Enum of possible round results to pass back
@@ -186,7 +186,7 @@ func (c *Client) getHistoricalRounds(msg *pb.HistoricalRounds,
 		// Find a gateway to request about the roundRequests
 		gwHost, err := gateway.Get(instance.GetPartialNdf().Get(), comms, c.rng.GetStream())
 		if err != nil {
-			globals.Log.FATAL.Panicf("Failed to track network, NDF has corrupt "+
+			jww.FATAL.Panicf("Failed to track network, NDF has corrupt "+
 				"data: %s", err)
 		}
 
diff --git a/api/utils_test.go b/api/utils_test.go
index 74360610803c2107181f87efa0f29363d6da3f80..31e09e2d4f5b87a16e434ef1c6eef58423a903b3 100644
--- a/api/utils_test.go
+++ b/api/utils_test.go
@@ -8,8 +8,10 @@
 package api
 
 import (
+	"testing"
+
 	"github.com/pkg/errors"
-	"gitlab.com/elixxir/client/globals"
+	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces/params"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/comms/network"
@@ -21,7 +23,6 @@ import (
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/ndf"
 	"gitlab.com/xx_network/primitives/utils"
-	"testing"
 )
 
 func newTestingClient(face interface{}) (*Client, error) {
@@ -29,7 +30,7 @@ func newTestingClient(face interface{}) (*Client, error) {
 	case *testing.T, *testing.M, *testing.B, *testing.PB:
 		break
 	default:
-		globals.Log.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", face)
+		jww.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", face)
 	}
 
 	def := getNDF(face)
@@ -50,7 +51,7 @@ func newTestingClient(face interface{}) (*Client, error) {
 
 	cert, err := utils.ReadFile(testkeys.GetNodeCertPath())
 	if err != nil {
-		globals.Log.FATAL.Panicf("Failed to create new test Instance: %v", err)
+		jww.FATAL.Panicf("Failed to create new test Instance: %v", err)
 	}
 
 	commsManager.AddHost(&id.Permissioning, "", cert, connect.GetDefaultHostParams())
@@ -74,7 +75,7 @@ func getNDF(face interface{}) *ndf.NetworkDefinition {
 	case *testing.T, *testing.M, *testing.B, *testing.PB:
 		break
 	default:
-		globals.Log.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", face)
+		jww.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", face)
 	}
 
 	cert, _ := utils.ReadFile(testkeys.GetNodeCertPath())
diff --git a/api/version_vars.go b/api/version_vars.go
index 157aee200d3d2502207ae1ed7dbcb66a20db02ba..dc333c8a688faddebc96c5de0f589d9137801308 100644
--- a/api/version_vars.go
+++ b/api/version_vars.go
@@ -1,36 +1,43 @@
 // Code generated by go generate; DO NOT EDIT.
 // This file was generated by robots at
-// 2020-08-10 10:46:23.193662 -0700 PDT m=+0.042594188
+// 2021-03-10 14:16:47.093264 -0800 PST m=+0.046129936
 package api
 
-const GITVERSION = `4ddf4b3 Merge branch 'XX-2471/XXPrimitives' into 'release'`
-const SEMVER = "1.4.0"
+const GITVERSION = `2096d6ae Merge branch 'Anne/v2' into 'release'`
+const SEMVER = "2.0.0"
 const DEPENDENCIES = `module gitlab.com/elixxir/client
 
 go 1.13
 
 require (
 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
-	github.com/golang/protobuf v1.4.2
+	github.com/golang/protobuf v1.4.3
 	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
-	github.com/pelletier/go-toml v1.6.0 // indirect
+	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
 	github.com/pkg/errors v0.9.1
 	github.com/smartystreets/assertions v1.0.1 // indirect
-	github.com/spf13/afero v1.2.2 // indirect
+	github.com/spf13/afero v1.5.1 // indirect
 	github.com/spf13/cast v1.3.1 // indirect
-	github.com/spf13/cobra v1.0.0
+	github.com/spf13/cobra v1.1.1
 	github.com/spf13/jwalterweatherman v1.1.0
-	github.com/spf13/pflag v1.0.5 // indirect
-	github.com/spf13/viper v1.6.2
-	gitlab.com/elixxir/comms v0.0.0-20200810165153-3039323b5656
-	gitlab.com/elixxir/crypto v0.0.0-20200806211835-b8ce4472f399
-	gitlab.com/elixxir/ekv v0.0.0-20200729182028-159355ea5842
-	gitlab.com/elixxir/primitives v0.0.0-20200805174810-86b366d1dd2d
-	gitlab.com/xx_network/comms v0.0.0-20200806235452-3a82720833ba
-	gitlab.com/xx_network/crypto v0.0.0-20200806235322-ede3c15881ce
-	gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da
-	golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
-	gopkg.in/ini.v1 v1.52.0 // indirect
+	github.com/spf13/viper v1.7.1
+	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
+	gitlab.com/elixxir/comms v0.0.4-0.20210310191636-1bca0ddac665
+	gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2
+	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.20210309192940-6b7fb39b4d01
+	gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96
+	gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a
+	golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
+	golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
+	golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
+	google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect
+	google.golang.org/grpc v1.34.0 // indirect
+	google.golang.org/protobuf v1.25.0
+	gopkg.in/ini.v1 v1.62.0 // indirect
 )
 
 replace google.golang.org/grpc => github.com/grpc/grpc-go v1.27.1
diff --git a/cmd/root.go b/cmd/root.go
index 1910ce9566b4ca404962d9099227aade0c81a590..fb1efae75e8475ef671c9fbddd5b94bba61f0d9f 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -96,6 +96,20 @@ var rootCmd = &cobra.Command{
 		client.GetHealth().AddChannel(connected)
 		waitUntilConnected(connected)
 
+		// After connection, make sure we have registered with at least
+		// 85% of the nodes
+		numReg := 1
+		numNotReg := 100
+		for numReg < 3*numNotReg {
+			time.Sleep(1 * time.Second)
+			numReg, numNotReg, err = client.GetNodeRegistrationStatus()
+			if err != nil {
+				jww.FATAL.Panicf("%+v", err)
+			}
+			jww.INFO.Printf("Registering with nodes (%d/%d)...",
+				numReg, (numReg + numNotReg))
+		}
+
 		// Send Messages
 		msgBody := viper.GetString("message")
 
diff --git a/cmd/version.go b/cmd/version.go
index bf0017c5806c652f5bec660b6c02cef246064e45..99af5a4c68449110c4b9281285596caf305b2e78 100644
--- a/cmd/version.go
+++ b/cmd/version.go
@@ -18,7 +18,7 @@ import (
 )
 
 // Change this value to set the version for this build
-const currentVersion = "1.4.0"
+const currentVersion = "2.0.0"
 
 func Version() string {
 	out := fmt.Sprintf("Elixxir Client v%s -- %s\n\n", api.SEMVER,
diff --git a/globals/log.go b/globals/log.go
deleted file mode 100644
index cbcdce07b2ed6a47f49bdf5368508b75e65c3ccf..0000000000000000000000000000000000000000
--- a/globals/log.go
+++ /dev/null
@@ -1,55 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package globals
-
-import (
-	jww "github.com/spf13/jwalterweatherman"
-	"io"
-	"io/ioutil"
-	"log"
-	"os"
-)
-
-// Log is logging everything to this notepad so that the CUI can replace it
-// with its own notepad and get logging statements from the client
-var Log = jww.NewNotepad(jww.LevelInfo, jww.LevelInfo, os.Stdout,
-	ioutil.Discard, "CLIENT", log.Ldate|log.Ltime)
-
-// InitLog initializes logging thresholds and the log path.
-// verbose turns on debug logging, setting the log path to nil
-// uses std out.
-func InitLog(verbose bool, logPath string) *jww.Notepad {
-	logLevel := jww.LevelInfo
-	logFlags := (log.Ldate | log.Ltime)
-	stdOut := io.Writer(os.Stdout)
-	logFile := ioutil.Discard
-
-	// If the verbose flag is set, print all logs and
-	// print microseconds as well
-	if verbose {
-		logLevel = jww.LevelDebug
-		logFlags = (log.Ldate | log.Ltime | log.Lmicroseconds)
-	}
-	// If the logpath is empty or not set to - (stdout),
-	// set up the log file and do not log to stdout
-	if logPath != "" && logPath != "-" {
-		// Create log file, overwrites if existing
-		lF, err := os.OpenFile(logPath,
-			os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
-		if err != nil {
-			Log.WARN.Println("Invalid or missing log path," +
-				" stdout used.")
-		} else {
-			logFile = io.Writer(lF)
-			stdOut = ioutil.Discard
-		}
-	}
-
-	return jww.NewNotepad(logLevel, logLevel, stdOut, logFile,
-		"CLIENT", logFlags)
-}
diff --git a/globals/statusEvents.go b/globals/statusEvents.go
deleted file mode 100644
index fdf8d6ff99f6ac0c5cbc155eebe1a9c645c0ab8f..0000000000000000000000000000000000000000
--- a/globals/statusEvents.go
+++ /dev/null
@@ -1,25 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package globals
-
-//Identity
-const REG_KEYGEN = 1       //Generating Cryptographic Keys
-const REG_PRECAN = 2       //Doing a Precanned Identity (Not Secure)
-const REG_UID_GEN = 3      //Generating User ID
-const REG_PERM = 4         //Validating User Identity With Permissioning Server
-const REG_NODE = 5         //Registering with Nodes
-const REG_FAIL = 6         //Failed to Register with Nodes
-const REG_SECURE_STORE = 7 //Creating Local Secure Session
-const REG_SAVE = 8         //Storing Session
-//UDB registration
-const UDB_REG_PUSHKEY = 9   //Pushing Cryptographic Material to the User Discovery Bot
-const UDB_REG_PUSHUSER = 10 //Registering User with the User Discovery Bot
-//UDB Search
-const UDB_SEARCH_LOOK = 11        //Searching for User in User Discovery
-const UDB_SEARCH_GETKEY = 12      //Getting Keying Material From User Discovery
-const UDB_SEARCH_BUILD_CREDS = 13 //Building secure end to end relationship
diff --git a/globals/storage.go b/globals/storage.go
deleted file mode 100644
index 63f5b2b360b7842cbbb435cf5495568b80c48549..0000000000000000000000000000000000000000
--- a/globals/storage.go
+++ /dev/null
@@ -1,203 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package globals
-
-import (
-	"os"
-	"sync"
-)
-
-const (
-	NoSave uint8 = iota
-	LocationA
-	LocationB
-)
-
-type Storage interface {
-	SetLocation(string, string) error
-	GetLocation() (string, string)
-	SaveA([]byte) error
-	SaveB([]byte) error
-	LoadA() []byte
-	LoadB() []byte
-	IsEmpty() bool
-}
-
-type DefaultStorage struct {
-	locationA string
-	locationB string
-	sync.Mutex
-}
-
-func (ds *DefaultStorage) SetLocation(locationA, locationB string) error {
-	ds.Lock()
-	ds.locationA = locationA
-	ds.locationB = locationB
-	ds.Unlock()
-	return nil
-}
-
-func (ds *DefaultStorage) GetLocation() (string, string) {
-	ds.Lock()
-	defer ds.Unlock()
-	return ds.locationA, ds.locationB
-}
-
-func (ds *DefaultStorage) IsEmpty() bool {
-	_, err := os.Stat(ds.locationA)
-	firstEmpty := err != nil && os.IsNotExist(err)
-	_, err = os.Stat(ds.locationB)
-	secondEmpty := err != nil && os.IsNotExist(err)
-	return firstEmpty && secondEmpty
-}
-
-func (ds *DefaultStorage) SaveA(data []byte) error {
-	return dsSaveHelper(ds.locationA, data)
-}
-
-func (ds *DefaultStorage) LoadA() []byte {
-	return dsLoadHelper(ds.locationA)
-}
-
-func (ds *DefaultStorage) SaveB(data []byte) error {
-	return dsSaveHelper(ds.locationB, data)
-}
-
-func (ds *DefaultStorage) LoadB() []byte {
-	return dsLoadHelper(ds.locationB)
-}
-
-type RamStorage struct {
-	DataA []byte
-	DataB []byte
-}
-
-func (rs *RamStorage) SetLocation(string, string) error {
-	return nil
-}
-
-func (rs *RamStorage) GetLocation() (string, string) {
-	return "", ""
-}
-
-func (rs *RamStorage) SaveA(data []byte) error {
-	rs.DataA = make([]byte, len(data))
-	copy(rs.DataA, data)
-	return nil
-}
-
-func (rs *RamStorage) SaveB(data []byte) error {
-	rs.DataB = make([]byte, len(data))
-	copy(rs.DataB, data)
-	return nil
-}
-
-func (rs *RamStorage) LoadA() []byte {
-	b := make([]byte, len(rs.DataA))
-	copy(b, rs.DataA)
-
-	return b
-}
-
-func (rs *RamStorage) LoadB() []byte {
-	b := make([]byte, len(rs.DataB))
-	copy(b, rs.DataB)
-
-	return b
-}
-
-func (rs *RamStorage) IsEmpty() bool {
-	return (rs.DataA == nil || len(rs.DataA) == 0) && (rs.DataB == nil || len(rs.DataB) == 0)
-}
-
-func dsLoadHelper(loc string) []byte {
-	// Check if the file exists, return nil if it does not
-	finfo, err1 := os.Stat(loc)
-
-	if err1 != nil {
-		Log.ERROR.Printf("Default Storage Load: Unknown Error Occurred on"+
-			" file check: \n  %v", err1.Error())
-		return nil
-	}
-
-	b := make([]byte, finfo.Size())
-
-	// Open the file, return nil if it cannot be opened
-	f, err2 := os.Open(loc)
-
-	defer func() {
-		if f != nil {
-			f.Close()
-		} else {
-			Log.WARN.Println("Could not close file, file is nil")
-		}
-	}()
-
-	if err2 != nil {
-		Log.ERROR.Printf("Default Storage Load: Unknown Error Occurred on"+
-			" file open: \n  %v", err2.Error())
-		return nil
-	}
-
-	// Read the data from the file, return nil if read fails
-	_, err3 := f.Read(b)
-
-	if err3 != nil {
-		Log.ERROR.Printf("Default Storage Load: Unknown Error Occurred on"+
-			" file read: \n  %v", err3.Error())
-		return nil
-	}
-
-	return b
-
-}
-
-func dsSaveHelper(loc string, data []byte) error {
-	//check if the file exists, delete if it does
-	_, err1 := os.Stat(loc)
-
-	if err1 == nil {
-		errRmv := os.Remove(loc)
-		if errRmv != nil {
-			Log.WARN.Printf("Could not remove Storage File B: %s", errRmv)
-		}
-	} else if !os.IsNotExist(err1) {
-		Log.ERROR.Printf("Default Storage Save: Unknown Error Occurred on"+
-			" file check: \n  %v",
-			err1.Error())
-		return err1
-	}
-
-	//create new file
-	f, err2 := os.Create(loc)
-
-	defer func() {
-		if f != nil {
-			f.Close()
-		} else {
-			Log.WARN.Println("Could not close file, file is nil")
-		}
-	}()
-
-	if err2 != nil {
-		Log.ERROR.Printf("Default Storage Save: Unknown Error Occurred on"+
-			" file creation: \n %v", err2.Error())
-		return err2
-	}
-
-	//Save to file
-	_, err3 := f.Write(data)
-
-	if err3 != nil {
-		Log.ERROR.Printf("Default Storage Save: Unknown Error Occurred on"+
-			" file write: \n %v", err3.Error())
-		return err3
-	}
-
-	return nil
-}
diff --git a/globals/storage_test.go b/globals/storage_test.go
deleted file mode 100644
index f7bd60f34ce90f1a474faf83b0918cb7a4b9eae3..0000000000000000000000000000000000000000
--- a/globals/storage_test.go
+++ /dev/null
@@ -1,145 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package globals
-
-import (
-	"os"
-	"reflect"
-	"testing"
-)
-
-func TestInitStorage(t *testing.T) {
-	TestDataA := []byte{12, 14, 54}
-	TestDataB := []byte{69, 42, 32}
-	TestSaveLocA := "testStorageA.data"
-	TestSaveLocB := "testStorageB.data"
-
-	// Test DefaultStorage initialization without existing storage
-	storage := &DefaultStorage{}
-	//Check that storage is empty prior to any Save calls
-	if !storage.IsEmpty() {
-		t.Errorf("ds.IsEmpty failed to detect an empty storage")
-	}
-
-	storage.SetLocation(TestSaveLocA, TestSaveLocB)
-
-	// Test DS saveA
-	err := storage.SaveA(TestDataA)
-	if err != nil {
-		t.Errorf("ds.Save failed to create a save file A at: %v",
-			TestSaveLocA)
-	}
-	// Check that save file was made
-	if !exists(TestSaveLocA) {
-		t.Errorf("ds.Save failed to create a save file A at: %v",
-			TestSaveLocA)
-	}
-	//Check that the storage is not empty after a saveA call
-	if storage.IsEmpty() {
-		t.Errorf("ds.IsEmpty failed to detect a non-empty storage")
-	}
-
-	// Test DS loadA
-	actualData := storage.LoadA()
-	if reflect.DeepEqual(actualData, TestDataA) != true {
-		t.Errorf("ds.Load failed to load expected data on A. Expected:%v Actual:%v",
-			TestDataA, actualData)
-	}
-
-	// Test DS saveB
-	err = storage.SaveB(TestDataB)
-	if err != nil {
-		t.Errorf("ds.Save failed to create a save file B at: %v",
-			TestSaveLocB)
-	}
-	// Check that save file was made
-	if !exists(TestSaveLocB) {
-		t.Errorf("ds.Save failed to create a save file B at: %v",
-			TestSaveLocB)
-	}
-
-	// Test DS loadA
-	actualData = storage.LoadB()
-	if reflect.DeepEqual(actualData, TestDataB) != true {
-		t.Errorf("ds.Load failed to load expected data on B. Expected:%v Actual:%v",
-			TestDataB, actualData)
-	}
-
-	// Test RamStorage
-	store := RamStorage{}
-	actualData = nil
-	// Test A
-	store.SaveA(TestDataA)
-	actualData = store.LoadA()
-	if reflect.DeepEqual(actualData, TestDataA) != true {
-		t.Errorf("rs.Load failed to load expected data A. Expected:%v Actual:%v",
-			TestDataA, actualData)
-	}
-	//Test B
-	store.SaveB(TestDataB)
-	actualData = store.LoadB()
-	if reflect.DeepEqual(actualData, TestDataB) != true {
-		t.Errorf("rs.Load failed to load expected data B. Expected:%v Actual:%v",
-			TestDataB, actualData)
-	}
-	os.Remove(TestSaveLocA)
-	os.Remove(TestSaveLocB)
-}
-
-// exists returns whether the given file or directory exists or not
-func exists(path string) bool {
-	_, err := os.Stat(path)
-	if err == nil {
-		return true
-	}
-	if os.IsNotExist(err) {
-		return false
-	}
-	return true
-}
-
-func TestDefaultStorage_GetLocation(t *testing.T) {
-	locationA := "hi"
-	locationB := "hi2"
-
-	ds := DefaultStorage{locationA: locationA, locationB: locationB}
-
-	recievedLocA, recievedLocB := ds.GetLocation()
-
-	if recievedLocA != locationA {
-		t.Errorf("defaultStorage.GetLocation returned incorrect location A. Expected:%v Actual:%v",
-			locationA, recievedLocA)
-	}
-
-	if recievedLocB != locationB {
-		t.Errorf("defaultStorage.GetLocation returned incorrect location B. Expected:%v Actual:%v",
-			locationB, recievedLocB)
-	}
-}
-
-func TestRamStorage_GetLocation(t *testing.T) {
-
-	ds := RamStorage{}
-
-	a, b := ds.GetLocation()
-
-	if a != "" && b != "" {
-		t.Errorf("RamStorage.GetLocation returned incorrect location. Actual: '', ''; Expected:'%v','%v'",
-			a, b)
-	}
-}
-
-func Test_dsLoadHelper_LocError(t *testing.T) {
-	testLoc := "~a/test"
-
-	result := dsLoadHelper(testLoc)
-
-	if result != nil {
-		t.Errorf("dsLoadHelper() did not error on invalid path.")
-	}
-}
diff --git a/globals/terminator.go b/globals/terminator.go
deleted file mode 100644
index 0b3155e19db73add793dad56fa45abc43a924724..0000000000000000000000000000000000000000
--- a/globals/terminator.go
+++ /dev/null
@@ -1,48 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package globals
-
-import (
-	"time"
-)
-
-type ThreadTerminator chan chan bool
-
-func NewThreadTerminator() ThreadTerminator {
-	t := make(chan chan bool, 1)
-	return t
-}
-
-func (t ThreadTerminator) Terminate() {
-	t <- nil
-}
-
-// Try's to kill a thread controlled by a termination channel for the length of
-// the timeout, returns its success. pass 0 for no timeout
-func (t ThreadTerminator) BlockingTerminate(timeout uint64) bool {
-
-	killNotify := make(chan bool)
-	defer close(killNotify)
-
-	if timeout != 0 {
-		timer := time.NewTimer(time.Duration(timeout) * time.Millisecond)
-		defer timer.Stop()
-
-		t <- killNotify
-
-		select {
-		case _ = <-killNotify:
-			return true
-		case <-timer.C:
-			return false
-		}
-	} else {
-		_ = <-killNotify
-		return true
-	}
-}
diff --git a/globals/terminator_test.go b/globals/terminator_test.go
deleted file mode 100644
index 8d18841de618d2f66407aae3e6fdd96bdc9688f9..0000000000000000000000000000000000000000
--- a/globals/terminator_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package globals
-
-import (
-	"testing"
-	"time"
-)
-
-func TestNewThreadTerminator(t *testing.T) {
-
-	term := NewThreadTerminator()
-
-	var success bool
-
-	go func(term ThreadTerminator) {
-		term <- nil
-	}(term)
-
-	timer := time.NewTimer(time.Duration(1000) * time.Millisecond)
-	defer timer.Stop()
-
-	select {
-	case _ = <-term:
-		success = true
-	case <-timer.C:
-		success = false
-	}
-
-	if !success {
-		t.Errorf("NewThreadTerminator: Could not use the ThreadTerminator to" +
-			" stop a thread")
-	}
-
-}
-
-func TestBlockingTerminate(t *testing.T) {
-
-	term := NewThreadTerminator()
-
-	go func(term ThreadTerminator) {
-		var killNotify chan<- bool
-
-		q := false
-
-		for !q {
-			select {
-			case killNotify = <-term:
-				q = true
-			}
-
-			close(term)
-
-			killNotify <- true
-
-		}
-	}(term)
-
-	success := term.BlockingTerminate(1000)
-
-	if !success {
-		t.Errorf("BlockingTerminate: Thread did not terminate in time")
-	}
-
-}
diff --git a/globals/version_vars.go b/globals/version_vars.go
deleted file mode 100644
index dd88debfef2a3612496bef795f5e60ef06bcfd1a..0000000000000000000000000000000000000000
--- a/globals/version_vars.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Code generated by go generate; DO NOT EDIT.
-// This file was generated by robots at
-// 2021-03-09 09:42:42.079202 -0800 PST m=+0.041021439
-package globals
-
-const GITVERSION = `b7ed6d75 Merge branch 'release' into 'XX-3118/ClientVersion'`
-const SEMVER = "1.4.0"
-const DEPENDENCIES = `module gitlab.com/elixxir/client
-
-go 1.13
-
-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/magiconair/properties v1.8.4 // indirect
-	github.com/mitchellh/mapstructure v1.4.0 // indirect
-	github.com/pelletier/go-toml v1.8.1 // indirect
-	github.com/pkg/errors v0.9.1
-	github.com/smartystreets/assertions v1.0.1 // indirect
-	github.com/spf13/afero v1.5.1 // indirect
-	github.com/spf13/cast v1.3.1 // indirect
-	github.com/spf13/cobra v1.1.1
-	github.com/spf13/jwalterweatherman v1.1.0
-	github.com/spf13/viper v1.7.1
-	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20210308190443-cc7781473650
-	gitlab.com/elixxir/crypto v0.0.7-0.20210308214444-5ff53d20fcfc
-	gitlab.com/elixxir/ekv v0.1.4
-	gitlab.com/elixxir/primitives v0.0.3-0.20210305221357-0b3f19cc3c8a
-	gitlab.com/xx_network/comms v0.0.4-0.20210305221336-a7e28febdbf6
-	gitlab.com/xx_network/crypto v0.0.5-0.20210305221255-f0ef174f5135
-	gitlab.com/xx_network/primitives v0.0.4-0.20210308175329-436b0c8753ea
-	golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
-	golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
-	golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
-	google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect
-	google.golang.org/grpc v1.34.0 // indirect
-	google.golang.org/protobuf v1.25.0
-	gopkg.in/ini.v1 v1.62.0 // indirect
-)
-
-replace google.golang.org/grpc => github.com/grpc/grpc-go v1.27.1
-`
diff --git a/go.mod b/go.mod
index 06725cf2cb1d5b599f102b16646401d8ecca4f96..cbd7c3d26f28e77fded7b281126283cacffa2857 100644
--- a/go.mod
+++ b/go.mod
@@ -17,13 +17,13 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20210309195247-fc17eb8560cf
-	gitlab.com/elixxir/crypto v0.0.7-0.20210308214444-5ff53d20fcfc
+	gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35
+	gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2
 	gitlab.com/elixxir/ekv v0.1.4
-	gitlab.com/elixxir/primitives v0.0.3-0.20210305221357-0b3f19cc3c8a
-	gitlab.com/xx_network/comms v0.0.4-0.20210305221336-a7e28febdbf6
-	gitlab.com/xx_network/crypto v0.0.5-0.20210305221255-f0ef174f5135
-	gitlab.com/xx_network/primitives v0.0.4-0.20210308175329-436b0c8753ea
+	gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b
+	gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01
+	gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96
+	gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a
 	golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
 	golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
 	golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
diff --git a/go.sum b/go.sum
index 32417a23e4d4a2a2ed4d7a4cb3bc69f6e1abb41b..15bdda0ba26fa5f23d7aa3c5993d95b458cf8a7d 100644
--- a/go.sum
+++ b/go.sum
@@ -251,20 +251,20 @@ github.com/zeebo/pcg v1.0.0 h1:dt+dx+HvX8g7Un32rY9XWoYnd0NmKmrIzpHF7qiTDj0=
 github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0BJIk1HIzBVMjWNjIUfstrsXC2VqLYPcA=
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
-gitlab.com/elixxir/comms v0.0.4-0.20210308190443-cc7781473650 h1:OAUlZT0kw51DZARSUzR4eEpQKigbafZ0faWGcsDXFD0=
-gitlab.com/elixxir/comms v0.0.4-0.20210308190443-cc7781473650/go.mod h1:DNJES7GZaGZnXsIDDP0nNS3lk21mhN27byQlODA+5fA=
-gitlab.com/elixxir/comms v0.0.4-0.20210309184509-e92f830c628b h1:+uPaSUaDIRG639c55WK6b16+fZnOygjDiqcx+ylzV8s=
-gitlab.com/elixxir/comms v0.0.4-0.20210309184509-e92f830c628b/go.mod h1:DNJES7GZaGZnXsIDDP0nNS3lk21mhN27byQlODA+5fA=
-gitlab.com/elixxir/comms v0.0.4-0.20210309195247-fc17eb8560cf h1:/VDBO0GxcpqGVOFBFeuk/yKxWOKC6j7A2gLnOc1Syuc=
-gitlab.com/elixxir/comms v0.0.4-0.20210309195247-fc17eb8560cf/go.mod h1:DNJES7GZaGZnXsIDDP0nNS3lk21mhN27byQlODA+5fA=
+gitlab.com/elixxir/comms v0.0.4-0.20210310191320-05cba0d1a468 h1:EnfzSAdV+3WwWJ6cGY5xENrHwafy8tIUtz5qvDEZ4sw=
+gitlab.com/elixxir/comms v0.0.4-0.20210310191320-05cba0d1a468/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210310191636-1bca0ddac665 h1:2tWjyhX21DBXeAjiHJTFL/MCpb9L9mg7NE09sS8tb2k=
+gitlab.com/elixxir/comms v0.0.4-0.20210310191636-1bca0ddac665/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210310223853-60622bd841a8 h1:jhka79rXEc7hHn6uDbAjY7NhhKYWwtQ+iHtsa/Jfw1w=
+gitlab.com/elixxir/comms v0.0.4-0.20210310223853-60622bd841a8/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35 h1:t/ILeoWel5Im+zLQUX2FIroZvrfAkxOaL3DCA8enKcE=
+gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4 h1:28ftZDeYEko7xptCZzeFWS1Iam95dj46TWFVVlKmw6A=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3 h1:znCt/x2bL4y8czTPaaFkwzdgSgW3BJc/1+dxyf1jqVw=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
-gitlab.com/elixxir/crypto v0.0.7-0.20210305221450-c94429c34886 h1:Jt1PV/plsHrb/mK92HVgu+OF9PBDamp8GdbsGj0OexY=
-gitlab.com/elixxir/crypto v0.0.7-0.20210305221450-c94429c34886/go.mod h1:ugRjIGSOJJvVKGfIDpS/k62yeG4cTIrS/YNLqmpEIDM=
-gitlab.com/elixxir/crypto v0.0.7-0.20210308214444-5ff53d20fcfc h1:xFMx+2WSvyYS9VNdrUIm7BJQZKwYRR4+anv2XkhfTHo=
-gitlab.com/elixxir/crypto v0.0.7-0.20210308214444-5ff53d20fcfc/go.mod h1:ugRjIGSOJJvVKGfIDpS/k62yeG4cTIrS/YNLqmpEIDM=
+gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2 h1:JMbUxcOjFpdCBUMZS5g8CWfNdPJ6pP8xsAZbnLj66jc=
+gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2/go.mod h1:TMZMB24OsjF6y3LCyBMzDucbOx1cGQCCeuKV9lJA/DU=
 gitlab.com/elixxir/ekv v0.1.4 h1:NLVMwsFEKArWcsDHu2DbXlm9374iSgn7oIA3rVSsvjc=
 gitlab.com/elixxir/ekv v0.1.4/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4=
 gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
@@ -272,25 +272,23 @@ gitlab.com/elixxir/primitives v0.0.0-20200804170709-a1896d262cd9/go.mod h1:p0Vel
 gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc=
 gitlab.com/elixxir/primitives v0.0.1 h1:q61anawANlNAExfkeQEE1NCsNih6vNV1FFLoUQX6txQ=
 gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE=
-gitlab.com/elixxir/primitives v0.0.3-0.20210305221357-0b3f19cc3c8a h1:NxCES61c3ycIAG76/dAKDTZvzxvh8szcL+RTX+6Wldw=
-gitlab.com/elixxir/primitives v0.0.3-0.20210305221357-0b3f19cc3c8a/go.mod h1:7o0Gcvq6tE2by2hZyd0CDueOLUQGfbcuStnQEtLc56Y=
+gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b h1:TswWfqiZqsdPLeWsfe7VJHMlV01W792kRHGYfYwb2Lk=
+gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b/go.mod h1:/e3a4KPqmA9V22qKSZ9prfYYNzIzvLI8xh7noVV091w=
 gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw=
-gitlab.com/xx_network/comms v0.0.4-0.20210305221336-a7e28febdbf6 h1:poMXjv5/r9TAQ2l0c1HfpMAoG6ASjitUbeS/FZnf3JI=
-gitlab.com/xx_network/comms v0.0.4-0.20210305221336-a7e28febdbf6/go.mod h1:bs4GLnnL1UqInyjT4MbHamgTpiTLNtdC5e61CCnVapo=
+gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01 h1:f93iz7mTHt3r37O97vaQD8otohihLN3OnAEEbDGQdVs=
+gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01/go.mod h1:aNPRHmPssXc1JMJ83DAknT2C2iMgKL1wH3//AqQrhQc=
 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=
-gitlab.com/xx_network/crypto v0.0.5-0.20210305221255-f0ef174f5135 h1:2QuO+5VZerO5hLNNdU9N9jX4Xa20aHs1h15RMNbgONY=
-gitlab.com/xx_network/crypto v0.0.5-0.20210305221255-f0ef174f5135/go.mod h1:Kg4EgRbpzHjxavzuKP9E1JEgYBT0ez80rFtB7Bwuw+g=
+gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96 h1:VZGJNhuU6YunKyK4MbNZf25UxQsmU1bH5SnbK93tI7Q=
+gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96/go.mod h1:TtaHpuX0lcuTTtcq+pz+lMusjyTgvSohIHFOlVwN1uU=
 gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA=
 gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da h1:CCVslUwNC7Ul7NG5nu3ThGTSVUt1TxNRX+47f5TUwnk=
 gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug=
 gitlab.com/xx_network/primitives v0.0.2 h1:r45yKenJ9e7PylI1ZXJ1Es09oYNaYXjxVy9+uYlwo7Y=
 gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc=
-gitlab.com/xx_network/primitives v0.0.4-0.20210303180604-1ee442e6463f h1:nC87BCbInkGS2iG5Z1g5PJC7gNKGgLlJmcMVYmsf7mo=
-gitlab.com/xx_network/primitives v0.0.4-0.20210303180604-1ee442e6463f/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
-gitlab.com/xx_network/primitives v0.0.4-0.20210308175329-436b0c8753ea h1:ljUr+XDu+8aD0ua7Q8ffd/ibEmyxDS6/+FqkHBoeb9U=
-gitlab.com/xx_network/primitives v0.0.4-0.20210308175329-436b0c8753ea/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
+gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a h1:Ume9QbJ4GoJh7v5yg/YVDjowJHx/VFeOC/A4PJZUm9g=
+gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
 gitlab.com/xx_network/ring v0.0.2 h1:TlPjlbFdhtJrwvRgIg4ScdngMTaynx/ByHBRZiXCoL0=
 gitlab.com/xx_network/ring v0.0.2/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
diff --git a/interfaces/params/network.go b/interfaces/params/network.go
index f3de8ea10ee17e5408c2b72d335e8b2ea884e428..b73efc7a3ee56c5d2f24d18643194a22852dbc92 100644
--- a/interfaces/params/network.go
+++ b/interfaces/params/network.go
@@ -21,6 +21,8 @@ type Network struct {
 	// Longest delay between network events for Health tracker to denote that
 	// the network is in a bad state
 	NetworkHealthTimeout time.Duration
+	//Number of parallel node registration the client is capable of
+	ParallelNodeRegistrations uint
 
 	Rounds
 	Messages
@@ -36,6 +38,7 @@ func GetDefaultNetwork() Network {
 		RegNodesBufferLen:    500,
 		NetworkHealthTimeout: 30 * time.Second,
 		E2EParams:            GetDefaultE2ESessionParams(),
+		ParallelNodeRegistrations: 8,
 	}
 	n.Rounds = GetDefaultRounds()
 	n.Messages = GetDefaultMessage()
diff --git a/keyExchange/utils_test.go b/keyExchange/utils_test.go
index 16bd2f4ec7d224ebed1e44b58ecc077b50e01e44..162db4728cec9ddfa0b91d0f0ae3acc6e0c7c522 100644
--- a/keyExchange/utils_test.go
+++ b/keyExchange/utils_test.go
@@ -8,8 +8,11 @@
 package keyExchange
 
 import (
+	"testing"
+	"time"
+
 	"github.com/golang/protobuf/proto"
-	"gitlab.com/elixxir/client/globals"
+	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces"
 	"gitlab.com/elixxir/client/interfaces/message"
 	"gitlab.com/elixxir/client/interfaces/params"
@@ -28,8 +31,6 @@ import (
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"gitlab.com/xx_network/primitives/ndf"
-	"testing"
-	"time"
 )
 
 // Generate partner ID for two people, used for smoke tests
@@ -108,7 +109,7 @@ func InitTestingContextGeneric(i interface{}) (*storage.Session, interfaces.Netw
 	case *testing.T, *testing.M, *testing.B, *testing.PB:
 		break
 	default:
-		globals.Log.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", i)
+		jww.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", i)
 	}
 
 	thisSession := storage.InitTestingSession(i)
@@ -215,7 +216,7 @@ func InitTestingContextFullExchange(i interface{}) (*storage.Session, *switchboa
 	case *testing.T, *testing.M, *testing.B, *testing.PB:
 		break
 	default:
-		globals.Log.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", i)
+		jww.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", i)
 	}
 
 	thisSession := storage.InitTestingSession(i)
diff --git a/network/ephemeral/testutil.go b/network/ephemeral/testutil.go
index 94df38ab2700eb1699b28db62ddf8b1d57e57745..fbcc4e3e8d031097f4d9447c0904f537451b3d40 100644
--- a/network/ephemeral/testutil.go
+++ b/network/ephemeral/testutil.go
@@ -8,7 +8,9 @@
 package ephemeral
 
 import (
-	"gitlab.com/elixxir/client/globals"
+	"testing"
+
+	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces"
 	"gitlab.com/elixxir/client/interfaces/message"
 	"gitlab.com/elixxir/client/interfaces/params"
@@ -22,7 +24,6 @@ import (
 	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"gitlab.com/xx_network/primitives/ndf"
 	"gitlab.com/xx_network/primitives/utils"
-	"testing"
 )
 
 // testNetworkManager is a test implementation of NetworkManager interface.
@@ -83,7 +84,7 @@ func NewTestNetworkManager(i interface{}) interfaces.NetworkManager {
 	case *testing.T, *testing.M, *testing.B:
 		break
 	default:
-		globals.Log.FATAL.Panicf("initTesting is restricted to testing only."+
+		jww.FATAL.Panicf("initTesting is restricted to testing only."+
 			"Got %T", i)
 	}
 
@@ -91,7 +92,7 @@ func NewTestNetworkManager(i interface{}) interfaces.NetworkManager {
 
 	cert, err := utils.ReadFile(testkeys.GetNodeCertPath())
 	if err != nil {
-		globals.Log.FATAL.Panicf("Failed to create new test Instance: %v", err)
+		jww.FATAL.Panicf("Failed to create new test Instance: %v", err)
 	}
 
 	commsManager.AddHost(&id.Permissioning, "", cert, connect.GetDefaultHostParams())
@@ -101,7 +102,7 @@ func NewTestNetworkManager(i interface{}) interfaces.NetworkManager {
 
 	thisInstance, err := network.NewInstanceTesting(instanceComms, getNDF(), getNDF(), nil, nil, i)
 	if err != nil {
-		globals.Log.FATAL.Panicf("Failed to create new test Instance: %v", err)
+		jww.FATAL.Panicf("Failed to create new test Instance: %v", err)
 	}
 
 	thisManager := &testNetworkManager{instance: thisInstance}
diff --git a/network/gateway/gateway.go b/network/gateway/gateway.go
index 2af245821076802716ffd2bd1412f5a3fdc43540..f4a16cf2e78a5be6fe9cadbcd496b2cd7bb95b79 100644
--- a/network/gateway/gateway.go
+++ b/network/gateway/gateway.go
@@ -12,6 +12,7 @@ import (
 	"fmt"
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/comms/mixmessages"
+	"gitlab.com/elixxir/crypto/shuffle"
 	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/ndf"
@@ -44,20 +45,42 @@ func Get(ndf *ndf.NetworkDefinition, hg HostGetter, rng io.Reader) (*connect.Hos
 	return gwHost, nil
 }
 
-// Get the last gateway Host from the given RoundInfo
-func GetLast(hg HostGetter, ri *mixmessages.RoundInfo) (*connect.Host, error) {
+// GetAllShuffled returns a shufled list of gateway hosts from the specified round
+func GetAllShuffled(hg HostGetter, ri *mixmessages.RoundInfo) ([]*connect.Host, error) {
 	roundTop := ri.GetTopology()
-	lastGw, err := id.Unmarshal(roundTop[len(roundTop)-1])
-	if err != nil {
-		return nil, err
+	hosts := make([]*connect.Host, 0)
+	shuffledList := make([]uint64, 0)
+
+	// Collect all host information from the round
+	for index, _ := range roundTop {
+		selectedId, err := id.Unmarshal(roundTop[index])
+		if err != nil {
+			return nil, err
+		}
+
+		selectedId.SetType(id.Gateway)
+
+		gwHost, ok := hg.GetHost(selectedId)
+		if !ok {
+			return nil, errors.Errorf("Could not find host for gateway %s", selectedId)
+		}
+		hosts = append(hosts, gwHost)
+		shuffledList = append(shuffledList, uint64(index))
 	}
-	lastGw.SetType(id.Gateway)
 
-	gwHost, ok := hg.GetHost(lastGw)
-	if !ok {
-		return nil, errors.Errorf("Could not find host for gateway %s", lastGw)
+	returnHosts := make([]*connect.Host, len(hosts))
+
+	// Shuffle a list corresponding to the valid gateway hosts
+	shuffle.Shuffle(&shuffledList)
+
+	// Index through the shuffled list, building a list
+	// of shuffled gateways from the round
+	for index, shuffledIndex := range shuffledList {
+		returnHosts[index] = hosts[shuffledIndex]
 	}
-	return gwHost, nil
+
+	return returnHosts, nil
+
 }
 
 // ReadUint32 reads an integer from an io.Reader (which should be a CSPRNG)
diff --git a/network/manager.go b/network/manager.go
index 61b86d350a17811452a11740ed288f0abc9d15c5..4693b0a2cfeeb851d53ee39942e64296b09037ea 100644
--- a/network/manager.go
+++ b/network/manager.go
@@ -91,6 +91,10 @@ func NewManager(session *storage.Session, switchboard *switchboard.Switchboard,
 		ReceptionID:      session.User().GetCryptographicIdentity().GetReceptionID(),
 	}
 
+	// register the node registration channel early so login connection updates
+	// get triggered for registration if necessary
+	instance.SetAddGatewayChan(m.NodeRegistration)
+
 	//create sub managers
 	m.message = message.NewManager(m.Internal, m.param.Messages, m.NodeRegistration)
 	m.round = rounds.NewManager(m.Internal, m.param.Rounds, m.message.GetMessageReceptionChannel())
@@ -124,7 +128,7 @@ func (m *manager) Follow(report interfaces.ClientErrorReport) (stoppable.Stoppab
 
 	// Node Updates
 	multi.Add(node.StartRegistration(m.Instance, m.Session, m.Rng,
-		m.Comms, m.NodeRegistration)) // Adding/Keys
+		m.Comms, m.NodeRegistration, m.param.ParallelNodeRegistrations)) // Adding/Keys
 	//TODO-remover
 	//m.runners.Add(StartNodeRemover(m.Context))        // Removing
 
@@ -169,7 +173,8 @@ func (m *manager) CheckGarbledMessages() {
 	m.message.CheckGarbledMessages()
 }
 
-// InProgressRegistrations returns the number of in progress node registrations.
+// InProgressRegistrations returns an approximation of the number of in progress
+// node registrations.
 func (m *manager) InProgressRegistrations() int {
-	return len(m.Internal.NodeRegistration) + 1
+	return len(m.Internal.NodeRegistration)
 }
diff --git a/network/message/sendCmix.go b/network/message/sendCmix.go
index 3994b2084074f5f3b2f4713bd6731201716f27f3..2a5892da077268b3ca9b5881e037fad7842480e3 100644
--- a/network/message/sendCmix.go
+++ b/network/message/sendCmix.go
@@ -80,6 +80,10 @@ func sendCmixHelper(msg format.Message, recipient *id.ID, param params.CMIX, ins
 			continue
 		}
 
+		//add the round on to the list of attempted so it is not tried again
+		attempted.Insert(bestRound)
+
+		//compute if the round is too close to send to
 		roundCutoffTime := time.Unix(0,
 			int64(bestRound.Timestamps[states.QUEUED]))
 		roundCutoffTime.Add(sendTimeBuffer)
@@ -93,7 +97,6 @@ func sendCmixHelper(msg format.Message, recipient *id.ID, param params.CMIX, ins
 				"received which has already started realtime: \n\t started: "+
 				"%s \n\t now: %s", bestRound.ID, recipient, msg.Digest(),
 				roundCutoffTime, now)
-			attempted.Insert(bestRound)
 			continue
 		}
 
@@ -195,9 +198,6 @@ func sendCmixHelper(msg format.Message, recipient *id.ID, param params.CMIX, ins
 		wrappedMsg.MAC = roundKeys.MakeClientGatewayKey(salt,
 			network.GenerateSlotDigest(wrappedMsg))
 
-		//add the round on to the list of attempted so it is not tried again
-		attempted.Insert(bestRound)
-
 		jww.INFO.Printf("Sending to EphID %d (%s) on round %d, "+
 			"(msgDigest: %s, ecrMsgDigest: %s) via gateway %s",
 			ephID.Int64(), recipient, bestRound.ID, msg.Digest(),
@@ -210,9 +210,24 @@ func sendCmixHelper(msg format.Message, recipient *id.ID, param params.CMIX, ins
 			if strings.Contains(err.Error(),
 				"try a different round.") {
 				jww.WARN.Printf("Failed to send to %s (msgDigest: %s) "+
-					"due to round error with rougn %d, retrying: %+v",
+					"due to round error with round %d, retrying: %+v",
 					recipient, msg.Digest(), bestRound.ID, err)
 				continue
+			}else if strings.Contains(err.Error(),
+				"Could not authenticate client. Is the client registered " +
+				"with this node?"){
+				jww.WARN.Printf("Failed to send to %s (msgDigest: %s) "+
+					"via %s due to failed authentication: %s",
+					recipient, msg.Digest(), transmitGateway.GetId(), err)
+				//if we failed to send due to the gateway not recognizing our
+				// authorization, renegotiate with the node to refresh it
+				nodeID := transmitGateway.GetId().DeepCopy()
+				nodeID.SetType(id.Node)
+				//delete the keys
+				session.Cmix().Remove(nodeID)
+				//trigger
+				go handleMissingNodeKeys(instance, nodeRegistration, []*id.ID{nodeID})
+				continue
 			}
 			jww.ERROR.Printf("Failed to send to EphID %d (%s) on "+
 				"round %d, bailing: %+v", ephID.Int64(), recipient,
diff --git a/network/node/register.go b/network/node/register.go
index 9393ec97e3508e1db5b10dc3fae53ac20269e0de..2b59d1e7ffd23853705541c46eb14242e8c3ca76 100644
--- a/network/node/register.go
+++ b/network/node/register.go
@@ -41,14 +41,18 @@ type RegisterNodeCommsInterface interface {
 }
 
 func StartRegistration(instance *network.Instance, session *storage.Session, rngGen *fastRNG.StreamGenerator, comms RegisterNodeCommsInterface,
-	c chan network.NodeGateway) stoppable.Stoppable {
-	stop := stoppable.NewSingle("NodeRegistration")
+	c chan network.NodeGateway, numParallel uint) stoppable.Stoppable {
 
-	instance.SetAddGatewayChan(c)
+	multi := stoppable.NewMulti("NodeRegistrations")
 
-	go registerNodes(session, rngGen, comms, stop, c)
+	for i:=uint(0);i<numParallel;i++{
+		stop := stoppable.NewSingle(fmt.Sprintf("NodeRegistration %d", i))
 
-	return stop
+		go registerNodes(session, rngGen, comms, stop, c)
+		multi.Add(stop)
+	}
+
+	return multi
 }
 
 func registerNodes(session *storage.Session, rngGen *fastRNG.StreamGenerator, comms RegisterNodeCommsInterface,
@@ -74,7 +78,6 @@ func registerNodes(session *storage.Session, rngGen *fastRNG.StreamGenerator, co
 		case <-t.C:
 		}
 	}
-
 }
 
 //registerWithNode serves as a helper for RegisterWithNodes
@@ -94,12 +97,12 @@ func registerWithNode(comms RegisterNodeCommsInterface, ngw network.NodeGateway,
 		return err
 	}
 
-	jww.INFO.Printf("registerWithNode() begin registration with node: %s", nodeID)
-
 	if store.IsRegistered(nodeID) {
 		return nil
 	}
 
+	jww.INFO.Printf("registerWithNode() begin registration with node: %s", nodeID)
+
 	var transmissionKey *cyclic.Int
 	// TODO: should move this to a precanned user initialization
 	if uci.IsPrecanned() {
diff --git a/network/rounds/retrieve.go b/network/rounds/retrieve.go
index e19b1d5f3491b0b6d75e7283632398fd11a5329e..5269357776e9be37984cacfbe31a2208dda912d2 100644
--- a/network/rounds/retrieve.go
+++ b/network/rounds/retrieve.go
@@ -30,6 +30,10 @@ type roundLookup struct {
 	identity  reception.IdentityUse
 }
 
+const noRoundError = "does not have round"
+
+// processMessageRetrieval received a roundLookup request and pings the gateways
+// of that round for messages for the requested identity in the roundLookup
 func (m *Manager) processMessageRetrieval(comms messageRetrievalComms,
 	quitCh <-chan struct{}) {
 
@@ -40,14 +44,50 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms,
 			done = true
 		case rl := <-m.lookupRoundMessages:
 			ri := rl.roundInfo
-			bundle, err := m.getMessagesFromGateway(ri, comms, rl.identity)
+			var bundle message.Bundle
+
+			// Get a shuffled list of gateways in the round
+			gwHosts, err := gateway.GetAllShuffled(comms, ri)
 			if err != nil {
-				jww.WARN.Printf("Failed to get messages for round %v: %s",
-					ri.ID, err)
+				jww.WARN.Printf("Failed to get gateway hosts from "+
+					"round %v, not requesting from them",
+					ri.ID)
+				break
+			}
+
+			// Attempt to request messages for every gateway in the list.
+			// If we retrieve without error, then we exit. If we error, then
+			// we retry with the next gateway in the list until we exhaust the list
+			for i, gwHost := range gwHosts {
+				// Attempt to request for this gateway
+				bundle, err = m.getMessagesFromGateway(id.Round(ri.ID), rl.identity, comms, gwHost)
+				if err != nil {
+
+					jww.WARN.Printf("Failed on gateway [%d/%d] to get messages for round %v",
+						i, len(gwHosts), ri.ID)
+
+					// Retry for the next gateway in the list
+					continue
+				}
+
+				// If a non-error request, no longer retry
 				break
+
+			}
+			gwIDs := make([]*id.ID, 0)
+			for _, gwHost := range gwHosts {
+				gwIDs = append(gwIDs, gwHost.GetId())
 			}
 
-			if len(bundle.Messages) != 0 {
+			// After trying all gateways, if none returned we mark the round as a
+			// failure and print out the last error
+			if err != nil {
+				m.p.Fail(id.Round(ri.ID), rl.identity.EphId, rl.identity.Source)
+				jww.ERROR.Printf("Failed to get pickup round %d "+
+					"from all gateways (%v): final gateway %s returned : %s",
+					id.Round(ri.ID), gwIDs, gwHosts[len(gwHosts)-1].GetId(), err)
+			} else if len(bundle.Messages) != 0 {
+				// If successful and there are messages, we send them to another thread
 				bundle.Identity = rl.identity
 				m.messageBundles <- bundle
 			}
@@ -55,38 +95,29 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms,
 	}
 }
 
-func (m *Manager) getMessagesFromGateway(roundInfo *pb.RoundInfo,
-	comms messageRetrievalComms, identity reception.IdentityUse) (message.Bundle, error) {
-
-	rid := id.Round(roundInfo.ID)
+// getMessagesFromGateway attempts to get messages from their assigned
+// gateway host in the round specified. If successful
+func (m *Manager) getMessagesFromGateway(roundID id.Round, identity reception.IdentityUse,
+	comms messageRetrievalComms, gwHost *connect.Host) (message.Bundle, error) {
 
-	//Get the host object for the gateway to send to
-	gwHost, err := gateway.GetLast(comms, roundInfo)
-	if err != nil {
-		return message.Bundle{}, errors.WithMessage(err, "Failed to get Gateway "+
-			"to request from")
-	}
-
-	jww.INFO.Printf("Getting messages for RoundID %v for EphID %d "+
-		"via Gateway: %s", rid, identity.EphId, gwHost.GetId())
+	jww.DEBUG.Printf("Trying to get messages for round %v for ephmeralID %d (%v)  "+
+		"via Gateway: %s", roundID, identity.EphId.Int64(), identity.Source.String(), gwHost.GetId())
 
 	// send the request
 	msgReq := &pb.GetMessages{
 		ClientID: identity.EphId[:],
-		RoundID:  uint64(rid),
+		RoundID:  uint64(roundID),
 	}
 	msgResp, err := comms.RequestMessages(gwHost, msgReq)
 	// Fail the round if an error occurs so it can be tried again later
 	if err != nil {
-		m.p.Fail(id.Round(roundInfo.ID), identity.EphId, identity.Source)
 		return message.Bundle{}, errors.WithMessagef(err, "Failed to "+
-			"request messages from %s for round %d", gwHost.GetId(), rid)
+			"request messages from %s for round %d", gwHost.GetId(), roundID)
 	}
 	// if the gateway doesnt have the round, return an error
 	if !msgResp.GetHasRound() {
-		m.p.Done(id.Round(roundInfo.ID), identity.EphId, identity.Source)
-		return message.Bundle{}, errors.Errorf("host %s does not have "+
-			"roundID: %d", gwHost.String(), rid)
+		m.p.Done(roundID, identity.EphId, identity.Source)
+		return message.Bundle{}, errors.Errorf(noRoundError)
 	}
 
 	// If there are no messages print a warning. Due to the probabilistic nature
@@ -96,19 +127,19 @@ func (m *Manager) getMessagesFromGateway(roundInfo *pb.RoundInfo,
 		jww.WARN.Printf("host %s has no messages for client %s "+
 			" in round %d. This happening every once in a while is normal,"+
 			" but can be indicitive of a problem if it is consistant", gwHost,
-			m.TransmissionID, rid)
+			m.TransmissionID, roundID)
 		return message.Bundle{}, nil
 	}
 
 	jww.INFO.Printf("Received %d messages in Round %v via Gateway %s for %d (%s)",
-		len(msgs), rid, gwHost.GetId(), identity.EphId.Int64(), identity.Source)
+		len(msgs), roundID, gwHost.GetId(), identity.EphId.Int64(), identity.Source)
 
 	//build the bundle of messages to send to the message processor
 	bundle := message.Bundle{
-		Round:    rid,
+		Round:    roundID,
 		Messages: make([]format.Message, len(msgs)),
 		Finish: func() {
-			m.p.Done(rid, identity.EphId, identity.Source)
+			m.p.Done(roundID, identity.EphId, identity.Source)
 		},
 	}
 
@@ -120,4 +151,5 @@ func (m *Manager) getMessagesFromGateway(roundInfo *pb.RoundInfo,
 	}
 
 	return bundle, nil
+
 }
diff --git a/network/rounds/retrieve_test.go b/network/rounds/retrieve_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2efd2c89c6865ba335d2e167cab343bb786370db
--- /dev/null
+++ b/network/rounds/retrieve_test.go
@@ -0,0 +1,383 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+package rounds
+
+import (
+	"bytes"
+	"gitlab.com/elixxir/client/network/message"
+	"gitlab.com/elixxir/client/storage/reception"
+	pb "gitlab.com/elixxir/comms/mixmessages"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/id/ephemeral"
+	"reflect"
+	"testing"
+	"time"
+)
+
+// Happy path
+func TestManager_ProcessMessageRetrieval(t *testing.T) {
+	// General initializations
+	testManager := newManager(t)
+	roundId := id.Round(5)
+	mockComms := &mockMessageRetrievalComms{testingSignature: t}
+	quitChan := make(chan struct{})
+
+	// Create a local channel so reception is possible (testManager.messageBundles is
+	// send only via newManager call above)
+	messageBundleChan := make(chan message.Bundle)
+	testManager.messageBundles = messageBundleChan
+
+	// Initialize the message retrieval
+	go testManager.processMessageRetrieval(mockComms, quitChan)
+
+	// Construct expected values for checking
+	expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8}
+	payloadMsg := []byte(PayloadMessage)
+	expectedPayload := make([]byte, 256)
+	copy(expectedPayload, payloadMsg)
+
+	go func() {
+		requestGateway := id.NewIdFromString(ReturningGateway, id.Gateway, t)
+
+		// Construct the round lookup
+		iu := reception.IdentityUse{
+			Identity: reception.Identity{
+				EphId:  expectedEphID,
+				Source: requestGateway,
+			},
+		}
+
+		idList := [][]byte{requestGateway.Bytes()}
+
+		roundInfo := &pb.RoundInfo{
+			ID:       uint64(roundId),
+			Topology: idList,
+		}
+
+		// Send a round look up request
+		testManager.lookupRoundMessages <- roundLookup{
+			roundInfo: roundInfo,
+			identity:  iu,
+		}
+
+	}()
+
+	var testBundle message.Bundle
+	go func() {
+		// Receive the bundle over the channel
+		time.Sleep(1 * time.Second)
+		testBundle = <-messageBundleChan
+
+		// Close the process
+		quitChan <- struct{}{}
+
+	}()
+
+	// Ensure bundle received and has expected values
+	time.Sleep(2 * time.Second)
+	if reflect.DeepEqual(testBundle, message.Bundle{}) {
+		t.Errorf("Did not receive a message bundle over the channel")
+		t.FailNow()
+	}
+
+	if testBundle.Identity.EphId.Int64() != expectedEphID.Int64() {
+		t.Errorf("Unexpected ephemeral ID in bundle."+
+			"\n\tExpected: %v"+
+			"\n\tReceived: %v", expectedEphID, testBundle.Identity.EphId)
+	}
+
+	if !bytes.Equal(expectedPayload, testBundle.Messages[0].GetPayloadA()) {
+		t.Errorf("Unexpected ephemeral ID in bundle."+
+			"\n\tExpected: %v"+
+			"\n\tReceived: %v", expectedPayload, testBundle.Messages[0].GetPayloadA())
+
+	}
+
+}
+
+// Utilize the mockComms to construct a gateway which does not have the round
+func TestManager_ProcessMessageRetrieval_NoRound(t *testing.T) {
+	// General initializations
+	testManager := newManager(t)
+	roundId := id.Round(5)
+	mockComms := &mockMessageRetrievalComms{testingSignature: t}
+	quitChan := make(chan struct{})
+
+	// Create a local channel so reception is possible (testManager.messageBundles is
+	// send only via newManager call above)
+	messageBundleChan := make(chan message.Bundle)
+	testManager.messageBundles = messageBundleChan
+
+	// Initialize the message retrieval
+	go testManager.processMessageRetrieval(mockComms, quitChan)
+
+	expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8}
+
+	// Construct a gateway without keyword ID in utils_test.go
+	// ie mockComms does not return a round
+	dummyGateway := id.NewIdFromString("Sauron", id.Gateway, t)
+
+	go func() {
+		// Construct the round lookup
+		iu := reception.IdentityUse{
+			Identity: reception.Identity{
+				EphId:  expectedEphID,
+				Source: dummyGateway,
+			},
+		}
+
+		idList := [][]byte{dummyGateway.Bytes()}
+
+		roundInfo := &pb.RoundInfo{
+			ID:       uint64(roundId),
+			Topology: idList,
+		}
+
+		// Send a round look up request
+		testManager.lookupRoundMessages <- roundLookup{
+			roundInfo: roundInfo,
+			identity:  iu,
+		}
+
+	}()
+
+	var testBundle message.Bundle
+	go func() {
+		// Receive the bundle over the channel
+		time.Sleep(1 * time.Second)
+		testBundle = <-messageBundleChan
+
+		// Close the process
+		quitChan <- struct{}{}
+
+	}()
+
+	time.Sleep(2 * time.Second)
+	if !reflect.DeepEqual(testBundle, message.Bundle{}) {
+		t.Errorf("Should not receive a message bundle, mock gateway should not return round."+
+			"\n\tExpected: %v"+
+			"\n\tReceived: %v", message.Bundle{}, testBundle)
+	}
+}
+
+// Test the path where there are no messages,
+// simulating a false positive in a bloom filter
+func TestManager_ProcessMessageRetrieval_FalsePositive(t *testing.T) {
+	// General initializations
+	testManager := newManager(t)
+	roundId := id.Round(5)
+	mockComms := &mockMessageRetrievalComms{testingSignature: t}
+	quitChan := make(chan struct{})
+
+	// Create a local channel so reception is possible (testManager.messageBundles is
+	// send only via newManager call above)
+	messageBundleChan := make(chan message.Bundle)
+	testManager.messageBundles = messageBundleChan
+
+	// Initialize the message retrieval
+	go testManager.processMessageRetrieval(mockComms, quitChan)
+
+	// Construct expected values for checking
+	expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8}
+	payloadMsg := []byte(PayloadMessage)
+	expectedPayload := make([]byte, 256)
+	copy(expectedPayload, payloadMsg)
+
+	go func() {
+		// Construct the round lookup
+		iu := reception.IdentityUse{
+			Identity: reception.Identity{
+				EphId:  expectedEphID,
+				Source: id.NewIdFromString("Source", id.User, t),
+			},
+		}
+
+		requestGateway := id.NewIdFromString(FalsePositive, id.Gateway, t)
+
+		idList := [][]byte{requestGateway.Bytes()}
+
+		roundInfo := &pb.RoundInfo{
+			ID:       uint64(roundId),
+			Topology: idList,
+		}
+
+		// Send a round look up request
+		testManager.lookupRoundMessages <- roundLookup{
+			roundInfo: roundInfo,
+			identity:  iu,
+		}
+
+	}()
+
+	var testBundle message.Bundle
+	go func() {
+		// Receive the bundle over the channel
+		time.Sleep(1 * time.Second)
+		testBundle = <-messageBundleChan
+
+		// Close the process
+		quitChan <- struct{}{}
+
+	}()
+
+	// Ensure no bundle was received due to false positive test
+	time.Sleep(2 * time.Second)
+	if !reflect.DeepEqual(testBundle, message.Bundle{}) {
+		t.Errorf("Received a message bundle over the channel, should receive empty message list")
+		t.FailNow()
+	}
+
+}
+
+// Ensure that the quit chan closes the program, on an otherwise happy path
+func TestManager_ProcessMessageRetrieval_Quit(t *testing.T) {
+	// General initializations
+	testManager := newManager(t)
+	roundId := id.Round(5)
+	mockComms := &mockMessageRetrievalComms{testingSignature: t}
+	quitChan := make(chan struct{})
+
+	// Create a local channel so reception is possible (testManager.messageBundles is
+	// send only via newManager call above)
+	messageBundleChan := make(chan message.Bundle)
+	testManager.messageBundles = messageBundleChan
+
+	// Initialize the message retrieval
+	go testManager.processMessageRetrieval(mockComms, quitChan)
+
+	// Close the process early, before any logic below can be completed
+	quitChan <- struct{}{}
+
+	// Construct expected values for checking
+	expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8}
+	payloadMsg := []byte(PayloadMessage)
+	expectedPayload := make([]byte, 256)
+	copy(expectedPayload, payloadMsg)
+
+	go func() {
+		// Construct the round lookup
+		iu := reception.IdentityUse{
+			Identity: reception.Identity{
+				EphId: expectedEphID,
+			},
+		}
+
+		requestGateway := id.NewIdFromString(ReturningGateway, id.Gateway, t)
+
+		idList := [][]byte{requestGateway.Bytes()}
+
+		roundInfo := &pb.RoundInfo{
+			ID:       uint64(roundId),
+			Topology: idList,
+		}
+
+		// Send a round look up request
+		testManager.lookupRoundMessages <- roundLookup{
+			roundInfo: roundInfo,
+			identity:  iu,
+		}
+
+	}()
+
+	var testBundle message.Bundle
+	go func() {
+		// Receive the bundle over the channel
+		testBundle = <-messageBundleChan
+
+	}()
+
+	time.Sleep(1 * time.Second)
+	// Ensure no bundle was received due to quiting process early
+	if !reflect.DeepEqual(testBundle, message.Bundle{}) {
+		t.Errorf("Received a message bundle over the channel, process should have quit before reception")
+		t.FailNow()
+	}
+
+}
+
+// Path in which multiple error comms are encountered before a happy path comms
+func TestManager_ProcessMessageRetrieval_MultipleGateways(t *testing.T) {
+	// General initializations
+	testManager := newManager(t)
+	roundId := id.Round(5)
+	mockComms := &mockMessageRetrievalComms{testingSignature: t}
+	quitChan := make(chan struct{})
+
+	// Create a local channel so reception is possible (testManager.messageBundles is
+	// send only via newManager call above)
+	messageBundleChan := make(chan message.Bundle)
+	testManager.messageBundles = messageBundleChan
+
+	// Initialize the message retrieval
+	go testManager.processMessageRetrieval(mockComms, quitChan)
+
+	// Construct expected values for checking
+	expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8}
+	payloadMsg := []byte(PayloadMessage)
+	expectedPayload := make([]byte, 256)
+	copy(expectedPayload, payloadMsg)
+
+	go func() {
+		requestGateway := id.NewIdFromString(ReturningGateway, id.Gateway, t)
+		errorGateway := id.NewIdFromString(ErrorGateway, id.Gateway, t)
+		// Construct the round lookup
+		iu := reception.IdentityUse{
+			Identity: reception.Identity{
+				EphId:  expectedEphID,
+				Source: requestGateway,
+			},
+		}
+
+		// Create a list of ID's in which some error gateways must be contacted before the happy path
+		idList := [][]byte{errorGateway.Bytes(), errorGateway.Bytes(), requestGateway.Bytes()}
+
+		roundInfo := &pb.RoundInfo{
+			ID:       uint64(roundId),
+			Topology: idList,
+		}
+
+		// Send a round look up request
+		testManager.lookupRoundMessages <- roundLookup{
+			roundInfo: roundInfo,
+			identity:  iu,
+		}
+
+	}()
+
+	var testBundle message.Bundle
+	go func() {
+		// Receive the bundle over the channel
+		time.Sleep(1 * time.Second)
+		testBundle = <-messageBundleChan
+
+		// Close the process
+		quitChan <- struct{}{}
+
+	}()
+
+	// Ensure that expected bundle is still received from happy comm
+	// despite initial errors
+	time.Sleep(2 * time.Second)
+	if reflect.DeepEqual(testBundle, message.Bundle{}) {
+		t.Errorf("Did not receive a message bundle over the channel")
+		t.FailNow()
+	}
+
+	if testBundle.Identity.EphId.Int64() != expectedEphID.Int64() {
+		t.Errorf("Unexpected ephemeral ID in bundle."+
+			"\n\tExpected: %v"+
+			"\n\tReceived: %v", expectedEphID, testBundle.Identity.EphId)
+	}
+
+	if !bytes.Equal(expectedPayload, testBundle.Messages[0].GetPayloadA()) {
+		t.Errorf("Unexpected ephemeral ID in bundle."+
+			"\n\tExpected: %v"+
+			"\n\tReceived: %v", expectedPayload, testBundle.Messages[0].GetPayloadA())
+
+	}
+
+}
diff --git a/network/rounds/utils_test.go b/network/rounds/utils_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f8f14e32778574850519b02fcd73da6d2171e2e5
--- /dev/null
+++ b/network/rounds/utils_test.go
@@ -0,0 +1,94 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+package rounds
+
+import (
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/network/internal"
+	"gitlab.com/elixxir/client/network/message"
+	"gitlab.com/elixxir/client/storage"
+	pb "gitlab.com/elixxir/comms/mixmessages"
+	"gitlab.com/xx_network/comms/connect"
+	"gitlab.com/xx_network/primitives/id"
+	"testing"
+)
+
+func newManager(face interface{}) *Manager {
+	sess1 := storage.InitTestingSession(face)
+
+	testManager := &Manager{
+		lookupRoundMessages: make(chan roundLookup),
+		messageBundles:      make(chan message.Bundle),
+		p:                   newProcessingRounds(),
+		Internal: internal.Internal{
+			Session:        sess1,
+			TransmissionID: sess1.GetUser().TransmissionID,
+		},
+	}
+
+	return testManager
+}
+
+// Build ID off of this string for expected gateway
+// which will return on over mock comm
+const ReturningGateway = "GetMessageRequest"
+const FalsePositive = "FalsePositive"
+const PayloadMessage = "Payload"
+const ErrorGateway = "Error"
+type mockMessageRetrievalComms struct {
+	testingSignature *testing.T
+}
+
+func (mmrc *mockMessageRetrievalComms) GetHost(hostId *id.ID) (*connect.Host, bool) {
+	h, _ := connect.NewHost(hostId, "0.0.0.0", []byte(""), connect.HostParams{
+		MaxRetries:  0,
+		AuthEnabled: false,
+	})
+	return h, true
+}
+
+// Mock comm which returns differently based on the host ID
+// ReturningGateway returns a happy path response, in which there is a message
+// FalsePositive returns a response in which there were no messages in the round
+// ErrorGateway returns an error on the mock comm
+// Any other ID returns default no round errors
+func (mmrc *mockMessageRetrievalComms) RequestMessages(host *connect.Host,
+	message *pb.GetMessages) (*pb.GetMessagesResponse, error) {
+	payloadMsg := []byte(PayloadMessage)
+	payload := make([]byte, 256)
+	copy(payload, payloadMsg)
+	testSlot := &pb.Slot{
+		PayloadA: payload,
+		PayloadB: payload,
+	}
+
+	// If we are the requesting on the returning gateway, return a mock response
+	returningGateway := id.NewIdFromString(ReturningGateway, id.Gateway, mmrc.testingSignature)
+	if host.GetId().Cmp(returningGateway) {
+		return &pb.GetMessagesResponse{
+			Messages: []*pb.Slot{testSlot},
+			HasRound: true,
+		}, nil
+	}
+
+	// Return an empty message structure (ie a false positive in the bloom filter)
+	falsePositive := id.NewIdFromString(FalsePositive, id.Gateway, mmrc.testingSignature)
+	if host.GetId().Cmp(falsePositive) {
+		return &pb.GetMessagesResponse{
+			Messages: []*pb.Slot{},
+			HasRound: true,
+		}, nil
+	}
+
+	// Return a mock error
+	errorGateway := id.NewIdFromString(ErrorGateway, id.Gateway, mmrc.testingSignature)
+	if host.GetId().Cmp(errorGateway) {
+		return &pb.GetMessagesResponse{}, errors.Errorf("Connection error")
+	}
+
+	return nil, nil
+}
diff --git a/storage/e2e/session.go b/storage/e2e/session.go
index fbfe0d0bd68b0bad85e97bcf94a5632bb2f7f8a5..76c614861f663ff834e22cc53e7e54c72f58d3ff 100644
--- a/storage/e2e/session.go
+++ b/storage/e2e/session.go
@@ -10,9 +10,13 @@ package e2e
 import (
 	"encoding/json"
 	"fmt"
+	"math/big"
+	"sync"
+	"testing"
+	"time"
+
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
-	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/interfaces/params"
 	"gitlab.com/elixxir/client/storage/versioned"
 	"gitlab.com/elixxir/crypto/cyclic"
@@ -20,10 +24,6 @@ import (
 	"gitlab.com/elixxir/crypto/hash"
 	"gitlab.com/xx_network/crypto/randomness"
 	"gitlab.com/xx_network/primitives/id"
-	"math/big"
-	"sync"
-	"testing"
-	"time"
 )
 
 const currentSessionVersion = 0
@@ -250,7 +250,7 @@ func GetSessionIDFromBaseKeyForTesting(baseKey *cyclic.Int, i interface{}) Sessi
 	case *testing.T, *testing.M, *testing.B, *testing.PB:
 		break
 	default:
-		globals.Log.FATAL.Panicf("GetSessionIDFromBaseKeyForTesting is restricted to testing only. Got %T", i)
+		jww.FATAL.Panicf("GetSessionIDFromBaseKeyForTesting is restricted to testing only. Got %T", i)
 	}
 	return getSessionIDFromBaseKey(baseKey)
 }
diff --git a/storage/session.go b/storage/session.go
index 2eb8652c501d957683f8c72947e3586848c13a11..be1837de7fb32adf9d13549c8f42bfe00683c05f 100644
--- a/storage/session.go
+++ b/storage/session.go
@@ -10,8 +10,11 @@
 package storage
 
 import (
+	"sync"
+	"testing"
+
 	"github.com/pkg/errors"
-	"gitlab.com/elixxir/client/globals"
+	jww "github.com/spf13/jwalterweatherman"
 	userInterface "gitlab.com/elixxir/client/interfaces/user"
 	"gitlab.com/elixxir/client/storage/auth"
 	"gitlab.com/elixxir/client/storage/clientVersion"
@@ -32,8 +35,6 @@ import (
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/ndf"
-	"sync"
-	"testing"
 )
 
 // Number of rounds to store in the CheckedRound buffer
@@ -303,7 +304,7 @@ func InitTestingSession(i interface{}) *Session {
 	case *testing.T, *testing.M, *testing.B, *testing.PB:
 		break
 	default:
-		globals.Log.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", i)
+		jww.FATAL.Panicf("InitTestingSession is restricted to testing only. Got %T", i)
 	}
 
 	privKey, _ := rsa.LoadPrivateKeyFromPem([]byte("-----BEGIN PRIVATE KEY-----\n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\nOaRA5ZbdE7g7vxKRV36jT3wvD7W+\n-----END PRIVATE KEY-----\n"))
@@ -313,7 +314,7 @@ func InitTestingSession(i interface{}) *Session {
 	uid := id.NewIdFromString("zezima", id.User, i)
 	u, err := user.NewUser(kv, uid, uid, []byte("salt"), []byte("salt"), privKey, privKey, false)
 	if err != nil {
-		globals.Log.FATAL.Panicf("InitTestingSession failed to create dummy user: %+v", err)
+		jww.FATAL.Panicf("InitTestingSession failed to create dummy user: %+v", err)
 	}
 	u.SetTransmissionRegistrationValidationSignature([]byte("sig"))
 	u.SetReceptionRegistrationValidationSignature([]byte("sig"))
@@ -337,25 +338,25 @@ func InitTestingSession(i interface{}) *Session {
 			"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", 16))
 	cmixStore, err := cmix.NewStore(cmixGrp, kv, cmixGrp.NewInt(2))
 	if err != nil {
-		globals.Log.FATAL.Panicf("InitTestingSession failed to create dummy cmix session: %+v", err)
+		jww.FATAL.Panicf("InitTestingSession failed to create dummy cmix session: %+v", err)
 	}
 	s.cmix = cmixStore
 
 	e2eStore, err := e2e.NewStore(cmixGrp, kv, cmixGrp.NewInt(2), uid,
 		fastRNG.NewStreamGenerator(7, 3, csprng.NewSystemRNG))
 	if err != nil {
-		globals.Log.FATAL.Panicf("InitTestingSession failed to create dummy cmix session: %+v", err)
+		jww.FATAL.Panicf("InitTestingSession failed to create dummy cmix session: %+v", err)
 	}
 	s.e2e = e2eStore
 
 	s.criticalMessages, err = utility.NewE2eMessageBuffer(s.kv, criticalMessagesKey)
 	if err != nil {
-		globals.Log.FATAL.Panicf("InitTestingSession failed to create dummy critical messages: %+v", err)
+		jww.FATAL.Panicf("InitTestingSession failed to create dummy critical messages: %+v", err)
 	}
 
 	s.garbledMessages, err = utility.NewMeteredCmixMessageBuffer(s.kv, garbledMessagesKey)
 	if err != nil {
-		globals.Log.FATAL.Panicf("Failed to create garbledMessages buffer: %+v", err)
+		jww.FATAL.Panicf("Failed to create garbledMessages buffer: %+v", err)
 	}
 
 	s.conversations = conversation.NewStore(s.kv)