github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/signer/core/api_test.go (about)

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