diff --git a/bindings/e2eHandler.go b/bindings/e2eHandler.go
index 2e7d002599bdee81b0894076a43a031e220f5a86..95256801441ed19654d1d516e824149c3ee040ea 100644
--- a/bindings/e2eHandler.go
+++ b/bindings/e2eHandler.go
@@ -10,12 +10,12 @@ package bindings
 import (
 	"encoding/json"
 	"fmt"
+
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/catalog"
 	"gitlab.com/elixxir/client/cmix/identity/receptionID"
 	"gitlab.com/elixxir/client/cmix/rounds"
-	"gitlab.com/elixxir/client/e2e"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/primitives/id"
 )
@@ -123,20 +123,24 @@ func (e *E2e) RemoveService(tag string) error {
 //  - []byte - the JSON marshalled bytes of the E2ESendReport object, which can
 //    be passed into Cmix.WaitForRoundResult to see if the send succeeded.
 func (e *E2e) SendE2E(messageType int, recipientId, payload,
-	e2eParams []byte) ([]byte, error) {
-	// Note that specifically these are the Base params from xxdk.E2EParams
-	params := e2e.GetDefaultParams()
-	err := params.UnmarshalJSON(e2eParams)
+	e2eParamsJSON []byte) ([]byte, error) {
+	if len(e2eParamsJSON) == 0 {
+		jww.WARN.Printf("e2e params not specified, using defaults...")
+		e2eParamsJSON = GetDefaultE2EParams()
+	}
+	params, err := parseE2EParams(e2eParamsJSON)
 	if err != nil {
 		return nil, err
 	}
+
 	recipient, err := id.Unmarshal(recipientId)
 	if err != nil {
 		return nil, err
 	}
 
 	sendReport, err := e.api.GetE2E().SendE2E(
-		catalog.MessageType(messageType), recipient, payload, params)
+		catalog.MessageType(messageType), recipient, payload,
+		params.Base)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cmix/sendCmix.go b/cmix/sendCmix.go
index 73dfcfbd2c02b6f1cafcf49cd4662826edf7e3e8..f0c97a6ffc47f7611bc73d22132f2d0e087abe72 100644
--- a/cmix/sendCmix.go
+++ b/cmix/sendCmix.go
@@ -109,6 +109,12 @@ func sendCmixHelper(sender gateway.Sender, msg format.Message, recipient *id.ID,
 	nodes nodes.Registrar, rng *fastRNG.StreamGenerator, events event.Reporter,
 	senderId *id.ID, comms SendCmixCommsInterface) (id.Round, ephemeral.Id, error) {
 
+	if cmixParams.RoundTries == 0 {
+		return 0, ephemeral.Id{},
+			errors.Errorf("invalid parameter set, "+
+				"RoundTries cannot be 0: %+v", cmixParams)
+	}
+
 	timeStart := netTime.Now()
 	maxTimeout := sender.GetHostParams().SendTimeout
 
@@ -120,7 +126,8 @@ func sendCmixHelper(sender gateway.Sender, msg format.Message, recipient *id.ID,
 	}
 
 	jww.INFO.Printf("[Send-%s] Looking for round to send cMix message to "+
-		"%s (msgDigest: %s)", cmixParams.DebugTag, recipient, msg.Digest())
+		"%s (msgDigest: %s)", cmixParams.DebugTag, recipient,
+		msg.Digest())
 
 	stream := rng.GetStream()
 	defer stream.Close()
@@ -129,7 +136,8 @@ func sendCmixHelper(sender gateway.Sender, msg format.Message, recipient *id.ID,
 	// See cmix.SetGroupBits for more info.
 	cmix.SetGroupBits(msg, grp, stream)
 
-	for numRoundTries := uint(0); numRoundTries < cmixParams.RoundTries; numRoundTries++ {
+	for numRoundTries := uint(
+		0); numRoundTries < cmixParams.RoundTries; numRoundTries++ {
 		elapsed := netTime.Since(timeStart)
 		jww.TRACE.Printf("[Send-%s] try %d, elapsed: %s",
 			cmixParams.DebugTag, numRoundTries, elapsed)
@@ -137,42 +145,50 @@ func sendCmixHelper(sender gateway.Sender, msg format.Message, recipient *id.ID,
 		if elapsed > cmixParams.Timeout {
 			jww.INFO.Printf("[Send-%s] No rounds to send to %s "+
 				"(msgDigest: %s) were found before timeout %s",
-				cmixParams.DebugTag, recipient, msg.Digest(), cmixParams.Timeout)
-			return 0, ephemeral.Id{}, errors.New("Sending cmix message timed out")
+				cmixParams.DebugTag, recipient, msg.Digest(),
+				cmixParams.Timeout)
+			return 0, ephemeral.Id{}, errors.New(
+				"Sending cmix message timed out")
 		}
 
 		if numRoundTries > 0 {
-			jww.INFO.Printf("[Send-%s] Attempt %d to find round to send "+
-				"message to %s (msgDigest: %s)", cmixParams.DebugTag,
+			jww.INFO.Printf("[Send-%s] Attempt %d to find round"+
+				" to send message to %s (msgDigest: %s)",
+				cmixParams.DebugTag,
 				numRoundTries+1, recipient, msg.Digest())
 		}
 
 		// Find the best round to send to, excluding attempted rounds
 		remainingTime := cmixParams.Timeout - elapsed
-		bestRound, err := instance.GetWaitingRounds().GetUpcomingRealtime(
+		waitingRounds := instance.GetWaitingRounds()
+		bestRound, err := waitingRounds.GetUpcomingRealtime(
 			remainingTime, attempted, sendTimeBuffer)
 		if err != nil {
-			jww.WARN.Printf("[Send-%s] Failed to GetUpcomingRealtime "+
-				"(msgDigest: %s): %+v", cmixParams.DebugTag, msg.Digest(), err)
+			jww.WARN.Printf("[Send-%s] GetUpcomingRealtime failed "+
+				"(msgDigest: %s): %+v", cmixParams.DebugTag,
+				msg.Digest(), err)
 		}
 
 		if bestRound == nil {
 			jww.WARN.Printf(
-				"[Send-%s] Best round on send is nil", cmixParams.DebugTag)
+				"[Send-%s] Best round on send is nil",
+				cmixParams.DebugTag)
 			continue
 		}
 
 		jww.TRACE.Printf("[Send-%s] Best round found: %+v",
 			cmixParams.DebugTag, bestRound)
 
-		// Determine whether the selected round contains any nodes that are
-		// blacklisted by the CMIXParams object
+		// Determine whether the selected round contains any
+		// nodes that are blacklisted by the CMIXParams object
 		containsBlacklisted := false
 		if cmixParams.BlacklistedNodes != nil {
+			blacklist := cmixParams.BlacklistedNodes
 			for _, nodeId := range bestRound.Topology {
 				var nid id.ID
 				copy(nid[:], nodeId)
-				if _, isBlacklisted := cmixParams.BlacklistedNodes[nid]; isBlacklisted {
+				_, isBlacklisted := blacklist[nid]
+				if isBlacklisted {
 					containsBlacklisted = true
 					break
 				}
@@ -180,8 +196,10 @@ func sendCmixHelper(sender gateway.Sender, msg format.Message, recipient *id.ID,
 		}
 
 		if containsBlacklisted {
-			jww.WARN.Printf("[Send-%s] Round %d contains blacklisted "+
-				"nodes, skipping...", cmixParams.DebugTag, bestRound.ID)
+			jww.WARN.Printf("[Send-%s] Round %d "+
+				"contains blacklisted nodes, skipping...",
+				cmixParams.DebugTag,
+				bestRound.ID)
 			continue
 		}
 
@@ -290,5 +308,5 @@ func sendCmixHelper(sender gateway.Sender, msg format.Message, recipient *id.ID,
 
 	}
 	return 0, ephemeral.Id{},
-		errors.New("failed to send the message, unknown error")
+		errors.New("failed to send the message, out of round retries")
 }
diff --git a/e2e/params.go b/e2e/params.go
index 2702d490656342e975883177b115f63dbf2df961..ce0b72ff54282a5798c0ea2e085d8cd3143bc734 100644
--- a/e2e/params.go
+++ b/e2e/params.go
@@ -100,3 +100,9 @@ func (p *Params) UnmarshalJSON(data []byte) error {
 
 	return nil
 }
+
+// String implements stringer interface by returning a json string
+func (p *Params) String() string {
+	json, _ := p.MarshalJSON()
+	return string(json)
+}
diff --git a/e2e/sendE2E.go b/e2e/sendE2E.go
index db231f74d8a68efbbfe8bfe0692f96ea09f080c1..3a181f3c7f2e508fe7372d7c2d8684919f016de8 100644
--- a/e2e/sendE2E.go
+++ b/e2e/sendE2E.go
@@ -76,23 +76,25 @@ func (m *manager) prepareSendE2E(mt catalog.MessageType, recipient *id.ID,
 	partitions, internalMsgId, err := m.partitioner.Partition(recipient,
 		mt, ts, payload)
 	if err != nil {
-		return nil,
-			errors.WithMessage(err, "failed to send unsafe message")
+		return nil, errors.WithMessage(err,
+			"failed to send unsafe message")
 	}
 
-	jww.INFO.Printf("E2E sending %d messages to %s", len(partitions), recipient)
+	jww.INFO.Printf("E2E sending %d messages to %s",
+		len(partitions), recipient)
 
-	// When sending E2E messages, we first partition into cMix packets and then
-	// send each partition over cMix
+	// When sending E2E messages, we first partition into cMix
+	// packets and then send each partition over cMix
 	roundIds := make([]id.Round, len(partitions))
 	errCh := make(chan error, len(partitions))
 
-	// The Key manager for the partner (recipient) ensures single use of each
-	// key negotiated for the ratchet
+	// The Key manager for the partner (recipient) ensures single
+	// use of each key negotiated for the ratchet
 	partner, err := m.Ratchet.GetPartner(recipient)
 	if err != nil {
 		return nil, errors.WithMessagef(err,
-			"cannot send E2E message no relationship found with %s", recipient)
+			"cannot send E2E message no relationship found with %s",
+			recipient)
 	}
 
 	msgID := e2e.NewMessageID(
@@ -103,14 +105,17 @@ func (m *manager) prepareSendE2E(mt catalog.MessageType, recipient *id.ID,
 	for i, p := range partitions {
 		if mt != catalog.KeyExchangeTrigger {
 			// Check if any rekeys need to happen and trigger them
-			rekeySendFunc := func(mt catalog.MessageType, recipient *id.ID,
-				payload []byte, cmixParams cmix.CMIXParams) (e2e.SendReport, error) {
+			rekeySendFunc := func(mt catalog.MessageType,
+				recipient *id.ID,
+				payload []byte, cmixParams cmix.CMIXParams) (
+				e2e.SendReport, error) {
 				par := params
 				par.CMIXParams = cmixParams
 				return m.SendE2E(mt, recipient, payload, par)
 			}
-			rekey.CheckKeyExchanges(m.net.GetInstance(), m.grp, rekeySendFunc,
-				m.events, partner, m.rekeyParams, 1*time.Minute)
+			rekey.CheckKeyExchanges(m.net.GetInstance(), m.grp,
+				rekeySendFunc, m.events, partner,
+				m.rekeyParams, 1*time.Minute)
 		}
 
 		var keyGetter func() (session.Cypher, error)
@@ -120,27 +125,29 @@ func (m *manager) prepareSendE2E(mt catalog.MessageType, recipient *id.ID,
 			keyGetter = partner.PopSendCypher
 		}
 
-		// FIXME: remove this wait, it is weird. Why is it here? we cant remember.
+		// FIXME: remove this wait, it is weird. Why is it
+		// here? we cant remember.
 		key, err := waitForKey(
-			keyGetter, params.KeyGetRetryCount, params.KeyGeRetryDelay,
+			keyGetter, params.KeyGetRetryCount,
+			params.KeyGeRetryDelay,
 			params.Stop, recipient, format.DigestContents(p), i)
 		if err != nil {
 			return nil, errors.WithMessagef(err,
 				"Failed to get key for end-to-end encryption")
 		}
 
-		// This does not encrypt for cMix but instead end-to-end encrypts the
-		// cMix message
+		// This does not encrypt for cMix but instead
+		// end-to-end encrypts the cMix message
 		contentsEnc, mac, residue := key.Encrypt(p)
 		// Carry the first key residue to the top level
 		if i == 0 {
 			keyResidue = residue
 		}
 
-		jww.INFO.Printf(
-			"E2E sending %d/%d to %s with key fp: %s, msgID: %s (msgDigest %s)",
-			i+i, len(partitions), recipient, key.Fingerprint(), msgID,
-			format.DigestContents(p))
+		jww.INFO.Printf("E2E sending %d/%d to %s with key fp: %s, "+
+			"msgID: %s (msgDigest %s)",
+			i+i, len(partitions), recipient, key.Fingerprint(),
+			msgID, format.DigestContents(p))
 
 		var s message.Service
 		if i == len(partitions)-1 {
@@ -149,16 +156,20 @@ func (m *manager) prepareSendE2E(mt catalog.MessageType, recipient *id.ID,
 			s = partner.MakeService(params.ServiceTag)
 		}
 
-		// We send each partition in its own thread here; some may send in round
-		// X, others in X+1 or X+2, and so on
+		// We send each partition in its own thread here; some
+		// may send in round X, others in X+1 or X+2, and so
+		// on
 		localI := i
 		thisSendFunc := func() {
 			wg.Add(1)
 			go func(i int) {
 				var err error
 				roundIds[i], _, err = m.net.Send(recipient,
-					key.Fingerprint(), s, contentsEnc, mac, params.CMIXParams)
+					key.Fingerprint(), s, contentsEnc, mac,
+					params.CMIXParams)
 				if err != nil {
+					jww.DEBUG.Printf("[E2E] cMix error on "+
+						"Send: %+v", err)
 					errCh <- err
 				}
 				wg.Done()
@@ -183,11 +194,12 @@ func (m *manager) prepareSendE2E(mt catalog.MessageType, recipient *id.ID,
 				numFail, len(partitions), errRtn)
 		} else {
 			jww.INFO.Printf("Successfully E2E sent %d/%d to %s",
-				len(partitions)-numFail, len(partitions), recipient)
+				len(partitions)-numFail, len(partitions),
+				recipient)
 		}
 
-		jww.INFO.Printf("Successful E2E Send of %d messages to %s with msgID %s",
-			len(partitions), recipient, msgID)
+		jww.INFO.Printf("Successful E2E Send of %d messages to %s "+
+			"with msgID %s", len(partitions), recipient, msgID)
 
 		return e2e.SendReport{
 			RoundList:  roundIds,