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