github.com/codingfuture/orig-energi3@v0.8.4/signer/core/cliui.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2018 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 package core 19 20 import ( 21 "bufio" 22 "fmt" 23 "os" 24 "strings" 25 26 "sync" 27 28 "github.com/davecgh/go-spew/spew" 29 "github.com/ethereum/go-ethereum/common/hexutil" 30 "github.com/ethereum/go-ethereum/internal/ethapi" 31 "github.com/ethereum/go-ethereum/log" 32 "golang.org/x/crypto/ssh/terminal" 33 ) 34 35 type CommandlineUI struct { 36 in *bufio.Reader 37 mu sync.Mutex 38 } 39 40 func NewCommandlineUI() *CommandlineUI { 41 return &CommandlineUI{in: bufio.NewReader(os.Stdin)} 42 } 43 44 // readString reads a single line from stdin, trimming if from spaces, enforcing 45 // non-emptyness. 46 func (ui *CommandlineUI) readString() string { 47 for { 48 fmt.Printf("> ") 49 text, err := ui.in.ReadString('\n') 50 if err != nil { 51 log.Crit("Failed to read user input", "err", err) 52 } 53 if text = strings.TrimSpace(text); text != "" { 54 return text 55 } 56 } 57 } 58 59 // readPassword reads a single line from stdin, trimming it from the trailing new 60 // line and returns it. The input will not be echoed. 61 func (ui *CommandlineUI) readPassword() string { 62 fmt.Printf("Enter password to approve:\n") 63 fmt.Printf("> ") 64 65 text, err := terminal.ReadPassword(int(os.Stdin.Fd())) 66 if err != nil { 67 log.Crit("Failed to read password", "err", err) 68 } 69 fmt.Println() 70 fmt.Println("-----------------------") 71 return string(text) 72 } 73 74 // readPassword reads a single line from stdin, trimming it from the trailing new 75 // line and returns it. The input will not be echoed. 76 func (ui *CommandlineUI) readPasswordText(inputstring string) string { 77 fmt.Printf("Enter %s:\n", inputstring) 78 fmt.Printf("> ") 79 text, err := terminal.ReadPassword(int(os.Stdin.Fd())) 80 if err != nil { 81 log.Crit("Failed to read password", "err", err) 82 } 83 fmt.Println("-----------------------") 84 return string(text) 85 } 86 87 func (ui *CommandlineUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) { 88 fmt.Println(info.Title) 89 fmt.Println(info.Prompt) 90 if info.IsPassword { 91 text, err := terminal.ReadPassword(int(os.Stdin.Fd())) 92 if err != nil { 93 log.Error("Failed to read password", "err", err) 94 } 95 fmt.Println("-----------------------") 96 return UserInputResponse{string(text)}, err 97 } 98 text := ui.readString() 99 fmt.Println("-----------------------") 100 return UserInputResponse{text}, nil 101 } 102 103 // confirm returns true if user enters 'Yes', otherwise false 104 func (ui *CommandlineUI) confirm() bool { 105 fmt.Printf("Approve? [y/N]:\n") 106 if ui.readString() == "y" { 107 return true 108 } 109 fmt.Println("-----------------------") 110 return false 111 } 112 113 func showMetadata(metadata Metadata) { 114 fmt.Printf("Request context:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local) 115 fmt.Printf("\nAdditional HTTP header data, provided by the external caller:\n") 116 fmt.Printf("\tUser-Agent: %v\n\tOrigin: %v\n", metadata.UserAgent, metadata.Origin) 117 } 118 119 // ApproveTx prompt the user for confirmation to request to sign Transaction 120 func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { 121 ui.mu.Lock() 122 defer ui.mu.Unlock() 123 weival := request.Transaction.Value.ToInt() 124 fmt.Printf("--------- Transaction request-------------\n") 125 if to := request.Transaction.To; to != nil { 126 fmt.Printf("to: %v\n", to.Original()) 127 if !to.ValidChecksum() { 128 fmt.Printf("\nWARNING: Invalid checksum on to-address!\n\n") 129 } 130 } else { 131 fmt.Printf("to: <contact creation>\n") 132 } 133 fmt.Printf("from: %v\n", request.Transaction.From.String()) 134 fmt.Printf("value: %v wei\n", weival) 135 fmt.Printf("gas: %v (%v)\n", request.Transaction.Gas, uint64(request.Transaction.Gas)) 136 fmt.Printf("gasprice: %v wei\n", request.Transaction.GasPrice.ToInt()) 137 fmt.Printf("nonce: %v (%v)\n", request.Transaction.Nonce, uint64(request.Transaction.Nonce)) 138 if request.Transaction.Data != nil { 139 d := *request.Transaction.Data 140 if len(d) > 0 { 141 142 fmt.Printf("data: %v\n", hexutil.Encode(d)) 143 } 144 } 145 if request.Callinfo != nil { 146 fmt.Printf("\nTransaction validation:\n") 147 for _, m := range request.Callinfo { 148 fmt.Printf(" * %s : %s\n", m.Typ, m.Message) 149 } 150 fmt.Println() 151 152 } 153 fmt.Printf("\n") 154 showMetadata(request.Meta) 155 fmt.Printf("-------------------------------------------\n") 156 if !ui.confirm() { 157 return SignTxResponse{request.Transaction, false, ""}, nil 158 } 159 return SignTxResponse{request.Transaction, true, ui.readPassword()}, nil 160 } 161 162 // ApproveSignData prompt the user for confirmation to request to sign data 163 func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { 164 ui.mu.Lock() 165 defer ui.mu.Unlock() 166 167 fmt.Printf("-------- Sign data request--------------\n") 168 fmt.Printf("Account: %s\n", request.Address.String()) 169 fmt.Printf("message: \n%q\n", request.Message) 170 fmt.Printf("raw data: \n%v\n", request.Rawdata) 171 fmt.Printf("message hash: %v\n", request.Hash) 172 fmt.Printf("-------------------------------------------\n") 173 showMetadata(request.Meta) 174 if !ui.confirm() { 175 return SignDataResponse{false, ""}, nil 176 } 177 return SignDataResponse{true, ui.readPassword()}, nil 178 } 179 180 // ApproveExport prompt the user for confirmation to export encrypted Account json 181 func (ui *CommandlineUI) ApproveExport(request *ExportRequest) (ExportResponse, error) { 182 ui.mu.Lock() 183 defer ui.mu.Unlock() 184 185 fmt.Printf("-------- Export Account request--------------\n") 186 fmt.Printf("A request has been made to export the (encrypted) keyfile\n") 187 fmt.Printf("Approving this operation means that the caller obtains the (encrypted) contents\n") 188 fmt.Printf("\n") 189 fmt.Printf("Account: %x\n", request.Address) 190 //fmt.Printf("keyfile: \n%v\n", request.file) 191 fmt.Printf("-------------------------------------------\n") 192 showMetadata(request.Meta) 193 return ExportResponse{ui.confirm()}, nil 194 } 195 196 // ApproveImport prompt the user for confirmation to import Account json 197 func (ui *CommandlineUI) ApproveImport(request *ImportRequest) (ImportResponse, error) { 198 ui.mu.Lock() 199 defer ui.mu.Unlock() 200 201 fmt.Printf("-------- Import Account request--------------\n") 202 fmt.Printf("A request has been made to import an encrypted keyfile\n") 203 fmt.Printf("-------------------------------------------\n") 204 showMetadata(request.Meta) 205 if !ui.confirm() { 206 return ImportResponse{false, "", ""}, nil 207 } 208 return ImportResponse{true, ui.readPasswordText("Old password"), ui.readPasswordText("New password")}, nil 209 } 210 211 // ApproveListing prompt the user for confirmation to list accounts 212 // the list of accounts to list can be modified by the UI 213 func (ui *CommandlineUI) ApproveListing(request *ListRequest) (ListResponse, error) { 214 215 ui.mu.Lock() 216 defer ui.mu.Unlock() 217 218 fmt.Printf("-------- List Account request--------------\n") 219 fmt.Printf("A request has been made to list all accounts. \n") 220 fmt.Printf("You can select which accounts the caller can see\n") 221 for _, account := range request.Accounts { 222 fmt.Printf(" [x] %v\n", account.Address.Hex()) 223 fmt.Printf(" URL: %v\n", account.URL) 224 fmt.Printf(" Type: %v\n", account.Typ) 225 } 226 fmt.Printf("-------------------------------------------\n") 227 showMetadata(request.Meta) 228 if !ui.confirm() { 229 return ListResponse{nil}, nil 230 } 231 return ListResponse{request.Accounts}, nil 232 } 233 234 // ApproveNewAccount prompt the user for confirmation to create new Account, and reveal to caller 235 func (ui *CommandlineUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { 236 237 ui.mu.Lock() 238 defer ui.mu.Unlock() 239 240 fmt.Printf("-------- New Account request--------------\n\n") 241 fmt.Printf("A request has been made to create a new account. \n") 242 fmt.Printf("Approving this operation means that a new account is created,\n") 243 fmt.Printf("and the address is returned to the external caller\n\n") 244 showMetadata(request.Meta) 245 if !ui.confirm() { 246 return NewAccountResponse{false, ""}, nil 247 } 248 return NewAccountResponse{true, ui.readPassword()}, nil 249 } 250 251 // ShowError displays error message to user 252 func (ui *CommandlineUI) ShowError(message string) { 253 fmt.Printf("-------- Error message from Clef-----------\n") 254 fmt.Println(message) 255 fmt.Printf("-------------------------------------------\n") 256 } 257 258 // ShowInfo displays info message to user 259 func (ui *CommandlineUI) ShowInfo(message string) { 260 fmt.Printf("Info: %v\n", message) 261 } 262 263 func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) { 264 fmt.Printf("Transaction signed:\n ") 265 spew.Dump(tx.Tx) 266 } 267 268 func (ui *CommandlineUI) OnSignerStartup(info StartupInfo) { 269 270 fmt.Printf("------- Signer info -------\n") 271 for k, v := range info.Info { 272 fmt.Printf("* %v : %v\n", k, v) 273 } 274 }