diff --git a/api/client.go b/api/client.go index 7885a70538470983de0779ce39280539f012c783..7c5a7a9b455ee89b6162ca921027bc2c81e95df1 100644 --- a/api/client.go +++ b/api/client.go @@ -24,7 +24,6 @@ import ( "gitlab.com/elixxir/client/rekey" "gitlab.com/elixxir/client/user" "gitlab.com/elixxir/comms/connect" - "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/large" "gitlab.com/elixxir/crypto/signature/rsa" @@ -48,8 +47,6 @@ type Client struct { registrationVersion string } -var noNDFErr = errors.New("Failed to get ndf from permissioning: rpc error: code = Unknown desc = Permissioning server does not have an ndf to give to client") - //used to report the state of registration type OperationProgressCallback func(int) @@ -79,11 +76,6 @@ func NewClient(s globals.Storage, locA, locB string, ndfJSON *ndf.NetworkDefinit cl.storage = store cl.commManager = io.NewReceptionManager(cl.rekeyChan) cl.ndf = ndfJSON - //build the topology - nodeIDs := make([]*id.Node, len(cl.ndf.Nodes)) - for i, node := range cl.ndf.Nodes { - nodeIDs[i] = id.NewNodeFromBytes(node.ID) - } //Create the cmix group and init the registry cmixGrp := cyclic.NewGroup( @@ -265,19 +257,23 @@ func FormatTextMessage(message string) []byte { var sessionFileError = errors.New("Session file cannot be loaded and " + "is possibly corrupt. Please contact support@xxmessenger.io") -// Logs in user and sets session on client object -// returns the nickname or error if login fails -func (cl *Client) StartMessageReceiver(callback func(error)) error { +func (cl *Client) InitListeners() error { transmitGateway := id.NewNodeFromBytes(cl.ndf.Nodes[0].ID).NewGateway() transmissionHost, ok := cl.commManager.Comms.GetHost(transmitGateway.String()) if !ok { return errors.New("Failed to retrieve host for transmission") } + // Initialize UDB and nickname "bot" stuff here bots.InitBots(cl.session, cl.commManager, cl.topology, id.NewUserFromBytes(cl.ndf.UDB.ID), transmissionHost) // Initialize Rekey listeners rekey.InitRekey(cl.session, cl.commManager, cl.topology, cl.rekeyChan) + return nil +} +// Logs in user and sets session on client object +// returns the nickname or error if login fails +func (cl *Client) StartMessageReceiver(callback func(error)) error { pollWaitTimeMillis := 1000 * time.Millisecond // TODO Don't start the message receiver if it's already started. // Should be a pretty rare occurrence except perhaps for mobile. @@ -518,7 +514,7 @@ func SetLogOutput(w goio.Writer) { globals.Log.SetLogOutput(w) } -// GetSession returns the session object for external access. Access at your +// GetSession returns the session object for external access. Access at yourx // own risk func (cl *Client) GetSession() user.Session { return cl.session @@ -565,72 +561,3 @@ func (cl *Client) WriteToSessionFile(replacement string, store globals.Storage) return nil } - -//GetUpdatedNDF: Connects to the permissioning server to get the updated NDF from it -func (cl *Client) getUpdatedNDF() (*ndf.NetworkDefinition, error) { // again, uses internal ndf. stay here, return results instead - - //Hash the client's ndf for comparison with registration's ndf - hash := sha256.New() - ndfBytes := cl.ndf.Serialize() - hash.Write(ndfBytes) - ndfHash := hash.Sum(nil) - - //Put the hash in a message - msg := &mixmessages.NDFHash{Hash: ndfHash} - - host, ok := cl.commManager.Comms.GetHost(PermissioningAddrID) - if !ok { - return nil, errors.New("Failed to find permissioning host") - } - - //Send the hash to registration - response, err := cl.commManager.Comms.SendGetUpdatedNDF(host, msg) - if err != nil { - errMsg := fmt.Sprintf("Failed to get ndf from permissioning: %v", err) - return nil, errors.New(errMsg) - } - - //If there was no error and the response is nil, client's ndf is up-to-date - if response == nil { - globals.Log.DEBUG.Printf("Client NDF up-to-date") - return nil, nil - } - - //FixMe: use verify instead? Probs need to add a signature to ndf, like in registration's getupdate? - - globals.Log.INFO.Printf("Remote NDF: %s", string(response.Ndf)) - - //Otherwise pull the ndf out of the response - updatedNdf, _, err := ndf.DecodeNDF(string(response.Ndf)) - if err != nil { - //If there was an error decoding ndf - errMsg := fmt.Sprintf("Failed to decode response to ndf: %v", err) - return nil, errors.New(errMsg) - } - return updatedNdf, nil -} - -//request calls getUpdatedNDF for a new NDF repeatedly until it gets an NDF -func requestNdf(cl *Client) error { - // Continuously polls for a new ndf after sleeping until response if gotten - globals.Log.INFO.Printf("Polling for a new NDF") - newNDf, err := cl.getUpdatedNDF() - - if err != nil { - //lets the client continue when permissioning does not provide NDFs - if err.Error() == noNDFErr.Error() { - globals.Log.WARN.Println("Continuing without an updated NDF") - return nil - } - - errMsg := fmt.Sprintf("Failed to get updated ndf: %v", err) - globals.Log.ERROR.Printf(errMsg) - return errors.New(errMsg) - } - - if newNDf != nil { - cl.ndf = newNDf - } - - return nil -} diff --git a/api/connect.go b/api/connect.go index 86baa3b6ce7e1758c2c28476b0a1fc6a74bf0d49..e449638bc14b2a827d949bd663f2538b611d19eb 100644 --- a/api/connect.go +++ b/api/connect.go @@ -32,10 +32,12 @@ func (cl *Client) InitNetwork() error { cl.registrationVersion = ver //Request a new ndf from - err = requestNdf(cl) + def, err = io.GetUpdatedNDF(cl.ndf, cl.commManager.Comms) if err != nil { return err - + } + if def != nil { + cl.ndf = def } } else { globals.Log.WARN.Println("Registration not defined, not contacted") @@ -90,19 +92,24 @@ func AddGatewayHosts(rm *io.ReceptionManager, definition *ndf.NetworkDefinition) gwAddr := gateway.Address _, err := rm.Comms.AddHost(gwID.String(), gwAddr, gwCreds, false) - if err != nil { - err = errors.Errorf("Failed to create host for gateway %s at %s: %+v", - gwID.String(), gwAddr, err) - if errs != nil { - errs = errors.Wrap(errs, err.Error()) - } else { - errs = err - } - } + errs = handleError(errs, err, gwID.String(), gwAddr) } return errs } +func handleError(base, err error, id, addr string) error { + if err != nil { + err = errors.Errorf("Failed to create host for gateway %s at %s: %+v", + id, addr, err) + if base != nil { + base = errors.Wrap(base, err.Error()) + } else { + base = err + } + } + return base +} + // There's currently no need to keep connected to permissioning constantly, // so we have functions to connect to and disconnect from it when a connection // to permissioning is needed diff --git a/bindings/client.go b/bindings/client.go index fad99df03b562d0aad6e207753caef23e4ef123e..58872b0a2aab3319b9625192185ba7e3fdff6fed 100644 --- a/bindings/client.go +++ b/bindings/client.go @@ -351,3 +351,8 @@ func (cl *Client) WriteToSession(replacement string, storage globals.Storage) er globals.Log.INFO.Printf("Binding call: WriteToSession") return cl.client.WriteToSessionFile(replacement, storage) } + +func (cl *Client) InitListeners() error { + globals.Log.INFO.Printf("Binding call: InitListeners") + return cl.client.InitListeners() +} diff --git a/cmd/root.go b/cmd/root.go index c6ee1af77d70ff453017ffd6e513496be44176b1..138c8b72946dc67f75cc0d7e484fa998de0071d2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -389,8 +389,12 @@ var rootCmd = &cobra.Command{ cb := func(err error) { globals.Log.ERROR.Print(err) } - err := client.StartMessageReceiver(cb) + err := client.InitListeners() + if err != nil { + globals.Log.FATAL.Panicf("Could not initialize receivers: %s\n", err) + } + err = client.StartMessageReceiver(cb) if err != nil { globals.Log.FATAL.Panicf("Could Not start message reciever: %s\n", err) } diff --git a/io/interface.go b/io/interface.go index c7253fda1c7b24b8f82ffc99a7d47e4dd418ab32..1aaf3ce3b57bec5b6ba485da40fe69e906c0e591 100644 --- a/io/interface.go +++ b/io/interface.go @@ -15,7 +15,7 @@ import ( ) // Communication interface implements send/receive functionality with the server -type Communications interface { // this can go +type Communications interface { // SendMessage to the server // TODO(nen) Can we get rid of the crypto type param here? diff --git a/io/receive.go b/io/receive.go index f5cb0991f20fecbe6ec81a61c679c413878e96ab..2eece09ddcebf06d0bd8e811e15fa9a831ca7076 100644 --- a/io/receive.go +++ b/io/receive.go @@ -81,7 +81,8 @@ func (rm *ReceptionManager) MessageReceiver(session user.Session, delay time.Dur globals.Log.WARN.Printf("Rate limit excceded on gateway, pausing polling for 5 seconds") time.Sleep(5 * time.Second) } - callback(err) + go callback(err) + return } NumMessages += len(encryptedMessages) case <-rm.rekeyChan: diff --git a/io/updateNdf.go b/io/updateNdf.go new file mode 100644 index 0000000000000000000000000000000000000000..cad01b158752600f109f4d5cf4de23c61e31b911 --- /dev/null +++ b/io/updateNdf.go @@ -0,0 +1,60 @@ +package io + +import ( + "crypto/sha256" + "fmt" + "github.com/pkg/errors" + "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/comms/client" + "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/primitives/ndf" +) + +var noNDFErr = errors.New("Failed to get ndf from permissioning: rpc error: code = Unknown desc = Permissioning server does not have an ndf to give to client") + +//GetUpdatedNDF: Connects to the permissioning server to get the updated NDF from it +func GetUpdatedNDF(currentDef *ndf.NetworkDefinition, comms *client.Comms) (*ndf.NetworkDefinition, error) { + //Hash the client's ndf for comparison with registration's ndf + hash := sha256.New() + ndfBytes := currentDef.Serialize() + hash.Write(ndfBytes) + ndfHash := hash.Sum(nil) + + //Put the hash in a message + msg := &mixmessages.NDFHash{Hash: ndfHash} + + host, ok := comms.GetHost(PermissioningAddrID) + if !ok { + return nil, errors.New("Failed to find permissioning host") + } + + //Send the hash to registration + response, err := comms.SendGetUpdatedNDF(host, msg) + if err != nil { + errMsg := fmt.Sprintf("Failed to get ndf from permissioning: %v", err) + if errMsg == noNDFErr.Error() { + globals.Log.WARN.Println("Continuing without an updated NDF") + return nil, nil + } + return nil, errors.New(errMsg) + } + + //If there was no error and the response is nil, client's ndf is up-to-date + if response == nil { + globals.Log.DEBUG.Printf("Client NDF up-to-date") + return nil, nil + } + + //FixMe: use verify instead? Probs need to add a signature to ndf, like in registration's getupdate? + + globals.Log.INFO.Printf("Remote NDF: %s", string(response.Ndf)) + + //Otherwise pull the ndf out of the response + updatedNdf, _, err := ndf.DecodeNDF(string(response.Ndf)) + if err != nil { + //If there was an error decoding ndf + errMsg := fmt.Sprintf("Failed to decode response to ndf: %v", err) + return nil, errors.New(errMsg) + } + return updatedNdf, nil +}