From 09108b2f83b6b3d8af5425ab43ca902bf92081b7 Mon Sep 17 00:00:00 2001
From: Jono Wenger <jono@elixxir.io>
Date: Mon, 17 Oct 2022 22:32:38 +0000
Subject: [PATCH] XX-4265 / Construct identity

---
 go.mod                           |  4 +-
 go.sum                           |  7 +--
 indexedDb/implementation.go      | 39 +++++++-------
 indexedDb/implementation_test.go | 43 +++++----------
 indexedDb/model.go               |  9 +---
 wasm/channels.go                 | 89 +++++++++++++++++++++++++++-----
 wasm/channels_test.go            | 62 ++++++++++++++++++++++
 7 files changed, 176 insertions(+), 77 deletions(-)

diff --git a/go.mod b/go.mod
index e054815c..bf5e93f4 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 v1.5.1-0.20221017175256-aedcb7c137b7
-	gitlab.com/elixxir/crypto v0.0.7-0.20221017173452-565da4101a3b
+	gitlab.com/elixxir/client v1.5.1-0.20221017222923-185226c4412d
+	gitlab.com/elixxir/crypto v0.0.7-0.20221017204335-9201b3672f3a
 	gitlab.com/elixxir/primitives v0.0.3-0.20221017172918-6176818d1aba
 	gitlab.com/xx_network/crypto v0.0.5-0.20221017172404-b384a8d8b171
 	gitlab.com/xx_network/primitives v0.0.4-0.20221017171439-42169a3e5c0d
diff --git a/go.sum b/go.sum
index ec4e5411..1c0830cd 100644
--- a/go.sum
+++ b/go.sum
@@ -616,14 +616,15 @@ 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-20211222005329-7d931ceead6f h1:yXGvNBqzZwAhDYlSnxPRbgor6JWoOt1Z7s3z1O9JR40=
 gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
-gitlab.com/elixxir/client v1.5.1-0.20221017175256-aedcb7c137b7 h1:NLsGSE/zZwXlMBxzIT7enoj9TRm6jkdfD+2MtsopwSM=
-gitlab.com/elixxir/client v1.5.1-0.20221017175256-aedcb7c137b7/go.mod h1:C/BcaLL/hDtub1wj10B8U6Ln6N5S/Q83/zOrE6VIMpE=
+gitlab.com/elixxir/client v1.5.1-0.20221017222923-185226c4412d h1:c6hhamrVh5xAG4n/mc4LuPXkPuREd1Ql181wL16i+bI=
+gitlab.com/elixxir/client v1.5.1-0.20221017222923-185226c4412d/go.mod h1:/j/GbuxAVfR5cqLqYAq5s8IgafpyHVO63efwh/Xob4w=
 gitlab.com/elixxir/comms v0.0.4-0.20221017173926-4eaa6061dfaa h1:/FEpu0N0rAyq74FkvO3uY8BcQoWLSbVPhj/s5QfscZw=
 gitlab.com/elixxir/comms v0.0.4-0.20221017173926-4eaa6061dfaa/go.mod h1:rW7xdbHntP2MoF3q+2+f+IR8OHol94MRyviotfR5rXg=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
-gitlab.com/elixxir/crypto v0.0.7-0.20221017173452-565da4101a3b h1:5VBuenFNKOq0omDYmUet3Deu3pAkzNIkveQBtNO/o0k=
 gitlab.com/elixxir/crypto v0.0.7-0.20221017173452-565da4101a3b/go.mod h1:1rftbwSVdy49LkBIkPr+w+P2mDOerYeBKoZuB3r0yqI=
+gitlab.com/elixxir/crypto v0.0.7-0.20221017204335-9201b3672f3a h1:RxobrpD5owwdyNg5KTqBINJ8z0zsXsbu+UhMEC80wIE=
+gitlab.com/elixxir/crypto v0.0.7-0.20221017204335-9201b3672f3a/go.mod h1:1rftbwSVdy49LkBIkPr+w+P2mDOerYeBKoZuB3r0yqI=
 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.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
