diff --git a/go.mod b/go.mod index 1c411160879cd506aecc8ee9457fa16a8a7345e5..88cf86454de4d418c927e2c7c99378b5f3d9f514 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,8 @@ require ( github.com/hack-pad/go-indexeddb v0.2.0 github.com/pkg/errors v0.9.1 github.com/spf13/jwalterweatherman v1.1.0 - gitlab.com/elixxir/client/v4 v4.6.2-0.20230404165346-ef90ea32cdc7 - gitlab.com/elixxir/crypto v0.0.7-0.20230322175717-4a3b5a24bdf4 + gitlab.com/elixxir/client/v4 v4.6.2-0.20230404214725-85793f3ddac0 + gitlab.com/elixxir/crypto v0.0.7-0.20230322181929-8cb5fa100824 gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c gitlab.com/xx_network/crypto v0.0.5-0.20230214003943-8a09396e95dd gitlab.com/xx_network/primitives v0.0.4-0.20230310205521-c440e68e34c4 diff --git a/go.sum b/go.sum index eef39c8cecbb5d0df70005f4dd38735fec9c7e7f..bdfcc0b0dde048a55da149aa29d28642382f04b7 100644 --- a/go.sum +++ b/go.sum @@ -391,12 +391,12 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/elixxir/bloomfilter v0.0.0-20230315224936-a4459418f300 h1:oF3Pkf5NBb48KB89Q4sQXKQCIsWp1IVsqKWHWFsfBRc= gitlab.com/elixxir/bloomfilter v0.0.0-20230315224936-a4459418f300/go.mod h1:1X8gRIAPDisS3W6Vtr/ymiUmZMJUIwDV1o5DEOo/pzw= -gitlab.com/elixxir/client/v4 v4.6.2-0.20230404165346-ef90ea32cdc7 h1:5dKfq6Vy5O4szG6clXv+6GjCVNXBsNgIOVlJW0DC7bM= -gitlab.com/elixxir/client/v4 v4.6.2-0.20230404165346-ef90ea32cdc7/go.mod h1:WEYVoIXHi2YMR0JafS5pHNWLOtnkLBBt8KdhKF/ZHcY= +gitlab.com/elixxir/client/v4 v4.6.2-0.20230404214725-85793f3ddac0 h1:17xwWK6AQYcpfJmYpFUEMU/PDMe+qqsRYPyZakHtAn4= +gitlab.com/elixxir/client/v4 v4.6.2-0.20230404214725-85793f3ddac0/go.mod h1:WEYVoIXHi2YMR0JafS5pHNWLOtnkLBBt8KdhKF/ZHcY= gitlab.com/elixxir/comms v0.0.4-0.20230310205528-f06faa0d2f0b h1:8AVK93UEs/aufoqtFgyMVt9gf0oJ8F4pA60ZvEVvG+s= gitlab.com/elixxir/comms v0.0.4-0.20230310205528-f06faa0d2f0b/go.mod h1:z+qW0D9VpY5QKTd7wRlb5SK4kBNqLYsa4DXBcUXue9Q= -gitlab.com/elixxir/crypto v0.0.7-0.20230322175717-4a3b5a24bdf4 h1:pLG3g2AI1o9zr4bHuScM9jVaxoAy+ZlEY2AsK33F9Qo= -gitlab.com/elixxir/crypto v0.0.7-0.20230322175717-4a3b5a24bdf4/go.mod h1:/SLOlvkYVVJf6IU+vEjMLnS7cjjcoTlPV45g6tv6INc= +gitlab.com/elixxir/crypto v0.0.7-0.20230322181929-8cb5fa100824 h1:6gmaBG4glJKA41SV2tNBbT6mFwTEXR9Jn9JaU6JSSKM= +gitlab.com/elixxir/crypto v0.0.7-0.20230322181929-8cb5fa100824/go.mod h1:/SLOlvkYVVJf6IU+vEjMLnS7cjjcoTlPV45g6tv6INc= gitlab.com/elixxir/ekv v0.2.1 h1:dtwbt6KmAXG2Tik5d60iDz2fLhoFBgWwST03p7T+9Is= gitlab.com/elixxir/ekv v0.2.1/go.mod h1:USLD7xeDnuZEavygdrgzNEwZXeLQJK/w1a+htpN+JEU= gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c h1:muG8ff95woeVVwQoJHCEclxBFB22lc7EixPylEkYDRU= diff --git a/indexedDb/impl/channels/callbacks.go b/indexedDb/impl/channels/callbacks.go index 79fba1cc290097701747c5403a7ebf364369ca46..b3add64fcf95da83000434c784e96199a5fc3c0e 100644 --- a/indexedDb/impl/channels/callbacks.go +++ b/indexedDb/impl/channels/callbacks.go @@ -77,7 +77,7 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) { return []byte(err.Error()), nil } - return nil, nil + return []byte{}, nil } // messageReceivedCallback sends calls to the channels.MessageReceivedCallback diff --git a/indexedDb/impl/dm/callbacks.go b/indexedDb/impl/dm/callbacks.go index a2deba1774d8c3936340a86170349def554ad705..5fe03874609ddfb4c92a4680494ac6c475568ae3 100644 --- a/indexedDb/impl/dm/callbacks.go +++ b/indexedDb/impl/dm/callbacks.go @@ -74,7 +74,7 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) { return []byte(err.Error()), nil } - return nil, nil + return []byte{}, nil } // messageReceivedCallback sends calls to the MessageReceivedCallback in the diff --git a/indexedDb/impl/dm/implementation.go b/indexedDb/impl/dm/implementation.go index cbb4744066833d77a3b075fcaa70a0219f5d1962..98907a7f9d60c1a7f8f1dbd84350113163f9443e 100644 --- a/indexedDb/impl/dm/implementation.go +++ b/indexedDb/impl/dm/implementation.go @@ -30,8 +30,7 @@ import ( "gitlab.com/xx_network/primitives/id" ) -// wasmModel implements dm.EventModel interface, which uses the channels system -// passed an object that adheres to in order to get events on the channel. +// wasmModel implements dm.EventModel interface backed by IndexedDb. // NOTE: This model is NOT thread safe - it is the responsibility of the // caller to ensure that its methods are called sequentially. type wasmModel struct { @@ -217,10 +216,10 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n data string, partnerKey, senderKey ed25519.PublicKey, dmToken uint32, codeset uint8, timestamp time.Time, round rounds.Round, mType dm.MessageType, status dm.Status) (uint64, error) { - // Keep track of whether Conversation was altered - // FIXME: this is very similar to updateConversation - //. below. Can we merge them? - conversationUpdated := false + // Keep track of whether a Conversation was altered + var convoToUpdate *Conversation + + // Determine whether Conversation needs to be created result, err := w.getConversation(partnerKey) if err != nil { if !strings.Contains(err.Error(), impl.ErrDoesNotExist) { @@ -229,18 +228,18 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n // If there is no extant Conversation, create one. jww.DEBUG.Printf( "[DM indexedDB] Joining conversation with %s", nickname) - err = w.upsertConversation(nickname, partnerKey, dmToken, - codeset, false) - if err != nil { - return 0, err + convoToUpdate = &Conversation{ + Pubkey: senderKey, + Nickname: nickname, + Token: dmToken, + CodesetVersion: codeset, + Blocked: false, } - conversationUpdated = true } } else { jww.DEBUG.Printf( "[DM indexedDB] Conversation with %s already joined", nickname) - updateConversation := false // Update Conversation if nickname was altered isFromPartner := bytes.Equal(result.Pubkey, senderKey) nicknameChanged := result.Nickname != nickname @@ -248,28 +247,29 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n jww.DEBUG.Printf( "[DM indexedDB] Updating from nickname %s to %s", result.Nickname, nickname) - updateConversation = true + convoToUpdate = result + convoToUpdate.Nickname = nickname } // Fix conversation if dmToken is altered dmTokenChanged := result.Token != dmToken if isFromPartner && dmTokenChanged { jww.WARN.Printf( - "[DM indexedDB] Updating from dmToken %s to %s", + "[DM indexedDB] Updating from dmToken %d to %d", result.Token, dmToken) - updateConversation = true + convoToUpdate = result + convoToUpdate.Token = dmToken } + } - if updateConversation { - err = w.upsertConversation(nickname, result.Pubkey, - result.Token, result.CodesetVersion, - result.Blocked) - if err != nil { - return 0, err - } - conversationUpdated = true + // Update the conversation in storage, if needed + conversationUpdated := convoToUpdate != nil + if conversationUpdated { + err = w.upsertConversation(convoToUpdate.Nickname, convoToUpdate.Pubkey, + convoToUpdate.Token, convoToUpdate.CodesetVersion, convoToUpdate.Blocked) + if err != nil { + return 0, err } - } // Handle encryption, if it is present diff --git a/indexedDb/impl/dm/implementation_test.go b/indexedDb/impl/dm/implementation_test.go index 409c6239b84787d3696e62fcb4f7f8e9092b9c2e..8e05e6af5fff85af79ea0f15be9dc1e2f5a77c12 100644 --- a/indexedDb/impl/dm/implementation_test.go +++ b/indexedDb/impl/dm/implementation_test.go @@ -10,9 +10,20 @@ package main import ( + "bytes" "crypto/ed25519" + "encoding/json" + "fmt" + "gitlab.com/elixxir/client/v4/cmix/rounds" + "gitlab.com/elixxir/client/v4/dm" + "gitlab.com/elixxir/crypto/message" + "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" + "gitlab.com/elixxir/xxdk-wasm/utils" + "gitlab.com/xx_network/primitives/id" "os" + "syscall/js" "testing" + "time" jww "github.com/spf13/jwalterweatherman" ) @@ -24,9 +35,98 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } +// Test simple receive of a new message for a new conversation. +func TestImpl_Receive(t *testing.T) { + m, err := newWASMModel("TestImpl_Receive", nil, + dummyReceivedMessageCB) + if err != nil { + t.Fatal(err.Error()) + } + + testString := "test" + testBytes := []byte(testString) + partnerPubKey := ed25519.PublicKey(testBytes) + testRound := id.Round(10) + + // Can use ChannelMessageID for ease, doesn't matter here + testMsgId := message.DeriveChannelMessageID(&id.ID{1}, uint64(testRound), testBytes) + + // Receive a test message + uuid := m.Receive(testMsgId, testString, testBytes, + partnerPubKey, partnerPubKey, 0, 0, time.Now(), + rounds.Round{ID: testRound}, dm.TextType, dm.Received) + if uuid == 0 { + t.Fatalf("Expected non-zero message uuid") + } + jww.DEBUG.Printf("Received test message: %d", uuid) + + // First, we expect a conversation to be created + testConvo := m.GetConversation(partnerPubKey) + if testConvo == nil { + t.Fatalf("Expected conversation to be created") + } + // Spot check a conversation attribute + if testConvo.Nickname != testString { + t.Fatalf("Expected conversation nickname %s, got %s", + testString, testConvo.Nickname) + } + + // Next, we expect the message to be created + testMessageObj, err := impl.Get(m.db, messageStoreName, js.ValueOf(uuid)) + if err != nil { + t.Fatalf(err.Error()) + } + testMessage := &Message{} + err = json.Unmarshal([]byte(utils.JsToJson(testMessageObj)), testMessage) + if err != nil { + t.Fatalf(err.Error()) + } + // Spot check a message attribute + if !bytes.Equal(testMessage.SenderPubKey, partnerPubKey) { + t.Fatalf("Expected message attibutes to match, expected %v got %v", + partnerPubKey, testMessage.SenderPubKey) + } +} + +// Test happy path. Insert some conversations and check they exist. +func TestImpl_GetConversations(t *testing.T) { + m, err := newWASMModel("TestImpl_GetConversations", nil, + dummyReceivedMessageCB) + if err != nil { + t.Fatal(err.Error()) + } + numTestConvo := 10 + + // Insert a test convo + for i := 0; i < numTestConvo; i++ { + testBytes := []byte(fmt.Sprintf("%d", i)) + testPubKey := ed25519.PublicKey(testBytes) + err = m.upsertConversation("test", testPubKey, + uint32(i), uint8(i), false) + if err != nil { + t.Fatal(err.Error()) + } + } + + results := m.GetConversations() + if len(results) != numTestConvo { + t.Fatalf("Expected %d convos, got %d", numTestConvo, len(results)) + } + + for i, convo := range results { + if convo.Token != uint32(i) { + t.Fatalf("Expected %d convo token, got %d", i, convo.Token) + } + if convo.CodesetVersion != uint8(i) { + t.Fatalf("Expected %d convo codeset, got %d", + i, convo.CodesetVersion) + } + } +} + // Test happy path toggling between blocked/unblocked in a Conversation. func TestWasmModel_BlockSender(t *testing.T) { - m, err := newWASMModel("test", nil, dummyReceivedMessageCB) + m, err := newWASMModel("TestWasmModel_BlockSender", nil, dummyReceivedMessageCB) if err != nil { t.Fatal(err.Error()) } diff --git a/indexedDb/worker/channels/init.go b/indexedDb/worker/channels/init.go index 0fa2fd566c50625af3d623eb3383a04bb035134d..2ee630caf43177460a19f14d2b953d3a03b9c687 100644 --- a/indexedDb/worker/channels/init.go +++ b/indexedDb/worker/channels/init.go @@ -121,7 +121,7 @@ func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher, select { case data := <-dataChan: - if data != nil { + if len(data) > 0 { return nil, errors.New(string(data)) } case <-time.After(worker.ResponseTimeout): diff --git a/indexedDb/worker/dm/init.go b/indexedDb/worker/dm/init.go index d8e905bed513f4bb44d0920ee18d75be7e3877e2..3fd1cd13897bdc8684e2efc13750e1fed20ff000 100644 --- a/indexedDb/worker/dm/init.go +++ b/indexedDb/worker/dm/init.go @@ -89,7 +89,7 @@ func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher, select { case data := <-dataChan: - if data != nil { + if len(data) > 0 { return nil, errors.New(string(data)) } case <-time.After(worker.ResponseTimeout): diff --git a/wasm_test.go b/wasm_test.go index 4e3983a3ae874938980e2ae28a23519900ff27a2..2d36f7b0c8d3ae5a024ea837f6da9526dc42d3c8 100644 --- a/wasm_test.go +++ b/wasm_test.go @@ -58,6 +58,7 @@ func TestPublicFunctions(t *testing.T) { // Mobile-specific bindings not supported by the browser "NewChannelsManagerMobile": {}, "LoadChannelsManagerMobile": {}, + "NewDmManagerMobile": {}, // C-Library specific bindings not needed by the browser "GetDMInstance": {},