Skip to content
Snippets Groups Projects
ud.go 13.5 KiB
Newer Older
Jono Wenger's avatar
Jono Wenger committed
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2022 xx foundation                                             //
Jono Wenger's avatar
Jono Wenger committed
//                                                                            //
// Use of this source code is governed by a license that can be found in the  //
// LICENSE file.                                                              //
Jono Wenger's avatar
Jono Wenger committed
////////////////////////////////////////////////////////////////////////////////

//go:build js && wasm

package wasm

import (
	"gitlab.com/elixxir/client/bindings"
	"syscall/js"
)

////////////////////////////////////////////////////////////////////////////////
// Structs and Interfaces                                                     //
////////////////////////////////////////////////////////////////////////////////

// UserDiscovery wraps the [bindings.UserDiscovery] object so its methods can be
// wrapped to be Javascript compatible.
type UserDiscovery struct {
	api *bindings.UserDiscovery
}

// newE2eJS creates a new Javascript compatible object (map[string]interface{})
// that matches the E2e structure.
func newUserDiscoveryJS(api *bindings.UserDiscovery) map[string]interface{} {
	ud := UserDiscovery{api}
	udMap := map[string]interface{}{
		"GetID":                  js.FuncOf(ud.GetID),
		"GetFacts":               js.FuncOf(ud.GetFacts),
		"GetContact":             js.FuncOf(ud.GetContact),
		"ConfirmFact":            js.FuncOf(ud.ConfirmFact),
		"SendRegisterFact":       js.FuncOf(ud.SendRegisterFact),
		"PermanentDeleteAccount": js.FuncOf(ud.PermanentDeleteAccount),
		"RemoveFact":             js.FuncOf(ud.RemoveFact),
	}

	return udMap
}

// GetID returns the ID for this UserDiscovery in the UserDiscovery tracker.
//
// Returns:
//  - int
func (ud *UserDiscovery) GetID(js.Value, []js.Value) interface{} {
	return ud.api.GetID()
}

// udNetworkStatus wraps Javascript callbacks to adhere to the
// [bindings.UdNetworkStatus] interface.
type udNetworkStatus struct {
	udNetworkStatus func(args ...interface{}) js.Value
}

func (uns *udNetworkStatus) UdNetworkStatus() int {
	return uns.udNetworkStatus().Int()
}

////////////////////////////////////////////////////////////////////////////////
// Manager functions                                                          //
////////////////////////////////////////////////////////////////////////////////

// NewOrLoadUd loads an existing Manager from storage or creates a new one if
// there is no extant storage information. Parameters need be provided to
// specify how to connect to the User Discovery service. These parameters may be
// used to contact either the UD server hosted by the xx network team or a
// custom third-party operated server. For the former, all the information may
// be pulled from the NDF using the bindings.
//
// Params
//  - args[0] - ID of E2e object in tracker (int).
//  - args[1] - Javascript object that has functions that implement the
//    [bindings.UdNetworkStatus] interface. This is the network follower
//    function wrapped in [bindings.UdNetworkStatus].
//  - args[2] - the username the user wants to register with UD. If the user is
//    already registered, this field may be blank (string).
//  - args[3] - the registration validation signature; a signature provided by
//    the network (i.e., the client registrar). This may be nil; however, UD may
//    return an error in some cases (e.g., in a production level environment)
//    (Uint8Array).
//  - args[4] - the TLS certificate for the UD server this call will connect
//    with. You may use the UD server run by the xx network team by using
//    E2e.GetUdCertFromNdf (Uint8Array).
//  - args[5] - marshalled [contact.Contact]. This represents the contact file
//    of the server this call will connect with. You may use the UD server run
//    by the xx network team by using E2e.GetUdContactFromNdf (Uint8Array).
//  - args[6] - the IP address of the UD server this call will connect with. You
//    may use the UD server run by the xx network team by using
//    E2e.GetUdAddressFromNdf (string).
//
// Returns:
//  - Javascript representation of the UserDiscovery object that is registered
//    to the specified UD service.
//  - Throws a TypeError if creating or loading fails.
func NewOrLoadUd(_ js.Value, args []js.Value) interface{} {
	e2eID := args[0].Int()
	follower := &udNetworkStatus{WrapCB(args[1], "UdNetworkStatus")}
Jono Wenger's avatar
Jono Wenger committed
	username := args[2].String()
	registrationValidationSignature := CopyBytesToGo(args[3])
	cert := CopyBytesToGo(args[4])
	contactFile := CopyBytesToGo(args[5])
	address := args[6].String()

	api, err := bindings.NewOrLoadUd(e2eID, follower, username,
		registrationValidationSignature, cert, contactFile, address)
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return newUserDiscoveryJS(api)
}

