diff --git a/api/client.go b/api/client.go index 3f4623c5af5179a02fc8ce368306c2f54974d0b4..3c39f1194060b4d3b18c280d7cd21766715132a8 100644 --- a/api/client.go +++ b/api/client.go @@ -215,8 +215,7 @@ func NewProtoClient_Unsafe(ndfJSON, storageDir string, password, } // Login initializes a client object from existing storage. -func Login(storageDir string, password []byte, - authCallbacks auth.Callbacks, parameters Params) (*Client, error) { +func Login(storageDir string, password []byte, parameters Params) (*Client, error) { jww.INFO.Printf("Login()") c, err := OpenClient(storageDir, password, parameters) diff --git a/bindings/client.go b/bindings/client.go index a160886728f0d7f0975be56956cca85171825883..e9a60f713ba0800cceefb7ed9bd4d19fabc9f7cd 100644 --- a/bindings/client.go +++ b/bindings/client.go @@ -1,54 +1,30 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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 ( - "bytes" - "encoding/csv" - "encoding/json" - "errors" "fmt" - "gitlab.com/elixxir/client/cmix" - "gitlab.com/elixxir/client/single/old" - "runtime/pprof" - "strings" - "sync" - "time" - + "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/api" - "gitlab.com/elixxir/client/interfaces/message" - "gitlab.com/elixxir/client/interfaces/params" - "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/crypto/contact" - "gitlab.com/elixxir/primitives/states" - "gitlab.com/xx_network/primitives/id" - "gitlab.com/xx_network/primitives/netTime" - "google.golang.org/grpc/grpclog" ) -var extantClient bool = false -var loginMux sync.Mutex - -var clientSingleton *Client - // sets the log level func init() { jww.SetLogThreshold(jww.LevelInfo) jww.SetStdoutThreshold(jww.LevelInfo) } -// BindingsClient wraps the api.Client, implementing additional functions +//client tracker singleton, used to track clients so they can be referenced by +//id back over the bindings +var clientTrackerSingleton = &clientTracker{ + clients: make(map[int]*Client), + count: 0, +} + +// Client BindingsClient wraps the api.Client, implementing additional functions // to support the gomobile Client interface type Client struct { - api api.Client - single *old.Manager - singleMux sync.Mutex + api api.Client + id int } // NewClient creates client storage, generates keys, connects, and registers @@ -65,51 +41,6 @@ func NewClient(network, storageDir string, password []byte, regCode string) erro return nil } -// NewPrecannedClient creates an insecure user with predetermined keys with nodes -// It creates client storage, generates keys, connects, and registers -// with the network. Note that this does not register a username/identity, but -// merely creates a new cryptographic identity for adding such information -// at a later date. -// -// Users of this function should delete the storage directory on error. -func NewPrecannedClient(precannedID int, network, storageDir string, password []byte) error { - if precannedID < 0 { - return errors.New("Cannot create precanned client with negative ID") - } - - if err := api.NewPrecannedClient(uint(precannedID), network, storageDir, password); err != nil { - return errors.New(fmt.Sprintf("Failed to create new precanned "+ - "client: %+v", err)) - } - return nil -} - -type BackupReport struct { - RestoredContacts []*id.ID - Params string -} - -// NewClientFromBackup constructs a new Client from an encrypted backup. The backup -// is decrypted using the backupPassphrase. On success a successful client creation, -// the function will return a JSON encoded list of the E2E partners -// contained in the backup and a json-encoded string of the parameters stored in the backup -func NewClientFromBackup(ndfJSON, storageDir string, sessionPassword, - backupPassphrase, backupFileContents []byte) ([]byte, error) { - backupPartnerIds, jsonParams, err := api.NewClientFromBackup(ndfJSON, storageDir, - sessionPassword, backupPassphrase, backupFileContents) - if err != nil { - return nil, errors.New(fmt.Sprintf("Failed to create new "+ - "client from backup: %+v", err)) - } - - report := BackupReport{ - RestoredContacts: backupPartnerIds, - Params: jsonParams, - } - - return json.Marshal(report) -} - // Login will load an existing client from the storageDir // using the password. This will fail if the client doesn't exist or // the password is incorrect. @@ -117,502 +48,20 @@ func NewClientFromBackup(ndfJSON, storageDir string, sessionPassword, // memory and stored as securely as possible using the memguard library. // Login does not block on network connection, and instead loads and // starts subprocesses to perform network operations. -func Login(storageDir string, password []byte, parameters string) (*Client, error) { - loginMux.Lock() - defer loginMux.Unlock() +// TODO: add in custom parameters instead of the default +func Login(storageDir string, password []byte) (*Client, error) { - if extantClient { - return nil, errors.New("cannot login when another session " + - "already exists") - } - // check if a client is already logged in, refuse to login if one is - p, err := params.GetNetworkParameters(parameters) + client, err := api.Login(storageDir, password, api.GetDefaultParams()) if err != nil { return nil, errors.New(fmt.Sprintf("Failed to login: %+v", err)) } - client, err := api.Login(storageDir, password, p) - if err != nil { - return nil, errors.New(fmt.Sprintf("Failed to login: %+v", err)) - } - extantClient = true - clientSingleton := &Client{api: *client} - - return clientSingleton, nil -} - -// returns a previously created client. IF be used if the garbage collector -// removes the client instance on the app side. Is NOT thread safe relative to -// login, newClient, or newPrecannedClient -func GetClientSingleton() *Client { - return clientSingleton -} - -// sets level of logging. All logs the set level and above will be displayed -// options are: -// TRACE - 0 -// DEBUG - 1 -// INFO - 2 -// WARN - 3 -// ERROR - 4 -// CRITICAL - 5 -// FATAL - 6 -// The default state without updates is: INFO -func LogLevel(level int) error { - if level < 0 || level > 6 { - return errors.New(fmt.Sprintf("log level is not valid: log level: %d", level)) - } - - threshold := jww.Threshold(level) - jww.SetLogThreshold(threshold) - jww.SetStdoutThreshold(threshold) - - switch threshold { - case jww.LevelTrace: - fallthrough - case jww.LevelDebug: - fallthrough - case jww.LevelInfo: - jww.INFO.Printf("Log level set to: %s", threshold) - case jww.LevelWarn: - jww.WARN.Printf("Log level set to: %s", threshold) - case jww.LevelError: - jww.ERROR.Printf("Log level set to: %s", threshold) - case jww.LevelCritical: - jww.CRITICAL.Printf("Log level set to: %s", threshold) - case jww.LevelFatal: - jww.FATAL.Printf("Log level set to: %s", threshold) - } - - return nil -} - -//RegisterLogWriter registers a callback on which logs are written. -func RegisterLogWriter(writer LogWriter) { - jww.SetLogOutput(&writerAdapter{lw: writer}) -} - -// EnableGrpcLogs sets GRPC trace logging -func EnableGrpcLogs(writer LogWriter) { - logger := &writerAdapter{lw: writer} - grpclog.SetLoggerV2(grpclog.NewLoggerV2WithVerbosity( - logger, logger, logger, 99)) -} - -//Unmarshals a marshaled contact object, returns an error if it fails -func UnmarshalContact(b []byte) (*Contact, error) { - c, err := contact.Unmarshal(b) - if err != nil { - return nil, errors.New(fmt.Sprintf("Failed to Unmarshal "+ - "Contact: %+v", err)) - } - return &Contact{c: &c}, nil -} - -//Unmarshals a marshaled send report object, returns an error if it fails -func UnmarshalSendReport(b []byte) (*SendReport, error) { - sr := &SendReport{} - return sr, sr.Unmarshal(b) -} - -// StartNetworkFollower kicks off the tracking of the network. It starts -// long running network client 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 which 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 which could be be decoded -// Uses a message store on disk for persistence -// - Critical Messages (/network/message/critical.go) -// Ensures all protocol layer mandatory messages are sent -// 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 -func (c *Client) StartNetworkFollower(timeoutMS int) error { - timeout := time.Duration(timeoutMS) * time.Millisecond - return c.api.StartNetworkFollower(timeout) -} - -// RegisterClientErrorCallback registers the callback to handle errors from the -// long running threads controlled by StartNetworkFollower and StopNetworkFollower -func (c *Client) RegisterClientErrorCallback(clientError ClientError) { - errChan := c.api.GetErrorsChannel() - go func() { - for report := range errChan { - go clientError.Report(report.Source, report.Message, report.Trace) - } - }() -} - -// StopNetworkFollower stops the network follower if it is running. -// It returns errors if the Follower is in the wrong status to stop or if it -// fails to stop it. -// if the network follower is running and this fails, the client object will -// most likely be in an unrecoverable state and need to be trashed. -func (c *Client) StopNetworkFollower() error { - if err := c.api.StopNetworkFollower(); err != nil { - return errors.New(fmt.Sprintf("Failed to stop the "+ - "network follower: %+v", err)) - } - return nil -} - -// WaitForNewtwork will block until either the network is healthy or the -// passed timeout. It will return true if the network is healthy -func (c *Client) WaitForNetwork(timeoutMS int) bool { - start := netTime.Now() - timeout := time.Duration(timeoutMS) * time.Millisecond - for netTime.Since(start) < timeout { - if c.api.GetHealth().IsHealthy() { - return true - } - time.Sleep(250 * time.Millisecond) - } - return false -} - -// Gets the state of the network follower. Returns: -// Stopped - 0 -// Starting - 1000 -// Running - 2000 -// Stopping - 3000 -func (c *Client) NetworkFollowerStatus() int { - return int(c.api.NetworkFollowerStatus()) -} - -// HasRunningProcessies checks if any background threads are running. -// returns true if none are running. This is meant to be -// used when NetworkFollowerStatus() returns Stopping. -// Due to the handling of comms on iOS, where the OS can -// block indefiently, it may not enter the stopped -// state apropreatly. This can be used instead. -func (c *Client) HasRunningProcessies() bool { - return c.api.HasRunningProcessies() + return clientTrackerSingleton.make(client), nil } -// returns true if the network is read to be in a healthy state where -// messages can be sent -func (c *Client) IsNetworkHealthy() bool { - return c.api.GetHealth().IsHealthy() +func (c *Client) GetID() int { + return c.id } -// RegisterNetworkHealthCB registers the network health callback to be called -// any time the network health changes. Returns a unique ID that can be used to -// unregister the network health callback. -func (c *Client) RegisterNetworkHealthCB(nhc NetworkHealthCallback) int64 { - return int64(c.api.GetHealth().AddFunc(nhc.Callback)) -} - -func (c *Client) UnregisterNetworkHealthCB(funcID int64) { - c.api.GetHealth().RemoveFunc(uint64(funcID)) -} - -// RegisterListener records and installs a listener for messages -// matching specific uid, msgType, and/or username -// Returns a ListenerUnregister interface which can be -// -// to register for any userID, pass in an id with length 0 or an id with -// all zeroes -// -// to register for any message type, pass in a message type of 0 -// -// Message Types can be found in client/interfaces/message/type.go -// Make sure to not conflict with ANY default message types -func (c *Client) RegisterListener(uid []byte, msgType int, - listener Listener) (*Unregister, error) { - jww.INFO.Printf("RegisterListener(%v, %d)", uid, - msgType) - - name := listener.Name() - var u *id.ID - if len(uid) == 0 { - u = &id.ID{} - } else { - var err error - u, err = id.Unmarshal(uid) - if err != nil { - return nil, errors.New(fmt.Sprintf("Failed to "+ - "ResgisterListener: %+v", err)) - } - } - - mt := message.Type(msgType) - - f := func(item message.Receive) { - listener.Hear(&Message{r: item}) - } - - lid := c.api.GetSwitchboard().RegisterFunc(name, u, mt, f) - - return newListenerUnregister(lid, c.api.GetSwitchboard()), nil -} - -// RegisterRoundEventsHandler registers a callback interface for round -// events. -// The rid is the round the event attaches to -// The timeoutMS is the number of milliseconds until the event fails, and the -// validStates are a list of states (one per byte) on which the event gets -// triggered -// States: -// 0x00 - PENDING (Never seen by client) -// 0x01 - PRECOMPUTING -// 0x02 - STANDBY -// 0x03 - QUEUED -// 0x04 - REALTIME -// 0x05 - COMPLETED -// 0x06 - FAILED -// These states are defined in elixxir/primitives/states/state.go -func (c *Client) RegisterRoundEventsHandler(rid int, cb RoundEventCallback, - timeoutMS int, il *IntList) *Unregister { - - rcb := func(ri *mixmessages.RoundInfo, timedOut bool) { - cb.EventCallback(int(ri.ID), int(ri.State), timedOut) - } - timeout := time.Duration(timeoutMS) * time.Millisecond - - vStates := make([]states.Round, len(il.lst)) - for i, s := range il.lst { - vStates[i] = states.Round(s) - } - - roundID := id.Round(rid) - - ec := c.api.GetRoundEvents().AddRoundEvent(roundID, rcb, timeout) - - return newRoundUnregister(roundID, ec, c.api.GetRoundEvents()) -} - -// WaitForRoundCompletion allows the caller to get notified if a round -// has completed (or failed). Under the hood, this uses an API which uses the internal -// round data, network historical round lookup, and waiting on network events -// to determine what has (or will) occur. -// -// The callbacks will return at timeoutMS if no state update occurs -func (c *Client) WaitForRoundCompletion(roundID int, - rec RoundCompletionCallback, timeoutMS int) error { - - f := func(allRoundsSucceeded, timedOut bool, rounds map[id.Round]cmix.RoundLookupStatus) { - rec.EventCallback(roundID, allRoundsSucceeded, timedOut) - } - - timeout := time.Duration(timeoutMS) * time.Millisecond - - return c.api.GetRoundResults([]id.Round{id.Round(roundID)}, timeout, f) -} - -// WaitForMessageDelivery allows the caller to get notified if the rounds a -// message was sent in successfully completed. Under the hood, this uses an API -// which uses the internal round data, network historical round lookup, and -// waiting on network events to determine what has (or will) occur. -// -// The callbacks will return at timeoutMS if no state update occurs -// -// This function takes the marshaled send report to ensure a memory leak does -// not occur as a result of both sides of the bindings holding a reference to -// the same pointer. -func (c *Client) WaitForMessageDelivery(marshaledSendReport []byte, - mdc MessageDeliveryCallback, timeoutMS int) error { - jww.INFO.Printf("WaitForMessageDelivery(%v, _, %v)", - marshaledSendReport, timeoutMS) - sr, err := UnmarshalSendReport(marshaledSendReport) - if err != nil { - return errors.New(fmt.Sprintf("Failed to "+ - "WaitForMessageDelivery callback due to bad Send Report: %+v", err)) - } - - if sr == nil || sr.rl == nil || len(sr.rl.list) == 0 { - return errors.New(fmt.Sprintf("Failed to "+ - "WaitForMessageDelivery callback due to invalid Send Report "+ - "unmarshal: %s", string(marshaledSendReport))) - } - - f := func(allRoundsSucceeded, timedOut bool, rounds map[id.Round]cmix.RoundLookupStatus) { - results := make([]byte, len(sr.rl.list)) - jww.INFO.Printf("Processing WaitForMessageDelivery report "+ - "for %v, success: %v, timedout: %v", sr.mid, allRoundsSucceeded, - timedOut) - for i, r := range sr.rl.list { - if result, exists := rounds[r]; exists { - results[i] = byte(result) - } - } - - mdc.EventCallback(sr.mid.Marshal(), allRoundsSucceeded, timedOut, results) - } - - timeout := time.Duration(timeoutMS) * time.Millisecond - - err = c.api.GetRoundResults(sr.rl.list, timeout, f) - - return err -} - -// Returns a user object from which all information about the current user -// can be gleaned -func (c *Client) GetUser() *User { - u := c.api.GetUser() - return &User{u: &u} -} - -// GetNodeRegistrationStatus returns a struct with the number of nodes the -// client is registered with and the number total. -func (c *Client) GetNodeRegistrationStatus() (*NodeRegistrationsStatus, error) { - registered, total, err := c.api.GetNodeRegistrationStatus() - - return &NodeRegistrationsStatus{registered, total}, err -} - -// DeleteRequest will delete a request, agnostic of request type -// for the given partner ID. If no request exists for this -// partner ID an error will be returned. -func (c *Client) DeleteRequest(requesterUserId []byte) error { - requesterId, err := id.Unmarshal(requesterUserId) - if err != nil { - return err - } - - jww.DEBUG.Printf("Deleting request for partner ID: %s", requesterId) - return c.api.DeleteRequest(requesterId) -} - -// DeleteAllRequests clears all requests from Client's auth storage. -func (c *Client) DeleteAllRequests() error { - return c.api.DeleteAllRequests() -} - -// DeleteSentRequests clears sent requests from Client's auth storage. -func (c *Client) DeleteSentRequests() error { - return c.api.DeleteSentRequests() -} - -// DeleteReceiveRequests clears receive requests from Client's auth storage. -func (c *Client) DeleteReceiveRequests() error { - return c.api.DeleteReceiveRequests() -} - -// DeleteContact is a function which removes a contact from Client's storage -func (c *Client) DeleteContact(b []byte) error { - contactObj, err := UnmarshalContact(b) - if err != nil { - return err - } - return c.api.DeleteContact(contactObj.c.ID) -} - -// SetProxiedBins updates the host pool filter that filters out gateways that -// are not in one of the specified bins. The provided bins should be CSV. -func (c *Client) SetProxiedBins(binStringsCSV string) error { - // Convert CSV to slice of strings - all, err := csv.NewReader(strings.NewReader(binStringsCSV)).ReadAll() - if err != nil { - return err - } - - binStrings := make([]string, 0, len(all[0])) - for _, a := range all { - binStrings = append(binStrings, a...) - } - - return c.api.SetProxiedBins(binStrings) -} - -// GetPreferredBins returns the geographic bin or bins that the provided two -// character country code is a part of. The bins are returned as CSV. -func (c *Client) GetPreferredBins(countryCode string) (string, error) { - bins, err := c.api.GetPreferredBins(countryCode) - if err != nil { - return "", err - } - - // Convert the slice of bins to CSV - buff := bytes.NewBuffer(nil) - csvWriter := csv.NewWriter(buff) - err = csvWriter.Write(bins) - if err != nil { - return "", err - } - csvWriter.Flush() - - return buff.String(), nil -} - -// GetRateLimitParams retrieves the rate limiting parameters. -func (c *Client) GetRateLimitParams() (uint32, uint32, int64) { - return c.api.GetRateLimitParams() -} - -/* -// SearchWithHandler is a non-blocking search that also registers -// a callback interface for user disovery events. -func (c *Client) SearchWithHandler(data, separator string, - searchTypes []byte, hdlr UserDiscoveryHandler) { -} - - -// RegisterAuthEventsHandler registers a callback interface for channel -// authentication events. -func (b *BindingsClient) RegisterAuthEventsHandler(hdlr AuthEventHandler) { -} - -// Search accepts a "separator" separated list of search elements with -// an associated list of searchTypes. It returns a ContactList which -// allows you to iterate over the found contact objects. -func (b *BindingsClient) Search(data, separator string, - searchTypes []byte) ContactList { - return nil -}*/ - -// getSingle is a function which returns the single mananger if it -// exists or creates a new one, checking appropriate constraints -// (that the network follower is running) if it needs to make one -func (c *Client) getSingle() (*old.Manager, error) { - c.singleMux.Lock() - defer c.singleMux.Unlock() - if c.single == nil { - apiClient := &c.api - c.single = old.NewManager(apiClient) - err := apiClient.AddService(c.single.StartProcesses) - if err != nil { - return nil, err - } - } - - return c.single, nil -} - -// GetInternalClient returns a reference to the client api. This is for internal -// use only and should not be called by bindings clients. -func (c *Client) GetInternalClient() api.Client { - return c.api -} - -func WrapAPIClient(c *api.Client) *Client { - return &Client{api: *c} -} - -// DumpStack returns a string with the stack trace of every running thread. -func DumpStack() (string, error) { - buf := new(bytes.Buffer) - err := pprof.Lookup("goroutine").WriteTo(buf, 2) - if err != nil { - return "", err - } - return buf.String(), nil -} diff --git a/bindings/clientTracker.go b/bindings/clientTracker.go new file mode 100644 index 0000000000000000000000000000000000000000..563f4957ea3159d86f478cddb19f43e0dac4cf71 --- /dev/null +++ b/bindings/clientTracker.go @@ -0,0 +1,54 @@ +package bindings + +import ( + "github.com/pkg/errors" + "gitlab.com/elixxir/client/api" + "sync" +) + +// clientTracker is a singleton used to keep track of extant clients, allowing +// for race condition free passing over the bindings + +type clientTracker struct { + clients map[int]*Client + count int + mux sync.RWMutex +} + +// make makes a client from an API client, assigning it a unique ID +func (ct *clientTracker) make(c *api.Client) *Client { + ct.mux.Lock() + defer ct.mux.Unlock() + + id := ct.count + ct.count++ + + ct.clients[id] = &Client{ + api: api.Client{}, + id: id, + } + + return ct.clients[id] +} + +//get returns a client given its ID +func (ct *clientTracker) get(id int) (*Client, error) { + ct.mux.RLock() + defer ct.mux.RUnlock() + + c, exist := ct.clients[id] + if !exist { + return nil, errors.Errorf("Cannot get client for id %d, client "+ + "does not exist", id) + } + + return c, nil +} + +//deletes a client if it exists +func (ct *clientTracker) delete(id int) { + ct.mux.Lock() + defer ct.mux.Unlock() + + delete(ct.clients, id) +} diff --git a/bindings/connect.go b/bindings/connect.go new file mode 100644 index 0000000000000000000000000000000000000000..c30a64e016e372971a271af8497cbb1ef39d7d29 --- /dev/null +++ b/bindings/connect.go @@ -0,0 +1,70 @@ +package bindings + +import ( + "encoding/json" + "gitlab.com/elixxir/client/catalog" + "gitlab.com/elixxir/client/connect" + e2e2 "gitlab.com/elixxir/client/e2e" + contact2 "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/crypto/e2e" + "gitlab.com/xx_network/primitives/id" + "time" +) + +//connection tracker singleton, used to track connections so they can be +//referenced by id back over the bindings +var connectionTrackerSingleton = &connectionTracker{ + connections: make(map[int]Connection), + count: 0, +} + +type Connection struct { + connection connect.Connection + id int +} + +// Connect blocks until it connects to the remote +func (c *Client) Connect(recipientContact []byte, myIdentity []byte) ( + *Connection, error) { + cont, err := contact2.Unmarshal(recipientContact) + if err != nil { + return nil, err + } + myID, _, _, myDHPriv, err := unmarshalIdentity(myIdentity) + if err != nil { + return nil, err + } + + connection, err := connect.Connect(cont, myID, myDHPriv, c.api.GetRng(), + c.api.GetStorage().GetE2EGroup(), c.api.GetCmix(), connect.GetDefaultParams()) + + if err != nil { + return nil, err + } + + return connectionTrackerSingleton.make(connection), nil +} + +type E2ESendReport struct { + roundsList + MessageID []byte + Timestamp int64 +} + +func (c *Connection) SendE2E(mt int, payload []byte) ([]byte, error) { + rounds, mid, ts, err := c.connection.SendE2E(catalog.MessageType(mt), payload, + e2e2.GetDefaultParams()) + + if err != nil { + return nil, err + } + + sr := E2ESendReport{ + MessageID: mid.Marshal(), + Timestamp: ts.UnixNano(), + } + + sr.roundsList = makeRoundsList(rounds) + + return json.Marshal(&sr) +} diff --git a/bindings/connectionTracker.go b/bindings/connectionTracker.go new file mode 100644 index 0000000000000000000000000000000000000000..19573ed4e3550a30c4e5d31d997abdc8de2b8bd3 --- /dev/null +++ b/bindings/connectionTracker.go @@ -0,0 +1,62 @@ +package bindings + +import ( + "github.com/pkg/errors" + "gitlab.com/elixxir/client/connect" + "sync" +) + +package bindings + +import ( +"github.com/pkg/errors" +"gitlab.com/elixxir/client/api" +"sync" +) + +// connectionTracker is a singleton used to keep track of extant clients, allowing +// for race condition free passing over the bindings + +type connectionTracker struct { + connections map[int]*Connection + count int + mux sync.RWMutex +} + +// make makes a client from an API client, assigning it a unique ID +func (ct *connectionTracker) make(c connect.Connection) *Connection { + ct.mux.Lock() + defer ct.mux.Unlock() + + id := ct.count + ct.count++ + + ct.connections[id] = &Connection{ + connection: c, + id: id, + } + + return ct.connections[id] +} + +//get returns a client given its ID +func (ct *connectionTracker) get(id int) (*Connection, error) { + ct.mux.RLock() + defer ct.mux.RUnlock() + + c, exist := ct.connections[id] + if !exist { + return nil, errors.Errorf("Cannot get client for id %d, client "+ + "does not exist", id) + } + + return c, nil +} + +//deletes a client if it exists +func (ct *connectionTracker) delete(id int) { + ct.mux.Lock() + defer ct.mux.Unlock() + + delete(ct.connections, id) +} diff --git a/bindings/contact.go b/bindings/contact.go index c02aa2cbfd8b276aa399ee1f7059b0564b7929ba..3f96d3b07b797006bb29a3eb38537c41f9e8e4f0 100644 --- a/bindings/contact.go +++ b/bindings/contact.go @@ -1,80 +1,54 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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 ( - "gitlab.com/elixxir/crypto/contact" - "gitlab.com/elixxir/primitives/fact" + "encoding/json" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" ) -/* fact object*/ -//creates a new fact. The factType must be either: -// 0 - Username -// 1 - Email -// 2 - Phone Number -// The fact must be well formed for the type and must not include commas or -// semicolons. If it is not well formed, it will be rejected. Phone numbers -// must have the two letter country codes appended. For the complete set of -// validation, see /elixxir/primitives/fact/fact.go -func NewFact(factType int, factStr string) (*Fact, error) { - f, err := fact.NewFact(fact.FactType(factType), factStr) - if err != nil { - return nil, err - } - return &Fact{f: &f}, nil +type Identity struct { + ID []byte + RSAPrivatePem []byte + Salt []byte + DHKeyPrivate []byte } type Fact struct { - f *fact.Fact + Fact string + Type string } -func (f *Fact) Get() string { - return f.f.Fact +// MakeIdentity generates a new cryptographic identity for receving +// messages +func (c *Client) MakeIdentity() ([]byte, error) { + I := Identity{} + return json.Marshal(&I) } -func (f *Fact) Type() int { - return int(f.f.T) +func GetContactFromIdentity(identity string) []byte { + I := Identity{} } -func (f *Fact) Stringify() string { - return f.f.Stringify() +func unmarshalIdentity(marshaled []byte) (*id.ID, *rsa.PrivateKey, []byte, + *cyclic.Int, error) { + return nil, nil, nil, nil, nil } -/* contact object*/ -type Contact struct { - c *contact.Contact +// SetFactsOnContact replaces the facts on the contact with the passed in facts +// pass in empty facts in order to clear the facts +func SetFactsOnContact(contact []byte, facts []byte) []byte { + I := Identity{} } -// GetID returns the user ID for this user. -func (c *Contact) GetID() []byte { - return c.c.ID.Bytes() -} +func GetIDFromContact(contact []byte) []byte { -// GetDHPublicKey returns the public key associated with the Contact. -func (c *Contact) GetDHPublicKey() []byte { - return c.c.DhPubKey.Bytes() } -// GetDHPublicKey returns hash of a DH proof of key ownership. -func (c *Contact) GetOwnershipProof() []byte { - return c.c.OwnershipProof -} +func GetPubkeyFromContact(contact []byte) []byte { -// Returns a fact list for adding and getting facts to and from the contact -func (c *Contact) GetFactList() *FactList { - return &FactList{c: c.c} } -func (c *Contact) Marshal() ([]byte, error) { - return c.c.Marshal(), nil -} +func GetFactsFromContact(contact []byte) []byte { -// GetAPIContact returns the api contact object. Not exported to bindings. -func (c *Contact) GetAPIContact() *contact.Contact { - return c.c } diff --git a/bindings/delivery.go b/bindings/delivery.go new file mode 100644 index 0000000000000000000000000000000000000000..1c5b2d01a8c43741cccab422398248c5192d5075 --- /dev/null +++ b/bindings/delivery.go @@ -0,0 +1,72 @@ +package bindings + +import ( + "fmt" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/cmix" + "gitlab.com/xx_network/primitives/id" + "time" +) + +type roundsList struct { + rounds []int +} + +func (rl roundsList) Marshal() []byte { + +} + +func unmarshalRoundsList(marshaled []byte) []id.Round { + +} + +func makeRoundsList(rounds []id.Round) roundsList { + +} + +// WaitForMessageDelivery allows the caller to get notified if the rounds a +// message was sent in successfully completed. Under the hood, this uses an API +// which uses the internal round data, network historical round lookup, and +// waiting on network events to determine what has (or will) occur. +// +// The callbacks will return at timeoutMS if no state update occurs +// +// This function takes the marshaled send report to ensure a memory leak does +// not occur as a result of both sides of the bindings holding a reference to +// the same pointer. +func (c *Client) WaitForMessageDelivery(marshaledSendReport []byte, + mdc MessageDeliveryCallback, timeoutMS int) error { + jww.INFO.Printf("WaitForMessageDelivery(%v, _, %v)", + marshaledSendReport, timeoutMS) + sr, err := UnmarshalSendReport(marshaledSendReport) + if err != nil { + return errors.New(fmt.Sprintf("Failed to "+ + "WaitForMessageDelivery callback due to bad Send Report: %+v", err)) + } + + if sr == nil || sr.rl == nil || len(sr.rl.list) == 0 { + return errors.New(fmt.Sprintf("Failed to "+ + "WaitForMessageDelivery callback due to invalid Send Report "+ + "unmarshal: %s", string(marshaledSendReport))) + } + + f := func(allRoundsSucceeded, timedOut bool, rounds map[id.Round]cmix.RoundLookupStatus) { + results := make([]byte, len(sr.rl.list)) + jww.INFO.Printf("Processing WaitForMessageDelivery report "+ + "for %v, success: %v, timedout: %v", sr.mid, allRoundsSucceeded, + timedOut) + for i, r := range sr.rl.list { + if result, exists := rounds[r]; exists { + results[i] = byte(result) + } + } + + mdc.EventCallback(sr.mid.Marshal(), allRoundsSucceeded, timedOut, results) + } + + timeout := time.Duration(timeoutMS) * time.Millisecond + + err = c.api.GetRoundResults(sr.rl.list, timeout, f) + + return err +} diff --git a/bindings/follow.go b/bindings/follow.go new file mode 100644 index 0000000000000000000000000000000000000000..4ad4620d19d5a385ac9d6b7fbad3efa00716bf5e --- /dev/null +++ b/bindings/follow.go @@ -0,0 +1,110 @@ +package bindings + +import ( + "gitlab.com/xx_network/primitives/netTime" + "time" +) + +// StartNetworkFollower kicks off the tracking of the network. It starts +// long running network client 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 which 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 which could be be decoded +// Uses a message store on disk for persistence +// - Critical Messages (/network/message/critical.go) +// Ensures all protocol layer mandatory messages are sent +// 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 +func (c *Client) StartNetworkFollower(timeoutMS int) error { + timeout := time.Duration(timeoutMS) * time.Millisecond + return c.api.StartNetworkFollower(timeout) +} + +// WaitForNewtwork will block until either the network is healthy or the +// passed timeout. It will return true if the network is healthy +func (c *Client) 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 +} + +// Gets the state of the network follower. Returns: +// Stopped - 0 +// Starting - 1000 +// Running - 2000 +// Stopping - 3000 +func (c *Client) NetworkFollowerStatus() int { + return int(c.api.NetworkFollowerStatus()) +} + +// HasRunningProcessies checks if any background threads are running. +// returns true if none are running. This is meant to be +// used when NetworkFollowerStatus() returns Stopping. +// Due to the handling of comms on iOS, where the OS can +// block indefiently, it may not enter the stopped +// state apropreatly. This can be used instead. +func (c *Client) HasRunningProcessies() bool { + return c.api.HasRunningProcessies() +} + +// IsNetworkHealthy returns true if the network is read to be in a healthy state where +// messages can be sent +func (c *Client) IsNetworkHealthy() bool { + return c.api.GetCmix().IsHealthy() +} + +// A callback when which is used to receive notification if network health +// changes +type NetworkHealthCallback interface { + Callback(bool) +} + +// RegisterNetworkHealthCB registers the network health callback to be called +// any time the network health changes. Returns a unique ID that can be used to +// unregister the network health callback. +func (c *Client) RegisterNetworkHealthCB(nhc NetworkHealthCallback) int64 { + return int64(c.api.GetCmix().AddHealthCallback(nhc.Callback)) +} + +func (c *Client) UnregisterNetworkHealthCB(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 *Client) RegisterClientErrorCallback(clientError ClientError) { + errChan := c.api.GetErrorsChannel() + go func() { + for report := range errChan { + go clientError.Report(report.Source, report.Message, report.Trace) + } + }() +} diff --git a/bindings/README.md b/bindings/old/README.md similarity index 100% rename from bindings/README.md rename to bindings/old/README.md diff --git a/bindings/authenticatedChannels.go b/bindings/old/authenticatedChannels.go similarity index 99% rename from bindings/authenticatedChannels.go rename to bindings/old/authenticatedChannels.go index af246b5408736224ec081ae7e45806b25b4a0a60..d64374c40d58f4f65053eb6fbfe144590bb5d7a0 100644 --- a/bindings/authenticatedChannels.go +++ b/bindings/old/authenticatedChannels.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "fmt" diff --git a/bindings/backup.go b/bindings/old/backup.go similarity index 99% rename from bindings/backup.go rename to bindings/old/backup.go index 955839e8cf326ecda83e36fd35f2ba68fdb657f9..e8d890b56acd283f85b90c71e7d0536309b5c078 100644 --- a/bindings/backup.go +++ b/bindings/old/backup.go @@ -5,7 +5,7 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "gitlab.com/elixxir/client/backup" diff --git a/bindings/callback.go b/bindings/old/callback.go similarity index 99% rename from bindings/callback.go rename to bindings/old/callback.go index 4ad87c94ae91e1dc5d688900d75d0b404f9a1765..9fd5ca3ae057365f058b2c0a5667c9a8edb03a4e 100644 --- a/bindings/callback.go +++ b/bindings/old/callback.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "gitlab.com/elixxir/client/interfaces" diff --git a/bindings/old/client.go b/bindings/old/client.go new file mode 100644 index 0000000000000000000000000000000000000000..bd5f31d84321f06346ad37e9f60ba7368073343b --- /dev/null +++ b/bindings/old/client.go @@ -0,0 +1,618 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package old + +import ( + "bytes" + "encoding/csv" + "encoding/json" + "errors" + "fmt" + "gitlab.com/elixxir/client/cmix" + "gitlab.com/elixxir/client/single/old" + "runtime/pprof" + "strings" + "sync" + "time" + + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/api" + "gitlab.com/elixxir/client/interfaces/message" + "gitlab.com/elixxir/client/interfaces/params" + "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/primitives/states" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" + "google.golang.org/grpc/grpclog" +) + +var extantClient bool = false +var loginMux sync.Mutex + +var clientSingleton *Client + +// sets the log level +func init() { + jww.SetLogThreshold(jww.LevelInfo) + jww.SetStdoutThreshold(jww.LevelInfo) +} + +// BindingsClient wraps the api.Client, implementing additional functions +// to support the gomobile Client interface +type Client struct { + api api.Client + single *old.Manager + singleMux sync.Mutex +} + +// NewClient creates client storage, generates keys, connects, and registers +// with the network. Note that this does not register a username/identity, but +// merely creates a new cryptographic identity for adding such information +// at a later date. +// +// Users of this function should delete the storage directory on error. +func NewClient(network, storageDir string, password []byte, regCode string) error { + if err := api.NewClient(network, storageDir, password, regCode); err != nil { + return errors.New(fmt.Sprintf("Failed to create new client: %+v", + err)) + } + return nil +} + +// NewPrecannedClient creates an insecure user with predetermined keys with nodes +// It creates client storage, generates keys, connects, and registers +// with the network. Note that this does not register a username/identity, but +// merely creates a new cryptographic identity for adding such information +// at a later date. +// +// Users of this function should delete the storage directory on error. +func NewPrecannedClient(precannedID int, network, storageDir string, password []byte) error { + if precannedID < 0 { + return errors.New("Cannot create precanned client with negative ID") + } + + if err := api.NewPrecannedClient(uint(precannedID), network, storageDir, password); err != nil { + return errors.New(fmt.Sprintf("Failed to create new precanned "+ + "client: %+v", err)) + } + return nil +} + +type BackupReport struct { + RestoredContacts []*id.ID + Params string +} + +// NewClientFromBackup constructs a new Client from an encrypted backup. The backup +// is decrypted using the backupPassphrase. On success a successful client creation, +// the function will return a JSON encoded list of the E2E partners +// contained in the backup and a json-encoded string of the parameters stored in the backup +func NewClientFromBackup(ndfJSON, storageDir string, sessionPassword, + backupPassphrase, backupFileContents []byte) ([]byte, error) { + backupPartnerIds, jsonParams, err := api.NewClientFromBackup(ndfJSON, storageDir, + sessionPassword, backupPassphrase, backupFileContents) + if err != nil { + return nil, errors.New(fmt.Sprintf("Failed to create new "+ + "client from backup: %+v", err)) + } + + report := BackupReport{ + RestoredContacts: backupPartnerIds, + Params: jsonParams, + } + + return json.Marshal(report) +} + +// Login will load an existing client from the storageDir +// using the password. This will fail if the client doesn't exist or +// the password is incorrect. +// The password is passed as a byte array so that it can be cleared from +// memory and stored as securely as possible using the memguard library. +// Login does not block on network connection, and instead loads and +// starts subprocesses to perform network operations. +func Login(storageDir string, password []byte, parameters string) (*Client, error) { + loginMux.Lock() + defer loginMux.Unlock() + + if extantClient { + return nil, errors.New("cannot login when another session " + + "already exists") + } + // check if a client is already logged in, refuse to login if one is + p, err := params.GetNetworkParameters(parameters) + if err != nil { + return nil, errors.New(fmt.Sprintf("Failed to login: %+v", err)) + } + + client, err := api.Login(storageDir, password, p) + if err != nil { + return nil, errors.New(fmt.Sprintf("Failed to login: %+v", err)) + } + extantClient = true + clientSingleton := &Client{api: *client} + + return clientSingleton, nil +} + +// returns a previously created client. IF be used if the garbage collector +// removes the client instance on the app side. Is NOT thread safe relative to +// login, newClient, or newPrecannedClient +func GetClientSingleton() *Client { + return clientSingleton +} + +// sets level of logging. All logs the set level and above will be displayed +// options are: +// TRACE - 0 +// DEBUG - 1 +// INFO - 2 +// WARN - 3 +// ERROR - 4 +// CRITICAL - 5 +// FATAL - 6 +// The default state without updates is: INFO +func LogLevel(level int) error { + if level < 0 || level > 6 { + return errors.New(fmt.Sprintf("log level is not valid: log level: %d", level)) + } + + threshold := jww.Threshold(level) + jww.SetLogThreshold(threshold) + jww.SetStdoutThreshold(threshold) + + switch threshold { + case jww.LevelTrace: + fallthrough + case jww.LevelDebug: + fallthrough + case jww.LevelInfo: + jww.INFO.Printf("Log level set to: %s", threshold) + case jww.LevelWarn: + jww.WARN.Printf("Log level set to: %s", threshold) + case jww.LevelError: + jww.ERROR.Printf("Log level set to: %s", threshold) + case jww.LevelCritical: + jww.CRITICAL.Printf("Log level set to: %s", threshold) + case jww.LevelFatal: + jww.FATAL.Printf("Log level set to: %s", threshold) + } + + return nil +} + +//RegisterLogWriter registers a callback on which logs are written. +func RegisterLogWriter(writer LogWriter) { + jww.SetLogOutput(&writerAdapter{lw: writer}) +} + +// EnableGrpcLogs sets GRPC trace logging +func EnableGrpcLogs(writer LogWriter) { + logger := &writerAdapter{lw: writer} + grpclog.SetLoggerV2(grpclog.NewLoggerV2WithVerbosity( + logger, logger, logger, 99)) +} + +//Unmarshals a marshaled contact object, returns an error if it fails +func UnmarshalContact(b []byte) (*Contact, error) { + c, err := contact.Unmarshal(b) + if err != nil { + return nil, errors.New(fmt.Sprintf("Failed to Unmarshal "+ + "Contact: %+v", err)) + } + return &Contact{c: &c}, nil +} + +//Unmarshals a marshaled send report object, returns an error if it fails +func UnmarshalSendReport(b []byte) (*SendReport, error) { + sr := &SendReport{} + return sr, sr.Unmarshal(b) +} + +// StartNetworkFollower kicks off the tracking of the network. It starts +// long running network client 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 which 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 which could be be decoded +// Uses a message store on disk for persistence +// - Critical Messages (/network/message/critical.go) +// Ensures all protocol layer mandatory messages are sent +// 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 +func (c *Client) StartNetworkFollower(timeoutMS int) error { + timeout := time.Duration(timeoutMS) * time.Millisecond + return c.api.StartNetworkFollower(timeout) +} + +// RegisterClientErrorCallback registers the callback to handle errors from the +// long running threads controlled by StartNetworkFollower and StopNetworkFollower +func (c *Client) RegisterClientErrorCallback(clientError ClientError) { + errChan := c.api.GetErrorsChannel() + go func() { + for report := range errChan { + go clientError.Report(report.Source, report.Message, report.Trace) + } + }() +} + +// StopNetworkFollower stops the network follower if it is running. +// It returns errors if the Follower is in the wrong status to stop or if it +// fails to stop it. +// if the network follower is running and this fails, the client object will +// most likely be in an unrecoverable state and need to be trashed. +func (c *Client) StopNetworkFollower() error { + if err := c.api.StopNetworkFollower(); err != nil { + return errors.New(fmt.Sprintf("Failed to stop the "+ + "network follower: %+v", err)) + } + return nil +} + +// WaitForNewtwork will block until either the network is healthy or the +// passed timeout. It will return true if the network is healthy +func (c *Client) WaitForNetwork(timeoutMS int) bool { + start := netTime.Now() + timeout := time.Duration(timeoutMS) * time.Millisecond + for netTime.Since(start) < timeout { + if c.api.GetHealth().IsHealthy() { + return true + } + time.Sleep(250 * time.Millisecond) + } + return false +} + +// Gets the state of the network follower. Returns: +// Stopped - 0 +// Starting - 1000 +// Running - 2000 +// Stopping - 3000 +func (c *Client) NetworkFollowerStatus() int { + return int(c.api.NetworkFollowerStatus()) +} + +// HasRunningProcessies checks if any background threads are running. +// returns true if none are running. This is meant to be +// used when NetworkFollowerStatus() returns Stopping. +// Due to the handling of comms on iOS, where the OS can +// block indefiently, it may not enter the stopped +// state apropreatly. This can be used instead. +func (c *Client) HasRunningProcessies() bool { + return c.api.HasRunningProcessies() +} + +// returns true if the network is read to be in a healthy state where +// messages can be sent +func (c *Client) IsNetworkHealthy() bool { + return c.api.GetHealth().IsHealthy() +} + +// RegisterNetworkHealthCB registers the network health callback to be called +// any time the network health changes. Returns a unique ID that can be used to +// unregister the network health callback. +func (c *Client) RegisterNetworkHealthCB(nhc NetworkHealthCallback) int64 { + return int64(c.api.GetHealth().AddFunc(nhc.Callback)) +} + +func (c *Client) UnregisterNetworkHealthCB(funcID int64) { + c.api.GetHealth().RemoveFunc(uint64(funcID)) +} + +// RegisterListener records and installs a listener for messages +// matching specific uid, msgType, and/or username +// Returns a ListenerUnregister interface which can be +// +// to register for any userID, pass in an id with length 0 or an id with +// all zeroes +// +// to register for any message type, pass in a message type of 0 +// +// Message Types can be found in client/interfaces/message/type.go +// Make sure to not conflict with ANY default message types +func (c *Client) RegisterListener(uid []byte, msgType int, + listener Listener) (*Unregister, error) { + jww.INFO.Printf("RegisterListener(%v, %d)", uid, + msgType) + + name := listener.Name() + + var u *id.ID + if len(uid) == 0 { + u = &id.ID{} + } else { + var err error + u, err = id.Unmarshal(uid) + if err != nil { + return nil, errors.New(fmt.Sprintf("Failed to "+ + "ResgisterListener: %+v", err)) + } + } + + mt := message.Type(msgType) + + f := func(item message.Receive) { + listener.Hear(&Message{r: item}) + } + + lid := c.api.GetSwitchboard().RegisterFunc(name, u, mt, f) + + return newListenerUnregister(lid, c.api.GetSwitchboard()), nil +} + +// RegisterRoundEventsHandler registers a callback interface for round +// events. +// The rid is the round the event attaches to +// The timeoutMS is the number of milliseconds until the event fails, and the +// validStates are a list of states (one per byte) on which the event gets +// triggered +// States: +// 0x00 - PENDING (Never seen by client) +// 0x01 - PRECOMPUTING +// 0x02 - STANDBY +// 0x03 - QUEUED +// 0x04 - REALTIME +// 0x05 - COMPLETED +// 0x06 - FAILED +// These states are defined in elixxir/primitives/states/state.go +func (c *Client) RegisterRoundEventsHandler(rid int, cb RoundEventCallback, + timeoutMS int, il *IntList) *Unregister { + + rcb := func(ri *mixmessages.RoundInfo, timedOut bool) { + cb.EventCallback(int(ri.ID), int(ri.State), timedOut) + } + + timeout := time.Duration(timeoutMS) * time.Millisecond + + vStates := make([]states.Round, len(il.lst)) + for i, s := range il.lst { + vStates[i] = states.Round(s) + } + + roundID := id.Round(rid) + + ec := c.api.GetRoundEvents().AddRoundEvent(roundID, rcb, timeout) + + return newRoundUnregister(roundID, ec, c.api.GetRoundEvents()) +} + +// WaitForRoundCompletion allows the caller to get notified if a round +// has completed (or failed). Under the hood, this uses an API which uses the internal +// round data, network historical round lookup, and waiting on network events +// to determine what has (or will) occur. +// +// The callbacks will return at timeoutMS if no state update occurs +func (c *Client) WaitForRoundCompletion(roundID int, + rec RoundCompletionCallback, timeoutMS int) error { + + f := func(allRoundsSucceeded, timedOut bool, rounds map[id.Round]cmix.RoundLookupStatus) { + rec.EventCallback(roundID, allRoundsSucceeded, timedOut) + } + + timeout := time.Duration(timeoutMS) * time.Millisecond + + return c.api.GetRoundResults([]id.Round{id.Round(roundID)}, timeout, f) +} + +// WaitForMessageDelivery allows the caller to get notified if the rounds a +// message was sent in successfully completed. Under the hood, this uses an API +// which uses the internal round data, network historical round lookup, and +// waiting on network events to determine what has (or will) occur. +// +// The callbacks will return at timeoutMS if no state update occurs +// +// This function takes the marshaled send report to ensure a memory leak does +// not occur as a result of both sides of the bindings holding a reference to +// the same pointer. +func (c *Client) WaitForMessageDelivery(marshaledSendReport []byte, + mdc MessageDeliveryCallback, timeoutMS int) error { + jww.INFO.Printf("WaitForMessageDelivery(%v, _, %v)", + marshaledSendReport, timeoutMS) + sr, err := UnmarshalSendReport(marshaledSendReport) + if err != nil { + return errors.New(fmt.Sprintf("Failed to "+ + "WaitForMessageDelivery callback due to bad Send Report: %+v", err)) + } + + if sr == nil || sr.rl == nil || len(sr.rl.list) == 0 { + return errors.New(fmt.Sprintf("Failed to "+ + "WaitForMessageDelivery callback due to invalid Send Report "+ + "unmarshal: %s", string(marshaledSendReport))) + } + + f := func(allRoundsSucceeded, timedOut bool, rounds map[id.Round]cmix.RoundLookupStatus) { + results := make([]byte, len(sr.rl.list)) + jww.INFO.Printf("Processing WaitForMessageDelivery report "+ + "for %v, success: %v, timedout: %v", sr.mid, allRoundsSucceeded, + timedOut) + for i, r := range sr.rl.list { + if result, exists := rounds[r]; exists { + results[i] = byte(result) + } + } + + mdc.EventCallback(sr.mid.Marshal(), allRoundsSucceeded, timedOut, results) + } + + timeout := time.Duration(timeoutMS) * time.Millisecond + + err = c.api.GetRoundResults(sr.rl.list, timeout, f) + + return err +} + +// Returns a user object from which all information about the current user +// can be gleaned +func (c *Client) GetUser() *User { + u := c.api.GetUser() + return &User{u: &u} +} + +// GetNodeRegistrationStatus returns a struct with the number of nodes the +// client is registered with and the number total. +func (c *Client) GetNodeRegistrationStatus() (*NodeRegistrationsStatus, error) { + registered, total, err := c.api.GetNodeRegistrationStatus() + + return &NodeRegistrationsStatus{registered, total}, err +} + +// DeleteRequest will delete a request, agnostic of request type +// for the given partner ID. If no request exists for this +// partner ID an error will be returned. +func (c *Client) DeleteRequest(requesterUserId []byte) error { + requesterId, err := id.Unmarshal(requesterUserId) + if err != nil { + return err + } + + jww.DEBUG.Printf("Deleting request for partner ID: %s", requesterId) + return c.api.DeleteRequest(requesterId) +} + +// DeleteAllRequests clears all requests from Client's auth storage. +func (c *Client) DeleteAllRequests() error { + return c.api.DeleteAllRequests() +} + +// DeleteSentRequests clears sent requests from Client's auth storage. +func (c *Client) DeleteSentRequests() error { + return c.api.DeleteSentRequests() +} + +// DeleteReceiveRequests clears receive requests from Client's auth storage. +func (c *Client) DeleteReceiveRequests() error { + return c.api.DeleteReceiveRequests() +} + +// DeleteContact is a function which removes a contact from Client's storage +func (c *Client) DeleteContact(b []byte) error { + contactObj, err := UnmarshalContact(b) + if err != nil { + return err + } + return c.api.DeleteContact(contactObj.c.ID) +} + +// SetProxiedBins updates the host pool filter that filters out gateways that +// are not in one of the specified bins. The provided bins should be CSV. +func (c *Client) SetProxiedBins(binStringsCSV string) error { + // Convert CSV to slice of strings + all, err := csv.NewReader(strings.NewReader(binStringsCSV)).ReadAll() + if err != nil { + return err + } + + binStrings := make([]string, 0, len(all[0])) + for _, a := range all { + binStrings = append(binStrings, a...) + } + + return c.api.SetProxiedBins(binStrings) +} + +// GetPreferredBins returns the geographic bin or bins that the provided two +// character country code is a part of. The bins are returned as CSV. +func (c *Client) GetPreferredBins(countryCode string) (string, error) { + bins, err := c.api.GetPreferredBins(countryCode) + if err != nil { + return "", err + } + + // Convert the slice of bins to CSV + buff := bytes.NewBuffer(nil) + csvWriter := csv.NewWriter(buff) + err = csvWriter.Write(bins) + if err != nil { + return "", err + } + csvWriter.Flush() + + return buff.String(), nil +} + +// GetRateLimitParams retrieves the rate limiting parameters. +func (c *Client) GetRateLimitParams() (uint32, uint32, int64) { + return c.api.GetRateLimitParams() +} + +/* +// SearchWithHandler is a non-blocking search that also registers +// a callback interface for user disovery events. +func (c *Client) SearchWithHandler(data, separator string, + searchTypes []byte, hdlr UserDiscoveryHandler) { +} + + +// RegisterAuthEventsHandler registers a callback interface for channel +// authentication events. +func (b *BindingsClient) RegisterAuthEventsHandler(hdlr AuthEventHandler) { +} + +// Search accepts a "separator" separated list of search elements with +// an associated list of searchTypes. It returns a ContactList which +// allows you to iterate over the found contact objects. +func (b *BindingsClient) Search(data, separator string, + searchTypes []byte) ContactList { + return nil +}*/ + +// getSingle is a function which returns the single mananger if it +// exists or creates a new one, checking appropriate constraints +// (that the network follower is running) if it needs to make one +func (c *Client) getSingle() (*old.Manager, error) { + c.singleMux.Lock() + defer c.singleMux.Unlock() + if c.single == nil { + apiClient := &c.api + c.single = old.NewManager(apiClient) + err := apiClient.AddService(c.single.StartProcesses) + if err != nil { + return nil, err + } + } + + return c.single, nil +} + +// GetInternalClient returns a reference to the client api. This is for internal +// use only and should not be called by bindings clients. +func (c *Client) GetInternalClient() api.Client { + return c.api +} + +func WrapAPIClient(c *api.Client) *Client { + return &Client{api: *c} +} + +// DumpStack returns a string with the stack trace of every running thread. +func DumpStack() (string, error) { + buf := new(bytes.Buffer) + err := pprof.Lookup("goroutine").WriteTo(buf, 2) + if err != nil { + return "", err + } + return buf.String(), nil +} diff --git a/bindings/old/contact.go b/bindings/old/contact.go new file mode 100644 index 0000000000000000000000000000000000000000..30fcdcc92c1a9d8b5146d6e9c2f049b7c5368ef3 --- /dev/null +++ b/bindings/old/contact.go @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package old + +import ( + "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/primitives/fact" +) + +/* fact object*/ +//creates a new fact. The factType must be either: +// 0 - Username +// 1 - Email +// 2 - Phone Number +// The fact must be well formed for the type and must not include commas or +// semicolons. If it is not well formed, it will be rejected. Phone numbers +// must have the two letter country codes appended. For the complete set of +// validation, see /elixxir/primitives/fact/fact.go +func NewFact(factType int, factStr string) (*Fact, error) { + f, err := fact.NewFact(fact.FactType(factType), factStr) + if err != nil { + return nil, err + } + return &Fact{f: &f}, nil +} + +type Fact struct { + f *fact.Fact +} + +func (f *Fact) Get() string { + return f.f.Fact +} + +func (f *Fact) Type() int { + return int(f.f.T) +} + +func (f *Fact) Stringify() string { + return f.f.Stringify() +} + +/* contact object*/ +type Contact struct { + c *contact.Contact +} + +// GetID returns the user ID for this user. +func (c *Contact) GetID() []byte { + return c.c.ID.Bytes() +} + +// GetDHPublicKey returns the public key associated with the Contact. +func (c *Contact) GetDHPublicKey() []byte { + return c.c.DhPubKey.Bytes() +} + +// GetDHPublicKey returns hash of a DH proof of key ownership. +func (c *Contact) GetOwnershipProof() []byte { + return c.c.OwnershipProof +} + +// Returns a fact list for adding and getting facts to and from the contact +func (c *Contact) GetFactList() *FactList { + return &FactList{c: c.c} +} + +func (c *Contact) Marshal() ([]byte, error) { + return c.c.Marshal(), nil +} + +// GetAPIContact returns the api contact object. Not exported to bindings. +func (c *Contact) GetAPIContact() *contact.Contact { + return c.c +} diff --git a/bindings/dummy.go b/bindings/old/dummy.go similarity index 99% rename from bindings/dummy.go rename to bindings/old/dummy.go index d6facc68e00687b9a951ce7793b62102b155d3f6..e393cf33b8da90d9b098af8d39be876f63ad8cc1 100644 --- a/bindings/dummy.go +++ b/bindings/old/dummy.go @@ -5,7 +5,7 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "gitlab.com/elixxir/client/dummy" diff --git a/bindings/errors.go b/bindings/old/errors.go similarity index 99% rename from bindings/errors.go rename to bindings/old/errors.go index cfffaf0155a8993787120d0bce24f6c1071baeb2..fa23b314fc3dd1a36f53fb09b5e334d24982409b 100644 --- a/bindings/errors.go +++ b/bindings/old/errors.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "context" diff --git a/bindings/errors_test.go b/bindings/old/errors_test.go similarity index 99% rename from bindings/errors_test.go rename to bindings/old/errors_test.go index d93a9924fb336e68fbe6c31af30174709f049d8d..c06ef20dfc2db495b26d96cdaebb83ec32d3cf85 100644 --- a/bindings/errors_test.go +++ b/bindings/old/errors_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "context" diff --git a/bindings/event.go b/bindings/old/event.go similarity index 98% rename from bindings/event.go rename to bindings/old/event.go index 4f5aeacf907edfb09f23662e986607c9d820633c..9c261d9d6db9611b3360ae23ce851b78306cd374 100644 --- a/bindings/event.go +++ b/bindings/old/event.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old // EventCallbackFunctionObject bindings interface which contains function // that implements the EventCallbackFunction diff --git a/bindings/fileTransfer.go b/bindings/old/fileTransfer.go similarity index 99% rename from bindings/fileTransfer.go rename to bindings/old/fileTransfer.go index 9e2d76169cd393422039b06de483905bd3a0de76..57ea3ed36d2d23c635347ecd5b5ffd12992310b3 100644 --- a/bindings/fileTransfer.go +++ b/bindings/old/fileTransfer.go @@ -5,7 +5,7 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "encoding/json" diff --git a/bindings/group.go b/bindings/old/group.go similarity index 99% rename from bindings/group.go rename to bindings/old/group.go index 3fce2e43b790c06dc64e0eb15490b5d795746060..edb7b776c0846844c2315796690f94108708faa8 100644 --- a/bindings/group.go +++ b/bindings/old/group.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "encoding/json" diff --git a/bindings/jsons.go b/bindings/old/jsons.go similarity index 98% rename from bindings/jsons.go rename to bindings/old/jsons.go index 53f15435c6e8750a5d08e18c18d23b81c4304f5d..049ced794b15f7355de718f1bc935d5f040cba71 100644 --- a/bindings/jsons.go +++ b/bindings/old/jsons.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "io/ioutil" diff --git a/bindings/jsons_test.go b/bindings/old/jsons_test.go similarity index 98% rename from bindings/jsons_test.go rename to bindings/old/jsons_test.go index a4405fd47fcd0908c0b8acff7a234b7732778e0f..917816613604b638e1bf6096406652745836fa7b 100644 --- a/bindings/jsons_test.go +++ b/bindings/old/jsons_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "fmt" diff --git a/bindings/list.go b/bindings/old/list.go similarity index 99% rename from bindings/list.go rename to bindings/old/list.go index 1331c378e2b2f31c457ef2f3d082c51e186f2fbb..c4b5d82282ff6f7bbcfa96a9dc7480df47b5064c 100644 --- a/bindings/list.go +++ b/bindings/old/list.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "github.com/pkg/errors" diff --git a/bindings/message.go b/bindings/old/message.go similarity index 99% rename from bindings/message.go rename to bindings/old/message.go index 3eee3b343627338fb907ba81b93fe2e73f8f4731..2a01ef8db7042a4464ea01d3b4d7bc85eb004065 100644 --- a/bindings/message.go +++ b/bindings/old/message.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "gitlab.com/elixxir/client/interfaces/message" diff --git a/bindings/mnemonic.go b/bindings/old/mnemonic.go similarity index 99% rename from bindings/mnemonic.go rename to bindings/old/mnemonic.go index 7062341a1958bb3cd2c0e0e9a960b454f1a143cf..6588270347ef1c892c7a7429848a01b2704a1f3e 100644 --- a/bindings/mnemonic.go +++ b/bindings/old/mnemonic.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import "gitlab.com/elixxir/client/api" diff --git a/bindings/ndf.go b/bindings/old/ndf.go similarity index 98% rename from bindings/ndf.go rename to bindings/old/ndf.go index 2c35c681799b251796fd7f4592311572468208b3..2b3bb21ba2b88f54f9587343fa888f32059fe586 100644 --- a/bindings/ndf.go +++ b/bindings/old/ndf.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "gitlab.com/elixxir/client/api" diff --git a/bindings/ndf_test.go b/bindings/old/ndf_test.go similarity index 99% rename from bindings/ndf_test.go rename to bindings/old/ndf_test.go index 3f7c68f26079621e63fe864587da5072ad04b22b..6aa788566a7479d96d3601bc8b0658bdbb501337 100644 --- a/bindings/ndf_test.go +++ b/bindings/old/ndf_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "fmt" diff --git a/bindings/notifications.go b/bindings/old/notifications.go similarity index 99% rename from bindings/notifications.go rename to bindings/old/notifications.go index 62cf55faea8eb127dddf116303a2153c41c16906..96df0171077284a505825ffa8b9da82a8b6f22c5 100644 --- a/bindings/notifications.go +++ b/bindings/old/notifications.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "encoding/json" diff --git a/bindings/notifications_test.go b/bindings/old/notifications_test.go similarity index 99% rename from bindings/notifications_test.go rename to bindings/old/notifications_test.go index b180d09d78f5acedfdd1a1eacd0a2356ede18106..e1c09a6be6e1d24a2be4f94e21ed2398a784c7ca 100644 --- a/bindings/notifications_test.go +++ b/bindings/old/notifications_test.go @@ -1,4 +1,4 @@ -package bindings +package old import ( "bytes" diff --git a/bindings/params.go b/bindings/old/params.go similarity index 98% rename from bindings/params.go rename to bindings/old/params.go index d4896a2e9e89c49c177689c85ff0a2cf410f9277..2b78f76abd934075503364b58377215d5676f287 100644 --- a/bindings/params.go +++ b/bindings/old/params.go @@ -7,7 +7,7 @@ // Contains params-related bindings -package bindings +package old import ( "gitlab.com/elixxir/client/interfaces/params" diff --git a/bindings/preimage.go b/bindings/old/preimage.go similarity index 98% rename from bindings/preimage.go rename to bindings/old/preimage.go index c5b7f60ab1e93e594664b86f9e6a0aa116f17b18..3c84f42d0c19cdcf55d10fc09f952bb91af1599c 100644 --- a/bindings/preimage.go +++ b/bindings/old/preimage.go @@ -1,4 +1,4 @@ -package bindings +package old import ( "encoding/json" diff --git a/bindings/registrationStatus.go b/bindings/old/registrationStatus.go similarity index 98% rename from bindings/registrationStatus.go rename to bindings/old/registrationStatus.go index eab2590984413d014695f1489989ae805653fff3..66c100acc2821779cc0b462c6b31ccae2ae8f94f 100644 --- a/bindings/registrationStatus.go +++ b/bindings/old/registrationStatus.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old // NodeRegistrationsStatus structure for returning nodes registration statuses // for bindings. diff --git a/bindings/restoreContacts.go b/bindings/old/restoreContacts.go similarity index 99% rename from bindings/restoreContacts.go rename to bindings/old/restoreContacts.go index b970c77a2ed8cbf5664f76a6754904edb9b8d0f0..ff1fae2388de69395088eea86e3033bd1ac92b57 100644 --- a/bindings/restoreContacts.go +++ b/bindings/old/restoreContacts.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "gitlab.com/elixxir/client/xxmutils" diff --git a/bindings/secrets.go b/bindings/old/secrets.go similarity index 98% rename from bindings/secrets.go rename to bindings/old/secrets.go index 5bdeeed03c35cf6e0af99b8a64aaf54f213e8a23..5ef85b2a870aa658283bdb0903a27d6ff8aa5801 100644 --- a/bindings/secrets.go +++ b/bindings/old/secrets.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( jww "github.com/spf13/jwalterweatherman" diff --git a/bindings/secrets_test.go b/bindings/old/secrets_test.go similarity index 98% rename from bindings/secrets_test.go rename to bindings/old/secrets_test.go index 20e1a7d51f1b182fc44df8902e85d462b615b64b..1887f9564c1ab52058dc5b6eec0ccf96855cd4c3 100644 --- a/bindings/secrets_test.go +++ b/bindings/old/secrets_test.go @@ -4,7 +4,7 @@ // Use of this source code is governed by a license that can be found in the LICENSE file // //////////////////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "bytes" diff --git a/bindings/send.go b/bindings/old/send.go similarity index 99% rename from bindings/send.go rename to bindings/old/send.go index 3b85a34b0f05fe6f119a7d45ffe4f195604fb8e3..99ff614ce3ed77ca6247868c4fe92a3f8d4b1d1c 100644 --- a/bindings/send.go +++ b/bindings/old/send.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "encoding/json" diff --git a/bindings/timeNow.go b/bindings/old/timeNow.go similarity index 97% rename from bindings/timeNow.go rename to bindings/old/timeNow.go index fc457b459ae43452a1920c26a8b9e24c53725d74..b8a15b7d8105203fd06dc6c48ceec4163fdcf4e6 100644 --- a/bindings/timeNow.go +++ b/bindings/old/timeNow.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "gitlab.com/xx_network/primitives/netTime" diff --git a/bindings/ud.go b/bindings/old/ud.go similarity index 99% rename from bindings/ud.go rename to bindings/old/ud.go index ea8f5390abb2b749d14c50b10ec21b9bf8e596d5..6f2f93fd82397937bb06a54fda3ed7db8f7e0dd2 100644 --- a/bindings/ud.go +++ b/bindings/old/ud.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "fmt" diff --git a/bindings/url.go b/bindings/old/url.go similarity index 97% rename from bindings/url.go rename to bindings/old/url.go index f561540f3ecf89fb1b67e139549208884fe46779..b4b0db76bc113f9f00910988567ce852f32d82cb 100644 --- a/bindings/url.go +++ b/bindings/old/url.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "fmt" diff --git a/bindings/user.go b/bindings/old/user.go similarity index 99% rename from bindings/user.go rename to bindings/old/user.go index c049eaeb141d9b19b2acb8772303edf2ba7c6ed9..0338236eb84fe019fab0767333247216b443b3a5 100644 --- a/bindings/user.go +++ b/bindings/old/user.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package bindings +package old import ( "gitlab.com/elixxir/client/interfaces/user" diff --git a/bindings/utils.go b/bindings/old/utils.go similarity index 98% rename from bindings/utils.go rename to bindings/old/utils.go index ddf302b237d3f38dc3aa7795aa8f2624bc86f470..c7cb1ebd04375887f46dab870d6cd940e2a631f1 100644 --- a/bindings/utils.go +++ b/bindings/old/utils.go @@ -6,7 +6,7 @@ // Provides various utility functions for access over the bindings -package bindings +package old import ( "gitlab.com/elixxir/client/api/messenger" diff --git a/bindings/version.go b/bindings/old/version.go similarity index 95% rename from bindings/version.go rename to bindings/old/version.go index ec517de07f5d262e8e761e85e39bcd68d83914b9..9501a3a00473c2898c84fbdb1224cc2445ab0b97 100644 --- a/bindings/version.go +++ b/bindings/old/version.go @@ -1,4 +1,4 @@ -package bindings +package old import "gitlab.com/elixxir/client/api" diff --git a/bindings/utilities.go b/bindings/utilities.go new file mode 100644 index 0000000000000000000000000000000000000000..96c7b346e6a920109d5678fa9802fe5fdfa37435 --- /dev/null +++ b/bindings/utilities.go @@ -0,0 +1,72 @@ +package bindings + +import ( + "fmt" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "google.golang.org/grpc/grpclog" +) + +// sets level of logging. All logs the set level and above will be displayed +// options are: +// TRACE - 0 +// DEBUG - 1 +// INFO - 2 +// WARN - 3 +// ERROR - 4 +// CRITICAL - 5 +// FATAL - 6 +// The default state without updates is: INFO +func LogLevel(level int) error { + if level < 0 || level > 6 { + return errors.New(fmt.Sprintf("log level is not valid: log level: %d", level)) + } + + threshold := jww.Threshold(level) + jww.SetLogThreshold(threshold) + jww.SetStdoutThreshold(threshold) + + switch threshold { + case jww.LevelTrace: + fallthrough + case jww.LevelDebug: + fallthrough + case jww.LevelInfo: + jww.INFO.Printf("Log level set to: %s", threshold) + case jww.LevelWarn: + jww.WARN.Printf("Log level set to: %s", threshold) + case jww.LevelError: + jww.ERROR.Printf("Log level set to: %s", threshold) + case jww.LevelCritical: + jww.CRITICAL.Printf("Log level set to: %s", threshold) + case jww.LevelFatal: + jww.FATAL.Printf("Log level set to: %s", threshold) + } + + return nil +} + +type LogWriter interface { + Log(string) +} + +//RegisterLogWriter registers a callback on which logs are written. +func RegisterLogWriter(writer LogWriter) { + jww.SetLogOutput(&writerAdapter{lw: writer}) +} + +// EnableGrpcLogs sets GRPC trace logging +func EnableGrpcLogs(writer LogWriter) { + logger := &writerAdapter{lw: writer} + grpclog.SetLoggerV2(grpclog.NewLoggerV2WithVerbosity( + logger, logger, logger, 99)) +} + +type writerAdapter struct { + lw LogWriter +} + +func (wa *writerAdapter) Write(p []byte) (n int, err error) { + wa.lw.Log(string(p)) + return len(p), nil +}