Skip to content
Snippets Groups Projects
Select Git revision
  • ee9a4eaed4c1992c7dc17da8d47d6a0811404acf
  • release default protected
  • 11-22-implement-kv-interface-defined-in-collectiveversionedkvgo
  • hotfix/TestHostPool_UpdateNdf_AddFilter
  • XX-4719/announcementChannels
  • xx-4717/logLevel
  • jonah/noob-channel
  • master protected
  • XX-4707/tagDiskJson
  • xx-4698/notification-retry
  • hotfix/notifylockup
  • syncNodes
  • hotfix/localCB
  • XX-4677/NewChanManagerMobile
  • XX-4689/DmSync
  • duplicatePrefix
  • XX-4601/HavenInvites
  • finalizedUICallbacks
  • XX-4673/AdminKeySync
  • debugNotifID
  • anne/test
  • v4.7.5
  • v4.7.4
  • v4.7.3
  • v4.7.2
  • v4.7.1
  • v4.6.3
  • v4.6.1
  • v4.5.0
  • v4.4.4
  • v4.3.11
  • v4.3.8
  • v4.3.7
  • v4.3.6
  • v4.3.5
  • v4.2.0
  • v4.3.0
  • v4.3.4
  • v4.3.3
  • v4.3.2
  • v4.3.1
41 results

main.go

