/////////////////////////////////////////////////////////////////////////////// // Copyright © 2020 xx network SEZC // // // // Use of this source code is governed by a license that can be found in the // // LICENSE file // /////////////////////////////////////////////////////////////////////////////// package bindings import ( "encoding/json" "fmt" "gitlab.com/elixxir/client/cmix/message" "time" "github.com/pkg/errors" "gitlab.com/xx_network/primitives/netTime" ) // StartNetworkFollower kicks off the tracking of the network. It starts long- // running network threads and returns an object for checking state and // stopping those threads. // // Call this when returning from sleep and close when going back to sleep. // // These threads may become a significant drain on battery when offline, ensure // they are stopped if there is no internet access. // // Threads Started: // - Network Follower (/network/follow.go) // tracks the network events and hands them off to workers for handling. // - Historical Round Retrieval (/network/rounds/historical.go) // retrieves data about rounds that are too old to be stored by the client. // - Message Retrieval Worker Group (/network/rounds/retrieve.go) // requests all messages in a given round from the gateway of the last // nodes. // - Message Handling Worker Group (/network/message/handle.go) // decrypts and partitions messages when signals via the Switchboard. // - Health Tracker (/network/health), // via the network instance, tracks the state of the network. // - Garbled Messages (/network/message/garbled.go) // can be signaled to check all recent messages that could be decoded. It // uses a message store on disk for persistence. // - Critical Messages (/network/message/critical.go) // ensures all protocol layer mandatory messages are sent. It uses a message // store on disk for persistence. // - KeyExchange Trigger (/keyExchange/trigger.go) // responds to sent rekeys and executes them. // - KeyExchange Confirm (/keyExchange/confirm.go) // responds to confirmations of successful rekey operations. // - Auth Callback (/auth/callback.go) // handles both auth confirm and requests. func (c *Cmix) StartNetworkFollower(timeoutMS int) error { timeout := time.Duration(timeoutMS) * time.Millisecond return c.api.StartNetworkFollower(timeout) } // StopNetworkFollower stops the network follower if it is running. It returns // an error if the follower is in the wrong state to stop or if it fails to stop // it. // // If the network follower is running and this fails, the Cmix object will // most likely be in an unrecoverable state and need to be trashed. func (c *Cmix) StopNetworkFollower() error { if err := c.api.StopNetworkFollower(); err != nil { return errors.New(fmt.Sprintf("Failed to stop the "+ "network follower: %+v", err)) } return nil } // WaitForNetwork will block until either the network is healthy or the passed // timeout is reached. It will return true if the network is healthy. func (c *Cmix) WaitForNetwork(timeoutMS int) bool { start := netTime.Now() timeout := time.Duration(timeoutMS) * time.Millisecond for netTime.Since(start) < timeout { if c.api.GetCmix().IsHealthy() { return true } time.Sleep(250 * time.Millisecond) } return false } // NetworkFollowerStatus gets the state of the network follower. It returns a // status with the following values: // Stopped - 0 // Running - 2000 // Stopping - 3000 func (c *Cmix) NetworkFollowerStatus() int { return int(c.api.NetworkFollowerStatus()) } // NodeRegistrationReport is the report structure which // Cmix.GetNodeRegistrationStatus returns JSON marshalled. type NodeRegistrationReport struct { NumberOfNodesRegistered int NumberOfNodes int } // GetNodeRegistrationStatus returns the current state of node registration. // // Returns: // - []byte - A marshalled NodeRegistrationReport containing the number of // nodes the user is registered with and the number of nodes present in the // NDF. // - An error if it cannot get the node registration status. The most likely // cause is that the network is unhealthy. func (c *Cmix) GetNodeRegistrationStatus() ([]byte, error) { numNodesRegistered, numNodes, err := c.api.GetNodeRegistrationStatus() if err != nil { return nil, err } nodeRegReport := NodeRegistrationReport{ NumberOfNodesRegistered: numNodesRegistered, NumberOfNodes: numNodes, } return json.Marshal(nodeRegReport) } // HasRunningProcessies checks if any background threads are running and returns // true if one or more are. // // This is meant to be used when NetworkFollowerStatus returns xxdk.Stopping. // Due to the handling of comms on iOS, where the OS can block indefinitely, it // may not enter the stopped state appropriately. This can be used instead. func (c *Cmix) HasRunningProcessies() bool { return c.api.HasRunningProcessies() } // IsHealthy returns true if the network is read to be in a healthy state where // messages can be sent. func (c *Cmix) IsHealthy() bool { return c.api.GetCmix().IsHealthy() } // NetworkHealthCallback contains a callback that is used to receive // notification if network health changes. type NetworkHealthCallback interface { Callback(bool) } // AddHealthCallback adds a callback that gets called whenever the network // health changes. Returns a registration ID that can be used to unregister. func (c *Cmix) AddHealthCallback(nhc NetworkHealthCallback) int64 { return int64(c.api.GetCmix().AddHealthCallback(nhc.Callback)) } // RemoveHealthCallback removes a health callback using its registration ID. func (c *Cmix) RemoveHealthCallback(funcID int64) { c.api.GetCmix().RemoveHealthCallback(uint64(funcID)) } type ClientError interface { Report(source, message, trace string) } // RegisterClientErrorCallback registers the callback to handle errors from the // long-running threads controlled by StartNetworkFollower and // StopNetworkFollower. func (c *Cmix) RegisterClientErrorCallback(clientError ClientError) { errChan := c.api.GetErrorsChannel() go func() { for report := range errChan { go clientError.Report(report.Source, report.Message, report.Trace) } }() } // TrackServicesCallback is the callback for Cmix.TrackServices. // This will pass to the user a JSON-marshalled list of backend services. // If there was an error retrieving or marshalling the service list, // there is an error for the second parameter which will be non-null. // // Example JSON: // // [ // { // "Id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD", // "Services": [ // { // "Identifier": null, // "Tag": "test", // "Metadata": null // } // ] // }, // { // "Id": "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD", // "Services": [ // { // "Identifier": null, // "Tag": "test", // "Metadata": null // } // ] // }, //] type TrackServicesCallback interface { Callback(marshalData []byte, err error) } // TrackServices will return via a callback the list of services the // backend keeps track of, which is formally referred to as a // [message.ServiceList]. This may be passed into other bindings call which // may need context on the available services for this client. // // Parameters: // - cb - A TrackServicesCallback, which will be passed the marshalled // message.ServiceList. func (c *Cmix) TrackServices(cb TrackServicesCallback) { c.api.GetCmix().TrackServices(func(list message.ServiceList) { cb.Callback(json.Marshal(list)) }) }