github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/chain/signer/rules/rules_test.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU 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  // go-ethereum 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 General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  //
    17  package rules
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/ethereum/go-ethereum/accounts"
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/common/hexutil"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/internal/ethapi"
    30  	"github.com/ethereum/go-ethereum/signer/core"
    31  	"github.com/ethereum/go-ethereum/signer/storage"
    32  )
    33  
    34  const JS = `
    35  /**
    36  This is an example implementation of a Javascript rule file.
    37  
    38  When the signer receives a request over the external API, the corresponding method is evaluated.
    39  Three things can happen:
    40  
    41  1. The method returns "Approve". This means the operation is permitted.
    42  2. The method returns "Reject". This means the operation is rejected.
    43  3. Anything else; other return values [*], method not implemented or exception occurred during processing. This means
    44  that the operation will continue to manual processing, via the regular UI method chosen by the user.
    45  
    46  [*] Note: Future version of the ruleset may use more complex json-based returnvalues, making it possible to not
    47  only respond Approve/Reject/Manual, but also modify responses. For example, choose to list only one, but not all
    48  accounts in a list-request. The points above will continue to hold for non-json based responses ("Approve"/"Reject").
    49  
    50  **/
    51  
    52  function ApproveListing(request){
    53  	console.log("In js approve listing");
    54  	console.log(request.accounts[3].Address)
    55  	console.log(request.meta.Remote)
    56  	return "Approve"
    57  }
    58  
    59  function ApproveTx(request){
    60  	console.log("test");
    61  	console.log("from");
    62  	return "Reject";
    63  }
    64  
    65  function test(thing){
    66  	console.log(thing.String())
    67  }
    68  
    69  `
    70  
    71  func mixAddr(a string) (*common.MixedcaseAddress, error) {
    72  	return common.NewMixedcaseAddressFromString(a)
    73  }
    74  
    75  type alwaysDenyUI struct{}
    76  
    77  func (alwaysDenyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) {
    78  	return core.UserInputResponse{}, nil
    79  }
    80  func (alwaysDenyUI) RegisterUIServer(api *core.UIServerAPI) {
    81  }
    82  
    83  func (alwaysDenyUI) OnSignerStartup(info core.StartupInfo) {
    84  }
    85  
    86  func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
    87  	return core.SignTxResponse{Transaction: request.Transaction, Approved: false, Password: ""}, nil
    88  }
    89  
    90  func (alwaysDenyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
    91  	return core.SignDataResponse{Approved: false, Password: ""}, nil
    92  }
    93  
    94  func (alwaysDenyUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
    95  	return core.ExportResponse{Approved: false}, nil
    96  }
    97  
    98  func (alwaysDenyUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
    99  	return core.ImportResponse{Approved: false, OldPassword: "", NewPassword: ""}, nil
   100  }
   101  
   102  func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
   103  	return core.ListResponse{Accounts: nil}, nil
   104  }
   105  
   106  func (alwaysDenyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
   107  	return core.NewAccountResponse{Approved: false, Password: ""}, nil
   108  }
   109  
   110  func (alwaysDenyUI) ShowError(message string) {
   111  	panic("implement me")
   112  }
   113  
   114  func (alwaysDenyUI) ShowInfo(message string) {
   115  	panic("implement me")
   116  }
   117  
   118  func (alwaysDenyUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
   119  	panic("implement me")
   120  }
   121  
   122  func initRuleEngine(js string) (*rulesetUI, error) {
   123  	r, err := NewRuleEvaluator(&alwaysDenyUI{}, storage.NewEphemeralStorage(), storage.NewEphemeralStorage())
   124  	if err != nil {
   125  		return nil, fmt.Errorf("failed to create js engine: %v", err)
   126  	}
   127  	if err = r.Init(js); err != nil {
   128  		return nil, fmt.Errorf("failed to load bootstrap js: %v", err)
   129  	}
   130  	return r, nil
   131  }
   132  
   133  func TestListRequest(t *testing.T) {
   134  	accs := make([]accounts.Account, 5)
   135  
   136  	for i := range accs {
   137  		addr := fmt.Sprintf("000000000000000000000000000000000000000%x", i)
   138  		acc := accounts.Account{
   139  			Address: common.BytesToAddress(common.Hex2Bytes(addr)),
   140  			URL:     accounts.URL{Scheme: "test", Path: fmt.Sprintf("acc-%d", i)},
   141  		}
   142  		accs[i] = acc
   143  	}
   144  
   145  	js := `function ApproveListing(){ return "Approve" }`
   146  
   147  	r, err := initRuleEngine(js)
   148  	if err != nil {
   149  		t.Errorf("Couldn't create evaluator %v", err)
   150  		return
   151  	}
   152  	resp, _ := r.ApproveListing(&core.ListRequest{
   153  		Accounts: accs,
   154  		Meta:     core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"},
   155  	})
   156  	if len(resp.Accounts) != len(accs) {
   157  		t.Errorf("Expected check to resolve to 'Approve'")
   158  	}
   159  }
   160  
   161  func TestSignTxRequest(t *testing.T) {
   162  
   163  	js := `
   164  	function ApproveTx(r){
   165  		console.log("transaction.from", r.transaction.from);
   166  		console.log("transaction.to", r.transaction.to);
   167  		console.log("transaction.value", r.transaction.value);
   168  		console.log("transaction.nonce", r.transaction.nonce);
   169  		if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"}
   170  		if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"}
   171  	}`
   172  
   173  	r, err := initRuleEngine(js)
   174  	if err != nil {
   175  		t.Errorf("Couldn't create evaluator %v", err)
   176  		return
   177  	}
   178  	to, err := mixAddr("000000000000000000000000000000000000dead")
   179  	if err != nil {
   180  		t.Error(err)
   181  		return
   182  	}
   183  	from, err := mixAddr("0000000000000000000000000000000000001337")
   184  
   185  	if err != nil {
   186  		t.Error(err)
   187  		return
   188  	}
   189  	fmt.Printf("to %v", to.Address().String())
   190  	resp, err := r.ApproveTx(&core.SignTxRequest{
   191  		Transaction: core.SendTxArgs{
   192  			From: *from,
   193  			To:   to},
   194  		Callinfo: nil,
   195  		Meta:     core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"},
   196  	})
   197  	if err != nil {
   198  		t.Errorf("Unexpected error %v", err)
   199  	}
   200  	if !resp.Approved {
   201  		t.Errorf("Expected check to resolve to 'Approve'")
   202  	}
   203  }
   204  
   205  type dummyUI struct {
   206  	calls []string
   207  }
   208  
   209  func (d *dummyUI) RegisterUIServer(api *core.UIServerAPI) {
   210  	panic("implement me")
   211  }
   212  
   213  func (d *dummyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) {
   214  	d.calls = append(d.calls, "OnInputRequired")
   215  	return core.UserInputResponse{}, nil
   216  }
   217  
   218  func (d *dummyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
   219  	d.calls = append(d.calls, "ApproveTx")
   220  	return core.SignTxResponse{}, core.ErrRequestDenied
   221  }
   222  
   223  func (d *dummyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
   224  	d.calls = append(d.calls, "ApproveSignData")
   225  	return core.SignDataResponse{}, core.ErrRequestDenied
   226  }
   227  
   228  func (d *dummyUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
   229  	d.calls = append(d.calls, "ApproveExport")
   230  	return core.ExportResponse{}, core.ErrRequestDenied
   231  }
   232  
   233  func (d *dummyUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
   234  	d.calls = append(d.calls, "ApproveImport")
   235  	return core.ImportResponse{}, core.ErrRequestDenied
   236  }
   237  
   238  func (d *dummyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
   239  	d.calls = append(d.calls, "ApproveListing")
   240  	return core.ListResponse{}, core.ErrRequestDenied
   241  }
   242  
   243  func (d *dummyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
   244  	d.calls = append(d.calls, "ApproveNewAccount")
   245  	return core.NewAccountResponse{}, core.ErrRequestDenied
   246  }
   247  
   248  func (d *dummyUI) ShowError(message string) {
   249  	d.calls = append(d.calls, "ShowError")
   250  }
   251  
   252  func (d *dummyUI) ShowInfo(message string) {
   253  	d.calls = append(d.calls, "ShowInfo")
   254  }
   255  
   256  func (d *dummyUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
   257  	d.calls = append(d.calls, "OnApprovedTx")
   258  }
   259  
   260  func (d *dummyUI) OnSignerStartup(info core.StartupInfo) {
   261  }
   262  
   263  //TestForwarding tests that the rule-engine correctly dispatches requests to the next caller
   264  func TestForwarding(t *testing.T) {
   265  
   266  	js := ""
   267  	ui := &dummyUI{make([]string, 0)}
   268  	jsBackend := storage.NewEphemeralStorage()
   269  	credBackend := storage.NewEphemeralStorage()
   270  	r, err := NewRuleEvaluator(ui, jsBackend, credBackend)
   271  	if err != nil {
   272  		t.Fatalf("Failed to create js engine: %v", err)
   273  	}
   274  	if err = r.Init(js); err != nil {
   275  		t.Fatalf("Failed to load bootstrap js: %v", err)
   276  	}
   277  	r.ApproveSignData(nil)
   278  	r.ApproveTx(nil)
   279  	r.ApproveImport(nil)
   280  	r.ApproveNewAccount(nil)
   281  	r.ApproveListing(nil)
   282  	r.ApproveExport(nil)
   283  	r.ShowError("test")
   284  	r.ShowInfo("test")
   285  
   286  	//This one is not forwarded
   287  	r.OnApprovedTx(ethapi.SignTransactionResult{})
   288  
   289  	expCalls := 8
   290  	if len(ui.calls) != expCalls {
   291  
   292  		t.Errorf("Expected %d forwarded calls, got %d: %s", expCalls, len(ui.calls), strings.Join(ui.calls, ","))
   293  
   294  	}
   295  
   296  }
   297  
   298  func TestMissingFunc(t *testing.T) {
   299  	r, err := initRuleEngine(JS)
   300  	if err != nil {
   301  		t.Errorf("Couldn't create evaluator %v", err)
   302  		return
   303  	}
   304  
   305  	_, err = r.execute("MissingMethod", "test")
   306  
   307  	if err == nil {
   308  		t.Error("Expected error")
   309  	}
   310  
   311  	approved, err := r.checkApproval("MissingMethod", nil, nil)
   312  	if err == nil {
   313  		t.Errorf("Expected missing method to yield error'")
   314  	}
   315  	if approved {
   316  		t.Errorf("Expected missing method to cause non-approval")
   317  	}
   318  	fmt.Printf("Err %v", err)
   319  
   320  }
   321  func TestStorage(t *testing.T) {
   322  
   323  	js := `
   324  	function testStorage(){
   325  		storage.Put("mykey", "myvalue")
   326  		a = storage.Get("mykey")
   327  
   328  		storage.Put("mykey", ["a", "list"])  	// Should result in "a,list"
   329  		a += storage.Get("mykey")
   330  
   331  
   332  		storage.Put("mykey", {"an": "object"}) 	// Should result in "[object Object]"
   333  		a += storage.Get("mykey")
   334  
   335  
   336  		storage.Put("mykey", JSON.stringify({"an": "object"})) // Should result in '{"an":"object"}'
   337  		a += storage.Get("mykey")
   338  
   339  		a += storage.Get("missingkey")		//Missing keys should result in empty string
   340  		storage.Put("","missing key==noop") // Can't store with 0-length key
   341  		a += storage.Get("")				// Should result in ''
   342  
   343  		var b = new BigNumber(2)
   344  		var c = new BigNumber(16)//"0xf0",16)
   345  		var d = b.plus(c)
   346  		console.log(d)
   347  		return a
   348  	}
   349  `
   350  	r, err := initRuleEngine(js)
   351  	if err != nil {
   352  		t.Errorf("Couldn't create evaluator %v", err)
   353  		return
   354  	}
   355  
   356  	v, err := r.execute("testStorage", nil)
   357  
   358  	if err != nil {
   359  		t.Errorf("Unexpected error %v", err)
   360  	}
   361  
   362  	retval, err := v.ToString()
   363  
   364  	if err != nil {
   365  		t.Errorf("Unexpected error %v", err)
   366  	}
   367  	exp := `myvaluea,list[object Object]{"an":"object"}`
   368  	if retval != exp {
   369  		t.Errorf("Unexpected data, expected '%v', got '%v'", exp, retval)
   370  	}
   371  	fmt.Printf("Err %v", err)
   372  
   373  }
   374  
   375  const ExampleTxWindow = `
   376  	function big(str){
   377  		if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)}
   378  		return new BigNumber(str)
   379  	}
   380  
   381  	// Time window: 1 week
   382  	var window = 1000* 3600*24*7;
   383  
   384  	// Limit : 1 ether
   385  	var limit = new BigNumber("1e18");
   386  
   387  	function isLimitOk(transaction){
   388  		var value = big(transaction.value)
   389  		// Start of our window function
   390  		var windowstart = new Date().getTime() - window;
   391  
   392  		var txs = [];
   393  		var stored = storage.Get('txs');
   394  
   395  		if(stored != ""){
   396  			txs = JSON.parse(stored)
   397  		}
   398  		// First, remove all that have passed out of the time-window
   399  		var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart});
   400  		console.log(txs, newtxs.length);
   401  
   402  		// Secondly, aggregate the current sum
   403  		sum = new BigNumber(0)
   404  
   405  		sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum);
   406  		console.log("ApproveTx > Sum so far", sum);
   407  		console.log("ApproveTx > Requested", value.toNumber());
   408  
   409  		// Would we exceed weekly limit ?
   410  		return sum.plus(value).lt(limit)
   411  
   412  	}
   413  	function ApproveTx(r){
   414  		console.log(r)
   415  		console.log(typeof(r))
   416  		if (isLimitOk(r.transaction)){
   417  			return "Approve"
   418  		}
   419  		return "Nope"
   420  	}
   421  
   422  	/**
   423  	* OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter
   424   	* 'response_str' contains the return value that will be sent to the external caller.
   425  	* The return value from this method is ignore - the reason for having this callback is to allow the
   426  	* ruleset to keep track of approved transactions.
   427  	*
   428  	* When implementing rate-limited rules, this callback should be used.
   429  	* If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user
   430  	* then accepts the transaction, this method will be called.
   431  	*
   432  	* TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx.
   433  	*/
   434   	function OnApprovedTx(resp){
   435  		var value = big(resp.tx.value)
   436  		var txs = []
   437  		// Load stored transactions
   438  		var stored = storage.Get('txs');
   439  		if(stored != ""){
   440  			txs = JSON.parse(stored)
   441  		}
   442  		// Add this to the storage
   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 ether: 429D069189E0000 wei
   495  	v := big.NewInt(0).SetBytes(common.Hex2Bytes("0429D069189E0000"))
   496  	h := hexutil.Big(*v)
   497  	// The first three should succeed
   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  		// Create a dummy signed transaction
   508  
   509  		response := ethapi.SignTransactionResult{
   510  			Tx:  dummySigned(v),
   511  			Raw: common.Hex2Bytes("deadbeef"),
   512  		}
   513  		r.OnApprovedTx(response)
   514  	}
   515  	// Fourth should fail
   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 is used as a next-handler that does not want to be called - it invokes test failure
   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  func (d *dontCallMe) RegisterUIServer(api *core.UIServerAPI) {
   533  }
   534  
   535  func (d *dontCallMe) OnSignerStartup(info core.StartupInfo) {
   536  }
   537  
   538  func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
   539  	d.t.Fatalf("Did not expect next-handler to be called")
   540  	return core.SignTxResponse{}, core.ErrRequestDenied
   541  }
   542  
   543  func (d *dontCallMe) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
   544  	d.t.Fatalf("Did not expect next-handler to be called")
   545  	return core.SignDataResponse{}, core.ErrRequestDenied
   546  }
   547  
   548  func (d *dontCallMe) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
   549  	d.t.Fatalf("Did not expect next-handler to be called")
   550  	return core.ExportResponse{}, core.ErrRequestDenied
   551  }
   552  
   553  func (d *dontCallMe) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
   554  	d.t.Fatalf("Did not expect next-handler to be called")
   555  	return core.ImportResponse{}, core.ErrRequestDenied
   556  }
   557  
   558  func (d *dontCallMe) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
   559  	d.t.Fatalf("Did not expect next-handler to be called")
   560  	return core.ListResponse{}, core.ErrRequestDenied
   561  }
   562  
   563  func (d *dontCallMe) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
   564  	d.t.Fatalf("Did not expect next-handler to be called")
   565  	return core.NewAccountResponse{}, core.ErrRequestDenied
   566  }
   567  
   568  func (d *dontCallMe) ShowError(message string) {
   569  	d.t.Fatalf("Did not expect next-handler to be called")
   570  }
   571  
   572  func (d *dontCallMe) ShowInfo(message string) {
   573  	d.t.Fatalf("Did not expect next-handler to be called")
   574  }
   575  
   576  func (d *dontCallMe) OnApprovedTx(tx ethapi.SignTransactionResult) {
   577  	d.t.Fatalf("Did not expect next-handler to be called")
   578  }
   579  
   580  //TestContextIsCleared tests that the rule-engine does not retain variables over several requests.
   581  // if it does, that would be bad since developers may rely on that to store data,
   582  // instead of using the disk-based data storage
   583  func TestContextIsCleared(t *testing.T) {
   584  
   585  	js := `
   586  	function ApproveTx(){
   587  		if (typeof foobar == 'undefined') {
   588  			foobar = "Approve"
   589   		}
   590  		console.log(foobar)
   591  		if (foobar == "Approve"){
   592  			foobar = "Reject"
   593  		}else{
   594  			foobar = "Approve"
   595  		}
   596  		return foobar
   597  	}
   598  	`
   599  	ui := &dontCallMe{t}
   600  	r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage(), storage.NewEphemeralStorage())
   601  	if err != nil {
   602  		t.Fatalf("Failed to create js engine: %v", err)
   603  	}
   604  	if err = r.Init(js); err != nil {
   605  		t.Fatalf("Failed to load bootstrap js: %v", err)
   606  	}
   607  	tx := dummyTxWithV(0)
   608  	r1, _ := r.ApproveTx(tx)
   609  	r2, _ := r.ApproveTx(tx)
   610  	if r1.Approved != r2.Approved {
   611  		t.Errorf("Expected execution context to be cleared between executions")
   612  	}
   613  }
   614  
   615  func TestSignData(t *testing.T) {
   616  
   617  	js := `function ApproveListing(){
   618      return "Approve"
   619  }
   620  function ApproveSignData(r){
   621      if( r.address.toLowerCase() == "0x694267f14675d7e1b9494fd8d72fefe1755710fa")
   622      {
   623          if(r.message[0].value.indexOf("bazonk") >= 0){
   624              return "Approve"
   625          }
   626          return "Reject"
   627      }
   628      // Otherwise goes to manual processing
   629  }`
   630  	r, err := initRuleEngine(js)
   631  	if err != nil {
   632  		t.Errorf("Couldn't create evaluator %v", err)
   633  		return
   634  	}
   635  	message := "baz bazonk foo"
   636  	hash, rawdata := accounts.TextAndHash([]byte(message))
   637  	addr, _ := mixAddr("0x694267f14675d7e1b9494fd8d72fefe1755710fa")
   638  
   639  	fmt.Printf("address %v %v\n", addr.String(), addr.Original())
   640  
   641  	nvt := []*core.NameValueType{
   642  		{
   643  			Name:  "message",
   644  			Typ:   "text/plain",
   645  			Value: message,
   646  		},
   647  	}
   648  	resp, err := r.ApproveSignData(&core.SignDataRequest{
   649  		Address: *addr,
   650  		Message: nvt,
   651  		Hash:    hash,
   652  		Meta:    core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"},
   653  		Rawdata: []byte(rawdata),
   654  	})
   655  	if err != nil {
   656  		t.Fatalf("Unexpected error %v", err)
   657  	}
   658  	if !resp.Approved {
   659  		t.Fatalf("Expected approved")
   660  	}
   661  }