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