github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/signer/rules/rules_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:42</date> 10 //</624450110952050688> 11 12 // 13 package rules 14 15 import ( 16 "fmt" 17 "math/big" 18 "strings" 19 "testing" 20 21 "github.com/ethereum/go-ethereum/accounts" 22 "github.com/ethereum/go-ethereum/common" 23 "github.com/ethereum/go-ethereum/common/hexutil" 24 "github.com/ethereum/go-ethereum/core/types" 25 "github.com/ethereum/go-ethereum/internal/ethapi" 26 "github.com/ethereum/go-ethereum/signer/core" 27 "github.com/ethereum/go-ethereum/signer/storage" 28 ) 29 30 const JS = ` 31 /* 32 这是一个JavaScript规则文件的示例实现。 33 34 当签名者通过外部API接收到请求时,将计算相应的方法。 35 可能发生三件事: 36 37 1。方法返回“批准”。这意味着操作是允许的。 38 2。方法返回“reject”。这意味着操作被拒绝。 39 三。其他任何内容;其他返回值[*],方法未实现或在处理过程中发生异常。这意味着 40 操作将继续通过用户选择的常规UI方法进行手动处理。 41 42 [*]注意:未来版本的规则集可能会使用更复杂的基于JSON的返回值,因此可能不会 43 只响应批准/拒绝/手动,但也修改响应。例如,选择只列出一个,而不是全部 44 列表请求中的帐户。以上几点将继续适用于非基于JSON的响应(“批准”/“拒绝”)。 45 46 */ 47 48 49 function ApproveListing(request){ 50 console.log("In js approve listing"); 51 console.log(request.accounts[3].Address) 52 console.log(request.meta.Remote) 53 return "Approve" 54 } 55 56 function ApproveTx(request){ 57 console.log("test"); 58 console.log("from"); 59 return "Reject"; 60 } 61 62 function test(thing){ 63 console.log(thing.String()) 64 } 65 66 ` 67 68 func mixAddr(a string) (*common.MixedcaseAddress, error) { 69 return common.NewMixedcaseAddressFromString(a) 70 } 71 72 type alwaysDenyUI struct{} 73 74 func (alwaysDenyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) { 75 return core.UserInputResponse{}, nil 76 } 77 78 func (alwaysDenyUI) OnSignerStartup(info core.StartupInfo) { 79 } 80 81 func (alwaysDenyUI) OnMasterPassword(request *core.PasswordRequest) (core.PasswordResponse, error) { 82 return core.PasswordResponse{}, nil 83 } 84 85 func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { 86 return core.SignTxResponse{Transaction: request.Transaction, Approved: false, Password: ""}, nil 87 } 88 89 func (alwaysDenyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { 90 return core.SignDataResponse{Approved: false, Password: ""}, nil 91 } 92 93 func (alwaysDenyUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { 94 return core.ExportResponse{Approved: false}, nil 95 } 96 97 func (alwaysDenyUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) { 98 return core.ImportResponse{Approved: false, OldPassword: "", NewPassword: ""}, nil 99 } 100 101 func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { 102 return core.ListResponse{Accounts: nil}, nil 103 } 104 105 func (alwaysDenyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { 106 return core.NewAccountResponse{Approved: false, Password: ""}, nil 107 } 108 109 func (alwaysDenyUI) ShowError(message string) { 110 panic("implement me") 111 } 112 113 func (alwaysDenyUI) ShowInfo(message string) { 114 panic("implement me") 115 } 116 117 func (alwaysDenyUI) OnApprovedTx(tx ethapi.SignTransactionResult) { 118 panic("implement me") 119 } 120 121 func initRuleEngine(js string) (*rulesetUI, error) { 122 r, err := NewRuleEvaluator(&alwaysDenyUI{}, storage.NewEphemeralStorage(), storage.NewEphemeralStorage()) 123 if err != nil { 124 return nil, fmt.Errorf("failed to create js engine: %v", err) 125 } 126 if err = r.Init(js); err != nil { 127 return nil, fmt.Errorf("failed to load bootstrap js: %v", err) 128 } 129 return r, nil 130 } 131 132 func TestListRequest(t *testing.T) { 133 accs := make([]core.Account, 5) 134 135 for i := range accs { 136 addr := fmt.Sprintf("000000000000000000000000000000000000000%x", i) 137 acc := core.Account{ 138 Address: common.BytesToAddress(common.Hex2Bytes(addr)), 139 URL: accounts.URL{Scheme: "test", Path: fmt.Sprintf("acc-%d", i)}, 140 } 141 accs[i] = acc 142 } 143 144 js := `function ApproveListing(){ return "Approve" }` 145 146 r, err := initRuleEngine(js) 147 if err != nil { 148 t.Errorf("Couldn't create evaluator %v", err) 149 return 150 } 151 resp, _ := r.ApproveListing(&core.ListRequest{ 152 Accounts: accs, 153 Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, 154 }) 155 if len(resp.Accounts) != len(accs) { 156 t.Errorf("Expected check to resolve to 'Approve'") 157 } 158 } 159 160 func TestSignTxRequest(t *testing.T) { 161 162 js := ` 163 function ApproveTx(r){ 164 console.log("transaction.from", r.transaction.from); 165 console.log("transaction.to", r.transaction.to); 166 console.log("transaction.value", r.transaction.value); 167 console.log("transaction.nonce", r.transaction.nonce); 168 if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"} 169 if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"} 170 }` 171 172 r, err := initRuleEngine(js) 173 if err != nil { 174 t.Errorf("Couldn't create evaluator %v", err) 175 return 176 } 177 to, err := mixAddr("000000000000000000000000000000000000dead") 178 if err != nil { 179 t.Error(err) 180 return 181 } 182 from, err := mixAddr("0000000000000000000000000000000000001337") 183 184 if err != nil { 185 t.Error(err) 186 return 187 } 188 fmt.Printf("to %v", to.Address().String()) 189 resp, err := r.ApproveTx(&core.SignTxRequest{ 190 Transaction: core.SendTxArgs{ 191 From: *from, 192 To: to}, 193 Callinfo: nil, 194 Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, 195 }) 196 if err != nil { 197 t.Errorf("Unexpected error %v", err) 198 } 199 if !resp.Approved { 200 t.Errorf("Expected check to resolve to 'Approve'") 201 } 202 } 203 204 type dummyUI struct { 205 calls []string 206 } 207 208 func (d *dummyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) { 209 d.calls = append(d.calls, "OnInputRequired") 210 return core.UserInputResponse{}, nil 211 } 212 213 func (d *dummyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { 214 d.calls = append(d.calls, "ApproveTx") 215 return core.SignTxResponse{}, core.ErrRequestDenied 216 } 217 218 func (d *dummyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { 219 d.calls = append(d.calls, "ApproveSignData") 220 return core.SignDataResponse{}, core.ErrRequestDenied 221 } 222 223 func (d *dummyUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { 224 d.calls = append(d.calls, "ApproveExport") 225 return core.ExportResponse{}, core.ErrRequestDenied 226 } 227 228 func (d *dummyUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) { 229 d.calls = append(d.calls, "ApproveImport") 230 return core.ImportResponse{}, core.ErrRequestDenied 231 } 232 233 func (d *dummyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { 234 d.calls = append(d.calls, "ApproveListing") 235 return core.ListResponse{}, core.ErrRequestDenied 236 } 237 238 func (d *dummyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { 239 d.calls = append(d.calls, "ApproveNewAccount") 240 return core.NewAccountResponse{}, core.ErrRequestDenied 241 } 242 243 func (d *dummyUI) ShowError(message string) { 244 d.calls = append(d.calls, "ShowError") 245 } 246 247 func (d *dummyUI) ShowInfo(message string) { 248 d.calls = append(d.calls, "ShowInfo") 249 } 250 251 func (d *dummyUI) OnApprovedTx(tx ethapi.SignTransactionResult) { 252 d.calls = append(d.calls, "OnApprovedTx") 253 } 254 255 func (d *dummyUI) OnMasterPassword(request *core.PasswordRequest) (core.PasswordResponse, error) { 256 return core.PasswordResponse{}, nil 257 } 258 259 func (d *dummyUI) OnSignerStartup(info core.StartupInfo) { 260 } 261 262 //TestForwarding测试规则引擎是否正确地将请求分派给下一个调用方 263 func TestForwarding(t *testing.T) { 264 265 js := "" 266 ui := &dummyUI{make([]string, 0)} 267 jsBackend := storage.NewEphemeralStorage() 268 credBackend := storage.NewEphemeralStorage() 269 r, err := NewRuleEvaluator(ui, jsBackend, credBackend) 270 if err != nil { 271 t.Fatalf("Failed to create js engine: %v", err) 272 } 273 if err = r.Init(js); err != nil { 274 t.Fatalf("Failed to load bootstrap js: %v", err) 275 } 276 r.ApproveSignData(nil) 277 r.ApproveTx(nil) 278 r.ApproveImport(nil) 279 r.ApproveNewAccount(nil) 280 r.ApproveListing(nil) 281 r.ApproveExport(nil) 282 r.ShowError("test") 283 r.ShowInfo("test") 284 285 //这个没有转发 286 r.OnApprovedTx(ethapi.SignTransactionResult{}) 287 288 expCalls := 8 289 if len(ui.calls) != expCalls { 290 291 t.Errorf("Expected %d forwarded calls, got %d: %s", expCalls, len(ui.calls), strings.Join(ui.calls, ",")) 292 293 } 294 295 } 296 297 func TestMissingFunc(t *testing.T) { 298 r, err := initRuleEngine(JS) 299 if err != nil { 300 t.Errorf("Couldn't create evaluator %v", err) 301 return 302 } 303 304 _, err = r.execute("MissingMethod", "test") 305 306 if err == nil { 307 t.Error("Expected error") 308 } 309 310 approved, err := r.checkApproval("MissingMethod", nil, nil) 311 if err == nil { 312 t.Errorf("Expected missing method to yield error'") 313 } 314 if approved { 315 t.Errorf("Expected missing method to cause non-approval") 316 } 317 fmt.Printf("Err %v", err) 318 319 } 320 func TestStorage(t *testing.T) { 321 322 js := ` 323 function testStorage(){ 324 storage.Put("mykey", "myvalue") 325 a = storage.Get("mykey") 326 327 storage.Put("mykey", ["a", "list"]) //应生成“a,list” 328 a += storage.Get("mykey") 329 330 331 storage.Put("mykey", {"an": "object"}) //应导致“[对象对象]” 332 a += storage.Get("mykey") 333 334 335 storage.Put("mykey", JSON.stringify({"an": "object"})) //应导致“”an“:”对象“” 336 a += storage.Get("mykey") 337 338 a += storage.Get("missingkey") //缺少键应导致空字符串 339 storage.Put("","missing key==noop") //无法使用0长度密钥存储 340 a += storage.Get("") //应导致“”。 341 342 var b = new BigNumber(2) 343 var c = new BigNumber(16)//“0xF0”,16) 344 var d = b.plus(c) 345 console.log(d) 346 return a 347 } 348 ` 349 r, err := initRuleEngine(js) 350 if err != nil { 351 t.Errorf("Couldn't create evaluator %v", err) 352 return 353 } 354 355 v, err := r.execute("testStorage", nil) 356 357 if err != nil { 358 t.Errorf("Unexpected error %v", err) 359 } 360 361 retval, err := v.ToString() 362 363 if err != nil { 364 t.Errorf("Unexpected error %v", err) 365 } 366 exp := `myvaluea,list[object Object]{"an":"object"}` 367 if retval != exp { 368 t.Errorf("Unexpected data, expected '%v', got '%v'", exp, retval) 369 } 370 fmt.Printf("Err %v", err) 371 372 } 373 374 const ExampleTxWindow = ` 375 function big(str){ 376 if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)} 377 return new BigNumber(str) 378 } 379 380 //时间窗口:1周 381 var window = 1000* 3600*24*7; 382 383 //极限:1醚 384 var limit = new BigNumber("1e18"); 385 386 function isLimitOk(transaction){ 387 var value = big(transaction.value) 388 //启动窗口功能 389 var windowstart = new Date().getTime() - window; 390 391 var txs = []; 392 var stored = storage.Get('txs'); 393 394 if(stored != ""){ 395 txs = JSON.parse(stored) 396 } 397 //首先,删除时间窗口之外的所有内容 398 var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart}); 399 console.log(txs, newtxs.length); 400 401 //第二,合计当前总额 402 sum = new BigNumber(0) 403 404 sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum); 405 console.log("ApproveTx > Sum so far", sum); 406 console.log("ApproveTx > Requested", value.toNumber()); 407 408 //我们会超过每周限额吗? 409 return sum.plus(value).lt(limit) 410 411 } 412 function ApproveTx(r){ 413 console.log(r) 414 console.log(typeof(r)) 415 if (isLimitOk(r.transaction)){ 416 return "Approve" 417 } 418 return "Nope" 419 } 420 421 /* 422 *OnApprovedTx(str)在交易被批准和签署后调用。参数 423 *“response_str”包含将发送给外部调用方的返回值。 424 *此方法的返回值为ignore-进行此回调的原因是允许 425 *用于跟踪已批准交易的规则集。 426 * 427 *在执行速率限制规则时,应使用此回调。 428 *如果规则的响应既不包含“批准”也不包含“拒绝”,则发送将进入手动处理。如果用户 429 *然后接受事务,将调用此方法。 430 * 431 *tldr;使用此方法跟踪已签名的事务,而不是使用approvetx中的数据。 432 **/ 433 434 function OnApprovedTx(resp){ 435 var value = big(resp.tx.value) 436 var txs = [] 437 //加载存储的事务 438 var stored = storage.Get('txs'); 439 if(stored != ""){ 440 txs = JSON.parse(stored) 441 } 442 //将此添加到存储 443 txs.push({tstamp: new Date().getTime(), value: value}); 444 storage.Put("txs", JSON.stringify(txs)); 445 } 446 447 ` 448 449 func dummyTx(value hexutil.Big) *core.SignTxRequest { 450 451 to, _ := mixAddr("000000000000000000000000000000000000dead") 452 from, _ := mixAddr("000000000000000000000000000000000000dead") 453 n := hexutil.Uint64(3) 454 gas := hexutil.Uint64(21000) 455 gasPrice := hexutil.Big(*big.NewInt(2000000)) 456 457 return &core.SignTxRequest{ 458 Transaction: core.SendTxArgs{ 459 From: *from, 460 To: to, 461 Value: value, 462 Nonce: n, 463 GasPrice: gasPrice, 464 Gas: gas, 465 }, 466 Callinfo: []core.ValidationInfo{ 467 {Typ: "Warning", Message: "All your base are bellong to us"}, 468 }, 469 Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, 470 } 471 } 472 func dummyTxWithV(value uint64) *core.SignTxRequest { 473 474 v := big.NewInt(0).SetUint64(value) 475 h := hexutil.Big(*v) 476 return dummyTx(h) 477 } 478 func dummySigned(value *big.Int) *types.Transaction { 479 to := common.HexToAddress("000000000000000000000000000000000000dead") 480 gas := uint64(21000) 481 gasPrice := big.NewInt(2000000) 482 data := make([]byte, 0) 483 return types.NewTransaction(3, to, value, gas, gasPrice, data) 484 485 } 486 func TestLimitWindow(t *testing.T) { 487 488 r, err := initRuleEngine(ExampleTxWindow) 489 if err != nil { 490 t.Errorf("Couldn't create evaluator %v", err) 491 return 492 } 493 494 //0.3乙醚:429D069189E0000 wei 495 v := big.NewInt(0).SetBytes(common.Hex2Bytes("0429D069189E0000")) 496 h := hexutil.Big(*v) 497 //前三个应该成功 498 for i := 0; i < 3; i++ { 499 unsigned := dummyTx(h) 500 resp, err := r.ApproveTx(unsigned) 501 if err != nil { 502 t.Errorf("Unexpected error %v", err) 503 } 504 if !resp.Approved { 505 t.Errorf("Expected check to resolve to 'Approve'") 506 } 507 //创建虚拟签名事务 508 509 response := ethapi.SignTransactionResult{ 510 Tx: dummySigned(v), 511 Raw: common.Hex2Bytes("deadbeef"), 512 } 513 r.OnApprovedTx(response) 514 } 515 //第四个应该失败 516 resp, _ := r.ApproveTx(dummyTx(h)) 517 if resp.Approved { 518 t.Errorf("Expected check to resolve to 'Reject'") 519 } 520 521 } 522 523 //dontcallme用作不希望调用的下一个处理程序-它调用测试失败 524 type dontCallMe struct { 525 t *testing.T 526 } 527 528 func (d *dontCallMe) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) { 529 d.t.Fatalf("Did not expect next-handler to be called") 530 return core.UserInputResponse{}, nil 531 } 532 533 func (d *dontCallMe) OnSignerStartup(info core.StartupInfo) { 534 } 535 536 func (d *dontCallMe) OnMasterPassword(request *core.PasswordRequest) (core.PasswordResponse, error) { 537 return core.PasswordResponse{}, nil 538 } 539 540 func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { 541 d.t.Fatalf("Did not expect next-handler to be called") 542 return core.SignTxResponse{}, core.ErrRequestDenied 543 } 544 545 func (d *dontCallMe) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { 546 d.t.Fatalf("Did not expect next-handler to be called") 547 return core.SignDataResponse{}, core.ErrRequestDenied 548 } 549 550 func (d *dontCallMe) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { 551 d.t.Fatalf("Did not expect next-handler to be called") 552 return core.ExportResponse{}, core.ErrRequestDenied 553 } 554 555 func (d *dontCallMe) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) { 556 d.t.Fatalf("Did not expect next-handler to be called") 557 return core.ImportResponse{}, core.ErrRequestDenied 558 } 559 560 func (d *dontCallMe) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { 561 d.t.Fatalf("Did not expect next-handler to be called") 562 return core.ListResponse{}, core.ErrRequestDenied 563 } 564 565 func (d *dontCallMe) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { 566 d.t.Fatalf("Did not expect next-handler to be called") 567 return core.NewAccountResponse{}, core.ErrRequestDenied 568 } 569 570 func (d *dontCallMe) ShowError(message string) { 571 d.t.Fatalf("Did not expect next-handler to be called") 572 } 573 574 func (d *dontCallMe) ShowInfo(message string) { 575 d.t.Fatalf("Did not expect next-handler to be called") 576 } 577 578 func (d *dontCallMe) OnApprovedTx(tx ethapi.SignTransactionResult) { 579 d.t.Fatalf("Did not expect next-handler to be called") 580 } 581 582 //testcontext清除了规则引擎不在多个请求上保留变量的测试。 583 //如果是这样,那就不好了,因为开发人员可能会依赖它来存储数据, 584 //而不是使用基于磁盘的数据存储 585 func TestContextIsCleared(t *testing.T) { 586 587 js := ` 588 function ApproveTx(){ 589 if (typeof foobar == 'undefined') { 590 foobar = "Approve" 591 } 592 console.log(foobar) 593 if (foobar == "Approve"){ 594 foobar = "Reject" 595 }else{ 596 foobar = "Approve" 597 } 598 return foobar 599 } 600 ` 601 ui := &dontCallMe{t} 602 r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage(), storage.NewEphemeralStorage()) 603 if err != nil { 604 t.Fatalf("Failed to create js engine: %v", err) 605 } 606 if err = r.Init(js); err != nil { 607 t.Fatalf("Failed to load bootstrap js: %v", err) 608 } 609 tx := dummyTxWithV(0) 610 r1, _ := r.ApproveTx(tx) 611 r2, _ := r.ApproveTx(tx) 612 if r1.Approved != r2.Approved { 613 t.Errorf("Expected execution context to be cleared between executions") 614 } 615 } 616 617 func TestSignData(t *testing.T) { 618 619 js := `function ApproveListing(){ 620 return "Approve" 621 } 622 function ApproveSignData(r){ 623 if( r.address.toLowerCase() == "0x694267f14675d7e1b9494fd8d72fefe1755710fa") 624 { 625 if(r.message.indexOf("bazonk") >= 0){ 626 return "Approve" 627 } 628 return "Reject" 629 } 630 //否则进入手动处理 631 }` 632 r, err := initRuleEngine(js) 633 if err != nil { 634 t.Errorf("Couldn't create evaluator %v", err) 635 return 636 } 637 message := []byte("baz bazonk foo") 638 hash, msg := core.SignHash(message) 639 raw := hexutil.Bytes(message) 640 addr, _ := mixAddr("0x694267f14675d7e1b9494fd8d72fefe1755710fa") 641 642 fmt.Printf("address %v %v\n", addr.String(), addr.Original()) 643 resp, err := r.ApproveSignData(&core.SignDataRequest{ 644 Address: *addr, 645 Message: msg, 646 Hash: hash, 647 Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, 648 Rawdata: raw, 649 }) 650 if err != nil { 651 t.Fatalf("Unexpected error %v", err) 652 } 653 if !resp.Approved { 654 t.Fatalf("Expected approved") 655 } 656 } 657