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 }