Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • elixxir/xxdk-wasm
1 result
Show changes
Commits on Source (60)
Showing
with 455 additions and 593 deletions
......@@ -38,12 +38,12 @@ wasm-test:
- tags
script:
- export PATH=/root/go/bin:$PATH
- echo > utils/utils_js.s
- go mod vendor
- unset SSH_PRIVATE_KEY
- unset $(env | grep '=' | awk -F= '{print $1}' | grep -v PATH | grep -v GO | grep -v HOME)
- echo "WASM TESTS DISABLED FOR XX-4522, but will run them just so you can see output"
- GOOS=js GOARCH=wasm go test ./... -v || true
- rm vendor/gitlab.com/elixxir/wasm-utils/exception/throw_js.s
- mv vendor/gitlab.com/elixxir/wasm-utils/exception/throws.dev vendor/gitlab.com/elixxir/wasm-utils/exception/throws.go
- GOOS=js GOARCH=wasm go test ./... -v
build:
stage: build
......@@ -69,9 +69,11 @@ build-workers:
- GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o release/xxdk-channelsIndexedDkWorker.wasm ./indexedDb/impl/channels/...
- GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o release/xxdk-dmIndexedDkWorker.wasm ./indexedDb/impl/dm/...
- GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o release/xxdk-logFileWorker.wasm ./logging/workerThread/...
- GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o release/xxdk-stateIndexedDbWorker.wasm ./indexedDb/impl/state/...
- cp indexedDb/impl/channels/channelsIndexedDbWorker.js release/
- cp indexedDb/impl/dm/dmIndexedDbWorker.js release/
- cp logging/workerThread/logFileWorker.js release/
- cp indexedDb/impl/state/stateIndexedDbWorker.js release/
artifacts:
paths:
- release/
......@@ -112,9 +114,11 @@ combine-artifacts:
- 'curl --fail --location --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" --output release/xxdk-channelsIndexedDkWorker.wasm $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$BUILD_WORKERS_JOB_ID/artifacts/release/xxdk-channelsIndexedDkWorker.wasm'
- 'curl --fail --location --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" --output release/xxdk-dmIndexedDkWorker.wasm $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$BUILD_WORKERS_JOB_ID/artifacts/release/xxdk-dmIndexedDkWorker.wasm'
- 'curl --fail --location --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" --output release/xxdk-logFileWorker.wasm $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$BUILD_WORKERS_JOB_ID/artifacts/release/xxdk-logFileWorker.wasm'
- 'curl --fail --location --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" --output release/xxdk-stateIndexedDbWorker.wasm $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$BUILD_WORKERS_JOB_ID/artifacts/release/xxdk-stateIndexedDbWorker.wasm'
- 'curl --fail --location --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" --output release/channelsIndexedDbWorker.js $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$BUILD_WORKERS_JOB_ID/artifacts/release/channelsIndexedDbWorker.js'
- 'curl --fail --location --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" --output release/dmIndexedDbWorker.js $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$BUILD_WORKERS_JOB_ID/artifacts/release/dmIndexedDbWorker.js'
- 'curl --fail --location --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" --output release/logFileWorker.js $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$BUILD_WORKERS_JOB_ID/artifacts/release/logFileWorker.js'
- 'curl --fail --location --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" --output release/stateIndexedDbWorker.js $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$BUILD_WORKERS_JOB_ID/artifacts/release/stateIndexedDbWorker.js'
- ls release
artifacts:
paths:
......
......@@ -11,18 +11,20 @@ build:
GOOS=js GOARCH=wasm go build ./...
update_release:
GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@release
GOFLAGS="" go get gitlab.com/xx_network/primitives@release
GOFLAGS="" go get gitlab.com/elixxir/primitives@release
GOFLAGS="" go get gitlab.com/xx_network/crypto@release
GOFLAGS="" go get gitlab.com/elixxir/crypto@release
GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@release
GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@project/HavenBeta
update_master:
GOFLAGS="" go get -d gitlab.com/elixxir/client@master
GOFLAGS="" go get gitlab.com/elixxir/crypto@master
GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@master
GOFLAGS="" go get gitlab.com/xx_network/primitives@master
GOFLAGS="" go get gitlab.com/elixxir/primitives@master
GOFLAGS="" go get gitlab.com/xx_network/crypto@master
GOFLAGS="" go get gitlab.com/xx_network/primitives@master
GOFLAGS="" go get gitlab.com/elixxir/crypto@master
GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@master
binary:
GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o xxdk.wasm main.go
......@@ -30,15 +32,21 @@ binary:
worker_binaries:
GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o xxdk-channelsIndexedDkWorker.wasm ./indexedDb/impl/channels/...
GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o xxdk-dmIndexedDkWorker.wasm ./indexedDb/impl/dm/...
GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o xxdk-stateIndexedDkWorker.wasm ./indexedDb/impl/state/...
GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o xxdk-logFileWorker.wasm ./logging/workerThread/...
binaries: binary worker_binaries
wasmException = "vendor/gitlab.com/elixxir/wasm-utils/exception"
wasm_tests:
cp utils/utils_js.s utils/utils_js.s.bak
> utils/utils_js.s
cp $(wasmException)/throw_js.s $(wasmException)/throw_js.s.bak
cp $(wasmException)/throws.go $(wasmException)/throws.go.bak
> $(wasmException)/throw_js.s
cp $(wasmException)/throws.dev $(wasmException)/throws.go
-GOOS=js GOARCH=wasm go test -v ./...
mv utils/utils_js.s.bak utils/utils_js.s
mv $(wasmException)/throw_js.s.bak $(wasmException)/throw_js.s
mv $(wasmException)/throws.go.bak $(wasmException)/throws.go
go_tests:
go test ./... -v
......
......@@ -77,7 +77,7 @@ global.Go = class {
go: {
// ...
// func Throw(exception string, message string)
'gitlab.com/elixxir/xxdk-wasm/utils.throw': (sp) => {
'gitlab.com/elixxir/wasm-utils/utils.throw': (sp) => {
const exception = loadString(sp + 8)
const message = loadString(sp + 24)
throw globalThis[exception](message)
......
......@@ -6,12 +6,15 @@ require (
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
github.com/hack-pad/go-indexeddb v0.2.0
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.7.0
github.com/spf13/jwalterweatherman v1.1.0
gitlab.com/elixxir/client/v4 v4.6.3
gitlab.com/elixxir/crypto v0.0.7-0.20230413162806-a99ec4bfea32
github.com/stretchr/testify v1.8.2
gitlab.com/elixxir/client/v4 v4.6.4-0.20230525161612-2bfadc894901
gitlab.com/elixxir/crypto v0.0.7-0.20230522162218-45433d877235
gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c
gitlab.com/elixxir/wasm-utils v0.0.0-20230522231408-a43b2c1481b2
gitlab.com/xx_network/crypto v0.0.5-0.20230214003943-8a09396e95dd
gitlab.com/xx_network/primitives v0.0.4-0.20230310205521-c440e68e34c4
gitlab.com/xx_network/primitives v0.0.4-0.20230522171102-940cdd68e516
golang.org/x/crypto v0.5.0
)
......@@ -22,49 +25,40 @@ require (
github.com/badoux/checkmail v1.2.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cloudflare/circl v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/elliotchance/orderedmap v1.4.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/gobwas/ws v1.1.0 // indirect
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/improbable-eng/grpc-web v0.15.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.15 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/oasisprotocol/curve25519-voi v0.0.0-20221003100820-41fad3beba17 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
github.com/pkg/profile v1.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/cors v1.8.2 // indirect
github.com/sethvargo/go-diceware v0.3.0 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.5.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.12.0 // indirect
github.com/subosito/gotenv v1.4.0 // indirect
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect
github.com/ttacon/libphonenumber v1.2.1 // indirect
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
gitlab.com/elixxir/bloomfilter v0.0.0-20230315224936-a4459418f300 // indirect
gitlab.com/elixxir/comms v0.0.4-0.20230310205528-f06faa0d2f0b // indirect
gitlab.com/elixxir/ekv v0.2.1 // indirect
gitlab.com/elixxir/bloomfilter v0.0.0-20230322223210-fa84f6842de8 // indirect
gitlab.com/elixxir/comms v0.0.4-0.20230519211512-4a998f4b0938 // indirect
gitlab.com/elixxir/ekv v0.3.1-0.20230504190918-f5e96603c2e0 // indirect
gitlab.com/xx_network/comms v0.0.4-0.20230214180029-5387fb85736d // indirect
gitlab.com/xx_network/ring v0.0.3-0.20220902183151-a7d3b15bc981 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
......@@ -78,8 +72,6 @@ require (
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
google.golang.org/grpc v1.49.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/sqlite v1.4.4 // indirect
gorm.io/gorm v1.24.3 // indirect
......
This diff is collapsed.
......@@ -10,8 +10,9 @@
package main
import (
"crypto/ed25519"
"encoding/json"
"time"
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/v4/channels"
......@@ -24,7 +25,6 @@ import (
"gitlab.com/elixxir/xxdk-wasm/worker"
"gitlab.com/xx_network/crypto/csprng"
"gitlab.com/xx_network/primitives/id"
"time"
)
var zeroUUID = []byte{0, 0, 0, 0, 0, 0, 0, 0}
......@@ -32,24 +32,24 @@ 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
wtm *worker.ThreadManager
model channels.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(wChannels.NewWASMEventModelTag, m.newWASMEventModelCB)
m.mh.RegisterCallback(wChannels.JoinChannelTag, m.joinChannelCB)
m.mh.RegisterCallback(wChannels.LeaveChannelTag, m.leaveChannelCB)
m.mh.RegisterCallback(wChannels.ReceiveMessageTag, m.receiveMessageCB)
m.mh.RegisterCallback(wChannels.ReceiveReplyTag, m.receiveReplyCB)
m.mh.RegisterCallback(wChannels.ReceiveReactionTag, m.receiveReactionCB)
m.mh.RegisterCallback(wChannels.UpdateFromUUIDTag, m.updateFromUUIDCB)
m.mh.RegisterCallback(wChannels.UpdateFromMessageIDTag, m.updateFromMessageIDCB)
m.mh.RegisterCallback(wChannels.GetMessageTag, m.getMessageCB)
m.mh.RegisterCallback(wChannels.DeleteMessageTag, m.deleteMessageCB)
m.mh.RegisterCallback(wChannels.MuteUserTag, m.muteUserCB)
m.wtm.RegisterCallback(wChannels.NewWASMEventModelTag, m.newWASMEventModelCB)
m.wtm.RegisterCallback(wChannels.JoinChannelTag, m.joinChannelCB)
m.wtm.RegisterCallback(wChannels.LeaveChannelTag, m.leaveChannelCB)
m.wtm.RegisterCallback(wChannels.ReceiveMessageTag, m.receiveMessageCB)
m.wtm.RegisterCallback(wChannels.ReceiveReplyTag, m.receiveReplyCB)
m.wtm.RegisterCallback(wChannels.ReceiveReactionTag, m.receiveReactionCB)
m.wtm.RegisterCallback(wChannels.UpdateFromUUIDTag, m.updateFromUUIDCB)
m.wtm.RegisterCallback(wChannels.UpdateFromMessageIDTag, m.updateFromMessageIDCB)
m.wtm.RegisterCallback(wChannels.GetMessageTag, m.getMessageCB)
m.wtm.RegisterCallback(wChannels.DeleteMessageTag, m.deleteMessageCB)
m.wtm.RegisterCallback(wChannels.MuteUserTag, m.muteUserCB)
}
// newWASMEventModelCB is the callback for NewWASMEventModel. Returns an empty
......@@ -71,8 +71,7 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) {
"failed to JSON unmarshal Cipher from main thread: %+v", err)
}
m.model, err = NewWASMEventModel(msg.DatabaseName, encryption,
m.messageReceivedCallback, m.deletedMessageCallback, m.mutedUserCallback)
m.model, err = NewWASMEventModel(msg.DatabaseName, encryption, m)
if err != nil {
return []byte(err.Error()), nil
}
......@@ -80,47 +79,12 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) {
return []byte{}, nil
}
// messageReceivedCallback sends calls to the channels.MessageReceivedCallback
// in the main thread.
//
// storeEncryptionStatus adhere to the channels.MessageReceivedCallback type.
func (m *manager) messageReceivedCallback(
uuid uint64, channelID *id.ID, update bool) {
// Package parameters for sending
msg := &wChannels.MessageReceivedCallbackMessage{
UUID: uuid,
ChannelID: channelID,
Update: update,
}
data, err := json.Marshal(msg)
if err != nil {
jww.ERROR.Printf("Could not JSON marshal %T: %+v", msg, err)
return
}
// Send it to the main thread
m.mh.SendMessage(wChannels.MessageReceivedCallbackTag, data)
}
// deletedMessageCallback sends calls to the channels.DeletedMessageCallback in
// the main thread.
//
// storeEncryptionStatus adhere to the channels.MessageReceivedCallback type.
func (m *manager) deletedMessageCallback(messageID message.ID) {
m.mh.SendMessage(wChannels.DeletedMessageCallbackTag, messageID.Marshal())
}
// mutedUserCallback sends calls to the channels.MutedUserCallback in the main
// thread.
//
// storeEncryptionStatus adhere to the channels.MessageReceivedCallback type.
func (m *manager) mutedUserCallback(
channelID *id.ID, pubKey ed25519.PublicKey, unmute bool) {
// EventUpdate implements [bindings.ChannelUICallbacks.EventUpdate].
func (m *manager) EventUpdate(eventType int64, jsonData []byte) {
// Package parameters for sending
msg := &wChannels.MuteUserMessage{
ChannelID: channelID,
PubKey: pubKey,
Unmute: unmute,
msg := &wChannels.EventUpdateCallbackMessage{
EventType: eventType,
JsonData: jsonData,
}
data, err := json.Marshal(msg)
if err != nil {
......@@ -129,7 +93,7 @@ func (m *manager) mutedUserCallback(
}
// Send it to the main thread
m.mh.SendMessage(wChannels.MutedUserCallbackTag, data)
m.wtm.SendMessage(wChannels.EventUpdateCallbackTag, data)
}
// joinChannelCB is the callback for wasmModel.JoinChannel. Always returns nil;
......@@ -368,7 +332,12 @@ func (m *manager) muteUserCB(data []byte) ([]byte, error) {
"failed to JSON unmarshal %T from main thread: %+v", msg, err)
}
m.model.MuteUser(msg.ChannelID, msg.PubKey, msg.Unmute)
channelID := id.ID{}
err = channelID.UnmarshalJSON(msg.ChannelID)
if err != nil {
return nil, err
}
m.model.MuteUser(&channelID, msg.PubKey, msg.Unmute)
return nil, nil
}
......@@ -7,11 +7,15 @@
importScripts('wasm_exec.js');
const isReady = new Promise((resolve) => {
self.onWasmInitialized = resolve;
});
const go = new Go();
const binPath = 'xxdk-channelsIndexedDkWorker.wasm'
WebAssembly.instantiateStreaming(fetch(binPath), go.importObject).then((result) => {
WebAssembly.instantiateStreaming(fetch(binPath), go.importObject).then(async (result) => {
go.run(result.instance);
LogLevel(1);
await isReady;
}).catch((err) => {
console.error(err);
});
\ No newline at end of file
......@@ -15,8 +15,8 @@ import (
"gitlab.com/elixxir/client/v4/channels"
cft "gitlab.com/elixxir/client/v4/channelsFileTransfer"
"gitlab.com/elixxir/crypto/fileTransfer"
"gitlab.com/elixxir/wasm-utils/utils"
"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
"gitlab.com/elixxir/xxdk-wasm/utils"
"strings"
"time"
)
......
......@@ -21,28 +21,24 @@ import (
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/v4/bindings"
"gitlab.com/elixxir/client/v4/channels"
"gitlab.com/elixxir/client/v4/cmix/rounds"
cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
cryptoChannel "gitlab.com/elixxir/crypto/channel"
"gitlab.com/elixxir/crypto/message"
"gitlab.com/elixxir/wasm-utils/utils"
"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
wChannels "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/channels"
"gitlab.com/elixxir/xxdk-wasm/utils"
"gitlab.com/xx_network/primitives/id"
)
// wasmModel implements [channels.EventModel] interface, which uses the channels
// system passed an object that adheres to in order to get events on the
// channel.
// wasmModel implements [channels.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 {
db *idb.Database
cipher cryptoChannel.Cipher
receivedMessageCB wChannels.MessageReceivedCallback
deletedMessageCB wChannels.DeletedMessageCallback
mutedUserCB wChannels.MutedUserCallback
db *idb.Database
cipher cryptoChannel.Cipher
eventUpdate func(eventType int64, jsonMarshallable any)
}
// JoinChannel is called whenever a channel is joined locally.
......@@ -121,19 +117,19 @@ func (w *wasmModel) deleteMsgByChannel(channelID *id.ID) error {
"Unable to get Index: %+v", err)
}
// Perform the operation
// Set up the operation
keyRange, err := idb.NewKeyRangeOnly(impl.EncodeBytes(channelID.Marshal()))
cursorRequest, err := index.OpenCursorRange(keyRange, idb.CursorNext)
if err != nil {
return errors.WithMessagef(parentErr, "Unable to open Cursor: %+v", err)
}
ctx, cancel := impl.NewContext()
err = cursorRequest.Iter(ctx,
// Perform the operation
err = impl.SendCursorRequest(cursorRequest,
func(cursor *idb.CursorWithValue) error {
_, err := cursor.Delete()
return err
})
cancel()
if err != nil {
return errors.WithMessagef(parentErr,
"Unable to delete Message data: %+v", err)
......@@ -161,8 +157,10 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID message.ID,
}
}
channelIDBytes := channelID.Marshal()
msgToInsert := buildMessage(
channelID.Marshal(), messageID.Bytes(), nil, nickname,
channelIDBytes, messageID.Bytes(), nil, nickname,
textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
false, hidden, status)
......@@ -172,7 +170,11 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID message.ID,
return 0
}
go w.receivedMessageCB(uuid, channelID, false)
go w.eventUpdate(bindings.MessageReceived, bindings.MessageReceivedJson{
Uuid: int64(uuid),
ChannelID: channelID,
Update: false,
})
return uuid
}
......@@ -199,7 +201,9 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID,
}
}
msgToInsert := buildMessage(channelID.Marshal(), messageID.Bytes(),
channelIDBytes := channelID.Marshal()
msgToInsert := buildMessage(channelIDBytes, messageID.Bytes(),
replyTo.Bytes(), nickname, textBytes, pubKey, dmToken, codeset,
timestamp, lease, round.ID, mType, hidden, false, status)
......@@ -208,7 +212,12 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID,
jww.ERROR.Printf("Failed to receive reply: %+v", err)
return 0
}
go w.receivedMessageCB(uuid, channelID, false)
go w.eventUpdate(bindings.MessageReceived, bindings.MessageReceivedJson{
Uuid: int64(uuid),
ChannelID: channelID,
Update: false,
})
return uuid
}
......@@ -235,8 +244,9 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID,
}
}
channelIDBytes := channelID.Marshal()
msgToInsert := buildMessage(
channelID.Marshal(), messageID.Bytes(), reactionTo.Bytes(), nickname,
channelIDBytes, messageID.Bytes(), reactionTo.Bytes(), nickname,
textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
false, hidden, status)
......@@ -245,7 +255,12 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID,
jww.ERROR.Printf("Failed to receive reaction: %+v", err)
return 0
}
go w.receivedMessageCB(uuid, channelID, false)
go w.eventUpdate(bindings.MessageReceived, bindings.MessageReceivedJson{
Uuid: int64(uuid),
ChannelID: channelID,
Update: false,
})
return uuid
}
......@@ -392,9 +407,17 @@ func (w *wasmModel) updateMessage(currentMsg *Message, messageID *message.ID,
if err != nil {
return 0, err
}
channelID := &id.ID{}
copy(channelID[:], currentMsg.ChannelID)
go w.receivedMessageCB(uuid, channelID, true)
channelID, err := id.Unmarshal(currentMsg.ChannelID)
if err != nil {
return 0, err
}
go w.eventUpdate(bindings.MessageReceived, bindings.MessageReceivedJson{
Uuid: int64(uuid),
ChannelID: channelID,
Update: true,
})
return uuid, nil
}
......@@ -415,7 +438,8 @@ func (w *wasmModel) upsertMessage(msg *Message) (uint64, error) {
// Store message to database
msgIdObj, err := impl.Put(w.db, messageStoreName, messageObj)
if err != nil {
return 0, errors.Errorf("Unable to put Message: %+v", err)
return 0, errors.Errorf("Unable to put Message: %+v\n%s",
err, newMessageJson)
}
uuid := msgIdObj.Int()
......@@ -491,7 +515,8 @@ func (w *wasmModel) DeleteMessage(messageID message.ID) error {
return err
}
go w.deletedMessageCB(messageID)
go w.eventUpdate(bindings.MessageDeleted,
bindings.MessageDeletedJson{MessageID: messageID})
return nil
}
......@@ -499,7 +524,12 @@ func (w *wasmModel) DeleteMessage(messageID message.ID) error {
// MuteUser is called whenever a user is muted or unmuted.
func (w *wasmModel) MuteUser(
channelID *id.ID, pubKey ed25519.PublicKey, unmute bool) {
go w.mutedUserCB(channelID, pubKey, unmute)
go w.eventUpdate(bindings.UserMuted, bindings.UserMutedJson{
ChannelID: channelID,
PubKey: pubKey,
Unmute: unmute,
})
}
// valueToMessage is a helper for converting js.Value to Message.
......
......@@ -11,12 +11,9 @@ package main
import (
"bytes"
"crypto/ed25519"
"encoding/json"
"errors"
"fmt"
cft "gitlab.com/elixxir/client/v4/channelsFileTransfer"
"gitlab.com/elixxir/crypto/fileTransfer"
"os"
"strconv"
"testing"
......@@ -24,14 +21,17 @@ import (
"github.com/hack-pad/go-indexeddb/idb"
jww "github.com/spf13/jwalterweatherman"
"github.com/stretchr/testify/require"
"gitlab.com/elixxir/client/v4/channels"
cft "gitlab.com/elixxir/client/v4/channelsFileTransfer"
"gitlab.com/elixxir/client/v4/cmix/rounds"
cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
cryptoChannel "gitlab.com/elixxir/crypto/channel"
"gitlab.com/elixxir/crypto/fileTransfer"
"gitlab.com/elixxir/crypto/message"
"gitlab.com/elixxir/wasm-utils/storage"
"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
"gitlab.com/elixxir/xxdk-wasm/storage"
"gitlab.com/xx_network/crypto/csprng"
"gitlab.com/xx_network/primitives/id"
"gitlab.com/xx_network/primitives/netTime"
......@@ -42,15 +42,14 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func dummyReceivedMessageCB(uint64, *id.ID, bool) {}
func dummyDeletedMessageCB(message.ID) {}
func dummyMutedUserCB(*id.ID, ed25519.PublicKey, bool) {}
type dummyCbs struct{}
func (c *dummyCbs) EventUpdate(int64, []byte) {}
// Happy path test for receiving, updating, getting, and deleting a File.
func TestWasmModel_ReceiveFile(t *testing.T) {
testString := "TestWasmModel_ReceiveFile"
m, err := newWASMModel(testString, nil,
dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB)
m, err := newWASMModel(testString, nil, &dummyCbs{})
if err != nil {
t.Fatal(err)
}
......@@ -137,7 +136,7 @@ func TestWasmModel_GetMessage(t *testing.T) {
testMsgId := message.DeriveChannelMessageID(&id.ID{1}, 0, []byte(testString))
eventModel, err := newWASMModel(testString, c,
dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB)
&dummyCbs{})
if err != nil {
t.Fatal(err)
}
......@@ -167,8 +166,7 @@ func TestWasmModel_DeleteMessage(t *testing.T) {
storage.GetLocalStorage().Clear()
testString := "TestWasmModel_DeleteMessage"
testMsgId := message.DeriveChannelMessageID(&id.ID{1}, 0, []byte(testString))
eventModel, err := newWASMModel(testString, nil, dummyReceivedMessageCB,
dummyDeletedMessageCB, dummyMutedUserCB)
eventModel, err := newWASMModel(testString, nil, &dummyCbs{})
if err != nil {
t.Fatal(err)
}
......@@ -225,13 +223,17 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
testMsgId := message.DeriveChannelMessageID(
&id.ID{1}, 0, []byte(testString))
eventModel, err2 := newWASMModel(testString, c,
dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB)
&dummyCbs{})
if err2 != nil {
t.Fatal(err)
}
cid, err := id.NewRandomID(csprng.NewSystemRNG(),
id.DummyUser.GetType())
require.NoError(t, err)
// Store a test message
testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
testMsg := buildMessage(cid.Bytes(), testMsgId.Bytes(), nil,
testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0,
netTime.Now(), time.Second, 0, 0, false, false, channels.Sent)
uuid, err2 := eventModel.upsertMessage(testMsg)
......@@ -292,8 +294,7 @@ func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) {
}
t.Run("Test_wasmModel_JoinChannel_LeaveChannel"+cs, func(t *testing.T) {
storage.GetLocalStorage().Clear()
eventModel, err2 := newWASMModel("test", c, dummyReceivedMessageCB,
dummyDeletedMessageCB, dummyMutedUserCB)
eventModel, err2 := newWASMModel("test", c, &dummyCbs{})
if err2 != nil {
t.Fatal(err2)
}
......@@ -347,7 +348,7 @@ func Test_wasmModel_UUIDTest(t *testing.T) {
storage.GetLocalStorage().Clear()
testString := "testHello" + cs
eventModel, err2 := newWASMModel(testString, c,
dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB)
&dummyCbs{})
if err2 != nil {
t.Fatal(err2)
}
......@@ -394,7 +395,7 @@ func Test_wasmModel_DuplicateReceives(t *testing.T) {
t.Run(testString, func(t *testing.T) {
storage.GetLocalStorage().Clear()
eventModel, err := newWASMModel(testString, c,
dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB)
&dummyCbs{})
if err != nil {
t.Fatal(err)
}
......@@ -443,7 +444,7 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) {
totalMessages := 10
expectedMessages := 5
eventModel, err := newWASMModel(testString, c,
dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB)
&dummyCbs{})
if err != nil {
t.Fatal(err)
}
......@@ -514,7 +515,7 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
storage.GetLocalStorage().Clear()
testString := fmt.Sprintf("test_receiveHelper_UniqueIndex_%d", i)
eventModel, err := newWASMModel(testString, c,
dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB)
&dummyCbs{})
if err != nil {
t.Fatal(err)
}
......
......@@ -10,49 +10,46 @@
package main
import (
"encoding/json"
"syscall/js"
"github.com/hack-pad/go-indexeddb/idb"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/v4/bindings"
"gitlab.com/elixxir/client/v4/channels"
cryptoChannel "gitlab.com/elixxir/crypto/channel"
"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
wChannels "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/channels"
)
// currentVersion is the current version of the IndexedDb runtime. Used for
// migration purposes.
const currentVersion uint = 2
const currentVersion uint = 1
// NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
// The name should be a base64 encoding of the users public key. Returns the
// EventModel based on IndexedDb and the database name as reported by IndexedDb.
func NewWASMEventModel(databaseName string, encryption cryptoChannel.Cipher,
messageReceivedCB wChannels.MessageReceivedCallback,
deletedMessageCB wChannels.DeletedMessageCallback,
mutedUserCB wChannels.MutedUserCallback) (channels.EventModel, error) {
return newWASMModel(databaseName, encryption, messageReceivedCB,
deletedMessageCB, mutedUserCB)
uiCallbacks bindings.ChannelUICallbacks) (channels.EventModel, error) {
return newWASMModel(databaseName, encryption, uiCallbacks)
}
// newWASMModel creates the given [idb.Database] and returns a wasmModel.
func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
messageReceivedCB wChannels.MessageReceivedCallback,
deletedMessageCB wChannels.DeletedMessageCallback,
mutedUserCB wChannels.MutedUserCallback) (*wasmModel, error) {
uiCallbacks bindings.ChannelUICallbacks) (*wasmModel, error) {
// Attempt to open database object
ctx, cancel := impl.NewContext()
defer cancel()
openRequest, err := idb.Global().Open(ctx, databaseName, currentVersion,
func(db *idb.Database, oldVersion, newVersion uint) error {
if oldVersion == newVersion {
jww.INFO.Printf("IndexDb version is current: v%d", newVersion)
jww.INFO.Printf("IndexDb version for %s is current: v%d",
databaseName, newVersion)
return nil
}
jww.INFO.Printf("IndexDb upgrade required: v%d -> v%d",
oldVersion, newVersion)
jww.INFO.Printf("IndexDb upgrade required for %s: v%d -> v%d",
databaseName, oldVersion, newVersion)
if oldVersion == 0 && newVersion >= 1 {
err := v1Upgrade(db)
......@@ -62,14 +59,6 @@ func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
oldVersion = 1
}
if oldVersion == 1 && newVersion >= 2 {
err := v2Upgrade(db)
if err != nil {
return err
}
oldVersion = 2
}
// if oldVersion == 1 && newVersion >= 2 { v2Upgrade(), oldVersion = 2 }
return nil
})
......@@ -81,16 +70,22 @@ func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
db, err := openRequest.Await(ctx)
if err != nil {
return nil, err
} else if ctx.Err() != nil {
return nil, ctx.Err()
}
wrapper := &wasmModel{
db: db,
cipher: encryption,
receivedMessageCB: messageReceivedCB,
deletedMessageCB: deletedMessageCB,
mutedUserCB: mutedUserCB,
db: db,
cipher: encryption,
eventUpdate: func(eventType int64, jsonMarshallable any) {
data, err := json.Marshal(jsonMarshallable)
if err != nil {
jww.FATAL.Panicf("Failed to JSON marshal %T for EventUpdate "+
"callback: %+v", jsonMarshallable, err)
}
uiCallbacks.EventUpdate(eventType, data)
},
}
return wrapper, nil
}
......@@ -149,15 +144,8 @@ func v1Upgrade(db *idb.Database) error {
return err
}
return nil
}
// v1Upgrade performs the v1 -> v2 database upgrade.
//
// This can never be changed without permanently breaking backwards
// compatibility.
func v2Upgrade(db *idb.Database) error {
_, err := db.CreateObjectStore(fileStoreName, idb.ObjectStoreOptions{
// Build File ObjectStore
_, err = db.CreateObjectStore(fileStoreName, idb.ObjectStoreOptions{
KeyPath: js.ValueOf(pkeyName),
AutoIncrement: false,
})
......
......@@ -11,32 +11,70 @@ package main
import (
"fmt"
"os"
"syscall/js"
"github.com/spf13/cobra"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/xxdk-wasm/logging"
"gitlab.com/elixxir/xxdk-wasm/wasm"
"gitlab.com/elixxir/xxdk-wasm/worker"
"syscall/js"
)
// SEMVER is the current semantic version of the xxDK channels web worker.
const SEMVER = "0.1.0"
func init() {
// Set up Javascript console listener set at level INFO
ll := logging.NewJsConsoleLogListener(jww.LevelInfo)
logging.AddLogListener(ll.Listen)
jww.SetStdoutThreshold(jww.LevelFatal + 1)
jww.INFO.Printf("xxDK channels web worker version: v%s", SEMVER)
func main() {
// Set to os.Args because the default is os.Args[1:] and in WASM, args start
// at 0, not 1.
channelsCmd.SetArgs(os.Args)
err := channelsCmd.Execute()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
jww.INFO.Print("[WW] Starting xxDK WebAssembly Channels Database Worker.")
var channelsCmd = &cobra.Command{
Use: "channelsIndexedDbWorker",
Short: "IndexedDb database for channels.",
Example: "const go = new Go();\ngo.argv = [\"--logLevel=1\"]",
Run: func(cmd *cobra.Command, args []string) {
// Start logger first to capture all logging events
err := logging.EnableLogging(logLevel, -1, 0, "", "")
if err != nil {
fmt.Printf("Failed to intialize logging: %+v", err)
os.Exit(1)
}
js.Global().Set("LogLevel", js.FuncOf(wasm.LogLevel))
jww.INFO.Printf("xxDK channels web worker version: v%s", SEMVER)
m := &manager{mh: worker.NewThreadManager("ChannelsIndexedDbWorker", true)}
m.registerCallbacks()
m.mh.SignalReady()
<-make(chan bool)
fmt.Println("[WW] Closing xxDK WebAssembly Channels Database Worker.")
jww.INFO.Print("[WW] Starting xxDK WebAssembly Channels Database Worker.")
m := &manager{
wtm: worker.NewThreadManager("ChannelsIndexedDbWorker", true),
}
m.registerCallbacks()
m.wtm.SignalReady()
// Indicate to the Javascript caller that the WASM is ready by resolving
// a promise created by the caller.
js.Global().Get("onWasmInitialized").Invoke()
<-make(chan bool)
fmt.Println("[WW] Closing xxDK WebAssembly Channels Database Worker.")
os.Exit(0)
},
}
var (
logLevel jww.Threshold
)
func init() {
// Initialize all startup flags
channelsCmd.Flags().IntVarP((*int)(&logLevel), "logLevel", "l", 2,
"Sets the log level output when outputting to the Javascript console. "+
"0 = TRACE, 1 = DEBUG, 2 = INFO, 3 = WARN, 4 = ERROR, "+
"5 = CRITICAL, 6 = FATAL, -1 = disabled.")
}
......@@ -29,24 +29,24 @@ 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
wtm *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)
m.wtm.RegisterCallback(wDm.NewWASMEventModelTag, m.newWASMEventModelCB)
m.wtm.RegisterCallback(wDm.ReceiveTag, m.receiveCB)
m.wtm.RegisterCallback(wDm.ReceiveTextTag, m.receiveTextCB)
m.wtm.RegisterCallback(wDm.ReceiveReplyTag, m.receiveReplyCB)
m.wtm.RegisterCallback(wDm.ReceiveReactionTag, m.receiveReactionCB)
m.wtm.RegisterCallback(wDm.UpdateSentStatusTag, m.updateSentStatusCB)
m.wtm.RegisterCallback(wDm.BlockSenderTag, m.blockSenderCB)
m.wtm.RegisterCallback(wDm.UnblockSenderTag, m.unblockSenderCB)
m.wtm.RegisterCallback(wDm.GetConversationTag, m.getConversationCB)
m.wtm.RegisterCallback(wDm.GetConversationsTag, m.getConversationsCB)
}
// newWASMEventModelCB is the callback for NewWASMEventModel. Returns an empty
......@@ -98,7 +98,7 @@ func (m *manager) messageReceivedCallback(uuid uint64, pubKey ed25519.PublicKey,
}
// Send it to the main thread
m.mh.SendMessage(wDm.MessageReceivedCallbackTag, data)
m.wtm.SendMessage(wDm.MessageReceivedCallbackTag, data)
}
// receiveCB is the callback for wasmModel.Receive. Returns a UUID of 0 on error
......
......@@ -7,11 +7,15 @@
importScripts('wasm_exec.js');
const isReady = new Promise((resolve) => {
self.onWasmInitialized = resolve;
});
const go = new Go();
const binPath = 'xxdk-dmIndexedDkWorker.wasm'
WebAssembly.instantiateStreaming(fetch(binPath), go.importObject).then((result) => {
WebAssembly.instantiateStreaming(fetch(binPath), go.importObject).then(async (result) => {
go.run(result.instance);
LogLevel(1);
await isReady;
}).catch((err) => {
console.error(err);
});
\ No newline at end of file
......@@ -13,6 +13,7 @@ import (
"bytes"
"crypto/ed25519"
"encoding/json"
"gitlab.com/xx_network/primitives/netTime"
"strings"
"syscall/js"
"time"
......@@ -25,8 +26,8 @@ import (
"gitlab.com/elixxir/client/v4/dm"
cryptoChannel "gitlab.com/elixxir/crypto/channel"
"gitlab.com/elixxir/crypto/message"
"gitlab.com/elixxir/wasm-utils/utils"
"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
"gitlab.com/elixxir/xxdk-wasm/utils"
"gitlab.com/xx_network/primitives/id"
)
......@@ -42,16 +43,16 @@ type wasmModel struct {
// upsertConversation is used for joining or updating a Conversation.
func (w *wasmModel) upsertConversation(nickname string,
pubKey ed25519.PublicKey, partnerToken uint32, codeset uint8,
blocked bool) error {
blockedTimestamp *time.Time) error {
parentErr := errors.New("[DM indexedDB] failed to upsertConversation")
// Build object
newConvo := Conversation{
Pubkey: pubKey,
Nickname: nickname,
Token: partnerToken,
CodesetVersion: codeset,
Blocked: blocked,
Pubkey: pubKey,
Nickname: nickname,
Token: partnerToken,
CodesetVersion: codeset,
BlockedTimestamp: blockedTimestamp,
}
// Convert to jsObject
......@@ -231,11 +232,11 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n
"[DM indexedDB] Joining conversation with %s", nickname)
convoToUpdate = &Conversation{
Pubkey: partnerKey,
Nickname: nickname,
Token: partnerToken,
CodesetVersion: codeset,
Blocked: false,
Pubkey: partnerKey,
Nickname: nickname,
Token: partnerToken,
CodesetVersion: codeset,
BlockedTimestamp: nil,
}
}
} else {
......@@ -268,7 +269,7 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n
conversationUpdated := convoToUpdate != nil
if conversationUpdated {
err = w.upsertConversation(convoToUpdate.Nickname, convoToUpdate.Pubkey,
convoToUpdate.Token, convoToUpdate.CodesetVersion, convoToUpdate.Blocked)
convoToUpdate.Token, convoToUpdate.CodesetVersion, convoToUpdate.BlockedTimestamp)
if err != nil {
return 0, err
}
......@@ -318,7 +319,8 @@ func (w *wasmModel) upsertMessage(msg *Message) (uint64, error) {
// Store message to database
msgIdObj, err := impl.Put(w.db, messageStoreName, messageObj)
if err != nil {
return 0, errors.Errorf("Unable to put Message: %+v", err)
return 0, errors.Errorf("Unable to put Message: %+v\n%s",
err, newMessageJson)
}
uuid := msgIdObj.Int()
......@@ -348,14 +350,20 @@ func (w *wasmModel) UnblockSender(senderPubKey ed25519.PublicKey) {
// setBlocked is a helper for blocking/unblocking a given Conversation.
func (w *wasmModel) setBlocked(senderPubKey ed25519.PublicKey, isBlocked bool) error {
// Get current Conversation and set blocked
// Get current Conversation and set blocked accordingly
resultConvo, err := w.getConversation(senderPubKey)
if err != nil {
return err
}
var timeBlocked *time.Time
if isBlocked {
blockUser := netTime.Now()
timeBlocked = &blockUser
}
return w.upsertConversation(resultConvo.Nickname, resultConvo.Pubkey,
resultConvo.Token, resultConvo.CodesetVersion, isBlocked)
resultConvo.Token, resultConvo.CodesetVersion, timeBlocked)
}
// GetConversation returns the conversation held by the model (receiver).
......@@ -368,11 +376,11 @@ func (w *wasmModel) GetConversation(senderPubKey ed25519.PublicKey) *dm.ModelCon
}
return &dm.ModelConversation{
Pubkey: resultConvo.Pubkey,
Nickname: resultConvo.Nickname,
Token: resultConvo.Token,
CodesetVersion: resultConvo.CodesetVersion,
Blocked: resultConvo.Blocked,
Pubkey: resultConvo.Pubkey,
Nickname: resultConvo.Nickname,
Token: resultConvo.Token,
CodesetVersion: resultConvo.CodesetVersion,
BlockedTimestamp: resultConvo.BlockedTimestamp,
}
}
......@@ -410,11 +418,11 @@ func (w *wasmModel) GetConversations() []dm.ModelConversation {
return nil
}
conversations[i] = dm.ModelConversation{
Pubkey: resultConvo.Pubkey,
Nickname: resultConvo.Nickname,
Token: resultConvo.Token,
CodesetVersion: resultConvo.CodesetVersion,
Blocked: resultConvo.Blocked,
Pubkey: resultConvo.Pubkey,
Nickname: resultConvo.Nickname,
Token: resultConvo.Token,
CodesetVersion: resultConvo.CodesetVersion,
BlockedTimestamp: resultConvo.BlockedTimestamp,
}
}
return conversations
......
......@@ -17,8 +17,8 @@ import (
"gitlab.com/elixxir/client/v4/cmix/rounds"
"gitlab.com/elixxir/client/v4/dm"
"gitlab.com/elixxir/crypto/message"
"gitlab.com/elixxir/wasm-utils/utils"
"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
"gitlab.com/elixxir/xxdk-wasm/utils"
"gitlab.com/xx_network/primitives/id"
"os"
"syscall/js"
......@@ -102,7 +102,7 @@ func TestImpl_GetConversations(t *testing.T) {
testBytes := []byte(fmt.Sprintf("%d", i))
testPubKey := ed25519.PublicKey(testBytes)
err = m.upsertConversation("test", testPubKey,
uint32(i), uint8(i), false)
uint32(i), uint8(i), nil)
if err != nil {
t.Fatal(err.Error())
}
......@@ -133,28 +133,28 @@ func TestWasmModel_BlockSender(t *testing.T) {
// Insert a test convo
testPubKey := ed25519.PublicKey{}
err = m.upsertConversation("test", testPubKey, 0, 0, false)
err = m.upsertConversation("test", testPubKey, 0, 0, nil)
if err != nil {
t.Fatal(err.Error())
}
// Default to unblocked
result := m.GetConversation(testPubKey)
if result.Blocked {
if result.BlockedTimestamp != nil {
t.Fatal("Expected blocked to be false")
}
// Now toggle blocked
m.BlockSender(testPubKey)
result = m.GetConversation(testPubKey)
if !result.Blocked {
if result.BlockedTimestamp == nil {
t.Fatal("Expected blocked to be true")
}
// Now toggle blocked again
m.UnblockSender(testPubKey)
result = m.GetConversation(testPubKey)
if result.Blocked {
if result.BlockedTimestamp != nil {
t.Fatal("Expected blocked to be false")
}
}
......@@ -49,12 +49,13 @@ func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
openRequest, err := idb.Global().Open(ctx, databaseName, currentVersion,
func(db *idb.Database, oldVersion, newVersion uint) error {
if oldVersion == newVersion {
jww.INFO.Printf("IndexDb version is current: v%d", newVersion)
jww.INFO.Printf("IndexDb version for %s is current: v%d",
databaseName, newVersion)
return nil
}
jww.INFO.Printf("IndexDb upgrade required: v%d -> v%d",
oldVersion, newVersion)
jww.INFO.Printf("IndexDb upgrade required for %s: v%d -> v%d",
databaseName, oldVersion, newVersion)
if oldVersion == 0 && newVersion >= 1 {
err := v1Upgrade(db)
......@@ -75,10 +76,11 @@ func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
db, err := openRequest.Await(ctx)
if err != nil {
return nil, err
} else if ctx.Err() != nil {
return nil, ctx.Err()
}
wrapper := &wasmModel{db: db, receivedMessageCB: cb, cipher: encryption}
return wrapper, nil
}
......
......@@ -11,32 +11,71 @@ package main
import (
"fmt"
"os"
"syscall/js"
"github.com/spf13/cobra"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/xxdk-wasm/logging"
"gitlab.com/elixxir/xxdk-wasm/wasm"
"gitlab.com/elixxir/xxdk-wasm/worker"
"syscall/js"
)
// SEMVER is the current semantic version of the xxDK DM web worker.
const SEMVER = "0.1.0"
func init() {
// Set up Javascript console listener set at level INFO
ll := logging.NewJsConsoleLogListener(jww.LevelInfo)
logging.AddLogListener(ll.Listen)
jww.SetStdoutThreshold(jww.LevelFatal + 1)
jww.INFO.Printf("xxDK DM web worker version: v%s", SEMVER)
func main() {
// Set to os.Args because the default is os.Args[1:] and in WASM, args start
// at 0, not 1.
dmCmd.SetArgs(os.Args)
err := dmCmd.Execute()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
jww.INFO.Print("[WW] Starting xxDK WebAssembly DM Database Worker.")
var dmCmd = &cobra.Command{
Use: "dmIndexedDbWorker",
Short: "IndexedDb database for DMs.",
Example: "const go = new Go();\ngo.argv = [\"--logLevel=1\"]",
Run: func(cmd *cobra.Command, args []string) {
// Start logger first to capture all logging events
err := logging.EnableLogging(logLevel, -1, 0, "", "")
if err != nil {
fmt.Printf(
"Failed to intialize logging in DM indexedDb worker: %+v", err)
os.Exit(1)
}
js.Global().Set("LogLevel", js.FuncOf(wasm.LogLevel))
jww.INFO.Printf("xxDK DM web worker version: v%s", SEMVER)
m := &manager{mh: worker.NewThreadManager("DmIndexedDbWorker", true)}
m.registerCallbacks()
m.mh.SignalReady()
<-make(chan bool)
fmt.Println("[WW] Closing xxDK WebAssembly Channels Database Worker.")
jww.INFO.Print("[WW] Starting xxDK WebAssembly DM Database Worker.")
m := &manager{
wtm: worker.NewThreadManager("DmIndexedDbWorker", true),
}
m.registerCallbacks()
m.wtm.SignalReady()
// Indicate to the Javascript caller that the WASM is ready by resolving
// a promise created by the caller.
js.Global().Get("onWasmInitialized").Invoke()
<-make(chan bool)
fmt.Println("[WW] Closing xxDK WebAssembly Channels Database Worker.")
os.Exit(0)
},
}
var (
logLevel jww.Threshold
)
func init() {
// Initialize all startup flags
dmCmd.Flags().IntVarP((*int)(&logLevel), "logLevel", "l", 2,
"Sets the log level output when outputting to the Javascript console. "+
"0 = TRACE, 1 = DEBUG, 2 = INFO, 3 = WARN, 4 = ERROR, "+
"5 = CRITICAL, 6 = FATAL, -1 = disabled.")
}
......@@ -55,9 +55,9 @@ type Message struct {
// message exchange between two recipients.
// A Conversation has many Message.
type Conversation struct {
Pubkey []byte `json:"pub_key"` // Matches convoPkeyName
Nickname string `json:"nickname"`
Token uint32 `json:"token"`
CodesetVersion uint8 `json:"codeset_version"`
Blocked bool `json:"blocked"`
Pubkey []byte `json:"pub_key"` // Matches convoPkeyName
Nickname string `json:"nickname"`
Token uint32 `json:"token"`
CodesetVersion uint8 `json:"codeset_version"`
BlockedTimestamp *time.Time `json:"blocked_timestamp"`
}
////////////////////////////////////////////////////////////////////////////////
// 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 (
"encoding/json"
"github.com/pkg/errors"
"gitlab.com/elixxir/client/v4/storage/utility"
stateWorker "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/state"
"gitlab.com/elixxir/xxdk-wasm/worker"
)
// manager handles the message callbacks, which is used to
// send information between the model and the main thread.
type manager struct {
wtm *worker.ThreadManager
model utility.WebState
}
// registerCallbacks registers all the reception callbacks to manage messages
// from the main thread.
func (m *manager) registerCallbacks() {
m.wtm.RegisterCallback(stateWorker.NewStateTag, m.newStateCB)
m.wtm.RegisterCallback(stateWorker.SetTag, m.setCB)
m.wtm.RegisterCallback(stateWorker.GetTag, m.getCB)
}
// newStateCB is the callback for NewState. Returns an empty
// slice on success or an error message on failure.
func (m *manager) newStateCB(data []byte) ([]byte, error) {
var msg stateWorker.NewStateMessage
err := json.Unmarshal(data, &msg)
if err != nil {
return []byte{}, errors.Errorf(
"failed to JSON unmarshal %T from main thread: %+v", msg, err)
}
m.model, err = NewState(msg.DatabaseName)
if err != nil {
return []byte(err.Error()), nil
}
return []byte{}, nil
}
// setCB is the callback for stateModel.Set.
// Returns nil on error or the resulting byte data on success.
func (m *manager) setCB(data []byte) ([]byte, error) {
var msg stateWorker.TransferMessage
err := json.Unmarshal(data, &msg)
if err != nil {
return nil, errors.Errorf(
"failed to JSON unmarshal %T from main thread: %+v", msg, err)
}
return nil, m.model.Set(msg.Key, msg.Value)
}
// getCB is the callback for stateModel.Get.
// Returns nil on error or the resulting byte data on success.
func (m *manager) getCB(data []byte) ([]byte, error) {
key := string(data)
result, err := m.model.Get(key)
msg := stateWorker.TransferMessage{
Key: key,
Value: result,
Error: err.Error(),
}
return json.Marshal(msg)
}