diff --git a/api/authenticatedChannel.go b/api/authenticatedChannel.go
index 84fd751178f710482c49c24df38449ef731f0779..93446db2b9bee15a5755f49d62ce42bcedf15794 100644
--- a/api/authenticatedChannel.go
+++ b/api/authenticatedChannel.go
@@ -158,6 +158,14 @@ func (c *Client) MakePrecannedAuthenticatedChannel(precannedID uint) (contact.Co
 		Source: precan.ID[:],
 	}, me)
 
+	// group request
+	c.storage.GetEdge().Add(edge.Preimage{
+		Data:   sessionPartner.GetGroupRequestPreimage(),
+		Type:   preimage.GroupRq,
+		Source: precan.ID[:],
+	}, me)
+
+
 	return precan, err
 }
 
diff --git a/api/client.go b/api/client.go
index c705187068226c528ac1ac24f1dfd28c49f1eb8f..2d455d90cc2ea0d972dc82eee94f7e8a2773f18d 100644
--- a/api/client.go
+++ b/api/client.go
@@ -696,6 +696,7 @@ func (c *Client) DeleteContact(partnerId *id.ID) error {
 	e2ePreimage := partner.GetE2EPreimage()
 	rekeyPreimage := partner.GetSilentPreimage()
 	fileTransferPreimage := partner.GetFileTransferPreimage()
+	groupRequestPreimage := partner.GetGroupRequestPreimage()
 
 	//delete the partner
 	if err = c.storage.E2e().DeletePartner(partnerId); err != nil {
@@ -729,6 +730,15 @@ func (c *Client) DeleteContact(partnerId *id.ID) error {
 			"from %s on contact deletion: %+v", partnerId, err)
 	}
 
+	if err = c.storage.GetEdge().Remove(edge.Preimage{
+		Data:   groupRequestPreimage,
+		Type:   preimage.GroupRq,
+		Source: partnerId[:],
+	}, c.storage.GetUser().ReceptionID); err != nil {
+		jww.WARN.Printf("Failed delete the preimage for group request "+
+			"from %s on contact deletion: %+v", partnerId, err)
+	}
+
 	if err = c.storage.Auth().Delete(partnerId); err != nil {
 		return err
 	}
diff --git a/api/event.go b/api/event.go
index 86e75b14ef212033ca906858e265c713022ee709..c5fcf03a90842095990568893051afea5a7e6cee 100644
--- a/api/event.go
+++ b/api/event.go
@@ -94,7 +94,7 @@ func (e *eventManager) reportEventsHandler(stop *stoppable.Single) {
 			stop.ToStopped()
 			return
 		case evt := <-e.eventCh:
-			jww.DEBUG.Printf("Received event: %s", evt)
+			jww.TRACE.Printf("Received event: %s", evt)
 			// NOTE: We could call each in a routine but decided
 			// against it. It's the users responsibility not to let
 			// the event queue explode. The API will report errors
diff --git a/api/results.go b/api/results.go
index 4a31b66b525e60037479ac4042017b21cfece313..4f5668fc90a6fcf53d61d6a440fe7fb35f35e7b5 100644
--- a/api/results.go
+++ b/api/results.go
@@ -89,17 +89,11 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 	roundEvents := c.GetRoundEvents()
 	roundsResults := make(map[id.Round]RoundResult)
 	allRoundsSucceeded := true
+	anyRoundTimedOut := false
 	numResults := 0
 
 	oldestRound := networkInstance.GetOldestRoundID()
 
-	// Set a lower timeout so there is room for retries,
-	// while ensuring it does not go too low and cause too many timeouts
-	roundEventTimeout := 5 * time.Second
-	if timeout < roundEventTimeout {
-		roundEventTimeout = timeout
-	}
-
 	// Parse and adjudicate every round
 	for _, rnd := range roundList {
 		// Every round is timed out by default, until proven to have finished
@@ -116,7 +110,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 			} else {
 				// If in progress, add a channel monitoring its state
 				roundEvents.AddRoundEventChan(rnd, sendResults,
-					roundEventTimeout, states.COMPLETED, states.FAILED)
+					timeout-time.Millisecond, states.COMPLETED, states.FAILED)
 				numResults++
 			}
 		} else {
@@ -129,7 +123,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 			} else {
 				// Otherwise, monitor its progress
 				roundEvents.AddRoundEventChan(rnd, sendResults,
-					roundEventTimeout, states.COMPLETED, states.FAILED)
+					timeout-time.Millisecond, states.COMPLETED, states.FAILED)
 				numResults++
 			}
 		}
@@ -148,7 +142,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 
 			// If we know about all rounds, return
 			if numResults == 0 {
-				roundCallback(allRoundsSucceeded, false, roundsResults)
+				roundCallback(allRoundsSucceeded, anyRoundTimedOut, roundsResults)
 				return
 			}
 
@@ -158,19 +152,14 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 				roundCallback(false, true, roundsResults)
 				return
 			case roundReport := <-sendResults:
+
+				numResults--
+
 				// Skip if the round is nil (unknown from historical rounds)
 				// they default to timed out, so correct behavior is preserved
-				if roundReport.RoundInfo == nil {
+				if roundReport.TimedOut {
 					allRoundsSucceeded = false
-					numResults--
-				} else if roundReport.TimedOut {
-					// Generate a message to track the timed out round
-					timeoutRequest := &pb.HistoricalRounds{
-						Rounds: []uint64{roundReport.RoundInfo.ID},
-					}
-					// Request that round's information, feeding back into sendResults
-					jww.DEBUG.Printf("Sending HistoricalRounds retry for Round %d", roundReport.RoundInfo.ID)
-					go c.getHistoricalRounds(timeoutRequest, sendResults, commsInterface)
+					anyRoundTimedOut = true
 				} else {
 					// If available, denote the result
 					roundId := id.Round(roundReport.RoundInfo.ID)
@@ -180,7 +169,6 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 						roundsResults[roundId] = Failed
 						allRoundsSucceeded = false
 					}
-					numResults--
 				}
 			}
 		}
@@ -219,9 +207,17 @@ func (c *Client) getHistoricalRounds(msg *pb.HistoricalRounds,
 	}
 
 	// Service historical rounds, sending back to the caller thread
