github.com/daeglee/go-ethereum@v0.0.0-20190504220456-cad3e8d18e9b/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 // 17 package core 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "io/ioutil" 24 "math/big" 25 "os" 26 "path/filepath" 27 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum/accounts" 31 "github.com/ethereum/go-ethereum/accounts/keystore" 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/common/hexutil" 34 "github.com/ethereum/go-ethereum/core/types" 35 "github.com/ethereum/go-ethereum/internal/ethapi" 36 "github.com/ethereum/go-ethereum/rlp" 37 "github.com/ethereum/go-ethereum/signer/storage" 38 ) 39 40 //Used for testing 41 type headlessUi struct { 42 approveCh chan string // to send approve/deny 43 inputCh chan string // to send password 44 } 45 46 func (ui *headlessUi) OnInputRequired(info UserInputRequest) (UserInputResponse, error) { 47 input := <-ui.inputCh 48 return UserInputResponse{Text: input}, nil 49 } 50 51 func (ui *headlessUi) OnSignerStartup(info StartupInfo) {} 52 func (ui *headlessUi) RegisterUIServer(api *UIServerAPI) {} 53 func (ui *headlessUi) OnApprovedTx(tx ethapi.SignTransactionResult) {} 54 55 func (ui *headlessUi) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { 56 57 switch <-ui.approveCh { 58 case "Y": 59 return SignTxResponse{request.Transaction, true}, nil 60 case "M": // modify 61 // The headless UI always modifies the transaction 62 old := big.Int(request.Transaction.Value) 63 newVal := big.NewInt(0).Add(&old, big.NewInt(1)) 64 request.Transaction.Value = hexutil.Big(*newVal) 65 return SignTxResponse{request.Transaction, true}, nil 66 default: 67 return SignTxResponse{request.Transaction, false}, nil 68 } 69 } 70 71 func (ui *headlessUi) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { 72 approved := "Y" == <-ui.approveCh 73 return SignDataResponse{approved}, nil 74 } 75 76 func (ui *headlessUi) ApproveListing(request *ListRequest) (ListResponse, error) { 77 approval := <-ui.approveCh 78 //fmt.Printf("approval %s\n", approval) 79 switch approval { 80 case "A": 81 return ListResponse{request.Accounts}, nil 82 case "1": 83 l := make([]accounts.Account, 1) 84 l[0] = request.Accounts[1] 85 return ListResponse{l}, nil 86 default: 87 return ListResponse{nil}, nil 88 } 89 } 90 91 func (ui *headlessUi) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { 92 if "Y" == <-ui.approveCh { 93 return NewAccountResponse{true}, nil 94 } 95 return NewAccountResponse{false}, nil 96 } 97 98 func (ui *headlessUi) ShowError(message string) { 99 //stdout is used by communication 100 fmt.Fprintln(os.Stderr, message) 101 } 102 103 func (ui *headlessUi) ShowInfo(message string) { 104 //stdout is used by communication 105 fmt.Fprintln(os.Stderr, message) 106 } 107 108 func tmpDirName(t *testing.T) string { 109 d, err := ioutil.TempDir("", "eth-keystore-test") 110 if err != nil { 111 t.Fatal(err) 112 } 113 d, err = filepath.EvalSymlinks(d) 114 if err != nil { 115 t.Fatal(err) 116 } 117 return d 118 } 119 120 func setup(t *testing.T) (*SignerAPI, *headlessUi) { 121 db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json") 122 if err != nil { 123 t.Fatal(err.Error()) 124 } 125 ui := &headlessUi{make(chan string, 20), make(chan string, 20)} 126 am := StartClefAccountManager(tmpDirName(t), true, true) 127 api := NewSignerAPI(am, 1337, true, ui, db, true, &storage.NoStorage{}) 128 return api, ui 129 130 } 131 func createAccount(ui *headlessUi, api *SignerAPI, t *testing.T) { 132 ui.approveCh <- "Y" 133 ui.inputCh <- "a_long_password" 134 _, err := api.New(context.Background()) 135 if err != nil { 136 t.Fatal(err) 137 } 138 // Some time to allow changes to propagate 139 time.Sleep(250 * time.Millisecond) 140 } 141 142 func failCreateAccountWithPassword(ui *headlessUi, api *SignerAPI, password string, t *testing.T) { 143 144 ui.approveCh <- "Y" 145 // We will be asked three times to provide a suitable password 146 ui.inputCh <- password 147 ui.inputCh <- password 148 ui.inputCh <- password 149 150 addr, err := api.New(context.Background()) 151 if err == nil { 152 t.Fatal("Should have returned an error") 153 } 154 if addr != (common.Address{}) { 155 t.Fatal("Empty address should be returned") 156 } 157 } 158 159 func failCreateAccount(ui *headlessUi, api *SignerAPI, t *testing.T) { 160 ui.approveCh <- "N" 161 addr, err := api.New(context.Background()) 162 if err != ErrRequestDenied { 163 t.Fatal(err) 164 } 165 if addr != (common.Address{}) { 166 t.Fatal("Empty address should be returned") 167 } 168 } 169 170 func list(ui *headlessUi, api *SignerAPI, t *testing.T) ([]common.Address, error) { 171 ui.approveCh <- "A" 172 return api.List(context.Background()) 173 174 } 175 176 func TestNewAcc(t *testing.T) { 177 api, control := setup(t) 178 verifyNum := func(num int) { 179 list, err := list(control, api, t) 180 if err != nil { 181 t.Errorf("Unexpected error %v", err) 182 } 183 if len(list) != num { 184 t.Errorf("Expected %d accounts, got %d", num, len(list)) 185 } 186 } 187 // Testing create and create-deny 188 createAccount(control, api, t) 189 createAccount(control, api, t) 190 failCreateAccount(control, api, t) 191 failCreateAccount(control, api, t) 192 createAccount(control, api, t) 193 failCreateAccount(control, api, t) 194 createAccount(control, api, t) 195 failCreateAccount(control, api, t) 196 verifyNum(4) 197 198 // Fail to create this, due to bad password 199 failCreateAccountWithPassword(control, api, "short", t) 200 failCreateAccountWithPassword(control, api, "longerbutbad\rfoo", t) 201 verifyNum(4) 202 203 // Testing listing: 204 // Listing one Account 205 control.approveCh <- "1" 206 list, err := api.List(context.Background()) 207 if err != nil { 208 t.Fatal(err) 209 } 210 if len(list) != 1 { 211 t.Fatalf("List should only show one Account") 212 } 213 // Listing denied 214 control.approveCh <- "Nope" 215 list, err = api.List(context.Background()) 216 if len(list) != 0 { 217 t.Fatalf("List should be empty") 218 } 219 if err != ErrRequestDenied { 220 t.Fatal("Expected deny") 221 } 222 } 223 224 func mkTestTx(from common.MixedcaseAddress) SendTxArgs { 225 to := common.NewMixedcaseAddress(common.HexToAddress("0x1337")) 226 gas := hexutil.Uint64(21000) 227 gasPrice := (hexutil.Big)(*big.NewInt(2000000000)) 228 value := (hexutil.Big)(*big.NewInt(1e18)) 229 nonce := (hexutil.Uint64)(0) 230 data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a")) 231 tx := SendTxArgs{ 232 From: from, 233 To: &to, 234 Gas: gas, 235 GasPrice: gasPrice, 236 Value: value, 237 Data: &data, 238 Nonce: nonce} 239 return tx 240 } 241 242 func TestSignTx(t *testing.T) { 243 var ( 244 list []common.Address 245 res, res2 *ethapi.SignTransactionResult 246 err error 247 ) 248 249 api, control := setup(t) 250 createAccount(control, api, t) 251 control.approveCh <- "A" 252 list, err = api.List(context.Background()) 253 if err != nil { 254 t.Fatal(err) 255 } 256 a := common.NewMixedcaseAddress(list[0]) 257 258 methodSig := "test(uint)" 259 tx := mkTestTx(a) 260 261 control.approveCh <- "Y" 262 control.inputCh <- "wrongpassword" 263 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 264 if res != nil { 265 t.Errorf("Expected nil-response, got %v", res) 266 } 267 if err != keystore.ErrDecrypt { 268 t.Errorf("Expected ErrLocked! %v", err) 269 } 270 control.approveCh <- "No way" 271 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 272 if res != nil { 273 t.Errorf("Expected nil-response, got %v", res) 274 } 275 if err != ErrRequestDenied { 276 t.Errorf("Expected ErrRequestDenied! %v", err) 277 } 278 // Sign with correct password 279 control.approveCh <- "Y" 280 control.inputCh <- "a_long_password" 281 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 282 283 if err != nil { 284 t.Fatal(err) 285 } 286 parsedTx := &types.Transaction{} 287 rlp.Decode(bytes.NewReader(res.Raw), parsedTx) 288 289 //The tx should NOT be modified by the UI 290 if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 { 291 t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value()) 292 } 293 control.approveCh <- "Y" 294 control.inputCh <- "a_long_password" 295 296 res2, err = api.SignTransaction(context.Background(), tx, &methodSig) 297 if err != nil { 298 t.Fatal(err) 299 } 300 if !bytes.Equal(res.Raw, res2.Raw) { 301 t.Error("Expected tx to be unmodified by UI") 302 } 303 304 //The tx is modified by the UI 305 control.approveCh <- "M" 306 control.inputCh <- "a_long_password" 307 308 res2, err = api.SignTransaction(context.Background(), tx, &methodSig) 309 if err != nil { 310 t.Fatal(err) 311 } 312 parsedTx2 := &types.Transaction{} 313 rlp.Decode(bytes.NewReader(res.Raw), parsedTx2) 314 315 //The tx should be modified by the UI 316 if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 { 317 t.Errorf("Expected value to be unchanged, got %v", parsedTx.Value()) 318 } 319 if bytes.Equal(res.Raw, res2.Raw) { 320 t.Error("Expected tx to be modified by UI") 321 } 322 323 }