Blame
  • callbacks.go 10.57 KiB
    ////////////////////////////////////////////////////////////////////////////////
    // Copyright © 2022 xx foundation                                             //
    //                                                                            //
    // Use of this source code is governed by a license that can be found in the  //
    // LICENSE file.                                                              //
    ////////////////////////////////////////////////////////////////////////////////
    
    //go:build js && wasm
    
    package main
    
    import (
    	"crypto/ed25519"
    	"encoding/json"
    	"time"
    
    	"github.com/pkg/errors"
    	jww "github.com/spf13/jwalterweatherman"
    	"gitlab.com/elixxir/client/v4/dm"
    	cryptoChannel "gitlab.com/elixxir/crypto/channel"
    	"gitlab.com/elixxir/crypto/fastRNG"
    	wDm "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/dm"
    	"gitlab.com/elixxir/xxdk-wasm/worker"
    	"gitlab.com/xx_network/crypto/csprng"
    )
    
    var zeroUUID = []byte{0, 0, 0, 0, 0, 0, 0, 0}
    
    // manager handles the event model and the message callbacks, which is used to
    // send information between the event model and the main thread.
    type manager struct {
    	mh    *worker.ThreadManager
    	model dm.EventModel
    }
    
    // registerCallbacks registers all the reception callbacks to manage messages
    // from the main thread for the channels.EventModel.
    func (m *manager) registerCallbacks() {
    	m.mh.RegisterCallback(wDm.NewWASMEventModelTag, m.newWASMEventModelCB)
    	m.mh.RegisterCallback(wDm.ReceiveTag, m.receiveCB)
    	m.mh.RegisterCallback(wDm.ReceiveTextTag, m.receiveTextCB)
    	m.mh.RegisterCallback(wDm.ReceiveReplyTag, m.receiveReplyCB)
    	m.mh.RegisterCallback(wDm.ReceiveReactionTag, m.receiveReactionCB)
    	m.mh.RegisterCallback(wDm.UpdateSentStatusTag, m.updateSentStatusCB)
    
    	m.mh.RegisterCallback(wDm.BlockSenderTag, m.blockSenderCB)
    	m.mh.RegisterCallback(wDm.UnblockSenderTag, m.unblockSenderCB)
    	m.mh.RegisterCallback(wDm.GetConversationTag, m.getConversationCB)
    	m.mh.RegisterCallback(wDm.GetConversationsTag, m.getConversationsCB)
    }
    
    // newWASMEventModelCB is the callback for NewWASMEventModel. Returns an empty
    // slice on success or an error message on failure.
    func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) {
    	var msg wDm.NewWASMEventModelMessage
    	err := json.Unmarshal(data, &msg)
    	if err != nil {
    		return []byte{}, errors.Errorf(
    			"failed to JSON unmarshal %T from main thread: %+v", msg, err)
    	}
    
    	// Create new encryption cipher
    	rng := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG)
    	encryption, err := cryptoChannel.NewCipherFromJSON(
    		[]byte(msg.EncryptionJSON), rng.GetStream())
    	if err != nil {
    		return []byte{}, errors.Errorf("failed to JSON unmarshal channel "+
    			"cipher from main thread: %+v", err)
    	}
    
    	m.model, err = NewWASMEventModel(msg.Path, encryption,
    		m.messageReceivedCallback, m.storeDatabaseName, m.storeEncryptionStatus)
    	if err != nil {
    		return []byte(err.Error()), nil
    	}
    	return []byte{}, nil
    }
    
    // messageReceivedCallback sends calls to the MessageReceivedCallback in the
    // main thread.
    //
    // messageReceivedCallback adhere to the MessageReceivedCallback type.
    func (m *manager) messageReceivedCallback(uuid uint64, pubKey ed25519.PublicKey,
    	messageUpdate, conversationUpdate bool) {
    	// Package parameters for sending
    	msg := &wDm.MessageReceivedCallbackMessage{
    		UUID:               uuid,
    		PubKey:             pubKey,
    		MessageUpdate:      messageUpdate,
    		ConversationUpdate: conversationUpdate,
    	}
    	data, err := json.Marshal(msg)
    	if err != nil {
    		jww.ERROR.Printf(
    			"Could not JSON marshal MessageReceivedCallbackMessage: %+v", err)
    		return
    	}
    
    	// Send it to the main thread
    	m.mh.SendMessage(wDm.MessageReceivedCallbackTag, data)
    }
    
    // storeDatabaseName sends the database name to the main thread and waits for
    // the response. This function mocks the behavior of storage.StoreIndexedDb.
    //
    // storeDatabaseName adheres to the storeDatabaseNameFn type.
    func (m *manager) storeDatabaseName(databaseName string) error {
    	// Register response callback with channel that will wait for the response
    	responseChan := make(chan []byte)
    	m.mh.RegisterCallback(wDm.StoreDatabaseNameTag,
    		func(data []byte) ([]byte, error) {
    			responseChan <- data
    			return nil, nil
    		})
    
    	// Send encryption status to main thread
    	m.mh.SendMessage(wDm.StoreDatabaseNameTag, []byte(databaseName))
    
    	// Wait for response
    	select {
    	case response := <-responseChan:
    		if len(response) > 0 {
    			return errors.New(string(response))
    		}
    	case <-time.After(worker.ResponseTimeout):
    		return errors.Errorf("[WW] Timed out after %s waiting for response "+
    			"about storing the database name in local storage in the main "+
    			"thread", worker.ResponseTimeout)
    	}
    
    	return nil
    }
    
    // storeEncryptionStatus sends the database name and encryption status to the
    // main thread and waits for the response. If the value has not been previously
    // saved, it returns the saves encryption status. This function mocks the
    // behavior of storage.StoreIndexedDbEncryptionStatus.
    //
    // storeEncryptionStatus adheres to the storeEncryptionStatusFn type.
    func (m *manager) storeEncryptionStatus(
    	databaseName string, encryption bool) (bool, error) {
    	// Package parameters for sending
    	msg := &wDm.EncryptionStatusMessage{
    		DatabaseName:     databaseName,
    		EncryptionStatus: encryption,
    	}
    	data, err := json.Marshal(msg)
    	if err != nil {
    		return false, err
    	}
    
    	// Register response callback with channel that will wait for the response
    	responseChan := make(chan []byte)
    	m.mh.RegisterCallback(wDm.EncryptionStatusTag,
    		func(data []byte) ([]byte, error) {
    			responseChan <- data
    			return nil, nil
    		})
    
    	// Send encryption status to main thread
    	m.mh.SendMessage(wDm.EncryptionStatusTag, data)
    
    	// Wait for response
    	var response wDm.EncryptionStatusReply
    	select {
    	case responseData := <-responseChan:
    		if err = json.Unmarshal(responseData, &response); err != nil {
    			return false, err
    		}
    	case <-time.After(worker.ResponseTimeout):
    		return false, errors.Errorf("timed out after %s waiting for "+
    			"response about the database encryption status from local "+
    			"storage in the main thread", worker.ResponseTimeout)
    	}
    
    	// If the response contain an error, return it
    	if response.Error != "" {
    		return false, errors.New(response.Error)
    	}
    
    	// Return the encryption status
    	return response.EncryptionStatus, nil
    }
    
    // receiveCB is the callback for wasmModel.Receive. Returns a UUID of 0 on error
    // or the JSON marshalled UUID (uint64) on success.
    func (m *manager) receiveCB(data []byte) ([]byte, error) {
    	var msg wDm.TransferMessage
    	err := json.Unmarshal(data, &msg)
    	if err != nil {
    		return zeroUUID, errors.Errorf(
    			"failed to JSON unmarshal %T from main thread: %+v", msg, err)
    	}
    
    	uuid := m.model.Receive(
    		msg.MessageID, msg.Nickname, msg.Text, msg.PartnerKey, msg.SenderKey, msg.DmToken,
    		msg.Codeset, msg.Timestamp, msg.Round, msg.MType, msg.Status)
    
    	uuidData, err := json.Marshal(uuid)
    	if err != nil {
    		return zeroUUID, errors.Errorf("failed to JSON marshal UUID : %+v", err)
    	}
    	return uuidData, nil
    }
    
    // receiveTextCB is the callback for wasmModel.ReceiveText. Returns a UUID of 0
    // on error or the JSON marshalled UUID (uint64) on success.
    func (m *manager) receiveTextCB(data []byte) ([]byte, error) {
    	var msg wDm.TransferMessage
    	err := json.Unmarshal(data, &msg)
    	if err != nil {
    		return []byte{}, errors.Errorf(
    			"failed to JSON unmarshal %T from main thread: %+v", msg, err)
    	}
    
    	uuid := m.model.ReceiveText(
    		msg.MessageID, msg.Nickname, string(msg.Text), msg.PartnerKey, msg.SenderKey, msg.DmToken,
    		msg.Codeset, msg.Timestamp, msg.Round, msg.Status)
    
    	uuidData, err := json.Marshal(uuid)
    	if err != nil {
    		return []byte{}, errors.Errorf("failed to JSON marshal UUID : %+v", err)
    	}
    
    	return uuidData, nil
    }
    
    // receiveReplyCB is the callback for wasmModel.ReceiveReply. Returns a UUID of
    // 0 on error or the JSON marshalled UUID (uint64) on success.
    func (m *manager) receiveReplyCB(data []byte) ([]byte, error) {
    	var msg wDm.TransferMessage
    	err := json.Unmarshal(data, &msg)
    	if err != nil {
    		return zeroUUID, errors.Errorf(
    			"failed to JSON unmarshal %T from main thread: %+v", msg, err)
    	}
    
    	uuid := m.model.ReceiveReply(msg.MessageID, msg.ReactionTo, msg.Nickname,
    		string(msg.Text), msg.PartnerKey, msg.SenderKey, msg.DmToken, msg.Codeset, msg.Timestamp,
    		msg.Round, msg.Status)
    
    	uuidData, err := json.Marshal(uuid)
    	if err != nil {
    		return zeroUUID, errors.Errorf("failed to JSON marshal UUID : %+v", err)
    	}
    
    	return uuidData, nil
    }
    
    // receiveReactionCB is the callback for wasmModel.ReceiveReaction. Returns a
    // UUID of 0 on error or the JSON marshalled UUID (uint64) on success.
    func (m *manager) receiveReactionCB(data []byte) ([]byte, error) {
    	var msg wDm.TransferMessage
    	err := json.Unmarshal(data, &msg)
    	if err != nil {
    		return zeroUUID, errors.Errorf(
    			"failed to JSON unmarshal %T from main thread: %+v", msg, err)
    	}
    
    	uuid := m.model.ReceiveReaction(msg.MessageID, msg.ReactionTo, msg.Nickname,
    		string(msg.Text), msg.PartnerKey, msg.SenderKey, msg.DmToken, msg.Codeset, msg.Timestamp,
    		msg.Round, msg.Status)
    
    	uuidData, err := json.Marshal(uuid)
    	if err != nil {
    		return zeroUUID, errors.Errorf("failed to JSON marshal UUID : %+v", err)
    	}
    
    	return uuidData, nil
    }
    
    // updateSentStatusCB is the callback for wasmModel.UpdateSentStatus. Always
    // returns nil; meaning, no response is supplied (or expected).
    func (m *manager) updateSentStatusCB(data []byte) ([]byte, error) {
    	var msg wDm.TransferMessage
    	err := json.Unmarshal(data, &msg)
    	if err != nil {
    		return nil, errors.Errorf(
    			"failed to JSON unmarshal %T from main thread: %+v", msg, err)
    	}
    
    	m.model.UpdateSentStatus(
    		msg.UUID, msg.MessageID, msg.Timestamp, msg.Round, msg.Status)
    
    	return nil, nil
    }
    
    // blockSenderCB is the callback for wasmModel.BlockSender. Always
    // returns nil; meaning, no response is supplied (or expected).
    func (m *manager) blockSenderCB(data []byte) ([]byte, error) {
    	m.model.BlockSender(data)
    	return nil, nil
    }
    
    // unblockSenderCB is the callback for wasmModel.UnblockSender. Always
    // returns nil; meaning, no response is supplied (or expected).
    func (m *manager) unblockSenderCB(data []byte) ([]byte, error) {
    	m.model.UnblockSender(data)
    	return nil, nil
    }
    
    // getConversationCB is the callback for wasmModel.GetConversation.
    // Returns nil on error or the JSON marshalled Conversation on success.
    func (m *manager) getConversationCB(data []byte) ([]byte, error) {
    	result := m.model.GetConversation(data)
    	return json.Marshal(result)
    }
    
    // getConversationsCB is the callback for wasmModel.GetConversations.
    // Returns nil on error or the JSON marshalled list of Conversation on success.
    func (m *manager) getConversationsCB(_ []byte) ([]byte, error) {
    	result := m.model.GetConversations()
    	return json.Marshal(result)
    }