From 5571a212c7d9186c3032243ffcd6a88924ba9a22 Mon Sep 17 00:00:00 2001
From: "Richard T. Carback III" <rick.carback@gmail.com>
Date: Wed, 30 Sep 2020 23:14:10 +0000
Subject: [PATCH] Initial SendCMIX hookup

---
 api/client.go |  14 ++++++
 api/send.go   |  11 ++++
 cmd/root.go   | 136 ++++++++++++++++++++++----------------------------
 3 files changed, 86 insertions(+), 75 deletions(-)

diff --git a/api/client.go b/api/client.go
index 0c4533d8f..da77598e3 100644
--- a/api/client.go
+++ b/api/client.go
@@ -10,6 +10,7 @@ import (
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces"
+	"gitlab.com/elixxir/client/interfaces/message"
 	"gitlab.com/elixxir/client/interfaces/params"
 	"gitlab.com/elixxir/client/interfaces/user"
 	"gitlab.com/elixxir/client/keyExchange"
@@ -332,6 +333,13 @@ func (c *Client) GetUser() user.User {
 	return c.storage.GetUser()
 }
 
+// RegisterListenerCallback records and installs a listener callback for
+// messages matching specific uid, msgType, and/or username
+func (c *Client) RegisterListenerCallback(uid []byte, msgType int,
+	username string, listenerCb func(msg message.Receive)) {
+
+}
+
 // ----- Utility Functions -----
 // parseNDF parses the initial ndf string for the client. do not check the
 // signature, it is deprecated.
@@ -348,6 +356,12 @@ func parseNDF(ndfString string) (*ndf.NetworkDefinition, error) {
 	return ndf, nil
 }
 
+func (c *Client) getCMIXPrimeSize() int {
+	ndf := c.network.GetInstance().GetPartialNdf().Get()
+	cmixGrp, _ := decodeGroups(ndf)
+	return len(cmixGrp.GetPBytes())
+}
+
 // decodeGroups returns the e2e and cmix groups from the ndf
 func decodeGroups(ndf *ndf.NetworkDefinition) (cmixGrp, e2eGrp *cyclic.Group) {
 	largeIntBits := 16
diff --git a/api/send.go b/api/send.go
index 758f0f553..670525548 100644
--- a/api/send.go
+++ b/api/send.go
@@ -40,3 +40,14 @@ func (c *Client) SendCMIX(msg format.Message, param params.CMIX) (id.Round,
 	jww.INFO.Printf("SendCMIX(%v)", msg)
 	return c.network.SendCMIX(msg, param)
 }
+
+// NewCMIXMessage Creates a new cMix message with the right properties
+// for the current cMix network.
+// FIXME: this is weird and shouldn't be necessary, but it is.
+func (c *Client) NewCMIXMessage(recipient *id.ID,
+	contents []byte) format.Message {
+	msg := format.NewMessage(c.getCMIXPrimeSize())
+	msg.SetContents(contents)
+	msg.SetRecipientID(recipient)
+	return msg
+}
diff --git a/cmd/root.go b/cmd/root.go
index 596772c09..eb39a4933 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -9,15 +9,18 @@ package cmd
 
 import (
 	"encoding/binary"
+	"encoding/hex"
 	"fmt"
 	"github.com/spf13/cobra"
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
 	"gitlab.com/elixxir/client/api"
+	"gitlab.com/elixxir/client/interfaces/params"
 	"gitlab.com/xx_network/primitives/id"
 	"io/ioutil"
 	"os"
 	"strconv"
+	"time"
 )
 
 var verbose bool
@@ -94,61 +97,6 @@ func Execute() {
 // 	params.MinNumKeys = uint16(minNumKeys)
 // }
 
-// type FallbackListener struct {
-// 	MessagesReceived int64
-// }
-
-// func (l *FallbackListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...interface{}) {
-// 	if !isHeardElsewhere {
-// 		message := item.(*parse.Message)
-// 		sender, ok := userRegistry.Users.GetUser(message.Sender)
-// 		var senderNick string
-// 		if !ok {
-// 			jww.ERROR.Printf("Couldn't get sender %v", message.Sender)
-// 		} else {
-// 			senderNick = sender.Username
-// 		}
-// 		atomic.AddInt64(&l.MessagesReceived, 1)
-// 		jww.INFO.Printf("Message of type %v from %q, %v received with fallback: %s\n",
-// 			message.MessageType, printIDNice(message.Sender), senderNick,
-// 			string(message.Body))
-// 	}
-// }
-
-// type TextListener struct {
-// 	MessagesReceived int64
-// }
-
-// func (l *TextListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...interface{}) {
-// 	message := item.(*parse.Message)
-// 	jww.INFO.Println("Hearing a text message")
-// 	result := cmixproto.TextMessage{}
-// 	err := proto.Unmarshal(message.Body, &result)
-// 	if err != nil {
-// 		jww.ERROR.Printf("Error unmarshaling text message: %v\n",
-// 			err.Error())
-// 	}
-
-// 	sender, ok := userRegistry.Users.GetUser(message.Sender)
-// 	var senderNick string
-// 	if !ok {
-// 		jww.INFO.Printf("First message from sender %v", printIDNice(message.Sender))
-// 		u := userRegistry.Users.NewUser(message.Sender, base64.StdEncoding.EncodeToString(message.Sender[:]))
-// 		userRegistry.Users.UpsertUser(u)
-// 		senderNick = u.Username
-// 	} else {
-// 		senderNick = sender.Username
-// 	}
-// 	logMsg := fmt.Sprintf("Message from %v, %v Received: %s\n",
-// 		printIDNice(message.Sender),
-// 		senderNick, result.Message)
-// 	jww.INFO.Printf("%s -- Timestamp: %s\n", logMsg,
-// 		message.Timestamp.String())
-// 	fmt.Printf(logMsg)
-
-// 	atomic.AddInt64(&l.MessagesReceived, 1)
-// }
-
 // type userSearcher struct {
 // 	foundUserChan chan []byte
 // }
@@ -209,7 +157,41 @@ var rootCmd = &cobra.Command{
 			jww.FATAL.Panicf("%+v", err)
 		}
 
-		select {}
+		// Send Messages
+		msgBody := viper.GetString("message")
+		recipientIDBytes, err := hex.DecodeString(
+			viper.GetString("destid"))
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+		recipientID, err := id.Unmarshal(recipientIDBytes)
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+
+		msg := client.NewCMIXMessage(recipientID, []byte(msgBody))
+		params := params.GetDefaultCMIX()
+
+		sendCnt := int(viper.GetUint("sendCount"))
+		sendDelay := time.Duration(viper.GetUint("sendDelay"))
+		for i := 0; i < sendCnt; i++ {
+			client.SendCMIX(msg, params)
+			time.Sleep(sendDelay * time.Millisecond)
+		}
+
+		// Wait until message timeout or we receive enough then exit
+		// TODO: Actually check for how many messages we've received
+		receiveCnt := viper.GetUint("receiveCount")
+		waitTimeout := time.Duration(viper.GetUint("waitTimeout"))
+		timeoutTick := time.NewTicker(waitTimeout * time.Second)
+		for {
+			select {
+			case <-timeoutTick.C:
+				fmt.Println("Timed out!")
+				break
+			}
+		}
+		fmt.Printf("Received %d", receiveCnt)
 	},
 }
 
@@ -286,16 +268,34 @@ func init() {
 		"Path to the log output path (- is stdout)")
 	viper.BindPFlag("log", rootCmd.Flags().Lookup("log"))
 
-	rootCmd.Flags().StringP("regcode", "r", "",
+	rootCmd.Flags().StringP("regcode", "", "",
 		"Registration code (optional)")
 	viper.BindPFlag("regcode", rootCmd.Flags().Lookup("regcode"))
 
+	rootCmd.Flags().StringP("message", "m", "", "Message to send")
+	viper.BindPFlag("message", rootCmd.Flags().Lookup("message"))
+
+	rootCmd.Flags().StringP("destid", "d", "0",
+		"ID to send message to (hexadecimal string up to 256 bits)")
+	viper.BindPFlag("destid", rootCmd.Flags().Lookup("destid"))
+
+	rootCmd.Flags().UintP("sendCount",
+		"", 1, "The number of times to send the message")
+	viper.BindPFlag("sendCount", rootCmd.Flags().Lookup("sendCount"))
+	rootCmd.Flags().UintP("sendDelay",
+		"", 500, "The delay between sending the messages in ms")
+	viper.BindPFlag("sendCount", rootCmd.Flags().Lookup("sendCount"))
+
+	rootCmd.Flags().UintP("receiveCount",
+		"", 1, "How many messages we should wait for before quitting")
+	viper.BindPFlag("sendCount", rootCmd.Flags().Lookup("sendCount"))
+	rootCmd.Flags().UintP("waitTimeout", "", 15,
+		"The number of seconds to wait for messages to arrive")
+	viper.BindPFlag("waitTimeout",
+		rootCmd.Flags().Lookup("waitTimeout"))
+
 	// Cobra also supports local flags, which will only run
 	// when this action is called directly.
-	rootCmd.Flags().StringVarP(&message, "message", "m", "", "Message to send")
-	rootCmd.PersistentFlags().Uint64VarP(&destinationUserId, "destid", "d", 0,
-		"ID to send message to")
-
 	rootCmd.Flags().StringVarP(&notificationToken, "nbRegistration", "x", "",
 		"Token to register user with notification bot")
 
@@ -306,13 +306,6 @@ func init() {
 		make([]string, 0), "Define key generation parameters. Pass values in comma separated list"+
 			" in the following order: MinKeys,MaxKeys,NumRekeys,TTLScalar,MinNumKeys")
 
-	rootCmd.Flags().BoolVarP(&noTLS, "noTLS", "", false,
-		"Set to ignore tls. Connections will fail if the network requires tls. For debugging")
-
-	rootCmd.Flags().StringVar(&privateKeyPath, "privateKey", "",
-		"The path for a PEM encoded private key which will be used "+
-			"to create the user")
-
 	rootCmd.Flags().StringVar(&destinationUserIDBase64, "dest64", "",
 		"Sets the destination user id encoded in base 64")
 
@@ -322,13 +315,6 @@ func init() {
 
 	// rootCmd.Flags().StringVarP(&searchForUser, "SearchForUser", "s", "",
 	// 	"Sets the email to search for to find a user with user discovery")
-
-	rootCmd.Flags().UintVarP(&messageTimeout, "messageTimeout",
-		"t", 45, "The number of seconds to wait for "+
-			"'waitForMessages' messages to arrive")
-
-	rootCmd.Flags().UintVarP(&messageCnt, "messageCount",
-		"c", 1, "The number of times to send the message")
 }
 
 // initConfig reads in config file and ENV variables if set.
-- 
GitLab