github.com/luckypickle/go-ethereum-vet@v1.14.2/signer/core/api_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 package core 17 18 import ( 19 "bytes" 20 "context" 21 "fmt" 22 "io/ioutil" 23 "math/big" 24 "os" 25 "path/filepath" 26 "testing" 27 "time" 28 29 "github.com/luckypickle/go-ethereum-vet/accounts/keystore" 30 "github.com/luckypickle/go-ethereum-vet/cmd/utils" 31 "github.com/luckypickle/go-ethereum-vet/common" 32 "github.com/luckypickle/go-ethereum-vet/common/hexutil" 33 "github.com/luckypickle/go-ethereum-vet/core/types" 34 "github.com/luckypickle/go-ethereum-vet/internal/ethapi" 35 "github.com/luckypickle/go-ethereum-vet/rlp" 36 ) 37 38 // Used for testing 39 type HeadlessUI struct { 40 controller chan string 41 } 42 43 func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) { 44 } 45 46 func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) { 47 fmt.Printf("OnApproved called") 48 } 49 50 func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { 51 52 switch <-ui.controller { 53 case "Y": 54 return SignTxResponse{request.Transaction, true, <-ui.controller}, nil 55 case "M": //Modify 56 old := big.Int(request.Transaction.Value) 57 newVal := big.NewInt(0).Add(&old, big.NewInt(1)) 58 request.Transaction.Value = hexutil.Big(*newVal) 59 return SignTxResponse{request.Transaction, true, <-ui.controller}, nil 60 default: 61 return SignTxResponse{request.Transaction, false, ""}, nil 62 } 63 } 64 func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { 65 if "Y" == <-ui.controller { 66 return SignDataResponse{true, <-ui.controller}, nil 67 } 68 return SignDataResponse{false, ""}, nil 69 } 70 func (ui *HeadlessUI) ApproveExport(request *ExportRequest) (ExportResponse, error) { 71 72 return ExportResponse{<-ui.controller == "Y"}, nil 73 74 } 75 func (ui *HeadlessUI) ApproveImport(request *ImportRequest) (ImportResponse, error) { 76 77 if "Y" == <-ui.controller { 78 return ImportResponse{true, <-ui.controller, <-ui.controller}, nil 79 } 80 return ImportResponse{false, "", ""}, nil 81 } 82 func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) { 83 84 switch <-ui.controller { 85 case "A": 86 return ListResponse{request.Accounts}, nil 87 case "1": 88 l := make([]Account, 1) 89 l[0] = request.Accounts[1] 90 return ListResponse{l}, nil 91 default: 92 return ListResponse{nil}, nil 93 } 94 } 95 func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { 96 97 if "Y" == <-ui.controller { 98 return NewAccountResponse{true, <-ui.controller}, nil 99 } 100 return NewAccountResponse{false, ""}, nil 101 } 102 func (ui *HeadlessUI) ShowError(message string) { 103 //stdout is used by communication 104 fmt.Fprint(os.Stderr, message) 105 } 106 func (ui *HeadlessUI) ShowInfo(message string) { 107 //stdout is used by communication 108 fmt.Fprint(os.Stderr, message) 109 } 110 111 func tmpDirName(t *testing.T) string { 112 d, err := ioutil.TempDir("", "eth-keystore-test") 113 if err != nil { 114 t.Fatal(err) 115 } 116 d, err = filepath.EvalSymlinks(d) 117 if err != nil { 118 t.Fatal(err) 119 } 120 return d 121 } 122 123 func setup(t *testing.T) (*SignerAPI, chan string) { 124 125 controller := make(chan string, 10) 126 127 db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json") 128 if err != nil { 129 utils.Fatalf(err.Error()) 130 } 131 var ( 132 ui = &HeadlessUI{controller} 133 api = NewSignerAPI( 134 1, 135 tmpDirName(t), 136 true, 137 ui, 138 db, 139 true) 140 ) 141 return api, controller 142 } 143 func createAccount(control chan string, api *SignerAPI, t *testing.T) { 144 145 control <- "Y" 146 control <- "apassword" 147 _, err := api.New(context.Background()) 148 if err != nil { 149 t.Fatal(err) 150 } 151 // Some time to allow changes to propagate 152 time.Sleep(250 * time.Millisecond) 153 } 154 func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) { 155 control <- "N" 156 acc, err := api.New(context.Background()) 157 if err != ErrRequestDenied { 158 t.Fatal(err) 159 } 160 if acc.Address != (common.Address{}) { 161 t.Fatal("Empty address should be returned") 162 } 163 } 164 func list(control chan string, api *SignerAPI, t *testing.T) []Account { 165 control <- "A" 166 list, err := api.List(context.Background()) 167 if err != nil { 168 t.Fatal(err) 169 } 170 return list 171 } 172 173 func TestNewAcc(t *testing.T) { 174 175 api, control := setup(t) 176 verifyNum := func(num int) { 177 if list := list(control, api, t); len(list) != num { 178 t.Errorf("Expected %d accounts, got %d", num, len(list)) 179 } 180 } 181 // Testing create and create-deny 182 createAccount(control, api, t) 183 createAccount(control, api, t) 184 failCreateAccount(control, api, t) 185 failCreateAccount(control, api, t) 186 createAccount(control, api, t) 187 failCreateAccount(control, api, t) 188 createAccount(control, api, t) 189 failCreateAccount(control, api, t) 190 verifyNum(4) 191 192 // Testing listing: 193 // Listing one Account 194 control <- "1" 195 list, err := api.List(context.Background()) 196 if err != nil { 197 t.Fatal(err) 198 } 199 if len(list) != 1 { 200 t.Fatalf("List should only show one Account") 201 } 202 // Listing denied 203 control <- "Nope" 204 list, err = api.List(context.Background()) 205 if len(list) != 0 { 206 t.Fatalf("List should be empty") 207 } 208 if err != ErrRequestDenied { 209 t.Fatal("Expected deny") 210 } 211 } 212 213 func TestSignData(t *testing.T) { 214 215 api, control := setup(t) 216 //Create two accounts 217 createAccount(control, api, t) 218 createAccount(control, api, t) 219 control <- "1" 220 list, err := api.List(context.Background()) 221 if err != nil { 222 t.Fatal(err) 223 } 224 a := common.NewMixedcaseAddress(list[0].Address) 225 226 control <- "Y" 227 control <- "wrongpassword" 228 h, err := api.Sign(context.Background(), a, []byte("EHLO world")) 229 if h != nil { 230 t.Errorf("Expected nil-data, got %x", h) 231 } 232 if err != keystore.ErrDecrypt { 233 t.Errorf("Expected ErrLocked! %v", err) 234 } 235 236 control <- "No way" 237 h, err = api.Sign(context.Background(), a, []byte("EHLO world")) 238 if h != nil { 239 t.Errorf("Expected nil-data, got %x", h) 240 } 241 if err != ErrRequestDenied { 242 t.Errorf("Expected ErrRequestDenied! %v", err) 243 } 244 245 control <- "Y" 246 control <- "apassword" 247 h, err = api.Sign(context.Background(), a, []byte("EHLO world")) 248 249 if err != nil { 250 t.Fatal(err) 251 } 252 if h == nil || len(h) != 65 { 253 t.Errorf("Expected 65 byte signature (got %d bytes)", len(h)) 254 } 255 } 256 func mkTestTx(from common.MixedcaseAddress) SendTxArgs { 257 to := common.NewMixedcaseAddress(common.HexToAddress("0x1337")) 258 gas := hexutil.Uint64(21000) 259 gasPrice := (hexutil.Big)(*big.NewInt(2000000000)) 260 value := (hexutil.Big)(*big.NewInt(1e18)) 261 nonce := (hexutil.Uint64)(0) 262 data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a")) 263 tx := SendTxArgs{ 264 From: from, 265 To: &to, 266 Gas: gas, 267 GasPrice: gasPrice, 268 Value: value, 269 Data: &data, 270 Nonce: nonce} 271 return tx 272 } 273 274 func TestSignTx(t *testing.T) { 275 276 var ( 277 list Accounts 278 res, res2 *ethapi.SignTransactionResult 279 err error 280 ) 281 282 api, control := setup(t) 283 createAccount(control, api, t) 284 control <- "A" 285 list, err = api.List(context.Background()) 286 if err != nil { 287 t.Fatal(err) 288 } 289 a := common.NewMixedcaseAddress(list[0].Address) 290 291 methodSig := "test(uint)" 292 tx := mkTestTx(a) 293 294 control <- "Y" 295 control <- "wrongpassword" 296 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 297 if res != nil { 298 t.Errorf("Expected nil-response, got %v", res) 299 } 300 if err != keystore.ErrDecrypt { 301 t.Errorf("Expected ErrLocked! %v", err) 302 } 303 304 control <- "No way" 305 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 306 if res != nil { 307 t.Errorf("Expected nil-response, got %v", res) 308 } 309 if err != ErrRequestDenied { 310 t.Errorf("Expected ErrRequestDenied! %v", err) 311 } 312 313 control <- "Y" 314 control <- "apassword" 315 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 316 317 if err != nil { 318 t.Fatal(err) 319 } 320 parsedTx := &types.Transaction{} 321 rlp.Decode(bytes.NewReader(res.Raw), parsedTx) 322 //The tx should NOT be modified by the UI 323 if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 { 324 t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value()) 325 } 326 control <- "Y" 327 control <- "apassword" 328 329 res2, err = api.SignTransaction(context.Background(), tx, &methodSig) 330 if err != nil { 331 t.Fatal(err) 332 } 333 if !bytes.Equal(res.Raw, res2.Raw) { 334 t.Error("Expected tx to be unmodified by UI") 335 } 336 337 //The tx is modified by the UI 338 control <- "M" 339 control <- "apassword" 340 341 res2, err = api.SignTransaction(context.Background(), tx, &methodSig) 342 if err != nil { 343 t.Fatal(err) 344 } 345 346 parsedTx2 := &types.Transaction{} 347 rlp.Decode(bytes.NewReader(res.Raw), parsedTx2) 348 //The tx should be modified by the UI 349 if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 { 350 t.Errorf("Expected value to be unchanged, got %v", parsedTx.Value()) 351 } 352 353 if bytes.Equal(res.Raw, res2.Raw) { 354 t.Error("Expected tx to be modified by UI") 355 } 356 357 } 358 359 /* 360 func TestAsyncronousResponses(t *testing.T){ 361 362 //Set up one account 363 api, control := setup(t) 364 createAccount(control, api, t) 365 366 // Two transactions, the second one with larger value than the first 367 tx1 := mkTestTx() 368 newVal := big.NewInt(0).Add((*big.Int) (tx1.Value), big.NewInt(1)) 369 tx2 := mkTestTx() 370 tx2.Value = (*hexutil.Big)(newVal) 371 372 control <- "W" //wait 373 control <- "Y" // 374 control <- "apassword" 375 control <- "Y" // 376 control <- "apassword" 377 378 var err error 379 380 h1, err := api.SignTransaction(context.Background(), common.HexToAddress("1111"), tx1, nil) 381 h2, err := api.SignTransaction(context.Background(), common.HexToAddress("2222"), tx2, nil) 382 383 384 } 385 */