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