diff --git a/api/client.go b/api/client.go index 2c28d84d33b193a3236f2332a9c69a16f4e3e5d9..0aee60d576cae5301f0e7d5e48f0d91e68f50a21 100644 --- a/api/client.go +++ b/api/client.go @@ -304,7 +304,7 @@ func Login(storageDir string, password []byte, parameters params.Network) (*Clie } // initialize the auth tracker - c.auth = auth.NewManager(c.switchboard, c.storage, c.network) + c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests) // Add all processes to the followerServices err = c.registerFollower() @@ -363,7 +363,7 @@ func LoginWithNewBaseNDF_UNSAFE(storageDir string, password []byte, } // initialize the auth tracker - c.auth = auth.NewManager(c.switchboard, c.storage, c.network) + c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests) err = c.registerFollower() if err != nil { @@ -420,7 +420,7 @@ func LoginWithProtoClient(storageDir string, password []byte, protoClientJSON [] } // initialize the auth tracker - c.auth = auth.NewManager(c.switchboard, c.storage, c.network) + c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests) err = c.registerFollower() if err != nil { diff --git a/api/results.go b/api/results.go index 4f5668fc90a6fcf53d61d6a440fe7fb35f35e7b5..04e91e15e7c6e3407ecfab759dd9eb1f598d9885 100644 --- a/api/results.go +++ b/api/results.go @@ -152,17 +152,28 @@ 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 + roundId := roundReport.RoundInfo.GetRoundId() if roundReport.TimedOut { + roundInfo, err := networkInstance.GetRound(roundId) + // If we have the round in the buffer + if err == nil { + // Check if the round is done (completed or failed) or in progress + if states.Round(roundInfo.State) == states.COMPLETED { + roundsResults[roundId] = Succeeded + } else if states.Round(roundInfo.State) == states.FAILED { + roundsResults[roundId] = Failed + allRoundsSucceeded = false + } + return + } allRoundsSucceeded = false anyRoundTimedOut = true } else { // If available, denote the result - roundId := id.Round(roundReport.RoundInfo.ID) if states.Round(roundReport.RoundInfo.State) == states.COMPLETED { roundsResults[roundId] = Succeeded } else { diff --git a/api/version_vars.go b/api/version_vars.go index 8a44f54952836daac733cc3e151032f5e6b042cb..158d3df9e628bc4966be47b94441bf79cffb1e3f 100644 --- a/api/version_vars.go +++ b/api/version_vars.go @@ -1,9 +1,9 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots at -// 2022-01-04 12:45:01.875155 -0600 CST m=+0.041061278 +// 2022-01-28 13:54:19.313216 -0600 CST m=+0.285271189 package api -const GITVERSION = `1144194c Merge branch 'dev' into 'release'` +const GITVERSION = `17cd83e0 re-check ram storage before timing out GetRoundresults` const SEMVER = "4.0.0" const DEPENDENCIES = `module gitlab.com/elixxir/client @@ -26,11 +26,11 @@ 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.20220104174855-044783c5c1e6 - gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553 - gitlab.com/elixxir/ekv v0.1.5 + gitlab.com/elixxir/comms v0.0.4-0.20220128193157-34178165415d + gitlab.com/elixxir/crypto v0.0.7-0.20220110170041-7e42f2e8b062 + 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 + gitlab.com/xx_network/comms v0.0.4-0.20220126231737-fe2338016cce 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 diff --git a/auth/callback.go b/auth/callback.go index c0acff7b66784d4834cdfddc78c297b5496ff84b..7d3f262165df1f8cb5eb1f30a5c3283a99da9150 100644 --- a/auth/callback.go +++ b/auth/callback.go @@ -165,7 +165,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, return } else { //check if the relationship already exists, - rType, _, _, err := m.storage.Auth().GetRequest(partnerID) + rType, _, c, err := m.storage.Auth().GetRequest(partnerID) if err != nil && !strings.Contains(err.Error(), auth.NoRequest) { // if another error is received, print it and exit em := fmt.Sprintf("Received new Auth request for %s, "+ @@ -183,6 +183,15 @@ func (m *Manager) handleRequest(cmixMsg format.Message, "is a duplicate", partnerID) jww.WARN.Print(em) events.Report(5, "Auth", "DuplicateRequest", em) + // if the caller of the API wants requests replayed, + // replay the duplicate request + if m.replayRequests{ + cbList := m.requestCallbacks.Get(c.ID) + for _, cb := range cbList { + rcb := cb.(interfaces.RequestCallback) + go rcb(c) + } + } return // if we sent a request, then automatically confirm // then exit, nothing else needed @@ -296,7 +305,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, cbList := m.requestCallbacks.Get(c.ID) for _, cb := range cbList { rcb := cb.(interfaces.RequestCallback) - go rcb(c, "") + go rcb(c) } return } diff --git a/auth/manager.go b/auth/manager.go index b5803d69feb7ae67073e351b1c7fd24c3ac5b597..20b341c3891212edf308ec9c7afcf43a5f1588f5 100644 --- a/auth/manager.go +++ b/auth/manager.go @@ -23,16 +23,19 @@ type Manager struct { storage *storage.Session net interfaces.NetworkManager + + replayRequests bool } func NewManager(sw interfaces.Switchboard, storage *storage.Session, - net interfaces.NetworkManager) *Manager { + net interfaces.NetworkManager, replayRequests bool) *Manager { m := &Manager{ requestCallbacks: newCallbackMap(), confirmCallbacks: newCallbackMap(), rawMessages: make(chan message.Receive, 1000), storage: storage, net: net, + replayRequests: replayRequests, } sw.RegisterChannel("Auth", switchboard.AnyUser(), message.Raw, m.rawMessages) @@ -89,3 +92,17 @@ func (m *Manager) AddSpecificConfirmCallback(id *id.ID, cb interfaces.ConfirmCal func (m *Manager) RemoveSpecificConfirmCallback(id *id.ID) { m.confirmCallbacks.RemoveSpecific(id) } + +// ReplayRequests will iterate through all pending contact requests and resend them +// to the desired contact. +func (m *Manager) ReplayRequests() { + cList := m.storage.Auth().GetAllReceived() + for i := range cList { + c := cList[i] + cbList := m.requestCallbacks.Get(c.ID) + for _, cb := range cbList { + rcb := cb.(interfaces.RequestCallback) + go rcb(c) + } + } +} diff --git a/auth/manager_test.go b/auth/manager_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7332a2db6d45ba7e2c0d9ddbfb18bc02d06ab286 --- /dev/null +++ b/auth/manager_test.go @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package auth + +import ( + "github.com/cloudflare/circl/dh/sidh" + "gitlab.com/elixxir/client/interfaces" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/auth" + util "gitlab.com/elixxir/client/storage/utility" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/ekv" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/crypto/large" + "gitlab.com/xx_network/primitives/id" + "io" + "math/rand" + "testing" + "time" +) + +func TestManager_ReplayRequests(t *testing.T) { + s := storage.InitTestingSession(t) + numReceived := 10 + + // Construct barebones manager + m := Manager{ + requestCallbacks: newCallbackMap(), + storage: s, + replayRequests: true, + } + + ch := make(chan struct{}, numReceived) + + // Add multiple received contact requests + for i := 0; i < numReceived; i++ { + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + rng := csprng.NewSystemRNG() + _, sidhPubKey := genSidhAKeys(rng) + + if err := m.storage.Auth().AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + m.requestCallbacks.AddSpecific(c.ID, interfaces.RequestCallback(func(c contact.Contact) { + ch <- struct{}{} + })) + + } + + m.ReplayRequests() + + timeout := time.NewTimer(1 * time.Second) + numChannelReceived := 0 +loop: + for { + select { + case <-ch: + numChannelReceived++ + case <-timeout.C: + break loop + } + } + + if numReceived != numChannelReceived { + t.Errorf("Unexpected number of callbacks called"+ + "\nExpected: %d"+ + "\nReceived: %d", numChannelReceived, numReceived) + } +} + +func makeTestStore(t *testing.T) (*auth.Store, *versioned.KV, []*cyclic.Int) { + kv := versioned.NewKV(make(ekv.Memstore)) + grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(0)) + privKeys := make([]*cyclic.Int, 10) + for i := range privKeys { + privKeys[i] = grp.NewInt(rand.Int63n(170) + 1) + } + + store, err := auth.NewStore(kv, grp, privKeys) + if err != nil { + t.Fatalf("Failed to create new Store: %+v", err) + } + + return store, kv, privKeys +} + +func genSidhAKeys(rng io.Reader) (*sidh.PrivateKey, *sidh.PublicKey) { + sidHPrivKeyA := util.NewSIDHPrivateKey(sidh.KeyVariantSidhA) + sidHPubKeyA := util.NewSIDHPublicKey(sidh.KeyVariantSidhA) + + if err := sidHPrivKeyA.Generate(rng); err != nil { + panic("failure to generate SidH A private key") + } + sidHPrivKeyA.GeneratePublicKey(sidHPubKeyA) + + return sidHPrivKeyA, sidHPubKeyA +} diff --git a/bindings/authenticatedChannels.go b/bindings/authenticatedChannels.go index 67d2a885098b34671d75014189b682ed75a6a32c..36888a604177ae36b37d80f97ad61b01700cb51a 100644 --- a/bindings/authenticatedChannels.go +++ b/bindings/authenticatedChannels.go @@ -63,7 +63,7 @@ func (c *Client) RequestAuthenticatedChannel(recipientMarshaled, func (c *Client) RegisterAuthCallbacks(request AuthRequestCallback, confirm AuthConfirmCallback) { - requestFunc := func(requestor contact.Contact, message string) { + requestFunc := func(requestor contact.Contact) { requestorBind := &Contact{c: &requestor} request.Callback(requestorBind) } @@ -136,3 +136,8 @@ func (c *Client) GetRelationshipFingerprint(partnerID []byte) (string, error) { return c.api.GetRelationshipFingerprint(partner) } + +// ReplayRequests Resends all pending requests over the normal callbacks +func (c *Client) ReplayRequests() () { + c.api.GetAuthRegistrar().ReplayRequests() +} \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go index 328f814cd5f67dd93b13075e93d62795b3684861..7f38db02a83e6cbcd869637e9c5a6fbec920cd14 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -473,7 +473,7 @@ func initClientCallbacks(client *api.Client) (chan *id.ID, }) if viper.GetBool("unsafe-channel-creation") { authMgr.AddGeneralRequestCallback(func( - requestor contact.Contact, message string) { + requestor contact.Contact) { jww.INFO.Printf("Channel Request: %s", requestor.ID) _, err := client.ConfirmAuthenticatedChannel( @@ -681,13 +681,11 @@ func deleteChannel(client *api.Client, partnerId *id.ID) { } } -func printChanRequest(requestor contact.Contact, message string) { +func printChanRequest(requestor contact.Contact) { msg := fmt.Sprintf("Authentication channel request from: %s\n", requestor.ID) jww.INFO.Printf(msg) fmt.Printf(msg) - msg = fmt.Sprintf("Authentication channel request message: %s\n", message) - jww.INFO.Printf(msg) // fmt.Printf(msg) } diff --git a/cmd/single.go b/cmd/single.go index cc0cadadd56ea05f1656a7ff7ed3858213ee3169..cd6cc0a8815caa9969f02123274a0023512988e9 100644 --- a/cmd/single.go +++ b/cmd/single.go @@ -53,7 +53,7 @@ var singleCmd = &cobra.Command{ // If unsafe channels, then add auto-acceptor if viper.GetBool("unsafe-channel-creation") { authMgr.AddGeneralRequestCallback(func( - requester contact.Contact, message string) { + requester contact.Contact) { jww.INFO.Printf("Got request: %s", requester.ID) _, err := client.ConfirmAuthenticatedChannel(requester) if err != nil { diff --git a/cmd/ud.go b/cmd/ud.go index 767bc12eab4e84c82c93efc2a80e6b4613bc0a7a..34fff1c098f490195a952ac302cc7256ccf7b577 100644 --- a/cmd/ud.go +++ b/cmd/ud.go @@ -53,7 +53,7 @@ var udCmd = &cobra.Command{ // If unsafe channels, add auto-acceptor if viper.GetBool("unsafe-channel-creation") { authMgr.AddGeneralRequestCallback(func( - requester contact.Contact, message string) { + requester contact.Contact) { jww.INFO.Printf("Got Request: %s", requester.ID) _, err := client.ConfirmAuthenticatedChannel(requester) if err != nil { diff --git a/go.mod b/go.mod index cf560a7ee0afe56f1ffc4e384dd44a3e14a9c3ef..f2dd1611dfaaa9197a41c589bc864be29b39485c 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ 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.20220126233028-dc64691c7d3e + gitlab.com/elixxir/comms v0.0.4-0.20220128193157-34178165415d gitlab.com/elixxir/crypto v0.0.7-0.20220110170041-7e42f2e8b062 gitlab.com/elixxir/ekv v0.1.6 gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f diff --git a/go.sum b/go.sum index b72cbc4972da274d0312bc7e5b776d757126e388..e0505afce0c8e5f8ec1f2b68f6ebb53b1a13158c 100644 --- a/go.sum +++ b/go.sum @@ -272,8 +272,8 @@ 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.20220126233028-dc64691c7d3e h1:RE2v+HOiDTCglR5+hxUuR+fkQHkZqya/HTSmg+EoqsQ= -gitlab.com/elixxir/comms v0.0.4-0.20220126233028-dc64691c7d3e/go.mod h1:pj1TXrpHKytF68y53BtCBRYhaiMJWquuVBk4iEN7wkk= +gitlab.com/elixxir/comms v0.0.4-0.20220128193157-34178165415d h1:207Okb8+amKnRzsKE/4ehl3eb6ZHkWXevq9TimOehzw= +gitlab.com/elixxir/comms v0.0.4-0.20220128193157-34178165415d/go.mod h1:pj1TXrpHKytF68y53BtCBRYhaiMJWquuVBk4iEN7wkk= 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.20220110170041-7e42f2e8b062 h1:6LLdEX2U/jA3RakJh/cKtjckMYBBjrjyowoBEtIF9L4= diff --git a/interfaces/auth.go b/interfaces/auth.go index bb3cae74d0a37f0fda2c34e8ce0623af6f8a789f..4ce22fba789b45a5616a640f0409a054f6a05ecb 100644 --- a/interfaces/auth.go +++ b/interfaces/auth.go @@ -12,7 +12,7 @@ import ( "gitlab.com/xx_network/primitives/id" ) -type RequestCallback func(requestor contact.Contact, message string) +type RequestCallback func(requestor contact.Contact) type ConfirmCallback func(partner contact.Contact) type Auth interface { @@ -42,4 +42,6 @@ type Auth interface { AddSpecificConfirmCallback(id *id.ID, cb ConfirmCallback) // Removes a specific callback to be used on auth confirm. RemoveSpecificConfirmCallback(id *id.ID) + //Replays all pending received requests over tha callbacks + ReplayRequests() } diff --git a/interfaces/params/network.go b/interfaces/params/network.go index d4c37b06cc6e5b9e508185b226f02dec5df749df..28ecdf5aa980259146091c06ae0a0ef7b3ee37ef 100644 --- a/interfaces/params/network.go +++ b/interfaces/params/network.go @@ -36,6 +36,8 @@ type Network struct { VerboseRoundTracking bool //disables all attempts to pick up dropped or missed messages RealtimeOnly bool + // Resends auth requests up the stack if received multiple times + ReplayRequests bool Rounds Messages @@ -56,7 +58,11 @@ func GetDefaultNetwork() Network { FastPolling: true, BlacklistedNodes: make([]string, 0), VerboseRoundTracking: false, +<<<<<<< interfaces/params/network.go RealtimeOnly: false, +======= + ReplayRequests: true, +>>>>>>> interfaces/params/network.go } n.Rounds = GetDefaultRounds() n.Messages = GetDefaultMessage() diff --git a/network/follow.go b/network/follow.go index 550ec81e87db3e026e1285c7a63749abd132a425..6f98a33b50127d78f10e703b1c75081d02357d0f 100644 --- a/network/follow.go +++ b/network/follow.go @@ -224,23 +224,12 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, } // Update the address space size - // todo: this is a fix for incompatibility with the live network - // remove once the live network has been pushed to if len(m.Instance.GetPartialNdf().Get().AddressSpace) != 0 { m.addrSpace.Update(m.Instance.GetPartialNdf().Get().AddressSpace[0].Size) - } else { - m.addrSpace.Update(18) } - // NOTE: this updates rounds and updates the tracking of the health of the - // network + // NOTE: this updates rounds and updates the tracking of the health of the network if pollResp.Updates != nil { - err = m.Instance.RoundUpdates(pollResp.Updates) - if err != nil { - jww.ERROR.Printf("%+v", err) - return - } - // TODO: ClientErr needs to know the source of the error and it doesn't yet // Iterate over ClientErrors for each RoundUpdate for _, update := range pollResp.Updates { @@ -266,19 +255,11 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, return } + // Mutate the update to indicate failure due to a ClientError // FIXME: Should be able to trigger proper type of round event // FIXME: without mutating the RoundInfo. Signature also needs verified // FIXME: before keys are deleted update.State = uint32(states.FAILED) - rnd, err := m.Instance.GetWrappedRound(id.Round(update.ID)) - if err != nil { - jww.ERROR.Printf("Failed to report client error: "+ - "Could not get round for event triggering: "+ - "Unable to get round %d from instance: %+v", - id.Round(update.ID), err) - break - } - m.Instance.GetRoundEvents().TriggerRoundEvent(rnd) // delete all existing keys and trigger a re-registration with the relevant Node m.Session.Cmix().Remove(nid) @@ -287,6 +268,13 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, } } + // Trigger RoundEvents for all polled updates, including modified rounds with ClientErrors + err = m.Instance.RoundUpdates(pollResp.Updates) + if err != nil { + jww.ERROR.Printf("%+v", err) + return + } + newestTS := uint64(0) for i := 0; i < len(pollResp.Updates[len(pollResp.Updates)-1].Timestamps); i++ { if pollResp.Updates[len(pollResp.Updates)-1].Timestamps[i] != 0 { diff --git a/network/manager.go b/network/manager.go index c968102e2f5b04269c123c5d1b624ae7a865678a..bba264c6b63263a196d4bfc135f1db42649948ec 100644 --- a/network/manager.go +++ b/network/manager.go @@ -105,6 +105,7 @@ func NewManager(session *storage.Session, switchboard *switchboard.Switchboard, events: events, earliestRound: &earliest, } + m.addrSpace.Update(18) if params.VerboseRoundTracking { m.verboseRounds = NewRoundTracker() diff --git a/single/transmission.go b/single/transmission.go index e22e38c9344acca265aa5acb33875024be1dca10..436420a7f3711d7bbb4a5271c207779f64bd8704 100644 --- a/single/transmission.go +++ b/single/transmission.go @@ -13,7 +13,6 @@ import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces/params" - "gitlab.com/elixxir/client/interfaces/utility" "gitlab.com/elixxir/client/storage/reception" ds "gitlab.com/elixxir/comms/network/dataStructures" contact2 "gitlab.com/elixxir/crypto/contact" @@ -51,8 +50,7 @@ func (m *Manager) TransmitSingleUse(partner contact2.Contact, payload []byte, rngReader := m.rng.GetStream() defer rngReader.Close() - return m.transmitSingleUse(partner, payload, tag, maxMsgs, rngReader, - callback, timeout, m.net.GetInstance().GetRoundEvents()) + return m.transmitSingleUse(partner, payload, tag, maxMsgs, rngReader, callback, timeout) } // roundEvents interface allows custom round events to be passed in for testing. @@ -63,7 +61,7 @@ type roundEvents interface { // transmitSingleUse has the fields passed in for easier testing. func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte, - tag string, MaxMsgs uint8, rng io.Reader, callback ReplyComm, timeout time.Duration, roundEvents roundEvents) error { + tag string, MaxMsgs uint8, rng io.Reader, callback ReplyComm, timeout time.Duration) error { // Get ephemeral ID address space size; this blocks until the address space // size is set for the first time @@ -123,7 +121,7 @@ func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte, if err != nil { errorString := fmt.Sprintf("failed to send single-use transmission "+ "CMIX message: %+v", err) - jww.ERROR.Print(errorString) + jww.ERROR.Printf(errorString) // Exit the state timeout handler, delete the state from map, and // return an error on the callback @@ -140,40 +138,9 @@ func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte, "message because the timeout handler quit.") return } - - // Update the timeout for the elapsed time - roundEventTimeout := timeout - netTime.Since(timeStart) - time.Millisecond - - // Check message delivery - sendResults := make(chan ds.EventReturn, 1) - roundEvents.AddRoundEventChan(round, sendResults, roundEventTimeout, - states.COMPLETED, states.FAILED) - - im := fmt.Sprintf("Sent single-use transmission CMIX "+ + jww.DEBUG.Printf("Sent single-use transmission CMIX "+ "message to %s and ephemeral ID %d on round %d.", partner.ID, ephID.Int64(), round) - jww.DEBUG.Print(im) - if m.client != nil { - m.client.ReportEvent(1, "SingleUse", "MessageSend", im) - } - - // Wait until the result tracking responds - success, numRoundFail, numTimeOut := utility.TrackResults(sendResults, 1) - if !success { - errorString := fmt.Sprintf("failed to send single-use transmission "+ - "message: %d round failures, %d round event time outs.", - numRoundFail, numTimeOut) - jww.ERROR.Print(errorString) - - // Exit the state timeout handler, delete the state from map, and - // return an error on the callback - quitChan <- struct{}{} - m.p.Lock() - delete(m.p.singleUse, *rid) - m.p.Unlock() - go callback(nil, errors.New(errorString)) - } - jww.DEBUG.Print("Tracked single-use transmission message round.") }() return nil diff --git a/single/transmission_test.go b/single/transmission_test.go index 548cefa7c0be402eda3671278a4234e6fb610672..15c557e60292021edab2252bd05ec10e5553d6a7 100644 --- a/single/transmission_test.go +++ b/single/transmission_test.go @@ -26,7 +26,7 @@ func TestManager_GetMaxTransmissionPayloadSize(t *testing.T) { m := newTestManager(0, false, t) cmixPrimeSize := m.store.Cmix().GetGroup().GetP().ByteLen() e2ePrimeSize := m.store.E2e().GetGroup().GetP().ByteLen() - expectedSize := 2*cmixPrimeSize - e2ePrimeSize - format.KeyFPLen - format.MacLen - format.RecipientIDLen - transmitPlMinSize - transmitMessageVersionSize-1 + expectedSize := 2*cmixPrimeSize - e2ePrimeSize - format.KeyFPLen - format.MacLen - format.RecipientIDLen - transmitPlMinSize - transmitMessageVersionSize - 1 testSize := m.GetMaxTransmissionPayloadSize() if expectedSize != testSize { @@ -50,8 +50,7 @@ func TestManager_transmitSingleUse(t *testing.T) { callback, callbackChan := createReplyComm() timeout := 15 * time.Millisecond - err := m.transmitSingleUse(partner, payload, tag, maxMsgs, prng, - callback, timeout, newTestRoundEvents(false)) + err := m.transmitSingleUse(partner, payload, tag, maxMsgs, prng, callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -93,7 +92,7 @@ func TestManager_transmitSingleUse_QuitChanError(t *testing.T) { timeout := 15 * time.Millisecond err := m.transmitSingleUse(partner, []byte{}, "testTag", 9, - rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false)) + rand.New(rand.NewSource(42)), callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -125,7 +124,7 @@ func TestManager_transmitSingleUse_AddIdentityError(t *testing.T) { callback, callbackChan := createReplyComm() err := m.transmitSingleUse(partner, []byte{}, "testTag", 9, - rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false)) + rand.New(rand.NewSource(42)), callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -158,7 +157,7 @@ func TestManager_transmitSingleUse_SendCMIXError(t *testing.T) { timeout := 15 * time.Millisecond err := m.transmitSingleUse(partner, []byte{}, "testTag", 9, - rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false)) + rand.New(rand.NewSource(42)), callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -182,7 +181,7 @@ func TestManager_transmitSingleUse_MakeTransmitCmixMessageError(t *testing.T) { prng := rand.New(rand.NewSource(42)) payload := make([]byte, m.store.Cmix().GetGroup().GetP().ByteLen()) - err := m.transmitSingleUse(contact2.Contact{}, payload, "", 0, prng, nil, 0, nil) + err := m.transmitSingleUse(contact2.Contact{}, payload, "", 0, prng, nil, 0) if err == nil { t.Error("transmitSingleUse() did not return an error when the payload " + "is too large.") @@ -212,7 +211,7 @@ func TestManager_transmitSingleUse_AddStateError(t *testing.T) { m.p.singleUse[*rid] = newState(dhKey, maxMsgs, nil) err = m.transmitSingleUse(partner, payload, tag, maxMsgs, - rand.New(rand.NewSource(42)), callback, timeout, nil) + rand.New(rand.NewSource(42)), callback, timeout) if !check(err, "failed to add pending state") { t.Errorf("transmitSingleUse() failed to error when on adding state "+ "when the state already exists: %+v", err) @@ -232,8 +231,7 @@ func TestManager_transmitSingleUse_RoundTimeoutError(t *testing.T) { callback, callbackChan := createReplyComm() timeout := 15 * time.Millisecond - err := m.transmitSingleUse(partner, payload, "testTag", 8, prng, callback, - timeout, newTestRoundEvents(true)) + err := m.transmitSingleUse(partner, payload, "testTag", 8, prng, callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -242,7 +240,7 @@ func TestManager_transmitSingleUse_RoundTimeoutError(t *testing.T) { select { case results := <-callbackChan: - if results.payload != nil || !check(results.err, "round failures") { + if results.payload != nil || !check(results.err, "timed out") { t.Errorf("Callback did not return the correct error when it "+ "should have timed out.\npayload: %+v\nerror: %+v", results.payload, results.err) diff --git a/storage/auth/store.go b/storage/auth/store.go index addd3d138499ae09cfc5e9dc5d221d79d95d060a..2bb90faf774bb777b28b33799934d89f9a966fe9 100644 --- a/storage/auth/store.go +++ b/storage/auth/store.go @@ -261,6 +261,20 @@ func (s *Store) AddReceived(c contact.Contact, key *sidh.PublicKey) error { return nil } +// GetAllReceived returns all pending received contact requests from storage. +func (s *Store) GetAllReceived() []contact.Contact { + s.mux.RLock() + defer s.mux.RUnlock() + cList := make([]contact.Contact, 0, len(s.requests)) + for key := range s.requests { + r := s.requests[key] + if r.rt == Receive { + cList = append(cList, *r.receive) + } + } + return cList +} + // GetFingerprint can return either a private key or a sentRequest if the // fingerprint is found. If it returns a sentRequest, then it takes the lock to // ensure there is only one operator at a time. The user of the API must release diff --git a/storage/auth/store_test.go b/storage/auth/store_test.go index b1c4bb92b95c98eadc23db6e66d1ab5e353efc51..6568cd75540f7633b711d64380e106435b50d359 100644 --- a/storage/auth/store_test.go +++ b/storage/auth/store_test.go @@ -8,6 +8,7 @@ package auth import ( + "bytes" "github.com/cloudflare/circl/dh/sidh" sidhinterface "gitlab.com/elixxir/client/interfaces/sidh" util "gitlab.com/elixxir/client/storage/utility" @@ -23,6 +24,7 @@ import ( "io" "math/rand" "reflect" + "sort" "sync" "testing" ) @@ -764,6 +766,145 @@ func TestStore_Delete_RequestNotInMap(t *testing.T) { } } +// Unit test of Store.GetAllReceived. +func TestStore_GetAllReceived(t *testing.T) { + s, _, _ := makeTestStore(t) + numReceived := 10 + + expectContactList := make([]contact.Contact, 0, numReceived) + // Add multiple received contact requests + for i := 0; i < numReceived; i++ { + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + rng := csprng.NewSystemRNG() + _, sidhPubKey := genSidhAKeys(rng) + + if err := s.AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + expectContactList = append(expectContactList, c) + } + + // Check that GetAllReceived returns all contacts + receivedContactList := s.GetAllReceived() + if len(receivedContactList) != numReceived { + t.Errorf("GetAllReceived did not return expected amount of contacts."+ + "\nExpected: %d"+ + "\nReceived: %d", numReceived, len(receivedContactList)) + } + + // Sort expected and received lists so that they are in the same order + // since extraction from a map does not maintain order + sort.Slice(expectContactList, func(i, j int) bool { + return bytes.Compare(expectContactList[i].ID.Bytes(), expectContactList[j].ID.Bytes()) == -1 + }) + sort.Slice(receivedContactList, func(i, j int) bool { + return bytes.Compare(receivedContactList[i].ID.Bytes(), receivedContactList[j].ID.Bytes()) == -1 + }) + + // Check validity of contacts + if !reflect.DeepEqual(expectContactList, receivedContactList) { + t.Errorf("GetAllReceived did not return expected contact list."+ + "\nExpected: %+v"+ + "\nReceived: %+v", expectContactList, receivedContactList) + } + +} + +// Tests that Store.GetAllReceived returns an empty list when there are no +// received requests. +func TestStore_GetAllReceived_EmptyList(t *testing.T) { + s, _, _ := makeTestStore(t) + + // Check that GetAllReceived returns all contacts + receivedContactList := s.GetAllReceived() + if len(receivedContactList) != 0 { + t.Errorf("GetAllReceived did not return expected amount of contacts."+ + "\nExpected: %d"+ + "\nReceived: %d", 0, len(receivedContactList)) + } + + // Add Sent and Receive requests + for i := 0; i < 10; i++ { + partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) + rng := csprng.NewSystemRNG() + sidhPrivKey, sidhPubKey := genSidhAKeys(rng) + sr := &SentRequest{ + kv: s.kv, + partner: partnerID, + partnerHistoricalPubKey: s.grp.NewInt(1), + myPrivKey: s.grp.NewInt(2), + myPubKey: s.grp.NewInt(3), + mySidHPrivKeyA: sidhPrivKey, + mySidHPubKeyA: sidhPubKey, + fingerprint: format.Fingerprint{5}, + } + if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey, + sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA, + sr.mySidHPubKeyA, sr.fingerprint); err != nil { + t.Fatalf("AddSent() returned an error: %+v", err) + } + } + + // Check that GetAllReceived returns all contacts + receivedContactList = s.GetAllReceived() + if len(receivedContactList) != 0 { + t.Errorf("GetAllReceived did not return expected amount of contacts. "+ + "It may be pulling from Sent Requests."+ + "\nExpected: %d"+ + "\nReceived: %d", 0, len(receivedContactList)) + } + +} + +// Tests that Store.GetAllReceived returns only Sent requests when there +// are both Sent and Receive requests in Store. +func TestStore_GetAllReceived_MixSentReceived(t *testing.T) { + s, _, _ := makeTestStore(t) + numReceived := 10 + + // Add multiple received contact requests + for i := 0; i < numReceived; i++ { + // Add received request + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + rng := csprng.NewSystemRNG() + _, sidhPubKey := genSidhAKeys(rng) + + if err := s.AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + // Add sent request + partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) + sidhPrivKey, sidhPubKey := genSidhAKeys(rng) + sr := &SentRequest{ + kv: s.kv, + partner: partnerID, + partnerHistoricalPubKey: s.grp.NewInt(1), + myPrivKey: s.grp.NewInt(2), + myPubKey: s.grp.NewInt(3), + mySidHPrivKeyA: sidhPrivKey, + mySidHPubKeyA: sidhPubKey, + fingerprint: format.Fingerprint{5}, + } + if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey, + sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA, + sr.mySidHPubKeyA, sr.fingerprint); err != nil { + t.Fatalf("AddSent() returned an error: %+v", err) + } + } + + // Check that GetAllReceived returns all contacts + receivedContactList := s.GetAllReceived() + if len(receivedContactList) != numReceived { + t.Errorf("GetAllReceived did not return expected amount of contacts. "+ + "It may be pulling from Sent Requests."+ + "\nExpected: %d"+ + "\nReceived: %d", numReceived, len(receivedContactList)) + } + +} + func makeTestStore(t *testing.T) (*Store, *versioned.KV, []*cyclic.Int) { kv := versioned.NewKV(make(ekv.Memstore)) grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(0)) diff --git a/storage/session.go b/storage/session.go index e54ab01fbc4a2002bac5ffa445d906ffd8710186..d11e11fc6697edd4e52d3550c6de2d6677a37bd1 100644 --- a/storage/session.go +++ b/storage/session.go @@ -486,6 +486,18 @@ func InitTestingSession(i interface{}) *Session { s.hostList = hostList.NewStore(s.kv) + privKeys := make([]*cyclic.Int, 10) + pubKeys := make([]*cyclic.Int, 10) + for i := range privKeys { + privKeys[i] = cmixGrp.NewInt(5) + pubKeys[i] = cmixGrp.ExpG(privKeys[i], cmixGrp.NewInt(1)) + } + + s.auth, err = auth.NewStore(s.kv, cmixGrp, privKeys) + if err != nil { + jww.FATAL.Panicf("Failed to create auth store: %v", err) + } + s.edgeCheck, err = edge.NewStore(s.kv, uid) if err != nil { jww.FATAL.Panicf("Failed to create new edge Store: %+v", err)