diff --git a/cmd/root.go b/cmd/root.go index 8bdd2944b77cb12258a4d73804b8ec22d3377c6a..2cb00cb226da464c30039c07cd125e79f37b90b9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -258,13 +258,22 @@ var rootCmd = &cobra.Command{ // Send Messages msgBody := viper.GetString("message") - time.Sleep(10 * time.Second) // Accept auth request for this recipient authConfirmed := false + paramsE2E := e2e.GetDefaultParams() + roundTimeout := paramsE2E.CMIXParams.SendTimeout if viper.GetBool("accept-channel") { - acceptChannel(client, recipientID) + // Verify that the confirmation message makes it to the + // original sender + if viper.GetBool("verify-sends") { + acceptChannelVerified(client, recipientID, roundTimeout) + } else { + // Accept channel, agnostic of round result + acceptChannel(client, recipientID) + } + // Do not wait for channel confirmations if we // accepted one authConfirmed = true @@ -278,7 +287,6 @@ var rootCmd = &cobra.Command{ // Send unsafe messages or not? unsafe := viper.GetBool("unsafe") - sendAuthReq := viper.GetBool("send-auth-request") if !unsafe && !authConfirmed && !isPrecanPartner && sendAuthReq { @@ -365,7 +373,6 @@ var rootCmd = &cobra.Command{ payload := []byte(msgBody) recipient := recipientID - paramsE2E := e2e.GetDefaultParams() if viper.GetBool("splitSends") { paramsE2E.ExcludedRounds = excludedRounds.NewSet() } @@ -402,8 +409,10 @@ var rootCmd = &cobra.Command{ // Construct the callback function which // verifies successful message send or retries - f := func(allRoundsSucceeded, timedOut bool, rounds map[id.Round]cmix.RoundResult) { - printRoundResults(rounds, roundIDs, payload, recipientID) + f := func(allRoundsSucceeded, timedOut bool, + rounds map[id.Round]cmix.RoundResult) { + printRoundResults( + rounds, roundIDs, payload, recipientID) if !allRoundsSucceeded { retryChan <- struct{}{} } else { @@ -707,17 +716,19 @@ func initClient() *messenger.Client { return client } -func acceptChannel(client *messenger.Client, recipientID *id.ID) { +func acceptChannel(client *messenger.Client, recipientID *id.ID) id.Round { recipientContact, err := client.GetAuth().GetReceivedRequest( recipientID) if err != nil { jww.FATAL.Panicf("%+v", err) } - _, err = client.GetAuth().Confirm( + rid, err := client.GetAuth().Confirm( recipientContact) if err != nil { jww.FATAL.Panicf("%+v", err) } + + return rid } func deleteChannel(client *messenger.Client, partnerId *id.ID) { @@ -753,11 +764,20 @@ func addAuthenticatedChannel(client *messenger.Client, recipientID *id.ID, me := client.GetUser().GetContact() jww.INFO.Printf("Requesting auth channel from: %s", recipientID) - _, err := client.GetAuth().Request(recipientContact, - me.Facts) - if err != nil { - jww.FATAL.Panicf("%+v", err) + + // Verify that the auth request makes it to the recipient + // by monitoring the round result + if viper.GetBool("verify-sends") { + requestChannelVerified(client, recipientContact, me) + } else { + // Just call Request, agnostic of round result + _, err := client.GetAuth().Request(recipientContact, + me.Facts) + if err != nil { + jww.FATAL.Panicf("%+v", err) + } } + } else { jww.ERROR.Printf("Could not add auth channel for %s", recipientID) @@ -789,9 +809,15 @@ func resetAuthenticatedChannel(client *messenger.Client, recipientID *id.ID, if recipientContact.ID != nil && recipientContact.DhPubKey != nil { jww.INFO.Printf("Requesting auth channel from: %s", recipientID) - _, err := client.GetAuth().Reset(recipientContact) - if err != nil { - jww.FATAL.Panicf("%+v", err) + // Verify that the auth request makes it to the recipient + // by monitoring the round result + if viper.GetBool("verify-sends") { + resetChannelVerified(client, recipientContact) + } else { + _, err := client.GetAuth().Reset(recipientContact) + if err != nil { + jww.FATAL.Panicf("%+v", err) + } } } else { jww.ERROR.Printf("Could not reset auth channel for %s", @@ -799,6 +825,121 @@ func resetAuthenticatedChannel(client *messenger.Client, recipientID *id.ID, } } +func acceptChannelVerified(client *messenger.Client, recipientID *id.ID, + roundTimeout time.Duration) { + done := make(chan struct{}, 1) + retryChan := make(chan struct{}, 1) + for { + rid := acceptChannel(client, recipientID) + + // Monitor rounds for results + err := client.GetCmix().GetRoundResults(roundTimeout, + makeVerifySendsCallback(retryChan, done), rid) + if err != nil { + jww.DEBUG.Printf("Could not verify "+ + "confirmation message for relationship with %s were sent "+ + "successfully, resending messages...", recipientID) + continue + } + + select { + case <-retryChan: + // On a retry, go to the top of the loop + jww.DEBUG.Printf("Confirmation message for relationship"+ + " with %s were not sent successfully, resending "+ + "messages...", recipientID) + continue + case <-done: + // Close channels on verification success + close(done) + close(retryChan) + break + } + break + } +} + +func requestChannelVerified(client *messenger.Client, + recipientContact, me contact.Contact) { + paramsE2E := e2e.GetDefaultParams() + roundTimeout := paramsE2E.CMIXParams.SendTimeout + + retryChan := make(chan struct{}, 1) + done := make(chan struct{}, 1) + for { + rid, err := client.GetAuth().Request(recipientContact, + me.Facts) + if err != nil { + continue + } + + // Monitor rounds for results + err = client.GetCmix().GetRoundResults(roundTimeout, + makeVerifySendsCallback(retryChan, done), + rid) + if err != nil { + jww.DEBUG.Printf("Could not verify auth request was sent " + + "successfully, resending...") + continue + } + + select { + case <-retryChan: + // On a retry, go to the top of the loop + jww.DEBUG.Printf("Auth request was not sent " + + "successfully, resending...") + continue + case <-done: + // Close channels on verification success + close(done) + close(retryChan) + break + } + break + } +} + +func resetChannelVerified(client *messenger.Client, recipientContact contact.Contact) { + paramsE2E := e2e.GetDefaultParams() + roundTimeout := paramsE2E.CMIXParams.SendTimeout + + retryChan := make(chan struct{}, 1) + done := make(chan struct{}, 1) + for { + + rid, err := client.GetAuth().Reset(recipientContact) + if err != nil { + jww.FATAL.Panicf("%+v", err) + } + + // Monitor rounds for results + err = client.GetCmix().GetRoundResults(roundTimeout, + makeVerifySendsCallback(retryChan, done), + rid) + if err != nil { + jww.DEBUG.Printf("Could not verify auth request was sent " + + "successfully, resending...") + continue + } + + select { + case <-retryChan: + // On a retry, go to the top of the loop + jww.DEBUG.Printf("Auth request was not sent " + + "successfully, resending...") + continue + case <-done: + // Close channels on verification success + close(done) + close(retryChan) + break + } + break + + } + +} + func waitUntilConnected(connected chan bool) { waitTimeout := time.Duration(viper.GetUint("waitTimeout")) timeoutTimer := time.NewTimer(waitTimeout * time.Second) diff --git a/cmd/utils.go b/cmd/utils.go index 918ef1dc4e07945f7386a021f4cb2eb24132b8c2..aca4fc016ca31eae6efee3087791510ddef0ea42 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -103,5 +103,16 @@ func readContact() contact.Contact { } jww.INFO.Printf("CONTACTPUBKEY READ: %s", c.DhPubKey.TextVerbose(16, 0)) + jww.INFO.Printf("Contact ID: %s", c.ID) return c } + +func makeVerifySendsCallback(retryChan, done chan struct{}) cmix.RoundEventCallback { + return func(allRoundsSucceeded, timedOut bool, rounds map[id.Round]cmix.RoundResult) { + if !allRoundsSucceeded { + retryChan <- struct{}{} + } else { + done <- struct{}{} + } + } +}