From f9acd4f64d775fe3857a1f18afc72ffb1a61fa89 Mon Sep 17 00:00:00 2001
From: Jono Wenger <jono@elixxir.io>
Date: Sat, 24 Sep 2022 17:47:36 -0700
Subject: [PATCH] Make channel manager creation functions take a function that
 creates the event model instead of the event model itself

---
 bindings/channels.go  | 190 +++++++++++++++++++++++++-----------------
 channels/interface.go |  44 +++++-----
 channels/manager.go   |  28 ++++---
 3 files changed, 153 insertions(+), 109 deletions(-)

diff --git a/bindings/channels.go b/bindings/channels.go
index 3c0dfdfd5..8206201b6 100644
--- a/bindings/channels.go
+++ b/bindings/channels.go
@@ -98,9 +98,16 @@ func (cm *ChannelsManager) GetID() int {
 	return cm.id
 }
 
-// GenerateChannelIdentity creates and returns a marshal for a private
-// channel Identity. The public component can be read as json via
-// [GetPublicChannelIdentityFromPrivate]
+// GenerateChannelIdentity creates a new private channel identity
+// ([channel.PrivateIdentity]). The public component can be retrieved as JSON
+// via [GetPublicChannelIdentityFromPrivate].
+//
+// Parameters:
+//  - cmixID - The tracked cmix object ID. This can be retrieved using
+//    [Cmix.GetID].
+//
+// Returns:
+//  - JSON of [channel.PrivateIdentity].
 func GenerateChannelIdentity(cmixID int) ([]byte, error) {
 	// Get user from singleton
 	user, err := cmixTrackerSingleton.get(cmixID)
@@ -117,8 +124,14 @@ func GenerateChannelIdentity(cmixID int) ([]byte, error) {
 	return pi.Marshal(), nil
 }
 
-// GetPublicChannelIdentity returns a json marshal of the public
-// identity related to the given marshaled public identity
+// GetPublicChannelIdentity constructs a public identity ([channel.Identity])
+// from a bytes version and returns it JSON marshaled.
+//
+// Parameters:
+//  - marshaledPublic - Bytes of the public identity ([channel.Identity]).
+//
+// Returns:
+//  - JSON of the constructed [channel.Identity].
 func GetPublicChannelIdentity(marshaledPublic []byte) ([]byte, error) {
 	i, err := cryptoChannel.UnmarshalIdentity(marshaledPublic)
 	if err != nil {
@@ -127,8 +140,16 @@ func GetPublicChannelIdentity(marshaledPublic []byte) ([]byte, error) {
 	return json.Marshal(&i)
 }
 
-// GetPublicChannelIdentityFromPrivate returns a json marshal of the public
-// identity related to the given private identity
+// GetPublicChannelIdentityFromPrivate returns the public identity
+// ([channel.Identity]) contained in the given private identity
+// ([channel.PrivateIdentity]).
+//
+// Parameters:
+//  - marshaledPrivate - Bytes of the private identity
+//    (channel.PrivateIdentity]).
+//
+// Returns:
+//  - JSON of the public [channel.Identity].
 func GetPublicChannelIdentityFromPrivate(marshaledPrivate []byte) ([]byte, error) {
 	pi, err := cryptoChannel.UnmarshalPrivateIdentity(marshaledPrivate)
 	if err != nil {
@@ -137,22 +158,24 @@ func GetPublicChannelIdentityFromPrivate(marshaledPrivate []byte) ([]byte, error
 	return json.Marshal(&pi.Identity)
 }
 
-// NewChannelsManagerGoEventModel creates a new ChannelsManager from a new
-// identity. This is not compatible with GoMobile Bindings because it
-// receives the go event model. This is for creating a manager for an identity
-// for the first time.
-// for generating a new one channel identity, please use
-// GenerateChannelIdentity
-// To reload this channel manager, use LoadChannelsManagerGoEventModel, passing
-// in the storageTag retrieved by
+// NewChannelsManagerGoEventModel creates a new [ChannelsManager] from a new
+// private identity ([channel.PrivateIdentity]). This is not compatible with
+// GoMobile Bindings because it receives the go event model.
+//
+// This is for creating a manager for an identity for the first time. For
+// generating a new one channel identity, use [GenerateChannelIdentity]. To
+// reload this channel manager, use [LoadChannelsManagerGoEventModel], passing
+// in the storage tag retrieved by [ChannelsManager.GetStorageTag].
 //
 // Parameters:
-//  - cmixID - The tracked cmix object ID. This can be retrieved using
+//  - cmixID - The tracked Cmix object ID. This can be retrieved using
 //    [Cmix.GetID].
-//  - privateIdentity - a private identity generated by GenerateChannelIdentity
-//  - goEvent the event model which is not compatible with GoMobile bindings
+//  - privateIdentity - Bytes of a private identity ([channel.PrivateIdentity])
+//    that is generated by [GenerateChannelIdentity].
+//  - goEvent - A function that initialises and returns the event model that is
+//    not compatible with GoMobile bindings.
 func NewChannelsManagerGoEventModel(cmixID int, privateIdentity []byte,
-	goEvent channels.EventModel) (*ChannelsManager, error) {
+	goEventBuilder channels.EventModelBuilder) (*ChannelsManager, error) {
 	pi, err := cryptoChannel.UnmarshalPrivateIdentity(privateIdentity)
 	if err != nil {
 		return nil, err
@@ -165,8 +188,8 @@ func NewChannelsManagerGoEventModel(cmixID int, privateIdentity []byte,
 	}
 
 	// Construct new channels manager
-	m, err := channels.NewManager(pi, user.api.GetStorage().GetKV(), user.api.GetCmix(),
-		user.api.GetRng(), goEvent)
+	m, err := channels.NewManager(pi, user.api.GetStorage().GetKV(),
+		user.api.GetCmix(), user.api.GetRng(), goEventBuilder)
 	if err != nil {
 		return nil, err
 	}
@@ -186,9 +209,10 @@ func NewChannelsManagerGoEventModel(cmixID int, privateIdentity []byte,
 //  - cmixID - The tracked cmix object ID. This can be retrieved using
 //    [Cmix.GetID].
 //  - storageTag - retrieved with ChannelsManager.GetStorageTag
-//  - goEvent the event model which is not compatible with GoMobile bindings
+//  - goEvent - A function that initialises and returns the event model that is
+//    not compatible with GoMobile bindings.
 func LoadChannelsManagerGoEventModel(cmixID int, storageTag string,
-	goEvent channels.EventModel) (*ChannelsManager, error) {
+	goEventBuilder channels.EventModelBuilder) (*ChannelsManager, error) {
 
 	// Get user from singleton
 	user, err := cmixTrackerSingleton.get(cmixID)
@@ -198,8 +222,7 @@ func LoadChannelsManagerGoEventModel(cmixID int, storageTag string,
 
 	// Construct new channels manager
 	m, err := channels.LoadManager(storageTag, user.api.GetStorage().GetKV(),
-		user.api.GetCmix(),
-		user.api.GetRng(), goEvent)
+		user.api.GetCmix(), user.api.GetRng(), goEventBuilder)
 	if err != nil {
 		return nil, err
 	}
@@ -208,21 +231,23 @@ func LoadChannelsManagerGoEventModel(cmixID int, storageTag string,
 	return channelManagerTrackerSingleton.make(m), nil
 }
 
-// NewChannelsManager creates a new ChannelsManager from a new
-// identity. This is for creating a manager for an identity
-// for the first time.
-// for generating a new one channel identity, please use
-// GenerateChannelIdentity
-// To reload this channel manager, use LoadChannelsManagerGoEventModel, passing
-// in the storageTag retrieved by
+// NewChannelsManager creates a new [ChannelsManager] from a new private
+// identity ([channel.PrivateIdentity]).
+//
+// This is for creating a manager for an identity for the first time. For
+// generating a new one channel identity, use [GenerateChannelIdentity]. To
+// reload this channel manager, use [LoadChannelsManager], passing in the
+// storage tag retrieved by [ChannelsManager.GetStorageTag].
 //
 // Parameters:
-//  - cmixID - The tracked cmix object ID. This can be retrieved using
+//  - cmixID - The tracked Cmix object ID. This can be retrieved using
 //    [Cmix.GetID].
-//  - privateIdentity - a private identity generated by GenerateChannelIdentity
-//  - event - the event model, compatible over the bindings
+//  - privateIdentity - Bytes of a private identity ([channel.PrivateIdentity])
+//    that is generated by [GenerateChannelIdentity].
+//  - event -  An interface that contains a function that initialises and returns
+//    the event model that is bindings-compatible.
 func NewChannelsManager(cmixID int, privateIdentity []byte,
-	event EventModel) (*ChannelsManager, error) {
+	eventBuilder EventModelBuilder) (*ChannelsManager, error) {
 	pi, err := cryptoChannel.UnmarshalPrivateIdentity(privateIdentity)
 	if err != nil {
 		return nil, err
@@ -234,12 +259,13 @@ func NewChannelsManager(cmixID int, privateIdentity []byte,
 		return nil, err
 	}
 
-	// wrap the event model to make it compatible
-	goEvent := NewEventModel(event)
+	eb := func(path string) channels.EventModel {
+		return NewEventModel(eventBuilder.Build(path))
+	}
 
 	// Construct new channels manager
-	m, err := channels.NewManager(pi, user.api.GetStorage().GetKV(), user.api.GetCmix(),
-		user.api.GetRng(), goEvent)
+	m, err := channels.NewManager(pi, user.api.GetStorage().GetKV(),
+		user.api.GetCmix(), user.api.GetRng(), eb)
 	if err != nil {
 		return nil, err
 	}
@@ -248,19 +274,22 @@ func NewChannelsManager(cmixID int, privateIdentity []byte,
 	return channelManagerTrackerSingleton.make(m), nil
 }
 
-// LoadChannelsManager loads an existing ChannelsManager.
-// This is for creating a manager for an identity for the first time.
-// The channel manager should have first been created with
-// NewChannelsManagerGoEventModel and then the storage tag can be retrieved
-// with ChannelsManager.GetStorageTag
+// LoadChannelsManager loads an existing [ChannelsManager].
+//
+// This is for loading a manager for an identity that has already been created.
+// The channel manager should have previously been created with
+// [NewChannelsManager] and the storage is retrievable with
+// [ChannelsManager.GetStorageTag].
 //
 // Parameters:
 //  - cmixID - The tracked cmix object ID. This can be retrieved using
 //    [Cmix.GetID].
-//  - storageTag - retrieved with ChannelsManager.GetStorageTag
-//  - event - the event model, compatible over the bindings
+//  - storageTag - The storage tag associated with the previously created
+//    channel manager and retrieved with [ChannelsManager.GetStorageTag].
+//  - event - An interface that contains a function that initialises and returns
+//    the event model that is bindings-compatible.
 func LoadChannelsManager(cmixID int, storageTag string,
-	event EventModel) (*ChannelsManager, error) {
+	eventBuilder EventModelBuilder) (*ChannelsManager, error) {
 
 	// Get user from singleton
 	user, err := cmixTrackerSingleton.get(cmixID)
@@ -268,13 +297,13 @@ func LoadChannelsManager(cmixID int, storageTag string,
 		return nil, err
 	}
 
-	// wrap the event model to make it compatible
-	goEvent := NewEventModel(event)
+	eb := func(path string) channels.EventModel {
+		return NewEventModel(eventBuilder.Build(path))
+	}
 
 	// Construct new channels manager
 	m, err := channels.LoadManager(storageTag, user.api.GetStorage().GetKV(),
-		user.api.GetCmix(),
-		user.api.GetRng(), goEvent)
+		user.api.GetCmix(), user.api.GetRng(), eb)
 	if err != nil {
 		return nil, err
 	}
@@ -288,24 +317,22 @@ type ChannelGeneration struct {
 	PrivateKey string
 }
 
-// GenerateChannel is used to create a channel. This makes a new channel of
-// which you are the admin. It is only for making new channels, not joining
-// existing ones.
+// GenerateChannel is used to create a channel a new channel of which you are
+// the admin. It is only for making new channels, not joining existing ones.
 //
 // It returns a pretty print of the channel and the private key.
 //
-// The name cannot be longer that ____ characters.
-//
-// the description cannot be longer than ___ and can only use ______ characters.
+// The name cannot be longer that __ characters. The description cannot be
+// longer than __ and can only use ______ characters.
 //
 // Parameters:
 //  - cmixID - The tracked cmix object ID. This can be retrieved using
 //    [Cmix.GetID].
-//  - name - The name of the new channel. The name cannot be longer than ____
+//  - name - The name of the new channel. The name cannot be longer than __
 //    characters and must contain only _____ characters. It cannot be changed
 //    once a channel is created.
 //  - description - The description of a channel. The description cannot be
-//    longer than ____ characters and must contain only _____ characters. It
+//    longer than __ characters and must contain only _____ characters. It
 //    cannot be changed once a channel is created.
 //
 // Returns:
@@ -321,7 +348,8 @@ func GenerateChannel(cmixID int, name, description string) ([]byte, error) {
 
 	stream := cmix.api.GetRng().GetStream()
 	defer stream.Close()
-	c, pk, err := cryptoBroadcast.NewChannel(name, description, cmix.api.GetCmix().GetMaxMessageLength(), stream)
+	c, pk, err := cryptoBroadcast.NewChannel(
+		name, description, cmix.api.GetCmix().GetMaxMessageLength(), stream)
 	if err != nil {
 		return nil, err
 	}
@@ -709,19 +737,20 @@ func (cm *ChannelsManager) SendReaction(marshalledChanId []byte,
 	return constructChannelSendReport(chanMsgId, rnd.ID, ephId)
 }
 
-// GetIdentity returns the marshaled public identity that the channel is
-// using
+// GetIdentity returns the marshaled public identity ([channel.Identity]) that
+// the channel is using.
 func (cm *ChannelsManager) GetIdentity() ([]byte, error) {
 	i := cm.api.GetIdentity()
 	return json.Marshal(&i)
 }
 
-// GetStorageTag returns the storage tag needed to reload the manager
+// GetStorageTag returns the storage tag needed to reload the manager.
 func (cm *ChannelsManager) GetStorageTag() string {
 	return cm.api.GetStorageTag()
 }
 
-// SetNickname sets the nickname for a given channel
+// SetNickname sets the nickname for a given channel. The nickname must be valid
+// according to [IsNicknameValid].
 func (cm *ChannelsManager) SetNickname(newNick string, ch []byte) error {
 	chid, err := id.Unmarshal(ch)
 	if err != nil {
@@ -730,7 +759,7 @@ func (cm *ChannelsManager) SetNickname(newNick string, ch []byte) error {
 	return cm.api.SetNickname(newNick, chid)
 }
 
-// DeleteNickname deletes the nickname for a given channel
+// DeleteNickname deletes the nickname for a given channel.
 func (cm *ChannelsManager) DeleteNickname(ch []byte) error {
 	chid, err := id.Unmarshal(ch)
 	if err != nil {
@@ -739,8 +768,8 @@ func (cm *ChannelsManager) DeleteNickname(ch []byte) error {
 	return cm.api.DeleteNickname(chid)
 }
 
-// GetNickname returns the nickname for a given channel. Returns an error if
-// there is no nickname
+// GetNickname returns the nickname set for a given channel. Returns an error if
+// there is no nickname set.
 func (cm *ChannelsManager) GetNickname(ch []byte) (string, error) {
 	chid, err := id.Unmarshal(ch)
 	if err != nil {
@@ -754,11 +783,11 @@ func (cm *ChannelsManager) GetNickname(ch []byte) (string, error) {
 	return nick, nil
 }
 
-// IsNicknameValid checks if a nickname is valid
+// IsNicknameValid checks if a nickname is valid.
 //
-// rules
-//   - a nickname must not be longer than 24 characters
-//   - a nickname must not be shorter than 1 character
+// Rules:
+//  1. A nickname must not be longer than 24 characters.
+//  2. A nickname must not be shorter than 1 character.
 func IsNicknameValid(nick string) error {
 	return channels.IsNicknameValid(nick)
 }
@@ -892,6 +921,11 @@ func (cm *ChannelsManager) RegisterReceiveHandler(messageType int,
 // Event Model Logic                                                          //
 ////////////////////////////////////////////////////////////////////////////////
 
+// EventModelBuilder builds an event model
+type EventModelBuilder interface {
+	Build(path string) EventModel
+}
+
 // EventModel is an interface which an external party which uses the channels
 // system passed an object which adheres to in order to get events on the
 // channel.
@@ -930,8 +964,8 @@ type EventModel interface {
 	//  Delivered =  1
 	//  Failed    =  2
 	//
-	// Returns a non-negative unique uuid for the message by which it can be
-	// referenced later with UpdateSentStatus
+	// Returns a non-negative unique UUID for the message that it can be
+	// referenced by later with [EventModel.UpdateSentStatus].
 	ReceiveMessage(channelID, messageID []byte, nickname, text string,
 		identity []byte, timestamp, lease, roundId, status int64) int64
 
@@ -962,8 +996,8 @@ type EventModel interface {
 	//  Delivered =  1
 	//  Failed    =  2
 	//
-	// Returns a non-negative unique uuid for the message by which it can be
-	// referenced later with UpdateSentStatus
+	// Returns a non-negative unique UUID for the message that it can be
+	// referenced by later with [EventModel.UpdateSentStatus].
 	ReceiveReply(channelID, messageID, reactionTo []byte,
 		nickname, text string, identity []byte,
 		timestamp, lease, roundId, status int64) int64
@@ -1015,8 +1049,8 @@ type EventModel interface {
 	//  Sent      =  0
 	//  Delivered =  1
 	//  Failed    =  2
-	UpdateSentStatus(uuint int64, messageID []byte, timestamp, roundid,
-		status int64)
+	UpdateSentStatus(
+		uuid int64, messageID []byte, timestamp, roundID, status int64)
 
 	// unimplemented
 	// IgnoreMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
diff --git a/channels/interface.go b/channels/interface.go
index fd22d6426..1d1874693 100644
--- a/channels/interface.go
+++ b/channels/interface.go
@@ -69,8 +69,8 @@ type Manager interface {
 	// it will always be possible to send a payload of 798 bytes at minimum
 	// The message will auto delete validUntil after the round it is sent in,
 	// lasting forever if ValidForever is used
-	SendMessage(channelID *id.ID, msg string,
-		validUntil time.Duration, params cmix.CMIXParams) (
+	SendMessage(channelID *id.ID, msg string, validUntil time.Duration,
+		params cmix.CMIXParams) (
 		cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error)
 
 	// SendReply is used to send a formatted message over a channel.
@@ -85,19 +85,22 @@ type Manager interface {
 		validUntil time.Duration, params cmix.CMIXParams) (
 		cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error)
 
-	// SendReaction is used to send a reaction to a message over a channel.
-	// The reaction must be a single emoji with no other characters, and will
-	// be rejected otherwise.
-	// Clients will drop the reaction if they do not recognize the reactTo message
-	SendReaction(channelID *id.ID, reaction string, reactTo cryptoChannel.MessageID,
-		params cmix.CMIXParams) (cryptoChannel.MessageID, rounds.Round,
-		ephemeral.Id, error)
+	// SendReaction is used to send a reaction to a message over a channel. The
+	// reaction must be a single emoji with no other characters, and will be
+	// rejected otherwise.
+	//
+	// Clients will drop the reaction if they do not recognize the reactTo
+	// message.
+	SendReaction(channelID *id.ID, reaction string,
+		reactTo cryptoChannel.MessageID, params cmix.CMIXParams) (
+		cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error)
 
-	// RegisterReceiveHandler is used to register handlers for non default message
-	// types s they can be processed by modules. it is important that such modules
-	// sync up with the event model implementation.
-	// There can only be one handler per message type, and this will return an error
-	// on a multiple registration.
+	// RegisterReceiveHandler is used to register handlers for non default
+	// message types so that they can be processed by modules. It is important
+	// that such modules sync up with the event model implementation.
+	//
+	// There can only be one handler per message type, and this will return an
+	// error on a multiple registration.
 	RegisterReceiveHandler(messageType MessageType,
 		listener MessageTypeReceiveMessage) error
 
@@ -105,7 +108,8 @@ type Manager interface {
 	// getChannelsUnsafe if you already have taken the mux.
 	GetChannels() []*id.ID
 
-	// GetChannel returns the underlying cryptographic structure for a given channel.
+	// GetChannel returns the underlying cryptographic structure for a given
+	// channel.
 	GetChannel(chID *id.ID) (*cryptoBroadcast.Channel, error)
 
 	// ReplayChannel replays all messages from the channel within the network's
@@ -114,14 +118,14 @@ type Manager interface {
 	// messages to be re-retrieved from the network
 	ReplayChannel(chID *id.ID) error
 
-	// SetNickname sets the nickname for a channel after checking that the nickname
-	// is valid using IsNicknameValid
+	// SetNickname sets the nickname for a channel after checking that the
+	// nickname is valid using IsNicknameValid.
 	SetNickname(newNick string, ch *id.ID) error
 
-	// DeleteNickname removes the nickname for a given channel, using the codename
-	// for that channel instead
+	// DeleteNickname removes the nickname for a given channel, using the
+	// codename for that channel instead.
 	DeleteNickname(ch *id.ID) error
 
-	// GetNickname returns the nickname for the given channel if it exists
+	// GetNickname returns the nickname for the given channel if it exists.
 	GetNickname(ch *id.ID) (nickname string, exists bool)
 }
diff --git a/channels/manager.go b/channels/manager.go
index ddbdb04f3..24aab8328 100644
--- a/channels/manager.go
+++ b/channels/manager.go
@@ -75,38 +75,44 @@ type Client interface {
 	RemoveHealthCallback(uint64)
 }
 
-// NewManager creates a new channel.Manager from a private identity. It
-// prefixes the KV with a tag derived from the public key which can be retried
-// for reloading using Manage.GetStorageTag.
+// EventModelBuilder initialises the event model using the given path.
+type EventModelBuilder func(path string) EventModel
+
+// NewManager creates a new channel Manager from a [channel.PrivateIdentity]. It
+// prefixes the KV with a tag derived from the public key that can be retried
+// for reloading using [Manager.GetStorageTag].
 func NewManager(identity cryptoChannel.PrivateIdentity, kv *versioned.KV,
-	net Client, rng *fastRNG.StreamGenerator, model EventModel) (Manager, error) {
+	net Client, rng *fastRNG.StreamGenerator, modelBuilder EventModelBuilder) (
+	Manager, error) {
 
 	// Prefix the kv with the username so multiple can be run
-	kv = kv.Prefix(getStorageTag(identity.PubKey))
+	storageTag := getStorageTag(identity.PubKey)
+	kv = kv.Prefix(storageTag)
 
 	if err := storeIdentity(kv, identity); err != nil {
 		return nil, err
 	}
-
-	m := setupManager(identity, kv, net, rng, model)
+	m := setupManager(identity, kv, net, rng, modelBuilder(storageTag))
 
 	return m, nil
 }
 
-// LoadManager restores a channel.Manager from disk stored at the given
-//storage tag.
+// LoadManager restores a channel Manager from disk stored at the given storage
+// tag.
 func LoadManager(storageTag string, kv *versioned.KV, net Client,
-	rng *fastRNG.StreamGenerator, model EventModel) (Manager, error) {
+	rng *fastRNG.StreamGenerator, modelBuilder EventModelBuilder) (Manager, error) {
 
 	// Prefix the kv with the username so multiple can be run
 	kv = kv.Prefix(storageTag)
 
-	//load the identity
+	// Load the identity
 	identity, err := loadIdentity(kv)
 	if err != nil {
 		return nil, err
 	}
 
+	model := modelBuilder(storageTag)
+
 	m := setupManager(identity, kv, net, rng, model)
 
 	return m, nil
-- 
GitLab