derohe-miniblock-mod/cmd/dero-wallet-cli/easymenu_post_open.go
2021-12-04 16:42:11 +00:00

439 lines
15 KiB
Go

// 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())
fmt.Fprintf(l.Stderr(), "Registration TXID %s\n", reg_tx.GetHash())
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, wallet)
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, wallet)
if err != nil {
logger.Error(err, "error reading address")
break
}
var amount_to_transfer uint64
var arguments = rpc.Arguments{
// { rpc.RPC_DESTINATION_PORT, rpc.DataUint64,uint64(0x1234567812345678)},
// { rpc.RPC_VALUE_TRANSFER, rpc.DataUint64,uint64(12345)},
// { rpc.RPC_EXPIRY , rpc.DataTime, time.Now().Add(time.Hour).UTC()},
// { rpc.RPC_COMMENT , rpc.DataString, "Purchase XYZ"},
}
if a.IsIntegratedAddress() { // read everything from the address
if a.Arguments.Validate_Arguments() != nil {
logger.Error(err, "Integrated Address arguments could not be validated.")
break
}
if !a.Arguments.Has(rpc.RPC_DESTINATION_PORT, rpc.DataUint64) { // but only it is present
logger.Error(fmt.Errorf("Integrated Address does not contain destination port."), "")
break
}
arguments = append(arguments, rpc.Argument{Name: rpc.RPC_DESTINATION_PORT, DataType: rpc.DataUint64, Value: a.Arguments.Value(rpc.RPC_DESTINATION_PORT, rpc.DataUint64).(uint64)})
// arguments = append(arguments, rpc.Argument{"Comment", rpc.DataString, "holygrail of all data is now working if you can see this"})
if a.Arguments.Has(rpc.RPC_EXPIRY, rpc.DataTime) { // but only it is present
if a.Arguments.Value(rpc.RPC_EXPIRY, rpc.DataTime).(time.Time).Before(time.Now().UTC()) {
logger.Error(nil, "This address has expired.", "expiry time", a.Arguments.Value(rpc.RPC_EXPIRY, rpc.DataTime))
break
} else {
logger.Info("This address will expire ", "expiry time", a.Arguments.Value(rpc.RPC_EXPIRY, rpc.DataTime))
}
}
logger.Info("Destination port is integreted in address.", "dst port", a.Arguments.Value(rpc.RPC_DESTINATION_PORT, rpc.DataUint64).(uint64))
if a.Arguments.Has(rpc.RPC_COMMENT, rpc.DataString) { // but only it is present
logger.Info("Integrated Message", "comment", a.Arguments.Value(rpc.RPC_COMMENT, rpc.DataString))
}
}
// arguments have been already validated
for _, arg := range a.Arguments {
if !(arg.Name == rpc.RPC_COMMENT || arg.Name == rpc.RPC_EXPIRY || arg.Name == rpc.RPC_DESTINATION_PORT || arg.Name == rpc.RPC_SOURCE_PORT || arg.Name == rpc.RPC_VALUE_TRANSFER || arg.Name == rpc.RPC_NEEDS_REPLYBACK_ADDRESS) {
switch arg.DataType {
case rpc.DataString:
if v, err := ReadString(l, arg.Name, arg.Value.(string)); err == nil {
arguments = append(arguments, rpc.Argument{Name: arg.Name, DataType: arg.DataType, Value: v})
} else {
logger.Error(fmt.Errorf("%s could not be parsed (type %s),", arg.Name, arg.DataType), "")
return
}
case rpc.DataInt64:
if v, err := ReadInt64(l, arg.Name, arg.Value.(int64)); err == nil {
arguments = append(arguments, rpc.Argument{Name: arg.Name, DataType: arg.DataType, Value: v})
} else {
logger.Error(fmt.Errorf("%s could not be parsed (type %s),", arg.Name, arg.DataType), "")
return
}
case rpc.DataUint64:
if v, err := ReadUint64(l, arg.Name, arg.Value.(uint64)); err == nil {
arguments = append(arguments, rpc.Argument{Name: arg.Name, DataType: arg.DataType, Value: v})
} else {
logger.Error(fmt.Errorf("%s could not be parsed (type %s),", arg.Name, arg.DataType), "")
return
}
case rpc.DataFloat64:
if v, err := ReadFloat64(l, arg.Name, arg.Value.(float64)); err == nil {
arguments = append(arguments, rpc.Argument{Name: arg.Name, DataType: arg.DataType, Value: v})
} else {
logger.Error(fmt.Errorf("%s could not be parsed (type %s),", arg.Name, arg.DataType), "")
return
}
case rpc.DataTime:
logger.Error(fmt.Errorf("time argument is currently not supported."), "")
break
}
}
}
if a.Arguments.Has(rpc.RPC_VALUE_TRANSFER, rpc.DataUint64) { // but only it is present
logger.Info("Transaction", "Value", globals.FormatMoney(a.Arguments.Value(rpc.RPC_VALUE_TRANSFER, rpc.DataUint64).(uint64)))
amount_to_transfer = a.Arguments.Value(rpc.RPC_VALUE_TRANSFER, rpc.DataUint64).(uint64)
} else {
amount_str := read_line_with_prompt(l, fmt.Sprintf("Enter amount to transfer in DERO (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
}
}
// check whether the service needs the address of sender
// this is required to enable services which are completely invisisble to external entities
// external entities means anyone except sender/receiver
if a.Arguments.Has(rpc.RPC_NEEDS_REPLYBACK_ADDRESS, rpc.DataUint64) {
logger.Info("This RPC has requested your address.")
logger.Info("If you are expecting something back, it needs to be sent")
logger.Info("Your address will remain completely invisible to external entities(only sender/receiver can see your address)")
arguments = append(arguments, rpc.Argument{Name: rpc.RPC_REPLYBACK_ADDRESS, DataType: rpc.DataAddress, Value: wallet.GetAddress()})
}
// if no arguments, use space by embedding a small comment
if len(arguments) == 0 { // allow user to enter Comment
if v, err := ReadString(l, "Comment", ""); err == nil {
arguments = append(arguments, rpc.Argument{Name: "Comment", DataType: rpc.DataString, Value: v})
} else {
logger.Error(fmt.Errorf("%s could not be parsed (type %s),", "Comment", rpc.DataString), "")
return
}
}
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
}