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 }