diff --git a/api/results.go b/api/results.go
index 885458fe2974d22b16592ba9e3f3ce2f5d4154a2..4767ec3466557d6b3acaaf1e45c61c6258f1fbc5 100644
--- a/api/results.go
+++ b/api/results.go
@@ -202,8 +202,7 @@ func (c *Client) getHistoricalRounds(msg *pb.HistoricalRounds,
 	// Process historical rounds, sending back to the caller thread
 	for _, ri := range resp.Rounds {
 		sendResults <- ds.EventReturn{
-			ri,
-			false,
+			RoundInfo: ri,
 		}
 	}
 }
diff --git a/api/version_vars.go b/api/version_vars.go
index d407cb5ddaf7f9df6c21780ee86ecf8188a61442..ea464d0318ed736a8a812c6288263928989d1e0d 100644
--- a/api/version_vars.go
+++ b/api/version_vars.go
@@ -1,9 +1,9 @@
 // Code generated by go generate; DO NOT EDIT.
 // This file was generated by robots at
-// 2021-03-23 10:00:45.2486862 -0700 PDT m=+0.046875701
+// 2021-03-23 15:42:17.414835 -0500 CDT m=+0.023247933
 package api
 
-const GITVERSION = `da6ce164 incrememented the version`
+const GITVERSION = `0e4fae51 Merge branch 'betterNumRegistered' into 'release'`
 const SEMVER = "2.2.1"
 const DEPENDENCIES = `module gitlab.com/elixxir/client
 
diff --git a/go.mod b/go.mod
index 9aad06cafa396fedbc8d08875d051383f09b7a0e..ba38a93cc58d70a70c57c5042ea1ec37b4f938b2 100644
--- a/go.mod
+++ b/go.mod
@@ -17,11 +17,11 @@ 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.20210323165848-495b7efbf1af
+	gitlab.com/elixxir/comms v0.0.4-0.20210323210045-5979aed86507
 	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.20210323140408-2b2613abb5a3
+	gitlab.com/xx_network/comms v0.0.4-0.20210323205910-f01316c830dd
 	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 24b4c8b6325e35062ef3f069d83d914ef1dc388b..8fd108a05a47213c580b49cf8fbf0fa768f7d653 100644
--- a/go.sum
+++ b/go.sum
@@ -243,6 +243,7 @@ github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
 github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
 github.com/zeebo/blake3 v0.0.4 h1:vtZ4X8B2lKXZFg2Xyg6Wo36mvmnJvc2VQYTtA4RDCkI=
 github.com/zeebo/blake3 v0.0.4/go.mod h1:YOZo8A49yNqM0X/Y+JmDUZshJWLt1laHsNSn5ny2i34=
+github.com/zeebo/blake3 v0.1.0/go.mod h1:YOZo8A49yNqM0X/Y+JmDUZshJWLt1laHsNSn5ny2i34=
 github.com/zeebo/blake3 v0.1.1 h1:Nbsts7DdKThRHHd+YNlqiGlRqGEF2bE2eXN+xQ1hsEs=
 github.com/zeebo/blake3 v0.1.1/go.mod h1:G9pM4qQwjRzF1/v7+vabMj/c5mWpGZ2Wzo3Eb4z0pb4=
 github.com/zeebo/pcg v0.0.0-20181207190024-3cdc6b625a05 h1:4pW5fMvVkrgkMXdvIsVRRTs69DWYA8uNNQsu1stfVKU=
@@ -251,12 +252,18 @@ 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.3/go.mod h1:5p7oz4yFrK037rPap6ooaWrloJrzuVZ4jnzOdvgyqnU=
 gitlab.com/elixxir/comms v0.0.4-0.20210323165848-495b7efbf1af h1:DyKv7x/N86GNOXoiQ1L2L00J4/n7JaoRlmCYBoyb7O4=
 gitlab.com/elixxir/comms v0.0.4-0.20210323165848-495b7efbf1af/go.mod h1:wuQNnysBXllhSV8ddynQorZEpcHzpMJJ9sPZGsKAxn4=
+gitlab.com/elixxir/comms v0.0.4-0.20210323205404-d2f5a0eddc7e h1:JtTr/HcYyoysRYYc1tbf9Lkda4EsqbwqVjTrHIDtoBI=
+gitlab.com/elixxir/comms v0.0.4-0.20210323205404-d2f5a0eddc7e/go.mod h1:MeAZ3Axo3mfhVU+4UEzyeO0rJ+ZaWcH7a4gqAdMMsFw=
+gitlab.com/elixxir/comms v0.0.4-0.20210323210045-5979aed86507 h1:dcg5pFWMVZntWDaGGjBWyqpz7dmm+A+9LkIQGIXACQ8=
+gitlab.com/elixxir/comms v0.0.4-0.20210323210045-5979aed86507/go.mod h1:aJevwZ7cbfhcd2zJgr+oU3SfWvmvRBw5P3ycbjSlbSc=
 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.4/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
 gitlab.com/elixxir/crypto v0.0.7-0.20210319231554-b73b6e62ddbc h1:r2nJ1opPnvaY/46nqOHxBEh/QlbNH3O7hurfDtJC0Wk=
 gitlab.com/elixxir/crypto v0.0.7-0.20210319231554-b73b6e62ddbc/go.mod h1:Th9bJRvEQecOFW0coD21AzzIBoNDOXcP3+hIGXELCXE=
 gitlab.com/elixxir/ekv v0.1.4 h1:NLVMwsFEKArWcsDHu2DbXlm9374iSgn7oIA3rVSsvjc=
@@ -266,11 +273,17 @@ 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.2/go.mod h1:3fxFHSlQhkV4vs+S0dZEz3Om3m+40WX8L806yvSnNFc=
 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.3/go.mod h1:YViGbRj7FjJYoaO4NpALGEd9dK/l8uUT000FEBbUTL8=
+gitlab.com/xx_network/comms v0.0.4-0.20210322201806-b35c5f0dc6e0 h1:j+Ky4+RstnRQVWIXssUImo6X9aNCgbIOOWJOpcpflak=
+gitlab.com/xx_network/comms v0.0.4-0.20210322201806-b35c5f0dc6e0/go.mod h1:0Hx+zO3Pr4uYw4RZXFnPM3ocjY6bPIKDiHCjWTZLOSI=
 gitlab.com/xx_network/comms v0.0.4-0.20210323140408-2b2613abb5a3 h1:Mh+YiOX89vG93nRodpPJQkLSipEFXmMyoDgWm77u4TA=
 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/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=
@@ -306,6 +319,7 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt
 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -371,6 +385,7 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201014080544-cc95f250f6bc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d h1:jbzgAvDZn8aEnytae+4ou0J0GwFZoHR0hOrTg4qH8GA=
 golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -447,6 +462,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
diff --git a/network/gateway/gateway.go b/network/gateway/gateway.go
index f4a16cf2e78a5be6fe9cadbcd496b2cd7bb95b79..dea0567e8441f173333662709beffb65596530a7 100644
--- a/network/gateway/gateway.go
+++ b/network/gateway/gateway.go
@@ -10,7 +10,9 @@ package gateway
 import (
 	"encoding/binary"
 	"fmt"
+	"github.com/golang-collections/collections/set"
 	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/crypto/shuffle"
 	"gitlab.com/xx_network/comms/connect"
@@ -18,12 +20,167 @@ import (
 	"gitlab.com/xx_network/primitives/ndf"
 	"io"
 	"math"
+	"time"
 )
 
 type HostGetter interface {
 	GetHost(hostId *id.ID) (*connect.Host, bool)
 }
 
+type HostPool struct {
+	hostMap  map[*id.ID]uint32 // map key to its index in the slice
+	hostList []*connect.Host   // each index in the slice contains the value
+	ndfSet   *set.Set
+
+	poolSize uint32
+	rng      io.Reader
+	ndf      *ndf.NetworkDefinition
+	getter   HostGetter
+}
+
+func NewHostPool(poolSize uint32, rng io.Reader, ndf *ndf.NetworkDefinition, getter HostGetter) (*HostPool, error) {
+	result := &HostPool{
+		getter:   getter,
+		hostMap:  make(map[*id.ID]uint32),
+		hostList: make([]*connect.Host, poolSize),
+		poolSize: poolSize,
+		ndf:      ndf,
+		rng:      rng,
+	}
+
+	// Convert the gate
+	var err error
+	result.ndfSet, err = convertNdfToSet(ndf)
+	if err != nil {
+		return nil, err
+	}
+
+	// Build the initial HostPool
+	err = result.buildHostPool()
+	if err != nil {
+		return nil, err
+	}
+	// Start the long-running thread and return
+	result.manageHostPool()
+	return result, nil
+}
+
+// Long-running thread that manages the HostPool on a timer
+func (g *HostPool) manageHostPool() {
+	// TODO: Configurable timer?
+	tick := time.Tick(10 * time.Second)
+	for {
+		select {
+		case <-tick:
+			err := g.pruneHostPool()
+			jww.ERROR.Printf("Unable to prune host pool: %+v", err)
+			// TODO: UpdateGwConns functionality here
+		}
+	}
+}
+
+// Iterate over the hostList, replacing any Hosts with errors
+// with new, randomly-selected Hosts from the NDF
+func (g *HostPool) pruneHostPool() error {
+	for poolIdx := uint32(0); poolIdx < g.poolSize; {
+		host := g.hostList[poolIdx]
+		errCounter := *host.GetMetrics().ErrCounter
+
+		// Check the Host for errors
+		// TODO: Configurable error threshold?
+		if errCounter > 0 {
+			// If errors occurred, randomly select a new Gw by index in the NDF
+			ndfIdx := readRangeUint32(0, uint32(len(g.ndf.Gateways)), g.rng)
+			// Use the random ndfIdx to obtain a GwId from the NDF
+			gwId, err := id.Unmarshal(g.ndf.Gateways[ndfIdx].ID)
+			if err != nil {
+				return errors.WithMessage(err, "failed to get Gateway for pruning")
+			}
+
+			// Verify the GwId is not already in the hostMap
+			if _, ok := g.hostMap[gwId]; !ok {
+				// If it is a new GwId, then obtain that GwId's Host object
+				gwHost, ok := g.getter.GetHost(gwId)
+				if !ok {
+					return errors.Errorf("host for gateway %s could not be "+
+						"retrieved", gwId)
+				}
+
+				// Use the poolIdx to overwrite the random Host in the corresponding index in the hostList
+				g.hostList[poolIdx] = gwHost
+				// Use the GwId to keep track of the new random Host's index in the hostList
+				g.hostMap[gwId] = poolIdx
+
+				// Clean up and move onto next Host
+				host.Disconnect()
+				poolIdx++
+			}
+		}
+	}
+	return nil
+}
+
+// Create the initial hostList and hostMap
+func (g *HostPool) buildHostPool() error {
+	// TODO: Verify this logic chunk
+	ndfLen := uint32(len(g.ndf.Gateways))
+	if ndfLen == 0 || ndfLen < g.poolSize {
+		return errors.Errorf("no gateways available")
+	}
+
+	// Map random NDF indexes to indexes in the hostList
+	indices := make(map[uint32]uint32) // map[ndfIdx]poolIdx
+	for poolIdx := uint32(0); poolIdx < g.poolSize; {
+
+		// Randomly select a Gw by index in the NDF
+		ndfIdx := readRangeUint32(0, ndfLen, g.rng)
+
+		// If that ndfIdx has already been chosen, skip and try again
+		if _, ok := indices[ndfIdx]; !ok {
+			// If not, record the Gw NDF index corresponding to its index in the hostList
+			indices[ndfIdx] = poolIdx
+			poolIdx++
+		}
+	}
+
+	for ndfIdx, poolIdx := range indices {
+
+		// Use the random ndfIdx to obtain a GwId from the NDF
+		gwId, err := id.Unmarshal(g.ndf.Gateways[ndfIdx].ID)
+		if err != nil {
+			return errors.WithMessage(err, "failed to get Gateway")
+		}
+
+		// Then obtain that GwId's Host object
+		gwHost, ok := g.getter.GetHost(gwId)
+		if !ok {
+			return errors.Errorf("host for gateway %s could not be "+
+				"retrieved", gwId)
+		}
+
+		// Use the poolIdx to assign the random Host to an index in the hostList
+		g.hostList[poolIdx] = gwHost
+		// Use the GwId to keep track of the Host's random index in the hostList
+		g.hostMap[gwId] = poolIdx
+	}
+	return nil
+}
+
+// Return a random GwHost from the HostPool
+func (g *HostPool) GetAny() *connect.Host {
+	gwIdx := readRangeUint32(0, g.poolSize, g.rng)
+	return g.hostList[gwIdx]
+}
+
+// Try to obtain a specific host from the HostPool.
+// If that GwId is not in the HostPool, return a random GwHost from the HostPool
+func (g *HostPool) GetSpecific(gwId *id.ID) *connect.Host {
+	if hostIdx, ok := g.hostMap[gwId]; ok {
+		return g.hostList[hostIdx]
+	}
+	return g.GetAny()
+}
+
 // Get the Host of a random gateway in the NDF
 func Get(ndf *ndf.NetworkDefinition, hg HostGetter, rng io.Reader) (*connect.Host, error) {
 	gwLen := uint32(len(ndf.Gateways))
@@ -31,7 +188,7 @@ func Get(ndf *ndf.NetworkDefinition, hg HostGetter, rng io.Reader) (*connect.Hos
 		return nil, errors.Errorf("no gateways available")
 	}
 
-	gwIdx := ReadRangeUint32(0, gwLen, rng)
+	gwIdx := readRangeUint32(0, gwLen, rng)
 	gwID, err := id.Unmarshal(ndf.Nodes[gwIdx].ID)
 	if err != nil {
 		return nil, errors.WithMessage(err, "failed to get Gateway")
@@ -45,14 +202,14 @@ func Get(ndf *ndf.NetworkDefinition, hg HostGetter, rng io.Reader) (*connect.Hos
 	return gwHost, nil
 }
 
-// GetAllShuffled returns a shufled list of gateway hosts from the specified round
+// GetAllShuffled returns a shuffled list of gateway hosts from the specified round
 func GetAllShuffled(hg HostGetter, ri *mixmessages.RoundInfo) ([]*connect.Host, error) {
 	roundTop := ri.GetTopology()
 	hosts := make([]*connect.Host, 0)
 	shuffledList := make([]uint64, 0)
 
 	// Collect all host information from the round
-	for index, _ := range roundTop {
+	for index := range roundTop {
 		selectedId, err := id.Unmarshal(roundTop[index])
 		if err != nil {
 			return nil, err
@@ -83,8 +240,24 @@ func GetAllShuffled(hg HostGetter, ri *mixmessages.RoundInfo) ([]*connect.Host,
 
 }
 
-// ReadUint32 reads an integer from an io.Reader (which should be a CSPRNG)
-func ReadUint32(rng io.Reader) uint32 {
+// Takes ndf.Gateways and puts their IDs into a set.Set object
+func convertNdfToSet(ndf *ndf.NetworkDefinition) (*set.Set, error) {
+	ndfSet := set.New()
+
+	// Process gateway Id's into set
+	for _, gw := range ndf.Gateways {
+		gwId, err := id.Unmarshal(gw.ID)
+		if err != nil {
+			return nil, err
+		}
+		ndfSet.Insert(gwId)
+	}
+
+	return ndfSet, nil
+}
+
+// readUint32 reads an integer from an io.Reader (which should be a CSPRNG)
+func readUint32(rng io.Reader) uint32 {
 	var rndBytes [4]byte
 	i, err := rng.Read(rndBytes[:])
 	if i != 4 || err != nil {
@@ -93,8 +266,8 @@ func ReadUint32(rng io.Reader) uint32 {
 	return binary.BigEndian.Uint32(rndBytes[:])
 }
 
-// ReadRangeUint32 reduces an integer from 0, MaxUint32 to the range start, end
-func ReadRangeUint32(start, end uint32, rng io.Reader) uint32 {
+// readRangeUint32 reduces an integer from 0, MaxUint32 to the range start, end
+func readRangeUint32(start, end uint32, rng io.Reader) uint32 {
 	size := end - start
 	// note we could just do the part inside the () here, but then extra
 	// can == size which means a little bit of range is wastes, either
@@ -103,7 +276,7 @@ func ReadRangeUint32(start, end uint32, rng io.Reader) uint32 {
 	limit := math.MaxUint32 - extra
 	// Loop until we read something inside the limit
 	for {
-		res := ReadUint32(rng)
+		res := readUint32(rng)
 		if res > limit {
 			continue
 		}