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