diff --git a/dummy/manager.go b/dummy/manager.go index 6a58eb981dda9dfa34876d6cfbd9c553e8c5b969..7bc4847134c5b95ebb776f81d04199ad0862878c 100644 --- a/dummy/manager.go +++ b/dummy/manager.go @@ -21,20 +21,25 @@ import ( "time" ) +// Manager related thread handling constants. const ( + // The name of the Manager's stoppable.Stoppable dummyTrafficStoppableName = "DummyTraffic" - statusChanLen = 100 + + // The amount of statuses in queue that can be placed + // by Manager.SetStatus. + statusChanLen = 100 ) -// Thread status. +// The thread status values. const ( - notStarted uint32 = iota - running - paused - stopped + notStarted uint32 = iota // Sending thread has not been started + running // Sending thread is currently operating + paused // Sending thread is temporarily halted. + stopped // Sending thread is halted. ) -// Error messages. +// Error messages for Manager. const ( setStatusErr = "Failed to change status of dummy traffic send thread to %t: channel full" ) @@ -56,15 +61,17 @@ type Manager struct { // Pauses/Resumes the dummy send thread when triggered statusChan chan bool - // Cmix interfaces + // Interfaces net cmix.Client store storage.Session - rng *fastRNG.StreamGenerator + + // Generates + rng *fastRNG.StreamGenerator } // NewManager creates a Manager object and initialises the // dummy traffic sending thread. Note that the Manager does not start sending dummy -// traffic until True is passed into Manager.SetStatus. The time duration +// traffic until `True` is passed into Manager.SetStatus. The time duration // between each sending operation and the amount of messages sent each interval // are randomly generated values with bounds defined by the // given parameters below. @@ -74,9 +81,9 @@ type Manager struct { // each sending cycle. // - avgSendDeltaMS - the average duration, in milliseconds, to wait // between sends. -// - randomRangeMS - the upper bound of the interval between sending cycles. -// Sends occur every avgSendDeltaMS +/- a random duration with an -// upper bound of randomRangeMS +// - randomRangeMS - the upper bound of the interval between sending cycles, +// in milliseconds. Sends occur every avgSendDeltaMS +/- a random duration +// with an upper bound of randomRangeMS. func NewManager(maxNumMessages int, avgSendDelta, randomRange time.Duration, net *xxdk.Cmix) *Manager { @@ -86,7 +93,7 @@ func NewManager(maxNumMessages int, } // newManager builds a new dummy Manager from fields explicitly passed in. This -// function is a helper function for NewManager to make it easier to test. +// function is a helper function for NewManager. func newManager(maxNumMessages int, avgSendDelta, randomRange time.Duration, net cmix.Client, store storage.Session, rng *fastRNG.StreamGenerator) *Manager { return &Manager{ @@ -102,7 +109,7 @@ func newManager(maxNumMessages int, avgSendDelta, randomRange time.Duration, } // StartDummyTraffic starts the process of sending dummy traffic. This function -// matches the xxdk.Service type. +// adheres to xxdk.Service. func (m *Manager) StartDummyTraffic() (stoppable.Stoppable, error) { stop := stoppable.NewSingle(dummyTrafficStoppableName) go m.sendThread(stop) @@ -118,10 +125,10 @@ func (m *Manager) StartDummyTraffic() (stoppable.Stoppable, error) { // operation has completed. // // Params: -// - boolean - True: Sending thread is sending dummy messages. -// False: Sending thread is paused/stopped and is not sending dummy messages +// - boolean - Input should be true if you want to send dummy messages. +// Input should be false if you want to pause dummy messages. // Returns: -// - error - if the DummyTraffic.SetStatus is called too frequently, causing the +// - error - if the Manager.SetStatus is called too frequently, causing the // internal status channel to fill. func (m *Manager) SetStatus(status bool) error { select { @@ -132,15 +139,16 @@ func (m *Manager) SetStatus(status bool) error { } } -// GetStatus returns the current state of the dummy traffic sending thread. +// GetStatus returns the current state of the Manager's sending thread. // Note that this function does not return the status set by the most recent call to -// SetStatus directly. Instead, this call returns the current status of the sending thread. +// SetStatus. Instead, this call returns the current status of the sending thread. // This is due to the small delay that may occur between calling SetStatus and the // sending thread taking into effect that status change. // // Returns: -// - boolean - True: Sending thread is sending dummy messages. -// - False: Sending thread is paused/stopped and is not sending dummy messages. +// - boolean - Returns true if sending thread is sending dummy messages. +// Returns false if sending thread is paused/stopped and is +// not sending dummy messages. func (m *Manager) GetStatus() bool { switch atomic.LoadUint32(&m.status) { case running: diff --git a/dummy/manager_test.go b/dummy/manager_test.go index 7d1b490812b883142060211b9342de8929317257..5ce9cdaee7792078681a22255e53a8039677f041 100644 --- a/dummy/manager_test.go +++ b/dummy/manager_test.go @@ -45,7 +45,7 @@ func Test_newManager(t *testing.T) { // Tests that Manager.StartDummyTraffic sends dummy messages and that it stops // when the stoppable is closed. func TestManager_StartDummyTraffic(t *testing.T) { - m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t) + m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t) err := m.SetStatus(true) if err != nil { @@ -104,7 +104,7 @@ func TestManager_StartDummyTraffic(t *testing.T) { // can be called multiple times with the same status without it affecting // anything. Also tests that the thread quits even when paused. func TestManager_SetStatus(t *testing.T) { - m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t) + m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t) err := m.SetStatus(false) if err != nil { @@ -213,7 +213,7 @@ func TestManager_SetStatus(t *testing.T) { // Error path: tests that Manager.SetStatus returns an error if the status // cannot be set. func TestManager_SetStatus_ChannelError(t *testing.T) { - m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t) + m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t) // Send the max number of status changes on the channel for i := 0; i < statusChanLen; i++ { @@ -236,7 +236,7 @@ func TestManager_SetStatus_ChannelError(t *testing.T) { // Tests that Manager.GetStatus gets the correct status before the send thread // starts, while sending, while paused, and after it is stopped. func TestManager_GetStatus(t *testing.T) { - m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t) + m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t) err := m.SetStatus(false) if err != nil { diff --git a/dummy/mockCmix_test.go b/dummy/mockCmix_test.go index bcee48e70c04c878a6283f154a0d07f6808147dc..f8211be97a777d23f59458416075154dd055e067 100644 --- a/dummy/mockCmix_test.go +++ b/dummy/mockCmix_test.go @@ -25,21 +25,23 @@ import ( // mockCmix is a testing structure that adheres to cmix.Client. type mockCmix struct { - messages map[id.ID][]byte + messages map[id.ID]format.Message sync.RWMutex + payloadSize int } -func newMockCmix() cmix.Client { +func newMockCmix(payloadSize int) cmix.Client { return &mockCmix{ - messages: make(map[id.ID][]byte), + messages: make(map[id.ID]format.Message), + payloadSize: payloadSize, } } func (m *mockCmix) Send(recipient *id.ID, fingerprint format.Fingerprint, service message.Service, payload, mac []byte, cmixParams cmix.CMIXParams) (id.Round, ephemeral.Id, error) { m.Lock() defer m.Unlock() - m.messages[*recipient] = fingerprint.Bytes() + m.messages[*recipient] = generateMessage(m.payloadSize, fingerprint, service, payload, mac) return 0, ephemeral.Id{}, nil } @@ -50,7 +52,7 @@ func (m *mockCmix) GetMsgListLen() int { return len(m.messages) } -func (m *mockCmix) GetMsgList() map[id.ID][]byte { +func (m *mockCmix) GetMsgList() map[id.ID]format.Message { m.RLock() defer m.RUnlock() return m.messages diff --git a/dummy/random.go b/dummy/random.go index 8c8a87a6cc0328f136f2444125cde62c92972a0b..a70eb72af368dbf19727a27f439eee2ff68f7a64 100644 --- a/dummy/random.go +++ b/dummy/random.go @@ -10,42 +10,78 @@ package dummy import ( "encoding/binary" "github.com/pkg/errors" + "gitlab.com/elixxir/client/cmix/message" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/primitives/id" "time" ) // Error messages. +// Error constants for Manager.newRandomCmixMessage and it's helper functions.. const ( payloadSizeRngErr = "failed to generate random payload size: %+v" + payloadRngErr = "failed to generate random payload: %+v" + fingerprintRngErr = "failed to generate random fingerprint: %+v" + macRngErr = "failed to generate random MAC: %+v" + recipientRngErr = "failed to generate random recipient: %+v" ) -// intRng returns, as an int, a non-negative, non-zero random number in [1, n) -// from the csprng.Source. -func intRng(n int, rng csprng.Source) (int, error) { - v, err := csprng.Generate(8, rng) +// newRandomCmixMessage returns random format.Message data. +// +// Returns in order a: +// - Recipient (id.ID) +// - Message fingerprint (format.Fingerprint) +// - Message service (message.Service) +// - Payload ([]byte) +// - MAC ([]byte) +// - Error if there was an issue randomly generating any of the above data. +// The error will specify which of the above failed to be randomly generated. +func (m *Manager) newRandomCmixMessage(rng csprng.Source) ( + recipient *id.ID, fingerprint format.Fingerprint, + service message.Service, + payload, mac []byte, err error) { + + // Generate random recipient + recipient, err = id.NewRandomID(rng, id.User) if err != nil { - return 0, err + return nil, format.Fingerprint{}, message.Service{}, nil, nil, + errors.Errorf(recipientRngErr, err) } - return int(binary.LittleEndian.Uint64(v)%uint64(n-1)) + 1, nil -} + // todo: use error constants above? + // Generate random message payload + payloadSize := m.store.GetCmixGroup().GetP().ByteLen() + payload, err = newRandomPayload(payloadSize, rng) + if err != nil { + return nil, format.Fingerprint{}, message.Service{}, nil, nil, + errors.Errorf(payloadRngErr, err) + } -// durationRng returns a duration that is the base duration plus or minus a -// random duration of max randomRange. -func durationRng(base, randomRange time.Duration, rng csprng.Source) ( - time.Duration, error) { - delta, err := intRng(int(2*randomRange), rng) + // Generate random fingerprint + fingerprint, err = newRandomFingerprint(rng) if err != nil { - return 0, err + return nil, format.Fingerprint{}, message.Service{}, nil, nil, + errors.Errorf(fingerprintRngErr, err) } - return base + randomRange - time.Duration(delta), nil + // Generate random MAC + mac, err = newRandomMAC(rng) + if err != nil { + return nil, format.Fingerprint{}, message.Service{}, nil, nil, + errors.Errorf(macRngErr, err) + } + + // Generate random service + service = message.GetRandomService(rng) + + return } -// newRandomPayload generates a random payload of a random length. +// newRandomPayload generates a random payload of a random length +// within the maxPayloadSize. func newRandomPayload(maxPayloadSize int, rng csprng.Source) ([]byte, error) { // Generate random payload size - randomPayloadSize, err := intRng(maxPayloadSize, rng) + randomPayloadSize, err := randomInt(maxPayloadSize, rng) if err != nil { return nil, errors.Errorf(payloadSizeRngErr, err) } @@ -86,3 +122,36 @@ func newRandomMAC(rng csprng.Source) ([]byte, error) { return mac, nil } + +////////////////////////////////////////////////////////////////////////////////// +// Random Duration functions +////////////////////////////////////////////////////////////////////////////////// + +// randomDuration returns a duration that is the base duration plus or minus a +// random duration of max randomRange. +func randomDuration(base, randomRange time.Duration, rng csprng.Source) ( + time.Duration, error) { + + // Generate a random duration + delta, err := randomInt(int(2*randomRange), rng) + if err != nil { + return 0, err + } + + return base + randomRange - time.Duration(delta), nil +} + +////////////////////////////////////////////////////////////////////////////////// +// Miscellaneous +////////////////////////////////////////////////////////////////////////////////// + +// randomInt returns, as an int, a non-negative, non-zero random number in [1, n) +// from the csprng.Source. +func randomInt(n int, rng csprng.Source) (int, error) { + v, err := csprng.Generate(8, rng) + if err != nil { + return 0, err + } + + return int(binary.LittleEndian.Uint64(v)%uint64(n-1)) + 1, nil +} diff --git a/dummy/random_test.go b/dummy/random_test.go index 661986a0416993e211209d009a023c451dd3ff60..3303425c884da1febf5ddecc9b3850b8221e1e33 100644 --- a/dummy/random_test.go +++ b/dummy/random_test.go @@ -13,7 +13,7 @@ import ( "time" ) -// Consistency test: tests that intRng returns the expected int when using a +// Consistency test: tests that randomInt returns the expected int when using a // PRNG and that the result is not larger than the max. func Test_intRng_Consistency(t *testing.T) { expectedInts := []int{15, 1, 35, 13, 42, 52, 57, 3, 48} @@ -22,9 +22,9 @@ func Test_intRng_Consistency(t *testing.T) { max := 64 for i, expected := range expectedInts { - v, err := intRng(max, prng) + v, err := randomInt(max, prng) if err != nil { - t.Errorf("intRng returned an error (%d): %+v", i, err) + t.Errorf("randomInt returned an error (%d): %+v", i, err) } if v != expected { @@ -40,7 +40,7 @@ func Test_intRng_Consistency(t *testing.T) { } } -// Consistency test: tests that durationRng returns the expected int when using +// Consistency test: tests that randomDuration returns the expected int when using // a PRNG and that the result is within the allowed range. func Test_durationRng_Consistency(t *testing.T) { expectedDurations := []time.Duration{ @@ -52,9 +52,9 @@ func Test_durationRng_Consistency(t *testing.T) { base, randomRange := time.Minute, 15*time.Second for i, expected := range expectedDurations { - v, err := durationRng(base, randomRange, prng) + v, err := randomDuration(base, randomRange, prng) if err != nil { - t.Errorf("durationRng returned an error (%d): %+v", i, err) + t.Errorf("randomDuration returned an error (%d): %+v", i, err) } if v != expected { diff --git a/dummy/send.go b/dummy/send.go index ffb6fed4247c16675e36e242b63fdfac355d527b..8dc698bc1b51a1bfde473b664d94bb25fca1f5d0 100644 --- a/dummy/send.go +++ b/dummy/send.go @@ -8,27 +8,20 @@ package dummy import ( - "gitlab.com/elixxir/client/cmix/message" + "gitlab.com/elixxir/client/cmix" + "gitlab.com/xx_network/crypto/csprng" "sync" "sync/atomic" "time" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/cmix" "gitlab.com/elixxir/client/stoppable" - "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/primitives/id" ) -// Error messages. +// Error messages for the Manager.sendThread and its helper functions. const ( - numMsgsRngErr = "failed to generate random number of messages to send: %+v" - payloadRngErr = "failed to generate random payload: %+v" - recipientRngErr = "failed to generate random recipient: %+v" - fingerprintRngErr = "failed to generate random fingerprint: %+v" - macRngErr = "failed to generate random MAC: %+v" + numMsgsRngErr = "failed to generate random number of messages to send: %+v" ) // sendThread is a thread that sends the dummy messages at random intervals. @@ -40,152 +33,113 @@ func (m *Manager) sendThread(stop *stoppable.Single) { for { select { - case <-stop.Quit(): - m.stopSendThread(stop) - return case status := <-m.statusChan: if status { atomic.StoreUint32(&m.status, running) - nextSendChanPtr = &(m.randomTimer().C) + // Generate random duration + rng := m.rng.GetStream() + duration, err := randomDuration(m.avgSendDelta, m.randomRange, rng) + if err != nil { + rng.Close() + jww.FATAL.Panicf("Failed to generate random sending interval: %+v", err) + } + rng.Close() + + // Create timer + nextSendChanPtr = &(time.NewTimer(duration).C) + } else { atomic.StoreUint32(&m.status, paused) nextSendChan = make(<-chan time.Time) nextSendChanPtr = &nextSendChan } case <-*nextSendChanPtr: - nextSendChanPtr = &(m.randomTimer().C) + // Generate random duration + rng := m.rng.GetStream() + duration, err := randomDuration(m.avgSendDelta, m.randomRange, rng) + if err != nil { + rng.Close() + jww.FATAL.Panicf("Failed to generate random sending interval: %+v", err) + } + rng.Close() - go func() { - // get list of random messages and recipients - rng := m.rng.GetStream() - defer rng.Close() - msgs, err := m.newRandomMessages(rng) - if err != nil { - jww.ERROR.Printf("Failed to generate dummy messages: %+v", err) - return - } + // Create timer + nextSendChanPtr = &(time.NewTimer(duration).C) - err = m.sendMessages(msgs, rng) + // Send messages + go func() { + err := m.sendMessages() if err != nil { jww.ERROR.Printf("Failed to send dummy messages: %+v", err) } }() + case <-stop.Quit(): + m.stopSendThread(stop) + return } } } -// stopSendThread is triggered when the stoppable is triggered. It prints a -// debug message, sets the thread status to stopped, and sets the status of the -// stoppable to stopped. -func (m *Manager) stopSendThread(stop *stoppable.Single) { - jww.DEBUG.Print( - "Stopping dummy traffic sending thread: stoppable triggered") - atomic.StoreUint32(&m.status, stopped) - stop.ToStopped() -} - // sendMessages generates and sends random messages. -func (m *Manager) sendMessages(msgs map[id.ID]format.Message, rng csprng.Source) error { - var sent, i int64 +func (m *Manager) sendMessages() error { + var sent int64 var wg sync.WaitGroup - for recipient, msg := range msgs { - wg.Add(1) + // Randomly generate amount of messages to send + rng := m.rng.GetStream() + defer rng.Close() + numMessages, err := randomInt(m.maxNumMessages+1, rng) + if err != nil { + return errors.Errorf(numMsgsRngErr, err) + } - go func(i int64, recipient id.ID, msg format.Message) { + for i := 0; i < numMessages; i++ { + wg.Add(1) + go func(localIndex, totalMessages int) { defer wg.Done() - // Fill the preimage with random data to ensure it is not repeatable - p := cmix.GetDefaultCMIXParams() - _, _, err := m.net.Send(&recipient, msg.GetKeyFP(), - message.GetRandomService(rng), msg.GetContents(), msg.GetMac(), p) + err = m.sendMessage(localIndex, totalMessages, rng) if err != nil { - jww.WARN.Printf("Failed to send dummy message %d/%d via "+ - "Send: %+v", i, len(msgs), err) - } else { - atomic.AddInt64(&sent, 1) + jww.ERROR.Printf(err.Error()) } - }(i, recipient, msg) - - i++ + // Add to counter of successful sends + atomic.AddInt64(&sent, 1) + }(i, numMessages) } wg.Wait() - - jww.INFO.Printf("Sent %d/%d dummy messages.", sent, len(msgs)) - + jww.INFO.Printf("Sent %d/%d dummy messages.", sent, numMessages) return nil } -// newRandomMessages returns a map of a random recipients and random messages of -// a randomly generated length in [1, Manager.maxNumMessages]. -func (m *Manager) newRandomMessages(rng csprng.Source) ( - map[id.ID]format.Message, error) { - numMessages, err := intRng(m.maxNumMessages+1, rng) - if err != nil { - return nil, errors.Errorf(numMsgsRngErr, err) - } - - msgs := make(map[id.ID]format.Message, numMessages) - - for i := 0; i < numMessages; i++ { - // Generate random recipient - recipient, err := id.NewRandomID(rng, id.User) - if err != nil { - return nil, errors.Errorf(recipientRngErr, err) - } - - msgs[*recipient], err = m.newRandomCmixMessage(rng) - if err != nil { - return nil, err - } - } - - return msgs, nil -} - -// newRandomCmixMessage returns a new cMix message filled with a randomly -// generated payload, fingerprint, and MAC. -func (m *Manager) newRandomCmixMessage(rng csprng.Source) (format.Message, error) { - // Create new empty cMix message - cMixMsg := format.NewMessage(m.store.GetCmixGroup().GetP().ByteLen()) - - // Generate random message - randomMsg, err := newRandomPayload(cMixMsg.ContentsSize(), rng) +// sendMessage is a helper function which generates a sends a single random format.Message +// to a random recipient. +func (m *Manager) sendMessage(index, totalMessages int, rng csprng.Source) error { + // Generate message data + recipient, fp, service, payload, mac, err := m.newRandomCmixMessage(rng) if err != nil { - return format.Message{}, errors.Errorf(payloadRngErr, err) + return errors.Errorf("Failed to create dummy message %d/%d: %+v", + index, totalMessages, err) } - // Generate random fingerprint - fingerprint, err := newRandomFingerprint(rng) + // Send message + p := cmix.GetDefaultCMIXParams() + _, _, err = m.net.Send(recipient, fp, service, payload, mac, p) if err != nil { - return format.Message{}, errors.Errorf(fingerprintRngErr, err) + return errors.Errorf("Failed to send dummy message %d/%d: %+v", + index, totalMessages, err) } - // Generate random MAC - mac, err := newRandomMAC(rng) - if err != nil { - return format.Message{}, errors.Errorf(macRngErr, err) - } - - // Set contents, fingerprint, and MAC, of the cMix message - cMixMsg.SetContents(randomMsg) - cMixMsg.SetKeyFP(fingerprint) - cMixMsg.SetMac(mac) - - return cMixMsg, nil + return nil } -// randomTimer generates a timer that will trigger after a random duration. -func (m *Manager) randomTimer() *time.Timer { - rng := m.rng.GetStream() - - duration, err := durationRng(m.avgSendDelta, m.randomRange, rng) - if err != nil { - jww.FATAL.Panicf("Failed to generate random duration to wait to send "+ - "dummy messages: %+v", err) - } - - return time.NewTimer(duration) +// stopSendThread is triggered when the stoppable is triggered. It prints a +// debug message, sets the thread status to stopped, and sets the status of the +// stoppable to stopped. +func (m *Manager) stopSendThread(stop *stoppable.Single) { + jww.DEBUG.Print( + "Stopping dummy traffic sending thread: stoppable triggered") + atomic.StoreUint32(&m.status, stopped) + stop.ToStopped() } diff --git a/dummy/send_test.go b/dummy/send_test.go index 625a203ae5b2d6beb9a8287d1f7adcfe412deebf..ef776b17ba9084fe07d34ab453ed890ab9b9f0f4 100644 --- a/dummy/send_test.go +++ b/dummy/send_test.go @@ -9,7 +9,6 @@ package dummy import ( "bytes" - "encoding/base64" "gitlab.com/elixxir/client/stoppable" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" @@ -21,7 +20,7 @@ import ( // Tests that Manager.sendThread sends multiple sets of messages. func TestManager_sendThread(t *testing.T) { - m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t) + m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t) stop := stoppable.NewSingle("sendThreadTest") go m.sendThread(stop) @@ -86,32 +85,33 @@ func TestManager_sendThread(t *testing.T) { } -// Tests that Manager.sendMessages sends all the messages with the correct -// recipient. -func TestManager_sendMessages(t *testing.T) { - m := newTestManager(100, 0, 0, false, t) - prng := NewPrng(42) +// Tests that sendMessage generates random message data using pseudo-RNGs. +func TestManager_sendMessage(t *testing.T) { + m := newTestManager(100, 0, 0, t) + + // Generate two identical RNGs, one for generating expected data (newRandomCmixMessage) + // and one for received data (sendMessage) + prngOne := NewPrng(42) + prngTwo := NewPrng(42) // Generate map of recipients and messages msgs := make(map[id.ID]format.Message, m.maxNumMessages) for i := 0; i < m.maxNumMessages; i++ { - recipient, err := id.NewRandomID(prng, id.User) + // Generate random data + recipient, fp, service, payload, mac, err := m.newRandomCmixMessage(prngOne) if err != nil { - t.Errorf("Failed to generate random recipient ID (%d): %+v", i, err) + t.Fatalf("Failed to generate random cMix message (%d): %+v", i, err) } - msg, err := m.newRandomCmixMessage(prng) + payloadSize := m.store.GetCmixGroup().GetP().ByteLen() + msgs[*recipient] = generateMessage(payloadSize, fp, service, payload, mac) + + // Send the messages + err = m.sendMessage(i, m.maxNumMessages, prngTwo) if err != nil { - t.Errorf("Failed to generate random cMix message (%d): %+v", i, err) + t.Errorf("sendMessages returned an error: %+v", err) } - msgs[*recipient] = msg - } - - // Send the messages - err := m.sendMessages(msgs, prng) - if err != nil { - t.Errorf("sendMessages returned an error: %+v", err) } // get sent messages @@ -127,61 +127,46 @@ func TestManager_sendMessages(t *testing.T) { for recipient, msg := range msgs { receivedMsg, exists := receivedMsgs[recipient] if !exists { - t.Errorf("Failed to receive message from %s: %+v", &recipient, msg) - } else if !reflect.DeepEqual(msg.GetKeyFP().Bytes(), receivedMsg) { + t.Errorf("Failed to receive message from %s: %+v", &recipient, msg.Marshal()) + } else if !reflect.DeepEqual(msg.Marshal(), receivedMsg.Marshal()) { // In mockCmix.Send, we map recipientId to the passed fingerprint. t.Errorf("Received unexpected message for recipient %s."+ - "\nexpected: %+v\nreceived: %+v", &recipient, msg.GetKeyFP(), receivedMsg) + "\nexpected: %+v\nreceived: %+v", &recipient, msg, receivedMsg) } } } -// Tests that Manager.newRandomMessages creates a non-empty map of messages and -// that each message is unique. -func TestManager_newRandomMessages(t *testing.T) { - m := newTestManager(10, 0, 0, false, t) +// Tests that newRandomCmixMessage generates cMix message data with +// populated recipient, payload, fingerprint, and MAC. +func TestManager_newRandomCmixMessage(t *testing.T) { + m := newTestManager(0, 0, 0, t) prng := NewPrng(42) - msgMap, err := m.newRandomMessages(prng) + // Generate data + recipient, fp, _, payload, mac, err := m.newRandomCmixMessage(prng) if err != nil { - t.Errorf("newRandomMessages returned an error: %+v", err) - } - - if len(msgMap) == 0 { - t.Error("Message map is empty.") + t.Fatalf("newRandomCmixMessage returned an error: %+v", err) } - marshalledMsgs := make(map[string]format.Message, len(msgMap)) - for _, msg := range msgMap { - msgString := base64.StdEncoding.EncodeToString(msg.Marshal()) - if _, exists := marshalledMsgs[msgString]; exists { - t.Errorf("Message not unique.") - } else { - marshalledMsgs[msgString] = msg - } + // Check that recipient is not empty data + if bytes.Equal(recipient.Bytes(), make([]byte, id.ArrIDLen)) { + t.Errorf("Recipient ID not set") } -} - -// Tests that Manager.newRandomCmixMessage generates a cMix message with -// populated contents, fingerprint, and MAC. -func TestManager_newRandomCmixMessage(t *testing.T) { - m := newTestManager(0, 0, 0, false, t) - prng := NewPrng(42) - cMixMsg, err := m.newRandomCmixMessage(prng) - if err != nil { - t.Errorf("newRandomCmixMessage returned an error: %+v", err) - } - - if bytes.Equal(cMixMsg.GetContents(), make([]byte, len(cMixMsg.GetContents()))) { + // Check that payload is not empty data + payloadSize := m.store.GetCmixGroup().GetP().ByteLen() + if bytes.Equal(payload, make([]byte, payloadSize)) { t.Error("cMix message contents not set.") } - if cMixMsg.GetKeyFP() == (format.Fingerprint{}) { + // Check that fingerprint is not empty data + if fp == (format.Fingerprint{}) { t.Error("cMix message fingerprint not set.") } - if bytes.Equal(cMixMsg.GetMac(), make([]byte, format.MacLen)) { + // Check that mac is not empty data + if bytes.Equal(mac, make([]byte, format.MacLen)) { t.Error("cMix message MAC not set.") } + } diff --git a/dummy/utils_test.go b/dummy/utils_test.go index bef9291f030af9f5d67119e65d5d5ec25640c0bf..a3780867a513a6c497157bd17ccfceef89f2a5ec 100644 --- a/dummy/utils_test.go +++ b/dummy/utils_test.go @@ -8,10 +8,11 @@ package dummy import ( + "gitlab.com/elixxir/client/cmix/message" "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/primitives/ndf" "io" "math/rand" "testing" @@ -36,59 +37,35 @@ func (s *Prng) SetSeed([]byte) error { return nil } // newTestManager creates a new Manager that has groups stored for testing. One // of the groups in the list is also returned. func newTestManager(maxNumMessages int, avgSendDelta, randomRange time.Duration, - sendErr bool, t *testing.T) *Manager { + t *testing.T) *Manager { store := storage.InitTestingSession(t) + payloadSize := store.GetCmixGroup().GetP().ByteLen() m := &Manager{ maxNumMessages: maxNumMessages, avgSendDelta: avgSendDelta, randomRange: randomRange, statusChan: make(chan bool, statusChanLen), store: store, - net: newMockCmix(), + net: newMockCmix(payloadSize), rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), } return m } -func getNDF() *ndf.NetworkDefinition { - return &ndf.NetworkDefinition{ - E2E: ndf.Group{ - Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B7A" + - "8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3D" + - "D2AEDF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E78615" + - "75E745D31F8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC" + - "6ADC718DD2A3E041023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C" + - "4A530E8FFB1BC51DADDF453B0B2717C2BC6669ED76B4BDD5C9FF558E88F2" + - "6E5785302BEDBCA23EAC5ACE92096EE8A60642FB61E8F3D24990B8CB12EE" + - "448EEF78E184C7242DD161C7738F32BF29A841698978825B4111B4BC3E1E" + - "198455095958333D776D8B2BEEED3A1A1A221A6E37E664A64B83981C46FF" + - "DDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F278DE8014A47323" + - "631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696015CB79C" + - "3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E63" + - "19BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC3" + - "5873847AEF49F66E43873", - Generator: "2", - }, - CMIX: ndf.Group{ - Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642" + - "F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757" + - "264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F" + - "9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091E" + - "B51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D" + - "0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D3" + - "92145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A" + - "2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7" + - "995FAD5AABBCFBE3EDA2741E375404AE25B", - Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E2480" + - "9670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D" + - "1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A33" + - "8661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361" + - "C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B28" + - "5DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD929" + - "59859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D83" + - "2186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8" + - "B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", - }, - } +// generateMessage is a utility function which generates a format.Message +// given message data. +func generateMessage(payloadSize int, + fingerprint format.Fingerprint, + service message.Service, + payload, mac []byte) format.Message { + + // Build message. Will panic if inputs are not correct. + msg := format.NewMessage(payloadSize) + msg.SetContents(payload) + msg.SetKeyFP(fingerprint) + msg.SetSIH(service.Hash(msg.GetContents())) + msg.SetMac(mac) + + return msg }