diff --git a/api/client.go b/api/client.go index 17ea0a289479a309311e9b8e721b91bedf87733d..a9e632ee8c20b3ade858d47fd33f30c658e3e0f4 100644 --- a/api/client.go +++ b/api/client.go @@ -13,12 +13,12 @@ import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/auth" "gitlab.com/elixxir/client/catalog" + keyExchange2 "gitlab.com/elixxir/client/e2e/rekey" "gitlab.com/elixxir/client/event" "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces/params" "gitlab.com/elixxir/client/interfaces/preimage" "gitlab.com/elixxir/client/interfaces/user" - "gitlab.com/elixxir/client/keyExchange" "gitlab.com/elixxir/client/network" "gitlab.com/elixxir/client/registration" "gitlab.com/elixxir/client/stoppable" @@ -564,7 +564,7 @@ func (c *Client) registerFollower() error { //register the key exchange service keyXchange := func() (stoppable.Stoppable, error) { - return keyExchange.Start(c.switchboard, c.storage, c.network, c.parameters.Rekey) + return keyExchange2.Start(c.switchboard, c.storage, c.network, c.parameters.Rekey) } err = c.followerServices.add(keyXchange) diff --git a/e2e/manager.go b/e2e/manager.go index 5871e7b069eed49e2db4584c6998d44b8239256e..f29c60f72f0bf52029d0e0cace409c909070271c 100644 --- a/e2e/manager.go +++ b/e2e/manager.go @@ -4,9 +4,11 @@ import ( "gitlab.com/elixxir/client/e2e/parse" "gitlab.com/elixxir/client/e2e/ratchet" "gitlab.com/elixxir/client/e2e/receive" + "gitlab.com/elixxir/client/event" "gitlab.com/elixxir/client/network" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/xx_network/primitives/id" ) @@ -16,8 +18,32 @@ type Manager struct { partitioner parse.Partitioner net network.Manager myID *id.ID + rng *fastRNG.StreamGenerator + events event.Manager + grp *cyclic.Group } -func InitManager(kv *versioned.KV, myID *id.ID, privKey *cyclic.Int) { +//Init Creates stores. After calling, use load +func Init(kv *versioned.KV, myID *id.ID, privKey *cyclic.Int, grp *cyclic.Group) error { + return ratchet.New(kv, myID, privKey, grp) +} +// Load returns an e2e manager from storage +func Load(kv *versioned.KV, net network.Manager, myID *id.ID, + grp *cyclic.Group, rng *fastRNG.StreamGenerator, events event.Manager) (*Manager, error) { + m := &Manager{ + Switchboard: receive.New(), + partitioner: parse.NewPartitioner(kv, net.GetMaxMessageLength()), + net: net, + myID: myID, + events: events, + grp: grp, + } + var err error + m.Ratchet, err = ratchet.Load(kv, myID, grp, + &fpGenerator{m}, net, rng) + if err != nil { + return nil, err + } + return m, nil } diff --git a/e2e/params.go b/e2e/params.go new file mode 100644 index 0000000000000000000000000000000000000000..f897548a94ec04ceff08e985e1f7eb31db7b11c3 --- /dev/null +++ b/e2e/params.go @@ -0,0 +1,60 @@ +package e2e + +import ( + "encoding/json" + "gitlab.com/elixxir/client/catalog" + "gitlab.com/elixxir/client/network" + "time" +) + +type Params struct { + // Tag to use to generate the service. + ServiceTag string + // Often, for notifications purposes, all messages except the last should + // use a silent service. This allows a + LastServiceTag string + + // The parameters adjust how the code behaves if there are not keys available. + // the number of times the code will attempt to get a key to encrypt with + KeyGetRetryCount uint + // Delay between attempting to get kets + KeyGeRetryDelay time.Duration + + //Underlying cmix tags. + // Note: if critical is true, an alternative critical messages system within + // e2e will be used which preserves privacy + CMIX network.CMIXParams + + //Authorizes the message to use a key reserved for rekeying. Do not use + //unless sending a rekey + Rekey bool +} + +func GetDefaultParams() Params { + return Params{ + ServiceTag: catalog.Silent, + LastServiceTag: catalog.E2e, + + KeyGetRetryCount: 10, + KeyGeRetryDelay: 500 * time.Millisecond, + + CMIX: network.GetDefaultCMIXParams(), + Rekey: false, + } +} +func (e Params) Marshal() ([]byte, error) { + return json.Marshal(e) +} + +// GetParameters Obtain default E2E parameters, or override with +// given parameters if set +func GetParameters(params string) (Params, error) { + p := GetDefaultParams() + if len(params) > 0 { + err := json.Unmarshal([]byte(params), &p) + if err != nil { + return Params{}, err + } + } + return p, nil +} diff --git a/e2e/parse/partition.go b/e2e/parse/partition.go index 06e10bcae00615e356ad305d4fef8d5ae5a38e4e..cb72eec3ea06f488169341c371277ad60803d5f0 100644 --- a/e2e/parse/partition.go +++ b/e2e/parse/partition.go @@ -31,7 +31,7 @@ type Partitioner struct { partition *partition.Store } -func NewPartitioner(messageSize int, kv *versioned.KV) Partitioner { +func NewPartitioner(kv *versioned.KV, messageSize int) Partitioner { p := Partitioner{ baseMessageSize: messageSize, firstContentsSize: messageSize - firstHeaderLen, diff --git a/e2e/processor.go b/e2e/processor.go index e86e1a20bd8e86b649637ff172274107898eaf67..04c6b68a9e37d0b36bd9b6d4de7d36d907985ee7 100644 --- a/e2e/processor.go +++ b/e2e/processor.go @@ -19,7 +19,7 @@ func (p *processor) Process(ecrMsg format.Message, receptionID receptionID.Ephem defer p.cy.Use() //decrypt - msg, err := p.cy.Decrypt(ecrMsg) + contents, err := p.cy.Decrypt(ecrMsg) if err != nil { jww.ERROR.Printf("Decryption Failed of %s (fp: %s), dropping: %+v", ecrMsg.Digest(), p.cy.Fingerprint(), err) @@ -29,7 +29,7 @@ func (p *processor) Process(ecrMsg format.Message, receptionID receptionID.Ephem //Parse sess := p.cy.GetSession() message, done := p.m.partitioner.HandlePartition(sess.GetPartner(), - msg.GetContents(), sess.GetRelationshipFingerprint()) + contents, sess.GetRelationshipFingerprint()) if done { message.RecipientID = receptionID.Source diff --git a/e2e/ratchet/partner/session/cypher.go b/e2e/ratchet/partner/session/cypher.go index 5a8f352b9147ca6bfefd09e6309a3ae8bf9107c6..fe713eb08c2cf07f563e9ebcfee80b8f0e94c5e2 100644 --- a/e2e/ratchet/partner/session/cypher.go +++ b/e2e/ratchet/partner/session/cypher.go @@ -86,45 +86,37 @@ func (k *Cypher) Fingerprint() format.Fingerprint { // the E2E key to encrypt msg to its intended recipient // It also properly populates the associated data, including the MAC, fingerprint, // and encrypted timestamp -func (k *Cypher) Encrypt(msg format.Message) format.Message { +func (k *Cypher) Encrypt(contents []byte) (ecrContents, mac []byte) { fp := k.Fingerprint() key := k.generateKey() - // set the fingerprint - msg.SetKeyFP(fp) - // encrypt the payload - encPayload := e2eCrypto.Crypt(key, fp, msg.GetContents()) - msg.SetContents(encPayload) + ecrContents = e2eCrypto.Crypt(key, fp, contents) // create the MAC // MAC is HMAC(key, ciphertext) // Currently, the MAC doesn't include any of the associated data - MAC := hash.CreateHMAC(encPayload, key[:]) - msg.SetMac(MAC) + mac = hash.CreateHMAC(ecrContents, key[:]) - return msg + return ecrContents, mac } // Decrypt uses the E2E key to decrypt the message // It returns an error in case of HMAC verification failure // or in case of a decryption error (related to padding) -func (k *Cypher) Decrypt(msg format.Message) (format.Message, error) { +func (k *Cypher) Decrypt(msg format.Message) ([]byte, error) { fp := k.Fingerprint() key := k.generateKey() // Verify the MAC is correct if !hash.VerifyHMAC(msg.GetContents(), msg.GetMac(), key[:]) { - return format.Message{}, errors.New("HMAC verification failed for E2E message") + return nil, errors.New("HMAC verification failed for E2E message") } // Decrypt the payload decryptedPayload := e2eCrypto.Crypt(key, fp, msg.GetContents()) - //put the decrypted payload back in the message - msg.SetContents(decryptedPayload) - - return msg, nil + return decryptedPayload, nil } // Use sets the key as used. It cannot be used again. diff --git a/e2e/ratchet/store.go b/e2e/ratchet/store.go index 0cf1ca902210122ab812113ddc3a7c28c57f6570..b300a7c4453ed782b10de3c9f4c3ccc45ec295fc 100644 --- a/e2e/ratchet/store.go +++ b/e2e/ratchet/store.go @@ -55,13 +55,12 @@ type Ratchet struct { kv *versioned.KV } -// NewRatchet creates a new store for the passed user id and private key. +// New creates a new store for the passed user id and private key. // The store can then be accessed by calling LoadStore. // Does not create at a unique prefix, if multiple Ratchets are needed, make // sure to add a uint prefix to the KV before instantiation. -func NewRatchet(kv *versioned.KV, privKey *cyclic.Int, - myID *id.ID, grp *cyclic.Group, cyHandler session.CypherHandler, - services Services, rng *fastRNG.StreamGenerator) error { +func New(kv *versioned.KV, myID *id.ID, privKey *cyclic.Int, + grp *cyclic.Group) error { // Generate public key pubKey := diffieHellman.GeneratePublicKey(privKey, grp) @@ -79,10 +78,7 @@ func NewRatchet(kv *versioned.KV, privKey *cyclic.Int, kv: kv, - cyHandler: cyHandler, - grp: grp, - rng: rng, - sInteface: services, + grp: grp, } err := util.StoreCyclicKey(kv, pubKey, pubKeyKey) @@ -100,8 +96,8 @@ func NewRatchet(kv *versioned.KV, privKey *cyclic.Int, return r.save() } -// LoadRatchet loads an extant ratchet from disk -func LoadRatchet(kv *versioned.KV, myID *id.ID, grp *cyclic.Group, +// Load loads an extant ratchet from disk +func Load(kv *versioned.KV, myID *id.ID, grp *cyclic.Group, cyHandler session.CypherHandler, services Services, rng *fastRNG.StreamGenerator) ( *Ratchet, error) { kv = kv.Prefix(packagePrefix) diff --git a/keyExchange/confirm.go b/e2e/rekey/confirm.go similarity index 88% rename from keyExchange/confirm.go rename to e2e/rekey/confirm.go index 3ff7e3183b279de0cebef1071a662f094e29de96..a8d79136355be9077a8869c61ef7e05ab425e440 100644 --- a/keyExchange/confirm.go +++ b/e2e/rekey/confirm.go @@ -5,19 +5,19 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey import ( "github.com/golang/protobuf/proto" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/e2e/ratchet" session2 "gitlab.com/elixxir/client/e2e/ratchet/partner/session" - "gitlab.com/elixxir/client/interfaces/message" + "gitlab.com/elixxir/client/e2e/receive" "gitlab.com/elixxir/client/stoppable" - "gitlab.com/elixxir/client/storage" ) -func startConfirm(sess *storage.Session, c chan message.Receive, +func startConfirm(ratchet *ratchet.Ratchet, c chan receive.Message, stop *stoppable.Single, cleanup func()) { for true { select { @@ -26,14 +26,14 @@ func startConfirm(sess *storage.Session, c chan message.Receive, stop.ToStopped() return case confirmation := <-c: - handleConfirm(sess, confirmation) + handleConfirm(ratchet, confirmation) } } } -func handleConfirm(sess *storage.Session, confirmation message.Receive) { +func handleConfirm(ratchet *ratchet.Ratchet, confirmation receive.Message) { //ensure the message was encrypted properly - if confirmation.Encryption != message.E2E { + if !confirmation.Encrypted { jww.ERROR.Printf( "[REKEY] Received non-e2e encrypted Key Exchange "+ "confirm from partner %s", confirmation.Sender) @@ -41,7 +41,7 @@ func handleConfirm(sess *storage.Session, confirmation message.Receive) { } //get the partner - partner, err := sess.E2e().GetPartner(confirmation.Sender) + partner, err := ratchet.GetPartner(confirmation.Sender) if err != nil { jww.ERROR.Printf( "[REKEY] Received Key Exchange Confirmation with unknown "+ diff --git a/keyExchange/confirm_test.go b/e2e/rekey/confirm_test.go similarity index 99% rename from keyExchange/confirm_test.go rename to e2e/rekey/confirm_test.go index 87c453093c6c0cdb0fb3d6d0d378441fbdf753bf..1d47e0d69cb138cd894864971fd356d0de113bc2 100644 --- a/keyExchange/confirm_test.go +++ b/e2e/rekey/confirm_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey import ( "github.com/cloudflare/circl/dh/sidh" diff --git a/keyExchange/exchange.go b/e2e/rekey/exchange.go similarity index 93% rename from keyExchange/exchange.go rename to e2e/rekey/exchange.go index 121d3b2b4e0853d96a3509a188fe5da56e264179..620d6308736ea937edf21ae56a9ce2228e297d51 100644 --- a/keyExchange/exchange.go +++ b/e2e/rekey/exchange.go @@ -5,9 +5,10 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey import ( + "gitlab.com/elixxir/client/e2e/receive" "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces/message" "gitlab.com/elixxir/client/interfaces/params" @@ -21,7 +22,7 @@ const keyExchangeTriggerName = "KeyExchangeTrigger" const keyExchangeConfirmName = "KeyExchangeConfirm" const keyExchangeMulti = "KeyExchange" -func Start(switchboard *switchboard.Switchboard, sess *storage.Session, net interfaces.NetworkManager, +func Start(switchboard *receive.Switchboard, sess *storage.Session, net interfaces.NetworkManager, params params.Rekey) (stoppable.Stoppable, error) { // register the rekey trigger thread diff --git a/keyExchange/exchange_test.go b/e2e/rekey/exchange_test.go similarity index 99% rename from keyExchange/exchange_test.go rename to e2e/rekey/exchange_test.go index 04d6c4ec205d5396fab1e29dd5b64540425fa547..4851e17d3edc6ef23f44ae1c49705df65cc3c44c 100644 --- a/keyExchange/exchange_test.go +++ b/e2e/rekey/exchange_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey import ( "fmt" diff --git a/keyExchange/generate.sh b/e2e/rekey/generate.sh old mode 100755 new mode 100644 similarity index 100% rename from keyExchange/generate.sh rename to e2e/rekey/generate.sh diff --git a/keyExchange/rekey.go b/e2e/rekey/rekey.go similarity index 99% rename from keyExchange/rekey.go rename to e2e/rekey/rekey.go index e76f54652cf58f9f1e9b0c0b5975c9d1eceb7a7d..888f85dad95716d9197ffb4e49dd61bc6c7961e1 100644 --- a/keyExchange/rekey.go +++ b/e2e/rekey/rekey.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey import ( "fmt" diff --git a/keyExchange/rekey_test.go b/e2e/rekey/rekey_test.go similarity index 98% rename from keyExchange/rekey_test.go rename to e2e/rekey/rekey_test.go index b507fc266803d597a6e39295911c3cb48894f263..6a07e438d95e3af31b598d58c86ee6a0b10b4d38 100644 --- a/keyExchange/rekey_test.go +++ b/e2e/rekey/rekey_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey /* func TestRekey(t *testing.T) { diff --git a/keyExchange/trigger.go b/e2e/rekey/trigger.go similarity index 99% rename from keyExchange/trigger.go rename to e2e/rekey/trigger.go index 85e68e48b2493e1131c5d1d09043a34f56b8770e..85011845f9f2db4fe17445b2f0c7324ee5af5043 100644 --- a/keyExchange/trigger.go +++ b/e2e/rekey/trigger.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey import ( "fmt" diff --git a/keyExchange/trigger_test.go b/e2e/rekey/trigger_test.go similarity index 99% rename from keyExchange/trigger_test.go rename to e2e/rekey/trigger_test.go index 6391fbab07cf54391c346f41162c42b556964583..2b568b2e29d6de6a0cd9abdab283a983a31c9449 100644 --- a/keyExchange/trigger_test.go +++ b/e2e/rekey/trigger_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey import ( "github.com/cloudflare/circl/dh/sidh" diff --git a/keyExchange/utils_test.go b/e2e/rekey/utils_test.go similarity index 99% rename from keyExchange/utils_test.go rename to e2e/rekey/utils_test.go index ed32032b4320569573019a3b74a6af65c218c652..2d4e7f7090fa2fcd65dd7f09f0fd3f8d93c3cb67 100644 --- a/keyExchange/utils_test.go +++ b/e2e/rekey/utils_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package keyExchange +package rekey import ( "github.com/cloudflare/circl/dh/sidh" diff --git a/keyExchange/xchange.pb.go b/e2e/rekey/xchange.pb.go similarity index 99% rename from keyExchange/xchange.pb.go rename to e2e/rekey/xchange.pb.go index 7871a130b05e6253843e73402d3d9f076d0b0189..a9b5f46ef0202e6b9e442b10f52c4298cf561489 100644 --- a/keyExchange/xchange.pb.go +++ b/e2e/rekey/xchange.pb.go @@ -13,7 +13,7 @@ // protoc v3.15.6 // source: xchange.proto -package keyExchange +package rekey import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" diff --git a/keyExchange/xchange.proto b/e2e/rekey/xchange.proto similarity index 100% rename from keyExchange/xchange.proto rename to e2e/rekey/xchange.proto diff --git a/e2e/sendE2E.go b/e2e/sendE2E.go new file mode 100644 index 0000000000000000000000000000000000000000..30acc83c9650b4a8b5d419368b4608604d6b467d --- /dev/null +++ b/e2e/sendE2E.go @@ -0,0 +1,171 @@ +package e2e + +import ( + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/catalog" + "gitlab.com/elixxir/client/e2e/ratchet/partner/session" + keyExchange2 "gitlab.com/elixxir/client/e2e/rekey" + "gitlab.com/elixxir/client/network/message" + "gitlab.com/elixxir/client/stoppable" + "gitlab.com/elixxir/crypto/e2e" + "gitlab.com/elixxir/primitives/format" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" + "sync" + "time" +) + +func (m *Manager) SendE2E(mt catalog.MessageType, recipient *id.ID, + payload []byte, params Params) ([]id.Round, e2e.MessageID, time.Time, error) { + //timestamp the message + ts := netTime.Now() + + //partition the message + partitions, internalMsgId, err := m.partitioner.Partition(recipient, mt, ts, + payload) + if err != nil { + return nil, e2e.MessageID{}, time.Time{}, errors.WithMessage(err, "failed to send unsafe message") + } + + jww.INFO.Printf("E2E sending %d messages to %s", + len(partitions), recipient) + + //encrypt then send the partitions over cmix + roundIds := make([]id.Round, len(partitions)) + errCh := make(chan error, len(partitions)) + + // get the key manager for the partner + partner, err := m.Ratchet.GetPartner(recipient) + if err != nil { + return nil, e2e.MessageID{}, time.Time{}, errors.WithMessagef(err, + "Could not send End to End encrypted "+ + "message, no relationship found with %s", recipient) + } + + //return the rounds if everything send successfully + msgID := e2e.NewMessageID(partner.GetSendRelationshipFingerprint(), internalMsgId) + + wg := sync.WaitGroup{} + + for i, p := range partitions { + if mt != catalog.KeyExchangeTrigger { + // check if any rekeys need to happen and trigger them + keyExchange2.CheckKeyExchanges(m.Instance, m.SendE2E, + m.Events, m.Session, partner, + 1*time.Minute, stop) + } + + //get a key to end to end encrypt + var keyGetter func() (*session.Cypher, error) + if params.Rekey { + keyGetter = partner.PopRekeyCypher + } else { + keyGetter = partner.PopSendCypher + } + + //fixme: remove this wait, it is weird. Why is it here? we cant remember. + key, err := waitForKey(keyGetter, params.KeyGetRetryCount, + params.KeyGeRetryDelay, params.CMIX.Stop, recipient, + format.DigestContents(p), i) + if err != nil { + return nil, e2e.MessageID{}, time.Time{}, errors.WithMessagef(err, + "Failed to get key for end to end encryption") + } + + //end to end encrypt the cmix message + contentsEnc, mac := key.Encrypt(p) + + jww.INFO.Printf("E2E sending %d/%d to %s with key fp: %s, msgID: %s", + i+i, len(partitions), recipient, format.DigestContents(p), + key.Fingerprint(), msgID) + + //set up the service tags + var s message.Service + if i == len(partitions)-1 { + s = partner.MakeService(params.LastServiceTag) + } else { + s = partner.MakeService(params.ServiceTag) + } + + //send the cmix message, each partition in its own thread + wg.Add(1) + go func(i int) { + var err error + roundIds[i], _, err = m.net.SendCMIX(recipient, key.Fingerprint(), + s, contentsEnc, mac, params.CMIX) + if err != nil { + errCh <- err + } + wg.Done() + }(i) + } + + wg.Wait() + + //see if any parts failed to send + numFail, errRtn := getSendErrors(errCh) + if numFail > 0 { + jww.INFO.Printf("Failed to E2E send %d/%d to %s", + numFail, len(partitions), recipient) + return nil, e2e.MessageID{}, time.Time{}, errors.Errorf("Failed to E2E send %v/%v sub payloads:"+ + " %s", numFail, len(partitions), errRtn) + } else { + jww.INFO.Printf("Successfully E2E sent %d/%d to %s", + len(partitions)-numFail, len(partitions), recipient) + } + + //return the rounds if everything send successfully + jww.INFO.Printf("Successful E2E Send of %d messages to %s with msgID %s", + len(partitions), recipient, msgID) + return roundIds, msgID, ts, nil +} + +// waitForKey waits the designated ammount of time for a +func waitForKey(keyGetter func() (*session.Cypher, error), numAttempts uint, + wait time.Duration, stop *stoppable.Single, recipient *id.ID, + digest string, partition int) (*session.Cypher, error) { + key, err := keyGetter() + if err == nil { + return key, nil + } + + ticker := time.NewTicker(wait) + defer ticker.Stop() + + for keyTries := uint(1); err != nil && keyTries < numAttempts; keyTries++ { + jww.WARN.Printf("Out of sending keys for %s "+ + "(digest: %s, partition: %d), this can "+ + "happen when sending messages faster than "+ + "the client can negotiate keys. Please "+ + "adjust your e2e key parameters", + recipient, digest, partition) + + select { + case <-ticker.C: + key, err = keyGetter() + case <-stop.Quit(): + stop.ToStopped() + return nil, errors.Errorf("Stopped by stopper") + } + } + + return key, err +} + +// getSendErrors returns any errors on the error channel +func getSendErrors(c chan error) (int, string) { + var errRtn string + numFail := 0 + done := false + for !done { + select { + case err := <-c: + errRtn += err.Error() + numFail++ + default: + done = true + } + } + return numFail, errRtn +} diff --git a/go.mod b/go.mod index 101ca34e6f5c908e2573a5d837cc41d95b3ac97c..0d045618487b259259566189623c0b31ac3990a9 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( gitlab.com/elixxir/comms v0.0.4-0.20220308183624-c2183e687a03 gitlab.com/elixxir/crypto v0.0.7-0.20220328164108-c72388181116 gitlab.com/elixxir/ekv v0.1.6 - gitlab.com/elixxir/primitives v0.0.3-0.20220325212708-5e2a7a385db7 + gitlab.com/elixxir/primitives v0.0.3-0.20220330212736-cce83b5f948f gitlab.com/xx_network/comms v0.0.4-0.20220311192415-d95fe8906580 gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4 gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b diff --git a/go.sum b/go.sum index 59dc6cb4ac92695cb2347125b6c174328fc3b3ed..a28b5a590f07d922e1e6993633a57c265559208e 100644 --- a/go.sum +++ b/go.sum @@ -297,6 +297,8 @@ gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623 h1:NzJ06KdJd3 gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623/go.mod h1:MtFIyJUQn9P7djzVlBpEYkPNnnWFTjZvw89swoXY+QM= gitlab.com/elixxir/primitives v0.0.3-0.20220325212708-5e2a7a385db7 h1:2yIEkxkPJboftkk/xiCONVP/FSl7Y3zOPpekvQ2dhO0= gitlab.com/elixxir/primitives v0.0.3-0.20220325212708-5e2a7a385db7/go.mod h1:MtFIyJUQn9P7djzVlBpEYkPNnnWFTjZvw89swoXY+QM= +gitlab.com/elixxir/primitives v0.0.3-0.20220330212736-cce83b5f948f h1:bOX9nsG+ihvZAUP2OimpM/0UNIcfz5tYlvQfS2IFUoU= +gitlab.com/elixxir/primitives v0.0.3-0.20220330212736-cce83b5f948f/go.mod h1:9Bb2+u+CDSwsEU5Droo6saDAXuBDvLRjexpBhPAYxhA= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= gitlab.com/xx_network/comms v0.0.4-0.20220223205228-7c4974139569/go.mod h1:isHnwem0v4rTcwwHP455FhVlFyPcHkHiVz+N3s/uCSI= gitlab.com/xx_network/comms v0.0.4-0.20220311192415-d95fe8906580 h1:IV0gDwdTxtCpc9Vkx7IeSStSqvG+0ZpF57X+OhTQDIM= diff --git a/network/params.go b/network/params.go index a37eaa3add93d73c58519cf63248b6ba8523c282..a36dcb018f4ae094388ea14b5285c839e9e09802 100644 --- a/network/params.go +++ b/network/params.go @@ -137,6 +137,8 @@ func GetDefaultCMIXParams() CMIXParams { RetryDelay: 1 * time.Second, SendTimeout: 3 * time.Second, DebugTag: "External", + //unused single so components that require one have a channel to wait on + Stop: stoppable.NewSingle("cmixParamsDefault"), } }