github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/signer/core/cliui.go (about)

     1  package core
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"sync"
    10  
    11  	"github.com/davecgh/go-spew/spew"
    12  	"github.com/quickchainproject/quickchain/common"
    13  	"github.com/quickchainproject/quickchain/internal/qctapi"
    14  	"github.com/quickchainproject/quickchain/log"
    15  	"golang.org/x/crypto/ssh/terminal"
    16  )
    17  
    18  type CommandlineUI struct {
    19  	in *bufio.Reader
    20  	mu sync.Mutex
    21  }
    22  
    23  func NewCommandlineUI() *CommandlineUI {
    24  	return &CommandlineUI{in: bufio.NewReader(os.Stdin)}
    25  }
    26  
    27  // readString reads a single line from stdin, trimming if from spaces, enforcing
    28  // non-emptyness.
    29  func (ui *CommandlineUI) readString() string {
    30  	for {
    31  		fmt.Printf("> ")
    32  		text, err := ui.in.ReadString('\n')
    33  		if err != nil {
    34  			log.Crit("Failed to read user input", "err", err)
    35  		}
    36  		if text = strings.TrimSpace(text); text != "" {
    37  			return text
    38  		}
    39  	}
    40  }
    41  
    42  // readPassword reads a single line from stdin, trimming it from the trailing new
    43  // line and returns it. The input will not be echoed.
    44  func (ui *CommandlineUI) readPassword() string {
    45  	fmt.Printf("Enter password to approve:\n")
    46  	fmt.Printf("> ")
    47  
    48  	text, err := terminal.ReadPassword(int(os.Stdin.Fd()))
    49  	if err != nil {
    50  		log.Crit("Failed to read password", "err", err)
    51  	}
    52  	fmt.Println()
    53  	fmt.Println("-----------------------")
    54  	return string(text)
    55  }
    56  
    57  // readPassword reads a single line from stdin, trimming it from the trailing new
    58  // line and returns it. The input will not be echoed.
    59  func (ui *CommandlineUI) readPasswordText(inputstring string) string {
    60  	fmt.Printf("Enter %s:\n", inputstring)
    61  	fmt.Printf("> ")
    62  	text, err := terminal.ReadPassword(int(os.Stdin.Fd()))
    63  	if err != nil {
    64  		log.Crit("Failed to read password", "err", err)
    65  	}
    66  	fmt.Println("-----------------------")
    67  	return string(text)
    68  }
    69  
    70  // confirm returns true if user enters 'Yes', otherwise false
    71  func (ui *CommandlineUI) confirm() bool {
    72  	fmt.Printf("Approve? [y/N]:\n")
    73  	if ui.readString() == "y" {
    74  		return true
    75  	}
    76  	fmt.Println("-----------------------")
    77  	return false
    78  }
    79  
    80  func showMetadata(metadata Metadata) {
    81  	fmt.Printf("Request context:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local)
    82  }
    83  
    84  // ApproveTx prompt the user for confirmation to request to sign Transaction
    85  func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
    86  	ui.mu.Lock()
    87  	defer ui.mu.Unlock()
    88  	weival := request.Transaction.Value.ToInt()
    89  	fmt.Printf("--------- Transaction request-------------\n")
    90  	if to := request.Transaction.To; to != nil {
    91  		fmt.Printf("to:    %v\n", to.Original())
    92  		if !to.ValidChecksum() {
    93  			fmt.Printf("\nWARNING: Invalid checksum on to-address!\n\n")
    94  		}
    95  	} else {
    96  		fmt.Printf("to:    <contact creation>\n")
    97  	}
    98  	fmt.Printf("from:  %v\n", request.Transaction.From.String())
    99  	fmt.Printf("value: %v wei\n", weival)
   100  	if request.Transaction.Data != nil {
   101  		d := *request.Transaction.Data
   102  		if len(d) > 0 {
   103  			fmt.Printf("data:  %v\n", common.Bytes2Hex(d))
   104  		}
   105  	}
   106  	if request.Callinfo != nil {
   107  		fmt.Printf("\nTransaction validation:\n")
   108  		for _, m := range request.Callinfo {
   109  			fmt.Printf("  * %s : %s", m.Typ, m.Message)
   110  		}
   111  		fmt.Println()
   112  
   113  	}
   114  	fmt.Printf("\n")
   115  	showMetadata(request.Meta)
   116  	fmt.Printf("-------------------------------------------\n")
   117  	if !ui.confirm() {
   118  		return SignTxResponse{request.Transaction, false, ""}, nil
   119  	}
   120  	return SignTxResponse{request.Transaction, true, ui.readPassword()}, nil
   121  }
   122  
   123  // ApproveSignData prompt the user for confirmation to request to sign data
   124  func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
   125  	ui.mu.Lock()
   126  	defer ui.mu.Unlock()
   127  
   128  	fmt.Printf("-------- Sign data request--------------\n")
   129  	fmt.Printf("Account:  %s\n", request.Address.String())
   130  	fmt.Printf("message:  \n%q\n", request.Message)
   131  	fmt.Printf("raw data: \n%v\n", request.Rawdata)
   132  	fmt.Printf("message hash:  %v\n", request.Hash)
   133  	fmt.Printf("-------------------------------------------\n")
   134  	showMetadata(request.Meta)
   135  	if !ui.confirm() {
   136  		return SignDataResponse{false, ""}, nil
   137  	}
   138  	return SignDataResponse{true, ui.readPassword()}, nil
   139  }
   140  
   141  // ApproveExport prompt the user for confirmation to export encrypted Account json
   142  func (ui *CommandlineUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
   143  	ui.mu.Lock()
   144  	defer ui.mu.Unlock()
   145  
   146  	fmt.Printf("-------- Export Account request--------------\n")
   147  	fmt.Printf("A request has been made to export the (encrypted) keyfile\n")
   148  	fmt.Printf("Approving this operation means that the caller obtains the (encrypted) contents\n")
   149  	fmt.Printf("\n")
   150  	fmt.Printf("Account:  %x\n", request.Address)
   151  	//fmt.Printf("keyfile:  \n%v\n", request.file)
   152  	fmt.Printf("-------------------------------------------\n")
   153  	showMetadata(request.Meta)
   154  	return ExportResponse{ui.confirm()}, nil
   155  }
   156  
   157  // ApproveImport prompt the user for confirmation to import Account json
   158  func (ui *CommandlineUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
   159  	ui.mu.Lock()
   160  	defer ui.mu.Unlock()
   161  
   162  	fmt.Printf("-------- Import Account request--------------\n")
   163  	fmt.Printf("A request has been made to import an encrypted keyfile\n")
   164  	fmt.Printf("-------------------------------------------\n")
   165  	showMetadata(request.Meta)
   166  	if !ui.confirm() {
   167  		return ImportResponse{false, "", ""}, nil
   168  	}
   169  	return ImportResponse{true, ui.readPasswordText("Old password"), ui.readPasswordText("New password")}, nil
   170  }
   171  
   172  // ApproveListing prompt the user for confirmation to list accounts
   173  // the list of accounts to list can be modified by the UI
   174  func (ui *CommandlineUI) ApproveListing(request *ListRequest) (ListResponse, error) {
   175  
   176  	ui.mu.Lock()
   177  	defer ui.mu.Unlock()
   178  
   179  	fmt.Printf("-------- List Account request--------------\n")
   180  	fmt.Printf("A request has been made to list all accounts. \n")
   181  	fmt.Printf("You can select which accounts the caller can see\n")
   182  	for _, account := range request.Accounts {
   183  		fmt.Printf("\t[x] %v\n", account.Address.Hex())
   184  	}
   185  	fmt.Printf("-------------------------------------------\n")
   186  	showMetadata(request.Meta)
   187  	if !ui.confirm() {
   188  		return ListResponse{nil}, nil
   189  	}
   190  	return ListResponse{request.Accounts}, nil
   191  }
   192  
   193  // ApproveNewAccount prompt the user for confirmation to create new Account, and reveal to caller
   194  func (ui *CommandlineUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
   195  
   196  	ui.mu.Lock()
   197  	defer ui.mu.Unlock()
   198  
   199  	fmt.Printf("-------- New Account request--------------\n")
   200  	fmt.Printf("A request has been made to create a new. \n")
   201  	fmt.Printf("Approving this operation means that a new Account is created,\n")
   202  	fmt.Printf("and the address show to the caller\n")
   203  	showMetadata(request.Meta)
   204  	if !ui.confirm() {
   205  		return NewAccountResponse{false, ""}, nil
   206  	}
   207  	return NewAccountResponse{true, ui.readPassword()}, nil
   208  }
   209  
   210  // ShowError displays error message to user
   211  func (ui *CommandlineUI) ShowError(message string) {
   212  
   213  	fmt.Printf("ERROR: %v\n", message)
   214  }
   215  
   216  // ShowInfo displays info message to user
   217  func (ui *CommandlineUI) ShowInfo(message string) {
   218  	fmt.Printf("Info: %v\n", message)
   219  }
   220  
   221  func (ui *CommandlineUI) OnApprovedTx(tx qctapi.SignTransactionResult) {
   222  	fmt.Printf("Transaction signed:\n ")
   223  	spew.Dump(tx.Tx)
   224  }
   225  
   226  func (ui *CommandlineUI) OnSignerStartup(info StartupInfo) {
   227  
   228  	fmt.Printf("------- Signer info -------\n")
   229  	for k, v := range info.Info {
   230  		fmt.Printf("* %v : %v\n", k, v)
   231  	}
   232  }