// NewUdManagerFromBackup builds a new user discover manager from a backup. It
// will construct a manager that is already registered and restore already
// registered facts into store.
//
// Note that it can take in both ane email address and a phone number or both.
// However, at least one fact must be specified; providing no facts will return
// an error.
//
// Parameters:
//  - args[0] - ID of E2e object in tracker (int).
//  - args[1] - Javascript object that has functions that implement the
//    [bindings.UdNetworkStatus] interface. This is the network follower
//    function wrapped in [bindings.UdNetworkStatus].
//  - args[2] - JSON of [fact.Fact] username that is registered with UD
Jono Wenger's avatar
Jono Wenger committed
//    (Uint8Array).
//  - args[3] - JSON of [fact.Fact] email address that is registered with UD
Jono Wenger's avatar
Jono Wenger committed
//    (Uint8Array).
//  - args[4] - JSON of [fact.Fact] phone number that is registered with UD
//    (Uint8Array).
//  - args[5] - the TLS certificate for the UD server this call will connect
Jono Wenger's avatar
Jono Wenger committed
//    with. You may use the UD server run by the xx network team by using
//    E2e.GetUdCertFromNdf (Uint8Array).
//  - args[6] - marshalled [contact.Contact]. This represents the contact file
Jono Wenger's avatar
Jono Wenger committed
//    of the server this call will connect with. You may use the UD server run
//    by the xx network team by using E2e.GetUdContactFromNdf (Uint8Array).
//  - args[7] - the IP address of the UD server this call will connect with. You
Jono Wenger's avatar
Jono Wenger committed
//    may use the UD server run by the xx network team by using
//    E2e.GetUdAddressFromNdf (string).
//
// Returns:
//  - Javascript representation of the UserDiscovery object that is loaded from
//    backup.
//  - Throws a TypeError if getting UD from backup fails.
func NewUdManagerFromBackup(_ js.Value, args []js.Value) interface{} {
	e2eID := args[0].Int()
	follower := &udNetworkStatus{WrapCB(args[1], "UdNetworkStatus")}
	usernameFactJson := CopyBytesToGo(args[2])
	emailFactJson := CopyBytesToGo(args[3])
	phoneFactJson := CopyBytesToGo(args[4])
	cert := CopyBytesToGo(args[5])
	contactFile := CopyBytesToGo(args[6])
	address := args[7].String()

	api, err := bindings.NewUdManagerFromBackup(e2eID, follower, usernameFactJson, emailFactJson,
Jono Wenger's avatar
Jono Wenger committed
		phoneFactJson, cert, contactFile, address)
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return newUserDiscoveryJS(api)
}

// GetFacts returns a JSON marshalled list of [fact.Fact] objects that exist
// within the Store's registeredFacts map.
//
// Returns:
//  - JSON of [fact.FactList] (Uint8Array).
func (ud *UserDiscovery) GetFacts(js.Value, []js.Value) interface{} {
	return CopyBytesToJS(ud.api.GetFacts())
}

// GetContact returns the marshalled bytes of the [contact.Contact] for UD as
// retrieved from the NDF.
//
// Returns:
//  - JSON of [contact.Contact] (Uint8Array).
//  - Throws TypeError if getting the contact fails.
func (ud *UserDiscovery) GetContact(js.Value, []js.Value) interface{} {
	c, err := ud.api.GetContact()
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return CopyBytesToJS(c)
}

// ConfirmFact confirms a fact first registered via SendRegisterFact. The
// confirmation ID comes from SendRegisterFact while the code will come over the
// associated communications system.
//
// Parameters:
//  - args[0] - confirmation ID (string).
//  - args[1] - code (string).
//
// Returns:
//  - Throws TypeError if confirming the fact fails.
func (ud *UserDiscovery) ConfirmFact(_ js.Value, args []js.Value) interface{} {
	err := ud.api.ConfirmFact(args[0].String(), args[1].String())
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return nil
}

// SendRegisterFact adds a fact for the user to user discovery. Will only
// succeed if the user is already registered and the system does not have the
// fact currently registered for any user.
//
// This does not complete the fact registration process, it returns a
// confirmation ID instead. Over the communications system the fact is
// associated with, a code will be sent. This confirmation ID needs to be called
// along with the code to finalize the fact.
//
// Parameters:
//  - args[0] - JSON of [fact.Fact] (Uint8Array).
//
// Returns:
//  - The confirmation ID (string).
//  - Throws TypeError if sending the fact fails.
func (ud *UserDiscovery) SendRegisterFact(_ js.Value, args []js.Value) interface{} {
	confirmationID, err := ud.api.SendRegisterFact(CopyBytesToGo(args[0]))
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return confirmationID
}