diff --git a/indexedDb/implementation.go b/indexedDb/implementation.go
index f546a822..b7ac9e54 100644
--- a/indexedDb/implementation.go
+++ b/indexedDb/implementation.go
@@ -11,6 +11,7 @@ package indexedDb
 
 import (
 	"context"
+	"crypto/ed25519"
 	"encoding/base64"
 	"encoding/json"
 	"sync"
@@ -201,12 +202,13 @@ func (w *wasmModel) deleteMsgByChannel(channelID *id.ID) error {
 // user of the API to filter such called by message ID.
 func (w *wasmModel) ReceiveMessage(channelID *id.ID,
 	messageID cryptoChannel.MessageID, nickname, text string,
-	identity cryptoChannel.Identity, timestamp time.Time, lease time.Duration,
-	round rounds.Round, mType channels.MessageType,
-	status channels.SentStatus) uint64 {
+	pubKey ed25519.PublicKey, codeset uint8,
+	timestamp time.Time, lease time.Duration, round rounds.Round,
+	mType channels.MessageType, status channels.SentStatus) uint64 {
 
-	msgToInsert := buildMessage(channelID.Marshal(), messageID.Bytes(), nil,
-		nickname, text, identity, timestamp, lease, round.ID, mType, status)
+	msgToInsert := buildMessage(
+		channelID.Marshal(), messageID.Bytes(), nil, nickname, text, pubKey,
+		codeset, timestamp, lease, round.ID, mType, status)
 
 	uuid, err := w.receiveHelper(msgToInsert)
 	if err != nil {
@@ -225,12 +227,12 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID,
 // the initial message. As a result, it may be important to buffer replies.
 func (w *wasmModel) ReceiveReply(channelID *id.ID,
 	messageID cryptoChannel.MessageID, replyTo cryptoChannel.MessageID,
-	nickname, text string, identity cryptoChannel.Identity, timestamp time.Time,
+	nickname, text string, pubKey ed25519.PublicKey, codeset uint8, timestamp time.Time,
 	lease time.Duration, round rounds.Round, mType channels.MessageType,
 	status channels.SentStatus) uint64 {
 
 	msgToInsert := buildMessage(channelID.Marshal(), messageID.Bytes(),
-		replyTo.Bytes(), nickname, text, identity, timestamp, lease, round.ID,
+		replyTo.Bytes(), nickname, text, pubKey, codeset, timestamp, lease, round.ID,
 		mType, status)
 
 	uuid, err := w.receiveHelper(msgToInsert)
@@ -250,13 +252,13 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID,
 // the initial message. As a result, it may be important to buffer reactions.
 func (w *wasmModel) ReceiveReaction(channelID *id.ID,
 	messageID cryptoChannel.MessageID, reactionTo cryptoChannel.MessageID,
-	nickname, reaction string, identity cryptoChannel.Identity,
+	nickname, reaction string, pubKey ed25519.PublicKey, codeset uint8,
 	timestamp time.Time, lease time.Duration, round rounds.Round,
 	mType channels.MessageType, status channels.SentStatus) uint64 {
 
-	msgToInsert := buildMessage(channelID.Marshal(), messageID.Bytes(),
-		reactionTo.Bytes(), nickname, reaction, identity, timestamp, lease,
-		round.ID, mType, status)
+	msgToInsert := buildMessage(
+		channelID.Marshal(), messageID.Bytes(), reactionTo.Bytes(), nickname,
+		reaction, pubKey, codeset, timestamp, lease, round.ID, mType, status)
 
 	uuid, err := w.receiveHelper(msgToInsert)
 	if err != nil {
@@ -326,8 +328,8 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64, messageID cryptoChannel.Messag
 //       an existing message, then you need to set it manually
 //       yourself.
 func buildMessage(channelID, messageID, parentID []byte, nickname, text string,
-	identity cryptoChannel.Identity, timestamp time.Time, lease time.Duration,
-	round id.Round, mType channels.MessageType,
+	pubKey ed25519.PublicKey, codeset uint8, timestamp time.Time,
+	lease time.Duration, round id.Round, mType channels.MessageType,
 	status channels.SentStatus) *Message {
 	return &Message{
 		MessageID:       messageID,
@@ -343,11 +345,8 @@ func buildMessage(channelID, messageID, parentID []byte, nickname, text string,
 		Type:            uint16(mType),
 		Round:           uint64(round),
 		// User Identity Info
-		Pubkey:         identity.PubKey,
-		Codename:       identity.Codename,
-		Color:          identity.Color,
-		Extension:      identity.Extension,
-		CodesetVersion: identity.CodesetVersion,
+		Pubkey:         pubKey,
+		CodesetVersion: codeset,
 	}
 }
 
@@ -409,9 +408,7 @@ func (w *wasmModel) receiveHelper(newMessage *Message) (uint64,
 		return 0, errors.Errorf("uuid lookup failure: %+v", err)
 	}
 	uuid := uint64(res.Int())
-	jww.DEBUG.Printf(
-		"Successfully stored message from %s, id %d",
-		newMessage.Codename, uuid)
+	jww.DEBUG.Printf("Successfully stored message %d", uuid)
 
 	return uuid, nil
 }
diff --git a/indexedDb/implementation_test.go b/indexedDb/implementation_test.go
index 61c55033..8baf354e 100644
--- a/indexedDb/implementation_test.go
+++ b/indexedDb/implementation_test.go
@@ -12,6 +12,7 @@ package indexedDb
 import (
 	"encoding/json"
 	"fmt"
+	"gitlab.com/xx_network/primitives/netTime"
 	"os"
 	"strconv"
 	"testing"
@@ -41,12 +42,10 @@ func TestWasmModel_UpdateSentStatus(t *testing.T) {
 		t.Fatalf("%+v", err)
 	}
 
-	cid := channel.Identity{}
-
 	// Store a test message
 	testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
-		testString, testString, cid, time.Now(), time.Second, 0, 0,
-		channels.Sent)
+		testString, testString, []byte{8, 6, 7, 5}, 0, netTime.Now(),
+		time.Second, 0, 0, channels.Sent)
 	uuid, err := eventModel.receiveHelper(testMsg)
 	if err != nil {
 		t.Fatalf("%+v", err)
@@ -63,7 +62,7 @@ func TestWasmModel_UpdateSentStatus(t *testing.T) {
 
 	// Update the sentStatus
 	expectedStatus := channels.Failed
-	eventModel.UpdateSentStatus(uuid, testMsgId, time.Now(),
+	eventModel.UpdateSentStatus(uuid, testMsgId, netTime.Now(),
 		rounds.Round{ID: 8675309}, expectedStatus)
 
 	// Check the resulting status
@@ -135,14 +134,6 @@ func TestWasmModel_UUIDTest(t *testing.T) {
 		t.Fatalf("%+v", err)
 	}
 
-	cid := channel.Identity{
-		Codename:       "codename123",
-		PubKey:         []byte{8, 6, 7, 5},
-		Color:          "#FFFFFF",
-		Extension:      "gif",
-		CodesetVersion: 0,
-	}
-
 	uuids := make([]uint64, 10)
 
 	for i := 0; i < 10; i++ {
@@ -151,9 +142,9 @@ func TestWasmModel_UUIDTest(t *testing.T) {
 		msgID := channel.MessageID{}
 		copy(msgID[:], testString+fmt.Sprintf("%d", i))
 		rnd := rounds.Round{ID: id.Round(42)}
-		uuid := eventModel.ReceiveMessage(channelID, msgID,
-			"test", testString+fmt.Sprintf("%d", i), cid, time.Now(),
-			time.Hour, rnd, 0, channels.Sent)
+		uuid := eventModel.ReceiveMessage(channelID, msgID, "test",
+			testString+fmt.Sprintf("%d", i), []byte{8, 6, 7, 5}, 0,
+			netTime.Now(), time.Hour, rnd, 0, channels.Sent)
 		uuids[i] = uuid
 	}
 
@@ -178,14 +169,6 @@ func TestWasmModel_DuplicateReceives(t *testing.T) {
 		t.Fatalf("%+v", err)
 	}
 
-	cid := channel.Identity{
-		Codename:       "codename123",
-		PubKey:         []byte{8, 6, 7, 5},
-		Color:          "#FFFFFF",
-		Extension:      "gif",
-		CodesetVersion: 0,
-	}
-
 	uuids := make([]uint64, 10)
 
 	msgID := channel.MessageID{}
@@ -194,9 +177,9 @@ func TestWasmModel_DuplicateReceives(t *testing.T) {
 		// Store a test message
 		channelID := id.NewIdFromBytes([]byte(testString), t)
 		rnd := rounds.Round{ID: id.Round(42)}
-		uuid := eventModel.ReceiveMessage(channelID, msgID,
-			"test", testString+fmt.Sprintf("%d", i), cid, time.Now(),
-			time.Hour, rnd, 0, channels.Sent)
+		uuid := eventModel.ReceiveMessage(channelID, msgID, "test",
+			testString+fmt.Sprintf("%d", i), []byte{8, 6, 7, 5}, 0,
+			netTime.Now(), time.Hour, rnd, 0, channels.Sent)
 		uuids[i] = uuid
 	}
 
@@ -228,7 +211,6 @@ func TestWasmModel_deleteMsgByChannel(t *testing.T) {
 	keepChannel := id.NewIdFromString("dontDeleteMe", id.Generic, t)
 
 	// Store some test messages
-	cid := channel.Identity{}
 	for i := 0; i < totalMessages; i++ {
 		testStr := testString + strconv.Itoa(i)
 
@@ -239,8 +221,9 @@ func TestWasmModel_deleteMsgByChannel(t *testing.T) {
 		}
 
 		testMsgId := channel.MakeMessageID([]byte(testStr), &id.ID{1})
-		eventModel.ReceiveMessage(thisChannel, testMsgId, testStr,
-			testStr, cid, time.Now(), time.Second, rounds.Round{ID: id.Round(0)}, 0, channels.Sent)
+		eventModel.ReceiveMessage(thisChannel, testMsgId, testStr, testStr,
+			[]byte{8, 6, 7, 5}, 0, netTime.Now(), time.Second,
+			rounds.Round{ID: id.Round(0)}, 0, channels.Sent)
 	}
 
 	// Check pre-results
diff --git a/indexedDb/model.go b/indexedDb/model.go
index 0e869277..3500e643 100644
--- a/indexedDb/model.go
+++ b/indexedDb/model.go
@@ -42,7 +42,6 @@ const (
 //
 // A Message may belong to one Message (Parent).
 //
-// A Message belongs to one User (cryptographic identity).
 // The user's nickname can change each message, but the rest does not. We
 // still duplicate all of it for each entry to simplify code for now.
 type Message struct {
@@ -61,13 +60,7 @@ type Message struct {
 	Round           uint64        `json:"round"`
 
 	// User cryptographic Identity struct -- could be pulled out
-	Pubkey []byte `json:"pubkey"` // Index
-	// Honorific      string `json:"honorific"`
-	// Adjective      string `json:"adjective"`
-	// Noun           string `json:"noun"`
-	Codename       string `json:"codename"`
-	Color          string `json:"color"`
-	Extension      string `json:"extension"`
+	Pubkey         []byte `json:"pubkey"` // Index
 	CodesetVersion uint8  `json:"codeset_version"`
 }
 
diff --git a/wasm/channels.go b/wasm/channels.go
index 7c24054f..317fd9b0 100644
--- a/wasm/channels.go
+++ b/wasm/channels.go
@@ -10,7 +10,9 @@
 package wasm
 
 import (
+	"encoding/base64"
 	"gitlab.com/xx_network/primitives/id"
+	"sync"
 	"syscall/js"
 
 	"gitlab.com/elixxir/client/bindings"
@@ -81,7 +83,7 @@ func (ch *ChannelsManager) GetID(js.Value, []js.Value) interface{} {
 //  - args[0] - ID of [Cmix] object in tracker (int).
 //
 // Returns:
-//  - JSON of [channel.PrivateIdentity] (Uint8Array).
+//  - Marshalled bytes of [channel.PrivateIdentity] (Uint8Array).
 //  - Throws a TypeError if generating the identity fails.
 func GenerateChannelIdentity(_ js.Value, args []js.Value) interface{} {
 	pi, err := bindings.GenerateChannelIdentity(args[0].Int())
@@ -93,6 +95,64 @@ func GenerateChannelIdentity(_ js.Value, args []js.Value) interface{} {
 	return utils.CopyBytesToJS(pi)
 }
 
+// identityMap stores identifies previously generated by ConstructIdentity.
+var identityMap sync.Map
+
+// ConstructIdentity constructs a [channel.Identity] from a user's public key
+// and codeset version.
+//
+// Parameters:
+//  - args[0] - The Ed25519 public key (Uint8Array).
+//  - args[1] - The version of the codeset used to generate the identity (int).
+//
+// Returns:
+//  - JSON of [channel.Identity] (Uint8Array).
+//  - Throws a TypeError if constructing the identity fails.
+func ConstructIdentity(_ js.Value, args []js.Value) interface{} {
+	// Note: This function is similar to constructIdentity below except that it
+	//  uses a sync.Map backend to increase efficiency for identities that were
+	//  already generated in this browser session.
+
+	pubKey := utils.CopyBytesToGo(args[0])
+	pubKeyBase64 := base64.StdEncoding.EncodeToString(pubKey)
+	identityObj, exists := identityMap.Load(pubKeyBase64)
+	if exists {
+		return utils.CopyBytesToJS(identityObj.([]byte))
+	}
+
+	identity, err := bindings.ConstructIdentity(
+		utils.CopyBytesToGo(args[0]), args[1].Int())
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	identityMap.Store(base64.StdEncoding.EncodeToString(pubKey), identity)
+
+	return utils.CopyBytesToJS(identity)
+}
+
+// constructIdentity constructs a [channel.Identity] from a user's public key
+// and codeset version. This function is retain for benchmarking purposes.
+//
+// Parameters:
+//  - args[0] - The Ed25519 public key (Uint8Array).
+//  - args[1] - The version of the codeset used to generate the identity (int).
+//
+// Returns:
+//  - JSON of [channel.Identity] (Uint8Array).
+//  - Throws a TypeError if constructing the identity fails.
+func constructIdentity(_ js.Value, args []js.Value) interface{} {
+	identity, err := bindings.ConstructIdentity(
+		utils.CopyBytesToGo(args[0]), args[1].Int())
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return utils.CopyBytesToJS(identity)
+}
+
 // ImportPrivateIdentity generates a new [channel.PrivateIdentity] from exported
 // data.
 //
@@ -1034,7 +1094,8 @@ func (em *eventModel) LeaveChannel(channelID []byte) {
 //    (Uint8Array).
 //  - nickname - The nickname of the sender of the message (string).
 //  - text - The content of the message (string).
-//  - identity - JSON of the sender's public ([channel.Identity]) (Uint8Array).
+//  - pubKey - The sender's Ed25519 public key (Uint8Array).
+//  - codeset - The codeset version (int).
 //  - timestamp - Time the message was received; represented as nanoseconds
 //    since unix epoch (int).
 //  - lease - The number of nanoseconds that the message is valid for (int).
@@ -1051,12 +1112,12 @@ func (em *eventModel) LeaveChannel(channelID []byte) {
 //  - A non-negative unique UUID for the message that it can be referenced by
 //    later with [eventModel.UpdateSentStatus].
 func (em *eventModel) ReceiveMessage(channelID, messageID []byte, nickname,
-	text string, identity []byte, timestamp, lease, roundId, msgType,
+	text string, pubKey []byte, codeset int, timestamp, lease, roundId, msgType,
 	status int64) int64 {
 	uuid := em.receiveMessage(utils.CopyBytesToJS(channelID),
 		utils.CopyBytesToJS(messageID), nickname, text,
-		utils.CopyBytesToJS(identity),
-		timestamp, lease, roundId, msgType, status)
+		utils.CopyBytesToJS(pubKey), codeset, timestamp, lease, roundId,
+		msgType, status)
 
 	return int64(uuid.Int())
 }
@@ -1076,7 +1137,8 @@ func (em *eventModel) ReceiveMessage(channelID, messageID []byte, nickname,
 //    (Uint8Array).
 //  - senderUsername - The username of the sender of the message (string).
 //  - text - The content of the message (string).
-//  - identity - JSON of the sender's public ([channel.Identity]) (Uint8Array).
+//  - pubKey - The sender's Ed25519 public key (Uint8Array).
+//  - codeset - The codeset version (int).
 //  - timestamp - Time the message was received; represented as nanoseconds
 //    since unix epoch (int).
 //  - lease - The number of nanoseconds that the message is valid for (int).
@@ -1093,11 +1155,11 @@ func (em *eventModel) ReceiveMessage(channelID, messageID []byte, nickname,
 //  - A non-negative unique UUID for the message that it can be referenced by
 //    later with [eventModel.UpdateSentStatus].
 func (em *eventModel) ReceiveReply(channelID, messageID, reactionTo []byte,
-	senderUsername, text string, identity []byte, timestamp, lease, roundId,
-	msgType, status int64) int64 {
+	senderUsername, text string, pubKey []byte, codeset int, timestamp, lease,
+	roundId, msgType, status int64) int64 {
 	uuid := em.receiveReply(utils.CopyBytesToJS(channelID),
 		utils.CopyBytesToJS(messageID), utils.CopyBytesToJS(reactionTo),
-		senderUsername, text, utils.CopyBytesToJS(identity),
+		senderUsername, text, utils.CopyBytesToJS(pubKey), codeset,
 		timestamp, lease, roundId, msgType, status)
 
 	return int64(uuid.Int())
@@ -1118,7 +1180,8 @@ func (em *eventModel) ReceiveReply(channelID, messageID, reactionTo []byte,
 //    (Uint8Array).
 //  - senderUsername - The username of the sender of the message (string).
 //  - reaction - The contents of the reaction message (string).
-//  - identity - JSON of the sender's public ([channel.Identity]) (Uint8Array).
+//  - pubKey - The sender's Ed25519 public key (Uint8Array).
+//  - codeset - The codeset version (int).
 //  - timestamp - Time the message was received; represented as nanoseconds
 //    since unix epoch (int).
 //  - lease - The number of nanoseconds that the message is valid for (int).
@@ -1135,11 +1198,11 @@ func (em *eventModel) ReceiveReply(channelID, messageID, reactionTo []byte,
 //  - A non-negative unique UUID for the message that it can be referenced by
 //    later with [eventModel.UpdateSentStatus].
 func (em *eventModel) ReceiveReaction(channelID, messageID, reactionTo []byte,
-	senderUsername, reaction string, identity []byte, timestamp, lease, roundId,
-	msgType, status int64) int64 {
+	senderUsername, reaction string, pubKey []byte, codeset int, timestamp,
+	lease, roundId, msgType, status int64) int64 {
 	uuid := em.receiveReaction(utils.CopyBytesToJS(channelID),
 		utils.CopyBytesToJS(messageID), utils.CopyBytesToJS(reactionTo),
-		senderUsername, reaction, utils.CopyBytesToJS(identity),
+		senderUsername, reaction, utils.CopyBytesToJS(pubKey), codeset,
 		timestamp, lease, roundId, msgType, status)
 
 	return int64(uuid.Int())
diff --git a/wasm/channels_test.go b/wasm/channels_test.go
index c9ec47fb..2e19cffd 100644
--- a/wasm/channels_test.go
+++ b/wasm/channels_test.go
@@ -11,7 +11,11 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
+	"gitlab.com/xx_network/crypto/csprng"
 	"reflect"
+	"syscall/js"
 	"testing"
 )
 
@@ -55,3 +59,61 @@ func Test_ChannelsManagerMethods(t *testing.T) {
 		}
 	}
 }
+
+type jsIdentity struct {
+	pubKey  js.Value
+	codeset js.Value
+}
+
+// Benchmark times the ConstructIdentity, which uses a sync.Map to increase
+// efficiency for previously generated identities.
+func BenchmarkConstructIdentity(b *testing.B) {
+	const n = 100_000
+	identities, j := make([]jsIdentity, 1000), 0
+	for i := 0; i < n; i++ {
+		pi, err := channel.GenerateIdentity(csprng.NewSystemRNG())
+		if err != nil {
+			b.Fatalf("%+v", err)
+		}
+
+		pubKey := utils.CopyBytesToJS(pi.PubKey)
+		codeset := js.ValueOf(int(pi.CodesetVersion))
+		ConstructIdentity(js.Value{}, []js.Value{pubKey, codeset})
+
+		if i%(n/len(identities)) == 0 {
+			identities[j] = jsIdentity{pubKey, codeset}
+			j++
+		}
+	}
+
+	b.ResetTimer()
+	for i := range identities {
+		go func(identity jsIdentity) {
+			ConstructIdentity(
+				js.Value{}, []js.Value{identity.pubKey, identity.codeset})
+		}(identities[i])
+	}
+}
+
+// Benchmark times the constructIdentity, which generates each new identity.
+func Benchmark_constructIdentity(b *testing.B) {
+	identities := make([]jsIdentity, b.N)
+	for i := range identities {
+		pi, err := channel.GenerateIdentity(csprng.NewSystemRNG())
+		if err != nil {
+			b.Fatalf("%+v", err)
+		}
+
+		pubKey := utils.CopyBytesToJS(pi.PubKey)
+		codeset := js.ValueOf(int(pi.CodesetVersion))
+		identities[i] = jsIdentity{pubKey, codeset}
+	}
+
+	b.ResetTimer()
+	for i := range identities {
+		go func(identity jsIdentity) {
+			constructIdentity(
+				js.Value{}, []js.Value{identity.pubKey, identity.codeset})
+		}(identities[i])
+	}
+}
-- 
GitLab