README ¶
Channels provide a channels implementation on top of broadcast which is capable of handing the user facing features of channels, including replies, reactions, and eventually admin commands.
on sending, data propagates as follows:
Send function (Example: SendMessage) - > SendGeneric ->
Broadcast.BroadcastWithAssembler -> cmix.SendWithAssembler
on receiving messages propagate as follows:
cmix message pickup (by service)- > broadcast.Processor ->
userListener -> events.triggerEvent ->
messageTypeHandler (example: Text) ->
eventModel (example: ReceiveMessage)
on sendingAdmin, data propagates as follows:
Send function - > SendAdminGeneric ->
Broadcast.BroadcastAsymmetricWithAssembler -> cmix.SendWithAssembler
on receiving admin messages propagate as follows:
cmix message pickup (by service)- > broadcast.Processor -> adminListener ->
events.triggerAdminEvent -> messageTypeHandler (example: Text) ->
eventModel (example: ReceiveMessage)
Documentation ¶
Overview ¶
Package channels provides a channels implementation on top of broadcast which is capable of handing the user facing features of channels, including replies, reactions, and eventually admin commands.
Index ¶
- Constants
- Variables
- func IsNicknameValid(nick string) error
- func ValidateReaction(reaction string) error
- type CMIXChannelReaction
- func (*CMIXChannelReaction) Descriptor() ([]byte, []int)deprecated
- func (x *CMIXChannelReaction) GetReaction() string
- func (x *CMIXChannelReaction) GetReactionMessageID() []byte
- func (x *CMIXChannelReaction) GetVersion() uint32
- func (*CMIXChannelReaction) ProtoMessage()
- func (x *CMIXChannelReaction) ProtoReflect() protoreflect.Message
- func (x *CMIXChannelReaction) Reset()
- func (x *CMIXChannelReaction) String() string
- type CMIXChannelText
- func (*CMIXChannelText) Descriptor() ([]byte, []int)deprecated
- func (x *CMIXChannelText) GetReplyMessageID() []byte
- func (x *CMIXChannelText) GetText() string
- func (x *CMIXChannelText) GetVersion() uint32
- func (*CMIXChannelText) ProtoMessage()
- func (x *CMIXChannelText) ProtoReflect() protoreflect.Message
- func (x *CMIXChannelText) Reset()
- func (x *CMIXChannelText) String() string
- type ChannelMessage
- func (*ChannelMessage) Descriptor() ([]byte, []int)deprecated
- func (x *ChannelMessage) GetLease() int64
- func (x *ChannelMessage) GetLocalTimestamp() int64
- func (x *ChannelMessage) GetNickname() string
- func (x *ChannelMessage) GetNonce() []byte
- func (x *ChannelMessage) GetPayload() []byte
- func (x *ChannelMessage) GetPayloadType() uint32
- func (x *ChannelMessage) GetRoundID() uint64
- func (*ChannelMessage) ProtoMessage()
- func (x *ChannelMessage) ProtoReflect() protoreflect.Message
- func (x *ChannelMessage) Reset()
- func (x *ChannelMessage) String() string
- type Client
- type EventModel
- type EventModelBuilder
- type Manager
- type MessageType
- type MessageTypeReceiveMessage
- type NameService
- type SentStatus
- type UserMessage
- func (*UserMessage) Descriptor() ([]byte, []int)deprecated
- func (x *UserMessage) GetECCPublicKey() []byte
- func (x *UserMessage) GetMessage() []byte
- func (x *UserMessage) GetSignature() []byte
- func (*UserMessage) ProtoMessage()
- func (x *UserMessage) ProtoReflect() protoreflect.Message
- func (x *UserMessage) Reset()
- func (x *UserMessage) String() string
Constants ¶
const ( // SendMessageTag is the base tag used when generating a debug tag for // sending a message. SendMessageTag = "ChMessage" // SendReplyTag is the base tag used when generating a debug tag for // sending a reply. SendReplyTag = "ChReply" // SendReactionTag is the base tag used when generating a debug tag for // sending a reaction. SendReactionTag = "ChReaction" )
const AdminUsername = "Admin"
AdminUsername defines the displayed username of admin messages, which are unique users for every channel defined by the channel's private key.
Variables ¶
var ( // ChannelAlreadyExistsErr is returned when attempting to join a channel // that the user is already in. ChannelAlreadyExistsErr = errors.New( "the channel cannot be added because it already exists") // ChannelDoesNotExistsErr is returned when a channel does not exist. ChannelDoesNotExistsErr = errors.New("the channel cannot be found") // MessageTooLongErr is returned when attempting to send a message that is // too large. MessageTooLongErr = errors.New("the passed message is too long") // WrongPrivateKey is returned when the private key does not match the // channel's public key. WrongPrivateKey = errors.New( "the passed private key does not match the channel") // MessageTypeAlreadyRegistered is returned if a handler has already been // registered with the supplied message type. Only one handler can be // registered per type. MessageTypeAlreadyRegistered = errors.New( "the given message type has already been registered") // InvalidReaction is returned if the passed reaction string is an invalid // emoji. InvalidReaction = errors.New( "The reaction is not valid, it must be a single emoji") )
var AdminFakePubKey = ed25519.PublicKey{}
var File_channelMessages_proto protoreflect.FileDescriptor
var File_text_proto protoreflect.FileDescriptor
var ValidForever = time.Duration(math.MaxInt64)
ValidForever is used as a validUntil lease when sending to denote the message or operation never expires.
Note: A message relay must be present to enforce this otherwise things expire after 3 weeks due to network retention.
Functions ¶
func IsNicknameValid ¶
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.
TODO: Add character filtering.
func ValidateReaction ¶
ValidateReaction checks that the reaction only contains a single emoji.
Types ¶
type CMIXChannelReaction ¶
type CMIXChannelReaction struct { Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` Reaction string `protobuf:"bytes,2,opt,name=reaction,proto3" json:"reaction,omitempty"` ReactionMessageID []byte `protobuf:"bytes,3,opt,name=reactionMessageID,proto3" json:"reactionMessageID,omitempty"` // contains filtered or unexported fields }
CMIXChannelReaction is the payload for reactions. The reaction must be a single emoji and the reactionMessageID must be non nil and a real message in the channel.
func (*CMIXChannelReaction) Descriptor
deprecated
func (*CMIXChannelReaction) Descriptor() ([]byte, []int)
Deprecated: Use CMIXChannelReaction.ProtoReflect.Descriptor instead.
func (*CMIXChannelReaction) GetReaction ¶
func (x *CMIXChannelReaction) GetReaction() string
func (*CMIXChannelReaction) GetReactionMessageID ¶
func (x *CMIXChannelReaction) GetReactionMessageID() []byte
func (*CMIXChannelReaction) GetVersion ¶
func (x *CMIXChannelReaction) GetVersion() uint32
func (*CMIXChannelReaction) ProtoMessage ¶
func (*CMIXChannelReaction) ProtoMessage()
func (*CMIXChannelReaction) ProtoReflect ¶
func (x *CMIXChannelReaction) ProtoReflect() protoreflect.Message
func (*CMIXChannelReaction) Reset ¶
func (x *CMIXChannelReaction) Reset()
func (*CMIXChannelReaction) String ¶
func (x *CMIXChannelReaction) String() string
type CMIXChannelText ¶
type CMIXChannelText struct { Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` Text string `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"` ReplyMessageID []byte `protobuf:"bytes,3,opt,name=replyMessageID,proto3" json:"replyMessageID,omitempty"` // contains filtered or unexported fields }
CMIXChannelText is the payload for sending normal text messages to channels the replyMessageID is nil when it is not a reply.
func (*CMIXChannelText) Descriptor
deprecated
func (*CMIXChannelText) Descriptor() ([]byte, []int)
Deprecated: Use CMIXChannelText.ProtoReflect.Descriptor instead.
func (*CMIXChannelText) GetReplyMessageID ¶
func (x *CMIXChannelText) GetReplyMessageID() []byte
func (*CMIXChannelText) GetText ¶
func (x *CMIXChannelText) GetText() string
func (*CMIXChannelText) GetVersion ¶
func (x *CMIXChannelText) GetVersion() uint32
func (*CMIXChannelText) ProtoMessage ¶
func (*CMIXChannelText) ProtoMessage()
func (*CMIXChannelText) ProtoReflect ¶
func (x *CMIXChannelText) ProtoReflect() protoreflect.Message
func (*CMIXChannelText) Reset ¶
func (x *CMIXChannelText) Reset()
func (*CMIXChannelText) String ¶
func (x *CMIXChannelText) String() string
type ChannelMessage ¶
type ChannelMessage struct { // Lease is the length that this channel message will take effect. Lease int64 `protobuf:"varint,1,opt,name=Lease,proto3" json:"Lease,omitempty"` // The round this message was sent on. RoundID uint64 `protobuf:"varint,2,opt,name=RoundID,proto3" json:"RoundID,omitempty"` // The type the below payload is. This may be some form of channel command, // such as BAN<username1>. PayloadType uint32 `protobuf:"varint,3,opt,name=PayloadType,proto3" json:"PayloadType,omitempty"` // Payload is the actual message payload. It will be processed differently // based on the PayloadType. Payload []byte `protobuf:"bytes,4,opt,name=Payload,proto3" json:"Payload,omitempty"` // nickname is the name which the user is using for this message it will not // be longer than 24 characters. Nickname string `protobuf:"bytes,5,opt,name=Nickname,proto3" json:"Nickname,omitempty"` // Nonce is 32 bits of randomness to ensure that two messages in the same // round with that have the same nickname, payload, and lease will not have // the same message ID. Nonce []byte `protobuf:"bytes,6,opt,name=Nonce,proto3" json:"Nonce,omitempty"` // LocalTimestamp is the timestamp when the "send call" is made based upon // the local clock. If this differs by more than 5 seconds +/- from when the // round it sent on is queued, then a random mutation on the queued time // (+/- 200ms) will be used by local clients instead. LocalTimestamp int64 `protobuf:"varint,7,opt,name=LocalTimestamp,proto3" json:"LocalTimestamp,omitempty"` // contains filtered or unexported fields }
ChannelMessage is transmitted by the channel. Effectively it is a command for the channel sent by a user with admin access of the channel.
func (*ChannelMessage) Descriptor
deprecated
func (*ChannelMessage) Descriptor() ([]byte, []int)
Deprecated: Use ChannelMessage.ProtoReflect.Descriptor instead.
func (*ChannelMessage) GetLease ¶
func (x *ChannelMessage) GetLease() int64
func (*ChannelMessage) GetLocalTimestamp ¶
func (x *ChannelMessage) GetLocalTimestamp() int64
func (*ChannelMessage) GetNickname ¶
func (x *ChannelMessage) GetNickname() string
func (*ChannelMessage) GetNonce ¶
func (x *ChannelMessage) GetNonce() []byte
func (*ChannelMessage) GetPayload ¶
func (x *ChannelMessage) GetPayload() []byte
func (*ChannelMessage) GetPayloadType ¶
func (x *ChannelMessage) GetPayloadType() uint32
func (*ChannelMessage) GetRoundID ¶
func (x *ChannelMessage) GetRoundID() uint64
func (*ChannelMessage) ProtoMessage ¶
func (*ChannelMessage) ProtoMessage()
func (*ChannelMessage) ProtoReflect ¶
func (x *ChannelMessage) ProtoReflect() protoreflect.Message
func (*ChannelMessage) Reset ¶
func (x *ChannelMessage) Reset()
func (*ChannelMessage) String ¶
func (x *ChannelMessage) String() string
type Client ¶
type Client interface { GetMaxMessageLength() int SendWithAssembler(recipient *id.ID, assembler cmix.MessageAssembler, cmixParams cmix.CMIXParams) (rounds.Round, ephemeral.Id, error) IsHealthy() bool AddIdentity(id *id.ID, validUntil time.Time, persistent bool) AddIdentityWithHistory( id *id.ID, validUntil, beginning time.Time, persistent bool) AddService(clientID *id.ID, newService message.Service, response message.Processor) DeleteClientService(clientID *id.ID) RemoveIdentity(id *id.ID) GetRoundResults(timeout time.Duration, roundCallback cmix.RoundEventCallback, roundList ...id.Round) AddHealthCallback(f func(bool)) uint64 RemoveHealthCallback(uint64) }
Client contains the methods from cmix.Client that are required by the Manager.
type EventModel ¶
type EventModel interface { // JoinChannel is called whenever a channel is joined locally. JoinChannel(channel *cryptoBroadcast.Channel) // LeaveChannel is called whenever a channel is left locally. LeaveChannel(channelID *id.ID) // ReceiveMessage is called whenever a message is received on a given // channel. It may be called multiple times on the same message. It is // incumbent on the user of the API to filter such called by message ID. // // The API needs to return a UUID of the message that can be referenced at a // later time. // // messageID, timestamp, and round are all nillable and may be updated based // upon the UUID at a later date. A time of time.Time{} will be passed for a // nilled timestamp. // // Nickname may be empty, in which case the UI is expected to display the // codename. // // Message type is included in the call; it will always be Text (1) for this // call, but it may be required in downstream databases. ReceiveMessage(channelID *id.ID, messageID cryptoChannel.MessageID, nickname, text string, pubKey ed25519.PublicKey, codeset uint8, timestamp time.Time, lease time.Duration, round rounds.Round, mType MessageType, status SentStatus) uint64 // ReceiveReply is called whenever a message is received that is a reply on // a given channel. It may be called multiple times on the same message. It // is incumbent on the user of the API to filter such called by message ID. // // Messages may arrive our of order, so a reply, in theory, can arrive // before the initial message. As a result, it may be important to buffer // replies. // // The API needs to return a UUID of the message that can be referenced at a // later time. // // messageID, timestamp, and round are all nillable and may be updated based // upon the UUID at a later date. A time of time.Time{} will be passed for a // nilled timestamp. // // Nickname may be empty, in which case the UI is expected to display the // codename. // // Message type is included in the call; it will always be Text (1) for this // call, but it may be required in downstream databases. ReceiveReply(channelID *id.ID, messageID cryptoChannel.MessageID, reactionTo cryptoChannel.MessageID, nickname, text string, pubKey ed25519.PublicKey, codeset uint8, timestamp time.Time, lease time.Duration, round rounds.Round, mType MessageType, status SentStatus) uint64 // ReceiveReaction is called whenever a reaction to a message is received on // a given channel. It may be called multiple times on the same reaction. It // is incumbent on the user of the API to filter such called by message ID. // // Messages may arrive our of order, so a reply, in theory, can arrive // before the initial message. As a result, it may be important to buffer // replies. // // The API needs to return a UUID of the message that can be referenced at a // later time. // // messageID, timestamp, and round are all nillable and may be updated based // upon the UUID at a later date. A time of time.Time{} will be passed for a // nilled timestamp. // // Nickname may be empty, in which case the UI is expected to display the // codename. // // Message type is included in the call; it will always be Text (1) for this // call, but it may be required in downstream databases. ReceiveReaction(channelID *id.ID, messageID cryptoChannel.MessageID, reactionTo cryptoChannel.MessageID, nickname, reaction string, pubKey ed25519.PublicKey, codeset uint8, timestamp time.Time, lease time.Duration, round rounds.Round, mType MessageType, status SentStatus) uint64 // UpdateSentStatus is called whenever the sent status of a message has // changed. // // messageID, timestamp, and round are all nillable and may be updated based // upon the UUID at a later date. A time of time.Time{} will be passed for a // nilled timestamp. If a nil value is passed, make no update. UpdateSentStatus(uuid uint64, messageID cryptoChannel.MessageID, timestamp time.Time, round rounds.Round, status SentStatus) }
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.
type EventModelBuilder ¶
type EventModelBuilder func(path string) (EventModel, error)
EventModelBuilder initialises the event model using the given path.
type Manager ¶
type Manager interface { // GetIdentity returns the public identity associated with this channel // manager. GetIdentity() cryptoChannel.Identity // ExportPrivateIdentity encrypts and exports the private identity to a // portable string. ExportPrivateIdentity(password string) ([]byte, error) // GetStorageTag returns the tag at where this manager is stored. To be used // when loading the manager. The storage tag is derived from the public key. GetStorageTag() string // JoinChannel joins the given channel. It will fail if the channel has // already been joined. JoinChannel(channel *cryptoBroadcast.Channel) error // LeaveChannel leaves the given channel. It will return an error if the // channel was not previously joined. LeaveChannel(channelID *id.ID) error // SendGeneric is used to send a raw message over a channel. In general, it // should be wrapped in a function that defines the wire protocol. // // If the final message, before being sent over the wire, is too long, this // will return an error. Due to the underlying encoding using compression, // it is not possible to define the largest payload that can be sent, but it // will always be possible to send a payload of 802 bytes at minimum. // // The meaning of validUntil depends on the use case. SendGeneric(channelID *id.ID, messageType MessageType, msg []byte, validUntil time.Duration, params cmix.CMIXParams) ( cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error) // SendAdminGeneric is used to send a raw message over a channel encrypted // with admin keys, identifying it as sent by the admin. In general, it // should be wrapped in a function that defines the wire protocol. // // If the final message, before being sent over the wire, is too long, this // will return an error. The message must be at most 510 bytes long. SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID, messageType MessageType, msg []byte, validUntil time.Duration, params cmix.CMIXParams) (cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error) // SendMessage is used to send a formatted message over a channel. // // Due to the underlying encoding using compression, it is not possible to // define the largest payload that can be sent, but 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) ( cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error) // SendReply is used to send a formatted message over a channel. // // Due to the underlying encoding using compression, it is not possible to // define the largest payload that can be sent, but it will always be // possible to send a payload of 766 bytes at minimum. // // If the message ID that the reply is sent to does not exist, then the // other side will post the message as a normal message and not as a reply. // // The message will auto delete validUntil after the round it is sent in, // lasting forever if ValidForever is used. SendReply(channelID *id.ID, msg string, replyTo cryptoChannel.MessageID, 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) // 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 // GetChannels returns the IDs of all channels that have been joined. Use // getChannelsUnsafe if you already have taken the mux. GetChannels() []*id.ID // 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 // memory (~3 weeks) over the event model. It does this by wiping the // underlying state tracking for message pickup for the channel, causing all // 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(newNick string, chID *id.ID) error // DeleteNickname removes the nickname for a given channel, using the // codename for that channel instead. DeleteNickname(chID *id.ID) error // GetNickname returns the nickname for the given channel, if it exists. GetNickname(chID *id.ID) (nickname string, exists bool) }
Manager provides an interface to manager channels.
func LoadManager ¶
func LoadManager(storageTag string, kv *versioned.KV, net Client, rng *fastRNG.StreamGenerator, modelBuilder EventModelBuilder) (Manager, error)
LoadManager restores a channel Manager from disk stored at the given storage tag.
func NewManager ¶
func NewManager(identity cryptoChannel.PrivateIdentity, kv *versioned.KV, net Client, rng *fastRNG.StreamGenerator, modelBuilder EventModelBuilder) ( Manager, error)
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].
type MessageType ¶
type MessageType uint32
MessageType is the type of message being sent to a channel.
const ( // Text is the default type for a message. It denotes that the message only // contains text. Text MessageType = 1 // AdminText denotes that the message only contains text and that it comes // from the channel admin. AdminText MessageType = 2 // Reaction denotes that the message is a reaction to another message. Reaction MessageType = 3 )
func (MessageType) String ¶
func (mt MessageType) String() string
String returns a human-readable version of MessageType, used for debugging and logging. This function adheres to the fmt.Stringer interface.
type MessageTypeReceiveMessage ¶
type MessageTypeReceiveMessage func(channelID *id.ID, messageID cryptoChannel.MessageID, messageType MessageType, nickname string, content []byte, pubKey ed25519.PublicKey, codeset uint8, timestamp time.Time, lease time.Duration, round rounds.Round, status SentStatus) uint64
MessageTypeReceiveMessage defines handlers for messages of various message types. Default ones for Text, Reaction, and AdminText.
A unique UUID must be returned by which the message can be referenced later via [EventModel.UpdateSentStatus].
It must return a unique UUID for the message by which it can be referenced later.
type NameService ¶
type NameService interface { // GetUsername returns the username. GetUsername() string // GetChannelValidationSignature returns the validation signature and the // time it was signed. GetChannelValidationSignature() ([]byte, time.Time) // GetChannelPubkey returns the user's public key. GetChannelPubkey() ed25519.PublicKey // SignChannelMessage returns the signature of the given message. SignChannelMessage(message []byte) (signature []byte, err error) // ValidateChannelMessage validates that a received channel message's // username lease is signed by the NameService. ValidateChannelMessage(username string, lease time.Time, pubKey ed25519.PublicKey, authorIDSignature []byte) bool }
NameService is an interface which encapsulates the user identity channel tracking service.
NameService is currently unused.
func NewDummyNameService ¶
func NewDummyNameService(username string, rng io.Reader) (NameService, error)
NewDummyNameService returns a dummy object adhering to the name service. This neither produces valid signatures nor validates passed signatures.
THIS IS FOR DEVELOPMENT AND DEBUGGING PURPOSES ONLY.
type SentStatus ¶
type SentStatus uint8
SentStatus represents the current status of a channel message.
const ( // Unsent is the status of a message when it is pending to be sent. Unsent SentStatus = iota // Sent is the status of a message once the round it is sent on completed. Sent // Delivered is the status of a message once is has been received. Delivered // Failed is the status of a message if it failed to send. Failed )
func (SentStatus) String ¶
func (ss SentStatus) String() string
String returns a human-readable version of SentStatus, used for debugging and logging. This function adheres to the fmt.Stringer interface.
type UserMessage ¶
type UserMessage struct { // Message contains the contents of the message. This is typically what the // end-user has submitted to the channel. This is a serialization of the // ChannelMessage. Message []byte `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"` // Signature is the signature proving this message has been sent by the // owner of this user's public key. // // Signature = Sig(User_ECCPublicKey, Message) Signature []byte `protobuf:"bytes,3,opt,name=Signature,proto3" json:"Signature,omitempty"` // ECCPublicKey is the user's EC Public key. This is provided by the // network. ECCPublicKey []byte `protobuf:"bytes,5,opt,name=ECCPublicKey,proto3" json:"ECCPublicKey,omitempty"` // contains filtered or unexported fields }
UserMessage is a message sent by a user who is a member within the channel.
func (*UserMessage) Descriptor
deprecated
func (*UserMessage) Descriptor() ([]byte, []int)
Deprecated: Use UserMessage.ProtoReflect.Descriptor instead.
func (*UserMessage) GetECCPublicKey ¶
func (x *UserMessage) GetECCPublicKey() []byte
func (*UserMessage) GetMessage ¶
func (x *UserMessage) GetMessage() []byte
func (*UserMessage) GetSignature ¶
func (x *UserMessage) GetSignature() []byte
func (*UserMessage) ProtoMessage ¶
func (*UserMessage) ProtoMessage()
func (*UserMessage) ProtoReflect ¶
func (x *UserMessage) ProtoReflect() protoreflect.Message
func (*UserMessage) Reset ¶
func (x *UserMessage) Reset()
func (*UserMessage) String ¶
func (x *UserMessage) String() string