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