// 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 "time" import "fmt" //import "io/ioutil" import "strings" //import "path/filepath" //import "encoding/hex" import "github.com/chzyer/readline" import "github.com/deroproject/derohe/rpc" import "github.com/deroproject/derohe/globals" //import "github.com/deroproject/derohe/address" import "github.com/deroproject/derohe/cryptography/crypto" 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 !wallet.IsRegistered() { 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, "\t\033[1m4\033[0m\tDisplay wallet pool\n") io.WriteString(w, "\t\033[1m5\033[0m\tTransfer (send DERO) to Another Wallet\n") io.WriteString(w, "\t\033[1m6\033[0m\tToken transfer to another wallet\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 wallet.IsRegistered() { 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 !wallet.IsRegistered() { 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) { logger.Error(fmt.Errorf("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) { logger.Error(fmt.Errorf("Invalid password"), "") PressAnyKey(l, wallet) break } display_spend_key(l, wallet) PressAnyKey(l, wallet) case "4": // Registration if !wallet.IsRegistered() { 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.\n", wallet.GetAddress()) reg_tx := wallet.GetRegistrationTX() // 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.\n", wallet.GetAddress()) err := wallet.SendTransaction(reg_tx) if err != nil { fmt.Fprintf(l.Stderr(), "sending registration tx err %s\n", err) } else { fmt.Fprintf(l.Stderr(), "registration tx dispatched successfully\n") } } else { } case "6": if !valid_registration_or_display_error(l, wallet) { break } if !ValidateCurrentPassword(l, wallet) { logger.Error(fmt.Errorf("Invalid password"), "") break } scid, err := ReadSCID(l) if err != nil { logger.Error(err, "error reading SCID") break } a, err := ReadAddress(l) if err != nil { logger.Error(err, "error reading address") break } var amount_to_transfer uint64 amount_str := read_line_with_prompt(l, fmt.Sprintf("Enter token amount to transfer in SCID (max TODO): ")) if amount_str == "" { amount_str = ".00001" } amount_to_transfer, err = globals.ParseAmount(amount_str) if err != nil { logger.Error(err, "Err parsing amount") break // invalid amount provided, bail out } if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") { tx, err := wallet.TransferPayload0([]rpc.Transfer{rpc.Transfer{SCID: scid, Amount: amount_to_transfer, Destination: a.String()}}, 0, false, rpc.Arguments{}, false) // empty SCDATA if err != nil { logger.Error(err, "Error while building Transaction") break } if err = wallet.SendTransaction(tx); err != nil { logger.Error(err, "Error while dispatching Transaction") break } logger.Info("Dispatched tx", "txid", tx.GetHash().String()) } case "5": if !valid_registration_or_display_error(l, wallet) { break } if !ValidateCurrentPassword(l, wallet) { logger.Error(fmt.Errorf("Invalid password"), "") break } // a , amount_to_transfer, err := collect_transfer_info(l,wallet) a, err := ReadAddress(l) if err != nil { logger.Error(err, "error reading address") break } var amount_to_transfer uint64 amount_str := read_line_with_prompt(l, fmt.Sprintf("Enter amount to transfer in SCID (max TODO): ")) if amount_str == "" { amount_str = ".00009" } amount_to_transfer, err = globals.ParseAmount(amount_str) if err != nil { logger.Error(err, "Err parsing amount") break // invalid amount provided, bail out } var arguments = rpc.Arguments{} if _, err := arguments.CheckPack(transaction.PAYLOAD0_LIMIT); err != nil { logger.Error(err, "Arguments packing err") return } if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") { //src_port := uint64(0xffffffffffffffff) tx, err := wallet.TransferPayload0([]rpc.Transfer{rpc.Transfer{Amount: amount_to_transfer, Destination: a.String(), Payload_RPC: arguments}}, 0, false, rpc.Arguments{}, false) // empty SCDATA if err != nil { logger.Error(err, "Error while building Transaction") break } if err = wallet.SendTransaction(tx); err != nil { logger.Error(err, "Error while dispatching Transaction") break } logger.Info("Dispatched tx", "txid", tx.GetHash().String()) //fmt.Printf("queued tx err %s\n") } case "12": if !valid_registration_or_display_error(l, wallet) { break } if !ValidateCurrentPassword(l, wallet) { logger.Error(fmt.Errorf("Invalid password"), "") break } logger.Error(err, "Not supported ") /* // 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 uid, err := wallet.PoolTransfer(addr_list, amount_list, fees_per_kb, 0, true) _ = uid if err != nil { globals.Logger.Warnf("Error while building Transaction err %s\n", err) break } } */ //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 { logger.Info("Wallet password successfully changed") } else { logger.Error(err, "Wallet password could not be changed") } } 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 logger.Info("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": var zeroscid crypto.Hash show_transfers(l, wallet, zeroscid, 100) case "14": logger.Info("Rescanning wallet history") rescan_bc(wallet) default: processed = false // just loop } return }