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