From 1f920df8e96a0fa3eeb62cb1051020add7559ba2 Mon Sep 17 00:00:00 2001
From: Jono Wenger <jono@elixxir.io>
Date: Tue, 16 Aug 2022 14:23:10 -0700
Subject: [PATCH] Implement backup.go

---
 main.go                              |   7 +-
 wasm/authenticatedConnection.go      |   3 +-
 wasm/authenticatedConnection_test.go |   2 +
 wasm/backup.go                       | 172 +++++++++++++++++++++++++++
 wasm/backup_test.go                  |  34 ++++++
 wasm/cmix.go                         |  10 +-
 wasm/connect.go                      |  16 +--
 wasm/e2e.go                          |   8 +-
 8 files changed, 232 insertions(+), 20 deletions(-)
 create mode 100644 wasm/backup.go
 create mode 100644 wasm/backup_test.go

diff --git a/main.go b/main.go
index 2bbdef83..930e10f0 100644
--- a/main.go
+++ b/main.go
@@ -74,9 +74,14 @@ func main() {
 	js.Global().Set("NewDummyTrafficManager",
 		js.FuncOf(wasm.NewDummyTrafficManager))
 
-	// bindings/broadcast
+	// bindings/broadcast.go
 	js.Global().Set("NewBroadcastChannel", js.FuncOf(wasm.NewBroadcastChannel))
 
+	// bindings/backup.go
+	js.Global().Set("NewCmixFromBackup", js.FuncOf(wasm.NewCmixFromBackup))
+	js.Global().Set("InitializeBackup", js.FuncOf(wasm.InitializeBackup))
+	js.Global().Set("ResumeBackup", js.FuncOf(wasm.ResumeBackup))
+
 	<-make(chan bool)
 	os.Exit(0)
 }
diff --git a/wasm/authenticatedConnection.go b/wasm/authenticatedConnection.go
index 9742895e..dceea6f0 100644
--- a/wasm/authenticatedConnection.go
+++ b/wasm/authenticatedConnection.go
@@ -39,10 +39,9 @@ func (ac *AuthenticatedConnection) IsAuthenticated(js.Value, []js.Value) interfa
 // ConnectWithAuthentication is called by the client (i.e., the one establishing
 // connection with the server). Once a connect.Connection has been established
 // with the server, it then authenticates their identity to the server.
-// accepts a marshalled ReceptionIdentity and contact.Contact object
 //
 // Parameters:
-//  - args[0] - ID of E2e object in tracker (int)
+//  - args[0] - ID of E2e object in tracker (int).
 //  - args[1] - marshalled recipient [contact.Contact] (Uint8Array).
 //  - args[3] - JSON of [xxdk.E2EParams] (Uint8Array).
 //
diff --git a/wasm/authenticatedConnection_test.go b/wasm/authenticatedConnection_test.go
index e700d3f9..68555c15 100644
--- a/wasm/authenticatedConnection_test.go
+++ b/wasm/authenticatedConnection_test.go
@@ -5,6 +5,8 @@
 // LICENSE file                                                               //
 ////////////////////////////////////////////////////////////////////////////////
 
