diff --git a/.gitignore b/.gitignore index 096a17885ce7c9822bf30441176d77f80c58222c..f19699de7d08271dcf680e406be00a8ca75699bf 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,6 @@ session*/ *.win64 *.xxchan *.pem -*.log \ No newline at end of file +*.log +statePath/ +*.xxc \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..8551215fa5b651bb93a247cde5ab89d4b8f5f7e6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2022, xx network SEZC + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/connectClient/README.md b/connectClient/README.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1ed0a3fdd16550f696c03c0432596e76cab95b1d 100644 --- a/connectClient/README.md +++ b/connectClient/README.md @@ -0,0 +1,89 @@ +# xxdk Connect Client Example + +This mini-repository contains the example logic for running a basic connection +client. This is provided by the xx network team as a springboard to +help consumers better understand our API and how it may be used. + +`main.go` contains the crux of the logic. We avoid complicating our example by +avoiding the usage of CLI flags for basic variables you may change in the code. +This file initiates an xxdk E2E client. With that established, a connection +client is built on top. Using a precanned contact object created in +`connectServer` this connection client contacts the server with a simple +message. + +`utils.go` contains utility functions for running the program. In this case, +we provide a tool initializing a log. + +`listener.go` contains logic for handling the reception of a message via the +established connection. In this example, it is very basic. We invite consumers +to use this as a basis to implement more complex message listeners. + +## Build Instructions + +In these instructions we will go over building a connection client using our +example. In order to build a client which successfully sends a message through +the connection, we must first go over how to build and run a connection server. + +### Building a Server + +In order to run a server, the following commands may be run: + +```bash +cd connectServer/ +go build -o server . +./server +``` + +This will initialize the server. You may verify its functionality by checking +the `server.log`file. It is a long-running process which may be +stopped by a user inputted kill signal. This will create a file +`connectServer.xxc`, which is the contat file for the server. A connection +client may parse this file in order to send a request to this server. + +### Building a Client + +Please follow the steps above before continuing to these instructions. +In order to run the client, you must first move the aforementioned +`connectServer.xxc` file to the path where you will run the client. + +```bash +cd connectServer/ +cp connectServer.xxc /path/to/connectClient +``` + +Once the contact object is local to the client, you may build and run +the client: + +```bash +cd connectClient/ +go build -o client . +./client +``` + +This is a long-running process which may be stopped by a user inputted kill +signal. We recommend allowing the process to run for a long enough time to +complete its requests to the server and receive the server's responses. We go +into detail on what this entails below. + +Once the connection client has set up and established its connection with the +server, you can verify by checking the server's log for the string +`Message received`. + +```bash +grep "Message received" server.log +INFO 2022/07/07 12:59:12.088046 Message received: {XxMessage WjdMwCH+... [73 102 32 116 104 105 115 32 109 101 115 115 97 103 101 32 105 115 32 115 101 110 116 32 115 117 99 99 101 115 115 102 117 108 108 121 44 32 119 101 39 108 108 32 104 97 118 101 32 101 115 116 97 98 108 105 115 104 101 100 32 99 111 110 116 97 99 116 32 119 105 116 104 32 116 104 101 32 115 101 114 118 101 114 46] kuycotVTjefJ4nZWJ+Ksg9/jviANn6suteW6HPmXroID l74No/qjr/8Q74mA9VadudforXet8OykqSvPIEFAeUQD [0 0 0 0 0 2 245 150] 2022-07-07 12:59:07.078570118 -0700 PDT true {58339144 QUEUED 0xc001e12780 map[PENDING:1969-12-31 16:00:01.65722394 -0800 PST PRECOMPUTING:2022-07-07 12:59:00.644730058 -0700 PDT STANDBY:2022-07-07 12:59:07.062879269 -0700 PDT QUEUED:2022-07-07 12:59:10.062881354 -0700 PDT] [] 1000 18 187058678 ID:58339144 UpdateID:187058678 State:3 BatchSize:1000 Topology:"3\xdd\xc9;\xce\xc5\xf0\xff&\x8c\xf1\x7f\nf\xa8K\x17\xb6\xd1\x0b|a\t[\x14\x8e\xde\xd1qϊB\x02" Topology:"\xf5\\\x94MB\x19ڣq݃\xbee\x99\xbfF\xb5\xa9\xf3k\x0e8 gl\xf5:d\x11\xab\x89\x17\x02" Topology:"\x01\xc1\xf6Gi\x972p\xa9\x96\xb4\x12\x0f1\x1c\xebw\xef\xca\xed\"F\xa7w\xe2\n\xbb8\xcbd\x05=\x02" Topology:"\xd5\xc3\xd00\xa3a;RqDs\xf0\xda<\xa3)$y\xef\xc1\xa0\x12_k?\x00\rIebL\xfe\x02" Topology:"vQ\xcd\t\xaf\x91ڤ\x86\x8ecl\x84\xb1\x95\x1e\x8f+ږQ\\ﷀ]7\x89\x08\x02" Timestamps:1657223940 Timestamps:1657223940644730058 Timestamps:1657223947062879269 Timestamps:1657223950062881354 Timestamps:0 Timestamps:0 Timestamps:0 ResourceQueueTimeoutMillis:3906340864 AddressSpaceSize:18 EccSignature:{Nonce:"\xb2y\xccf\x86E\xe0NR\xd2J3|\xb8d\xfe\xb3\xa8\xad\xa2\x92\xe0\xe4\x0bZ\x07\xbeٓ\xb4z\xf2" Signature:"\xe1\xc9 \x92_\xfe\x9d\x7f\x18\xb920C \xa6\xd1\xe9U\xbb\x93o\x9b\x1bp<Y\xb1\x9f\xb7O\x012^^\x9doa\x06P\x83\xfes\xbf\xe1\xaeL\xb0+\\\xdc\x12r4)\xdas49\xf6=\xd2\x13\xa0\x07"}}} +``` + +By default, the client sends a single message to the server, with the server +registering a `receive.Listener` which listens to messages of type +`catalog.NoType` from the client. + +Verification that the server is able to send messages back to the client may +also be done. This can be done by checking the client's log for the string +`Message received`. + +```bash +grep "Message received" client.log +INFO 2022/07/07 13:53:34.242752 Message received: {NoType S5yr+Zo1... [73 102 32 116 104 105 115 32 109 101 115 115 97 103 101 32 105 115 32 115 101 110 116 32 115 117 99 99 101 115 115 102 117 108 108 121 44 32 119 101 39 108 108 32 104 97 118 101 32 101 115 116 97 98 108 105 115 104 101 100 32 99 111 110 116 97 99 116 32 119 105 116 104 32 116 104 101 32 99 108 105 101 110 116 46] aT1Z9OLBN+WLMSk9UjN/2Jxe6xtmU9RadnqXU9mf8kgD q9G7VmURis3jNwj8FHxgpS3sukSdL/8+MlLaqyGbJwYD [0 0 0 0 0 1 159 92] 2022-07-07 13:53:31.078117153 -0700 PDT true {58349730 QUEUED 0xc000bc04c0 map[PENDING:1969-12-31 16:00:01.657227203 -0800 PST PRECOMPUTING:2022-07-07 13:53:23.246307109 -0700 PDT STANDBY:2022-07-07 13:53:29.216187342 -0700 PDT QUEUED:2022-07-07 13:53:32.216189662 -0700 PDT] [] 1000 18 187093087 ID:58349730 UpdateID:187093087 State:3 BatchSize:1000 Topology:"\x9bTM6\xeeh,\x7fT\xf1\xe9\x1f\xd4\x07X\x98T\xdb\x7fy\xd7\x0e\x84p\x04:\xe2m\x95E9J\x02" Topology:"\xdd4\x04ʧ\xdd\xdd<\x86\x85\x00{\x03\xdb\xd0rC\xcc\xe5<\xc6>\xf1~\x17\xe2\xcc\xcd`\xfcBm\x02" Topology:"\xdax\xa2\xe1f\x033!S\x9a1cX\xadKƗ\x90\x97c\xcc$|j\x9e\xc1Z\x9b\xc6@5%\x02" Topology:"\xd53\xc0\x1a\x9fm\x90-~D%kE\x1e+\xfc0d:R\xe20\xaa2\xa5\\N\x19\xb0e\xc5_\x02" Topology:"\xb59M\xf4w\xc6\x07\xec\xd61A\x02\xf9\xf3\x14\x9d\xf5\xd8F0\x84a\xea:\xcc\x10\x95&8du\x14\x02" Timestamps:1657227203 Timestamps:1657227203246307109 Timestamps:1657227209216187342 Timestamps:1657227212216189662 Timestamps:0 Timestamps:0 Timestamps:0 ResourceQueueTimeoutMillis:3906340864 AddressSpaceSize:18 EccSignature:{Nonce:"\xc3Ͼ\xc8T\xe1\xadE\x81\x94r\t\x14,\xfa\u008a\xfc8\x93h\xac\xf4e\xe4Y \xeb\xa5v\xa9\x82" Signature:"E$\xffjŲ\xfa/\xe6U@\x1d\xedp\xc4\xd7ջ\x88\xe1\xea?7\x8f\x837\xc2 ?\x99\xe5\xcc\xd7\xcd}\xadw`fPĸ\x06\x1bm\x84,\x02f\xce\xe4\x08\x96\x84K\x0c\x88wy\xfds\n"}}} +``` + diff --git a/connectClient/main.go b/connectClient/main.go index 0b1e74f16bbdb6125574f929574e394860a3ddbf..079f071f1997cf558274b660706dc20c02d17d7d 100644 --- a/connectClient/main.go +++ b/connectClient/main.go @@ -21,7 +21,7 @@ func main() { // Logging initLog(1, "client.log") - // Create a new client object------------------------------------------------------- + // Create a new client object---------------------------------------------- // Path to the server contact file serverContactPath := "connectServer.xxc" @@ -29,9 +29,9 @@ func main() { // You would ideally use a configuration tool to acquire these parameters statePath := "statePath" statePass := "password" - // The following connects to mainnet. For historical reasons it is called a json file - // but it is actually a marshalled file with a cryptographic signature attached. - // This may change in the future. + // The following connects to mainnet. For historical reasons it is called a + // json file but it is actually a marshalled file with a cryptographic + // signature attached. This may change in the future. ndfURL := "https://elixxir-bins.s3.us-west-1.amazonaws.com/ndf/mainnet.json" certificatePath := "../mainnet.crt" ndfPath := "ndf.json" @@ -53,7 +53,8 @@ func main() { jww.FATAL.Panicf("Failed to read certificate: %v", err) } - ndfJSON, err = xxdk.DownloadAndVerifySignedNdfWithUrl(ndfURL, string(cert)) + ndfJSON, err = xxdk.DownloadAndVerifySignedNdfWithUrl( + ndfURL, string(cert)) if err != nil { jww.FATAL.Panicf("Failed to download NDF: %+v", err) } @@ -66,10 +67,11 @@ func main() { } } - // Login to your client session----------------------------------------------------- + // Login to your client session-------------------------------------------- // Login with the same sessionPath and sessionPass used to call NewClient() - baseClient, err := xxdk.LoadCmix(statePath, []byte(statePass), xxdk.GetDefaultCMixParams()) + baseClient, err := xxdk.LoadCmix(statePath, []byte(statePass), + xxdk.GetDefaultCMixParams()) if err != nil { jww.FATAL.Panicf("Failed to load state: %+v", err) } @@ -83,22 +85,25 @@ func main() { if err != nil { jww.FATAL.Panicf("Failed to generate reception identity: %+v", err) } - err = xxdk.StoreReceptionIdentity(identityStorageKey, identity, baseClient) + err = xxdk.StoreReceptionIdentity( + identityStorageKey, identity, baseClient) if err != nil { jww.FATAL.Panicf("Failed to store new reception identity: %+v", err) } } // Create an E2E client - // The connect packages handles AuthCallbacks, xxdk.DefaultAuthCallbacks is fine here + // The `connect` packages handles AuthCallbacks, + // `xxdk.DefaultAuthCallbacks` is fine here params := xxdk.GetDefaultE2EParams() jww.INFO.Printf("Using E2E parameters: %+v", params) - e2eClient, err := xxdk.Login(baseClient, xxdk.DefaultAuthCallbacks{}, identity, params) + e2eClient, err := xxdk.Login(baseClient, xxdk.DefaultAuthCallbacks{}, + identity, params) if err != nil { jww.FATAL.Panicf("Unable to Login: %+v", err) } - // Start network threads------------------------------------------------------------ + // Start network threads---------------------------------------------------- // Set networkFollowerTimeout to a value of your choice (seconds) networkFollowerTimeout := 5 * time.Second @@ -126,7 +131,8 @@ func main() { // Create a tracker channel to be notified of network changes connected := make(chan bool, 10) - // Provide a callback that will be signalled when network health status changes + // Provide a callback that will be signalled when network + // health status changes e2eClient.GetCmix().AddHealthCallback( func(isConnected bool) { connected <- isConnected @@ -134,7 +140,7 @@ func main() { // Wait until connected or crash on timeout waitUntilConnected(connected) - // Connect with the server-------------------------------------------------- + // Connect with the server------------------------------------------------- // Recipient's contact (read from a Client CLI-generated contact file) contactData, err := ioutil.ReadFile(serverContactPath) @@ -143,8 +149,9 @@ func main() { } // Imported "gitlab.com/elixxir/crypto/contact" - // which provides an `Unmarshal` function to convert the byte slice ([]byte) output - // of `ioutil.ReadFile()` to the `Contact` type expected by `RequestAuthenticatedChannel()` + // which provides an `Unmarshal` function to convert the + // byte slice ([]byte) output of `ioutil.ReadFile()` to the `Contact` type + // expected by `RequestAuthenticatedChannel()` recipientContact, err := contact.Unmarshal(contactData) if err != nil { jww.FATAL.Panicf("Failed to get contact data: %+v", err) @@ -156,9 +163,10 @@ func main() { if err != nil { jww.FATAL.Panicf("Failed to create connection object: %+v", err) } - jww.INFO.Printf("Connect with %s successfully established!", recipientContact.ID) + jww.INFO.Printf("Connect with %s successfully established!", + recipientContact.ID) - // Register a listener for messages-------------------------------------------------- + // Register a listener for messages---------------------------------------- // Listen for all types of messages using catalog.NoType // User-defined behavior for message reception goes in the listener @@ -169,17 +177,20 @@ func main() { jww.FATAL.Panicf("Could not register message listener: %+v", err) } - // Send a message to the server---------------------------------------------------- + // Send a message to the server-------------------------------------------- // Test message - msgBody := "If this message is sent successfully, we'll have established contact with the server." - roundIDs, messageID, timeSent, err := handler.SendE2E(catalog.XxMessage, []byte(msgBody), params.Base) + msgBody := "If this message is sent successfully, we'll have established " + + "contact with the server." + roundIDs, messageID, timeSent, err := handler.SendE2E( + catalog.XxMessage, []byte(msgBody), params.Base) if err != nil { jww.FATAL.Panicf("Failed to send message: %+v", err) } - jww.INFO.Printf("Message %v sent in RoundIDs: %+v at %v", messageID, roundIDs, timeSent) + jww.INFO.Printf("Message %v sent in RoundIDs: %+v at %v", + messageID, roundIDs, timeSent) - // Keep app running to receive messages----------------------------------------------- + // Keep app running to receive messages------------------------------------ // Wait until the user terminates the program c := make(chan os.Signal) diff --git a/connectServer/README.md b/connectServer/README.md index 1f35dc23982bf593935def6b5f60484f1578b9ef..f7e8213b49275d37e4d349db01e3af137f1514d7 100644 --- a/connectServer/README.md +++ b/connectServer/README.md @@ -1,2 +1,40 @@ # xxdk Connection Server Example +This mini-respository contains the example logic for running a basic connection +server. This is provided by the xx network team as a springboard +to help consumers better understand our API and how it may be used. + +`main.go` contains the crux of the logic. We avoid complicating our example by +avoiding the usage of CLI flags for basic variables you may change in the code. +This file initiates an xxdk E2E client. With that client established, a +connection server is built on top. This program creates contact file +`connectServer.xxc` which may be used by a client to contact the server. + +`utils.go` contains utility functions for running the program. In this case, +we provide a tool initializing a log. It also contains a utility to write the +contact file to disk. + +`listener.go` contains logic for handling the reception of a message via the +established connection. In this example, it is very basic. We invite consumers +to use this as a basis to implement more complex message listeners. + +## Build Instructions + +In these instructions we will go over building a connection server using our +example. This will not include instructions on running a client which +establishes a connection. That documentation may be found in the `README.md` for +`connectClient`. + +In order to run a server, the following commands may be run: + +```bash +cd connectServer/ +go build -o server . +./server +``` + +This will initialize the server. You may verify its functionality by checking +the `server.log` file. It is a long-running process which may be stopped by a +user inputted kill signal. This will create a file `connectServer.xxc`, which is +the contact file for the server. A connection client may parse this file in +order to send a request to this server. \ No newline at end of file diff --git a/connectServer/go.mod b/connectServer/go.mod index 933387a98afe809a4522371bef60c53f2f8282b4..69d952e3ad8ff3d77f1409e7b885e58de176076e 100644 --- a/connectServer/go.mod +++ b/connectServer/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/spf13/jwalterweatherman v1.1.0 - gitlab.com/elixxir/client v1.5.1-0.20220705214402-3ebd3c1fcfec + gitlab.com/elixxir/client v1.5.1-0.20220707193810-ed09e1277c6d gitlab.com/elixxir/crypto v0.0.7-0.20220606201132-c370d5039cea gitlab.com/xx_network/primitives v0.0.4-0.20220630163313-7890038258c6 ) diff --git a/connectServer/go.sum b/connectServer/go.sum index 22feb53a3cbad61a10be95c209f6aaf01a4d75f1..f2bf1a8584a124ce686b86ab17f7bf30c13f1cb5 100644 --- a/connectServer/go.sum +++ b/connectServer/go.sum @@ -253,8 +253,8 @@ github.com/zeebo/pcg v0.0.0-20181207190024-3cdc6b625a05/go.mod h1:Gr+78ptB0MwXxm github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f h1:yXGvNBqzZwAhDYlSnxPRbgor6JWoOt1Z7s3z1O9JR40= gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= -gitlab.com/elixxir/client v1.5.1-0.20220705214402-3ebd3c1fcfec h1:TpO05QgOP3sa0V3KGhOWy6k80YYCvpH/I6/hgwVcpxY= -gitlab.com/elixxir/client v1.5.1-0.20220705214402-3ebd3c1fcfec/go.mod h1:GCevkp2aa+vpIsSrlF/HsRlTjf107jgCglZ/0JKqW7E= +gitlab.com/elixxir/client v1.5.1-0.20220707193810-ed09e1277c6d h1:MxseLDnuqGkrnlDDzlH7AYlfm6qPDfy2wf2Qlt4AZbQ= +gitlab.com/elixxir/client v1.5.1-0.20220707193810-ed09e1277c6d/go.mod h1:GCevkp2aa+vpIsSrlF/HsRlTjf107jgCglZ/0JKqW7E= gitlab.com/elixxir/comms v0.0.4-0.20220603231314-e47e4af13326 h1:Zid8oNHtbOqF6ebrcGIccvIMabFNGh9dzY1b7mgIcF0= gitlab.com/elixxir/comms v0.0.4-0.20220603231314-e47e4af13326/go.mod h1:tlHSrtSliKWUxsck8z/Ql/VJkMdSONV2BeWaUAAXzgk= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= diff --git a/connectServer/listener.go b/connectServer/listener.go index a82a99f50c43ce23bd712d2242ec641696c574c8..e27b5108874bda280bc27c10c3111e1395083ca2 100644 --- a/connectServer/listener.go +++ b/connectServer/listener.go @@ -10,8 +10,8 @@ type listener struct { name string } -// Hear will be called whenever a message matching the RegisterListener call is -// received. +// Hear will be called whenever a message matching the +// RegisterListener call is received. // // User-defined message handling logic goes here. func (l *listener) Hear(item receive.Message) { @@ -21,4 +21,4 @@ func (l *listener) Hear(item receive.Message) { // Name is used for debugging purposes. func (l *listener) Name() string { return l.name -} \ No newline at end of file +} diff --git a/connectServer/main.go b/connectServer/main.go index 2c783f6894410cfe12a7dda6afaebaa3cd8b825a..78c049f293fea91024da03182184acffda0a9ebe 100644 --- a/connectServer/main.go +++ b/connectServer/main.go @@ -9,6 +9,8 @@ import ( "io/fs" "io/ioutil" "os" + "os/signal" + "syscall" "time" "gitlab.com/elixxir/client/connect" @@ -16,9 +18,9 @@ import ( func main() { // Logging - initLog(1, "client.log") + initLog(1, "server.log") - // Create a new client object------------------------------------------------------- + // Create a new client object---------------------------------------------- // Set the output contact file path contactFilePath := "connectServer.xxc" @@ -26,9 +28,9 @@ func main() { // You would ideally use a configuration tool to acquire these parameters statePath := "statePath" statePass := "password" - // The following connects to mainnet. For historical reasons it is called a json file - // but it is actually a marshalled file with a cryptographic signature attached. - // This may change in the future. + // The following connects to mainnet. For historical reasons it is called + // a json file but it is actually a marshalled file with a cryptographic + // signature attached. This may change in the future. ndfURL := "https://elixxir-bins.s3.us-west-1.amazonaws.com/ndf/mainnet.json" certificatePath := "../mainnet.crt" ndfPath := "ndf.json" @@ -50,7 +52,8 @@ func main() { jww.FATAL.Panicf("Failed to read certificate: %v", err) } - ndfJSON, err = xxdk.DownloadAndVerifySignedNdfWithUrl(ndfURL, string(cert)) + ndfJSON, err = xxdk.DownloadAndVerifySignedNdfWithUrl( + ndfURL, string(cert)) if err != nil { jww.FATAL.Panicf("Failed to download NDF: %+v", err) } @@ -63,10 +66,11 @@ func main() { } } - // Login to your client session----------------------------------------------------- + // Load client state and identity------------------------------------------ - // Login with the same sessionPath and sessionPass used to call NewClient() - baseClient, err := xxdk.LoadCmix(statePath, []byte(statePass), xxdk.GetDefaultCMixParams()) + // Load with the same sessionPath and sessionPass used to call NewClient() + baseClient, err := xxdk.LoadCmix(statePath, []byte(statePass), + xxdk.GetDefaultCMixParams()) if err != nil { jww.FATAL.Panicf("Failed to load state: %+v", err) } @@ -86,12 +90,13 @@ func main() { } } - // Save contact file---------------------------------------------------------------- + // Save contact file------------------------------------------------------- // Save the contact file so that client can connect to this server writeContact(contactFilePath, identity.GetContact()) - // Handle incoming connections------------------------------------------------------ + // Handle incoming connections--------------------------------------------- + e2eParams := xxdk.GetDefaultE2EParams() // Create callback for incoming connections cb := func(connection connect.Connection) { @@ -102,12 +107,24 @@ func main() { if err != nil { jww.FATAL.Panicf("Failed to register listener: %+v", err) } + + msgBody := "If this message is sent successfully, we'll have " + + "established contact with the client." + + roundIDs, messageID, timeSent, err := connection.SendE2E(catalog.NoType, + []byte(msgBody), e2eParams.Base) + if err != nil { + jww.FATAL.Panicf("Failed to send message: %+v", err) + } + jww.INFO.Printf("Message %v sent in RoundIDs: %+v at %v", messageID, + roundIDs, timeSent) + } - // Start connection server---------------------------------------------------------- + // Start connection server------------------------------------------------- - // Start the connection server, which will allow clients to start connections with you - e2eParams := xxdk.GetDefaultE2EParams() + // Start the connection server, which will allow clients to start + //connections with you connectionListParams := connect.DefaultConnectionListParams() connectServer, err := connect.StartServer( identity, cb, baseClient, e2eParams, connectionListParams) @@ -115,7 +132,7 @@ func main() { jww.FATAL.Panicf("Unable to start connection server: %+v", err) } - // Start network threads------------------------------------------------------------ + // Start network threads--------------------------------------------------- // Set networkFollowerTimeout to a value of your choice (seconds) networkFollowerTimeout := 5 * time.Second @@ -143,7 +160,8 @@ func main() { // Create a tracker channel to be notified of network changes connected := make(chan bool, 10) - // Provide a callback that will be signalled when network health status changes + // Provide a callback that will be signalled when network health + // status changes connectServer.E2e.GetCmix().AddHealthCallback( func(isConnected bool) { connected <- isConnected @@ -151,7 +169,19 @@ func main() { // Wait until connected or crash on timeout waitUntilConnected(connected) - // Keep app running to receive messages----------------------------------------------- + // Keep app running to receive messages------------------------------------ + + // Wait until the user terminates the program + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + <-c + + err = connectServer.E2e.StopNetworkFollower() + if err != nil { + jww.ERROR.Printf("Failed to stop network follower: %+v", err) + } else { + jww.INFO.Printf("Stopped network follower.") + } - select {} + os.Exit(0) } diff --git a/restSingleUseClient/README.md b/restSingleUseClient/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2ce63caf457aa63471b7b08cebf8514bfb547c94 --- /dev/null +++ b/restSingleUseClient/README.md @@ -0,0 +1,90 @@ +# xxdk Restlike Single Use Client Example + +This mini-repository contains the example logic for running a basic REST-like +single use client. This is provided by the xx network team as a springboard to +help consumers better understand our API and how it may be used. + +`main.go` contains the crux of the logic. We avoid complicating our example by +avoiding the usage of CLI flags for basic variables you may change in the code. +This file initiates an xxdk E2E client. With that client established, a +REST-like client is built on top. Using a precanned contact object created +in `restSingleUseServer` this REST-like client contacts the server with a simple +request. + +`utils.go` contains utility functions for running the program. In this case, +we provide a tool initializing a log. + +## Build Instructions + +In these instructions we will go over building a REST-like client using our +example. In order to build a client which successfully sends a request and +receives a response, we must first go over how to build and run a REST-like +single use server. + +### Building a Server + +In order to run a server, the following commands may be run: + +```bash +cd restSingleUseServer/ +go build -o server . +./server +``` + +This will initialize the server. You may verify its functionality by checking +the `server.log` file. It is a long-running process which may be +stopped by a user inputted kill signal. This will create a file +`restSingleUseServer.xxc`, which is the contact file for the server. +A REST-like client may parse this file in order to send a request to this +server. + +### Building a Client + +Please follow the steps above before continuing to these instructions. +In order to run the client, you must first move the aforementioned +`restSingleUseServer.xxc` file to the path where you will run the client. + +```bash +cd restSingleUseServer/ +cp restSingleUseServer.xxc /path/to/restSingleUseClient +``` + +Once the contact object is local to the client, you may build and run +the client: + +```bash +cd restSingleUseClient/ +go build -o client . +./client +``` + +This is a long-running process which may be stopped by a user inputted kill +signal. We recommend allowing the process to run for a long enough time to +complete its requests to the server and receive the server's responses. We go +into detail on what this entails below. + +Once the REST-like client has set up and sent its request, you can verify +by checking the server's log for the string `Request received:` + +```bash +grep "Request received" restSingleUseServer/server.log +INFO 2022/07/07 10:55:57.623516 Request received: headers:{headers:"This is a header"} method:1 uri:"handleClient" +INFO 2022/07/07 10:56:21.181945 Request received: headers:{headers:"This is a header"} method:1 uri:"handleClient" +``` + +By default, the client sends two requests, synchronous and asynchronous. Both +requests should be received by the server in order to accomplish a successful +client-server request. + +In order to verify the response, look at the client log for the string +`Response: `: + +```bash + grep "Response: " restSingleUseClient/client.log +INFO 2022/07/07 11:43:42.923030 Response: content:"This is content" headers:{headers:"this is a response"} +INFO 2022/07/07 11:43:50.376968 Response: content:"This is content" headers:{headers:"this is a response"} +``` + +As by default, there are two requests received by the server, the client will +receive two responses. + diff --git a/restSingleUseClient/go.mod b/restSingleUseClient/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..ec804cd1ec4ea373a7647e620812616b17ae89f8 --- /dev/null +++ b/restSingleUseClient/go.mod @@ -0,0 +1,39 @@ +module restlikeClientExample + +go 1.18 + +require ( + github.com/spf13/jwalterweatherman v1.1.0 + gitlab.com/elixxir/client v1.5.1-0.20220706193049-a0b718049663 +) + +require ( + github.com/badoux/checkmail v1.2.1 // indirect + github.com/cloudflare/circl v1.1.0 // indirect + github.com/elliotchance/orderedmap v1.4.0 // indirect + github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect + github.com/ttacon/libphonenumber v1.2.1 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f // indirect + gitlab.com/elixxir/comms v0.0.4-0.20220603231314-e47e4af13326 // indirect + gitlab.com/elixxir/crypto v0.0.7-0.20220606201132-c370d5039cea // indirect + gitlab.com/elixxir/ekv v0.1.7 // indirect + gitlab.com/elixxir/primitives v0.0.3-0.20220606195757-40f7a589347f // indirect + gitlab.com/xx_network/comms v0.0.4-0.20220630163702-f3d372ef6acd // indirect + gitlab.com/xx_network/crypto v0.0.5-0.20220606200528-3f886fe49e81 // indirect + gitlab.com/xx_network/primitives v0.0.4-0.20220630163313-7890038258c6 // indirect + gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 // indirect + golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed // indirect + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect + golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect + google.golang.org/grpc v1.42.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect +) diff --git a/restSingleUseClient/go.sum b/restSingleUseClient/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..f9aa3c251aca58d4c5330a5d3b2e2378f13036a7 --- /dev/null +++ b/restSingleUseClient/go.sum @@ -0,0 +1,243 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/badoux/checkmail v1.2.1 h1:TzwYx5pnsV6anJweMx2auXdekBwGr/yt1GgalIx9nBQ= +github.com/badoux/checkmail v1.2.1/go.mod h1:XroCOBU5zzZJcLvgwU15I+2xXyCdTWXyR9MGfRhBYy0= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A= +github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4= +github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea h1:uyJ13zfy6l79CM3HnVhDalIyZ4RJAyVfDrbnfFeJoC4= +github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea/go.mod h1:w4pGU9PkiX2hAWyF0yuHEHmYTQFAd6WHzp6+IY7JVjE= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0= +github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w= +github.com/ttacon/libphonenumber v1.2.1 h1:fzOfY5zUADkCkbIafAed11gL1sW+bJ26p6zWLBMElR4= +github.com/ttacon/libphonenumber v1.2.1/go.mod h1:E0TpmdVMq5dyVlQ7oenAkhsLu86OkUl+yR4OAxyEg/M= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/zeebo/assert v0.0.0-20181109011804-10f827ce2ed6/go.mod h1:yssERNPivllc1yU3BvpjYI5BUW+zglcz6QWqeVRL5t0= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.0.4/go.mod h1:YOZo8A49yNqM0X/Y+JmDUZshJWLt1laHsNSn5ny2i34= +github.com/zeebo/blake3 v0.1.1 h1:Nbsts7DdKThRHHd+YNlqiGlRqGEF2bE2eXN+xQ1hsEs= +github.com/zeebo/blake3 v0.1.1/go.mod h1:G9pM4qQwjRzF1/v7+vabMj/c5mWpGZ2Wzo3Eb4z0pb4= +github.com/zeebo/pcg v0.0.0-20181207190024-3cdc6b625a05/go.mod h1:Gr+78ptB0MwXxm//LBaEvBiaXY7hXJ6KGe2V32X2F6E= +github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f h1:yXGvNBqzZwAhDYlSnxPRbgor6JWoOt1Z7s3z1O9JR40= +gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= +gitlab.com/elixxir/client v1.5.1-0.20220706193049-a0b718049663 h1:7bhDI2jg/aKkYNGiSsFUGkDykb1H43/+5mBCEvmnkjA= +gitlab.com/elixxir/client v1.5.1-0.20220706193049-a0b718049663/go.mod h1:GCevkp2aa+vpIsSrlF/HsRlTjf107jgCglZ/0JKqW7E= +gitlab.com/elixxir/comms v0.0.4-0.20220603231314-e47e4af13326 h1:Zid8oNHtbOqF6ebrcGIccvIMabFNGh9dzY1b7mgIcF0= +gitlab.com/elixxir/comms v0.0.4-0.20220603231314-e47e4af13326/go.mod h1:tlHSrtSliKWUxsck8z/Ql/VJkMdSONV2BeWaUAAXzgk= +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.20220606201132-c370d5039cea h1:+FjwbKl6X9TDT7qd7gG5N5PSbziPWP3NgjK5ci1b7/8= +gitlab.com/elixxir/crypto v0.0.7-0.20220606201132-c370d5039cea/go.mod h1:Oy+VWQ2Sa0Ybata3oTV+Yc46hkaDwAsuIMW0wJ01z2M= +gitlab.com/elixxir/ekv v0.1.7 h1:OW2z+N4QCqqMFzouAwFTWWMKz0Y/PDhyYReN7gQ5NiQ= +gitlab.com/elixxir/ekv v0.1.7/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4= +gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= +gitlab.com/elixxir/primitives v0.0.0-20200804170709-a1896d262cd9/go.mod h1:p0VelQda72OzoUckr1O+vPW0AiFe0nyKQ6gYcmFSuF8= +gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc= +gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE= +gitlab.com/elixxir/primitives v0.0.3-0.20220606195757-40f7a589347f h1:CTf2+ewHWYrzp5Ar3RwNvHePfTHyFniJTVjFW4zqoaE= +gitlab.com/elixxir/primitives v0.0.3-0.20220606195757-40f7a589347f/go.mod h1:9Bb2+u+CDSwsEU5Droo6saDAXuBDvLRjexpBhPAYxhA= +gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= +gitlab.com/xx_network/comms v0.0.4-0.20220630163702-f3d372ef6acd h1:qYR2V/8KliGyJ2clWpVYhpgwpnfS1MXGpAbrFoOkSWk= +gitlab.com/xx_network/comms v0.0.4-0.20220630163702-f3d372ef6acd/go.mod h1:TraR4sW+YxK/2CV+IQUNaAV61+ElrBV0kXd5HLEjM7M= +gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE= +gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk= +gitlab.com/xx_network/crypto v0.0.5-0.20220606200528-3f886fe49e81 h1:9HK48ZEGFKLm3HBcE/FdQitllJRYPPS0zeaiRL+MBhI= +gitlab.com/xx_network/crypto v0.0.5-0.20220606200528-3f886fe49e81/go.mod h1:/SJf+R75E+QepdTLh0H1/udsovxx2Q5ru34q1v0umKk= +gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA= +gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug= +gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc= +gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20220324193139-b292d1ae6e7e/go.mod h1:AXVVFt7dDAeIUpOGPiStCcUIKsBXLWbmV/BgZ4T+tOo= +gitlab.com/xx_network/primitives v0.0.4-0.20220630163313-7890038258c6 h1:3It6ILDHn/9J/Oi7MfMjkidKPe7vbFCy5JQtXx8EfYM= +gitlab.com/xx_network/primitives v0.0.4-0.20220630163313-7890038258c6/go.mod h1:AXVVFt7dDAeIUpOGPiStCcUIKsBXLWbmV/BgZ4T+tOo= +gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 h1:eJZrXqHsMmmejEPWw8gNAt0I8CGAMNO/7C339Zco3TM= +gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200707235045-ab33eee955e0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA= +golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201014080544-cc95f250f6bc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 h1:Zk6zlGXdtYdcY5TL+VrbTfmifvk3VvsXopCpszsHPBA= +google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/restSingleUseClient/main.go b/restSingleUseClient/main.go new file mode 100644 index 0000000000000000000000000000000000000000..881569a97cc6bb4dec139025bb4ad78c28ba1d41 --- /dev/null +++ b/restSingleUseClient/main.go @@ -0,0 +1,242 @@ +package main + +import ( + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/restlike" + restSingle "gitlab.com/elixxir/client/restlike/single" + "gitlab.com/elixxir/client/single" + "gitlab.com/elixxir/client/xxdk" + "gitlab.com/elixxir/crypto/contact" + "io/fs" + "io/ioutil" + "os" + "os/signal" + "syscall" + "time" +) + +func main() { + // Logging + initLog(1, "client.log") + + // Create a new client object---------------------------------------------- + // NOTE: For some (or all) of these parameters, you may want to use a + // configuration tool of some kind + + // Path to the server contact file + serverContactPath := "restSingleUseServer.xxc" + + // Set state file parameters + statePath := "statePath" + statePass := "password" + + // The following connects to mainnet. For historical reasons + // it is called a json file but it is actually a marshalled + // file with a cryptographic signature attached. + // This may change in the future. + ndfURL := "https://elixxir-bins.s3.us-west-1.amazonaws.com/ndf/mainnet.json" + certificatePath := "../mainnet.crt" + ndfPath := "ndf.json" + + // Set the restlike parameters + exampleURI := restlike.URI("handleClient") + exampleMethod := restlike.Get + exampleContentBytes := []byte("this is some content") + exampleContent := restlike.Data{} + copy(exampleContent[:], exampleContentBytes) + exampleHeaders := &restlike.Headers{ + Headers: []byte("This is a header"), + } + singleParams := single.GetDefaultRequestParams() + + // Check if state exists + if _, err := os.Stat(statePath); errors.Is(err, fs.ErrNotExist) { + + // Attempt to read the NDF + var ndfJSON []byte + ndfJSON, err = ioutil.ReadFile(ndfPath) + if err != nil { + jww.INFO.Printf("NDF does not exist: %+v", err) + } + + // If NDF can't be read, retrieve it remotely + if ndfJSON == nil { + cert, err := ioutil.ReadFile(certificatePath) + if err != nil { + jww.FATAL.Panicf("Failed to read certificate: %v", err) + } + + ndfJSON, err = xxdk.DownloadAndVerifySignedNdfWithUrl(ndfURL, + string(cert)) + if err != nil { + jww.FATAL.Panicf("Failed to download NDF: %+v", err) + } + } + + // Initialize the state using the state file + err = xxdk.NewCmix(string(ndfJSON), statePath, []byte(statePass), "") + if err != nil { + jww.FATAL.Panicf("Failed to initialize state: %+v", err) + } + } + + // Login to your client session-------------------------------------------- + + // Login with the same sessionPath and sessionPass used to call NewClient() + baseClient, err := xxdk.LoadCmix(statePath, []byte(statePass), + xxdk.GetDefaultCMixParams()) + if err != nil { + jww.FATAL.Panicf("Failed to load state: %+v", err) + } + + // Get reception identity (automatically created if one does not exist) + identityStorageKey := "identityStorageKey" + identity, err := xxdk.LoadReceptionIdentity(identityStorageKey, baseClient) + if err != nil { + // If no extant xxdk.ReceptionIdentity, generate and store a new one + identity, err = xxdk.MakeReceptionIdentity(baseClient) + if err != nil { + jww.FATAL.Panicf("Failed to generate reception identity: %+v", err) + } + err = xxdk.StoreReceptionIdentity(identityStorageKey, identity, baseClient) + if err != nil { + jww.FATAL.Panicf("Failed to store new reception identity: %+v", err) + } + } + + // Create an E2E client + // The 'restlike' package handles AuthCallbacks, + // xxdk.DefaultAuthCallbacks is fine here + params := xxdk.GetDefaultE2EParams() + jww.INFO.Printf("Using E2E parameters: %+v", params) + e2eClient, err := xxdk.Login(baseClient, xxdk.DefaultAuthCallbacks{}, + identity, params) + if err != nil { + jww.FATAL.Panicf("Unable to Login: %+v", err) + } + + // Start network threads--------------------------------------------------- + + // Set networkFollowerTimeout to a value of your choice (seconds) + networkFollowerTimeout := 5 * time.Second + err = e2eClient.StartNetworkFollower(networkFollowerTimeout) + if err != nil { + jww.FATAL.Panicf("Failed to start network follower: %+v", err) + } + + // Set up a wait for the network to be connected + waitUntilConnected := func(connected chan bool) { + waitTimeout := 30 * time.Second + timeoutTimer := time.NewTimer(waitTimeout) + isConnected := false + // Wait until we connect or panic if we cannot before the timeout + for !isConnected { + select { + case isConnected = <-connected: + jww.INFO.Printf("Network Status: %v", isConnected) + break + case <-timeoutTimer.C: + jww.FATAL.Panicf("Timeout on starting network follower") + } + } + } + + // Create a tracker channel to be notified of network changes + connected := make(chan bool, 10) + // Provide a callback that will be signalled when network health status + // changes + e2eClient.GetCmix().AddHealthCallback( + func(isConnected bool) { + connected <- isConnected + }) + // Wait until connected or crash on timeout + waitUntilConnected(connected) + + // Build contact object---------------------------------------------------- + + // Recipient's contact (read from a Client CLI-generated contact file) + contactData, err := ioutil.ReadFile(serverContactPath) + if err != nil { + jww.FATAL.Panicf("Failed to read server contact file: %+v", err) + } + + // Imported "gitlab.com/elixxir/crypto/contact" + // which provides an `Unmarshal` function to convert the byte slice ([]byte) + // output of `ioutil.ReadFile()` to the `Contact` type expected by + // `RequestAuthenticatedChannel()` + serverContact, err := contact.Unmarshal(contactData) + if err != nil { + jww.FATAL.Panicf("Failed to get contact data: %+v", err) + } + jww.INFO.Printf("Recipient contact: %+v", serverContact) + + // Construct request------------------------------------------------------- + + stream := e2eClient.GetRng().GetStream() + defer stream.Close() + + grp, err := identity.GetGroup() + if err != nil { + jww.FATAL.Panicf("Failed to get group from identity: %+v", err) + } + + request := restSingle.Request{ + Net: e2eClient.GetCmix(), + Rng: stream, + E2eGrp: grp, + } + + // Send request to the server synchronously-------------------------------- + + // This is a synchronous request, meaning it will block until + // a response is received + response, err := request.Request(serverContact, exampleMethod, exampleURI, + exampleContent, exampleHeaders, singleParams) + if err != nil { + jww.FATAL.Panicf("Failed to call synchronous request "+ + "with server: %+v", err) + } + + jww.INFO.Printf("Response: %+v", response) + + // Send request to the server asynchronously-------------------------------- + + // In order to asynchronously request, a callback is used to handle + // when the response is received. More complex response handling may be + // implemented within the `restlike.RequestCallback`. + responseChan := make(chan *restlike.Message, 1) + cb := restlike.RequestCallback(func(message *restlike.Message) { + responseChan <- message + }) + + // Make request + err = request.AsyncRequest(serverContact, exampleMethod, exampleURI, + exampleContent, exampleHeaders, cb, singleParams) + if err != nil { + jww.FATAL.Panicf("Failed to call asynchronous request with server: %+v", + err) + } + + response = <-responseChan + + jww.INFO.Printf("Response: %+v", response) + + // Keep app running to receive messages------------------------------------ + + // Wait until the user terminates the program + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + jww.DEBUG.Printf("Waiting for SIGTERM signal to close process") + <-c + + err = e2eClient.StopNetworkFollower() + if err != nil { + jww.ERROR.Printf("Failed to stop network follower: %+v", err) + } else { + jww.INFO.Printf("Stopped network follower.") + } + + os.Exit(0) + +} diff --git a/restSingleUseClient/utils.go b/restSingleUseClient/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..71346e5874140f36a6cf413490b5e09cc2ca13e2 --- /dev/null +++ b/restSingleUseClient/utils.go @@ -0,0 +1,38 @@ +package main + +import ( + jww "github.com/spf13/jwalterweatherman" + "io/ioutil" + "log" + "os" +) + +func initLog(threshold uint, logPath string) { + if logPath != "-" && logPath != "" { + // Disable stdout output + jww.SetStdoutOutput(ioutil.Discard) + // Use log file + logOutput, err := os.OpenFile(logPath, + os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + panic(err.Error()) + } + jww.SetLogOutput(logOutput) + } + + if threshold > 1 { + jww.INFO.Printf("log level set to: TRACE") + jww.SetStdoutThreshold(jww.LevelTrace) + jww.SetLogThreshold(jww.LevelTrace) + jww.SetFlags(log.LstdFlags | log.Lmicroseconds) + } else if threshold == 1 { + jww.INFO.Printf("log level set to: DEBUG") + jww.SetStdoutThreshold(jww.LevelDebug) + jww.SetLogThreshold(jww.LevelDebug) + jww.SetFlags(log.LstdFlags | log.Lmicroseconds) + } else { + jww.INFO.Printf("log level set to: INFO") + jww.SetStdoutThreshold(jww.LevelInfo) + jww.SetLogThreshold(jww.LevelInfo) + } +} diff --git a/restSingleUseServer/README.md b/restSingleUseServer/README.md index 28ff195e2beb927c110b84c7dcf6d824e87913ed..2a48e21e9810abc453d1e4417afab5465b02ee0d 100644 --- a/restSingleUseServer/README.md +++ b/restSingleUseServer/README.md @@ -1 +1,41 @@ -# xxdk Connection Server Example +# xxdk Restlike Single Use Server Example + +This mini-respository contains the example logic for running a basic REST-like +single use server. This is provided by the xx network team as a springboard +to help consumers better understand our API and how it may be used. + +`main.go` contains the crux of the logic. We avoid complicating our example by +avoiding the usage of CLI flags for basic variables you may change in the code. +This file initiates an xxdk E2E client. With that client established, a +REST-like server is built on top. This program creates contact file +`restSingleUseServer.xxc` which may be used by a client to contact the server. + +`utils.go` contains utility functions for running the program. In this case, +we provide a tool initializing a log. It also contains a utility to write the +contact file to disk. + +`endpoints.go` contains a simple example of request handling by the server. +This prints out the request and builds a response to return the to requester. +This endpoint may be modified for more complex request handling, or more +endpoints with various request handling may be put here. + +## Build Instructions + +In these instructions we will go over building a REST-like server using our +example. This will not include instructions on running a client which sends +requests. That documentation may be found in the `README.md` for +`restSingleUseClient`. + +In order to run a server, the following commands may be run: + +```bash +cd restSingleUseServer/ +go build -o server . +./server +``` + +This will initialize the server. You may verify its functionality by checking +the `server.log` file. It is a long-running process which may be stopped by a +user inputted kill signal. This will create a file `restSingleUseServer.xxc`, +which is the contact file for the server. A REST-like client may parse this +file in order to send a request to this server. diff --git a/restSingleUseServer/endpoints.go b/restSingleUseServer/endpoints.go index 746ff6edf9276b639f60502b213445f082e91068..871f01a2dba3cd04144fb215ad4250c09b78fb48 100644 --- a/restSingleUseServer/endpoints.go +++ b/restSingleUseServer/endpoints.go @@ -12,7 +12,10 @@ import ( // the lower level of the restlike package returns an error // to the requester. // User-defined message handling logic goes here. -func Callback(request *restlike.Message) (response *restlike.Message) { +func Callback(request *restlike.Message) *restlike.Message { jww.INFO.Printf("Request received: %v", request) - return + response := &restlike.Message{} + response.Headers = &restlike.Headers{Headers: []byte("this is a response")} + response.Content = []byte("This is content") + return response } diff --git a/restSingleUseServer/main.go b/restSingleUseServer/main.go index f5c0ab2518fb5b17452aa379843ce2a3b35ebef8..881e364aeae0309febf2d6d318ed155281637010 100644 --- a/restSingleUseServer/main.go +++ b/restSingleUseServer/main.go @@ -16,14 +16,14 @@ import ( func main() { // Logging - initLog(1, "client.log") + initLog(1, "server.log") // Create a new client object---------------------------------------------- // NOTE: For some (or all) of these parameters, you may want to use a // configuration tool of some kind // Set the output contact file path - contactFilePath := "restlikeServer.xxc" + contactFilePath := "restSingleUseServer.xxc" // Set state file parameters statePath := "statePath" @@ -127,6 +127,7 @@ func main() { // Initialize the server restlikeServer := single.NewServer(identity.ID, dhKeyPrivateKey, grp, e2eClient.GetCmix()) + jww.INFO.Printf("Initialized restlike single use server") // Implement restlike endpoint--------------------------------------------- @@ -135,6 +136,7 @@ func main() { if err != nil { jww.FATAL.Panicf("Failed to add endpoint to server: %v", err) } + jww.DEBUG.Printf("Added endpoint for restlike single use server") // Start network threads--------------------------------------------------- @@ -178,6 +180,7 @@ func main() { // Wait until the user terminates the program c := make(chan os.Signal) signal.Notify(c, os.Interrupt, syscall.SIGTERM) + jww.DEBUG.Printf("Waiting for SIGTERM signal to close process") <-c err = e2eClient.StopNetworkFollower() @@ -189,6 +192,7 @@ func main() { // Close server on function exit restlikeServer.Close() + jww.INFO.Printf("Closed restlike server") os.Exit(0)