diff --git a/README.md b/README.md index e494e57d83614256744b08ea2adf0af371103f4d..26fc33259e9795344bb0477e1fcb97c94e287c05 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,10 @@ Available Commands: Flags: --accept-channel Accept the channel request for the corresponding recipient ID --auth-timeout uint The number of seconds to wait for an authentication channelto confirm (default 120) + --delete-all-requests Delete the all contact requests, both sent and received. --delete-channel Delete the channel information for the corresponding recipient ID + --delete-receive-requests Delete the all received contact requests. + --delete-sent-requests Delete the all sent contact requests. --destfile string Read this contact file for the destination id -d, --destid string ID to send message to (if below 40, will be precanned. Use '0x' or 'b64:' for hex and base64 representations) (default "0") --e2eMaxKeys uint Max keys used before blocking until a rekey completes (default 800) diff --git a/api/client.go b/api/client.go index 2c28d84d33b193a3236f2332a9c69a16f4e3e5d9..a289fea1e030118346df4011ef5601e49438fe6d 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 { @@ -684,6 +684,24 @@ func (c *Client) GetNodeRegistrationStatus() (int, int, error) { return numRegistered, len(nodes) - numStale, nil } +// DeleteAllRequests clears all requests from client's auth storage. +func (c *Client) DeleteAllRequests() error { + jww.DEBUG.Printf("Deleting all requests") + return c.GetStorage().Auth().DeleteAllRequests() +} + +// DeleteSentRequests clears sent requests from client's auth storage. +func (c *Client) DeleteSentRequests() error { + jww.DEBUG.Printf("Deleting all sent requests") + return c.GetStorage().Auth().DeleteSentRequests() +} + +// DeleteReceiveRequests clears receive requests from client's auth storage. +func (c *Client) DeleteReceiveRequests() error { + jww.DEBUG.Printf("Deleting all received requests") + return c.GetStorage().Auth().DeleteReceiveRequests() +} + // DeleteContact is a function which removes a partner from Client's storage func (c *Client) DeleteContact(partnerId *id.ID) error { jww.DEBUG.Printf("Deleting contact with ID %s", partnerId) @@ -742,8 +760,8 @@ func (c *Client) DeleteContact(partnerId *id.ID) error { //delete conversations c.storage.Conversations().Delete(partnerId) - // call delete requests to make sure nothing is lingering. - // this is for saftey to ensure the contact can be readded + // call delete requests to make sure nothing is lingering. + // this is for saftey to ensure the contact can be readded // in the future _ = c.storage.Auth().Delete(partnerId) 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..7ae57d144bfb2a3c6e0b7b8cd02cb610d4694553 100644 --- a/auth/callback.go +++ b/auth/callback.go @@ -9,6 +9,8 @@ package auth import ( "fmt" + "strings" + "github.com/cloudflare/circl/dh/sidh" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" @@ -26,7 +28,6 @@ import ( "gitlab.com/elixxir/primitives/fact" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/crypto/csprng" - "strings" ) func (m *Manager) StartProcesses() (stoppable.Stoppable, error) { @@ -165,7 +166,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 +184,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 @@ -220,12 +230,28 @@ func (m *Manager) handleRequest(cmixMsg format.Message, } // If I do, delete my request on disk - _, _, partnerContact, _ := m.storage.Auth().GetRequest(partnerID) m.storage.Auth().Delete(partnerID) - // Use the public key sent to me, not the one I - // first retrieved to initiate the auth request - partnerContact.DhPubKey = partnerPubKey + //process the inner payload + facts, _, err := fact.UnstringifyFactList( + string(requestFmt.msgPayload)) + if err != nil { + em := fmt.Sprintf("failed to parse facts and message "+ + "from Auth Request: %s", err) + jww.WARN.Print(em) + events.Report(10, "Auth", "RequestError", em) + return + } + + // create the contact, note that we use the data + // sent in the request and not any data we had + // already + partnerContact := contact.Contact{ + ID: partnerID, + DhPubKey: partnerPubKey, + OwnershipProof: copySlice(ownership), + Facts: facts, + } // add a confirmation to disk if err = m.storage.Auth().AddReceived(partnerContact, @@ -296,7 +322,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/bindings/client.go b/bindings/client.go index eb48e2694f9ec79c79e467ea389a8ec68dda7e83..1996507cbc0a005ae20006763f6596fe1ca25834 100644 --- a/bindings/client.go +++ b/bindings/client.go @@ -450,6 +450,21 @@ func (c *Client) GetNodeRegistrationStatus() (*NodeRegistrationsStatus, error) { return &NodeRegistrationsStatus{registered, total}, err } +// DeleteAllRequests clears all requests from Client's auth storage. +func (c *Client) DeleteAllRequests() error { + return c.api.DeleteAllRequests() +} + +// DeleteSentRequests clears sent requests from Client's auth storage. +func (c *Client) DeleteSentRequests() error { + return c.api.DeleteSentRequests() +} + +// DeleteReceiveRequests clears receive requests from Client's auth storage. +func (c *Client) DeleteReceiveRequests() error { + return c.api.DeleteReceiveRequests() +} + // DeleteContact is a function which removes a contact from Client's storage func (c *Client) DeleteContact(b []byte) error { contactObj, err := UnmarshalContact(b) diff --git a/cmd/root.go b/cmd/root.go index 328f814cd5f67dd93b13075e93d62795b3684861..a3ce0ce3bad8f8188e3a24dad103d75b138dcfbc 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -304,6 +304,18 @@ var rootCmd = &cobra.Command{ deleteChannel(client, recipientID) } + if viper.GetBool("delete-receive-requests") { + client.DeleteReceiveRequests() + } + + if viper.GetBool("delete-sent-requests") { + client.DeleteSentRequests() + } + + if viper.GetBool("delete-all-requests") { + client.DeleteAllRequests() + } + msg := message.Send{ Recipient: recipientID, Payload: []byte(msgBody), @@ -473,7 +485,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 +693,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) } @@ -1032,6 +1042,21 @@ func init() { viper.BindPFlag("delete-channel", rootCmd.PersistentFlags().Lookup("delete-channel")) + rootCmd.PersistentFlags().Bool("delete-receive-requests", false, + "Delete the all received contact requests.") + viper.BindPFlag("delete-receive-requests", + rootCmd.PersistentFlags().Lookup("delete-receive-requests")) + + rootCmd.PersistentFlags().Bool("delete-sent-requests", false, + "Delete the all sent contact requests.") + viper.BindPFlag("delete-sent-requests", + rootCmd.PersistentFlags().Lookup("delete-sent-requests")) + + rootCmd.PersistentFlags().Bool("delete-all-requests", false, + "Delete the all contact requests, both sent and received.") + viper.BindPFlag("delete-all-requests", + rootCmd.PersistentFlags().Lookup("delete-all-requests")) + rootCmd.Flags().BoolP("send-auth-request", "", false, "Send an auth request to the specified destination and wait"+ "for confirmation") 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.sum b/go.sum index 17e6b8e459490d28188989baed6697e6221324b6..a4e00c7b0cc6a5518018f50769a1144cd04c416f 100644 --- a/go.sum +++ b/go.sum @@ -272,10 +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.20220128144348-f01dc3227d76 h1:bmc/tvLJOIciHRCcRhkZqYKDcH4F503BKUitocEcQk0= -gitlab.com/elixxir/comms v0.0.4-0.20220128144348-f01dc3227d76/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/message.go b/interfaces/params/message.go index fbf9779829b939145cf7bc1277fa79b5617b826a..27a8ebd7d626445cc4c2ace03772d5dbbb6105ed 100644 --- a/interfaces/params/message.go +++ b/interfaces/params/message.go @@ -16,6 +16,7 @@ type Messages struct { MessageReceptionWorkerPoolSize uint MaxChecksGarbledMessage uint GarbledMessageWait time.Duration + RealtimeOnly bool } func GetDefaultMessage() Messages { @@ -24,5 +25,6 @@ func GetDefaultMessage() Messages { MessageReceptionWorkerPoolSize: 4, MaxChecksGarbledMessage: 10, GarbledMessageWait: 15 * time.Minute, + RealtimeOnly: false, } } diff --git a/interfaces/params/network.go b/interfaces/params/network.go index 67d083802e516de5d0feca95a299416489561a8a..7e3b6f4ab3f72f664af8e5b7e9cc5d226950a37f 100644 --- a/interfaces/params/network.go +++ b/interfaces/params/network.go @@ -34,6 +34,10 @@ type Network struct { // Determines if the state of every round processed is tracked in ram. // This is very memory intensive and is primarily used for debugging 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 @@ -54,6 +58,8 @@ func GetDefaultNetwork() Network { FastPolling: true, BlacklistedNodes: make([]string, 0), VerboseRoundTracking: false, + RealtimeOnly: false, + ReplayRequests: true, } n.Rounds = GetDefaultRounds() n.Messages = GetDefaultMessage() @@ -65,6 +71,13 @@ func (n Network) Marshal() ([]byte, error) { return json.Marshal(n) } +func (n Network) SetRealtimeOnlyAll()Network { + n.RealtimeOnly = true + n.Rounds.RealtimeOnly = true + n.Messages.RealtimeOnly = true + return n +} + // Obtain default Network parameters, or override with given parameters if set func GetNetworkParameters(params string) (Network, error) { p := GetDefaultNetwork() diff --git a/interfaces/params/rounds.go b/interfaces/params/rounds.go index 40bd41bfbd80a6cef2ecf30d8a1c645782f7b305..75e4270987ab2b5d8ea1c4edb6e49efb9f32a863 100644 --- a/interfaces/params/rounds.go +++ b/interfaces/params/rounds.go @@ -43,6 +43,9 @@ type Rounds struct { // Duration to wait before sending on a round times out and a new round is // tried SendTimeout time.Duration + + //disables all attempts to pick up dropped or missed messages + RealtimeOnly bool } func GetDefaultRounds() Rounds { @@ -58,5 +61,6 @@ func GetDefaultRounds() Rounds { UncheckRoundPeriod: 20 * time.Second, ForceMessagePickupRetry: false, SendTimeout: 1 * time.Second, + RealtimeOnly: false, } } diff --git a/network/follow.go b/network/follow.go index ec9e10a47cec044b0aa2b8e44ac93e53edd3f941..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 { @@ -382,12 +370,17 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, jww.DEBUG.Printf("New Earliest Remaining: %d, Gateways last checked: %d", earliestRemaining, gwRoundsState.GetLastChecked()) } - roundsWithMessages2 := identity.UR.Iterate(func(rid id.Round) bool { - if gwRoundsState.Checked(rid) { - return rounds.Checker(rid, filterList, identity.CR) - } - return false - }, roundsUnknown, abandon) + var roundsWithMessages2 []id.Round + + if !m.param.RealtimeOnly{ + roundsWithMessages2 = identity.UR.Iterate(func(rid id.Round) bool { + if gwRoundsState.Checked(rid) { + return rounds.Checker(rid, filterList, identity.CR) + } + return false + }, roundsUnknown, abandon) + } + for _, rid := range roundsWithMessages { //denote that the round has been looked at in the tracking store 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/network/rounds/check.go b/network/rounds/check.go index 3b9eb2d2849400d2fea2227373839cbc0def44e6..295a63f0abdb607660c0e6bc6865e7d026d608f6 100644 --- a/network/rounds/check.go +++ b/network/rounds/check.go @@ -56,6 +56,11 @@ func serializeRound(roundId id.Round) []byte { func (m *Manager) GetMessagesFromRound(roundID id.Round, identity reception.IdentityUse) { ri, err := m.Instance.GetRound(roundID) if err != nil || m.params.ForceHistoricalRounds { + if m.params.RealtimeOnly { + jww.WARN.Printf("Skipping round %d because it is not in ram and we are realtime only mode", + roundID) + return + } if m.params.ForceHistoricalRounds { jww.WARN.Printf("Forcing use of historical rounds for round ID %d.", roundID) @@ -80,11 +85,14 @@ func (m *Manager) GetMessagesFromRound(roundID id.Round, identity reception.Iden "up messages via in ram lookup", roundID, identity.EphId.Int64(), identity.Source) //store the round as an unretreived round - err = m.Session.UncheckedRounds().AddRound(roundID, ri, - identity.Source, identity.EphId) - if err != nil { - jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID) + if !m.params.RealtimeOnly { + err = m.Session.UncheckedRounds().AddRound(roundID, ri, + identity.Source, identity.EphId) + if err != nil { + jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID) + } } + // If found, send to Message Retrieval Workers m.lookupRoundMessages <- roundLookup{ roundInfo: ri, diff --git a/network/rounds/manager.go b/network/rounds/manager.go index c695cd7a8c78be622769a4388a669eda5757b304..f220583de8de98ab706b48db3a2b24d6a6168078 100644 --- a/network/rounds/manager.go +++ b/network/rounds/manager.go @@ -58,9 +58,12 @@ func (m *Manager) StartProcessors() stoppable.Stoppable { } // Start the periodic unchecked round worker - stopper := stoppable.NewSingle("UncheckRound") - go m.processUncheckedRounds(m.params.UncheckRoundPeriod, backOffTable, stopper) - multi.Add(stopper) + if !m.params.RealtimeOnly{ + stopper := stoppable.NewSingle("UncheckRound") + go m.processUncheckedRounds(m.params.UncheckRoundPeriod, backOffTable, stopper) + multi.Add(stopper) + } + return multi } diff --git a/network/rounds/retrieve.go b/network/rounds/retrieve.go index bd378ecf24728018dbdf824d97efc522bd227ed3..00f6675375243ef4025ad86f87751b578f185c1c 100644 --- a/network/rounds/retrieve.go +++ b/network/rounds/retrieve.go @@ -49,12 +49,15 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, case rl := <-m.lookupRoundMessages: ri := rl.roundInfo jww.DEBUG.Printf("Checking for messages in round %d", ri.ID) - err := m.Session.UncheckedRounds().AddRound(id.Round(ri.ID), ri, - rl.identity.Source, rl.identity.EphId) - if err != nil { - jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", id.Round(ri.ID)) + if !m.params.RealtimeOnly{ + err := m.Session.UncheckedRounds().AddRound(id.Round(ri.ID), ri, + rl.identity.Source, rl.identity.EphId) + if err != nil { + jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", id.Round(ri.ID)) + } } + // Convert gateways in round to proper ID format gwIds := make([]*id.ID, len(ri.Topology)) for i, idBytes := range ri.Topology { @@ -73,7 +76,7 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, // messages first, randomize other members of the team var rndBytes [32]byte stream := m.Rng.GetStream() - _, err = stream.Read(rndBytes[:]) + _, err := stream.Read(rndBytes[:]) stream.Close() if err != nil { jww.FATAL.Panicf("Failed to randomize shuffle in round %d "+ @@ -129,12 +132,15 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, m.messageBundles <- bundle jww.DEBUG.Printf("Removing round %d from unchecked store", ri.ID) - err = m.Session.UncheckedRounds().Remove(id.Round(ri.ID), rl.identity.Source, rl.identity.EphId) - if err != nil { - jww.ERROR.Printf("Could not remove round %d "+ - "from unchecked rounds store: %v", ri.ID, err) + if !m.params.RealtimeOnly{ + err = m.Session.UncheckedRounds().Remove(id.Round(ri.ID), rl.identity.Source, rl.identity.EphId) + if err != nil { + jww.ERROR.Printf("Could not remove round %d "+ + "from unchecked rounds store: %v", ri.ID, err) + } } + } } @@ -190,12 +196,14 @@ func (m *Manager) getMessagesFromGateway(roundID id.Round, " in round %d. This happening every once in a while is normal,"+ " but can be indicative of a problem if it is consistent", m.TransmissionID, roundID) - - err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId) - if err != nil { - jww.ERROR.Printf("Failed to remove round %d: %+v", roundID, err) + if m.params.RealtimeOnly{ + err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId) + if err != nil { + jww.ERROR.Printf("Failed to remove round %d: %+v", roundID, err) + } } + return message.Bundle{}, nil } 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..fb9aa2b04be8875bae32de4c74752db3dd80ddee 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 @@ -389,7 +403,7 @@ func (s *Store) Done(partner *id.ID) { r.mux.Unlock() } -// delete is one of two calls after using a request. This one is to be used when +// Delete is one of two calls after using a request. This one is to be used when // the use is unsuccessful. It deletes all references to the request associated // with the passed partner, if it exists. It will allow any thread waiting on // access to continue. They should fail due to the deletion of the structure. @@ -404,16 +418,9 @@ func (s *Store) Delete(partner *id.ID) error { switch r.rt { case Sent: - delete(s.fingerprints, r.sent.fingerprint) - if err := r.sent.delete(); err != nil { - jww.FATAL.Panicf("Failed to delete sent request: %+v", err) - } - + s.deleteSentRequest(r) case Receive: - if err := util.DeleteContact(s.kv, r.receive.ID); err != nil { - jww.FATAL.Panicf("Failed to delete recieved request "+ - "contact: %+v", err) - } + s.deleteReceiveRequest(r) } delete(s.requests, *partner) @@ -424,3 +431,91 @@ func (s *Store) Delete(partner *id.ID) error { return nil } + +// DeleteAllRequests clears the request map and all associated storage objects +// containing request data. +func (s *Store) DeleteAllRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.requests { + switch req.rt { + case Sent: + s.deleteSentRequest(req) + delete(s.requests, partnerId) + case Receive: + s.deleteReceiveRequest(req) + delete(s.requests, partnerId) + } + + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all requests: %+v", err) + } + + return nil +} + +// DeleteSentRequests deletes all Sent requests from Store. +func (s *Store) DeleteSentRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.requests { + switch req.rt { + case Sent: + s.deleteSentRequest(req) + delete(s.requests, partnerId) + case Receive: + continue + } + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all sent requests: %+v", err) + } + + return nil +} + +// DeleteReceiveRequests deletes all Receive requests from Store. +func (s *Store) DeleteReceiveRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.requests { + switch req.rt { + case Sent: + continue + case Receive: + s.deleteReceiveRequest(req) + delete(s.requests, partnerId) + } + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all receive requests: %+v", err) + } + + return nil +} + +// deleteSentRequest is a helper function which deletes a Sent request from storage. +func (s *Store) deleteSentRequest(r *request) { + delete(s.fingerprints, r.sent.fingerprint) + if err := r.sent.delete(); err != nil { + jww.FATAL.Panicf("Failed to delete sent request: %+v", err) + } +} + +// deleteReceiveRequest is a helper function which deletes a Receive request from storage. +func (s *Store) deleteReceiveRequest(r *request) { + if err := util.DeleteContact(s.kv, r.receive.ID); err != nil { + jww.FATAL.Panicf("Failed to delete recieved request "+ + "contact: %+v", err) + } +} diff --git a/storage/auth/store_test.go b/storage/auth/store_test.go index b1c4bb92b95c98eadc23db6e66d1ab5e353efc51..f1f10414836eb6c37596593322d112dbcfcc9394 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,309 @@ 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)) + } + +} + +// Unit test. +func TestStore_DeleteReceiveRequests(t *testing.T) { + s, _, _ := makeTestStore(t) + 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) + } + if _, _, err := s.GetReceivedRequest(c.ID); err != nil { + t.Fatalf("GetReceivedRequest() returned an error: %+v", err) + } + + err := s.DeleteReceiveRequests() + if err != nil { + t.Fatalf("DeleteReceiveRequests returned an error: %+v", err) + } + + if s.requests[*c.ID] != nil { + t.Errorf("delete() failed to delete request for user %s.", c.ID) + } +} + +// Unit test. +func TestStore_DeleteSentRequests(t *testing.T) { + s, _, _ := makeTestStore(t) + 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) + } + + err := s.DeleteSentRequests() + if err != nil { + t.Fatalf("DeleteSentRequests returned an error: %+v", err) + } + + if s.requests[*sr.partner] != nil { + t.Errorf("delete() failed to delete request for user %s.", + sr.partner) + } + + if _, exists := s.fingerprints[sr.fingerprint]; exists { + t.Errorf("delete() failed to delete fingerprint for fp %v.", + sr.fingerprint) + } +} + +// Tests that DeleteSentRequests does not affect receive requests in map +func TestStore_DeleteSentRequests_ReceiveInMap(t *testing.T) { + s, _, _ := makeTestStore(t) + 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) + } + + err := s.DeleteSentRequests() + if err != nil { + t.Fatalf("DeleteSentRequests returned an error: %+v", err) + } + + if s.requests[*c.ID] == nil { + t.Fatalf("DeleteSentRequests removes receive requests!") + } + +} + +// Tests that DeleteReceiveRequests does not affect sent requests in map +func TestStore_DeleteReceiveRequests_SentInMap(t *testing.T) { + s, _, _ := makeTestStore(t) + 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) + } + + err := s.DeleteReceiveRequests() + if err != nil { + t.Fatalf("DeleteSentRequests returned an error: %+v", err) + } + + if s.requests[*partnerID] == nil { + t.Fatalf("DeleteReceiveRequests removes sent requests!") + } + +} + +// Unit test. +func TestStore_DeleteAllRequests(t *testing.T) { + s, _, _ := makeTestStore(t) + 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) + } + + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + _, sidhPubKey = genSidhAKeys(rng) + if err := s.AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + err := s.DeleteAllRequests() + if err != nil { + t.Fatalf("DeleteAllRequests returned an error: %+v", err) + } + + if s.requests[*sr.partner] != nil { + t.Errorf("delete() failed to delete request for user %s.", + sr.partner) + } + + if _, exists := s.fingerprints[sr.fingerprint]; exists { + t.Errorf("delete() failed to delete fingerprint for fp %v.", + sr.fingerprint) + } + + if s.requests[*c.ID] != nil { + t.Errorf("delete() failed to delete request for user %s.", c.ID) + } + +} + 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) diff --git a/ud/manager.go b/ud/manager.go index 79e439e60b2aaf7e01a6b53b20138984f9b315b8..fca70a4b641c6bfd1716588d2810e221c60183f3 100644 --- a/ud/manager.go +++ b/ud/manager.go @@ -169,6 +169,7 @@ func (m *Manager) getHost() (*connect.Host, error) { params := connect.GetDefaultHostParams() params.AuthEnabled = false + params.SendTimeout = 20 * time.Second // Add a new host and return it if it does not already exist host, err = m.comms.AddHost(udID, netDef.UDB.Address,