// Copyright 2017-2021 DERO Project. All rights reserved. // Use of this source code in any form is governed by RESEARCH license. // license can be found in the LICENSE file. // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8 // // // 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 HOLDER 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. package main import "io" import "os" import "fmt" import "io/ioutil" import "strings" import "path/filepath" import "encoding/hex" import "github.com/chzyer/readline" import "github.com/deroproject/derohe/globals" import "github.com/deroproject/derohe/address" //import "github.com/deroproject/derohe/walletapi" import "github.com/deroproject/derohe/transaction" // handle menu if a wallet is currently opened func display_easymenu_post_open_command(l *readline.Instance) { w := l.Stderr() io.WriteString(w, "Menu:\n") io.WriteString(w, "\t\033[1m1\033[0m\tDisplay account Address \n") io.WriteString(w, "\t\033[1m2\033[0m\tDisplay Seed "+color_red+"(Please save seed in safe location)\n\033[0m") io.WriteString(w, "\t\033[1m3\033[0m\tDisplay Keys (hex)\n") if !is_registered(wallet) { io.WriteString(w, "\t\033[1m4\033[0m\tAccount registration to blockchain (registration has no fee requirement and is precondition to use the account)\n") io.WriteString(w, "\n") io.WriteString(w, "\n") } else { // hide some commands, if view only wallet io.WriteString(w, "\n") io.WriteString(w, "\t\033[1m5\033[0m\tTransfer (send DERO) To Another Wallet\n") //io.WriteString(w, "\t\033[1m6\033[0m\tCreate Transaction in offline mode\n") io.WriteString(w, "\n") } io.WriteString(w, "\t\033[1m7\033[0m\tChange wallet password\n") io.WriteString(w, "\t\033[1m8\033[0m\tClose Wallet\n") if is_registered(wallet) { io.WriteString(w, "\t\033[1m12\033[0m\tTransfer all balance (send DERO) To Another Wallet\n") io.WriteString(w, "\t\033[1m13\033[0m\tShow transaction history\n") io.WriteString(w, "\t\033[1m14\033[0m\tRescan transaction history\n") } io.WriteString(w, "\n\t\033[1m9\033[0m\tExit menu and start prompt\n") io.WriteString(w, "\t\033[1m0\033[0m\tExit Wallet\n") } // this handles all the commands if wallet in menu mode and a wallet is opened func handle_easymenu_post_open_command(l *readline.Instance, line string) (processed bool) { var err error _ = err line = strings.TrimSpace(line) line_parts := strings.Fields(line) processed = true if len(line_parts) < 1 { // if no command return return } command := "" if len(line_parts) >= 1 { command = strings.ToLower(line_parts[0]) } offline_tx := false _ = offline_tx switch command { case "1": fmt.Fprintf(l.Stderr(), "Wallet address : "+color_green+"%s"+color_white+"\n", wallet.GetAddress()) if !is_registered(wallet) { reg_tx := wallet.GetRegistrationTX() fmt.Fprintf(l.Stderr(), "Registration TX : "+color_green+"%x"+color_white+"\n", reg_tx.Serialize()) } PressAnyKey(l, wallet) case "2": // give user his seed if !ValidateCurrentPassword(l, wallet) { globals.Logger.Warnf("Invalid password") PressAnyKey(l, wallet) break } display_seed(l, wallet) // seed should be given only to authenticated users PressAnyKey(l, wallet) case "3": // give user his keys in hex form if !ValidateCurrentPassword(l, wallet) { globals.Logger.Warnf("Invalid password") PressAnyKey(l, wallet) break } display_spend_key(l, wallet) PressAnyKey(l, wallet) case "4": // Registration if !ValidateCurrentPassword(l, wallet) { globals.Logger.Warnf("Invalid password") PressAnyKey(l, wallet) break } //if valid_registration_or_display_error(l, wallet) { // globals.Logger.Warnf("This wallet address is already registered.") // break //} fmt.Fprintf(l.Stderr(), "Wallet address : "+color_green+"%s"+color_white+" is going to be registered.This is a pre-condition for using the online chain.It will take few seconds to register/", wallet.GetAddress()) reg_tx := wallet.GetRegistrationTX() if ConfirmYesNoDefaultNo(l, "Confirm Registration (y/N)") { // at this point we must send the registration transaction fmt.Fprintf(l.Stderr(), "Wallet address : "+color_green+"%s"+color_white+" is going to be registered.Pls wait till the account is registered.", wallet.GetAddress()) wallet.SendTransaction(reg_tx) } case "6": offline_tx = true fallthrough case "5": if !valid_registration_or_display_error(l, wallet) { break } if !ValidateCurrentPassword(l, wallet) { globals.Logger.Warnf("Invalid password") break } // a , amount_to_transfer, err := collect_transfer_info(l,wallet) a, err := ReadAddress(l) if err != nil { globals.Logger.Warnf("Err :%s", err) break } amount_str := read_line_with_prompt(l, fmt.Sprintf("Enter amount to transfer in DERO (max TODO .00004 hard coded): ")) if amount_str == "" { amount_str = ".00009" } amount_to_transfer, err := globals.ParseAmount(amount_str) if err != nil { globals.Logger.Warnf("Err :%s", err) break // invalid amount provided, bail out } var payment_id []byte _ = payment_id // if user provided an integrated address donot ask him payment id if a.IsIntegratedAddress() { globals.Logger.Infof("Payment ID is integreted in address ID:%x", a.PaymentID) } if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") { addr_list := []address.Address{*a} amount_list := []uint64{amount_to_transfer} // transfer 50 dero, 2 dero fees_per_kb := uint64(0) // fees must be calculated by walletapi tx, err := wallet.Transfer(addr_list, amount_list, 0, hex.EncodeToString(payment_id), fees_per_kb, 0, false) if err != nil { globals.Logger.Warnf("Error while building Transaction err %s\n", err) break } build_relay_transaction(l, tx, err, offline_tx, amount_list) } case "12": if !valid_registration_or_display_error(l, wallet) { break } if !ValidateCurrentPassword(l, wallet) { globals.Logger.Warnf("Invalid password") break } // a , amount_to_transfer, err := collect_transfer_info(l,wallet) fmt.Printf("dest address %s\n", "deroi1qxqqkmaz8nhv4q07w3cjyt84kmrqnuw4nprpqfl9xmmvtvwa7cdykxq5dph4ufnx5ndq4ltraf (14686f5e2666a4da) dero1qxqqkmaz8nhv4q07w3cjyt84kmrqnuw4nprpqfl9xmmvtvwa7cdykxqpfpaes") a, err := ReadAddress(l) if err != nil { globals.Logger.Warnf("Err :%s", err) break } // if user provided an integrated address donot ask him payment id if a.IsIntegratedAddress() { globals.Logger.Infof("Payment ID is integreted in address ID:%x", a.PaymentID) } if ConfirmYesNoDefaultNo(l, "Confirm Transaction to send entire balance (y/N)") { addr_list := []address.Address{*a} amount_list := []uint64{0} // transfer 50 dero, 2 dero fees_per_kb := uint64(0) // fees must be calculated by walletapi tx, err := wallet.Transfer(addr_list, amount_list, 0, "", fees_per_kb, 0, true) if err != nil { globals.Logger.Warnf("Error while building Transaction err %s\n", err) break } build_relay_transaction(l, tx, err, offline_tx, amount_list) } //PressAnyKey(l, wallet) // wait for a key press case "7": // change password if ConfirmYesNoDefaultNo(l, "Change wallet password (y/N)") && ValidateCurrentPassword(l, wallet) { new_password := ReadConfirmedPassword(l, "Enter new password", "Confirm password") err = wallet.Set_Encrypted_Wallet_Password(new_password) if err == nil { globals.Logger.Infof("Wallet password successfully changed") } else { globals.Logger.Warnf("Wallet password could not be changed err %s", err) } } case "8": // close and discard user key wallet.Close_Encrypted_Wallet() prompt_mutex.Lock() wallet = nil // overwrite previous instance prompt_mutex.Unlock() fmt.Fprintf(l.Stderr(), color_yellow+"Wallet closed"+color_white) case "9": // enable prompt mode menu_mode = false globals.Logger.Infof("Prompt mode enabled, type \"menu\" command to start menu mode") case "0", "bye", "exit", "quit": wallet.Close_Encrypted_Wallet() // save the wallet prompt_mutex.Lock() wallet = nil globals.Exit_In_Progress = true prompt_mutex.Unlock() fmt.Fprintf(l.Stderr(), color_yellow+"Wallet closed"+color_white) fmt.Fprintf(l.Stderr(), color_yellow+"Exiting"+color_white) case "13": show_transfers(l, wallet, 100) case "14": globals.Logger.Infof("Rescanning wallet history") rescan_bc(wallet) default: processed = false // just loop } return } // handles the output after building tx, takes feedback, confirms or relays tx func build_relay_transaction(l *readline.Instance, tx *transaction.Transaction, err error, offline_tx bool, amount_list []uint64) { if err != nil { globals.Logger.Warnf("Error while building Transaction err %s\n", err) return } amount := uint64(0) for i := range amount_list { amount += amount_list[i] } globals.Logger.Infof("Transfering total amount %s DERO", globals.FormatMoney(amount)) globals.Logger.Infof("fees %s DERO", globals.FormatMoney(tx.Statement.Fees)) globals.Logger.Infof("TX Size %0.1f KiB", float32(len(tx.Serialize()))/1024.0) //if input_sum != (amount + change + tx.Fee()) { // panic(fmt.Sprintf("Inputs %d != outputs ( %d + %d + %d )", input_sum, amount, change, tx.RctSignature.Get_TX_Fee())) //} //if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") { if true { if offline_tx { // if its an offline tx, dump it to a file cur_dir, err := os.Getwd() if err != nil { globals.Logger.Warnf("Cannot obtain current directory to save tx") globals.Logger.Infof("Transaction discarded") return } filename := filepath.Join(cur_dir, tx.GetHash().String()+".tx") err = ioutil.WriteFile(filename, []byte(hex.EncodeToString(tx.Serialize())), 0600) if err == nil { if err == nil { globals.Logger.Infof("Transaction saved successfully. txid = %s", tx.GetHash()) globals.Logger.Infof("Saved to %s", filename) } else { globals.Logger.Warnf("Error saving tx to %s, err %s", filename, err) } } } else { err = wallet.SendTransaction(tx) // relay tx to daemon/network if err == nil { globals.Logger.Infof("Transaction sent successfully. txid = %s", tx.GetHash()) } else { globals.Logger.Warnf("Transaction sending failed txid = %s, err %s", tx.GetHash(), err) } } PressAnyKey(l, wallet) // wait for a key press } else { globals.Logger.Infof("Transaction discarded") } }