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