diff --git a/bindings/channels.go b/bindings/channels.go index f58af96029bfeb55ff800e13d6be032185e2823e..1da21111341f7c11ad2157e2f4ab16fd4340e577 100644 --- a/bindings/channels.go +++ b/bindings/channels.go @@ -906,6 +906,9 @@ func (cm *ChannelsManager) SendAdminGeneric(adminPrivateKey, chanMsgId, rnd, ephId, err := cm.api.SendAdminGeneric(rsaPrivKey, chanId, msgTy, message, time.Duration(leaseTimeMS), params.CMIX) + if err != nil { + return nil, err + } // Construct send report return constructChannelSendReport(chanMsgId, rnd.ID, ephId) diff --git a/broadcast/client.go b/broadcast/client.go index 678c7e9dccf7ef0322aea6369a92d6dcfde032ca..d18ec8248142a3f97b00c67f3fab3ade8c68ca65 100644 --- a/broadcast/client.go +++ b/broadcast/client.go @@ -24,6 +24,8 @@ type broadcastClient struct { rng *fastRNG.StreamGenerator } +// NewBroadcastChannelFunc creates a broadcast Channel. Used so that it can be +// replaced in tests. type NewBroadcastChannelFunc func(channel *crypto.Channel, net Client, rng *fastRNG.StreamGenerator) (Channel, error) @@ -52,7 +54,8 @@ func NewBroadcastChannel(channel *crypto.Channel, net Client, } // RegisterListener registers a listener for broadcast messages. -func (bc *broadcastClient) RegisterListener(listenerCb ListenerFunc, method Method) error { +func (bc *broadcastClient) RegisterListener( + listenerCb ListenerFunc, method Method) error { var tag string switch method { case Symmetric: diff --git a/broadcast/interface.go b/broadcast/interface.go index 4c390995abaf0b942e5d17ae03b14064fd7fac0d..1160955667d473ca064c4a11c16bbafd6ea1c271 100644 --- a/broadcast/interface.go +++ b/broadcast/interface.go @@ -83,7 +83,7 @@ type Channel interface { Stop() } -// Assembler is a function which allows a bre +// Assembler is a function which allows a bre. type Assembler func(rid id.Round) (payload []byte, err error) // Client contains the methods from [cmix.Client] that are required by @@ -92,7 +92,8 @@ type Client interface { SendWithAssembler(recipient *id.ID, assembler cmix.MessageAssembler, cmixParams cmix.CMIXParams) (rounds.Round, ephemeral.Id, error) IsHealthy() bool - AddIdentityWithHistory(id *id.ID, validUntil, beginning 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) diff --git a/broadcast/method.go b/broadcast/method.go index c154fc027b81a55f600c5f54fa3847aa7616ea89..5a87d0f00dbd272c37c725e6662aff5b21b5d767 100644 --- a/broadcast/method.go +++ b/broadcast/method.go @@ -7,7 +7,7 @@ package broadcast -// Method enum for broadcast type +// Method enum for broadcast type. type Method uint8 const ( diff --git a/broadcast/processor.go b/broadcast/processor.go index 8ee1f85d07ad093337711ef15e7f6e3ced3dd1f8..d17790489c843153bd11b4c641cf39026efc4a2e 100644 --- a/broadcast/processor.go +++ b/broadcast/processor.go @@ -21,7 +21,7 @@ const ( errDecrypt = "[BCAST] Failed to decrypt payload for broadcast %s (%q): %+v" ) -// processor struct for message handling +// processor struct for message handling. type processor struct { c *crypto.Channel cb ListenerFunc @@ -36,16 +36,19 @@ func (p *processor) Process(msg format.Message, var err error switch p.method { case RSAToPublic: - decodedMessage, decryptErr := p.c.DecryptRSAToPublic(msg.GetContents(), msg.GetMac(), msg.GetKeyFP()) + decodedMessage, decryptErr := p.c.DecryptRSAToPublic( + msg.GetContents(), msg.GetMac(), msg.GetKeyFP()) if decryptErr != nil { jww.ERROR.Printf(errDecrypt, p.c.ReceptionID, p.c.Name, decryptErr) return } - size := binary.BigEndian.Uint16(decodedMessage[:internalPayloadSizeLength]) + size := binary.BigEndian.Uint16( + decodedMessage[:internalPayloadSizeLength]) payload = decodedMessage[internalPayloadSizeLength : size+internalPayloadSizeLength] case Symmetric: - payload, err = p.c.DecryptSymmetric(msg.GetContents(), msg.GetMac(), msg.GetKeyFP()) + payload, err = p.c.DecryptSymmetric( + msg.GetContents(), msg.GetMac(), msg.GetKeyFP()) if err != nil { jww.ERROR.Printf(errDecrypt, p.c.ReceptionID, p.c.Name, err) return @@ -57,7 +60,8 @@ func (p *processor) Process(msg format.Message, p.cb(payload, receptionID, round) } -// String returns a string identifying the symmetricProcessor for debugging purposes. +// String returns a string identifying the symmetricProcessor for debugging +// purposes. func (p *processor) String() string { return "broadcastChannel-" + p.c.Name } diff --git a/broadcast/rsaToPublic.go b/broadcast/rsaToPublic.go index 26c4d0924af04c6464610f20fe8040ac1aa7b082..158fd9e7de36a1e997775a0604b03b6ca1b722be 100644 --- a/broadcast/rsaToPublic.go +++ b/broadcast/rsaToPublic.go @@ -35,13 +35,10 @@ const ( // or smaller and the channel [rsa.PrivateKey] must be passed in. // // The network must be healthy to send. -func (bc *broadcastClient) BroadcastRSAtoPublic(pk rsa.PrivateKey, - payload []byte, cMixParams cmix.CMIXParams) (rounds.Round, ephemeral.Id, error) { - // Confirm network health - - assemble := func(rid id.Round) ([]byte, error) { - return payload, nil - } +func (bc *broadcastClient) BroadcastRSAtoPublic( + pk rsa.PrivateKey, payload []byte, cMixParams cmix.CMIXParams) ( + rounds.Round, ephemeral.Id, error) { + assemble := func(rid id.Round) ([]byte, error) { return payload, nil } return bc.BroadcastRSAToPublicWithAssembler(pk, assemble, cMixParams) } @@ -91,7 +88,7 @@ func (bc *broadcastClient) BroadcastRSAToPublicWithAssembler( "asymmetric broadcast message") } - // Create service using asymmetric broadcast service tag & channel + // Create service using asymmetric broadcast service tag and channel // reception ID allows anybody with this info to listen for messages on // this channel service = message.Service{ diff --git a/broadcast/rsaToPublic_test.go b/broadcast/rsaToPublic_test.go index 9be17453b96db68ca8a735fdcd9234ccd7ad2e3e..c9d1d3d270ce907a79b7a5c1d138e8bdda4a8174 100644 --- a/broadcast/rsaToPublic_test.go +++ b/broadcast/rsaToPublic_test.go @@ -158,7 +158,8 @@ func Test_asymmetricClient_Smoke(t *testing.T) { } // Broadcast payload - _, _, err := clients[0].BroadcastRSAtoPublic(pk, payload, cmix.GetDefaultCMIXParams()) + _, _, err := clients[0].BroadcastRSAtoPublic( + pk, payload, cmix.GetDefaultCMIXParams()) if err != nil { t.Errorf("Cmix 0 failed to send broadcast: %+v", err) } diff --git a/broadcast/symmetric.go b/broadcast/symmetric.go index 432c53c8cab2ae80ea16f915d0142136a0d1532d..cf5d83de00409b2d3b4e7e13d0e54c262c059797 100644 --- a/broadcast/symmetric.go +++ b/broadcast/symmetric.go @@ -20,9 +20,8 @@ import ( // Error messages. const ( // broadcastClient.Broadcast - errNetworkHealth = "cannot send broadcast when the network is not healthy" - errPayloadSize = "size of payload %d must be less than %d" - errBroadcastMethodType = "cannot call %s broadcast using %s channel" + errNetworkHealth = "cannot send broadcast when the network is not healthy" + errPayloadSize = "size of payload %d must be less than %d" ) // Tags. @@ -50,7 +49,8 @@ func (bc *broadcastClient) Broadcast(payload []byte, cMixParams cmix.CMIXParams) // The payload must be of the size [Channel.MaxPayloadSize] or smaller. // // The network must be healthy to send. -func (bc *broadcastClient) BroadcastWithAssembler(assembler Assembler, cMixParams cmix.CMIXParams) ( +func (bc *broadcastClient) BroadcastWithAssembler( + assembler Assembler, cMixParams cmix.CMIXParams) ( rounds.Round, ephemeral.Id, error) { if !bc.net.IsHealthy() { return rounds.Round{}, ephemeral.Id{}, errors.New(errNetworkHealth) @@ -59,7 +59,7 @@ func (bc *broadcastClient) BroadcastWithAssembler(assembler Assembler, cMixParam assemble := func(rid id.Round) (fp format.Fingerprint, service message.Service, encryptedPayload, mac []byte, err error) { - //assemble the passed payload + // Assemble the passed payload payload, err := assembler(rid) if err != nil { return format.Fingerprint{}, message.Service{}, nil, nil, err @@ -93,6 +93,6 @@ func (bc *broadcastClient) BroadcastWithAssembler(assembler Assembler, cMixParam return } - return bc.net.SendWithAssembler(bc.channel.ReceptionID, assemble, - cMixParams) + return bc.net.SendWithAssembler( + bc.channel.ReceptionID, assemble, cMixParams) } diff --git a/channels/readme.md b/channels/README.md similarity index 72% rename from channels/readme.md rename to channels/README.md index 9327e9635afd9c109054bf7b486d24c97a53c0bc..33a8253c1746a98a607ac4ac91e29ae6b77f0efb 100644 --- a/channels/readme.md +++ b/channels/README.md @@ -1,21 +1,30 @@ -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. +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: +```text Send function (Example: SendMessage) - > SendGeneric -> Broadcast.BroadcastWithAssembler -> cmix.SendWithAssembler +``` on receiving messages propagate as follows: +```text cmix message pickup (by service)- > broadcast.Processor -> userListener -> events.triggerEvent -> messageTypeHandler (example: Text) -> eventModel (example: ReceiveMessage) +``` on sendingAdmin, data propagates as follows: +```text Send function - > SendAdminGeneric -> Broadcast.BroadcastAsymmetricWithAssembler -> cmix.SendWithAssembler +``` on receiving admin messages propagate as follows: +```text cmix message pickup (by service)- > broadcast.Processor -> adminListener -> events.triggerAdminEvent -> messageTypeHandler (example: Text) -> -eventModel (example: ReceiveMessage) \ No newline at end of file +eventModel (example: ReceiveMessage) +``` diff --git a/channels/adminListener.go b/channels/adminListener.go index 86f80142ef5345a3aad346907d96940097a281a6..8ced505b43570c82e7e1de91b29b6883b78e0052 100644 --- a/channels/adminListener.go +++ b/channels/adminListener.go @@ -26,7 +26,7 @@ type adminListener struct { checkSent messageReceiveFunc } -// Listen is called when a message is received for the admin listener +// Listen is called when a message is received for the admin listener. func (al *adminListener) Listen(payload []byte, receptionID receptionID.EphemeralIdentity, round rounds.Round) { // Get the message ID @@ -40,7 +40,7 @@ func (al *adminListener) Listen(payload []byte, return } - //check if we sent the message, ignore triggering if we sent + // Check if we sent the message, ignore triggering if we sent if al.checkSent(msgID, round) { return } @@ -49,14 +49,13 @@ func (al *adminListener) Listen(payload []byte, // Check the round to ensure that the message is not a replay if id.Round(cm.RoundID) != round.ID { - jww.WARN.Printf("The round message %s send on %s referenced "+ - "(%d) was not the same as the round the message was found on (%d)", + jww.WARN.Printf("The round message %s send on %s referenced (%d) was "+ + "not the same as the round the message was found on (%d)", msgID, al.chID, cm.RoundID, round.ID) return } - // Replace the timestamp on the message if it is outside of the - // allowable range + // Replace the timestamp on the message if it is outside the allowable range ts := vetTimestamp(time.Unix(0, cm.LocalTimestamp), round.Timestamps[states.QUEUED], msgID) diff --git a/channels/adminListener_test.go b/channels/adminListener_test.go index cb571d5724dcd7318d7ab4dec78bd2c0fe14866c..7fd37cb727dc5b5ee9fa0dda1a4e527757d31556 100644 --- a/channels/adminListener_test.go +++ b/channels/adminListener_test.go @@ -33,9 +33,9 @@ type triggerAdminEventDummy struct { } func (taed *triggerAdminEventDummy) triggerAdminEvent(chID *id.ID, - cm *ChannelMessage, ts time.Time, messageID cryptoChannel.MessageID, + cm *ChannelMessage, _ time.Time, messageID cryptoChannel.MessageID, receptionID receptionID.EphemeralIdentity, round rounds.Round, - status SentStatus) (uint64, error) { + _ SentStatus) (uint64, error) { taed.gotData = true taed.chID = chID @@ -75,9 +75,11 @@ func TestAdminListener_Listen(t *testing.T) { dummy := &triggerAdminEventDummy{} al := adminListener{ - chID: chID, - trigger: dummy.triggerAdminEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + trigger: dummy.triggerAdminEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } // Call the listener @@ -111,8 +113,7 @@ func TestAdminListener_Listen(t *testing.T) { // Tests that the message is rejected when the round it came on doesn't match // the round in the channel message. func TestAdminListener_Listen_BadRound(t *testing.T) { - - // build inputs + // Build inputs chID := &id.ID{} chID[0] = 1 @@ -136,15 +137,17 @@ func TestAdminListener_Listen_BadRound(t *testing.T) { dummy := &triggerAdminEventDummy{} al := adminListener{ - chID: chID, - trigger: dummy.triggerAdminEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + trigger: dummy.triggerAdminEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } // Call the listener al.Listen(cmSerial, receptionID.EphemeralIdentity{}, r) - // check the results + // Check the results if dummy.gotData { t.Fatalf("payload handled when it should have failed due to " + "a round issue") @@ -168,9 +171,11 @@ func TestAdminListener_Listen_BadChannelMessage(t *testing.T) { dummy := &triggerAdminEventDummy{} al := adminListener{ - chID: chID, - trigger: dummy.triggerAdminEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + trigger: dummy.triggerAdminEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } // Call the listener @@ -187,8 +192,7 @@ func TestAdminListener_Listen_BadChannelMessage(t *testing.T) { // Tests that the message is rejected when the sized broadcast message is // malformed. func TestAdminListener_Listen_BadSizedBroadcast(t *testing.T) { - - // build inputs + // Build inputs chID := &id.ID{} chID[0] = 1 @@ -215,9 +219,11 @@ func TestAdminListener_Listen_BadSizedBroadcast(t *testing.T) { dummy := &triggerAdminEventDummy{} al := adminListener{ - chID: chID, - trigger: dummy.triggerAdminEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + trigger: dummy.triggerAdminEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } // Call the listener diff --git a/channels/channelMessages.pb.go b/channels/channelMessages.pb.go index 1a7a414fe860fcfbf9439ed5422a3e550c5a55a0..7bc7f599dc9c5556853a4b596f421b526ec6c540 100644 --- a/channels/channelMessages.pb.go +++ b/channels/channelMessages.pb.go @@ -44,17 +44,17 @@ type ChannelMessage struct { // 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 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 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"` } diff --git a/channels/channelMessages.proto b/channels/channelMessages.proto index d89337d366a8cc4cfc27948f9e37e7570eddb254..e949765b448816cc76fa341ebfb4f3f8676f9454 100644 --- a/channels/channelMessages.proto +++ b/channels/channelMessages.proto @@ -15,7 +15,7 @@ package channels; // the channel sent by a user with admin access of the channel. message ChannelMessage{ // Lease is the length that this channel message will take effect. - int64 Lease = 1; + int64 Lease = 1; // The round this message was sent on. uint64 RoundID = 2; @@ -26,10 +26,10 @@ message ChannelMessage{ // Payload is the actual message payload. It will be processed differently // based on the PayloadType. - bytes Payload = 4; + bytes Payload = 4; - // nickname is the name which the user is using for this message - // it will not be longer than 24 characters + // nickname is the name which the user is using for this message it will not + // be longer than 24 characters. string Nickname = 5; // Nonce is 32 bits of randomness to ensure that two messages in the same @@ -37,10 +37,10 @@ message ChannelMessage{ // the same message ID. bytes Nonce = 6; - // 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 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. int64 LocalTimestamp = 7; } @@ -49,15 +49,15 @@ message UserMessage { // 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. - bytes Message = 1; + bytes Message = 1; // Signature is the signature proving this message has been sent by the // owner of this user's public key. // // Signature = Sig(User_ECCPublicKey, Message) - bytes Signature = 3; + bytes Signature = 3; // ECCPublicKey is the user's EC Public key. This is provided by the // network. - bytes ECCPublicKey = 5; + bytes ECCPublicKey = 5; } \ No newline at end of file diff --git a/channels/dummyNameServer.go b/channels/dummyNameServer.go index 8c14e40279d8091bf34f29a5a881246715c27c34..50c6c4578871ab6c748fce4af0aabd6be0e9bbf4 100644 --- a/channels/dummyNameServer.go +++ b/channels/dummyNameServer.go @@ -16,8 +16,8 @@ import ( "time" ) -// NewDummyNameService returns a dummy object adhering to the name service -// This neither produces valid signatures nor validates passed signatures. +// 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. func NewDummyNameService(username string, rng io.Reader) (NameService, error) { @@ -31,23 +31,23 @@ func NewDummyNameService(username string, rng io.Reader) (NameService, error) { lease: netTime.Now().Add(35 * 24 * time.Hour), } - //generate the private key + // Generate the private key var err error dns.public, dns.private, err = ed25519.GenerateKey(rng) if err != nil { return nil, err } - //generate a dummy user discover identity to produce a validation signature - //just sign with our own key, it wont be evaluated anyhow + // Generate a dummy user discover identity to produce a validation signature + // just sign with our own key, it will not be evaluated anyhow dns.validationSig = channel.SignChannelLease(dns.public, dns.username, dns.lease, dns.private) return dns, nil } -// dummyNameService is a dummy NameService implementation. This is NOT meant -// for use in production +// dummyNameService is a dummy NameService implementation. This is NOT meant for +// use in production. type dummyNameService struct { private ed25519.PrivateKey public ed25519.PublicKey @@ -94,14 +94,14 @@ func (dns *dummyNameService) SignChannelMessage(message []byte) ( return sig, nil } -// ValidateChannelMessage will always return true, indicating the the channel +// ValidateChannelMessage will always return true, indicating that the channel // message is valid. This will ignore the passed in arguments. As a result, // these values may be dummy or precanned. // // THIS IS FOR DEVELOPMENT AND DEBUGGING PURPOSES ONLY. -func (dns *dummyNameService) ValidateChannelMessage(username string, lease time.Time, - pubKey ed25519.PublicKey, authorIDSignature []byte) bool { - //ignore the authorIDSignature +func (dns *dummyNameService) ValidateChannelMessage( + string, time.Time, ed25519.PublicKey, []byte) bool { + // Ignore the authorIDSignature jww.WARN.Printf("ValidateChannelMessage called on Dummy Name Service, " + "no validation done - identity not validated. YOU SHOULD NEVER SEE " + "THIS MESSAGE IN PRODUCTION") diff --git a/channels/emoji.go b/channels/emoji.go index bb9250601f324a9ff964b23cd7559ab6c99701d2..3c442dfcd3fb125cb5a8e32324950eda1945b966 100644 --- a/channels/emoji.go +++ b/channels/emoji.go @@ -7,19 +7,17 @@ package channels -import ( - "regexp" -) - -//based on emojis found at https://unicode.org/emoji/charts/full-emoji-list.html +/*// Based on emojis found at +// https://unicode.org/emoji/charts/full-emoji-list.html const findEmoji = `[\xA9\xAE\x{2000}-\x{3300}\x{1F000}-\x{1FBFF}]` -var compiledFindEmoji = regexp.MustCompile(findEmoji) +// compiledFindEmoji is a regular expression for matching an emoji. +var compiledFindEmoji = regexp.MustCompile(findEmoji)*/ // ValidateReaction checks that the reaction only contains a single emoji. func ValidateReaction(reaction string) error { - //make sure it is only only character + // Make sure it is the only character reactRunes := []rune(reaction) if len(reactRunes) > 1 { return InvalidReaction @@ -27,10 +25,10 @@ func ValidateReaction(reaction string) error { /* reader := bytes.NewReader([]byte(reaction)) - // make sure it has emojis - if !compiledFindEmoji.MatchReader(reader) { - return InvalidReaction - } + // Make sure it has emojis + if !compiledFindEmoji.MatchReader(reader) { + return InvalidReaction + } */ return nil } diff --git a/channels/errors.go b/channels/errors.go index 319f065966a899f5a64c05b41eda84caa887fd02..ec6d8bfde368da2a24472fe3fd4b2efbcc1b96a2 100644 --- a/channels/errors.go +++ b/channels/errors.go @@ -10,16 +10,31 @@ package channels import "github.com/pkg/errors" 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 = errors.New( - "the channel cannot be found") - MessageTooLongErr = errors.New( - "the passed message is too long") + + // 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 = errors.New("the given message type has " + - "already been registered") + + // 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") ) diff --git a/channels/eventModel.go b/channels/eventModel.go index 153a6bd68df07144219fc249332c5c3f0f316db2..20c9b4b64162f8429fd7f48c97e36ed4fe1e5714 100644 --- a/channels/eventModel.go +++ b/channels/eventModel.go @@ -15,6 +15,7 @@ import ( "github.com/golang/protobuf/proto" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/cmix/identity/receptionID" + "strconv" "sync" "time" @@ -28,15 +29,40 @@ import ( // unique users for every channel defined by the channel's private key. const AdminUsername = "Admin" +// SentStatus represents the current status of a channel message. type SentStatus uint8 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 ) +// String returns a human-readable version of [SentStatus], used for debugging +// and logging. This function adheres to the [fmt.Stringer] interface. +func (ss SentStatus) String() string { + switch ss { + case Unsent: + return "unsent" + case Sent: + return "sent" + case Delivered: + return "delivered" + case Failed: + return "failed" + default: + return "Invalid SentStatus: " + strconv.Itoa(int(ss)) + } +} + var AdminFakePubKey = ed25519.PublicKey{} // EventModel is an interface which an external party which uses the channels @@ -187,8 +213,8 @@ func initEvents(model EventModel) *events { // // There can only be one handler per message type, and this will return an error // on a multiple registration. -func (e *events) RegisterReceiveHandler(messageType MessageType, - listener MessageTypeReceiveMessage) error { +func (e *events) RegisterReceiveHandler( + messageType MessageType, listener MessageTypeReceiveMessage) error { e.mux.Lock() defer e.mux.Unlock() @@ -225,16 +251,17 @@ func (e *events) triggerEvent(chID *id.ID, umi *userMessageInternal, if !exists { errStr := fmt.Sprintf("Received message from %x on channel %s in "+ "round %d which could not be handled due to unregistered message "+ - "type %s; Contents: %v", um.ECCPublicKey, chID, round.ID, messageType, - cm.Payload) + "type %s; Contents: %v", + um.ECCPublicKey, chID, round.ID, messageType, cm.Payload) jww.WARN.Printf(errStr) return 0, errors.New(errStr) } - // Call the listener. This is already in an instanced event, no new thread needed. - uuid := listener(chID, umi.GetMessageID(), messageType, cm.Nickname, cm.Payload, - um.ECCPublicKey, 0, ts, time.Duration(cm.Lease), - round, status) + // Call the listener. This is already in an instanced event; no new thread + // is needed. + uuid := listener( + chID, umi.GetMessageID(), messageType, cm.Nickname, cm.Payload, + um.ECCPublicKey, 0, ts, time.Duration(cm.Lease), round, status) return uuid, nil } @@ -257,18 +284,19 @@ func (e *events) triggerAdminEvent(chID *id.ID, cm *ChannelMessage, listener, exists := e.registered[messageType] e.mux.RUnlock() if !exists { - errStr := fmt.Sprintf("Received Admin message from %s on channel %s in "+ - "round %d which could not be handled due to unregistered message "+ - "type %s; Contents: %v", AdminUsername, chID, round.ID, messageType, - cm.Payload) + errStr := fmt.Sprintf("Received Admin message from %s on channel %s "+ + "in round %d which could not be handled due to unregistered "+ + "message type %s; Contents: %v", + AdminUsername, chID, round.ID, messageType, cm.Payload) jww.WARN.Printf(errStr) return 0, errors.New(errStr) } - // Call the listener. This is already in an instanced event, no new thread needed. - uuid := listener(chID, messageID, messageType, AdminUsername, cm.Payload, - AdminFakePubKey, 0, ts, - time.Duration(cm.Lease), round, status) + // Call the listener. This is already in an instanced event; no new thread + // is needed. + uuid := listener( + chID, messageID, messageType, AdminUsername, cm.Payload, + AdminFakePubKey, 0, ts, time.Duration(cm.Lease), round, status) return uuid, nil } @@ -303,8 +331,8 @@ func (e *events) receiveTextMessage(channelID *id.ID, "to %s on %s", tag, base64.StdEncoding.EncodeToString(pubKey), base64.StdEncoding.EncodeToString(txt.ReplyMessageID), channelID) - return e.model.ReceiveReply(channelID, messageID, replyTo, - nickname, txt.Text, pubKey, codeset, timestamp, lease, round, Text, status) + return e.model.ReceiveReply(channelID, messageID, replyTo, nickname, + txt.Text, pubKey, codeset, timestamp, lease, round, Text, status) } else { jww.ERROR.Printf("Failed process reply to for message %s from "+ @@ -320,11 +348,10 @@ func (e *events) receiveTextMessage(channelID *id.ID, tag := makeChaDebugTag(channelID, pubKey, content, SendMessageTag) jww.INFO.Printf("[%s]Channels - Received message from %s "+ "to %s on %s", tag, base64.StdEncoding.EncodeToString(pubKey), - base64.StdEncoding.EncodeToString(txt.ReplyMessageID), - channelID) + base64.StdEncoding.EncodeToString(txt.ReplyMessageID), channelID) - return e.model.ReceiveMessage(channelID, messageID, nickname, txt.Text, pubKey, codeset, - timestamp, lease, round, Text, status) + return e.model.ReceiveMessage(channelID, messageID, nickname, txt.Text, + pubKey, codeset, timestamp, lease, round, Text, status) } // receiveReaction is the internal function that handles the reception of @@ -358,7 +385,8 @@ func (e *events) receiveReaction(channelID *id.ID, return 0 } - if react.ReactionMessageID != nil && len(react.ReactionMessageID) == cryptoChannel.MessageIDLen { + if react.ReactionMessageID != nil && + len(react.ReactionMessageID) == cryptoChannel.MessageIDLen { var reactTo cryptoChannel.MessageID copy(reactTo[:], react.ReactionMessageID) @@ -369,7 +397,8 @@ func (e *events) receiveReaction(channelID *id.ID, channelID) return e.model.ReceiveReaction(channelID, messageID, reactTo, nickname, - react.Reaction, pubKey, codeset, timestamp, lease, round, Reaction, status) + react.Reaction, pubKey, codeset, timestamp, lease, round, Reaction, + status) } else { jww.ERROR.Printf("Failed process reaction %s from public key %v "+ "(codeset %d) on channel %s, type %s, ts: %s, lease: %s, "+ diff --git a/channels/eventModel_test.go b/channels/eventModel_test.go index 4edf780c46076867789a298c224bfb6194e073b0..e1f566c74bc152e3665d483132a11953d3bf2ca8 100644 --- a/channels/eventModel_test.go +++ b/channels/eventModel_test.go @@ -102,7 +102,6 @@ func (m *MockEvent) ReceiveReaction(channelID *id.ID, func (m *MockEvent) UpdateSentStatus(uint64, cryptoChannel.MessageID, time.Time, rounds.Round, SentStatus) { - // TODO implement me panic("implement me") } @@ -248,7 +247,8 @@ func TestEvents_triggerEvents(t *testing.T) { r.Timestamps[states.QUEUED] = netTime.Now() // call the trigger - _, err = e.triggerEvent(chID, umi, netTime.Now(), receptionID.EphemeralIdentity{}, r, Delivered) + _, err = e.triggerEvent( + chID, umi, netTime.Now(), receptionID.EphemeralIdentity{}, r, Delivered) if err != nil { t.Fatalf(err.Error()) } @@ -319,7 +319,8 @@ func TestEvents_triggerEvents_noChannel(t *testing.T) { r.Timestamps[states.QUEUED] = netTime.Now() // call the trigger - _, err := e.triggerEvent(chID, umi, netTime.Now(), receptionID.EphemeralIdentity{}, r, Delivered) + _, err := e.triggerEvent( + chID, umi, netTime.Now(), receptionID.EphemeralIdentity{}, r, Delivered) if err != nil { t.Fatalf(err.Error()) } @@ -357,8 +358,8 @@ func TestEvents_triggerAdminEvents(t *testing.T) { msgID := cryptoChannel.MakeMessageID(u.userMessage.Message, chID) // call the trigger - _, err = e.triggerAdminEvent(chID, cm, netTime.Now(), msgID, receptionID.EphemeralIdentity{}, r, - Delivered) + _, err = e.triggerAdminEvent(chID, cm, netTime.Now(), msgID, + receptionID.EphemeralIdentity{}, r, Delivered) if err != nil { t.Fatalf(err.Error()) } @@ -432,8 +433,8 @@ func TestEvents_triggerAdminEvents_noChannel(t *testing.T) { msgID := cryptoChannel.MakeMessageID(u.userMessage.Message, chID) // call the trigger - _, err := e.triggerAdminEvent(chID, cm, netTime.Now(), msgID, receptionID.EphemeralIdentity{}, r, - Delivered) + _, err := e.triggerAdminEvent(chID, cm, netTime.Now(), msgID, + receptionID.EphemeralIdentity{}, r, Delivered) if err != nil { t.Fatalf(err.Error()) } diff --git a/channels/identityStore_test.go b/channels/identityStore_test.go index ec2d72f9b90d77accb42d757682fa83f333689f2..3c3d74d57c1348cf0811b733c9334eecb37469f9 100644 --- a/channels/identityStore_test.go +++ b/channels/identityStore_test.go @@ -43,4 +43,4 @@ func TestStoreLoadIdentity(t *testing.T) { base64.StdEncoding.EncodeToString(loadedIdentity.Marshal())) } -} \ No newline at end of file +} diff --git a/channels/interface.go b/channels/interface.go index b7d5445ced2690ef80a77ca0b30f087f814de43f..c8cd63b119aabbae7cff50627b82defdd1cdb30b 100644 --- a/channels/interface.go +++ b/channels/interface.go @@ -20,23 +20,25 @@ import ( "gitlab.com/xx_network/primitives/id/ephemeral" ) -// 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. +// 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. var ValidForever = time.Duration(math.MaxInt64) +// Manager provides an interface to manager channels. type Manager interface { - - // GetIdentity returns the public identity associated with this channel manager + // 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 which this manager is store for loading - // it is derived from the public key + // 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 @@ -48,44 +50,52 @@ type Manager interface { 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 which 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 isn't - // 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 - // Them meaning of validUntil depends on the use case. + // 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 which 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. + // 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 isn't - // 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 + // + // 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 + // 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 isn't - // 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 the reply is sent to doesnt exist, the other side will - // post the message as a normal message and not a reply. + // + // 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 + // 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) @@ -106,8 +116,8 @@ type Manager interface { // // 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 + 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. @@ -120,17 +130,17 @@ type Manager interface { // 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 + // 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, ch *id.ID) error + // 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(ch *id.ID) error + DeleteNickname(chID *id.ID) error - // GetNickname returns the nickname for the given channel if it exists. - GetNickname(ch *id.ID) (nickname string, exists bool) + // GetNickname returns the nickname for the given channel, if it exists. + GetNickname(chID *id.ID) (nickname string, exists bool) } diff --git a/channels/joinedChannel.go b/channels/joinedChannel.go index 1b6aa53e7f9b2539b983c418eab52368d7c129c0..60ea54463bbeda09ccacdd0337e58a3af68e0a9d 100644 --- a/channels/joinedChannel.go +++ b/channels/joinedChannel.go @@ -211,7 +211,8 @@ func (jc *joinedChannel) Store(kv *versioned.KV) error { // loadJoinedChannel loads a given channel from ekv storage. func loadJoinedChannel(chId *id.ID, kv *versioned.KV, net broadcast.Client, rngGen *fastRNG.StreamGenerator, e *events, - broadcastMaker broadcast.NewBroadcastChannelFunc, mr messageReceiveFunc) (*joinedChannel, error) { + broadcastMaker broadcast.NewBroadcastChannelFunc, mr messageReceiveFunc) ( + *joinedChannel, error) { obj, err := kv.Get(makeJoinedChannelKey(chId), joinedChannelVersion) if err != nil { return nil, err @@ -240,10 +241,10 @@ func makeJoinedChannelKey(chId *id.ID) string { return joinedChannelKey + chId.HexEncode() } -func initBroadcast(c *cryptoBroadcast.Channel, - e *events, net broadcast.Client, +func initBroadcast(c *cryptoBroadcast.Channel, e *events, net broadcast.Client, broadcastMaker broadcast.NewBroadcastChannelFunc, - rngGen *fastRNG.StreamGenerator, mr messageReceiveFunc) (broadcast.Channel, error) { + rngGen *fastRNG.StreamGenerator, mr messageReceiveFunc) ( + broadcast.Channel, error) { b, err := broadcastMaker(c, net, rngGen) if err != nil { return nil, err diff --git a/channels/joinedChannel_test.go b/channels/joinedChannel_test.go index 5b77b6a0114cfcf715e98b9cb071ef746efb5dd1..4d73a365a8f7b43a52215464df9943ef069d15c5 100644 --- a/channels/joinedChannel_test.go +++ b/channels/joinedChannel_test.go @@ -103,7 +103,8 @@ func Test_manager_loadChannels(t *testing.T) { for i := range expected { ch, _, err := newTestChannel( - "name_"+strconv.Itoa(i), "description_"+strconv.Itoa(i), m.rng.GetStream(), cryptoBroadcast.Public) + "name_"+strconv.Itoa(i), "description_"+strconv.Itoa(i), + m.rng.GetStream(), cryptoBroadcast.Public) if err != nil { t.Errorf("Failed to create new channel %d: %+v", i, err) } @@ -423,7 +424,8 @@ func Test_manager_getChannels(t *testing.T) { for i := range expected { ch, _, err := newTestChannel( - "name_"+strconv.Itoa(i), "description_"+strconv.Itoa(i), m.rng.GetStream(), cryptoBroadcast.Public) + "name_"+strconv.Itoa(i), "description_"+strconv.Itoa(i), + m.rng.GetStream(), cryptoBroadcast.Public) if err != nil { t.Errorf("Failed to create new channel %d: %+v", i, err) } @@ -511,9 +513,8 @@ func Test_loadJoinedChannel(t *testing.T) { } loadedJc, err := loadJoinedChannel(ch.ReceptionID, m.kv, m.net, m.rng, - m.events, m.broadcastMaker, func(messageID cryptoChannel.MessageID, r rounds.Round) bool { - return false - }) + m.events, m.broadcastMaker, + func(cryptoChannel.MessageID, rounds.Round) bool { return false }) if err != nil { t.Errorf("Failed to load joinedChannel: %+v", err) } @@ -618,13 +619,12 @@ func (m *mockBroadcastClient) SendWithAssembler(*id.ID, return rounds.Round{ID: id.Round(567)}, ephemeral.Id{}, nil } -func (m *mockBroadcastClient) IsHealthy() bool { return true } -func (m *mockBroadcastClient) AddIdentity(*id.ID, time.Time, bool) {} -func (m *mockBroadcastClient) AddIdentityWithHistory(id *id.ID, validUntil, beginning time.Time, persistent bool) { -} -func (m *mockBroadcastClient) AddService(*id.ID, message.Service, message.Processor) {} -func (m *mockBroadcastClient) DeleteClientService(*id.ID) {} -func (m *mockBroadcastClient) RemoveIdentity(*id.ID) {} +func (m *mockBroadcastClient) IsHealthy() bool { return true } +func (m *mockBroadcastClient) AddIdentity(*id.ID, time.Time, bool) {} +func (m *mockBroadcastClient) AddIdentityWithHistory(*id.ID, time.Time, time.Time, bool) {} +func (m *mockBroadcastClient) AddService(*id.ID, message.Service, message.Processor) {} +func (m *mockBroadcastClient) DeleteClientService(*id.ID) {} +func (m *mockBroadcastClient) RemoveIdentity(*id.ID) {} func (m *mockBroadcastClient) GetRoundResults(time.Duration, clientCmix.RoundEventCallback, ...id.Round) { } func (m *mockBroadcastClient) AddHealthCallback(func(bool)) uint64 { return 0 } @@ -667,6 +667,5 @@ func (m *mockEventModel) ReceiveReaction(*id.ID, cryptoChannel.MessageID, func (m *mockEventModel) UpdateSentStatus(uint64, cryptoChannel.MessageID, time.Time, rounds.Round, SentStatus) { - // TODO implement me panic("implement me") } diff --git a/channels/manager.go b/channels/manager.go index 56926aa1a474c992b48eae10cccedb9a1fd3bb6b..48505d51d917976186a1991d35fca1603ff4342f 100644 --- a/channels/manager.go +++ b/channels/manager.go @@ -67,7 +67,8 @@ type Client interface { 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) + AddIdentityWithHistory( + id *id.ID, validUntil, beginning time.Time, persistent bool) AddService(clientID *id.ID, newService message.Service, response message.Processor) DeleteClientService(clientID *id.ID) @@ -194,7 +195,8 @@ func (m *manager) GetChannels() []*id.ID { return m.getChannelsUnsafe() } -// GetChannel returns the underlying cryptographic structure for a given channel. +// GetChannel returns the underlying cryptographic structure for a given +// channel. func (m *manager) GetChannel(chID *id.ID) (*cryptoBroadcast.Channel, error) { jww.INFO.Printf("GetChannel(%s)", chID) jc, err := m.getChannel(chID) @@ -207,9 +209,9 @@ func (m *manager) 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 +// 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. func (m *manager) ReplayChannel(chID *id.ID) error { jww.INFO.Printf("ReplayChannel(%s)", chID) m.mux.RLock() @@ -238,13 +240,7 @@ func (m *manager) ReplayChannel(chID *id.ID) error { } -// GetStorageTag returns the tag at which this manager is store for loading -// it is derived from the public key -func (m *manager) GetStorageTag() string { - return getStorageTag(m.me.PubKey) -} - -// GetIdentity returns the public identity associated with this channel manager +// GetIdentity returns the public identity associated with this channel manager. func (m *manager) GetIdentity() cryptoChannel.Identity { return m.me.Identity } @@ -258,6 +254,13 @@ func (m *manager) ExportPrivateIdentity(password string) ([]byte, error) { return m.me.Export(password, rng) } +// 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. +func (m *manager) GetStorageTag() string { + return getStorageTag(m.me.PubKey) +} + +// getStorageTag generates a storage tag from an Ed25519 public key. func getStorageTag(pub ed25519.PublicKey) string { return fmt.Sprintf(storageTagFormat, base64.StdEncoding.EncodeToString(pub)) } diff --git a/channels/manager_test.go b/channels/manager_test.go index e4213bd7c369143fec41c60895150bf79e0ea3a6..0242ea41a2281dc971cb6102958a3be74d7573b5 100644 --- a/channels/manager_test.go +++ b/channels/manager_test.go @@ -67,12 +67,11 @@ func TestManager_JoinChannel(t *testing.T) { t.Errorf("Channel %s not added to channel map.", ch.Name) } - //wait because the event model is called in another thread + // Wait because the event model is called in another thread time.Sleep(1 * time.Second) if mem.joinedCh == nil { - t.Errorf("the channel join call was not propogated to the event " + - "model") + t.Errorf("the channel join call was not propogated to the event model") } } @@ -116,7 +115,7 @@ func TestManager_LeaveChannel(t *testing.T) { t.Errorf("Channel %s still in map.", ch.Name) } - //wait because the event model is called in another thread + // Wait because the event model is called in another thread time.Sleep(1 * time.Second) if mem.leftCh == nil { @@ -133,12 +132,12 @@ func TestManager_GetChannels(t *testing.T) { rng := fastRNG.NewStreamGenerator(1, 1, csprng.NewSystemRNG) - numtests := 10 + n := 10 chList := make(map[id.ID]interface{}) for i := 0; i < 10; i++ { - name := fmt.Sprintf("testChannel_%d", numtests) + name := fmt.Sprintf("testChannel_%d", n) s := rng.GetStream() tc, _, err := newTestChannel(name, "blarg", s, broadcast2.Public) s.Close() @@ -170,12 +169,12 @@ func TestManager_GetChannel(t *testing.T) { rng := fastRNG.NewStreamGenerator(1, 1, csprng.NewSystemRNG) - numtests := 10 + n := 10 - chList := make([]*id.ID, 0, numtests) + chList := make([]*id.ID, 0, n) for i := 0; i < 10; i++ { - name := fmt.Sprintf("testChannel_%d", numtests) + name := fmt.Sprintf("testChannel_%d", n) s := rng.GetStream() tc, _, err := newTestChannel(name, "blarg", s, broadcast2.Public) s.Close() @@ -206,9 +205,9 @@ func TestManager_GetChannel_BadChannel(t *testing.T) { mux: sync.RWMutex{}, } - numtests := 10 + n := 10 - chList := make([]*id.ID, 0, numtests) + chList := make([]*id.ID, 0, n) for i := 0; i < 10; i++ { chId := &id.ID{} @@ -219,7 +218,7 @@ func TestManager_GetChannel_BadChannel(t *testing.T) { for i, receivedCh := range chList { _, err := m.GetChannel(receivedCh) if err == nil { - t.Errorf("Channel %d returned when it doesnt exist", i) + t.Errorf("Channel %d returned when it does not exist", i) } } } diff --git a/channels/messageTypes.go b/channels/messageTypes.go index 5ac6e2de3c914131655e484599171c5ac6114d90..e0ae79a0c09c91ac00489496ff99a257d881ed27 100644 --- a/channels/messageTypes.go +++ b/channels/messageTypes.go @@ -7,16 +7,28 @@ package channels -import "fmt" +import ( + "strconv" +) +// MessageType is the type of message being sent to a channel. type MessageType uint32 const ( - Text = MessageType(1) - AdminText = MessageType(2) - Reaction = MessageType(3) + // 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 ) +// String returns a human-readable version of [MessageType], used for debugging +// and logging. This function adheres to the [fmt.Stringer] interface. func (mt MessageType) String() string { switch mt { case Text: @@ -26,6 +38,6 @@ func (mt MessageType) String() string { case Reaction: return "Reaction" default: - return fmt.Sprintf("Unknown messageType %d", mt) + return "Unknown messageType " + strconv.Itoa(int(mt)) } } diff --git a/channels/messages.go b/channels/messages.go index 0162c3c62f62c10c7c652d206e405e7a2383327b..c57ba731276fd989712e3c267d0e76006fc30b8c 100644 --- a/channels/messages.go +++ b/channels/messages.go @@ -20,7 +20,8 @@ type userMessageInternal struct { messageID channel.MessageID } -func newUserMessageInternal(ursMsg *UserMessage, chid *id.ID) (*userMessageInternal, error) { +func newUserMessageInternal( + ursMsg *UserMessage, chID *id.ID) (*userMessageInternal, error) { chanMessage := &ChannelMessage{} err := proto.Unmarshal(ursMsg.Message, chanMessage) if err != nil { @@ -31,11 +32,12 @@ func newUserMessageInternal(ursMsg *UserMessage, chid *id.ID) (*userMessageInter return &userMessageInternal{ userMessage: ursMsg, channelMessage: channelMessage, - messageID: channel.MakeMessageID(ursMsg.Message, chid), + messageID: channel.MakeMessageID(ursMsg.Message, chID), }, nil } -func unmarshalUserMessageInternal(usrMsg []byte, chid *id.ID) (*userMessageInternal, error) { +func unmarshalUserMessageInternal( + usrMsg []byte, chID *id.ID) (*userMessageInternal, error) { um := &UserMessage{} if err := proto.Unmarshal(usrMsg, um); err != nil { @@ -53,7 +55,7 @@ func unmarshalUserMessageInternal(usrMsg []byte, chid *id.ID) (*userMessageInter return &userMessageInternal{ userMessage: um, channelMessage: channelMessage, - messageID: channel.MakeMessageID(um.Message, chid), + messageID: channel.MakeMessageID(um.Message, chID), }, nil } diff --git a/channels/messages_test.go b/channels/messages_test.go index b5109bd30a535156edc2e80556b9db51d55313e4..2f99e6e851700e6055d0e2130e9832d39f18acfd 100644 --- a/channels/messages_test.go +++ b/channels/messages_test.go @@ -120,10 +120,10 @@ func TestUserMessageInternal_GetMessageID(t *testing.T) { } } -// Ensures the serialization hasn't changed, changing the message IDs. The -// protocol is tolerant of this because only the sender seralizes, but -// it would be good to know when this changes. If this test breaks, report it, -// but it should be safe to update the expected +// Ensures the serialization has not changed, changing the message IDs. The +// protocol is tolerant of this because only the sender serializes, but it would +// be good to know when this changes. If this test breaks, report it, but it +// should be safe to update the expected. func TestUserMessageInternal_GetMessageID_Consistency(t *testing.T) { expected := "ChMsgID-LrGYLFCaPamZk44X+c/b08qtmJIorgNnoE68v1HYrf8=" @@ -138,7 +138,8 @@ func TestUserMessageInternal_GetMessageID_Consistency(t *testing.T) { } } -func builtTestUMI(t *testing.T, mt MessageType) (*userMessageInternal, *UserMessage, *ChannelMessage) { +func builtTestUMI(t *testing.T, mt MessageType) ( + *userMessageInternal, *UserMessage, *ChannelMessage) { channelMsg := &ChannelMessage{ Lease: 69, RoundID: 42, diff --git a/channels/mutateTimestamp.go b/channels/mutateTimestamp.go index 791bfc1601d840e6dfebb70aadb95b036ee5da98..a91c2e2e9b49dfceb97c353c5b5b5686cb73d0be 100644 --- a/channels/mutateTimestamp.go +++ b/channels/mutateTimestamp.go @@ -14,8 +14,8 @@ import ( ) const ( - // tenMsInNs is a prime close to one million to ensure patterns dont - // arise due to cofactors with the message ID when doing the modulo + // tenMsInNs is a prime close to one million to ensure that patterns do not + // arise due to cofactors with the message ID when doing the modulo. tenMsInNs = 10000019 halfTenMsInNs = tenMsInNs / 2 beforeGrace = 25 * time.Second @@ -24,10 +24,10 @@ const ( var tenMsInNsLargeInt = large.NewInt(tenMsInNs) -// vetTimestamp determines which timestamp to use for a message. It will -// use the local timestamp provided in the message as long as it is within 25 -// seconds before the round and 2 second after the round. Otherwise, it will -// use the round timestamp via mutateTimestamp +// vetTimestamp determines which timestamp to use for a message. It will use the +// local timestamp provided in the message as long as it is within 25 seconds +// before the round and 2 second after the round. Otherwise, it will use the +// round timestamp via mutateTimestamp. func vetTimestamp(localTS, ts time.Time, msgID channel.MessageID) time.Time { before := ts.Add(-beforeGrace) @@ -40,17 +40,18 @@ func vetTimestamp(localTS, ts time.Time, msgID channel.MessageID) time.Time { return localTS } -// mutateTimestamp is used to modify the the timestamps on all messages in a +// mutateTimestamp is used to modify the timestamps on all messages in a // deterministic manner. This is because message ordering is done by timestamp // and the timestamps come from the rounds, which means multiple messages can // have the same timestamp due to being in the same round. The meaning of // conversations can change depending on order, so while no explicit order // can be discovered because to do so can leak potential ordering info for the // mix, choosing an arbitrary order and having all clients agree will at least -// ensure that misunderstandings due to disagreements in order cannot occur +// ensure that misunderstandings due to disagreements in order cannot occur. // // In order to do this, this function mutates the timestamp of the round within // +/- 5ms seeded based upon the message ID. +// // It should be noted that this is only a reasonable assumption when the number // of messages in a channel isn't too much. For example, under these conditions // the birthday paradox of getting a collision if there are 10 messages for the @@ -59,7 +60,7 @@ func vetTimestamp(localTS, ts time.Time, msgID channel.MessageID) time.Time { // channel (1000 messages), .0487. func mutateTimestamp(ts time.Time, msgID channel.MessageID) time.Time { - // Treat the message ID as a number and mod it by the number of ns in an ms + // Treat the message ID as a number and mod it by the number of ns in a ms // to get an offset factor. Use a prime close to 1000000 to make sure there // are no patterns in the output and reduce the chance of collision. While // the fields do not align, so there is some bias towards some parts of the diff --git a/channels/mutateTimestamp_test.go b/channels/mutateTimestamp_test.go index 93561868b10ac781ef2c86cc0b483a0fed1815f8..e328e6a4d6946df309b3989f3df8d588328227e1 100644 --- a/channels/mutateTimestamp_test.go +++ b/channels/mutateTimestamp_test.go @@ -56,8 +56,8 @@ func TestMutateTimestampDeltaAverage(t *testing.T) { const generationRange = beforeGrace + afterGrace -// TestVetTimestamp_Happy tests that when the localTS is within -// the allowed range, it is unmodified +// TestVetTimestamp_Happy tests that when the localTS is within the allowed +// range, it is unmodified. func TestVetTimestamp_Happy(t *testing.T) { samples := 10000 @@ -67,7 +67,8 @@ func TestVetTimestamp_Happy(t *testing.T) { now := time.Now() - tested := now.Add(-beforeGrace).Add(time.Duration(rng.Int63()) % generationRange) + tested := now.Add(-beforeGrace).Add( + time.Duration(rng.Int63()) % generationRange) var msgID channel.MessageID rng.Read(msgID[:]) @@ -80,8 +81,8 @@ func TestVetTimestamp_Happy(t *testing.T) { } } -// TestVetTimestamp_Happy tests that when the localTS is less than -// the allowed time period it is replaced +// TestVetTimestamp_Happy tests that when the localTS is less than the allowed +// time period it is replaced. func TestVetTimestamp_BeforePeriod(t *testing.T) { samples := 10000 @@ -91,7 +92,8 @@ func TestVetTimestamp_BeforePeriod(t *testing.T) { now := time.Now() - tested := now.Add(-beforeGrace).Add(-time.Duration(rng.Int63()) % (100000 * time.Hour)) + tested := now.Add(-beforeGrace).Add( + -time.Duration(rng.Int63()) % (100000 * time.Hour)) var msgID channel.MessageID rng.Read(msgID[:]) @@ -104,8 +106,8 @@ func TestVetTimestamp_BeforePeriod(t *testing.T) { } } -// TestVetTimestamp_Happy tests that when the localTS is greater than -// the allowed time period it is replaced +// TestVetTimestamp_Happy tests that when the localTS is greater than the +// allowed time period it is replaced func TestVetTimestamp_AfterPeriod(t *testing.T) { samples := 10000 @@ -115,7 +117,8 @@ func TestVetTimestamp_AfterPeriod(t *testing.T) { now := time.Now() - tested := now.Add(afterGrace).Add(-time.Duration(rng.Int63()) % (100000 * time.Hour)) + tested := now.Add(afterGrace).Add( + -time.Duration(rng.Int63()) % (100000 * time.Hour)) var msgID channel.MessageID rng.Read(msgID[:]) diff --git a/channels/nameService.go b/channels/nameService.go index d77fa9414a4f0fe1d8a32fae130936187f2fcf92..23714e4d1166fc869ca0596a4576027980e9d0a8 100644 --- a/channels/nameService.go +++ b/channels/nameService.go @@ -12,27 +12,26 @@ import ( "time" ) -// NameService is an interface which encapsulates -// the user identity channel tracking service. -// NameService is currently unused +// NameService is an interface which encapsulates the user identity channel +// tracking service. +// +// NameService is currently unused. type NameService interface { - // GetUsername returns the username. GetUsername() string - // GetChannelValidationSignature returns the validation - // signature and the time it was signed. + // 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 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 + // username lease is signed by the NameService. ValidateChannelMessage(username string, lease time.Time, pubKey ed25519.PublicKey, authorIDSignature []byte) bool } diff --git a/channels/nickname.go b/channels/nickname.go index 5116b597f754c09ce1b6bae06ec878c5192debfe..fda7b7aa504dae2b80dddad1e765b73f5897be1b 100644 --- a/channels/nickname.go +++ b/channels/nickname.go @@ -23,8 +23,8 @@ type nicknameManager struct { kv *versioned.KV } -// loadOrNewNicknameManager returns the stored nickname manager if there is -// one or returns a new one +// loadOrNewNicknameManager returns the stored nickname manager if there is one +// or returns a new one. func loadOrNewNicknameManager(kv *versioned.KV) *nicknameManager { nm := &nicknameManager{ byChannel: make(map[id.ID]string), @@ -39,19 +39,19 @@ func loadOrNewNicknameManager(kv *versioned.KV) *nicknameManager { } -// GetNickname returns the nickname for the given channel if it exists -func (nm *nicknameManager) GetNickname(ch *id.ID) ( +// GetNickname returns the nickname for the given channel if it exists. +func (nm *nicknameManager) GetNickname(chID *id.ID) ( nickname string, exists bool) { nm.mux.RLock() defer nm.mux.RUnlock() - nickname, exists = nm.byChannel[*ch] + nickname, exists = nm.byChannel[*chID] return } // SetNickname sets the nickname for a channel after checking that the nickname -// is valid using IsNicknameValid -func (nm *nicknameManager) SetNickname(newNick string, ch *id.ID) error { +// is valid using [IsNicknameValid]. +func (nm *nicknameManager) SetNickname(newNick string, chID *id.ID) error { nm.mux.Lock() defer nm.mux.Unlock() @@ -59,17 +59,17 @@ func (nm *nicknameManager) SetNickname(newNick string, ch *id.ID) error { return err } - nm.byChannel[*ch] = newNick + nm.byChannel[*chID] = newNick return nm.save() } // DeleteNickname removes the nickname for a given channel, using the codename -// for that channel instead -func (nm *nicknameManager) DeleteNickname(ch *id.ID) error { +// for that channel instead. +func (nm *nicknameManager) DeleteNickname(chID *id.ID) error { nm.mux.Lock() defer nm.mux.Unlock() - delete(nm.byChannel, *ch) + delete(nm.byChannel, *chID) return nm.save() } @@ -126,12 +126,13 @@ func (nm *nicknameManager) load() error { return 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 -// todo: add character filtering +// Rules: +// - A nickname must not be longer than 24 characters. +// - A nickname must not be shorter than 1 character. +// +// TODO: Add character filtering. func IsNicknameValid(nick string) error { runeNick := []rune(nick) if len(runeNick) > 24 { diff --git a/channels/nickname_test.go b/channels/nickname_test.go index cf535245f82bb1e4b5c4caf5ac0b65f6355b4653..c49149636f330d22c605f439f3a9ce1a27bc20b4 100644 --- a/channels/nickname_test.go +++ b/channels/nickname_test.go @@ -56,7 +56,8 @@ func TestNicknameManager_SetGetNickname_Reload(t *testing.T) { } expected := "nickname#" + strconv.Itoa(i) if nick != expected { - t.Fatalf("Nickname %d not found, expected: %s, received: %s ", i, expected, nick) + t.Fatalf("Nickname %d not found, expected: %s, received: %s ", + i, expected, nick) } } } diff --git a/channels/send.go b/channels/send.go index 0e508d4cc6d6c952554d90849541ba6514f46463..44b1b38a624b20d9e30cd1666fb69b843b3237ba 100644 --- a/channels/send.go +++ b/channels/send.go @@ -29,20 +29,30 @@ import ( const ( cmixChannelTextVersion = 0 cmixChannelReactionVersion = 0 - SendMessageTag = "ChMessage" - SendReplyTag = "ChReply" - SendReactionTag = "ChReaction" + + // 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" ) // The size of the nonce used in the message ID. const messageNonceSize = 4 // SendGeneric is used to send a raw message over a channel. In general, it -// should be wrapped in a function which defines the wire protocol +// 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 isn't -// 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 +// 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. func (m *manager) SendGeneric(channelID *id.ID, messageType MessageType, msg []byte, validUntil time.Duration, params cmix.CMIXParams) ( cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error) { @@ -50,15 +60,13 @@ func (m *manager) SendGeneric(channelID *id.ID, messageType MessageType, // Note: We log sends on exit, and append what happened to the message // this cuts down on clutter in the log. sendPrint := fmt.Sprintf("[%s] Sending ch %s type %d at %s", - params.DebugTag, channelID, messageType, - netTime.Now()) + params.DebugTag, channelID, messageType, netTime.Now()) defer jww.INFO.Println(sendPrint) - //find the channel + // Find the channel ch, err := m.getChannel(channelID) if err != nil { - return cryptoChannel.MessageID{}, rounds.Round{}, - ephemeral.Id{}, err + return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, err } nickname, _ := m.GetNickname(channelID) @@ -74,56 +82,52 @@ func (m *manager) SendGeneric(channelID *id.ID, messageType MessageType, LocalTimestamp: netTime.Now().UnixNano(), } - // Generate random nonce to be used for message ID - // generation. This makes it so two identical messages sent on - // the same round have different message IDs + // Generate random nonce to be used for message ID generation. This makes it + // so two identical messages sent on the same round have different message + // IDs. rng := m.rng.GetStream() n, err := rng.Read(chMsg.Nonce) rng.Close() if err != nil { sendPrint += fmt.Sprintf(", failed to generate nonce: %+v", err) - return cryptoChannel.MessageID{}, rounds.Round{}, - ephemeral.Id{}, + return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, errors.Errorf("Failed to generate nonce: %+v", err) } else if n != messageNonceSize { - sendPrint += fmt.Sprintf(", got %d bytes for %d-byte nonce", n, - messageNonceSize) - return cryptoChannel.MessageID{}, rounds.Round{}, - ephemeral.Id{}, + sendPrint += fmt.Sprintf( + ", got %d bytes for %d-byte nonce", n, messageNonceSize) + return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, errors.Errorf( - "Generated %d bytes for %d-byte nonce", n, - messageNonceSize) + "Generated %d bytes for %d-byte nonce", n, messageNonceSize) } usrMsg := &UserMessage{ ECCPublicKey: m.me.PubKey, } - //Note: we are not checking if message is too long before trying to - //find a round + // Note: we are not checking if message is too long before trying to find a + // round - //Build the function pointer that will build the message + // Build the function pointer that will build the message assemble := func(rid id.Round) ([]byte, error) { - - //Build the message + // Build the message chMsg.RoundID = uint64(rid) - //Serialize the message + // Serialize the message chMsgSerial, err := proto.Marshal(chMsg) if err != nil { return nil, err } - //make the messageID + // Make the messageID msgId = cryptoChannel.MakeMessageID(chMsgSerial, channelID) - //Sign the message + // Sign the message messageSig := ed25519.Sign(*m.me.Privkey, chMsgSerial) usrMsg.Message = chMsgSerial usrMsg.Signature = messageSig - //Serialize the user message + // Serialize the user message usrMsgSerial, err := proto.Marshal(usrMsg) if err != nil { return nil, err @@ -139,37 +143,37 @@ func (m *manager) SendGeneric(channelID *id.ID, messageType MessageType, messageID: msgId, }) if err != nil { - sendPrint += fmt.Sprintf(", pending send failed %s", - err.Error()) + sendPrint += fmt.Sprintf(", pending send failed %s", err.Error()) return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, err } sendPrint += fmt.Sprintf(", broadcasting message %s", netTime.Now()) - r, ephid, err := ch.broadcast.BroadcastWithAssembler(assemble, params) + r, ephID, err := ch.broadcast.BroadcastWithAssembler(assemble, params) if err != nil { - sendPrint += fmt.Sprintf(", broadcast failed %s, %s", - netTime.Now(), err.Error()) + sendPrint += fmt.Sprintf( + ", broadcast failed %s, %s", netTime.Now(), err.Error()) errDenote := m.st.failedSend(uuid) if errDenote != nil { - sendPrint += fmt.Sprintf(", failed to denote failed "+ - "broadcast: %s", err.Error()) + sendPrint += fmt.Sprintf( + ", failed to denote failed broadcast: %s", err.Error()) } return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, err } - sendPrint += fmt.Sprintf(", broadcast succeeded %s, success!", - netTime.Now()) + sendPrint += fmt.Sprintf( + ", broadcast succeeded %s, success!", netTime.Now()) err = m.st.send(uuid, msgId, r) if err != nil { sendPrint += fmt.Sprintf(", broadcast failed: %s ", err.Error()) } - return msgId, r, ephid, err + return msgId, r, ephID, err } -// 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 which defines the wire protocol +// 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. func (m *manager) SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID, @@ -180,11 +184,10 @@ func (m *manager) SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID, // Note: We log sends on exit, and append what happened to the message // this cuts down on clutter in the log. sendPrint := fmt.Sprintf("[%s] Admin sending ch %s type %d at %s", - params.DebugTag, channelID, messageType, - netTime.Now()) + params.DebugTag, channelID, messageType, netTime.Now()) defer jww.INFO.Println(sendPrint) - //find the channel + // Find the channel ch, err := m.getChannel(channelID) if err != nil { return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, err @@ -201,7 +204,8 @@ func (m *manager) SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID, } // Generate random nonce to be used for message ID generation. This makes it - // so two identical messages sent on the same round have different message IDs + // so two identical messages sent on the same round have different message + // IDs rng := m.rng.GetStream() n, err := rng.Read(chMsg.Nonce) rng.Close() @@ -217,13 +221,12 @@ func (m *manager) SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID, // Note: we are not checking if message is too long before trying to // find a round - //Build the function pointer that will build the message + // Build the function pointer that will build the message assemble := func(rid id.Round) ([]byte, error) { - - //Build the message + // Build the message chMsg.RoundID = uint64(rid) - //Serialize the message + // Serialize the message chMsgSerial, err := proto.Marshal(chMsg) if err != nil { return nil, err @@ -231,7 +234,7 @@ func (m *manager) SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID, msgId = cryptoChannel.MakeMessageID(chMsgSerial, channelID) - //check if the message is too long + // Check if the message is too long if len(chMsgSerial) > ch.broadcast.MaxRSAToPublicPayloadSize() { return nil, MessageTooLongErr } @@ -242,39 +245,42 @@ func (m *manager) SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID, sendPrint += fmt.Sprintf(", pending send %s", netTime.Now()) uuid, err := m.st.denotePendingAdminSend(channelID, chMsg) if err != nil { - sendPrint += fmt.Sprintf(", pending send failed %s", - err.Error()) + sendPrint += fmt.Sprintf(", pending send failed %s", err.Error()) return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, err } sendPrint += fmt.Sprintf(", broadcasting message %s", netTime.Now()) - r, ephid, err := ch.broadcast.BroadcastRSAToPublicWithAssembler(privKey, + r, ephID, err := ch.broadcast.BroadcastRSAToPublicWithAssembler(privKey, assemble, params) if err != nil { - sendPrint += fmt.Sprintf(", broadcast failed %s, %s", - netTime.Now(), err.Error()) + sendPrint += fmt.Sprintf( + ", broadcast failed %s, %s", netTime.Now(), err.Error()) errDenote := m.st.failedSend(uuid) if errDenote != nil { - sendPrint += fmt.Sprintf(", failed to denote failed "+ - "broadcast: %s", err.Error()) - jww.ERROR.Printf("Failed to update for a failed send to "+ - "%s: %+v", channelID, err) + sendPrint += fmt.Sprintf( + ", failed to denote failed broadcast: %s", err.Error()) + jww.ERROR.Printf( + "Failed to update for a failed send to %s: %+v", channelID, err) } return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, err } - sendPrint += fmt.Sprintf(", broadcast succeeded %s, success!", - netTime.Now()) + sendPrint += fmt.Sprintf( + ", broadcast succeeded %s, success!", netTime.Now()) err = m.st.send(uuid, msgId, r) if err != nil { sendPrint += fmt.Sprintf(", broadcast failed: %s ", err.Error()) } - return msgId, r, ephid, err + return msgId, r, ephID, err } // SendMessage is used to send a formatted message over a channel. -// Due to the underlying encoding using compression, it isn't -// 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 +// +// 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. func (m *manager) SendMessage(channelID *id.ID, msg string, validUntil time.Duration, params cmix.CMIXParams) ( cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error) { @@ -294,20 +300,24 @@ func (m *manager) SendMessage(channelID *id.ID, msg string, return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, err } - return m.SendGeneric(channelID, Text, txtMarshaled, validUntil, - params) + return m.SendGeneric(channelID, Text, txtMarshaled, validUntil, params) } // SendReply is used to send a formatted message over a channel. -// Due to the underlying encoding using compression, it isn't -// 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 the reply is sent to doesnt exist, the other side will -// post the message as a normal message and not a reply. +// +// 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. func (m *manager) SendReply(channelID *id.ID, msg string, replyTo cryptoChannel.MessageID, validUntil time.Duration, - params cmix.CMIXParams) (cryptoChannel.MessageID, rounds.Round, - ephemeral.Id, error) { + params cmix.CMIXParams) ( + cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error) { tag := makeChaDebugTag(channelID, m.me.PubKey, []byte(msg), SendReplyTag) jww.INFO.Printf("[%s]SendReply(%s, to %s)", tag, channelID, replyTo) txt := &CMIXChannelText{ @@ -327,14 +337,16 @@ func (m *manager) SendReply(channelID *id.ID, msg string, params) } -// 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 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. func (m *manager) SendReaction(channelID *id.ID, reaction string, reactTo cryptoChannel.MessageID, params cmix.CMIXParams) ( cryptoChannel.MessageID, rounds.Round, ephemeral.Id, error) { - tag := makeChaDebugTag(channelID, m.me.PubKey, []byte(reaction), SendReactionTag) + tag := makeChaDebugTag( + channelID, m.me.PubKey, []byte(reaction), SendReactionTag) jww.INFO.Printf("[%s]SendReply(%s, to %s)", tag, channelID, reactTo) if err := ValidateReaction(reaction); err != nil { @@ -354,13 +366,14 @@ func (m *manager) SendReaction(channelID *id.ID, reaction string, return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, err } - return m.SendGeneric(channelID, Reaction, reactMarshaled, ValidForever, - params) + return m.SendGeneric( + channelID, Reaction, reactMarshaled, ValidForever, params) } -// makeChaDebugTag is a debug helper that creates non-unique msg identifier -// This is set as the debug tag on messages and enables some level -// of tracing a message (if it's contents/chan/type are unique) +// makeChaDebugTag is a debug helper that creates non-unique msg identifier. +// +// This is set as the debug tag on messages and enables some level of tracing a +// message (if its contents/chan/type are unique). func makeChaDebugTag(channelID *id.ID, id ed25519.PublicKey, msg []byte, baseTag string) string { @@ -369,6 +382,6 @@ func makeChaDebugTag(channelID *id.ID, id ed25519.PublicKey, h.Write(msg) h.Write(id) - tripcode := base64.RawStdEncoding.EncodeToString(h.Sum(nil))[:12] - return fmt.Sprintf("%s-%s", baseTag, tripcode) + tripCode := base64.RawStdEncoding.EncodeToString(h.Sum(nil))[:12] + return fmt.Sprintf("%s-%s", baseTag, tripCode) } diff --git a/channels/sendTracker.go b/channels/sendTracker.go index f75819fe2ccea74308cce60ee9087a3b1fc40320..b439291f0e8361b90b910788e0d74f25590fd6ba 100644 --- a/channels/sendTracker.go +++ b/channels/sendTracker.go @@ -32,9 +32,8 @@ const ( sendTrackerUnsentStorageVersion = 0 getRoundResultsTimeout = 60 * time.Second - // number of times it will attempt to get round status before the round - // is assumed to have failed. Tracking per round does not persist across - // runs + // Number of times it will attempt to get round status before the round is + // assumed to have failed. Tracking per round does not persist across runs maxChecks = 3 oneSecond = 1000 * time.Millisecond @@ -52,10 +51,10 @@ type trackedList struct { RoundCompleted bool } -// the sendTracker tracks outbound messages and denotes when they are delivered -// to the event model. It also captures incoming messages and in the event they +// sendTracker tracks outbound messages and denotes when they are delivered to +// the event model. It also captures incoming messages and in the event they // were sent by this user diverts them as status updates on the previously sent -// messages +// messages. type sendTracker struct { byRound map[id.Round]trackedList @@ -77,8 +76,9 @@ type sendTracker struct { } // messageReceiveFunc is a function type for sendTracker.MessageReceive so it -// can be mocked for testing where used -type messageReceiveFunc func(messageID cryptoChannel.MessageID, r rounds.Round) bool +// can be mocked for testing where used. +type messageReceiveFunc func( + messageID cryptoChannel.MessageID, r rounds.Round) bool // loadSendTracker loads a sent tracker, restoring from disk. It will register a // function with the cmix client, delayed on when the network goes healthy, @@ -103,22 +103,21 @@ func loadSendTracker(net Client, kv *versioned.KV, trigger triggerEventFunc, }*/ st.load() - //denote all unsent messages as failed and clear + // Denote all unsent messages as failed and clear for uuid, t := range st.unsent { - updateStatus(uuid, t.MsgID, - time.Time{}, rounds.Round{}, Failed) + updateStatus(uuid, t.MsgID, time.Time{}, rounds.Round{}, Failed) } st.unsent = make(map[uint64]*tracked) - //register to check all outstanding rounds when the network becomes healthy + // Register to check all outstanding rounds when the network becomes healthy var callBackID uint64 callBackID = net.AddHealthCallback(func(f bool) { if !f { return } + net.RemoveHealthCallback(callBackID) for rid, oldTracked := range st.byRound { - if oldTracked.RoundCompleted { continue } @@ -127,16 +126,16 @@ func loadSendTracker(net Client, kv *versioned.KV, trigger triggerEventFunc, round: rid, st: st, } - st.net.GetRoundResults(getRoundResultsTimeout, rr.callback, rr.round) + st.net.GetRoundResults( + getRoundResultsTimeout, rr.callback, rr.round) } }) return st } -// store writes the list of rounds that have been +// store writes the list of rounds that have been. func (st *sendTracker) store() error { - if err := st.storeSent(); err != nil { return err } @@ -145,8 +144,7 @@ func (st *sendTracker) store() error { } func (st *sendTracker) storeSent() error { - - //save sent messages + // Save sent messages data, err := json.Marshal(&st.byRound) if err != nil { return err @@ -158,9 +156,9 @@ func (st *sendTracker) storeSent() error { }) } -// store writes the list of rounds that have been +// store writes the list of rounds that have been. func (st *sendTracker) storeUnsent() error { - //save unsent messages + // Save unsent messages data, err := json.Marshal(&st.unsent) if err != nil { return err @@ -173,8 +171,8 @@ func (st *sendTracker) storeUnsent() error { }) } -// load will get the stored rounds to be checked from disk and builds -// internal datastructures +// load will get the stored rounds to be checked from disk and builds internal +// datastructures. func (st *sendTracker) load() error { obj, err := st.kv.Get(sendTrackerStorageKey, sendTrackerStorageVersion) if err != nil { @@ -193,7 +191,8 @@ func (st *sendTracker) load() error { } } - obj, err = st.kv.Get(sendTrackerUnsentStorageKey, sendTrackerUnsentStorageVersion) + obj, err = st.kv.Get( + sendTrackerUnsentStorageKey, sendTrackerUnsentStorageVersion) if err != nil { return err } @@ -207,78 +206,78 @@ func (st *sendTracker) load() error { } // denotePendingSend is called before the pending send. It tracks the send -// internally and notifies the UI of the send +// internally and notifies the UI of the send. func (st *sendTracker) denotePendingSend(channelID *id.ID, umi *userMessageInternal) (uint64, error) { - // for a timestamp for the message, use 1 second from now to - // approximate the lag due to round submission + // For the message timestamp, use 1 second from now to approximate the lag + // due to round submission ts := netTime.Now().Add(oneSecond) - // create a random message id so there will not be collisions in a database - // that requires a unique message ID + // Create a random message ID so that there won't be collisions in a + // database that requires a unique message ID stream := st.rngSrc.GetStream() umi.messageID = cryptoChannel.MessageID{} - num, err := stream.Read(umi.messageID[:]) - if num != len(umi.messageID[:]) || err != nil { - jww.FATAL.Panicf("failed to get a random message ID, read "+ - "len: %d, err: %+v", num, err) + n, err := stream.Read(umi.messageID[:]) + if err != nil { + jww.FATAL.Panicf("Failed to get generate random message ID: %+v", err) + } else if n != len(umi.messageID[:]) { + jww.FATAL.Panicf("Generated %d bytes for message ID; %d bytes required.", + n, len(umi.messageID[:])) } stream.Close() - // submit the message to the UI + // Submit the message to the UI uuid, err := st.trigger(channelID, umi, ts, receptionID.EphemeralIdentity{}, rounds.Round{}, Unsent) if err != nil { return 0, err } - // track the message on disk - st.handleDenoteSend(uuid, channelID, umi.messageID, - rounds.Round{}) + // Track the message on disk + st.handleDenoteSend(uuid, channelID, umi.messageID, rounds.Round{}) return uuid, nil } // denotePendingAdminSend is called before the pending admin send. It tracks the -// send internally and notifies the UI of the send +// send internally and notifies the UI of the send. func (st *sendTracker) denotePendingAdminSend(channelID *id.ID, cm *ChannelMessage) (uint64, error) { - // for a timestamp for the message, use 1 second from now to - // approximate the lag due to round submission + // For a timestamp for the message, use 1 second from now to approximate the + // lag due to round submission ts := netTime.Now().Add(oneSecond) - // create a random message id so there will not be collisions in a database + // Create a random message ID so there will not be collisions in a database // that requires a unique message ID stream := st.rngSrc.GetStream() randMid := cryptoChannel.MessageID{} num, err := stream.Read(randMid[:]) if num != len(randMid[:]) || err != nil { - jww.FATAL.Panicf("failed to get a random message ID, read "+ - "len: %d, err: %+v", num, err) + jww.FATAL.Panicf( + "Failed to get a random message ID, read len: %d, err: %+v", + num, err) } stream.Close() - // submit the message to the UI + // Submit the message to the UI uuid, err := st.adminTrigger(channelID, cm, ts, randMid, - receptionID.EphemeralIdentity{}, - rounds.Round{}, Unsent) + receptionID.EphemeralIdentity{}, rounds.Round{}, Unsent) if err != nil { return 0, err } - // track the message on disk - st.handleDenoteSend(uuid, channelID, randMid, - rounds.Round{}) + // Track the message on disk + st.handleDenoteSend(uuid, channelID, randMid, rounds.Round{}) return uuid, nil } -// handleDenoteSend does the nity gritty of editing internal structures +// handleDenoteSend does the nitty-gritty of editing internal structures. func (st *sendTracker) handleDenoteSend(uuid uint64, channelID *id.ID, messageID cryptoChannel.MessageID, round rounds.Round) { st.mux.Lock() defer st.mux.Unlock() - //skip if already added + // Skip if already added _, existsMessage := st.unsent[uuid] if existsMessage { return @@ -292,11 +291,10 @@ func (st *sendTracker) handleDenoteSend(uuid uint64, channelID *id.ID, } } -// send tracks a generic send message -func (st *sendTracker) send(uuid uint64, msgID cryptoChannel.MessageID, - round rounds.Round) error { - - // update the on disk message status +// send tracks a generic send message. +func (st *sendTracker) send( + uuid uint64, msgID cryptoChannel.MessageID, round rounds.Round) error { + // Update the on disk message status t, err := st.handleSend(uuid, msgID, round) if err != nil { return err @@ -305,32 +303,32 @@ func (st *sendTracker) send(uuid uint64, msgID cryptoChannel.MessageID, // Modify the timestamp to reduce the chance message order will be ambiguous ts := mutateTimestamp(round.Timestamps[states.QUEUED], msgID) - //update the message on the UI + // Update the message in the UI go st.updateStatus(t.UUID, msgID, ts, round, Sent) return nil } -// send tracks a generic send message +// send tracks a generic send message. func (st *sendTracker) failedSend(uuid uint64) error { - - // update the on disk message status + // Update the on disk message status t, err := st.handleSendFailed(uuid) if err != nil { return err } - //update the message on the UI - go st.updateStatus(t.UUID, cryptoChannel.MessageID{}, time.Time{}, rounds.Round{}, Failed) + // Update the message in the UI + go st.updateStatus( + t.UUID, cryptoChannel.MessageID{}, time.Time{}, rounds.Round{}, Failed) return nil } -// handleSend does the nity gritty of editing internal structures +// handleSend does the nitty-gritty of editing internal structures. func (st *sendTracker) handleSend(uuid uint64, messageID cryptoChannel.MessageID, round rounds.Round) (*tracked, error) { st.mux.Lock() defer st.mux.Unlock() - //check if in unsent + // Check if it is in unsent t, exists := st.unsent[uuid] if !exists { return nil, errors.New("cannot handle send on an unprepared message") @@ -338,19 +336,19 @@ func (st *sendTracker) handleSend(uuid uint64, _, existsMessage := st.byMessageID[messageID] if existsMessage { - return nil, errors.New("cannot handle send on a message which was " + - "already sent") + return nil, + errors.New("cannot handle send on a message which was already sent") } t.MsgID = messageID t.RoundID = round.ID - //add the roundID + // Add the roundID roundsList, existsRound := st.byRound[round.ID] roundsList.List = append(roundsList.List, t) st.byRound[round.ID] = roundsList - //add the round + // Add the round st.byMessageID[messageID] = t if !existsRound { @@ -363,7 +361,7 @@ func (st *sendTracker) handleSend(uuid uint64, delete(st.unsent, uuid) - //store the changed list to disk + // Store the changed list to disk err := st.store() if err != nil { jww.FATAL.Panicf(err.Error()) @@ -372,12 +370,12 @@ func (st *sendTracker) handleSend(uuid uint64, return t, nil } -// handleSendFailed does the nity gritty of editing internal structures +// handleSendFailed does the nitty-gritty of editing internal structures. func (st *sendTracker) handleSendFailed(uuid uint64) (*tracked, error) { st.mux.Lock() defer st.mux.Unlock() - //check if in unsent + // Check if it is in unsent t, exists := st.unsent[uuid] if !exists { return nil, errors.New("cannot handle send on an unprepared message") @@ -385,7 +383,7 @@ func (st *sendTracker) handleSendFailed(uuid uint64) (*tracked, error) { delete(st.unsent, uuid) - //store the changed list to disk + // Store the changed list to disk err := st.storeUnsent() if err != nil { jww.FATAL.Panicf(err.Error()) @@ -394,14 +392,15 @@ func (st *sendTracker) handleSendFailed(uuid uint64) (*tracked, error) { return t, nil } -// MessageReceive is used when a message is received to check if the message -// was sent by this user. If it was, the correct signal is sent to the event -// model and the function returns true, notifying the caller to not process -// the message -func (st *sendTracker) MessageReceive(messageID cryptoChannel.MessageID, round rounds.Round) bool { +// MessageReceive is used when a message is received to check if the message was +// sent by this user. If it was, the correct signal is sent to the event model +// and the function returns true, notifying the caller to not process the +// message. +func (st *sendTracker) MessageReceive( + messageID cryptoChannel.MessageID, round rounds.Round) bool { st.mux.RLock() - //skip if already added + // Skip if already added _, existsMessage := st.byMessageID[messageID] st.mux.RUnlock() if !existsMessage { @@ -444,7 +443,8 @@ func (st *sendTracker) MessageReceive(messageID cryptoChannel.MessageID, round r return true } -// roundResults represents a round which results are waiting on from the cmix layer +// roundResults represents a round which results are waiting on from the cMix +// layer. type roundResults struct { round id.Round st *sendTracker @@ -453,11 +453,11 @@ type roundResults struct { // callback is called when results are known about a round. it will re-trigger // the wait if it fails up to 'maxChecks' times. -func (rr *roundResults) callback(allRoundsSucceeded, timedOut bool, results map[id.Round]cmix.RoundResult) { - +func (rr *roundResults) callback( + allRoundsSucceeded, timedOut bool, results map[id.Round]cmix.RoundResult) { rr.st.mux.Lock() - //if the message was already handled, do nothing + // If the message was already handled, then do nothing registered, existsRound := rr.st.byRound[rr.round] if !existsRound { rr.st.mux.Unlock() @@ -480,8 +480,9 @@ func (rr *roundResults) callback(allRoundsSucceeded, timedOut bool, results map[ rr.st.mux.Unlock() - //retry if timed out - go rr.st.net.GetRoundResults(getRoundResultsTimeout, rr.callback, []id.Round{rr.round}...) + // Retry if timed out + go rr.st.net.GetRoundResults( + getRoundResultsTimeout, rr.callback, []id.Round{rr.round}...) return } @@ -498,8 +499,8 @@ func (rr *roundResults) callback(allRoundsSucceeded, timedOut bool, results map[ if status == Failed { for i := range registered.List { round := results[rr.round].Round - go rr.st.updateStatus(registered.List[i].UUID, registered.List[i].MsgID, time.Time{}, - round, Failed) + go rr.st.updateStatus(registered.List[i].UUID, + registered.List[i].MsgID, time.Time{}, round, Failed) } } } diff --git a/channels/sendTracker_test.go b/channels/sendTracker_test.go index 2f564b88cd7f0d2468f1009c2f478de0c2f6a277..e730146bbb8d45dec2456d9f72277ac406daea47 100644 --- a/channels/sendTracker_test.go +++ b/channels/sendTracker_test.go @@ -23,30 +23,23 @@ type mockClient struct{} func (mc *mockClient) GetMaxMessageLength() int { return 2048 } -func (mc *mockClient) SendWithAssembler(recipient *id.ID, assembler cmix.MessageAssembler, - cmixParams cmix.CMIXParams) (rounds.Round, ephemeral.Id, error) { +func (mc *mockClient) SendWithAssembler(*id.ID, cmix.MessageAssembler, + cmix.CMIXParams) (rounds.Round, ephemeral.Id, error) { return rounds.Round{}, ephemeral.Id{}, nil } func (mc *mockClient) IsHealthy() bool { return true } -func (mc *mockClient) AddIdentity(id *id.ID, validUntil time.Time, persistent bool) {} -func (mc *mockClient) AddIdentityWithHistory(id *id.ID, validUntil, beginning time.Time, persistent bool) { -} -func (mc *mockClient) AddService(clientID *id.ID, newService message.Service, - response message.Processor) { -} -func (mc *mockClient) DeleteClientService(clientID *id.ID) {} -func (mc *mockClient) RemoveIdentity(id *id.ID) {} -func (mc *mockClient) GetRoundResults(timeout time.Duration, roundCallback cmix.RoundEventCallback, - roundList ...id.Round) { -} -func (mc *mockClient) AddHealthCallback(f func(bool)) uint64 { - return 0 -} -func (mc *mockClient) RemoveHealthCallback(uint64) {} - -// Test MessageReceive basic logic +func (mc *mockClient) AddIdentity(*id.ID, time.Time, bool) {} +func (mc *mockClient) AddIdentityWithHistory(*id.ID, time.Time, time.Time, bool) {} +func (mc *mockClient) AddService(*id.ID, message.Service, message.Processor) {} +func (mc *mockClient) DeleteClientService(*id.ID) {} +func (mc *mockClient) RemoveIdentity(*id.ID) {} +func (mc *mockClient) GetRoundResults(time.Duration, cmix.RoundEventCallback, ...id.Round) {} +func (mc *mockClient) AddHealthCallback(func(bool)) uint64 { return 0 } +func (mc *mockClient) RemoveHealthCallback(uint64) {} + +// Test MessageReceive basic logic. func TestSendTracker_MessageReceive(t *testing.T) { kv := versioned.NewKV(ekv.MakeMemstore()) uuidNum := uint64(0) @@ -128,8 +121,8 @@ func TestSendTracker_MessageReceive(t *testing.T) { } } -// Test failedSend function, confirming that data is stored appropriately -// and callbacks are called +// Test failedSend function, confirming that data is stored appropriately and +// callbacks are called. func TestSendTracker_failedSend(t *testing.T) { triggerCh := make(chan SentStatus) @@ -205,7 +198,8 @@ func TestSendTracker_send(t *testing.T) { kv := versioned.NewKV(ekv.MakeMemstore()) trigger := func(chID *id.ID, umi *userMessageInternal, ts time.Time, - receptionID receptionID.EphemeralIdentity, round rounds.Round, status SentStatus) (uint64, error) { + receptionID receptionID.EphemeralIdentity, round rounds.Round, + status SentStatus) (uint64, error) { return 0, nil } @@ -271,7 +265,7 @@ func TestSendTracker_send(t *testing.T) { } } -// Test loading stored byRound map from storage +// Test loading stored byRound map from storage. func TestSendTracker_load_store(t *testing.T) { kv := versioned.NewKV(ekv.MakeMemstore()) @@ -341,10 +335,8 @@ func TestRoundResult_callback(t *testing.T) { numChecks: 0, } - rr.callback(true, false, map[id.Round]cmix.RoundResult{rid: {cmix.Succeeded, rounds.Round{ - ID: rid, - State: 0, - }}}) + rr.callback(true, false, map[id.Round]cmix.RoundResult{ + rid: {Status: cmix.Succeeded, Round: rounds.Round{ID: rid, State: 0}}}) timeout := time.NewTicker(time.Second * 5) select { diff --git a/channels/send_test.go b/channels/send_test.go index 7c3205115a1947cf3f5a20b81d79f794959d57dd..fc95c85f51a365524f78716bf991fd3c69419af9 100644 --- a/channels/send_test.go +++ b/channels/send_test.go @@ -59,8 +59,8 @@ func (m *mockBroadcastChannel) Get() *cryptoBroadcast.Channel { return m.crypto } -func (m *mockBroadcastChannel) Broadcast(payload []byte, cMixParams cmix.CMIXParams) ( - rounds.Round, ephemeral.Id, error) { +func (m *mockBroadcastChannel) Broadcast(payload []byte, + cMixParams cmix.CMIXParams) (rounds.Round, ephemeral.Id, error) { m.hasRun = true @@ -70,7 +70,8 @@ func (m *mockBroadcastChannel) Broadcast(payload []byte, cMixParams cmix.CMIXPar return rounds.Round{ID: 123}, ephemeral.Id{}, nil } -func (m *mockBroadcastChannel) BroadcastWithAssembler(assembler broadcast.Assembler, cMixParams cmix.CMIXParams) ( +func (m *mockBroadcastChannel) BroadcastWithAssembler( + assembler broadcast.Assembler, cMixParams cmix.CMIXParams) ( rounds.Round, ephemeral.Id, error) { m.hasRun = true @@ -82,8 +83,9 @@ func (m *mockBroadcastChannel) BroadcastWithAssembler(assembler broadcast.Assemb return rounds.Round{ID: 123}, ephemeral.Id{}, err } -func (m *mockBroadcastChannel) BroadcastRSAtoPublic(pk rsa.PrivateKey, payload []byte, - cMixParams cmix.CMIXParams) (rounds.Round, ephemeral.Id, error) { +func (m *mockBroadcastChannel) BroadcastRSAtoPublic(pk rsa.PrivateKey, + payload []byte, cMixParams cmix.CMIXParams) ( + rounds.Round, ephemeral.Id, error) { m.hasRun = true m.payload = payload @@ -109,12 +111,11 @@ func (m *mockBroadcastChannel) BroadcastRSAToPublicWithAssembler( return rounds.Round{ID: 123}, ephemeral.Id{}, err } -func (m *mockBroadcastChannel) RegisterListener(listenerCb broadcast.ListenerFunc, method broadcast.Method) error { +func (m *mockBroadcastChannel) RegisterListener( + broadcast.ListenerFunc, broadcast.Method) error { return nil } - -func (m *mockBroadcastChannel) Stop() { -} +func (m *mockBroadcastChannel) Stop() {} type mockNameService struct { validChMsg bool @@ -124,7 +125,8 @@ func (m *mockNameService) GetUsername() string { return "Alice" } -func (m *mockNameService) GetChannelValidationSignature() (signature []byte, lease time.Time) { +func (m *mockNameService) GetChannelValidationSignature() ( + signature []byte, lease time.Time) { return []byte("fake validation sig"), netTime.Now() } @@ -132,12 +134,12 @@ func (m *mockNameService) GetChannelPubkey() ed25519.PublicKey { return []byte("fake pubkey") } -func (m *mockNameService) SignChannelMessage(message []byte) (signature []byte, err error) { +func (m *mockNameService) SignChannelMessage([]byte) (signature []byte, err error) { return []byte("fake sig"), nil } -func (m *mockNameService) ValidateChannelMessage(username string, lease time.Time, - pubKey ed25519.PublicKey, authorIDSignature []byte) bool { +func (m *mockNameService) ValidateChannelMessage( + string, time.Time, ed25519.PublicKey, []byte) bool { return m.validChMsg } @@ -171,8 +173,9 @@ func TestSendGeneric(t *testing.T) { round rounds.Round, status SentStatus) (uint64, error) { return 0, nil }, func(chID *id.ID, cm *ChannelMessage, ts time.Time, - messageID cryptoChannel.MessageID, receptionID receptionID.EphemeralIdentity, - round rounds.Round, status SentStatus) (uint64, error) { + messageID cryptoChannel.MessageID, + receptionID receptionID.EphemeralIdentity, round rounds.Round, + status SentStatus) (uint64, error) { return 0, nil }, func(uuid uint64, messageID cryptoChannel.MessageID, timestamp time.Time, round rounds.Round, status SentStatus) { @@ -201,17 +204,18 @@ func TestSendGeneric(t *testing.T) { t.Logf("ERROR %v", err) t.Fail() } - t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) + t.Logf("messageId %v, roundId %v, ephemeralId %v", + messageId, roundId, ephemeralId) - // verify the message was handled correctly + // Verify the message was handled correctly - // decode the user message + // Decode the user message umi, err := unmarshalUserMessageInternal(mbc.payload, channelID) if err != nil { t.Fatalf("Failed to decode the user message: %s", err) } - // do checks of the data + // Do checks of the data if !umi.GetMessageID().Equals(messageId) { t.Errorf("The message IDs do not match. %s vs %s ", umi.messageID, messageId) @@ -260,8 +264,9 @@ func TestAdminGeneric(t *testing.T) { round rounds.Round, status SentStatus) (uint64, error) { return 0, nil }, func(chID *id.ID, cm *ChannelMessage, ts time.Time, - messageID cryptoChannel.MessageID, receptionID receptionID.EphemeralIdentity, - round rounds.Round, status SentStatus) (uint64, error) { + messageID cryptoChannel.MessageID, + receptionID receptionID.EphemeralIdentity, round rounds.Round, + status SentStatus) (uint64, error) { return 0, nil }, func(uuid uint64, messageID cryptoChannel.MessageID, timestamp time.Time, round rounds.Round, status SentStatus) { @@ -291,9 +296,10 @@ func TestAdminGeneric(t *testing.T) { if err != nil { t.Fatalf("Failed to SendAdminGeneric: %v", err) } - t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) + t.Logf("messageId %v, roundId %v, ephemeralId %v", + messageId, roundId, ephemeralId) - // verify the message was handled correctly + // Verify the message was handled correctly msgID := cryptoChannel.MakeMessageID(mbc.payload, ch.ReceptionID) @@ -302,7 +308,7 @@ func TestAdminGeneric(t *testing.T) { msgID, messageId) } - // decode the channel message + // Decode the channel message chMgs := &ChannelMessage{} err = proto.Unmarshal(mbc.payload, chMgs) if err != nil { @@ -353,8 +359,9 @@ func TestSendMessage(t *testing.T) { round rounds.Round, status SentStatus) (uint64, error) { return 0, nil }, func(chID *id.ID, cm *ChannelMessage, ts time.Time, - messageID cryptoChannel.MessageID, receptionID receptionID.EphemeralIdentity, - round rounds.Round, status SentStatus) (uint64, error) { + messageID cryptoChannel.MessageID, + receptionID receptionID.EphemeralIdentity, round rounds.Round, + status SentStatus) (uint64, error) { return 0, nil }, func(uuid uint64, messageID cryptoChannel.MessageID, timestamp time.Time, round rounds.Round, status SentStatus) { @@ -382,17 +389,18 @@ func TestSendMessage(t *testing.T) { t.Logf("ERROR %v", err) t.Fail() } - t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) + t.Logf("messageId %v, roundId %v, ephemeralId %v", + messageId, roundId, ephemeralId) - // verify the message was handled correctly + // Verify the message was handled correctly - // decode the user message + // Decode the user message umi, err := unmarshalUserMessageInternal(mbc.payload, channelID) if err != nil { t.Fatalf("Failed to decode the user message: %s", err) } - // do checks of the data + // Do checks of the data if !umi.GetMessageID().Equals(messageId) { t.Errorf("The message IDs do not match. %s vs %s ", umi.messageID, messageId) @@ -408,7 +416,7 @@ func TestSendMessage(t *testing.T) { umi.GetChannelMessage().RoundID, returnedRound) } - // decode the text message + // Decode the text message txt := &CMIXChannelText{} err = proto.Unmarshal(umi.GetChannelMessage().Payload, txt) if err != nil { @@ -450,8 +458,9 @@ func TestSendReply(t *testing.T) { round rounds.Round, status SentStatus) (uint64, error) { return 0, nil }, func(chID *id.ID, cm *ChannelMessage, ts time.Time, - messageID cryptoChannel.MessageID, receptionID receptionID.EphemeralIdentity, - round rounds.Round, status SentStatus) (uint64, error) { + messageID cryptoChannel.MessageID, + receptionID receptionID.EphemeralIdentity, round rounds.Round, + status SentStatus) (uint64, error) { return 0, nil }, func(uuid uint64, messageID cryptoChannel.MessageID, timestamp time.Time, round rounds.Round, status SentStatus) { @@ -479,17 +488,18 @@ func TestSendReply(t *testing.T) { t.Logf("ERROR %v", err) t.Fail() } - t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) + t.Logf("messageId %v, roundId %v, ephemeralId %v", + messageId, roundId, ephemeralId) - // verify the message was handled correctly + // Verify the message was handled correctly - // decode the user message + // Decode the user message umi, err := unmarshalUserMessageInternal(mbc.payload, channelID) if err != nil { t.Fatalf("Failed to decode the user message: %s", err) } - // do checks of the data + // Do checks of the data if !umi.GetMessageID().Equals(messageId) { t.Errorf("The message IDs do not match. %s vs %s ", umi.messageID, messageId) @@ -505,7 +515,7 @@ func TestSendReply(t *testing.T) { umi.GetChannelMessage().RoundID, returnedRound) } - // decode the text message + // Decode the text message txt := &CMIXChannelText{} err = proto.Unmarshal(umi.GetChannelMessage().Payload, txt) if err != nil { @@ -547,8 +557,9 @@ func TestSendReaction(t *testing.T) { round rounds.Round, status SentStatus) (uint64, error) { return 0, nil }, func(chID *id.ID, cm *ChannelMessage, ts time.Time, - messageID cryptoChannel.MessageID, receptionID receptionID.EphemeralIdentity, - round rounds.Round, status SentStatus) (uint64, error) { + messageID cryptoChannel.MessageID, + receptionID receptionID.EphemeralIdentity, round rounds.Round, + status SentStatus) (uint64, error) { return 0, nil }, func(uuid uint64, messageID cryptoChannel.MessageID, timestamp time.Time, round rounds.Round, status SentStatus) { @@ -575,17 +586,18 @@ func TestSendReaction(t *testing.T) { t.Logf("ERROR %v", err) t.Fail() } - t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) + t.Logf("messageId %v, roundId %v, ephemeralId %v", + messageId, roundId, ephemeralId) - // verify the message was handled correctly + // Verify the message was handled correctly - // decode the user message + // Decode the user message umi, err := unmarshalUserMessageInternal(mbc.payload, channelID) if err != nil { t.Fatalf("Failed to decode the user message: %s", err) } - // do checks of the data + // Do checks of the data if !umi.GetMessageID().Equals(messageId) { t.Errorf("The message IDs do not match. %s vs %s ", umi.messageID, messageId) @@ -601,7 +613,7 @@ func TestSendReaction(t *testing.T) { umi.GetChannelMessage().RoundID, returnedRound) } - // decode the text message + // Decode the text message txt := &CMIXChannelReaction{} err = proto.Unmarshal(umi.GetChannelMessage().Payload, txt) if err != nil { diff --git a/channels/text.pb.go b/channels/text.pb.go index 5eea7e0ed48b0a5decd88d14d002fbf24c03b39a..4a441d489c4006a3beb5590267597561f8b6b378 100644 --- a/channels/text.pb.go +++ b/channels/text.pb.go @@ -28,7 +28,7 @@ const ( ) // CMIXChannelText is the payload for sending normal text messages to channels -// the replyMessageID is nil when it is not a reply +// the replyMessageID is nil when it is not a reply. type CMIXChannelText struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -94,7 +94,7 @@ func (x *CMIXChannelText) GetReplyMessageID() []byte { // 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 +// in the channel. type CMIXChannelReaction struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/channels/text.proto b/channels/text.proto index 26cc59ca76ed8b888b9969730ad9fe2e1fe80d8b..30048e36e014aea59656689fd60ad30cc492f4ce 100644 --- a/channels/text.proto +++ b/channels/text.proto @@ -12,18 +12,18 @@ option go_package = "gitlab.com/elixxir/client/channels"; package channels; // CMIXChannelText is the payload for sending normal text messages to channels -// the replyMessageID is nil when it is not a reply +// the replyMessageID is nil when it is not a reply. message CMIXChannelText { - uint32 version = 1; - string text = 2; - bytes replyMessageID = 3; + uint32 version = 1; + string text = 2; + bytes replyMessageID = 3; } // 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 +// in the channel. message CMIXChannelReaction { - uint32 version = 1; - string reaction = 2; - bytes reactionMessageID = 3; + uint32 version = 1; + string reaction = 2; + bytes reactionMessageID = 3; } \ No newline at end of file diff --git a/channels/userListener.go b/channels/userListener.go index 202e5fa7d93b4ba15485cc2549806e0b8b014dea..403d63983eca62891c82c90815c49bead44051cf 100644 --- a/channels/userListener.go +++ b/channels/userListener.go @@ -17,8 +17,8 @@ import ( "time" ) -// the userListener adheres to the [broadcast.ListenerFunc] interface and is -// used when user messages are received on the channel +// userListener adheres to the [broadcast.ListenerFunc] interface and is used +// when user messages are received on the channel. type userListener struct { name NameService chID *id.ID @@ -26,15 +26,15 @@ type userListener struct { checkSent messageReceiveFunc } -// Listen is called when a message is received for the user listener +// Listen is called when a message is received for the user listener. func (ul *userListener) Listen(payload []byte, receptionID receptionID.EphemeralIdentity, round rounds.Round) { - //Decode the message as a user message + // Decode the message as a user message umi, err := unmarshalUserMessageInternal(payload, ul.chID) if err != nil { - jww.WARN.Printf("Failed to unmarshal User Message on "+ - "channel %s", ul.chID) + jww.WARN.Printf( + "Failed to unmarshal User Message on channel %s", ul.chID) return } @@ -42,14 +42,14 @@ func (ul *userListener) Listen(payload []byte, cm := umi.GetChannelMessage() msgID := umi.GetMessageID() - //check if we sent the message, ignore triggering if we sent + // Check if we sent the message and ignore triggering if we sent if ul.checkSent(msgID, round) { return } - /*CRYPTOGRAPHICALLY RELEVANT CHECKS*/ + /* CRYPTOGRAPHICALLY RELEVANT CHECKS */ - // check the round to ensure the message is not a replay + // Check the round to ensure the message is not a replay if id.Round(cm.RoundID) != round.ID { jww.WARN.Printf("The round message %s send on %d referenced "+ "(%d) was not the same as the round the message was found on (%d)", @@ -57,7 +57,7 @@ func (ul *userListener) Listen(payload []byte, return } - // check that the user properly signed the message + // Check that the user properly signed the message if !ed25519.Verify(um.ECCPublicKey, um.Message, um.Signature) { jww.WARN.Printf("Message %s on channel %s purportedly from %s "+ "failed its user signature with signature %v", msgID, @@ -65,15 +65,15 @@ func (ul *userListener) Listen(payload []byte, return } - // Replace the timestamp on the message if it is outside of the - // allowable range - ts := vetTimestamp(time.Unix(0, cm.LocalTimestamp), round.Timestamps[states.QUEUED], msgID) + // Replace the timestamp on the message if it is outside the allowable range + ts := vetTimestamp( + time.Unix(0, cm.LocalTimestamp), round.Timestamps[states.QUEUED], msgID) - //TODO: Processing of the message relative to admin commands will be here + // TODO: Processing of the message relative to admin commands will be here. - //Submit the message to the event model for listening - if uuid, err := ul.trigger(ul.chID, umi, ts, receptionID, round, - Delivered); err != nil { + // Submit the message to the event model for listening + uuid, err := ul.trigger(ul.chID, umi, ts, receptionID, round, Delivered) + if err != nil { jww.WARN.Printf("Error in passing off trigger for "+ "message (UUID: %d): %+v", uuid, err) } diff --git a/channels/userListener_test.go b/channels/userListener_test.go index 23d8ae8c1188ef7c717680f4b6a980dace81e0cc..49b46ecf37b84c6761b644529cee4946da22bffc 100644 --- a/channels/userListener_test.go +++ b/channels/userListener_test.go @@ -35,8 +35,8 @@ type triggerEventDummy struct { } func (ted *triggerEventDummy) triggerEvent(chID *id.ID, umi *userMessageInternal, - ts time.Time, receptionID receptionID.EphemeralIdentity, round rounds.Round, - sent SentStatus) (uint64, error) { + _ time.Time, receptionID receptionID.EphemeralIdentity, round rounds.Round, + _ SentStatus) (uint64, error) { ted.gotData = true ted.chID = chID @@ -48,10 +48,10 @@ func (ted *triggerEventDummy) triggerEvent(chID *id.ID, umi *userMessageInternal return 0, nil } -// Tests the happy path +// Tests the happy path. func TestUserListener_Listen(t *testing.T) { - //build inputs + // Build inputs chID := &id.ID{} chID[0] = 1 @@ -92,20 +92,22 @@ func TestUserListener_Listen(t *testing.T) { t.Fatalf("Failed to marshal proto: %+v", err) } - //build the listener + // Build the listener dummy := &triggerEventDummy{} al := userListener{ - chID: chID, - name: ns, - trigger: dummy.triggerEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + name: ns, + trigger: dummy.triggerEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } - //call the listener + // Call the listener al.Listen(umSerial, receptionID.EphemeralIdentity{}, r) - //check the results + // Check the results if !dummy.gotData { t.Fatalf("No data returned after valid listen") } @@ -130,10 +132,9 @@ func TestUserListener_Listen(t *testing.T) { } } -//tests that the message is rejected when the user signature is invalid +// Tests that the message is rejected when the user signature is invalid. func TestUserListener_Listen_BadUserSig(t *testing.T) { - - //build inputs + // Build inputs chID := &id.ID{} chID[0] = 1 @@ -158,12 +159,12 @@ func TestUserListener_Listen_BadUserSig(t *testing.T) { t.Fatalf("Failed to marshal proto: %+v", err) } - _, badpriv, err := ed25519.GenerateKey(rng) + _, badPrivKey, err := ed25519.GenerateKey(rng) if err != nil { t.Fatalf("failed to generate ed25519 keypair, cant run test") } - sig := ed25519.Sign(badpriv, cmSerial) + sig := ed25519.Sign(badPrivKey, cmSerial) ns := &mockNameService{validChMsg: true} um := &UserMessage{ @@ -177,30 +178,31 @@ func TestUserListener_Listen_BadUserSig(t *testing.T) { t.Fatalf("Failed to marshal proto: %+v", err) } - //build the listener + // Build the listener dummy := &triggerEventDummy{} al := userListener{ - chID: chID, - name: ns, - trigger: dummy.triggerEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + name: ns, + trigger: dummy.triggerEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } - //call the listener + // Call the listener al.Listen(umSerial, receptionID.EphemeralIdentity{}, r) - //check the results + // Check the results if dummy.gotData { t.Fatalf("Data returned after invalid listen") } } -//tests that the message is rejected when the round in the message does not -//match the round passed in +// Tests that the message is rejected when the round in the message does not +// match the round passed in. func TestUserListener_Listen_BadRound(t *testing.T) { - - //build inputs + // Build inputs chID := &id.ID{} chID[0] = 1 @@ -214,9 +216,8 @@ func TestUserListener_Listen_BadRound(t *testing.T) { } cm := &ChannelMessage{ - Lease: int64(time.Hour), - //make the round not match - RoundID: 69, + Lease: int64(time.Hour), + RoundID: 69, // Make the round not match PayloadType: 42, Payload: []byte("blarg"), } @@ -240,29 +241,30 @@ func TestUserListener_Listen_BadRound(t *testing.T) { t.Fatalf("Failed to marshal proto: %+v", err) } - //build the listener + // Build the listener dummy := &triggerEventDummy{} al := userListener{ - chID: chID, - name: ns, - trigger: dummy.triggerEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + name: ns, + trigger: dummy.triggerEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } - //call the listener + // Call the listener al.Listen(umSerial, receptionID.EphemeralIdentity{}, r) - //check the results + // Check the results if dummy.gotData { t.Fatalf("Data returned after invalid listen") } } -//tests that the message is rejected when the user message is malformed +// Tests that the message is rejected when the user message is malformed. func TestUserListener_Listen_BadMessage(t *testing.T) { - - //build inputs + // Build inputs chID := &id.ID{} chID[0] = 1 @@ -273,29 +275,30 @@ func TestUserListener_Listen_BadMessage(t *testing.T) { umSerial := []byte("malformed") - //build the listener + // Build the listener dummy := &triggerEventDummy{} al := userListener{ - chID: chID, - name: ns, - trigger: dummy.triggerEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + name: ns, + trigger: dummy.triggerEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } - //call the listener + // Call the listener al.Listen(umSerial, receptionID.EphemeralIdentity{}, r) - //check the results + // Check the results if dummy.gotData { t.Fatalf("Data returned after invalid listen") } } -//tests that the message is rejected when the sized broadcast is malformed +// Tests that the message is rejected when the sized broadcast is malformed. func TestUserListener_Listen_BadSizedBroadcast(t *testing.T) { - - //build inputs + // Build inputs chID := &id.ID{} chID[0] = 1 @@ -309,9 +312,8 @@ func TestUserListener_Listen_BadSizedBroadcast(t *testing.T) { } cm := &ChannelMessage{ - Lease: int64(time.Hour), - //make the round not match - RoundID: 69, + Lease: int64(time.Hour), + RoundID: 69, // Make the round not match PayloadType: 42, Payload: []byte("blarg"), } @@ -335,23 +337,25 @@ func TestUserListener_Listen_BadSizedBroadcast(t *testing.T) { t.Fatalf("Failed to marshal proto: %+v", err) } - //remove half the sized broadcast to make it malformed + // Remove half the sized broadcast to make it malformed umSerial = umSerial[:len(umSerial)/2] - //build the listener + // Build the listener dummy := &triggerEventDummy{} al := userListener{ - chID: chID, - name: ns, - trigger: dummy.triggerEvent, - checkSent: func(messageID cryptoChannel.MessageID, r rounds.Round) bool { return false }, + chID: chID, + name: ns, + trigger: dummy.triggerEvent, + checkSent: func(cryptoChannel.MessageID, rounds.Round) bool { + return false + }, } - //call the listener + // Call the listener al.Listen(umSerial, receptionID.EphemeralIdentity{}, r) - //check the results + // Check the results if dummy.gotData { t.Fatalf("Data returned after invalid listen") }