// PermanentDeleteAccount removes the username associated with this user from
// the UD service. This will only take a username type fact, and the fact must
// be associated with this user.
//
// Parameters:
//  - args[0] - JSON of [fact.Fact] (Uint8Array).
//
// Returns:
//  - Throws TypeError if deletion fails.
func (ud *UserDiscovery) PermanentDeleteAccount(_ js.Value, args []js.Value) interface{} {
	err := ud.api.PermanentDeleteAccount(CopyBytesToGo(args[0]))
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return nil
}

// RemoveFact removes a previously confirmed fact. This will fail if the fact
// passed in is not UD service does not associate this fact with this user.
//
// Parameters:
//  - args[0] - JSON of [fact.Fact] (Uint8Array).
//
// Returns:
//  - Throws TypeError if removing the fact fails.
func (ud *UserDiscovery) RemoveFact(_ js.Value, args []js.Value) interface{} {
	err := ud.api.RemoveFact(CopyBytesToGo(args[0]))
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return nil
}

////////////////////////////////////////////////////////////////////////////////
// User Discovery Lookup                                                      //
////////////////////////////////////////////////////////////////////////////////

// udLookupCallback wraps Javascript callbacks to adhere to the
// [bindings.UdLookupCallback] interface.
type udLookupCallback struct {
	callback func(args ...interface{}) js.Value
}

func (ulc *udLookupCallback) Callback(contactBytes []byte, err error) {
	ulc.callback(CopyBytesToJS(contactBytes), err.Error())
}

// LookupUD returns the public key of the passed ID as known by the user
// discovery system or returns by the timeout.
//
// Parameters:
//  - args[0] - ID of E2e object in tracker (int).
//  - args[1] - JSON of User Discovery's [contact.Contact] (Uint8Array).
//  - args[2] - Javascript object that has functions that implement the
//    [bindings.UdLookupCallback] interface.
//  - args[3] - JSON of [id.ID] for the user to look up (Uint8Array).
//  - args[4] - JSON of [single.RequestParams] (Uint8Array).
//
// Returns:
//  - JSON of [bindings.SingleUseSendReport], which can be passed into
//    Cmix.WaitForRoundResult to see if the send succeeded.
//  - Throws a TypeError if the lookup fails.
func LookupUD(_ js.Value, args []js.Value) interface{} {
	e2eID := args[0].Int()
	udContact := CopyBytesToGo(args[1])
	cb := &udLookupCallback{WrapCB(args[2], "Callback")}
Jono Wenger's avatar
Jono Wenger committed
	lookupId := CopyBytesToGo(args[3])
	singleRequestParamsJSON := CopyBytesToGo(args[4])

	report, err := bindings.LookupUD(
		e2eID, udContact, cb, lookupId, singleRequestParamsJSON)
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return CopyBytesToJS(report)
}

////////////////////////////////////////////////////////////////////////////////
// User Discovery Search                                                      //
////////////////////////////////////////////////////////////////////////////////

// udSearchCallback wraps Javascript callbacks to adhere to the
// [bindings.UdSearchCallback] interface.
type udSearchCallback struct {
	callback func(args ...interface{}) js.Value
}

func (usc *udSearchCallback) Callback(contactListJSON []byte, err error) {
	usc.callback(CopyBytesToJS(contactListJSON), err.Error())
}

// SearchUD searches user discovery for the passed Facts. The searchCallback
// will return a list of contacts, each having the facts it hit against. This is
// NOT intended to be used to search for multiple users at once; that can have a
// privacy reduction. Instead, it is intended to be used to search for a user
// where multiple pieces of information is known.
//
// Parameters:
//  - args[0] - ID of E2e object in tracker (int).
//  - args[1] - JSON of User Discovery's [contact.Contact] (Uint8Array).
//  - args[2] - JSON of [fact.FactList] (Uint8Array).
//  - args[4] - JSON of [single.RequestParams] (Uint8Array).
//
// Returns:
//  - JSON of [bindings.SingleUseSendReport], which can be passed into
//    Cmix.WaitForRoundResult to see if the send succeeded.
//  - Throws a TypeError if the search fails.
func SearchUD(_ js.Value, args []js.Value) interface{} {
	e2eID := args[0].Int()
	udContact := CopyBytesToGo(args[1])
	cb := &udSearchCallback{WrapCB(args[2], "Callback")}
Jono Wenger's avatar
Jono Wenger committed
	factListJSON := CopyBytesToGo(args[3])
	singleRequestParamsJSON := CopyBytesToGo(args[4])

	report, err := bindings.SearchUD(
		e2eID, udContact, cb, factListJSON, singleRequestParamsJSON)
	if err != nil {
Jono Wenger's avatar
Jono Wenger committed
		return nil
	}

	return CopyBytesToJS(report)
}