github.com/aiyaya188/klaytn@v0.0.0-20220629133911-2c66fd5546f4/signer/core/api_test.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser 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  // The go-ethereum library 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 Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core_test
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"math/big"
    24  	"os"
    25  	"path/filepath"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/aiyaya188/klaytn/accounts"
    30  	"github.com/aiyaya188/klaytn/accounts/keystore"
    31  	"github.com/aiyaya188/klaytn/common"
    32  	"github.com/aiyaya188/klaytn/common/hexutil"
    33  	"github.com/aiyaya188/klaytn/core/types"
    34  	"github.com/aiyaya188/klaytn/internal/ethapi"
    35  	"github.com/aiyaya188/klaytn/rlp"
    36  	"github.com/aiyaya188/klaytn/signer/core"
    37  	"github.com/aiyaya188/klaytn/signer/core/apitypes"
    38  	"github.com/aiyaya188/klaytn/signer/fourbyte"
    39  	"github.com/aiyaya188/klaytn/signer/storage"
    40  )
    41  
    42  //Used for testing
    43  type headlessUi struct {
    44  	approveCh chan string // to send approve/deny
    45  	inputCh   chan string // to send password
    46  }
    47  
    48  func (ui *headlessUi) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) {
    49  	input := <-ui.inputCh
    50  	return core.UserInputResponse{Text: input}, nil
    51  }
    52  
    53  func (ui *headlessUi) OnSignerStartup(info core.StartupInfo)        {}
    54  func (ui *headlessUi) RegisterUIServer(api *core.UIServerAPI)       {}
    55  func (ui *headlessUi) OnApprovedTx(tx ethapi.SignTransactionResult) {}
    56  
    57  func (ui *headlessUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
    58  
    59  	switch <-ui.approveCh {
    60  	case "Y":
    61  		return core.SignTxResponse{request.Transaction, true}, nil
    62  	case "M": // modify
    63  		// The headless UI always modifies the transaction
    64  		old := big.Int(request.Transaction.Value)
    65  		newVal := new(big.Int).Add(&old, big.NewInt(1))
    66  		request.Transaction.Value = hexutil.Big(*newVal)
    67  		return core.SignTxResponse{request.Transaction, true}, nil
    68  	default:
    69  		return core.SignTxResponse{request.Transaction, false}, nil
    70  	}
    71  }
    72  
    73  func (ui *headlessUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
    74  	approved := (<-ui.approveCh == "Y")
    75  	return core.SignDataResponse{approved}, nil
    76  }
    77  
    78  func (ui *headlessUi) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
    79  	approval := <-ui.approveCh
    80  	//fmt.Printf("approval %s\n", approval)
    81  	switch approval {
    82  	case "A":
    83  		return core.ListResponse{request.Accounts}, nil
    84  	case "1":
    85  		l := make([]accounts.Account, 1)
    86  		l[0] = request.Accounts[1]
    87  		return core.ListResponse{l}, nil
    88  	default:
    89  		return core.ListResponse{nil}, nil
    90  	}
    91  }
    92  
    93  func (ui *headlessUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
    94  	if <-ui.approveCh == "Y" {
    95  		return core.NewAccountResponse{true}, nil
    96  	}
    97  	return core.NewAccountResponse{false}, nil
    98  }
    99  
   100  func (ui *headlessUi) ShowError(message string) {
   101  	//stdout is used by communication
   102  	fmt.Fprintln(os.Stderr, message)
   103  }
   104  
   105  func (ui *headlessUi) ShowInfo(message string) {
   106  	//stdout is used by communication
   107  	fmt.Fprintln(os.Stderr, message)
   108  }
   109  
   110  func tmpDirName(t *testing.T) string {
   111  	d := t.TempDir()
   112  	d, err := filepath.EvalSymlinks(d)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	return d
   117  }
   118  
   119  func setup(t *testing.T) (*core.SignerAPI, *headlessUi) {
   120  	db, err := fourbyte.New()
   121  	if err != nil {
   122  		t.Fatal(err.Error())
   123  	}
   124  	ui := &headlessUi{make(chan string, 20), make(chan string, 20)}
   125  	am := core.StartClefAccountManager(tmpDirName(t), true, true, "")
   126  	api := core.NewSignerAPI(am, 1337, true, ui, db, true, &storage.NoStorage{})
   127  	return api, ui
   128  
   129  }
   130  func createAccount(ui *headlessUi, api *core.SignerAPI, t *testing.T) {
   131  	ui.approveCh <- "Y"
   132  	ui.inputCh <- "a_long_password"
   133  	_, err := api.New(context.Background())
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  	// Some time to allow changes to propagate
   138  	time.Sleep(250 * time.Millisecond)
   139  }
   140  
   141  func failCreateAccountWithPassword(ui *headlessUi, api *core.SignerAPI, password string, t *testing.T) {
   142  
   143  	ui.approveCh <- "Y"
   144  	// We will be asked three times to provide a suitable password
   145  	ui.inputCh <- password
   146  	ui.inputCh <- password
   147  	ui.inputCh <- password
   148  
   149  	addr, err := api.New(context.Background())
   150  	if err == nil {
   151  		t.Fatal("Should have returned an error")
   152  	}
   153  	if addr != (common.Address{}) {
   154  		t.Fatal("Empty address should be returned")
   155  	}
   156  }
   157  
   158  func failCreateAccount(ui *headlessUi, api *core.SignerAPI, t *testing.T) {
   159  	ui.approveCh <- "N"
   160  	addr, err := api.New(context.Background())
   161  	if err != core.ErrRequestDenied {
   162  		t.Fatal(err)
   163  	}
   164  	if addr != (common.Address{}) {
   165  		t.Fatal("Empty address should be returned")
   166  	}
   167  }
   168  
   169  func list(ui *headlessUi, api *core.SignerAPI, t *testing.T) ([]common.Address, error) {
   170  	ui.approveCh <- "A"
   171  	return api.List(context.Background())
   172  
   173  }
   174  
   175  func TestNewAcc(t *testing.T) {
   176  	api, control := setup(t)
   177  	verifyNum := func(num int) {
   178  		list, err := list(control, api, t)
   179  		if err != nil {
   180  			t.Errorf("Unexpected error %v", err)
   181  		}
   182  		if len(list) != num {
   183  			t.Errorf("Expected %d accounts, got %d", num, len(list))
   184  		}
   185  	}
   186  	// Testing create and create-deny
   187  	createAccount(control, api, t)
   188  	createAccount(control, api, t)
   189  	failCreateAccount(control, api, t)
   190  	failCreateAccount(control, api, t)
   191  	createAccount(control, api, t)
   192  	failCreateAccount(control, api, t)
   193  	createAccount(control, api, t)
   194  	failCreateAccount(control, api, t)
   195  	verifyNum(4)
   196  
   197  	// Fail to create this, due to bad password
   198  	failCreateAccountWithPassword(control, api, "short", t)
   199  	failCreateAccountWithPassword(control, api, "longerbutbad\rfoo", t)
   200  	verifyNum(4)
   201  
   202  	// Testing listing:
   203  	// Listing one Account
   204  	control.approveCh <- "1"
   205  	list, err := api.List(context.Background())
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	if len(list) != 1 {
   210  		t.Fatalf("List should only show one Account")
   211  	}
   212  	// Listing denied
   213  	control.approveCh <- "Nope"
   214  	list, err = api.List(context.Background())
   215  	if len(list) != 0 {
   216  		t.Fatalf("List should be empty")
   217  	}
   218  	if err != core.ErrRequestDenied {
   219  		t.Fatal("Expected deny")
   220  	}
   221  }
   222  
   223  func mkTestTx(from common.MixedcaseAddress) apitypes.SendTxArgs {
   224  	to := common.NewMixedcaseAddress(common.HexToAddress("0x1337"))
   225  	gas := hexutil.Uint64(21000)
   226  	gasPrice := (hexutil.Big)(*big.NewInt(2000000000))
   227  	value := (hexutil.Big)(*big.NewInt(1e18))
   228  	nonce := (hexutil.Uint64)(0)
   229  	data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a"))
   230  	tx := apitypes.SendTxArgs{
   231  		From:     from,
   232  		To:       &to,
   233  		Gas:      gas,
   234  		GasPrice: &gasPrice,
   235  		Value:    value,
   236  		Data:     &data,
   237  		Nonce:    nonce}
   238  	return tx
   239  }
   240  
   241  func TestSignTx(t *testing.T) {
   242  	var (
   243  		list      []common.Address
   244  		res, res2 *ethapi.SignTransactionResult
   245  		err       error
   246  	)
   247  
   248  	api, control := setup(t)
   249  	createAccount(control, api, t)
   250  	control.approveCh <- "A"
   251  	list, err = api.List(context.Background())
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	if len(list) == 0 {
   256  		t.Fatal("Unexpected empty list")
   257  	}
   258  	a := common.NewMixedcaseAddress(list[0])
   259  
   260  	methodSig := "test(uint)"
   261  	tx := mkTestTx(a)
   262  
   263  	control.approveCh <- "Y"
   264  	control.inputCh <- "wrongpassword"
   265  	res, err = api.SignTransaction(context.Background(), tx, &methodSig)
   266  	if res != nil {
   267  		t.Errorf("Expected nil-response, got %v", res)
   268  	}
   269  	if err != keystore.ErrDecrypt {
   270  		t.Errorf("Expected ErrLocked! %v", err)
   271  	}
   272  	control.approveCh <- "No way"
   273  	res, err = api.SignTransaction(context.Background(), tx, &methodSig)
   274  	if res != nil {
   275  		t.Errorf("Expected nil-response, got %v", res)
   276  	}
   277  	if err != core.ErrRequestDenied {
   278  		t.Errorf("Expected ErrRequestDenied! %v", err)
   279  	}
   280  	// Sign with correct password
   281  	control.approveCh <- "Y"
   282  	control.inputCh <- "a_long_password"
   283  	res, err = api.SignTransaction(context.Background(), tx, &methodSig)
   284  
   285  	if err != nil {
   286  		t.Fatal(err)
   287  	}
   288  	parsedTx := &types.Transaction{}
   289  	rlp.Decode(bytes.NewReader(res.Raw), parsedTx)
   290  
   291  	//The tx should NOT be modified by the UI
   292  	if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 {
   293  		t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value())
   294  	}
   295  	control.approveCh <- "Y"
   296  	control.inputCh <- "a_long_password"
   297  
   298  	res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  	if !bytes.Equal(res.Raw, res2.Raw) {
   303  		t.Error("Expected tx to be unmodified by UI")
   304  	}
   305  
   306  	//The tx is modified by the UI
   307  	control.approveCh <- "M"
   308  	control.inputCh <- "a_long_password"
   309  
   310  	res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  	parsedTx2 := &types.Transaction{}
   315  	rlp.Decode(bytes.NewReader(res.Raw), parsedTx2)
   316  
   317  	//The tx should be modified by the UI
   318  	if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 {
   319  		t.Errorf("Expected value to be unchanged, got %v", parsedTx.Value())
   320  	}
   321  	if bytes.Equal(res.Raw, res2.Raw) {
   322  		t.Error("Expected tx to be modified by UI")
   323  	}
   324  
   325  }