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 }