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