-	for _, ri := range resp.Rounds {
-		sendResults <- ds.EventReturn{
-			RoundInfo: ri,
+	for i, ri := range resp.Rounds {
+		if ri == nil {
+			// Handle unknown by historical rounds
+			sendResults <- ds.EventReturn{
+				RoundInfo: &pb.RoundInfo{ID: msg.Rounds[i]},
+				TimedOut:  true,
+			}
+		} else {
+			sendResults <- ds.EventReturn{
+				RoundInfo: ri,
+			}
 		}
 	}
 }
diff --git a/api/utils.go b/api/utils.go
index 96eb44f8834cb5b07cf020556aeb637caf2dd00a..2486d4d66143bb7fe6b660a9d23da26e70828652 100644
--- a/api/utils.go
+++ b/api/utils.go
@@ -20,7 +20,9 @@ const (
 	// Maximum input image size (in bytes)
 	maxSize int64 = 12000000
 	// Desired number of pixels in output image
-	desiredSize = 307200
+	desiredSize = 640*480
+	// Desired number of pixels in output image for preview
+	desiredPreviewSize = 32*24
 )
 
 // CompressJpeg takes a JPEG image in byte format
@@ -73,3 +75,55 @@ func CompressJpeg(imgBytes []byte) ([]byte, error) {
 	// Return the compressed image in byte form
 	return newImgBuf.Bytes(), nil
 }
+
+
+// CompressJpeg takes a JPEG image in byte format
+// and compresses it based on desired output size
+func CompressJpegForPreview(imgBytes []byte) ([]byte, error) {
+	// Convert bytes to a reader
+	imgBuf := bytes.NewReader(imgBytes)
+
+	// Ensure the size of the image is under the limit
+	if imgSize := imgBuf.Size(); imgSize > maxSize {
+		return nil, errors.Errorf("Image is too large: %d/%d", imgSize, maxSize)
+	}
+
+	// Decode the image information
+	imgInfo, err := jpeg.DecodeConfig(imgBuf)
+	if err != nil {
+		return nil, errors.Errorf("Unable to decode image config: %+v", err)
+	}
+
+	// If the dimensions of the image are below desiredSize, no compression is required
+	if imgInfo.Width*imgInfo.Height < desiredSize {
+		return imgBytes, nil
+	}
+
+	// Reset the buffer to the beginning to begin decoding the image
+	_, err = imgBuf.Seek(0, 0)
+	if err != nil {
+		return nil, errors.Errorf("Unable to reset image buffer: %+v", err)
+	}
+
+	// Decode image into image.Image object
+	img, err := jpeg.Decode(imgBuf)
+	if err != nil {
+		return nil, errors.Errorf("Unable to decode image: %+v", err)
+	}
+
+	// Determine the new width of the image based on desiredSize
+	newWidth := uint(math.Sqrt(float64(desiredSize) * (float64(imgInfo.Width) / float64(imgInfo.Height))))
+
+	// Resize the image based on newWidth while preserving aspect ratio
+	newImg := resize.Resize(newWidth, 0, img, resize.Bicubic)
+
+	// Encode the new image to a buffer
+	newImgBuf := new(bytes.Buffer)
+	err = jpeg.Encode(newImgBuf, newImg, nil)
+	if err != nil {
+		return nil, errors.Errorf("Unable to encode image: %+v", err)
+	}
+
+	// Return the compressed image in byte form
+	return newImgBuf.Bytes(), nil
+}
diff --git a/api/version_vars.go b/api/version_vars.go
index 8ca79de091b8427e4dd1a5f7d0afa55557668dfc..8a44f54952836daac733cc3e151032f5e6b042cb 100644
--- a/api/version_vars.go
+++ b/api/version_vars.go
@@ -1,15 +1,16 @@
 // Code generated by go generate; DO NOT EDIT.
 // This file was generated by robots at
-// 2021-12-27 11:52:01.388598 -0600 CST m=+0.029765559
+// 2022-01-04 12:45:01.875155 -0600 CST m=+0.041061278
 package api
 
-const GITVERSION = `6faba65a Merge branch 'XX-3650/dummyMessageSendStatus' into 'release'`
-const SEMVER = "3.3.0"
+const GITVERSION = `1144194c Merge branch 'dev' into 'release'`
+const SEMVER = "4.0.0"
 const DEPENDENCIES = `module gitlab.com/elixxir/client
 
 go 1.13
 
 require (
+	github.com/cloudflare/circl v1.0.1-0.20211008185751-59b49bc148ce
 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
 	github.com/golang/protobuf v1.5.2
 	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
@@ -25,17 +26,17 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20211222195106-4ec0e4f02f69
-	gitlab.com/elixxir/crypto v0.0.7-0.20211222194952-736897f54f09
+	gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6
+	gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553
 	gitlab.com/elixxir/ekv v0.1.5
-	gitlab.com/elixxir/primitives v0.0.3-0.20211222194918-5c28e9620d4e
-	gitlab.com/xx_network/comms v0.0.4-0.20211222194906-4c28450f7144
-	gitlab.com/xx_network/crypto v0.0.5-0.20211222194842-09692b01f03e
+	gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f
+	gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda
+	gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467
 	gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0
 	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
 	golang.org/x/net v0.0.0-20210525063256-abc453219eb5
 	google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect
-	google.golang.org/grpc v1.38.0
+	google.golang.org/grpc v1.42.0
 	google.golang.org/protobuf v1.27.1
 	gopkg.in/ini.v1 v1.62.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
diff --git a/auth/callback.go b/auth/callback.go
index 3151fdb43de00b988d3356ea1833c9979c83cfba..471d415a8e4147e6718e926553619cd2908403e8 100644
--- a/auth/callback.go
+++ b/auth/callback.go
@@ -434,6 +434,13 @@ func (m *Manager) doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
 		Source: sr.GetPartner()[:],
 	}, me)
 
+	//group Request
+	m.storage.GetEdge().Add(edge.Preimage{
+		Data:   sessionPartner.GetGroupRequestPreimage(),
+		Type:   preimage.GroupRq,
+		Source: sr.GetPartner()[:],
+	}, me)
+
 	// delete the in progress negotiation
 	// this undoes the request lock
 	if err := m.storage.Auth().Delete(sr.GetPartner()); err != nil {
diff --git a/auth/confirm.go b/auth/confirm.go
index 2a8169d21eb5be5f80ccbe5519aeb6a5a1e0d783..492e758a38c55fb0cda1aed0feedcb8fcce66f73 100644
--- a/auth/confirm.go
+++ b/auth/confirm.go
@@ -147,6 +147,13 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
 		Source: partner.ID[:],
 	}, me)
 
+	//group Request
+	storage.GetEdge().Add(edge.Preimage{
+		Data:   sessionPartner.GetGroupRequestPreimage(),
+		Type:   preimage.GroupRq,
+		Source: partner.ID[:],
+	}, me)
+
 	// delete the in progress negotiation
 	// this unlocks the request lock
 	//fixme - do these deletes at a later date
diff --git a/bindings/notifications.go b/bindings/notifications.go
index 71db73728f59e1fcd4d382fe79de310eddf860d4..7f393cf7271c253cf5e07771f9f570d89b69a3de 100644
--- a/bindings/notifications.go
+++ b/bindings/notifications.go
@@ -59,6 +59,7 @@ func (mnfmr *ManyNotificationForMeReport) Len() int {
 //	"e2e"		sender user ID		reception of an E2E message
 //	"group"		group ID			reception of a group chat message
 //  "endFT"     sender user ID		Last message sent confirming end of file transfer
+//  "groupRQ"   sender user ID		Request from sender to join a group chat
 func NotificationsForMe(notifCSV, preimages string) (*ManyNotificationForMeReport, error) {
 	//handle deserialization of preimages
 	var preimageList []edge.Preimage
diff --git a/bindings/utils.go b/bindings/utils.go
index b7249f6b910005962f27cbeb34679698e8c69c63..93afd4aab7bbf66970f9041551c3c32f6bdf19a7 100644
--- a/bindings/utils.go
+++ b/bindings/utils.go
@@ -15,3 +15,9 @@ import "gitlab.com/elixxir/client/api"
 func CompressJpeg(imgBytes []byte) ([]byte, error) {
 	return api.CompressJpeg(imgBytes)
 }
+
+// CompressJpegForPreview takes a JPEG image in byte format
+// and compresses it based on desired output size
+func CompressJpegForPreview(imgBytes []byte) ([]byte, error) {
+	return api.CompressJpegForPreview(imgBytes)
+}
diff --git a/cmd/root.go b/cmd/root.go
index 28dd11f80212f4c8e1e7749020c54df2d042707b..a5e84631a369ae594a576c36e5a02c3f1924c9f8 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -599,7 +599,7 @@ func initClient() *api.Client {
 		viper.GetUint("e2eNumReKeys"))
 	netParams.E2EParams.RekeyThreshold = viper.GetFloat64("e2eRekeyThreshold")
 	netParams.ForceHistoricalRounds = viper.GetBool("forceHistoricalRounds")
-	netParams.FastPolling = viper.GetBool(" slowPolling")
+	netParams.FastPolling = !viper.GetBool("slowPolling")
 	netParams.ForceMessagePickupRetry = viper.GetBool("forceMessagePickupRetry")
 	if netParams.ForceMessagePickupRetry {
 		period := 3 * time.Second
diff --git a/cmd/version.go b/cmd/version.go
index 23aa30916e42b6883393e6165440b2f6d2f7dbf0..f6443702ed89544cfeda43c6671e75d740bb21fc 100644
--- a/cmd/version.go
+++ b/cmd/version.go
@@ -18,7 +18,7 @@ import (
 )
 
 // Change this value to set the version for this build
-const currentVersion = "3.3.0"
+const currentVersion = "4.0.0"
 
 func Version() string {
 	out := fmt.Sprintf("Elixxir Client v%s -- %s\n\n", api.SEMVER,
diff --git a/fileTransfer/fileMessage.go b/fileTransfer/fileMessage.go
deleted file mode 100644
index b73b948c90cc4d99bad2d459be6a8f98df61f2a5..0000000000000000000000000000000000000000
--- a/fileTransfer/fileMessage.go
+++ /dev/null
@@ -1,130 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package fileTransfer
-
-import (
-	"encoding/binary"
-	"github.com/pkg/errors"
-	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
-)
-
-// Size constants.
-const (
-	paddingLen = ftCrypto.NonceSize      // The length of the padding in bytes
-	partNumLen = 2                       // The length of the part number in bytes
-	fmMinSize  = partNumLen + paddingLen // Minimum size for the partMessage
-)
-
-// Error messages.
-const (
-	newFmSizeErr       = "size of external payload (%d) must be greater than %d"
-	unmarshalFmSizeErr = "size of passed in bytes (%d) must be greater than %d"
-	setFileFmErr       = "length of part bytes (%d) must be smaller than maximum payload size %d"
-)
-
-/*
-+-----------------------------------------+
-|          CMIX Message Contents          |
-+---------+-------------+-----------------+
-| Padding | Part Number |    File Data    |
-| 8 bytes |   2 bytes   | remaining space |
-+---------+-------------+-----------------+
-*/
-
-// partMessage contains part of the data being transferred and 256-bit padding
-// that is used as a nonce.
-type partMessage struct {
-	data    []byte // Serial of all contents
-	padding []byte // Random padding bytes
-	partNum []byte // The part number of the file
-	part    []byte // File part data
-}
-
-// newPartMessage generates a new part message that fits into the specified
-// external payload size. An error is returned if the external payload size is
-// too small to fit the part message.
-func newPartMessage(externalPayloadSize int) (partMessage, error) {
-	if externalPayloadSize < fmMinSize {
-		return partMessage{},
-			errors.Errorf(newFmSizeErr, externalPayloadSize, fmMinSize)
-	}
-
-	return mapPartMessage(make([]byte, externalPayloadSize)), nil
-}
-
-// mapPartMessage maps the data to the components of a partMessage. It is mapped
-// by reference; a copy is not made.
-func mapPartMessage(data []byte) partMessage {
-	return partMessage{
-		data:    data,
-		padding: data[:paddingLen],
-		partNum: data[paddingLen : paddingLen+partNumLen],
-		part:    data[paddingLen+partNumLen:],
-	}
-}
-
-// unmarshalPartMessage converts the bytes into a partMessage. An error is
-// returned if the size of the data is too small for a partMessage.
-func unmarshalPartMessage(b []byte) (partMessage, error) {
-	if len(b) < fmMinSize {
-		return partMessage{},
-			errors.Errorf(unmarshalFmSizeErr, len(b), fmMinSize)
-	}
-
-	return mapPartMessage(b), nil
-}
-
-// marshal returns the byte representation of the partMessage.
-func (m partMessage) marshal() []byte {
-	return m.data
-}
-
-// getPadding returns the padding in the message.
-func (m partMessage) getPadding() []byte {
-	return m.padding
-}
-
-// setPadding sets the partMessage padding to the given bytes. Note that this
-// padding should be random bytes generated via the appropriate crypto function.
-func (m partMessage) setPadding(b []byte) {
-	copy(m.padding, b)
-}
-
-// getPartNum returns the file part number.
-func (m partMessage) getPartNum() uint16 {
-	return binary.LittleEndian.Uint16(m.partNum)
-}
-
-// setPartNum sets the file part number.
-func (m partMessage) setPartNum(num uint16) {
-	b := make([]byte, partNumLen)
-	binary.LittleEndian.PutUint16(b, num)
-	copy(m.partNum, b)
-}
-
-// getPart returns the file part data from the message.
-func (m partMessage) getPart() []byte {
-	return m.part
-}
-
-// setPart sets the partMessage part to the given bytes. An error is returned if
-// the size of the provided part data is too large to store.
-func (m partMessage) setPart(b []byte) error {
-	if len(b) > len(m.part) {
-		return errors.Errorf(setFileFmErr, len(b), len(m.part))
-	}
-
-	copy(m.part, b)
-
-	return nil
-}
-
-// getPartSize returns the number of bytes available to store part data.
-func (m partMessage) getPartSize() int {
-	return len(m.part)
-}
diff --git a/fileTransfer/fileMessage_test.go b/fileTransfer/fileMessage_test.go
deleted file mode 100644
index 77b72eac9fa78a04d6c4c36aceda9a0effbd8a3e..0000000000000000000000000000000000000000
--- a/fileTransfer/fileMessage_test.go
+++ /dev/null
@@ -1,283 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package fileTransfer
-
-import (
-	"bytes"
-	"encoding/binary"
-	"fmt"
-	"math/rand"
-	"testing"
-)
-
-// Tests that newPartMessage returns a partMessage of the expected size.
-func Test_newPartMessage(t *testing.T) {
-	externalPayloadSize := 256
-
-	fm, err := newPartMessage(externalPayloadSize)
-	if err != nil {
-		t.Errorf("newPartMessage returned an error: %+v", err)
-	}
-
-	if len(fm.data) != externalPayloadSize {
-		t.Errorf("Size of partMessage data does not match payload size."+
-			"\nexpected: %d\nreceived: %d", externalPayloadSize, len(fm.data))
-	}
-}
-
-// Error path: tests that newPartMessage returns the expected error when the
-// external payload size is too small.
-func Test_newPartMessage_SmallPayloadSizeError(t *testing.T) {
-	externalPayloadSize := fmMinSize - 1
-	expectedErr := fmt.Sprintf(newFmSizeErr, externalPayloadSize, fmMinSize)
-
-	_, err := newPartMessage(externalPayloadSize)
-	if err == nil || err.Error() != expectedErr {
-		t.Errorf("newPartMessage did not return the expected error when the "+
-			"given external payload size is too small."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
-
-// Tests that mapPartMessage maps the data to the correct parts of the
-// partMessage.
-func Test_mapPartMessage(t *testing.T) {
-	// Generate expected values
-	_, expectedData, expectedPadding, expectedPartNum, expectedFile :=
-		newRandomFileMessage()
-
-	fm := mapPartMessage(expectedData)
-
-	if !bytes.Equal(expectedData, fm.data) {
-		t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q",
-			expectedData, fm.data)
-	}
-
-	if !bytes.Equal(expectedPadding, fm.padding) {
-		t.Errorf("Incorrect padding data.\nexpected: %q\nreceived: %q",
-			expectedPadding, fm.padding)
-	}
-
-	if !bytes.Equal(expectedPartNum, fm.partNum) {
-		t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q",
-			expectedPartNum, fm.partNum)
-	}
-
-	if !bytes.Equal(expectedFile, fm.part) {
-		t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q",
-			expectedFile, fm.part)
-	}
-
-}
-
-// Tests that unmarshalPartMessage returns a partMessage with the expected
-// values.
-func Test_unmarshalPartMessage(t *testing.T) {
-	// Generate expected values
-	_, expectedData, expectedPadding, expectedPartNumb, expectedFile :=
-		newRandomFileMessage()
-
-	fm, err := unmarshalPartMessage(expectedData)
-	if err != nil {
-		t.Errorf("unmarshalPartMessage return an error: %+v", err)
-	}
-
-	if !bytes.Equal(expectedData, fm.data) {
-		t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q",
-			expectedData, fm.data)
-	}
-
-	if !bytes.Equal(expectedPadding, fm.padding) {
-		t.Errorf("Incorrect padding data.\nexpected: %q\nreceived: %q",
-			expectedPadding, fm.padding)
-	}
-
-	if !bytes.Equal(expectedPartNumb, fm.partNum) {
-		t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q",
-			expectedPartNumb, fm.partNum)
-	}
-
-	if !bytes.Equal(expectedFile, fm.part) {
-		t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q",
-			expectedFile, fm.part)
-	}
-}
-
-// Error path: tests that unmarshalPartMessage returns the expected error when
-// the provided data is too small to be unmarshalled into a partMessage.
-func Test_unmarshalPartMessage_SizeError(t *testing.T) {
-	data := make([]byte, fmMinSize-1)
-	expectedErr := fmt.Sprintf(unmarshalFmSizeErr, len(data), fmMinSize)
-
-	_, err := unmarshalPartMessage(data)
-	if err == nil || err.Error() != expectedErr {
-		t.Errorf("unmarshalPartMessage did not return the expected error when "+
-			"the given bytes are too small to be a partMessage."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
-
-// Tests that partMessage.marshal returns the correct data.
-func Test_fileMessage_marshal(t *testing.T) {
-	fm, expectedData, _, _, _ := newRandomFileMessage()
-
-	data := fm.marshal()
-
-	if !bytes.Equal(expectedData, data) {
-		t.Errorf("Marshalled data does not match expected."+
-			"\nexpected: %q\nreceived: %q", expectedData, data)
-	}
-}
-
-// Tests that partMessage.getPadding returns the correct padding data.
-func Test_fileMessage_getPadding(t *testing.T) {
-	fm, _, expectedPadding, _, _ := newRandomFileMessage()
-
-	padding := fm.getPadding()
-
-	if !bytes.Equal(expectedPadding, padding) {
-		t.Errorf("Padding data does not match expected."+
-			"\nexpected: %q\nreceived: %q", expectedPadding, padding)
-	}
-}
-
-// Tests that partMessage.setPadding sets the correct data.
-func Test_fileMessage_setPadding(t *testing.T) {
-	fm, err := newPartMessage(256)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	expectedPadding := make([]byte, paddingLen)
-	rand.New(rand.NewSource(42)).Read(expectedPadding)
-
-	fm.setPadding(expectedPadding)
-
-	if !bytes.Equal(expectedPadding, fm.getPadding()) {
-		t.Errorf("Failed to set correct padding.\nexpected: %q\nreceived: %q",
-			expectedPadding, fm.getPadding())
-	}
-}
-
-// Tests that partMessage.getPartNum returns the correct part number.
-func Test_fileMessage_getPartNum(t *testing.T) {
-	fm, _, _, expectedPartNum, _ := newRandomFileMessage()
-
-	partNum := fm.getPartNum()
-	expected := binary.LittleEndian.Uint16(expectedPartNum)
-
-	if expected != partNum {
-		t.Errorf("Part number does not match expected."+
-			"\nexpected: %d\nreceived: %d", expected, partNum)
-	}
-}
-
-// Tests that partMessage.setPartNum sets the correct part number.
-func Test_fileMessage_setPartNum(t *testing.T) {
-	fm, err := newPartMessage(256)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	expectedPartNum := make([]byte, partNumLen)
-	rand.New(rand.NewSource(42)).Read(expectedPartNum)
-	expected := binary.LittleEndian.Uint16(expectedPartNum)
-
-	fm.setPartNum(expected)
-
-	if expected != fm.getPartNum() {
-		t.Errorf("Failed to set correct part number.\nexpected: %d\nreceived: %d",
-			expected, fm.getPartNum())
-	}
-}
-
-// Tests that partMessage.getPart returns the correct part data.
-func Test_fileMessage_getFile(t *testing.T) {
-	fm, _, _, _, expectedFile := newRandomFileMessage()
-
-	file := fm.getPart()
-
-	if !bytes.Equal(expectedFile, file) {
-		t.Errorf("File data does not match expected."+
-			"\nexpected: %q\nreceived: %q", expectedFile, file)
-	}
-}
-
-// Tests that partMessage.setPart sets the correct part data.
-func Test_fileMessage_setFile(t *testing.T) {
-	fm, err := newPartMessage(256)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	fileData := make([]byte, 64)
-	rand.New(rand.NewSource(42)).Read(fileData)
-	expectedFile := make([]byte, fm.getPartSize())
-	copy(expectedFile, fileData)
-
-	err = fm.setPart(expectedFile)
-	if err != nil {
-		t.Errorf("setPart returned an error: %+v", err)
-	}
-
-	if !bytes.Equal(expectedFile, fm.getPart()) {
-		t.Errorf("Failed to set correct part data.\nexpected: %q\nreceived: %q",
-			expectedFile, fm.getPart())
-	}
-}
-
-// Error path: tests that partMessage.setPart returns the expected error when
-// the provided part data is too large for the message.
-func Test_fileMessage_setFile_FileTooLargeError(t *testing.T) {
-	fm, err := newPartMessage(fmMinSize + 1)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	expectedErr := fmt.Sprintf(setFileFmErr, fm.getPartSize()+1, fm.getPartSize())
-
-	err = fm.setPart(make([]byte, fm.getPartSize()+1))
-	if err == nil || err.Error() != expectedErr {
-		t.Errorf("setPart did not return the expected error when the given "+
-			"part data is too large to fit in the partMessage."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
-
-// Tests that partMessage.getPartSize returns the expected available space for
-// the part data.
-func Test_fileMessage_getFileSize(t *testing.T) {
-	expectedSize := 256
-
-	fm, err := newPartMessage(fmMinSize + expectedSize)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	if expectedSize != fm.getPartSize() {
-		t.Errorf("File size incorrect.\nexpected: %d\nreceived: %d",
-			expectedSize, fm.getPartSize())
-	}
-}
-
-// newRandomFileMessage generates a new partMessage filled with random data and
-// return the partMessage and its individual parts.
-func newRandomFileMessage() (partMessage, []byte, []byte, []byte, []byte) {
-	prng := rand.New(rand.NewSource(42))
-	padding := make([]byte, paddingLen)
-	prng.Read(padding)
-	partNum := make([]byte, partNumLen)
-	prng.Read(partNum)
-	part := make([]byte, 64)
-	prng.Read(part)
-	data := append(append(padding, partNum...), part...)
-
-	fm := mapPartMessage(data)
-
-	return fm, data, padding, partNum, part
-}
diff --git a/fileTransfer/manager.go b/fileTransfer/manager.go
index e91303742ebc27ccb95810e004cd69b2d8449d62..b188bcc203fe5e286507a5f7e7de4cb34485a836 100644
--- a/fileTransfer/manager.go
+++ b/fileTransfer/manager.go
@@ -101,8 +101,8 @@ type Manager struct {
 	sendQueue chan queuedPart
 
 	// Indicates if old transfers saved to storage have been recovered after
-	// file transfer is closed and reopened
-	oldTransfersRecovered bool
+	// file transfer is closed and reopened; this is an atomic
+	oldTransfersRecovered *uint32
 
 	// File transfer parameters
 	p Params
@@ -159,14 +159,16 @@ func newManager(client *api.Client, store *storage.Session,
 	}
 
 	jww.DEBUG.Printf(""+
-		"[FT] Created mew file transfer manager with params: %+v", p)
+		"[FT] Created new file transfer manager with params: %+v", p)
+
+	oldTransfersRecovered := uint32(0)
 
 	return &Manager{
 		receiveCB:             receiveCB,
 		sent:                  sent,
 		received:              received,
 		sendQueue:             make(chan queuedPart, sendQueueBuffLen),
-		oldTransfersRecovered: false,
+		oldTransfersRecovered: &oldTransfersRecovered,
 		p:                     p,
 		client:                client,
 		store:                 store,
diff --git a/fileTransfer/manager_test.go b/fileTransfer/manager_test.go
index c1133aa32a8f0f0e2fa09139678cdfd224e44fa2..63952f154487962fc6e504c56d58f6d0d00cda02 100644
--- a/fileTransfer/manager_test.go
+++ b/fileTransfer/manager_test.go
@@ -388,14 +388,13 @@ func TestManager_Resend_NoFingerprints(t *testing.T) {
 func TestManager_CloseSend_NoFingerprints(t *testing.T) {
 	m, sti, _ := newTestManagerWithTransfers(
 		[]uint16{16}, false, false, nil, nil, nil, t)
-	prng := NewPrng(42)
 	partSize, _ := m.getPartSize()
 
 	// Use up all the fingerprints in the transfer
 	transfer, _ := m.sent.GetTransfer(sti[0].tid)
 	for fpNum := uint16(0); fpNum < sti[0].numFps; fpNum++ {
 		partNum := fpNum % sti[0].numParts
-		_, _, _, _, err := transfer.GetEncryptedPart(partNum, partSize, prng)
+		_, _, _, err := transfer.GetEncryptedPart(partNum, partSize+2)
 		if err != nil {
 			t.Errorf("Failed to encrypt part %d (%d): %+v", partNum, fpNum, err)
 		}
diff --git a/fileTransfer/oldTransferRecovery.go b/fileTransfer/oldTransferRecovery.go
index 2957e1b156835be260c470910b8767d0653a8b4b..7fc4d41c61915d070717a3001c4154237cf0f81c 100644
--- a/fileTransfer/oldTransferRecovery.go
+++ b/fileTransfer/oldTransferRecovery.go
@@ -12,6 +12,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
 	"gitlab.com/xx_network/primitives/id"
+	"sync/atomic"
 )
 
 // Error messages.
@@ -29,7 +30,9 @@ const roundResultsMaxAttempts = 5
 func (m Manager) oldTransferRecovery(healthyChan chan bool, chanID uint64) {
 
 	// Exit if old transfers have already been recovered
-	if m.oldTransfersRecovered {
+	// TODO: move GetUnsentPartsAndSentRounds to manager creation and remove the
+	//  atomic
+	if !atomic.CompareAndSwapUint32(m.oldTransfersRecovered, 0, 1) {
 		jww.DEBUG.Printf("[FT] Old file transfer recovery thread not " +
 			"starting: none to recover (app was not closed)")
 		return
@@ -38,6 +41,9 @@ func (m Manager) oldTransferRecovery(healthyChan chan bool, chanID uint64) {
 	// Get list of unsent parts and rounds that parts were sent on
 	unsentParts, sentRounds, err := m.sent.GetUnsentPartsAndSentRounds()
 
+	jww.DEBUG.Printf("Adding unsent parts from %d recovered transfers: %v",
+		len(unsentParts), unsentParts)
+
 	// Add all unsent parts to the queue
 	for tid, partNums := range unsentParts {
 		m.queueParts(tid, partNums)
@@ -106,7 +112,9 @@ func (m Manager) updateSentRounds(healthyChan chan bool,
 					roundList, err)
 			} else {
 				jww.INFO.Printf(
-					"[FT] Successfully recovered old file transfers.")
+					"[FT] Successfully recovered old file transfers: %v",
+					sentRounds)
+
 				return nil
 			}
 			getRoundResultsAttempts++
diff --git a/fileTransfer/receive.go b/fileTransfer/receive.go
index 9a70f5853212bae431653c096d9168d567008b2a..3552fdf5635a819f66cbecfb9945ca01ab529846 100644
--- a/fileTransfer/receive.go
+++ b/fileTransfer/receive.go
@@ -8,7 +8,6 @@
 package fileTransfer
 
 import (
-	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces/message"
 	"gitlab.com/elixxir/client/stoppable"
@@ -64,16 +63,8 @@ func (m *Manager) readMessage(msg message.Receive) (format.Message, error) {
 		return cMixMsg, err
 	}
 
-	// Unmarshal cMix message contents into a file part message
-	partMsg, err := unmarshalPartMessage(cMixMsg.GetContents())
-	if err != nil {
-		return cMixMsg, errors.Errorf(unmarshalPartMessageErr, err)
-	}
-
 	// Add part to received transfer
-	rt, tid, completed, err := m.received.AddPart(partMsg.getPart(),
-		partMsg.getPadding(), cMixMsg.GetMac(), partMsg.getPartNum(),
-		cMixMsg.GetKeyFP())
+	rt, tid, completed, err := m.received.AddPart(cMixMsg)
 	if err != nil {
 		return cMixMsg, err
 	}
diff --git a/fileTransfer/receiveNew_test.go b/fileTransfer/receiveNew_test.go
index 872012beee4ed019a035212c85f45a16ea7a1926..4aea311faa6e0fedadf6f627e8f4b43eb544753e 100644
--- a/fileTransfer/receiveNew_test.go
+++ b/fileTransfer/receiveNew_test.go
@@ -51,7 +51,7 @@ func TestManager_receiveNewFileTransfer(t *testing.T) {
 	}
 	marshalledMsg, err := proto.Marshal(protoMsg)
 	if err != nil {
-		t.Errorf("Failed to marshal proto message: %+v", err)
+		t.Errorf("Failed to Marshal proto message: %+v", err)
 	}
 	receiveMsg := message.Receive{
 		Payload:     marshalledMsg,
@@ -120,7 +120,7 @@ func TestManager_receiveNewFileTransfer_Stop(t *testing.T) {
 	}
 	marshalledMsg, err := proto.Marshal(protoMsg)
 	if err != nil {
-		t.Errorf("Failed to marshal proto message: %+v", err)
+		t.Errorf("Failed to Marshal proto message: %+v", err)
 	}
 	receiveMsg := message.Receive{
 		Payload:     marshalledMsg,
diff --git a/fileTransfer/send.go b/fileTransfer/send.go
index dbab0994774046b8ce5fa62ab8030fbde4335fd8..daeca373c1cba813f000840f39f747049db6262d 100644
--- a/fileTransfer/send.go
+++ b/fileTransfer/send.go
@@ -33,7 +33,7 @@ import (
 const (
 	// Manager.sendParts
 	sendManyCmixWarn   = "[FT] Failed to send %d file parts %v via SendManyCMIX: %+v"
-	setInProgressErr   = "[FT] Failed to set parts %v to in-progress for transfer %s"
+	setInProgressErr   = "[FT] Failed to set parts %v to in-progress for transfer %s: %+v"
 	getRoundResultsErr = "[FT] Failed to get round results for round %d for file transfers %v: %+v"
 
 	// Manager.buildMessages
@@ -47,7 +47,7 @@ const (
 	finishedEndE2eMsfErr    = "[FT] Failed to send E2E message to %s on completion of file transfer %s: %+v"
 	roundFailureWarn        = "[FT] Failed to send file parts for file transfers %v on round %d: round %s"
 	finishFailNoTransferErr = "[FT] Failed to requeue in-progress parts on failure of round %d for transfer %s: %+v"
-	unsetInProgressErr      = "[FT] Failed to remove parts from in-progress list for transfer %s: round %s"
+	unsetInProgressErr      = "[FT] Failed to remove parts from in-progress list for transfer %s: round %s: %+v"
 
 	// Manager.sendEndE2eMessage
 	endE2eGetPartnerErr = "failed to get file transfer partner %s: %+v"
@@ -211,7 +211,7 @@ func (m *Manager) handleSend(partList *[]queuedPart, lastSend *time.Time,
 	// Send all the messages
 	err := m.sendParts(*partList, sentRounds)
 	if err != nil {
-		jww.FATAL.Panic(err)
+		jww.ERROR.Print(err)
 	}
 
 	// Update the timestamp of the send
@@ -270,7 +270,7 @@ func (m *Manager) sendParts(partList []queuedPart,
 	for tid, transfer := range transfers {
 		exists, err := transfer.SetInProgress(rid, groupedParts[tid]...)
 		if err != nil {
-			return errors.Errorf(setInProgressErr, groupedParts[tid], tid)
+			return errors.Errorf(setInProgressErr, groupedParts[tid], tid, err)
 		}
 
 		transfer.CallProgressCB(nil)
@@ -364,30 +364,14 @@ func (m *Manager) newCmixMessage(transfer *ftStorage.SentTransfer,
 	// Create new empty cMix message
 	cmixMsg := format.NewMessage(m.store.Cmix().GetGroup().GetP().ByteLen())
 
-	// Create new empty file part message of size equal to the available payload
-	// size in the cMix message
-	partMsg, err := newPartMessage(cmixMsg.ContentsSize())
-	if err != nil {
-		return cmixMsg, err
-	}
-
-	// Get encrypted file part, file part MAC, padding (nonce), and fingerprint
-	encPart, mac, padding, fp, err := transfer.GetEncryptedPart(
-		partNum, partMsg.getPartSize(), rng)
-	if err != nil {
-		return cmixMsg, err
-	}
-
-	// Construct file part message from padding (
-	partMsg.setPadding(padding)
-	partMsg.setPartNum(partNum)
-	err = partMsg.setPart(encPart)
+	// Get encrypted file part, file part MAC, nonce (nonce), and fingerprint
+	encPart, mac, fp, err := transfer.GetEncryptedPart(partNum, cmixMsg.ContentsSize())
 	if err != nil {
-		return cmixMsg, err
+		return format.Message{}, err
 	}
 
 	// Construct cMix message
-	cmixMsg.SetContents(partMsg.marshal())
+	cmixMsg.SetContents(encPart)
 	cmixMsg.SetKeyFP(fp)
 	cmixMsg.SetMac(mac)
 
@@ -460,7 +444,8 @@ func (m *Manager) makeRoundEventCallback(
 					// Remove parts from in-progress list
 					partsToResend, err := st.UnsetInProgress(rid)
 					if err != nil {
-						jww.ERROR.Printf(unsetInProgressErr, tid, roundResult)
+						jww.ERROR.Printf(
+							unsetInProgressErr, tid, roundResult, err)
 					}
 
 					// Call progress callback after change in progress
@@ -529,7 +514,7 @@ func (m *Manager) sendEndE2eMessage(recipient *id.ID) error {
 	// the session and the log
 	m.store.GetCriticalMessages().Succeeded(sendMsg, e2eParams)
 	jww.INFO.Printf("[FT] Sending of message %s informing %s that a transfer "+
-		"ended successful.", e2eMsgID, recipient)
+		"completed successfully.", e2eMsgID, recipient)
 
 	return nil
 }
@@ -565,12 +550,12 @@ func (m *Manager) getPartSize() (int, error) {
 
 	// Create new empty file part message of size equal to the available payload
 	// size in the cMix message
-	partMsg, err := newPartMessage(cmixMsg.ContentsSize())
+	partMsg, err := ftStorage.NewPartMessage(cmixMsg.ContentsSize())
 	if err != nil {
 		return 0, err
 	}
 
-	return partMsg.getPartSize(), nil
+	return partMsg.GetPartSize(), nil
 }
 
 // partitionFile splits the file into parts of the specified part size.
diff --git a/fileTransfer/send_test.go b/fileTransfer/send_test.go
index ee10658b1fe6c190cbaa9a04879c72829a726787..2e3d3d16d8325be32ba975decb14b3af41f57636 100644
--- a/fileTransfer/send_test.go
+++ b/fileTransfer/send_test.go
@@ -662,20 +662,20 @@ func TestManager_newCmixMessage(t *testing.T) {
 			"\nexpected: %s\nrecieved: %s", fp, cmixMsg.GetKeyFP())
 	}
 
-	partMsg, err := unmarshalPartMessage(cmixMsg.GetContents())
+	decrPart, err := ftCrypto.DecryptPart(key, cmixMsg.GetContents(),
+		 cmixMsg.GetMac(), 0,cmixMsg.GetKeyFP())
 	if err != nil {
-		t.Errorf("Failed to unmarshal part message: %+v", err)
+		t.Errorf("Failed to decrypt file part: %+v", err)
 	}
 
-	decrPart, err := ftCrypto.DecryptPart(key, partMsg.getPart(),
-		partMsg.getPadding(), cmixMsg.GetMac(), partMsg.getPartNum())
+	partMsg, err := ftStorage.UnmarshalPartMessage(decrPart)
 	if err != nil {
-		t.Errorf("Failed to decrypt file part: %+v", err)
+		t.Errorf("Failed to unmarshal part message: %+v", err)
 	}
 
-	if !bytes.Equal(decrPart, parts[0]) {
+	if !bytes.Equal(partMsg.GetPart(), parts[0]) {
 		t.Errorf("Decrypted part does not match expected."+
-			"\nexpected: %q\nreceived: %q", parts[0], decrPart)
+			"\nexpected: %q\nreceived: %q", parts[0], partMsg.GetPart())
 	}
 }
 
@@ -998,7 +998,7 @@ func Test_makeListOfPartNums(t *testing.T) {
 	}
 }
 
-// Tests that the part size returned by Manager.getPartSize matches the manually
+// Tests that the part size returned by Manager.GetPartSize matches the manually
 // calculated part size.
 func TestManager_getPartSize(t *testing.T) {
 	m := newTestManager(false, nil, nil, nil, nil, t)
@@ -1006,13 +1006,13 @@ func TestManager_getPartSize(t *testing.T) {
 	// Calculate the expected part size
 	primeByteLen := m.store.Cmix().GetGroup().GetP().ByteLen()
 	cmixMsgUsedLen := format.AssociatedDataSize
-	filePartMsgUsedLen := fmMinSize
+	filePartMsgUsedLen := ftStorage.FmMinSize
 	expected := 2*primeByteLen - cmixMsgUsedLen - filePartMsgUsedLen-1
 
 	// Get the part size
 	partSize, err := m.getPartSize()
 	if err != nil {
-		t.Errorf("getPartSize returned an error: %+v", err)
+		t.Errorf("GetPartSize returned an error: %+v", err)
 	}
 
 	if expected != partSize {
diff --git a/fileTransfer/utils_test.go b/fileTransfer/utils_test.go
index 7738b4529052317bf77a952da2d0e5dea311249d..a982d3ba226565db9e4886132f164998560826a6 100644
--- a/fileTransfer/utils_test.go
+++ b/fileTransfer/utils_test.go
@@ -191,17 +191,20 @@ func newTestManager(sendErr bool, sendChan, sendE2eChan chan message.Receive,
 	avgSendSize := avgNumMessages * (8192 / 8)
 	p.MaxThroughput = int(time.Second) * avgSendSize
 
+	oldTransfersRecovered := uint32(0)
+
 	m := &Manager{
-		receiveCB:       receiveCB,
-		sent:            sent,
-		received:        received,
-		sendQueue:       make(chan queuedPart, sendQueueBuffLen),
-		p:               p,
-		store:           storage.InitTestingSession(t),
-		swb:             switchboard.New(),
-		net:             net,
-		rng:             fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG),
-		getRoundResults: rr,
+		receiveCB:             receiveCB,
+		sent:                  sent,
+		received:              received,
+		sendQueue:             make(chan queuedPart, sendQueueBuffLen),
+		oldTransfersRecovered: &oldTransfersRecovered,
+		p:                     p,
+		store:                 storage.InitTestingSession(t),
+		swb:                   switchboard.New(),
+		net:                   net,
+		rng:                   fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG),
+		getRoundResults:       rr,
 	}
 
 	return m
@@ -495,6 +498,7 @@ func (tnm *testNetworkManager) SendManyCMIX(messages []message.TargetedCmixMessa
 		for _, msg := range messages {
 			tnm.sendChan <- message.Receive{
 				Payload: msg.Message.Marshal(),
+				Sender:  &id.ID{0},
 				RoundId: tnm.rid,
 			}
 		}
diff --git a/go.mod b/go.mod
index 640dc632a534b03e130a0a34cd69655706fa4213..612f49f79f95504cc7ef282b6ca2418cb00e7370 100644
--- a/go.mod
+++ b/go.mod
@@ -19,8 +19,8 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20220107213343-9f1e103033c0
-	gitlab.com/elixxir/crypto v0.0.7-0.20220107220614-ebd5833c6a97
+	gitlab.com/elixxir/comms v0.0.4-0.20220108185838-d88d862a7a6a
+	gitlab.com/elixxir/crypto v0.0.7-0.20220108181613-7e3f9b9e7434
 	gitlab.com/elixxir/ekv v0.1.6
 	gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f
 	gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda
diff --git a/go.sum b/go.sum
index cf19fd81937f634eb318444c3632d385e45fd953..6c1b5be8af49318b1add387e789a73f95cd13cdc 100644
--- a/go.sum
+++ b/go.sum
@@ -270,18 +270,12 @@ github.com/zeebo/pcg v1.0.0 h1:dt+dx+HvX8g7Un32rY9XWoYnd0NmKmrIzpHF7qiTDj0=
 github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0BJIk1HIzBVMjWNjIUfstrsXC2VqLYPcA=
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
-gitlab.com/elixxir/comms v0.0.4-0.20220107213343-9f1e103033c0 h1:txl/CpDfILEXnbXrKytuQ3DO7mEf0c3gffUOkF07aG8=
-gitlab.com/elixxir/comms v0.0.4-0.20220107213343-9f1e103033c0/go.mod h1:hO02O34PPUz1ozBc0UR1UIEooniuoI9ssRXBUiSEU40=
+gitlab.com/elixxir/comms v0.0.4-0.20220108185838-d88d862a7a6a h1:CV844Fkl7lFAG1DgPybFGYnmyYNZHSaedxsMqyfoDlI=
+gitlab.com/elixxir/comms v0.0.4-0.20220108185838-d88d862a7a6a/go.mod h1:IOzur3kBTqxV24izawS2VmIyvM8qSmXRg+uY5ooVpt8=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
-gitlab.com/elixxir/crypto v0.0.7-0.20220107212555-7b80ecaff6d6 h1:/ZZzlUqeeN+VWnaU+MfOJ743VnkhrUrSWO9pdz9PJHU=
-gitlab.com/elixxir/crypto v0.0.7-0.20220107212555-7b80ecaff6d6/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48=
-gitlab.com/elixxir/crypto v0.0.7-0.20220107220614-ebd5833c6a97 h1:SPfQBMMCKJHHG5ceKsrYzdixzCGQFf2sL0dO+FeLqks=
-gitlab.com/elixxir/crypto v0.0.7-0.20220107220614-ebd5833c6a97/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48=
-gitlab.com/elixxir/crypto v0.0.7-0.20220107220814-3dc5e0f3df54 h1:mWxjZTVPV4nxdz7IAWWGUoARWGCKhEhagaq89uSkPBA=
-gitlab.com/elixxir/crypto v0.0.7-0.20220107220814-3dc5e0f3df54/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48=
-gitlab.com/elixxir/crypto v0.0.7-0.20220107220916-b98ad66916dd h1:OKbDcBirU3zCG+1K/FGu1Z/chjxXEsUGzyQtEQHRm4Q=
-gitlab.com/elixxir/crypto v0.0.7-0.20220107220916-b98ad66916dd/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48=
+gitlab.com/elixxir/crypto v0.0.7-0.20220108181613-7e3f9b9e7434 h1:4wXhEflExxO5Dzaxmz4WqUFmoRjr3ZNJ8pFBsyRXq+c=
+gitlab.com/elixxir/crypto v0.0.7-0.20220108181613-7e3f9b9e7434/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48=
 gitlab.com/elixxir/ekv v0.1.6 h1:M2hUSNhH/ChxDd+s8xBqSEKgoPtmE6hOEBqQ73KbN6A=
 gitlab.com/elixxir/ekv v0.1.6/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4=
 gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
diff --git a/groupChat/sendRequests.go b/groupChat/sendRequests.go
index edcecc77f1b5516a9ed75d9481738210374b88e4..b48335911b5ff2d193a070dc62455b5378f769fb 100644
--- a/groupChat/sendRequests.go
+++ b/groupChat/sendRequests.go
@@ -117,7 +117,17 @@ func (m Manager) sendRequest(memberID *id.ID, request []byte) ([]id.Round, error
 		MessageType: message.GroupCreationRequest,
 	}
 
-	rounds, _, _, err := m.net.SendE2E(sendMsg, params.GetDefaultE2E(), nil)
+
+	recipent, err := m.store.E2e().GetPartner(memberID)
+	if err!=nil{
+		return nil, errors.WithMessagef(err,"Failed to send request to %s " +
+			"because e2e relationship could not be found", memberID)
+	}
+
+	p := params.GetDefaultE2E()
+	p.IdentityPreimage = recipent.GetGroupRequestPreimage()
+
+	rounds, _, _, err := m.net.SendE2E(sendMsg, p, nil)
 	if err != nil {
 		return nil, errors.Errorf(sendE2eErr, memberID, err)
 	}
diff --git a/groupChat/sendRequests_test.go b/groupChat/sendRequests_test.go
index 9c22c13fed92f6b986a6f71db63d6f9aea3a230a..9f2b9c19f2eb2c65dd51d3f66e3dc512cfe991b3 100644
--- a/groupChat/sendRequests_test.go
+++ b/groupChat/sendRequests_test.go
@@ -9,8 +9,13 @@ package groupChat
 
 import (
 	"fmt"
+	"github.com/cloudflare/circl/dh/sidh"
 	"github.com/golang/protobuf/proto"
 	"gitlab.com/elixxir/client/interfaces/message"
+	"gitlab.com/elixxir/client/interfaces/params"
+	util "gitlab.com/elixxir/client/storage/utility"
+	"gitlab.com/elixxir/crypto/diffieHellman"
+	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/primitives/id"
 	"math/rand"
 	"reflect"
@@ -33,11 +38,29 @@ func TestManager_ResendRequest(t *testing.T) {
 		Created:     g.Created.UnixNano(),
 	}
 
+	for i := range g.Members{
+		grp := m.store.E2e().GetGroup()
+		dhKey := grp.NewInt(int64(i + 42))
+		pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+		p := params.GetDefaultE2ESessionParams()
+		rng := csprng.NewSystemRNG()
+		_, mySidhPriv := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhA, rng)
+		theirSidhPub, _ := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhB, rng)
+		err := m.store.E2e().AddPartner(g.Members[i].ID, pubKey, dhKey,
+			mySidhPriv, theirSidhPub, p, p)
+		if err != nil {
+			t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err)
+		}
+	}
+
 	_, status, err := m.ResendRequest(g.ID)
 	if err != nil {
 		t.Errorf("ResendRequest() returned an error: %+v", err)
 	}
 
+
 	if status != AllSent {
 		t.Errorf("ResendRequest() failed to return the expected status."+
 			"\nexpected: %s\nreceived: %s", AllSent, status)
@@ -112,6 +135,23 @@ func TestManager_sendRequests(t *testing.T) {
 		Created:     g.Created.UnixNano(),
 	}
 
+	for i := range g.Members{
+		grp := m.store.E2e().GetGroup()
+		dhKey := grp.NewInt(int64(i + 42))
+		pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+		p := params.GetDefaultE2ESessionParams()
+		rng := csprng.NewSystemRNG()
+		_, mySidhPriv := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhA, rng)
+		theirSidhPub, _ := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhB, rng)
+		err := m.store.E2e().AddPartner(g.Members[i].ID, pubKey, dhKey,
+			mySidhPriv, theirSidhPub, p, p)
+		if err != nil {
+			t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err)
+		}
+	}
+
 	_, status, err := m.sendRequests(g)
 	if err != nil {
 		t.Errorf("sendRequests() returned an error: %+v", err)
@@ -195,6 +235,23 @@ func TestManager_sendRequests_SendPartialSent(t *testing.T) {
 	expectedErr := fmt.Sprintf(sendRequestPartialErr, (len(g.Members)-1)/2,
 		len(g.Members)-1, "")
 
+	for i := range g.Members{
+		grp := m.store.E2e().GetGroup()
+		dhKey := grp.NewInt(int64(i + 42))
+		pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+		p := params.GetDefaultE2ESessionParams()
+		rng := csprng.NewSystemRNG()
+		_, mySidhPriv := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhA, rng)
+		theirSidhPub, _ := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhB, rng)
+		err := m.store.E2e().AddPartner(g.Members[i].ID, pubKey, dhKey,
+			mySidhPriv, theirSidhPub, p, p)
+		if err != nil {
+			t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err)
+		}
+	}
+
 	_, status, err := m.sendRequests(g)
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("sendRequests() failed to return the expected error."+
@@ -217,6 +274,23 @@ func TestManager_sendRequest(t *testing.T) {
 	prng := rand.New(rand.NewSource(42))
 	m, g := newTestManagerWithStore(prng, 10, 0, nil, nil, t)
 
+	for i := range g.Members{
+		grp := m.store.E2e().GetGroup()
+		dhKey := grp.NewInt(int64(i + 42))
+		pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+		p := params.GetDefaultE2ESessionParams()
+		rng := csprng.NewSystemRNG()
+		_, mySidhPriv := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhA, rng)
+		theirSidhPub, _ := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhB, rng)
+		err := m.store.E2e().AddPartner(g.Members[i].ID, pubKey, dhKey,
+			mySidhPriv, theirSidhPub, p, p)
+		if err != nil {
+			t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err)
+		}
+	}
+
 	expected := message.Send{
 		Recipient:   g.Members[0].ID,
 		Payload:     []byte("request message"),
@@ -241,7 +315,25 @@ func TestManager_sendRequest_SendE2eError(t *testing.T) {
 	m, _ := newTestManagerWithStore(prng, 10, 1, nil, nil, t)
 	expectedErr := strings.SplitN(sendE2eErr, "%", 2)[0]
 
-	_, err := m.sendRequest(id.NewIdFromString("memberID", id.User, t), nil)
+	recipientID := id.NewIdFromString("memberID", id.User, t)
+
+	grp := m.store.E2e().GetGroup()
+	dhKey := grp.NewInt(int64(42))
+	pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+	p := params.GetDefaultE2ESessionParams()
+	rng := csprng.NewSystemRNG()
+	_, mySidhPriv := util.GenerateSIDHKeyPair(
+		sidh.KeyVariantSidhA, rng)
+	theirSidhPub, _ := util.GenerateSIDHKeyPair(
+		sidh.KeyVariantSidhB, rng)
+	err := m.store.E2e().AddPartner(recipientID, pubKey, dhKey,
+		mySidhPriv, theirSidhPub, p, p)
+	if err != nil {
+		t.Errorf("Failed to add partner %s: %+v", recipientID, err)
+	}
+
+
+	_, err = m.sendRequest(recipientID, nil)
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("sendRequest() failed to return the expected error."+
 			"\nexpected: %s\nreceived: %+v", expectedErr, err)
diff --git a/interfaces/preimage/types.go b/interfaces/preimage/types.go
index 0b87ef34c47bc1574a94813ebb77ebb0155a67d8..bd6226e623594561ef0338210af8824c1b789f27 100644
--- a/interfaces/preimage/types.go
+++ b/interfaces/preimage/types.go
@@ -8,4 +8,5 @@ const (
 	E2e     = "e2e"
 	Group   = "group"
 	EndFT   = "endFT"
+	GroupRq = "groupRq"
 )
diff --git a/keyExchange/confirm.go b/keyExchange/confirm.go
index d754acc1da040efed8e55227d623c0cc5c414342..4c21f77db86638f24a50fcf65b65001e71e081c9 100644
--- a/keyExchange/confirm.go
+++ b/keyExchange/confirm.go
@@ -61,7 +61,7 @@ func handleConfirm(sess *storage.Session, confirmation message.Receive) {
 	confirmedSession := partner.GetSendSession(confimedSessionID)
 	if confirmedSession == nil {
 		jww.ERROR.Printf("[REKEY] Failed to find confirmed session %s from "+
-			"partner %s: %s", confimedSessionID, confirmation.Sender, err)
+			"partner %s", confimedSessionID, confirmation.Sender)
 		return
 	}
 
diff --git a/network/message/garbled.go b/network/message/garbled.go
index 9388f4dcfb2c42f3f9c106cc949c893207451ced..b2449ca9f0d79c8d1b5cdd7a8e9cd9db1ae2863f 100644
--- a/network/message/garbled.go
+++ b/network/message/garbled.go
@@ -45,6 +45,7 @@ func (m *Manager) processGarbledMessages(stop *stoppable.Single) {
 			stop.ToStopped()
 			return
 		case <-m.triggerGarbled:
+			jww.INFO.Printf("[GARBLE] Checking Garbled messages")
 			m.handleGarbledMessages()
 		}
 	}
@@ -57,65 +58,72 @@ func (m *Manager) handleGarbledMessages() {
 	var failedMsgs []format.Message
 	//try to decrypt every garbled message, excising those who's counts are too high
 	for grbldMsg, count, timestamp, has := garbledMsgs.Next(); has; grbldMsg, count, timestamp, has = garbledMsgs.Next() {
-		fingerprint := grbldMsg.GetKeyFP()
-		// Check if the key is there, process it if it is
-		if key, isE2E := e2eKv.PopKey(fingerprint); isE2E {
-			jww.INFO.Printf("[GARBLE] Check E2E for %s, KEYFP: %s",
-				grbldMsg.Digest(), grbldMsg.GetKeyFP())
-			// Decrypt encrypted message
-			msg, err := key.Decrypt(grbldMsg)
-			if err == nil {
-				// get the sender
-				sender := key.GetSession().GetPartner()
-				//remove from the buffer if decryption is successful
-				garbledMsgs.Remove(grbldMsg)
+		//if it exists, check against all in the list
+		grbldContents := grbldMsg.GetContents()
+		identity := m.Session.GetUser().ReceptionID
+		_, forMe, _ := m.Session.GetEdge().Check(identity, grbldMsg.GetIdentityFP(), grbldContents)
+		if forMe {
+			fingerprint := grbldMsg.GetKeyFP()
+			// Check if the key is there, process it if it is
+			if key, isE2E := e2eKv.PopKey(fingerprint); isE2E {
+				jww.INFO.Printf("[GARBLE] Check E2E for %s, KEYFP: %s",
+					grbldMsg.Digest(), grbldMsg.GetKeyFP())
+				// Decrypt encrypted message
+				msg, err := key.Decrypt(grbldMsg)
+				if err == nil {
+					// get the sender
+					sender := key.GetSession().GetPartner()
+					//remove from the buffer if decryption is successful
+					garbledMsgs.Remove(grbldMsg)
 
-				jww.INFO.Printf("[GARBLE] message decoded as E2E from "+
-					"%s, msgDigest: %s", sender, grbldMsg.Digest())
+					jww.INFO.Printf("[GARBLE] message decoded as E2E from "+
+						"%s, msgDigest: %s", sender, grbldMsg.Digest())
 
-				//handle the successfully decrypted message
-				xxMsg, ok := m.partitioner.HandlePartition(sender, message.E2E,
-					msg.GetContents(),
-					key.GetSession().GetRelationshipFingerprint())
-				if ok {
-					m.Switchboard.Speak(xxMsg)
-					continue
-				}
-			}
-		} else {
-			// todo: figure out how to get the ephermal reception id in here.
-			// we have the raw data, but do not know what address space was
-			// used int he round
-			// todo: figure out how to get the round id, the recipient id, and the round timestamp
-			/*
-				ephid, err := ephemeral.Marshal(garbledMsg.GetEphemeralRID())
-				if err!=nil{
-					jww.WARN.Printf("failed to get the ephemeral id for a garbled " +
-						"message, clearing the message: %+v", err)
-					garbledMsgs.Remove(garbledMsg)
-					continue
+					//handle the successfully decrypted message
+					xxMsg, ok := m.partitioner.HandlePartition(sender, message.E2E,
+						msg.GetContents(),
+						key.GetSession().GetRelationshipFingerprint())
+					if ok {
+						m.Switchboard.Speak(xxMsg)
+						continue
+					}
 				}
+			} else {
+				// todo: figure out how to get the ephermal reception id in here.
+				// we have the raw data, but do not know what address space was
+				// used int he round
+				// todo: figure out how to get the round id, the recipient id, and the round timestamp
+				/*
+					ephid, err := ephemeral.Marshal(garbledMsg.GetEphemeralRID())
+					if err!=nil{
+						jww.WARN.Printf("failed to get the ephemeral id for a garbled " +
+							"message, clearing the message: %+v", err)
+						garbledMsgs.Remove(garbledMsg)
+						continue
+					}
 
-				ephid.Clear(m.)*/
+					ephid.Clear(m.)*/
 
-			raw := message.Receive{
-				Payload:        grbldMsg.Marshal(),
-				MessageType:    message.Raw,
-				Sender:         &id.ID{},
-				EphemeralID:    ephemeral.Id{},
-				Timestamp:      time.Time{},
-				Encryption:     message.None,
-				RecipientID:    &id.ID{},
-				RoundId:        0,
-				RoundTimestamp: time.Time{},
+				raw := message.Receive{
+					Payload:        grbldMsg.Marshal(),
+					MessageType:    message.Raw,
+					Sender:         &id.ID{},
+					EphemeralID:    ephemeral.Id{},
+					Timestamp:      time.Time{},
+					Encryption:     message.None,
+					RecipientID:    &id.ID{},
+					RoundId:        0,
+					RoundTimestamp: time.Time{},
+				}
+				im := fmt.Sprintf("[GARBLE] RAW Message reprocessed: keyFP: %v, "+
+					"msgDigest: %s", grbldMsg.GetKeyFP(), grbldMsg.Digest())
+				jww.INFO.Print(im)
+				m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
+				m.Session.GetGarbledMessages().Add(grbldMsg)
+				m.Switchboard.Speak(raw)
 			}
-			im := fmt.Sprintf("[GARBLE] RAW Message reprecessed: keyFP: %v, "+
-				"msgDigest: %s", grbldMsg.GetKeyFP(), grbldMsg.Digest())
-			jww.INFO.Print(im)
-			m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
-			m.Session.GetGarbledMessages().Add(grbldMsg)
-			m.Switchboard.Speak(raw)
 		}
+
 		// fail the message if any part of the decryption fails,
 		// unless it is the last attempts and has been in the buffer long
 		// enough, in which case remove it
diff --git a/network/message/garbled_test.go b/network/message/garbled_test.go
index 8656eda570f80f0a7f9333cbaa6f82b6ea80594e..4a0f7eda9cb50fe7e7ed61aee72040d3756edb10 100644
--- a/network/message/garbled_test.go
+++ b/network/message/garbled_test.go
@@ -11,10 +11,12 @@ import (
 	"gitlab.com/elixxir/client/network/message/parse"
 	"gitlab.com/elixxir/client/stoppable"
 	"gitlab.com/elixxir/client/storage"
+	"gitlab.com/elixxir/client/storage/edge"
 	util "gitlab.com/elixxir/client/storage/utility"
 	"gitlab.com/elixxir/client/switchboard"
 	"gitlab.com/elixxir/comms/client"
 	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/elixxir/crypto/fingerprint"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/crypto/csprng"
@@ -104,6 +106,13 @@ func TestManager_CheckGarbledMessages(t *testing.T) {
 		t.FailNow()
 	}
 
+	preimage := edge.Preimage{
+		Data:   []byte{0},
+		Type:   "test",
+		Source: nil,
+	}
+	m.Session.GetEdge().Add(preimage, sess2.GetUser().ReceptionID)
+
 	err = sess2.E2e().AddPartner(sess1.GetUser().TransmissionID,
 		sess1.E2e().GetDHPublicKey(), sess2.E2e().GetDHPrivateKey(),
 		mySIDHPubKey, partnerSIDHPrivKey,
@@ -143,6 +152,7 @@ func TestManager_CheckGarbledMessages(t *testing.T) {
 	copy(fmp.Timestamp, ts)
 	msg.SetContents(fmp.Bytes())
 	encryptedMsg := key.Encrypt(msg)
+	msg.SetIdentityFP(fingerprint.IdentityFP( msg.GetContents(), preimage.Data))
 	i.Session.GetGarbledMessages().Add(encryptedMsg)
 
 	stop := stoppable.NewSingle("stop")
diff --git a/network/message/handler.go b/network/message/handler.go
index b7574f4cf8636b67aaa37ef7330058dd037de2f6..7aa63e6ad85293b57d716d9bdf894736a3fd4bed 100644
--- a/network/message/handler.go
+++ b/network/message/handler.go
@@ -54,23 +54,27 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
 	var relationshipFingerprint []byte
 
 	//if it exists, check against all in the list
-	has, forMe, _ := m.Session.GetEdge().Check(identity.Source, ecrMsg.GetIdentityFP(), ecrMsg.GetContents())
+	ecrMsgContents := ecrMsg.GetContents()
+	has, forMe, _ := m.Session.GetEdge().Check(identity.Source, ecrMsg.GetIdentityFP(), ecrMsgContents)
 	if !has {
 		jww.INFO.Printf("checking backup %v", preimage.MakeDefault(identity.Source))
 		//if it doesnt exist, check against the default fingerprint for the identity
 		forMe = fingerprint2.CheckIdentityFP(ecrMsg.GetIdentityFP(),
-			ecrMsg.GetContents(), preimage.MakeDefault(identity.Source))
+			ecrMsgContents, preimage.MakeDefault(identity.Source))
 	}
 
 	if !forMe {
 		if jww.GetLogThreshold() == jww.LevelTrace {
-			expectedFP := fingerprint2.IdentityFP(ecrMsg.GetContents(),
+			expectedFP := fingerprint2.IdentityFP(ecrMsgContents,
 				preimage.MakeDefault(identity.Source))
 			jww.TRACE.Printf("Message for %d (%s) failed identity "+
 				"check: %v (expected-default) vs %v (received)", identity.EphId,
 				identity.Source, expectedFP, ecrMsg.GetIdentityFP())
 		}
-
+		im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+
+			"msgDigest: %s, not determined to be for client", ecrMsg.GetKeyFP(), bundle.Round, ecrMsg.Digest())
+		m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
+		m.Session.GetGarbledMessages().Add(ecrMsg)
 		return
 	}
 
@@ -103,7 +107,7 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
 		msg = ecrMsg
 		encTy = message.None
 	} else {
-		// if it doesnt match any form of encrypted, hear it as a raw message
+		// if it doesn't match any form of encrypted, hear it as a raw message
 		// and add it to garbled messages to be handled later
 		msg = ecrMsg
 		raw := message.Receive{
@@ -117,7 +121,7 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
 			RoundId:        id.Round(bundle.RoundInfo.ID),
 			RoundTimestamp: time.Unix(0, int64(bundle.RoundInfo.Timestamps[states.QUEUED])),
 		}
-		im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+
+		im := fmt.Sprintf("Received message of type Garbled/RAW: keyFP: %v, round: %d, "+
 			"msgDigest: %s", msg.GetKeyFP(), bundle.Round, msg.Digest())
 		jww.INFO.Print(im)
 		m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
@@ -126,14 +130,12 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
 		return
 	}
 
-
-
 	// Process the decrypted/unencrypted message partition, to see if
 	// we get a full message
 	xxMsg, ok := m.partitioner.HandlePartition(sender, encTy, msg.GetContents(),
 		relationshipFingerprint)
 
-	im := fmt.Sprintf("Received message of ecr type %s and msg type " +
+	im := fmt.Sprintf("Received message of ecr type %s and msg type "+
 		"%d from %s in round %d,msgDigest: %s, keyFP: %v", encTy,
 		xxMsg.MessageType, sender, bundle.Round, msgDigest, msg.GetKeyFP())
 	jww.INFO.Print(im)
diff --git a/network/message/sendCmix.go b/network/message/sendCmix.go
index 173fb1f8c2bbb87eb56d48472a737aff950300f9..e0b127542041623ba3437e289d872632c36184a5 100644
--- a/network/message/sendCmix.go
+++ b/network/message/sendCmix.go
@@ -123,7 +123,7 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 			jww.WARN.Printf("Best round on send is nil")
 			continue
 		}
-		jww.DEBUG.Printf("[sendCMIX] bestRound: %v", bestRound)
+		jww.TRACE.Printf("[sendCMIX] bestRound: %v", bestRound)
 
 		// add the round on to the list of attempted, so it is not tried again
 		attempted.Insert(bestRound.GetRoundId())
@@ -149,7 +149,7 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 			continue
 		}
 
-		jww.DEBUG.Printf("[sendCMIX] round %v processed, firstGW: %s",
+		jww.TRACE.Printf("[sendCMIX] round %v processed, firstGW: %s",
 			bestRound, firstGateway)
 
 		// Build the messages to send
@@ -196,7 +196,7 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 			}
 			return result, err
 		}
-		jww.DEBUG.Printf("[sendCMIX] sendToPreferred %s", firstGateway)
+		jww.TRACE.Printf("[sendCMIX] sendToPreferred %s", firstGateway)
 		result, err := sender.SendToPreferred(
 			[]*id.ID{firstGateway}, sendFunc, stop, cmixParams.SendTimeout)
 		jww.DEBUG.Printf("[sendCMIX] sendToPreferred %s returned",
diff --git a/network/message/sendCmixUtils.go b/network/message/sendCmixUtils.go
index 15674cd2f0de004d306e44ebc9e1280845fedfa5..ae2344415338685af084a81744e941691854415c 100644
--- a/network/message/sendCmixUtils.go
+++ b/network/message/sendCmixUtils.go
@@ -154,12 +154,11 @@ func buildSlotMessage(msg format.Message, recipient *id.ID, target *id.ID,
 	}
 
 	// Set the identity fingerprint
+
 	ifp := fingerprint.IdentityFP(msg.GetContents(), preimage)
 
 	msg.SetIdentityFP(ifp)
 
-	jww.INFO.Printf(" Sending to %s with preimage %v, ifp: %v, contents: %v", recipient, preimage, ifp, msg.GetContents())
-
 	// Encrypt the message
 	salt := make([]byte, 32)
 	_, err = stream.Read(salt)
diff --git a/network/rounds/retrieve.go b/network/rounds/retrieve.go
index 1db833b43ccf1b415efe44b1d0f10aefe3aa2bed..bd378ecf24728018dbdf824d97efc522bd227ed3 100644
--- a/network/rounds/retrieve.go
+++ b/network/rounds/retrieve.go
@@ -193,7 +193,7 @@ func (m *Manager) getMessagesFromGateway(roundID id.Round,
 
 		err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId)
 		if err != nil {
-			jww.FATAL.Panicf("Failed to remove round %d: %+v", roundID, err)
+			jww.ERROR.Printf("Failed to remove round %d: %+v", roundID, err)
 		}
 
 		return message.Bundle{}, nil
diff --git a/network/rounds/unchecked.go b/network/rounds/unchecked.go
index 0618a95c75d07faa2bdc030348bb0c27b68fca84..a73bf6eb24eef97f0fa47a693d045fcd18a38bae 100644
--- a/network/rounds/unchecked.go
+++ b/network/rounds/unchecked.go
@@ -115,8 +115,8 @@ func (m *Manager) processUncheckedRounds(checkInterval time.Duration, backoffTab
 func isRoundCheckDue(tries uint64, ts time.Time, backoffTable [cappedTries]time.Duration) bool {
 	now := netTime.Now()
 
-	if tries > cappedTries {
-		tries = cappedTries
+	if tries >= uint64(len(backoffTable)) {
+		tries = uint64(len(backoffTable)) - 1
 	}
 	roundCheckTime := ts.Add(backoffTable[tries])
 
diff --git a/storage/e2e/manager.go b/storage/e2e/manager.go
index d8ec60e65209a217e45bcc28d2387ce6eadd1d9d..f6c000f78ff002e3a3c82db143b8e783ca8a497e 100644
--- a/storage/e2e/manager.go
+++ b/storage/e2e/manager.go
@@ -292,3 +292,9 @@ func (m *Manager) GetSilentPreimage() []byte {
 func (m *Manager) GetFileTransferPreimage() []byte {
 	return preimage.Generate(m.GetRelationshipFingerprintBytes(), preimage.EndFT)
 }
+
+// GetGroupRequestPreimage returns a hash of the unique
+// fingerprint for group requests received from this user.
+func (m *Manager) GetGroupRequestPreimage() []byte {
+	return preimage.Generate(m.GetRelationshipFingerprintBytes(), preimage.GroupRq)
+}
\ No newline at end of file
diff --git a/storage/fileTransfer/fileMessage.go b/storage/fileTransfer/fileMessage.go
new file mode 100644
index 0000000000000000000000000000000000000000..03269326bbeab891e4678c888994fc35ea1026a6
--- /dev/null
+++ b/storage/fileTransfer/fileMessage.go
@@ -0,0 +1,119 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package fileTransfer
+
+import (
+	"encoding/binary"
+	"github.com/pkg/errors"
+)
+
+// Size constants.
+const (
+	partNumLen = 2          // The length of the part number in bytes
+	FmMinSize  = partNumLen // Minimum size for the PartMessage
+)
+
+// Error messages.
+const (
+	newFmSizeErr       = "size of external payload (%d) must be greater than %d"
+	unmarshalFmSizeErr = "size of passed in bytes (%d) must be greater than %d"
+	setFileFmErr       = "length of part bytes (%d) must be smaller than maximum payload size %d"
+)
+
+/*
++-----------------------------------------+
+|          CMIX Message Contents          |
++---------+-------------+-----------------+
+| Padding | Part Number |    File Data    |
+| 8 bytes |   2 bytes   | remaining space |
++---------+-------------+-----------------+
+*/
+
+// PartMessage contains part of the data being transferred and 256-bit nonce
+// that is used as a nonce.
+type PartMessage struct {
+	data    []byte // Serial of all contents
+	partNum []byte // The part number of the file
+	part    []byte // File part data
+}
+
+// NewPartMessage generates a new part message that fits into the specified
+// external payload size. An error is returned if the external payload size is
+// too small to fit the part message.
+func NewPartMessage(externalPayloadSize int) (PartMessage, error) {
+	if externalPayloadSize < FmMinSize {
+		return PartMessage{},
+			errors.Errorf(newFmSizeErr, externalPayloadSize, FmMinSize)
+	}
+
+	return MapPartMessage(make([]byte, externalPayloadSize)), nil
+}
+
+// MapPartMessage maps the data to the components of a PartMessage. It is mapped
+// by reference; a copy is not made.
+func MapPartMessage(data []byte) PartMessage {
+	return PartMessage{
+		data:    data,
+		partNum: data[:partNumLen],
+		part:    data[partNumLen:],
+	}
+}
+
+// UnmarshalPartMessage converts the bytes into a PartMessage. An error is
+// returned if the size of the data is too small for a PartMessage.
+func UnmarshalPartMessage(b []byte) (PartMessage, error) {
+	if len(b) < FmMinSize {
+		return PartMessage{},
+			errors.Errorf(unmarshalFmSizeErr, len(b), FmMinSize)
+	}
+
+	return MapPartMessage(b), nil
+}
+
+// Marshal returns the byte representation of the PartMessage.
+func (m PartMessage) Marshal() []byte {
+	b := make([]byte, len(m.data))
+	copy(b,m.data)
+	return b
+}
+
+// GetPartNum returns the file part number.
+func (m PartMessage) GetPartNum() uint16 {
+	return binary.LittleEndian.Uint16(m.partNum)
+}
+
+// SetPartNum sets the file part number.
+func (m PartMessage) SetPartNum(num uint16) {
+	b := make([]byte, partNumLen)
+	binary.LittleEndian.PutUint16(b, num)
+	copy(m.partNum, b)
+}
+
+// GetPart returns the file part data from the message.
+func (m PartMessage) GetPart() []byte {
+	b := make([]byte, len(m.part))
+	copy(b,m.part)
+	return b
+}
+
+// SetPart sets the PartMessage part to the given bytes. An error is returned if
+// the size of the provided part data is too large to store.
+func (m PartMessage) SetPart(b []byte) error {
+	if len(b) > len(m.part) {
+		return errors.Errorf(setFileFmErr, len(b), len(m.part))
+	}
+
+	copy(m.part, b)
+
+	return nil
+}
+
+// GetPartSize returns the number of bytes available to store part data.
+func (m PartMessage) GetPartSize() int {
+	return len(m.part)
+}
diff --git a/storage/fileTransfer/fileMessage_test.go b/storage/fileTransfer/fileMessage_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad565931e6d44d60f887552e9cb4c1a954fe5cf0
--- /dev/null
+++ b/storage/fileTransfer/fileMessage_test.go
@@ -0,0 +1,241 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package fileTransfer
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"math/rand"
+	"testing"
+)
+
+// Tests that NewPartMessage returns a PartMessage of the expected size.
+func Test_newPartMessage(t *testing.T) {
+	externalPayloadSize := 256
+
+	fm, err := NewPartMessage(externalPayloadSize)
+	if err != nil {
+		t.Errorf("NewPartMessage returned an error: %+v", err)
+	}
+
+	if len(fm.data) != externalPayloadSize {
+		t.Errorf("Size of PartMessage data does not match payload size."+
+			"\nexpected: %d\nreceived: %d", externalPayloadSize, len(fm.data))
+	}
+}
+
+// Error path: tests that NewPartMessage returns the expected error when the
+// external payload size is too small.
+func Test_newPartMessage_SmallPayloadSizeError(t *testing.T) {
+	externalPayloadSize := FmMinSize - 1
+	expectedErr := fmt.Sprintf(newFmSizeErr, externalPayloadSize, FmMinSize)
+
+	_, err := NewPartMessage(externalPayloadSize)
+	if err == nil || err.Error() != expectedErr {
+		t.Errorf("NewPartMessage did not return the expected error when the "+
+			"given external payload size is too small."+
+			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
+// Tests that MapPartMessage maps the data to the correct parts of the
+// PartMessage.
+func Test_mapPartMessage(t *testing.T) {
+	// Generate expected values
+	_, expectedData, expectedPartNum, expectedFile :=
+		newRandomFileMessage()
+
+	fm := MapPartMessage(expectedData)
+
+	if !bytes.Equal(expectedData, fm.data) {
+		t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q",
+			expectedData, fm.data)
+	}
+
+	if !bytes.Equal(expectedPartNum, fm.partNum) {
+		t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q",
+			expectedPartNum, fm.partNum)
+	}
+
+	if !bytes.Equal(expectedFile, fm.part) {
+		t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q",
+			expectedFile, fm.part)
+	}
+
+}
+
+// Tests that UnmarshalPartMessage returns a PartMessage with the expected
+// values.
+func Test_unmarshalPartMessage(t *testing.T) {
+	// Generate expected values
+	_, expectedData, expectedPartNumb, expectedFile :=
+		newRandomFileMessage()
+
+	fm, err := UnmarshalPartMessage(expectedData)
+	if err != nil {
+		t.Errorf("UnmarshalPartMessage return an error: %+v", err)
+	}
+
+	if !bytes.Equal(expectedData, fm.data) {
+		t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q",
+			expectedData, fm.data)
+	}
+
+	if !bytes.Equal(expectedPartNumb, fm.partNum) {
+		t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q",
+			expectedPartNumb, fm.partNum)
+	}
+
+	if !bytes.Equal(expectedFile, fm.part) {
+		t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q",
+			expectedFile, fm.part)
+	}
+}
+
+// Error path: tests that UnmarshalPartMessage returns the expected error when
+// the provided data is too small to be unmarshalled into a PartMessage.
+func Test_unmarshalPartMessage_SizeError(t *testing.T) {
+	data := make([]byte, FmMinSize-1)
+	expectedErr := fmt.Sprintf(unmarshalFmSizeErr, len(data), FmMinSize)
+
+	_, err := UnmarshalPartMessage(data)
+	if err == nil || err.Error() != expectedErr {
+		t.Errorf("UnmarshalPartMessage did not return the expected error when "+
+			"the given bytes are too small to be a PartMessage."+
+			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
+// Tests that PartMessage.Marshal returns the correct data.
+func Test_fileMessage_marshal(t *testing.T) {
+	fm, expectedData, _, _ := newRandomFileMessage()
+
+	data := fm.Marshal()
+
+	if !bytes.Equal(expectedData, data) {
+		t.Errorf("Marshalled data does not match expected."+
+			"\nexpected: %q\nreceived: %q", expectedData, data)
+	}
+}
+
+// Tests that PartMessage.GetPartNum returns the correct part number.
+func Test_fileMessage_getPartNum(t *testing.T) {
+	fm, _, expectedPartNum, _ := newRandomFileMessage()
+
+	partNum := fm.GetPartNum()
+	expected := binary.LittleEndian.Uint16(expectedPartNum)
+
+	if expected != partNum {
+		t.Errorf("Part number does not match expected."+
+			"\nexpected: %d\nreceived: %d", expected, partNum)
+	}
+}
+
+// Tests that PartMessage.SetPartNum sets the correct part number.
+func Test_fileMessage_setPartNum(t *testing.T) {
+	fm, err := NewPartMessage(256)
+	if err != nil {
+		t.Errorf("Failed to create new PartMessage: %+v", err)
+	}
+
+	expectedPartNum := make([]byte, partNumLen)
+	rand.New(rand.NewSource(42)).Read(expectedPartNum)
+	expected := binary.LittleEndian.Uint16(expectedPartNum)
+
+	fm.SetPartNum(expected)
+
+	if expected != fm.GetPartNum() {
+		t.Errorf("Failed to set correct part number.\nexpected: %d\nreceived: %d",
+			expected, fm.GetPartNum())
+	}
+}
+
+// Tests that PartMessage.GetPart returns the correct part data.
+func Test_fileMessage_getFile(t *testing.T) {
+	fm, _, _, expectedFile := newRandomFileMessage()
+
+	file := fm.GetPart()
+
+	if !bytes.Equal(expectedFile, file) {
+		t.Errorf("File data does not match expected."+
+			"\nexpected: %q\nreceived: %q", expectedFile, file)
+	}
+}
+
+// Tests that PartMessage.SetPart sets the correct part data.
+func Test_fileMessage_setFile(t *testing.T) {
+	fm, err := NewPartMessage(256)
+	if err != nil {
+		t.Errorf("Failed to create new PartMessage: %+v", err)
+	}
+
+	fileData := make([]byte, 64)
+	rand.New(rand.NewSource(42)).Read(fileData)
+	expectedFile := make([]byte, fm.GetPartSize())
+	copy(expectedFile, fileData)
+
+	err = fm.SetPart(expectedFile)
+	if err != nil {
+		t.Errorf("SetPart returned an error: %+v", err)
+	}
+
+	if !bytes.Equal(expectedFile, fm.GetPart()) {
+		t.Errorf("Failed to set correct part data.\nexpected: %q\nreceived: %q",
+			expectedFile, fm.GetPart())
+	}
+}
+
+// Error path: tests that PartMessage.SetPart returns the expected error when
+// the provided part data is too large for the message.
+func Test_fileMessage_setFile_FileTooLargeError(t *testing.T) {
+	fm, err := NewPartMessage(FmMinSize + 1)
+	if err != nil {
+		t.Errorf("Failed to create new PartMessage: %+v", err)
+	}
+
+	expectedErr := fmt.Sprintf(setFileFmErr, fm.GetPartSize()+1, fm.GetPartSize())
+
+	err = fm.SetPart(make([]byte, fm.GetPartSize()+1))
+	if err == nil || err.Error() != expectedErr {
+		t.Errorf("SetPart did not return the expected error when the given "+
+			"part data is too large to fit in the PartMessage."+
+			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
+// Tests that PartMessage.GetPartSize returns the expected available space for
+// the part data.
+func Test_fileMessage_getFileSize(t *testing.T) {
+	expectedSize := 256
+
+	fm, err := NewPartMessage(FmMinSize + expectedSize)
+	if err != nil {
+		t.Errorf("Failed to create new PartMessage: %+v", err)
+	}
+
+	if expectedSize != fm.GetPartSize() {
+		t.Errorf("File size incorrect.\nexpected: %d\nreceived: %d",
+			expectedSize, fm.GetPartSize())
+	}
+}
+
+// newRandomFileMessage generates a new PartMessage filled with random data and
+// return the PartMessage and its individual parts.
+func newRandomFileMessage() (PartMessage, []byte, []byte, []byte) {
+	prng := rand.New(rand.NewSource(42))
+	partNum := make([]byte, partNumLen)
+	prng.Read(partNum)
+	part := make([]byte, 64)
+	prng.Read(part)
+	data := append(partNum, part...)
+
+	fm := MapPartMessage(data)
+
+	return fm, data, partNum, part
+}
diff --git a/storage/fileTransfer/partStore_test.go b/storage/fileTransfer/partStore_test.go
index 5e3eabae208b88c8bfed5c2131727939c7c448e7..597fea4b20a6871cf44310e97cbd3c4f06c8aa59 100644
--- a/storage/fileTransfer/partStore_test.go
+++ b/storage/fileTransfer/partStore_test.go
@@ -12,6 +12,7 @@ import (
 	"encoding/binary"
 	"gitlab.com/elixxir/client/storage/versioned"
 	"gitlab.com/elixxir/ekv"
+	"gitlab.com/elixxir/primitives/format"
 	"io"
 	"math/rand"
 	"reflect"
@@ -521,7 +522,10 @@ func Test_makePartsKey_consistency(t *testing.T) {
 func newRandomPartStore(numParts uint16, kv *versioned.KV, prng io.Reader,
 	t *testing.T) (*partStore, []byte) {
 
-	partSize := 64
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partSize := partData.GetPartSize()
 
 	ps, err := newPartStore(kv, numParts)
 	if err != nil {
diff --git a/storage/fileTransfer/receiveFileTransfers.go b/storage/fileTransfer/receiveFileTransfers.go
index ac9f262e6b2f6920fad1cf17a0453e3f89ace8c2..0c7bffdc6881e96fd79026deb58c98195b05dfbf 100644
--- a/storage/fileTransfer/receiveFileTransfers.go
+++ b/storage/fileTransfer/receiveFileTransfers.go
@@ -35,7 +35,7 @@ const (
 	getReceivedTransferErr    = "received transfer with ID %s not found"
 	addTransferNewIdErr       = "could not generate new transfer ID: %+v"
 	noFingerprintErr          = "no part found with fingerprint %s"
-	addPartErr                = "failed to add part number %d/%d to transfer %s: %+v"
+	addPartErr                = "failed to add part to transfer %s: %+v"
 	deleteReceivedTransferErr = "failed to delete received transfer with ID %s from store: %+v"
 )
 
@@ -176,17 +176,18 @@ func (rft *ReceivedFileTransfersStore) DeleteTransfer(tid ftCrypto.TransferID) e
 // transfer that the part was added to so that a progress callback can be
 // called. Returns the transfer ID so that it can be used for logging. Also
 // returns of the transfer is complete after adding the part.
-func (rft *ReceivedFileTransfersStore) AddPart(encryptedPart, padding,
-	mac []byte, partNum uint16, fp format.Fingerprint) (*ReceivedTransfer,
+func (rft *ReceivedFileTransfersStore) AddPart(cmixMsg format.Message) (*ReceivedTransfer,
 	ftCrypto.TransferID, bool, error) {
 	rft.mux.Lock()
 	defer rft.mux.Unlock()
 
+	keyfp := cmixMsg.GetKeyFP()
+
 	// Lookup the part info for the given fingerprint
-	info, exists := rft.info[fp]
+	info, exists := rft.info[cmixMsg.GetKeyFP()]
 	if !exists {
 		return nil, ftCrypto.TransferID{}, false,
-			errors.Errorf(noFingerprintErr, fp)
+			errors.Errorf(noFingerprintErr, keyfp)
 	}
 
 	// Lookup the transfer with the ID in the part info
@@ -197,15 +198,14 @@ func (rft *ReceivedFileTransfersStore) AddPart(encryptedPart, padding,
 	}
 
 	// Add the part to the transfer
-	completed, err := transfer.AddPart(
-		encryptedPart, padding, mac, partNum, info.fpNum)
+	completed, err := transfer.AddPart(cmixMsg, info.fpNum)
 	if err != nil {
 		return transfer, info.id, false, errors.Errorf(
-			addPartErr, partNum, transfer.numParts, info.id, err)
+			addPartErr, info.id, err)
 	}
 
 	// Remove the part info from the map
-	delete(rft.info, fp)
+	delete(rft.info, keyfp)
 
 	return transfer, info.id, completed, nil
 }
diff --git a/storage/fileTransfer/receiveFileTransfers_test.go b/storage/fileTransfer/receiveFileTransfers_test.go
index 0c048246023069538f20870b89a8a2dbb60d1d47..4fd245ec4d902d99f8389a8a4a1a6393c95d1432 100644
--- a/storage/fileTransfer/receiveFileTransfers_test.go
+++ b/storage/fileTransfer/receiveFileTransfers_test.go
@@ -345,14 +345,26 @@ func TestReceivedFileTransfersStore_AddPart(t *testing.T) {
 	}
 
 	// Create encrypted part
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
 	expectedData := []byte("test")
+
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(
-		key, expectedData, fpNum, t)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
 	fp := ftCrypto.GenerateFingerprint(key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	rt, _, _, err := rft.AddPart(encryptedPart, padding, mac, partNum, fp)
+	rt, _, _, err := rft.AddPart(cmixMsg)
 	if err != nil {
 		t.Errorf("AddPart returned an error: %+v", err)
 	}
@@ -373,9 +385,9 @@ func TestReceivedFileTransfersStore_AddPart(t *testing.T) {
 
 	// Check that the correct part was stored
 	receivedPart := expectedRT.receivedParts.parts[partNum]
-	if !bytes.Equal(receivedPart, expectedData) {
+	if !bytes.Equal(receivedPart[:len(expectedData)], expectedData) {
 		t.Errorf("Part in memory is not expected."+
-			"\nexpected: %q\nreceived: %q", expectedData, receivedPart)
+			"\nexpected: %q\nreceived: %q", expectedData, receivedPart[:len(expectedData)])
 	}
 }
 
@@ -391,9 +403,12 @@ func TestReceivedFileTransfersStore_AddPart_NoFingerprintError(t *testing.T) {
 	// Create encrypted part
 	fp := format.NewFingerprint([]byte("invalidTransferKey"))
 
+	msg := format.NewMessage(1000)
+	msg.SetKeyFP(fp)
+
 	// Add encrypted part
 	expectedErr := fmt.Sprintf(noFingerprintErr, fp)
-	_, _, _, err = rft.AddPart([]byte{}, []byte{}, []byte{}, 0, fp)
+	_, _, _, err = rft.AddPart(msg)
 	if err == nil || err.Error() != expectedErr {
 		t.Errorf("AddPart did not return the expected error when no part for "+
 			"the fingerprint exists.\nexpected: %s\nreceived: %+v",
@@ -425,9 +440,12 @@ func TestReceivedFileTransfersStore_AddPart_NoTransferError(t *testing.T) {
 	invalidTid, _ := ftCrypto.NewTransferID(prng)
 	rft.info[fp].id = invalidTid
 
+	msg := format.NewMessage(1000)
+	msg.SetKeyFP(fp)
+
 	// Add encrypted part
 	expectedErr := fmt.Sprintf(getReceivedTransferErr, invalidTid)
-	_, _, _, err = rft.AddPart([]byte{}, []byte{}, []byte{}, 0, fp)
+	_, _, _, err = rft.AddPart(msg)
 	if err == nil || err.Error() != expectedErr {
 		t.Errorf("AddPart did not return the expected error when no transfer "+
 			"for the ID exists.\nexpected: %s\nreceived: %+v", expectedErr, err)
@@ -455,14 +473,24 @@ func TestReceivedFileTransfersStore_AddPart_AddPartError(t *testing.T) {
 
 	// Create encrypted part
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart := []byte("invalidPart")
-	mac = []byte("invalidMAC")
-	padding := make([]byte, 24)
+	part := []byte("invalidPart")
+	mac = make([]byte, format.MacLen)
 	fp := ftCrypto.GenerateFingerprint(key, fpNum)
 
 	// Add encrypted part
-	expectedErr := fmt.Sprintf(addPartErr, partNum, numParts, tid, "")
-	_, _, _, err = rft.AddPart(encryptedPart, padding, mac, partNum, fp)
+	expectedErr := fmt.Sprintf(addPartErr, tid, "")
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(part)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(partData.Marshal())
+	cmixMsg.SetMac(mac)
+
+	_, _, _, err = rft.AddPart(cmixMsg)
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("AddPart did not return the expected error when the "+
 			"encrypted part, padding, and MAC are invalid."+
diff --git a/storage/fileTransfer/receiveTransfer.go b/storage/fileTransfer/receiveTransfer.go
index df4bad66163245c9593bfbbc949d73ccf04b5bc3..b3e387a750d4cb6ba398c28195add01ec5b5763e 100644
--- a/storage/fileTransfer/receiveTransfer.go
+++ b/storage/fileTransfer/receiveTransfer.go
@@ -16,6 +16,7 @@ import (
 	"gitlab.com/elixxir/client/storage/utility"
 	"gitlab.com/elixxir/client/storage/versioned"
 	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
+	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/primitives/netTime"
 	"sync"
 	"time"
@@ -276,20 +277,25 @@ func (rt *ReceivedTransfer) AddProgressCB(
 // AddPart decrypts an encrypted file part, adds it to the list of received
 // parts and marks its fingerprint as used. Returns true if the part added was
 // the last in the transfer.
-func (rt *ReceivedTransfer) AddPart(encryptedPart, padding, mac []byte, partNum,
+func (rt *ReceivedTransfer) AddPart(cmixMsg format.Message,
 	fpNum uint16) (bool, error) {
 	rt.mux.Lock()
 	defer rt.mux.Unlock()
 
 	// Decrypt the encrypted file part
-	decryptedPart, err := ftCrypto.DecryptPart(
-		rt.key, encryptedPart, padding, mac, fpNum)
+	decryptedPart, err := ftCrypto.DecryptPart(rt.key,
+		cmixMsg.GetContents(), cmixMsg.GetMac(), fpNum, cmixMsg.GetKeyFP())
+	if err != nil {
+		return false, err
+	}
+
+	part, err := UnmarshalPartMessage(decryptedPart)
 	if err != nil {
 		return false, err
 	}
 
 	// Add the part to the list of parts
-	err = rt.receivedParts.addPart(decryptedPart, partNum)
+	err = rt.receivedParts.addPart(part.GetPart(), part.GetPartNum())
 	if err != nil {
 		return false, err
 	}
@@ -298,7 +304,7 @@ func (rt *ReceivedTransfer) AddPart(encryptedPart, padding, mac []byte, partNum,
 	rt.fpVector.Use(uint32(fpNum))
 
 	// Mark part as received
-	rt.receivedStatus.Use(uint32(partNum))
+	rt.receivedStatus.Use(uint32(part.GetPartNum()))
 
 	if rt.receivedStatus.GetNumUsed() >= uint32(rt.numParts) {
 		return true, nil
diff --git a/storage/fileTransfer/receiveTransfer_test.go b/storage/fileTransfer/receiveTransfer_test.go
index 7c0382ec2f038899e9f1d047b087e958e0795abc..281bb373822ee0642a6c02c07c814d402cd3d174 100644
--- a/storage/fileTransfer/receiveTransfer_test.go
+++ b/storage/fileTransfer/receiveTransfer_test.go
@@ -16,6 +16,7 @@ import (
 	"gitlab.com/elixxir/client/storage/versioned"
 	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
 	"gitlab.com/elixxir/ekv"
+	"gitlab.com/elixxir/primitives/format"
 	"reflect"
 	"strings"
 	"sync"
@@ -538,13 +539,25 @@ func TestReceivedTransfer_AddPart(t *testing.T) {
 	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)
 
 	// Create encrypted part
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
 	expectedData := []byte("test")
+
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(
-		rt.key, expectedData, fpNum, t)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	complete, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	complete, err := rt.AddPart(cmixMsg,fpNum)
 	if err != nil {
 		t.Errorf("AddPart returned an error: %+v", err)
 	}
@@ -556,9 +569,9 @@ func TestReceivedTransfer_AddPart(t *testing.T) {
 	receivedData, exists := rt.receivedParts.parts[partNum]
 	if !exists {
 		t.Errorf("Part #%d not found in part map.", partNum)
-	} else if !bytes.Equal(expectedData, receivedData) {
+	} else if !bytes.Equal(expectedData, receivedData[:len(expectedData)]) {
 		t.Fatalf("Part data in list does not match expected."+
-			"\nexpected: %+v\nreceived: %+v", expectedData, receivedData)
+			"\nexpected: %+v\nreceived: %+v", expectedData, receivedData[:len(expectedData)])
 	}
 
 	// Check that the fingerprint vector has correct values
@@ -594,14 +607,27 @@ func TestReceivedTransfer_AddPart_DecryptPartError(t *testing.T) {
 	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)
 
 	// Create encrypted part
-	Data := []byte("test")
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	expectedData := []byte("test")
+
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, _, padding := newEncryptedPartData(rt.key, Data, fpNum, t)
-	mac := []byte("invalidMAC")
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, _, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+	badMac := make([]byte, format.MacLen)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(badMac)
 
 	// Add encrypted part
 	expectedErr := "reconstructed MAC from decrypting does not match MAC from sender"
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err == nil || err.Error() != expectedErr {
 		t.Errorf("AddPart did not return the expected error when the MAC is "+
 			"invalid.\nexpected: %s\nreceived: %+v", expectedErr, err)
@@ -670,11 +696,22 @@ func Test_loadReceivedTransfer(t *testing.T) {
 	// Create encrypted part
 	expectedData := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(
-		expectedRT.key, expectedData, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
+	fp := ftCrypto.GenerateFingerprint(expectedRT.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(expectedRT.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := expectedRT.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = expectedRT.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Errorf("Failed to add test part: %+v", err)
 	}
@@ -712,12 +749,25 @@ func Test_loadReceivedTransfer_LoadFpVectorError(t *testing.T) {
 	tid, rt, _ := newRandomReceivedTransfer(16, 20, kv, t)
 
 	// Create encrypted part
+
 	data := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(data)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Errorf("Failed to add test part: %+v", err)
 	}
@@ -746,10 +796,22 @@ func Test_loadReceivedTransfer_LoadPartStoreError(t *testing.T) {
 	// Create encrypted part
 	data := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(data)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Errorf("Failed to add test part: %+v", err)
 	}
@@ -778,10 +840,22 @@ func Test_loadReceivedTransfer_LoadReceivedVectorError(t *testing.T) {
 	// Create encrypted part
 	data := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(data)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Errorf("Failed to add test part: %+v", err)
 	}
@@ -871,11 +945,22 @@ func TestReceivedTransfer_delete(t *testing.T) {
 	// Create encrypted part
 	expectedData := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(
-		rt.key, expectedData, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Fatalf("Failed to add test part: %+v", err)
 	}
@@ -1030,8 +1115,20 @@ func newRandomReceivedTransfer(numParts, numFps uint16, kv *versioned.KV,
 	}
 
 	for partNum, part := range parts.parts {
-		encryptedPart, mac, padding := newEncryptedPartData(key, part, partNum, t)
-		_, err := rt.AddPart(encryptedPart, padding, mac, partNum, partNum)
+		cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+		partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+		partData.SetPartNum(partNum)
+		_ = partData.SetPart(part)
+
+		fp := ftCrypto.GenerateFingerprint(rt.key, partNum)
+		encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), partNum, fp)
+
+		cmixMsg.SetKeyFP(fp)
+		cmixMsg.SetContents(encryptedPart)
+		cmixMsg.SetMac(mac)
+
+		_, err = rt.AddPart(cmixMsg, partNum)
 		if err != nil {
 			t.Errorf("Failed to add part #%d: %+v", partNum, err)
 		}
@@ -1059,18 +1156,4 @@ func newEmptyReceivedTransfer(numParts, numFps uint16, kv *versioned.KV,
 	}
 
 	return tid, rt, fileData
-}
-
-// newEncryptedPartData encrypts the part data and returns the encrypted part
-// its MAC, and its padding.
-func newEncryptedPartData(key ftCrypto.TransferKey, part []byte, fpNum uint16,
-	t *testing.T) ([]byte, []byte, []byte) {
-	// Create encrypted part
-	prng := NewPrng(42)
-	encPart, mac, padding, err := ftCrypto.EncryptPart(key, part, fpNum, prng)
-	if err != nil {
-		t.Fatalf("Failed to encrypt data: %+v", err)
-	}
-
-	return encPart, mac, padding
-}
+}
\ No newline at end of file
diff --git a/storage/fileTransfer/sentFileTransfers.go b/storage/fileTransfer/sentFileTransfers.go
index 485eb67d062eca8ccb75f9b53bda262d64996eb3..e672ce184fa4674a47b94c89bb47abdc2b420eb8 100644
--- a/storage/fileTransfer/sentFileTransfers.go
+++ b/storage/fileTransfer/sentFileTransfers.go
@@ -193,11 +193,6 @@ func (sft *SentFileTransfersStore) GetUnsentPartsAndSentRounds() (
 	sentRounds := map[id.Round][]ftCrypto.TransferID{}
 
 	for tid, st := range sft.transfers {
-		// Get list of round IDs that transfers have in-progress rounds on
-		for _, rid := range st.GetSentRounds() {
-			sentRounds[rid] = append(sentRounds[rid], tid)
-		}
-
 		// Get list of unsent part numbers for each transfer
 		stUnsentParts, err := st.GetUnsentPartNums()
 		if err != nil {
@@ -206,6 +201,11 @@ func (sft *SentFileTransfersStore) GetUnsentPartsAndSentRounds() (
 		if len(stUnsentParts) > 0 {
 			unsentParts[tid] = stUnsentParts
 		}
+
+		// Get list of round IDs that transfers have in-progress rounds on
+		for _, rid := range st.GetSentRounds() {
+			sentRounds[rid] = append(sentRounds[rid], tid)
+		}
 	}
 
 	return unsentParts, sentRounds, nil
diff --git a/storage/fileTransfer/sentPartTracker_test.go b/storage/fileTransfer/sentPartTracker_test.go
index 8d5a476ed542563b7524e9f3da9d668680f9ff22..af50953b889415c3c11bf4969199ee6d318408fb 100644
--- a/storage/fileTransfer/sentPartTracker_test.go
+++ b/storage/fileTransfer/sentPartTracker_test.go
@@ -56,21 +56,20 @@ func Test_sentPartTracker_GetPartStatus(t *testing.T) {
 
 		switch partStatuses[partNum] {
 		case interfaces.FpSent:
-			err := st.partStats.Set(partNum, uint8(interfaces.FpSent))
+			err := st.partStats.Set(partNum, inProgress)
 			if err != nil {
-				t.Errorf("Failed to set part %d to %s: %+v",
-					partNum, interfaces.FpSent, err)
+				t.Errorf(
+					"Failed to set part %d to in-progress: %+v", partNum, err)
 			}
 		case interfaces.FpArrived:
-			err := st.partStats.Set(partNum, uint8(interfaces.FpSent))
+			err := st.partStats.Set(partNum, inProgress)
 			if err != nil {
-				t.Errorf("Failed to set part %d to %s: %+v",
-					partNum, interfaces.FpSent, err)
+				t.Errorf(
+					"Failed to set part %d to in-progress: %+v", partNum, err)
 			}
-			err = st.partStats.Set(partNum, uint8(interfaces.FpArrived))
+			err = st.partStats.Set(partNum, finished)
 			if err != nil {
-				t.Errorf("Failed to set part %d to %s: %+v",
-					partNum, interfaces.FpArrived, err)
+				t.Errorf("Failed to set part %d to finished: %+v", partNum, err)
 			}
 		}
 	}
diff --git a/storage/fileTransfer/sentTransfer.go b/storage/fileTransfer/sentTransfer.go
index a91a59847fee21466f0bff6a80a49ad01b67c82d..cdffaaeca6807c4025b9bf90925130e6be9cbcb5 100644
--- a/storage/fileTransfer/sentTransfer.go
+++ b/storage/fileTransfer/sentTransfer.go
@@ -17,7 +17,6 @@ import (
 	"gitlab.com/elixxir/client/storage/versioned"
 	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
 	"gitlab.com/elixxir/primitives/format"
-	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/netTime"
 	"sync"
@@ -91,6 +90,14 @@ const (
 // been used.
 var MaxRetriesErr = errors.New(maxRetriesErr)
 
+// States for parts in the partStats MultiStateVector.
+const (
+	unsent = iota
+	inProgress
+	finished
+	numStates // The number of part states (for initialisation of the vector)
+)
+
 // sentTransferStateMap prevents illegal state changes for part statuses.
 var sentTransferStateMap = [][]bool{
 	{false, true, false},
@@ -188,8 +195,8 @@ func NewSentTransfer(recipient *id.ID, tid ftCrypto.TransferID,
 	}
 
 	// Create new MultiStateVector for storing part statuses
-	st.partStats, err = utility.NewMultiStateVector(
-		st.numParts, 3, sentTransferStateMap, sentPartStatsVectorKey, st.kv)
+	st.partStats, err = utility.NewMultiStateVector(st.numParts, numStates,
+		sentTransferStateMap, sentPartStatsVectorKey, st.kv)
 	if err != nil {
 		return nil, errors.Errorf(newSentPartStatusVectorErr, err)
 	}
@@ -236,8 +243,8 @@ func (st *SentTransfer) ReInit(numFps uint16,
 	}
 
 	// Overwrite new part status MultiStateVector
-	st.partStats, err = utility.NewMultiStateVector(
-		st.numParts, 3, sentTransferStateMap, sentPartStatsVectorKey, st.kv)
+	st.partStats, err = utility.NewMultiStateVector(st.numParts, numStates,
+		sentTransferStateMap, sentPartStatsVectorKey, st.kv)
 	if err != nil {
 		return errors.Errorf(reInitSentPartStatusVectorErr, err)
 	}
@@ -314,7 +321,7 @@ func (st *SentTransfer) IsPartInProgress(partNum uint16) (bool, error) {
 	if err != nil {
 		return false, errors.Errorf(getStatusErr, partNum, err)
 	}
-	return status == 1, nil
+	return status == inProgress, nil
 }
 
 // IsPartFinished returns true if the part has successfully arrived. Returns
@@ -325,7 +332,7 @@ func (st *SentTransfer) IsPartFinished(partNum uint16) (bool, error) {
 	if err != nil {
 		return false, errors.Errorf(getStatusErr, partNum, err)
 	}
-	return status == 2, nil
+	return status == finished, nil
 }
 
 // GetProgress returns the current progress of the transfer. Completed is true
@@ -345,8 +352,8 @@ func (st *SentTransfer) GetProgress() (completed bool, sent, arrived,
 // getProgress is the thread-unsafe helper function for GetProgress.
 func (st *SentTransfer) getProgress() (completed bool, sent, arrived,
 	total uint16, t interfaces.FilePartTracker) {
-	arrived, _ = st.partStats.GetCount(2)
-	sent, _ = st.partStats.GetCount(1)
+	arrived, _ = st.partStats.GetCount(finished)
+	sent, _ = st.partStats.GetCount(inProgress)
 	total = st.numParts
 
 	if sent == 0 && arrived == total {
@@ -419,31 +426,44 @@ func (st *SentTransfer) AddProgressCB(cb interfaces.SentProgressCallback,
 
 // GetEncryptedPart gets the specified part, encrypts it, and returns the
 // encrypted part along with its MAC, padding, and fingerprint.
-func (st *SentTransfer) GetEncryptedPart(partNum uint16, partSize int,
-	rng csprng.Source) (encPart, mac, padding []byte, fp format.Fingerprint,
-	err error) {
+func (st *SentTransfer) GetEncryptedPart(partNum uint16, contentsSize int) (encPart, mac []byte,
+	fp format.Fingerprint, err error) {
 	st.mux.Lock()
 	defer st.mux.Unlock()
 
+	// Create new empty file part message of size equal to the available payload
+	// size in the cMix message
+	partMsg, err := NewPartMessage(contentsSize)
+	if err != nil {
+		return nil, nil, format.Fingerprint{}, err
+	}
+
+	partMsg.SetPartNum(partNum)
+
 	// Lookup part
 	part, exists := st.sentParts.getPart(partNum)
 	if !exists {
-		return nil, nil, nil, format.Fingerprint{},
+		return nil, nil, format.Fingerprint{},
 			errors.Errorf(noPartNumErr, partNum)
 	}
 
+	if err = partMsg.SetPart(part); err != nil{
+		return nil, nil, format.Fingerprint{},
+			err
+	}
+
 	// If all fingerprints have been used but parts still remain, then change
 	// the status to stopping and return an error specifying that all the
 	// retries have been used
 	if st.fpVector.GetNumAvailable() < 1 {
 		st.status = Stopping
-		return nil, nil, nil, format.Fingerprint{}, MaxRetriesErr
+		return nil, nil, format.Fingerprint{}, MaxRetriesErr
 	}
 
 	// Get next unused fingerprint number and mark it as used
 	nextKey, err := st.fpVector.Next()
 	if err != nil {
-		return nil, nil, nil, format.Fingerprint{},
+		return nil, nil, format.Fingerprint{},
 			errors.Errorf(fingerprintErr, err)
 	}
 	fpNum := uint16(nextKey)
@@ -452,16 +472,13 @@ func (st *SentTransfer) GetEncryptedPart(partNum uint16, partSize int,
 	fp = ftCrypto.GenerateFingerprint(st.key, fpNum)
 
 	// Encrypt the file part and generate the file part MAC and padding (nonce)
-	maxLengthPart := make([]byte, partSize)
-	copy(maxLengthPart, part)
-	encPart, mac, padding, err = ftCrypto.EncryptPart(
-		st.key, maxLengthPart, fpNum, rng)
+	encPart, mac, err = ftCrypto.EncryptPart(st.key, partMsg.Marshal(), fpNum, fp)
 	if err != nil {
-		return nil, nil, nil, format.Fingerprint{},
+		return nil, nil, format.Fingerprint{},
 			errors.Errorf(encryptPartErr, partNum, err)
 	}
 
-	return encPart, mac, padding, fp, err
+	return encPart, mac, fp, err
 }
 
 // SetInProgress adds the specified file part numbers to the in-progress
@@ -476,7 +493,7 @@ func (st *SentTransfer) SetInProgress(rid id.Round, partNums ...uint16) (bool,
 	_, exists := st.inProgressTransfers.getPartNums(rid)
 
 	// Set parts as in-progress in part status vector
-	err := st.partStats.SetMany(partNums, 1)
+	err := st.partStats.SetMany(partNums, inProgress)
 	if err != nil {
 		return false, err
 	}
@@ -503,8 +520,16 @@ func (st *SentTransfer) UnsetInProgress(rid id.Round) ([]uint16, error) {
 	// Get the list of part numbers to be removed from list
 	partNums, _ := st.inProgressTransfers.getPartNums(rid)
 
+	// The part status is set in partStats before the parts and round ID so that
+	// in the event of recovery after a crash, the parts will be resent on a new
+	// round and the parts in the inProgressTransfers will be left until deleted
+	// with the rest of the storage on transfer completion. The side effect is
+	// that on recovery, the status of the round will be looked up again and the
+	// progress callback will be called for an event that has already been
+	// called on the callback.
+
 	// Set parts as unsent in part status vector
-	err := st.partStats.SetMany(partNums, 0)
+	err := st.partStats.SetMany(partNums, unsent)
 	if err != nil {
 		return nil, err
 	}
@@ -539,7 +564,7 @@ func (st *SentTransfer) FinishTransfer(rid id.Round) (bool, error) {
 	}
 
 	// Set parts as finished in part status vector
-	err = st.partStats.SetMany(partNums, 2)
+	err = st.partStats.SetMany(partNums, finished)
 	if err != nil {
 		return false, err
 	}
@@ -561,7 +586,7 @@ func (st *SentTransfer) GetUnsentPartNums() ([]uint16, error) {
 	defer st.mux.RUnlock()
 
 	// Get list of parts with a status of unsent
-	unsentPartNums, err := st.partStats.GetKeys(0)
+	unsentPartNums, err := st.partStats.GetKeys(unsent)
 	if err != nil {
 		return nil, errors.Errorf(getUnsentPartsErr, err)
 	}
diff --git a/storage/fileTransfer/sentTransfer_test.go b/storage/fileTransfer/sentTransfer_test.go
index 79415befe0632d48092d75f078969f28f86f5693..033992bf84b42aa4bf54431ac50e3c3c0b132998 100644
--- a/storage/fileTransfer/sentTransfer_test.go
+++ b/storage/fileTransfer/sentTransfer_test.go
@@ -803,7 +803,6 @@ func TestSentTransfer_AddProgressCB(t *testing.T) {
 func TestSentTransfer_GetEncryptedPart(t *testing.T) {
 	kv := versioned.NewKV(make(ekv.Memstore))
 	_, st := newRandomSentTransfer(16, 24, kv, t)
-	prng := NewPrng(42)
 
 	// Create and fill fingerprint map used to check fingerprint validity
 	// The first item in the uint16 slice is the fingerprint number and the
@@ -816,7 +815,7 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) {
 	for i := uint16(0); i < st.numFps; i++ {
 		partNum := i % st.numParts
 
-		encPart, mac, padding, fp, err := st.GetEncryptedPart(partNum, 16, prng)
+		encPart, mac, fp, err := st.GetEncryptedPart(partNum, 18)
 		if err != nil {
 			t.Fatalf("GetEncryptedPart returned an error for part number "+
 				"%d (%d): %+v", partNum, i, err)
@@ -836,17 +835,24 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) {
 		}
 
 		// Attempt to decrypt the part
-		part, err := ftCrypto.DecryptPart(st.key, encPart, padding, mac, fpNum[0])
+		partMarshaled, err := ftCrypto.DecryptPart(st.key, encPart, mac, fpNum[0], fp)
 		if err != nil {
 			t.Errorf("Failed to decrypt file part number %d (%d): %+v",
 				partNum, i, err)
 		}
 
+		partMsg, _ := UnmarshalPartMessage(partMarshaled)
+
 		// Make sure the decrypted part matches the original
 		expectedPart, _ := st.sentParts.getPart(i % st.numParts)
-		if !bytes.Equal(expectedPart, part) {
+		if !bytes.Equal(expectedPart, partMsg.GetPart()) {
 			t.Errorf("Decyrpted part number %d does not match expected (%d)."+
-				"\nexpected: %+v\nreceived: %+v", partNum, i, expectedPart, part)
+				"\nexpected: %+v\nreceived: %+v", partNum, i, expectedPart, partMsg.GetPart())
+		}
+
+		if partMsg.GetPartNum()!=i % st.numParts{
+			t.Errorf("Number of part did not match, expected: %d, " +
+				"received: %d", i % st.numParts, partMsg.GetPartNum())
 		}
 	}
 }
@@ -856,12 +862,11 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) {
 func TestSentTransfer_GetEncryptedPart_NoPartError(t *testing.T) {
 	kv := versioned.NewKV(make(ekv.Memstore))
 	_, st := newRandomSentTransfer(16, 24, kv, t)
-	prng := NewPrng(42)
 
 	partNum := st.numParts + 1
 	expectedErr := fmt.Sprintf(noPartNumErr, partNum)
 
-	_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
+	_, _, _, err := st.GetEncryptedPart(partNum, 16)
 	if err == nil || err.Error() != expectedErr {
 		t.Errorf("GetEncryptedPart did not return the expected error for a "+
 			"nonexistent part number %d.\nexpected: %s\nreceived: %+v",
@@ -874,12 +879,11 @@ func TestSentTransfer_GetEncryptedPart_NoPartError(t *testing.T) {
 func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) {
 	kv := versioned.NewKV(make(ekv.Memstore))
 	_, st := newRandomSentTransfer(16, 24, kv, t)
-	prng := NewPrng(42)
 
 	// Use up all the fingerprints
 	for i := uint16(0); i < st.numFps; i++ {
 		partNum := i % st.numParts
-		_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
+		_, _, _, err := st.GetEncryptedPart(partNum, 18)
 		if err != nil {
 			t.Errorf("Error when encyrpting part number %d (%d): %+v",
 				partNum, i, err)
@@ -887,7 +891,7 @@ func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) {
 	}
 
 	// Try to encrypt without any fingerprints
-	_, _, _, _, err := st.GetEncryptedPart(5, 16, prng)
+	_, _, _, err := st.GetEncryptedPart(5, 18)
 	if err != MaxRetriesErr {
 		t.Errorf("GetEncryptedPart did not return MaxRetriesErr when all "+
 			"fingerprints have been used.\nexpected: %s\nreceived: %+v",
@@ -895,32 +899,6 @@ func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) {
 	}
 }
 
-// Error path: tests that SentTransfer.GetEncryptedPart returns the expected
-// error when encrypting the part fails due to a PRNG error.
-func TestSentTransfer_GetEncryptedPart_EncryptPartError(t *testing.T) {
-	kv := versioned.NewKV(make(ekv.Memstore))
-	_, st := newRandomSentTransfer(16, 24, kv, t)
-	prng := NewPrngErr()
-
-	// Create and fill fingerprint map used to check fingerprint validity
-	// The first item in the uint16 slice is the fingerprint number and the
-	// second item is the number of times it has been used
-	fpMap := make(map[format.Fingerprint][]uint16, st.numFps)
-	for num, fp := range ftCrypto.GenerateFingerprints(st.key, st.numFps) {
-		fpMap[fp] = []uint16{uint16(num), 0}
-	}
-
-	partNum := uint16(0)
-	expectedErr := fmt.Sprintf(encryptPartErr, partNum, "")
-
-	_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
-	if err == nil || !strings.Contains(err.Error(), expectedErr) {
-		t.Errorf("GetEncryptedPart did not return the expected error when "+
-			"the PRNG should have errored.\nexpected: %s\nreceived: %+v",
-			expectedErr, err)
-	}
-}
-
 // Tests that SentTransfer.SetInProgress correctly adds the part numbers for the
 // given round ID to the in-progress map and sets the correct parts as
 // in-progress in the state vector.
@@ -962,15 +940,15 @@ func TestSentTransfer_SetInProgress(t *testing.T) {
 
 	// Check that the part numbers were set on the in-progress status vector
 	for i, partNum := range expectedPartNums {
-		if status, _ := st.partStats.Get(partNum); status != 1 {
-			t.Errorf("Part number %d not marked as used in status vector (%d).",
-				partNum, i)
+		if status, _ := st.partStats.Get(partNum); status != inProgress {
+			t.Errorf("Part number %d not marked as in-progress in status "+
+				"vector (%d).", partNum, i)
 		}
 	}
 
 	// Check that the correct number of parts were marked as in-progress in the
 	// status vector
-	count, _ := st.partStats.GetCount(1)
+	count, _ := st.partStats.GetCount(inProgress)
 	if int(count) != len(expectedPartNums) {
 		t.Errorf("Incorrect number of parts marked as in-progress."+
 			"\nexpected: %d\nreceived: %d", len(expectedPartNums), count)
@@ -989,7 +967,7 @@ func TestSentTransfer_SetInProgress(t *testing.T) {
 	}
 
 	// Check that the number of parts were marked as in-progress is unchanged
-	count, _ = st.partStats.GetCount(1)
+	count, _ = st.partStats.GetCount(inProgress)
 	if int(count) != len(expectedPartNums2)+len(expectedPartNums) {
 		t.Errorf("Incorrect number of parts marked as in-progress."+
 			"\nexpected: %d\nreceived: %d",
@@ -1075,10 +1053,10 @@ func TestSentTransfer_UnsetInProgress(t *testing.T) {
 	}
 
 	// Check that there are no set parts in the in-progress status vector
-	status, _ := st.partStats.Get(1)
-	if status != 0 {
+	status, _ := st.partStats.Get(inProgress)
+	if status != unsent {
 		t.Errorf("Failed to unset all parts in the in-progress vector."+
-			"\nexpected: %d\nreceived: %d", 0, status)
+			"\nexpected: %d\nreceived: %d", unsent, status)
 	}
 }
 
@@ -1134,7 +1112,7 @@ func TestSentTransfer_FinishTransfer(t *testing.T) {
 	}
 
 	// Check that there are no set parts in the in-progress status vector
-	count, _ := st.partStats.GetCount(1)
+	count, _ := st.partStats.GetCount(inProgress)
 	if count != 0 {
 		t.Errorf("Failed to unset all parts in the in-progress vector."+
 			"\nexpected: %d\nreceived: %d", 0, count)
@@ -1142,17 +1120,16 @@ func TestSentTransfer_FinishTransfer(t *testing.T) {
 
 	// Check that the part numbers were set on the finished status vector
 	for i, partNum := range expectedPartNums {
-
-		status, _ := st.partStats.Get(1)
-		if status != 2 {
-			t.Errorf("Part number %d not marked as used in status vector (%d).",
-				partNum, i)
+		status, _ := st.partStats.Get(inProgress)
+		if status != finished {
+			t.Errorf("Part number %d not marked as finished in status vector "+
+				"(%d).", partNum, i)
 		}
 	}
 
 	// Check that the correct number of parts were marked as finished in the
 	// status vector
-	count, _ = st.partStats.GetCount(2)
+	count, _ = st.partStats.GetCount(finished)
 	if int(count) != len(expectedPartNums) {
 		t.Errorf("Incorrect number of parts marked as finished."+
 			"\nexpected: %d\nreceived: %d", len(expectedPartNums), count)
diff --git a/storage/utility/meteredCmixMessageBuffer.go b/storage/utility/meteredCmixMessageBuffer.go
index ed1920ff42f2cf0f122549584ef11d480d43ebbb..9060dbb7ea2424f87879032371ee25944ff64851 100644
--- a/storage/utility/meteredCmixMessageBuffer.go
+++ b/storage/utility/meteredCmixMessageBuffer.go
@@ -114,6 +114,10 @@ func LoadMeteredCmixMessageBuffer(kv *versioned.KV, key string) (*MeteredCmixMes
 }
 
 func (mcmb *MeteredCmixMessageBuffer) Add(m format.Message) {
+	if m.GetPrimeByteLen()==0{
+		jww.FATAL.Panicf("Cannot handle a metered " +
+			"cmix message with a length of 0")
+	}
 	msg := meteredCmixMessage{
 		M:         m.Marshal(),
 		Count:     0,
diff --git a/storage/utility/multiStateVector.go b/storage/utility/multiStateVector.go
index 0a882ada564d7044ae1a5b42e914b1b311692146..77cf24ad1435c6c83ce431a13080a4d9d27cae68 100644
--- a/storage/utility/multiStateVector.go
+++ b/storage/utility/multiStateVector.go
@@ -43,7 +43,7 @@ const (
 	saveSetStateErr = "failed to save MultiStateVector after setting key %d state to %d: %+v"
 
 	// MultiStateVector.SetMany
-	setManyStateErr     = "failed to set state of key %d (%d/%d): %+v"
+	setManyStateErr     = "failed to set state of key %d (%d of %d): %+v"
 	saveManySetStateErr = "failed to save MultiStateVector after setting keys %d state to %d: %+v"
 
 	// MultiStateVector.set