+//go:build js && wasm
+
 package wasm
 
 import (
diff --git a/wasm/backup.go b/wasm/backup.go
new file mode 100644
index 00000000..cfa426ef
--- /dev/null
+++ b/wasm/backup.go
@@ -0,0 +1,172 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package wasm
+
+import (
+	"gitlab.com/elixxir/client/bindings"
+	"syscall/js"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Structs and Interfaces                                                     //
+////////////////////////////////////////////////////////////////////////////////
+
+// Backup wraps the [bindings.Backup] object so its methods can be wrapped to be
+// Javascript compatible.
+type Backup struct {
+	api *bindings.Backup
+}
+
+// newBackupJS creates a new Javascript compatible object
+// (map[string]interface{}) that matches the Backup structure.
+func newBackupJS(api *bindings.Backup) map[string]interface{} {
+	b := Backup{api}
+	backupMap := map[string]interface{}{
+		"StopBackup":      js.FuncOf(b.StopBackup),
+		"IsBackupRunning": js.FuncOf(b.IsBackupRunning),
+		"AddJson":         js.FuncOf(b.AddJson),
+	}
+
+	return backupMap
+}
+
+// updateBackupFunc wraps Javascript callbacks to adhere to the
+// [bindings.UpdateBackupFunc] interface.
+type updateBackupFunc struct {
+	updateBackup func(args ...interface{}) js.Value
+}
+
+func (ubf *updateBackupFunc) UpdateBackup(encryptedBackup []byte) {
+	ubf.updateBackup(CopyBytesToJS(encryptedBackup))
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Client functions                                                           //
+////////////////////////////////////////////////////////////////////////////////
+
+// NewCmixFromBackup initializes a new e2e storage from an encrypted
+// backup. Users of this function should delete the storage directory on error.
+// Users of this function should call LoadCmix as normal once this call succeeds.
+//
+// Parameters:
+//  - args[0] - JSON of the NDF (string).
+//  - args[1] - storage directory (string).
+//  - args[2] - backup passphrase (string).
+//  - args[3] - session password (Uint8Array).
+//  - args[4] - backup file contents (Uint8Array).
+//
+// Returns:
+//  - JSON of [bindings.BackupReport] (Uint8Array).
+//  - throws a TypeError if creating Cmix from backup fails.
+func NewCmixFromBackup(_ js.Value, args []js.Value) interface{} {
+	ndfJSON := args[0].String()
+	storageDir := args[1].String()
+	backupPassphrase := args[2].String()
+	sessionPassword := CopyBytesToGo(args[3])
+	backupFileContents := CopyBytesToGo(args[4])
+
+	report, err := bindings.NewCmixFromBackup(ndfJSON, storageDir,
+		backupPassphrase, sessionPassword, backupFileContents)
+	if err != nil {
+		Throw(TypeError, err.Error())
+		return nil
+	}
+
+	return CopyBytesToJS(report)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Backup functions                                                           //
+////////////////////////////////////////////////////////////////////////////////
+
+// InitializeBackup creates a bindings-layer Backup object.
+//
+// Parameters:
+//  - args[0] - ID of E2e object in tracker (int).
+//  - args[1] - ID of UserDiscovery object in tracker (int).
+//  - args[2] - backup passphrase provided by the user (string). Used to decrypt
+//    backup.
+//  - args[3] - the callback to be called when a backup is triggered. Must be
+//    Javascript object that has functions that implement the
+//    [bindings.UpdateBackupFunc] interface.
+//
+// Returns:
+//  - Javascript representation of the Backup object
+//  - Throws a TypeError if initializing the Backup fails.
+func InitializeBackup(_ js.Value, args []js.Value) interface{} {
+	cb := &updateBackupFunc{args[3].Get("UpdateBackup").Invoke}
+	api, err := bindings.InitializeBackup(
+		args[0].Int(), args[1].Int(), args[2].String(), cb)
+	if err != nil {
+		Throw(TypeError, err.Error())
+		return nil
+	}
+
+	return newBackupJS(api)
+}
+
+// ResumeBackup resumes the backup processes with a new callback.
+// Call this function only when resuming a backup that has already been
+// initialized or to replace the callback.
+// To start the backup for the first time or to use a new password, use
+// InitializeBackup.
+//
+// Parameters:
+//  - args[0] - ID of E2e object in tracker (int).
+//  - args[1] - ID of UserDiscovery object in tracker (int).
+//  - args[2] - the callback to be called when a backup is triggered. Must be
+//    Javascript object that has functions that implement the
+//    [bindings.UpdateBackupFunc] interface. This will replace any callback that
+//    has been passed into InitializeBackup.
+//
+// Returns:
+//  - Javascript representation of the Backup object
+//  - Throws a TypeError if initializing the Backup fails.
+func ResumeBackup(_ js.Value, args []js.Value) interface{} {
+	cb := &updateBackupFunc{args[2].Get("UpdateBackup").Invoke}
+	api, err := bindings.ResumeBackup(args[0].Int(), args[1].Int(), cb)
+	if err != nil {
+		Throw(TypeError, err.Error())
+		return nil
+	}
+
+	return newBackupJS(api)
+}
+
+// StopBackup stops the backup processes and deletes the user's password from
+// storage. To enable backups again, call InitializeBackup.
+//
+// Returns:
+//  - Throws a TypeError if stopping the backup fails.
+func (b *Backup) StopBackup(js.Value, []js.Value) interface{} {
+	err := b.api.StopBackup()
+	if err != nil {
+		Throw(TypeError, err.Error())
+		return nil
+	}
+
+	return nil
+}
+
+// IsBackupRunning returns true if the backup has been initialized and is
+// running. Returns false if it has been stopped.
+//
+// Returns:
+//  - boolean
+func (b *Backup) IsBackupRunning(js.Value, []js.Value) interface{} {
+	return b.api.IsBackupRunning()
+}
+
+// AddJson stores the argument within the Backup structure.
+//
+// Parameters:
+//  - args[0] - JSON to store (string).
+func (b *Backup) AddJson(_ js.Value, args []js.Value) interface{} {
+	b.api.AddJson(args[0].String())
+	return nil
+}
diff --git a/wasm/backup_test.go b/wasm/backup_test.go
new file mode 100644
index 00000000..0ec31c9b
--- /dev/null
+++ b/wasm/backup_test.go
@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package wasm
+
+import (
+	"gitlab.com/elixxir/client/bindings"
+	"reflect"
+	"testing"
+)
+
+// Tests that the map representing Backup returned by newBackupJS contains all
+// of the methods on Backup.
+func Test_newBackupJS(t *testing.T) {
+	buType := reflect.TypeOf(&Backup{})
+
+	b := newBackupJS(&bindings.Backup{})
+	if len(b) != buType.NumMethod() {
+		t.Errorf("Backup JS object does not have all methods."+
+			"\nexpected: %d\nreceived: %d", buType.NumMethod(), len(b))
+	}
+
+	for i := 0; i < buType.NumMethod(); i++ {
+		method := buType.Method(i)
+
+		if _, exists := b[method.Name]; !exists {
+			t.Errorf("Method %s does not exist.", method.Name)
+		}
+	}
+}
diff --git a/wasm/cmix.go b/wasm/cmix.go
index 3c26acd0..489d9f4c 100644
--- a/wasm/cmix.go
+++ b/wasm/cmix.go
@@ -66,13 +66,13 @@ func newCmixJS(api *bindings.Cmix) map[string]interface{} {
 // Users of this function should delete the storage directory on error.
 //
 // Parameters:
-//  - args[0] - NDF JSON (string)
-//  - args[1] - storage directory path (string)
-//  - args[2] - password used for storage (Uint8Array)
-//  - args[3] - registration code (string)
+//  - args[0] - NDF JSON (string).
+//  - args[1] - storage directory path (string).
+//  - args[2] - password used for storage (Uint8Array).
+//  - args[3] - registration code (string).
 //
 // Returns:
-//  - throws a TypeError if creating new Cmix fails
+//  - throws a TypeError if creating new Cmix fails.
 func NewCmix(_ js.Value, args []js.Value) interface{} {
 	password := CopyBytesToGo(args[2])
 
diff --git a/wasm/connect.go b/wasm/connect.go
index 1149fbe4..ea87b216 100644
--- a/wasm/connect.go
+++ b/wasm/connect.go
@@ -17,14 +17,14 @@ import (
 // Connection wraps the [bindings.Connection] object so its methods can be
 // wrapped to be Javascript compatible.
 type Connection struct {
-	c *bindings.Connection
+	api *bindings.Connection
 }
 
 // newConnectJS creates a new Javascript compatible object
 // (map[string]interface{}) that matches the Connection structure.
 func newConnectJS(api *bindings.Connection) map[string]interface{} {
 	c := Connection{api}
-	connection := map[string]interface{}{
+	connectionMap := map[string]interface{}{
 		// connect.go
 		"GetID":            js.FuncOf(c.GetID),
 		"SendE2E":          js.FuncOf(c.SendE2E),
@@ -33,7 +33,7 @@ func newConnectJS(api *bindings.Connection) map[string]interface{} {
 		"RegisterListener": js.FuncOf(c.RegisterListener),
 	}
 
-	return connection
+	return connectionMap
 }
 
 // GetID returns the ID for this [bindings.Connection] in the connectionTracker.
@@ -41,7 +41,7 @@ func newConnectJS(api *bindings.Connection) map[string]interface{} {
 // Returns:
 //  - int of the ID
 func (c *Connection) GetID(js.Value, []js.Value) interface{} {
-	return c.c.GetId()
+	return c.api.GetId()
 }
 
 // Connect performs auth key negotiation with the given recipient and returns a
@@ -86,7 +86,7 @@ func (c *Cmix) Connect(_ js.Value, args []js.Value) interface{} {
 //    cmix.WaitForRoundResult to see if the send succeeded (Uint8Array)
 //  - throws a TypeError if sending fails
 func (c *Connection) SendE2E(_ js.Value, args []js.Value) interface{} {
-	sendReport, err := c.c.SendE2E(args[0].Int(), CopyBytesToGo(args[1]))
+	sendReport, err := c.api.SendE2E(args[0].Int(), CopyBytesToGo(args[1]))
 	if err != nil {
 		Throw(TypeError, err.Error())
 		return nil
@@ -99,7 +99,7 @@ func (c *Connection) SendE2E(_ js.Value, args []js.Value) interface{} {
 // Returns:
 //  - throws a TypeError if closing fails
 func (c *Connection) Close(js.Value, []js.Value) interface{} {
-	err := c.c.Close()
+	err := c.api.Close()
 	if err != nil {
 		Throw(TypeError, err.Error())
 		return nil
@@ -113,7 +113,7 @@ func (c *Connection) Close(js.Value, []js.Value) interface{} {
 // Returns:
 //  - bytes of the partner's [id.ID] (Uint8Array)
 func (c *Connection) GetPartner(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(c.c.GetPartner())
+	return CopyBytesToJS(c.api.GetPartner())
 }
 
 // listener adheres to the [bindings.Listener] interface.
@@ -136,7 +136,7 @@ func (l *listener) Name() string     { return l.name().String() }
 // Returns:
 //  - throws a TypeError is registering the listener fails
 func (c *Connection) RegisterListener(_ js.Value, args []js.Value) interface{} {
-	err := c.c.RegisterListener(args[0].Int(),
+	err := c.api.RegisterListener(args[0].Int(),
 		&listener{args[1].Get("Hear").Invoke, args[1].Get("Name").Invoke})
 	if err != nil {
 		Throw(TypeError, err.Error())
diff --git a/wasm/e2e.go b/wasm/e2e.go
index 4abdf18b..4a754fe7 100644
--- a/wasm/e2e.go
+++ b/wasm/e2e.go
@@ -80,11 +80,11 @@ func (e *E2e) GetID(js.Value, []js.Value) interface{} {
 // default [auth.Callbacks] will be used.
 //
 // Parameters:
-//  - args[0] - ID of Cmix object in tracker (int)
+//  - args[0] - ID of Cmix object in tracker (int).
 //  - args[1] - Javascript object that has functions that implement the
-//    [bindings.AuthCallbacks] interface
-//  - args[2] - JSON of the [xxdk.ReceptionIdentity] object (Uint8Array)
-//  - args[3] - JSON of [xxdk.E2EParams] (Uint8Array)
+//    [bindings.AuthCallbacks] interface.
+//  - args[2] - JSON of the [xxdk.ReceptionIdentity] object (Uint8Array).
+//  - args[3] - JSON of [xxdk.E2EParams] (Uint8Array).
 //
 // Returns:
 //  - Javascript representation of the E2e object
-- 
GitLab