github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/cmd/dero-wallet-cli/easymenu_post_open.go (about)

     1  // Copyright 2017-2018 DERO Project. All rights reserved.
     2  // Use of this source code in any form is governed by RESEARCH license.
     3  // license can be found in the LICENSE file.
     4  // GPG: 0F39 E425 8C65 3947 702A  8234 08B2 0360 A03A 9DE8
     5  //
     6  //
     7  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
     8  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     9  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
    10  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    11  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    12  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    13  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    14  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    15  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    16  
    17  package main
    18  
    19  import "io"
    20  import "os"
    21  import "fmt"
    22  import "io/ioutil"
    23  import "strings"
    24  import "path/filepath"
    25  import "encoding/hex"
    26  
    27  import "github.com/chzyer/readline"
    28  
    29  import "github.com/deroproject/derosuite/globals"
    30  import "github.com/deroproject/derosuite/address"
    31  import "github.com/deroproject/derosuite/walletapi"
    32  import "github.com/deroproject/derosuite/transaction"
    33  
    34  // handle menu if a wallet is currently opened
    35  func display_easymenu_post_open_command(l *readline.Instance) {
    36  	w := l.Stderr()
    37  	io.WriteString(w, "Menu:\n")
    38  
    39  	io.WriteString(w, "\t\033[1m1\033[0m\tDisplay account Address \n")
    40  	if !wallet.Is_View_Only() { // hide some commands, if view only wallet
    41  		io.WriteString(w, "\t\033[1m2\033[0m\tDisplay Seed "+color_red+"(Please save seed in safe location)\n\033[0m")
    42  	}
    43  	io.WriteString(w, "\t\033[1m3\033[0m\tDisplay Keys (hex)\n")
    44  	io.WriteString(w, "\t\033[1m4\033[0m\tDisplay Watch-able View only wallet key ( cannot spend any funds from this wallet) (hex)\n")
    45  
    46  	if !wallet.Is_View_Only() { // hide some commands, if view only wallet
    47  		io.WriteString(w, "\t\033[1m5\033[0m\tTransfer (send  DERO) To Another Wallet\n")
    48  		io.WriteString(w, "\t\033[1m6\033[0m\tCreate Transaction in offline mode\n")
    49  
    50  	}
    51  
    52  	io.WriteString(w, "\t\033[1m7\033[0m\tChange wallet password\n")
    53  	io.WriteString(w, "\t\033[1m8\033[0m\tClose Wallet\n")
    54  	io.WriteString(w, "\t\033[1m11\033[0m\tRescan Blockchain\n")
    55  	if !wallet.Is_View_Only() {
    56  		io.WriteString(w, "\t\033[1m12\033[0m\tTransfer all balance (send  DERO) To Another Wallet\n")
    57  		io.WriteString(w, "\t\033[1m13\033[0m\tShow transaction history\n")
    58  	}
    59  	io.WriteString(w, "\t\033[1m14\033[0m\tRescan Balance\n")
    60  
    61  	io.WriteString(w, "\n\t\033[1m9\033[0m\tExit menu and start prompt\n")
    62  	io.WriteString(w, "\t\033[1m0\033[0m\tExit Wallet\n")
    63  
    64  }
    65  
    66  // this handles all the commands if wallet in menu mode  and a wallet is opened
    67  func handle_easymenu_post_open_command(l *readline.Instance, line string) (processed bool) {
    68  
    69  	var err error
    70  	_ = err
    71  	line = strings.TrimSpace(line)
    72  	line_parts := strings.Fields(line)
    73  	processed = true
    74  
    75  	if len(line_parts) < 1 { // if no command return
    76  		return
    77  	}
    78  
    79  	command := ""
    80  	if len(line_parts) >= 1 {
    81  		command = strings.ToLower(line_parts[0])
    82  	}
    83  
    84  	offline_tx := false
    85  	switch command {
    86  	case "1":
    87  		fmt.Fprintf(l.Stderr(), "Wallet address : "+color_green+"%s"+color_white+"\n", wallet.GetAddress())
    88  		PressAnyKey(l, wallet)
    89  
    90  	case "2": // give user his seed
    91  		if !wallet.Is_View_Only() {
    92  			if !ValidateCurrentPassword(l, wallet) {
    93  				globals.Logger.Warnf("Invalid password")
    94  				PressAnyKey(l, wallet)
    95  				break
    96  			}
    97  			display_seed(l, wallet) // seed should be given only to authenticated users
    98  		} else {
    99  			fmt.Fprintf(l.Stderr(), color_red+" View wallet do not have seeds"+color_white)
   100  		}
   101  		PressAnyKey(l, wallet)
   102  
   103  	case "3": // give user his keys in hex form
   104  
   105  		if !ValidateCurrentPassword(l, wallet) {
   106  			globals.Logger.Warnf("Invalid password")
   107  			PressAnyKey(l, wallet)
   108  			break
   109  		}
   110  
   111  		display_spend_key(l, wallet)
   112  		display_view_key(l, wallet)
   113  		PressAnyKey(l, wallet)
   114  
   115  	case "4": // display user keys to create view only wallet
   116  		if !ValidateCurrentPassword(l, wallet) {
   117  			globals.Logger.Warnf("Invalid password")
   118  			break
   119  		}
   120  		display_viewwallet_key(l, wallet)
   121  		PressAnyKey(l, wallet)
   122  	case "6":
   123  		offline_tx = true
   124  		fallthrough
   125  	case "5":
   126  		if wallet.Is_View_Only() {
   127  			fmt.Fprintf(l.Stderr(), color_yellow+"View Only wallet cannot transfer."+color_white)
   128  		}
   129  
   130  		if !ValidateCurrentPassword(l, wallet) {
   131  			globals.Logger.Warnf("Invalid password")
   132  			break
   133  		}
   134  
   135  		// a , amount_to_transfer, err := collect_transfer_info(l,wallet)
   136  		a, err := ReadAddress(l)
   137  		if err != nil {
   138  			globals.Logger.Warnf("Err :%s", err)
   139  			break
   140  		}
   141  
   142  		amount_str := read_line_with_prompt(l, fmt.Sprintf("Enter amount to transfer in DERO (max TODO): "))
   143  		amount_to_transfer, err := globals.ParseAmount(amount_str)
   144  		if err != nil {
   145  			globals.Logger.Warnf("Err :%s", err)
   146  			break // invalid amount provided, bail out
   147  		}
   148  
   149  		var payment_id []byte
   150  		// if user provided an integrated address donot ask him payment id
   151  		if !a.IsIntegratedAddress() {
   152  			payment_id, err = ReadPaymentID(l)
   153  			if err != nil {
   154  				globals.Logger.Warnf("Err :%s", err)
   155  				break
   156  			}
   157  		} else {
   158  			globals.Logger.Infof("Payment ID is integreted in address ID:%x", a.PaymentID)
   159  		}
   160  
   161  		addr_list := []address.Address{*a}
   162  		amount_list := []uint64{amount_to_transfer} // transfer 50 dero, 2 dero
   163  		fees_per_kb := uint64(0)                    // fees  must be calculated by walletapi
   164  		tx, inputs, input_sum, change, err := wallet.Transfer(addr_list, amount_list, 0, hex.EncodeToString(payment_id), fees_per_kb, 0)
   165  		_ = inputs
   166  		if err != nil {
   167  			globals.Logger.Warnf("Error while building Transaction err %s\n", err)
   168  			break
   169  		}
   170  
   171  		build_relay_transaction(l, tx, inputs, input_sum, change, err, offline_tx, amount_list)
   172  
   173  	case "12":
   174  		Transfer_Everything(l)
   175  		PressAnyKey(l, wallet) // wait for a key press
   176  
   177  	case "7": // change password
   178  		if ConfirmYesNoDefaultNo(l, "Change wallet password (y/N)") &&
   179  			ValidateCurrentPassword(l, wallet) {
   180  
   181  			new_password := ReadConfirmedPassword(l, "Enter new password", "Confirm password")
   182  			err = wallet.Set_Encrypted_Wallet_Password(new_password)
   183  			if err == nil {
   184  				globals.Logger.Infof("Wallet password successfully changed")
   185  			} else {
   186  				globals.Logger.Warnf("Wallet password could not be changed err %s", err)
   187  			}
   188  		}
   189  
   190  	case "8": // close and discard user key
   191  
   192  		wallet.Close_Encrypted_Wallet()
   193  		prompt_mutex.Lock()
   194  		wallet = nil // overwrite previous instance
   195  		prompt_mutex.Unlock()
   196  
   197  		fmt.Fprintf(l.Stderr(), color_yellow+"Wallet closed"+color_white)
   198  
   199  	case "9": // enable prompt mode
   200  		menu_mode = false
   201  		globals.Logger.Infof("Prompt mode enabled, type \"menu\" command to start menu mode")
   202  
   203  	case "0", "bye", "exit", "quit":
   204  		wallet.Close_Encrypted_Wallet() // save the wallet
   205  		prompt_mutex.Lock()
   206  		wallet = nil
   207  		globals.Exit_In_Progress = true
   208  		prompt_mutex.Unlock()
   209  		fmt.Fprintf(l.Stderr(), color_yellow+"Wallet closed"+color_white)
   210  		fmt.Fprintf(l.Stderr(), color_yellow+"Exiting"+color_white)
   211  
   212  	case "11":
   213  		rescan_bc(wallet)
   214  	case "13":
   215  		show_transfers(l, wallet, 100)
   216  
   217  	case "14":
   218  		globals.Logger.Infof("Rescanning wallet")
   219  		balance_unlocked, locked_balance := wallet.Get_Balance_Rescan()
   220  		fmt.Fprintf(l.Stderr(), "Locked balance   : "+color_green+"%s"+color_white+"\n", globals.FormatMoney12(locked_balance))
   221  		fmt.Fprintf(l.Stderr(), "Unlocked balance : "+color_green+"%s"+color_white+"\n", globals.FormatMoney12(balance_unlocked))
   222  		fmt.Fprintf(l.Stderr(), "Total balance    : "+color_green+"%s"+color_white+"\n\n", globals.FormatMoney12(locked_balance+balance_unlocked))
   223  
   224  	default:
   225  		processed = false // just loop
   226  
   227  	}
   228  	return
   229  }
   230  
   231  // handles the output after building tx, takes feedback, confirms or relays tx
   232  func build_relay_transaction(l *readline.Instance, tx *transaction.Transaction, inputs []uint64, input_sum uint64, change uint64, err error, offline_tx bool, amount_list []uint64) {
   233  
   234  	if err != nil {
   235  		globals.Logger.Warnf("Error while building Transaction err %s\n", err)
   236  		return
   237  	}
   238  	globals.Logger.Infof("%d Inputs Selected for %s DERO", len(inputs), globals.FormatMoney12(input_sum))
   239  	amount := uint64(0)
   240  	for i := range amount_list {
   241  		amount += amount_list[i]
   242  	}
   243  	globals.Logger.Infof("Transfering total amount %s DERO", globals.FormatMoney12(amount))
   244  	globals.Logger.Infof("change amount ( will come back ) %s DERO", globals.FormatMoney12(change))
   245  
   246  	globals.Logger.Infof("fees %s DERO", globals.FormatMoney12(tx.RctSignature.Get_TX_Fee()))
   247  	globals.Logger.Infof("TX Size %0.1f KiB", float32(len(tx.Serialize()))/1024.0)
   248  
   249  	if input_sum != (amount + change + tx.RctSignature.Get_TX_Fee()) {
   250  		panic(fmt.Sprintf("Inputs %d != outputs ( %d + %d + %d )", input_sum, amount, change, tx.RctSignature.Get_TX_Fee()))
   251  	}
   252  
   253  	if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") {
   254  
   255  		if offline_tx { // if its an offline tx, dump it to a file
   256  			cur_dir, err := os.Getwd()
   257  			if err != nil {
   258  				globals.Logger.Warnf("Cannot obtain current directory to save tx")
   259  				globals.Logger.Infof("Transaction discarded")
   260  				return
   261  			}
   262  			filename := filepath.Join(cur_dir, tx.GetHash().String()+".tx")
   263  			err = ioutil.WriteFile(filename, []byte(hex.EncodeToString(tx.Serialize())), 0600)
   264  			if err == nil {
   265  				if err == nil {
   266  					globals.Logger.Infof("Transaction saved successfully. txid = %s", tx.GetHash())
   267  					globals.Logger.Infof("Saved to %s", filename)
   268  				} else {
   269  					globals.Logger.Warnf("Error saving tx to %s, err %s", filename, err)
   270  				}
   271  			}
   272  
   273  		} else {
   274  
   275  			err = wallet.SendTransaction(tx) // relay tx to daemon/network
   276  			if err == nil {
   277  				globals.Logger.Infof("Transaction sent successfully. txid = %s", tx.GetHash())
   278  			} else {
   279  				globals.Logger.Warnf("Transaction sending failed txid = %s, err %s", tx.GetHash(), err)
   280  			}
   281  
   282  		}
   283  
   284  		PressAnyKey(l, wallet) // wait for a key press
   285  	} else {
   286  		globals.Logger.Infof("Transaction discarded")
   287  	}
   288  
   289  }
   290  
   291  // collects inputs from user to send some dero
   292  func collect_transfer_info(l *readline.Instance, wallet *walletapi.Wallet) (a *address.Address, amount uint64, err error) {
   293  
   294  	address_str := read_line_with_prompt(l, "Please enter destination address: ")
   295  	// we have an address parse and verify, whethers is mainnet, testnet, belongs to our network
   296  	a, err = globals.ParseValidateAddress(address_str)
   297  	if err != nil {
   298  		return // invalid address provided, bail out
   299  	}
   300  
   301  	amount_str := read_line_with_prompt(l, fmt.Sprintf("Please enter amount to transfer in DERO (max %s): ", "0"))
   302  	amount, err = globals.ParseAmount(amount_str)
   303  	if err != nil {
   304  		return // invalid amount provided, bail out
   305  	}
   306  
   307  	//TODO check whether amount provided is less than balance, similiar checks are done within wallet also
   308  	return // everything is okay return
   309  
   310  	//err = fmt.Errorf("Account is invalid")
   311  	//return
   312  }