123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- // Copyright 2018 The go-ethereum Authors
- // This file is part of go-ethereum.
- //
- // go-ethereum is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // go-ethereum is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
- //
- package core
- import (
- "bytes"
- "context"
- "fmt"
- "io/ioutil"
- "math/big"
- "os"
- "path/filepath"
- "testing"
- "time"
- "github.com/ethereum/go-ethereum/accounts/keystore"
- "github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/internal/ethapi"
- "github.com/ethereum/go-ethereum/rlp"
- )
- //Used for testing
- type HeadlessUI struct {
- controller chan string
- }
- func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) {
- }
- func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
- fmt.Printf("OnApproved called")
- }
- func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
- switch <-ui.controller {
- case "Y":
- return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
- case "M": //Modify
- old := big.Int(request.Transaction.Value)
- newVal := big.NewInt(0).Add(&old, big.NewInt(1))
- request.Transaction.Value = hexutil.Big(*newVal)
- return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
- default:
- return SignTxResponse{request.Transaction, false, ""}, nil
- }
- }
- func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
- if "Y" == <-ui.controller {
- return SignDataResponse{true, <-ui.controller}, nil
- }
- return SignDataResponse{false, ""}, nil
- }
- func (ui *HeadlessUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
- return ExportResponse{<-ui.controller == "Y"}, nil
- }
- func (ui *HeadlessUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
- if "Y" == <-ui.controller {
- return ImportResponse{true, <-ui.controller, <-ui.controller}, nil
- }
- return ImportResponse{false, "", ""}, nil
- }
- func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) {
- switch <-ui.controller {
- case "A":
- return ListResponse{request.Accounts}, nil
- case "1":
- l := make([]Account, 1)
- l[0] = request.Accounts[1]
- return ListResponse{l}, nil
- default:
- return ListResponse{nil}, nil
- }
- }
- func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
- if "Y" == <-ui.controller {
- return NewAccountResponse{true, <-ui.controller}, nil
- }
- return NewAccountResponse{false, ""}, nil
- }
- func (ui *HeadlessUI) ShowError(message string) {
- //stdout is used by communication
- fmt.Fprint(os.Stderr, message)
- }
- func (ui *HeadlessUI) ShowInfo(message string) {
- //stdout is used by communication
- fmt.Fprint(os.Stderr, message)
- }
- func tmpDirName(t *testing.T) string {
- d, err := ioutil.TempDir("", "eth-keystore-test")
- if err != nil {
- t.Fatal(err)
- }
- d, err = filepath.EvalSymlinks(d)
- if err != nil {
- t.Fatal(err)
- }
- return d
- }
- func setup(t *testing.T) (*SignerAPI, chan string) {
- controller := make(chan string, 10)
- db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json")
- if err != nil {
- utils.Fatalf(err.Error())
- }
- var (
- ui = &HeadlessUI{controller}
- api = NewSignerAPI(
- 1,
- tmpDirName(t),
- true,
- ui,
- db,
- true)
- )
- return api, controller
- }
- func createAccount(control chan string, api *SignerAPI, t *testing.T) {
- control <- "Y"
- control <- "apassword"
- _, err := api.New(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- // Some time to allow changes to propagate
- time.Sleep(250 * time.Millisecond)
- }
- func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
- control <- "N"
- acc, err := api.New(context.Background())
- if err != ErrRequestDenied {
- t.Fatal(err)
- }
- if acc.Address != (common.Address{}) {
- t.Fatal("Empty address should be returned")
- }
- }
- func list(control chan string, api *SignerAPI, t *testing.T) []Account {
- control <- "A"
- list, err := api.List(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- return list
- }
- func TestNewAcc(t *testing.T) {
- api, control := setup(t)
- verifyNum := func(num int) {
- if list := list(control, api, t); len(list) != num {
- t.Errorf("Expected %d accounts, got %d", num, len(list))
- }
- }
- // Testing create and create-deny
- createAccount(control, api, t)
- createAccount(control, api, t)
- failCreateAccount(control, api, t)
- failCreateAccount(control, api, t)
- createAccount(control, api, t)
- failCreateAccount(control, api, t)
- createAccount(control, api, t)
- failCreateAccount(control, api, t)
- verifyNum(4)
- // Testing listing:
- // Listing one Account
- control <- "1"
- list, err := api.List(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- if len(list) != 1 {
- t.Fatalf("List should only show one Account")
- }
- // Listing denied
- control <- "Nope"
- list, err = api.List(context.Background())
- if len(list) != 0 {
- t.Fatalf("List should be empty")
- }
- if err != ErrRequestDenied {
- t.Fatal("Expected deny")
- }
- }
- func TestSignData(t *testing.T) {
- api, control := setup(t)
- //Create two accounts
- createAccount(control, api, t)
- createAccount(control, api, t)
- control <- "1"
- list, err := api.List(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- a := common.NewMixedcaseAddress(list[0].Address)
- control <- "Y"
- control <- "wrongpassword"
- h, err := api.Sign(context.Background(), a, []byte("EHLO world"))
- if h != nil {
- t.Errorf("Expected nil-data, got %x", h)
- }
- if err != keystore.ErrDecrypt {
- t.Errorf("Expected ErrLocked! %v", err)
- }
- control <- "No way"
- h, err = api.Sign(context.Background(), a, []byte("EHLO world"))
- if h != nil {
- t.Errorf("Expected nil-data, got %x", h)
- }
- if err != ErrRequestDenied {
- t.Errorf("Expected ErrRequestDenied! %v", err)
- }
- control <- "Y"
- control <- "apassword"
- h, err = api.Sign(context.Background(), a, []byte("EHLO world"))
- if err != nil {
- t.Fatal(err)
- }
- if h == nil || len(h) != 65 {
- t.Errorf("Expected 65 byte signature (got %d bytes)", len(h))
- }
- }
- func mkTestTx(from common.MixedcaseAddress) SendTxArgs {
- to := common.NewMixedcaseAddress(common.HexToAddress("0x1337"))
- gas := hexutil.Uint64(21000)
- gasPrice := (hexutil.Big)(*big.NewInt(2000000000))
- value := (hexutil.Big)(*big.NewInt(1e18))
- nonce := (hexutil.Uint64)(0)
- data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a"))
- tx := SendTxArgs{
- From: from,
- To: &to,
- Gas: gas,
- GasPrice: gasPrice,
- Value: value,
- Data: &data,
- Nonce: nonce}
- return tx
- }
- func TestSignTx(t *testing.T) {
- var (
- list Accounts
- res, res2 *ethapi.SignTransactionResult
- err error
- )
- api, control := setup(t)
- createAccount(control, api, t)
- control <- "A"
- list, err = api.List(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- a := common.NewMixedcaseAddress(list[0].Address)
- methodSig := "test(uint)"
- tx := mkTestTx(a)
- control <- "Y"
- control <- "wrongpassword"
- res, err = api.SignTransaction(context.Background(), tx, &methodSig)
- if res != nil {
- t.Errorf("Expected nil-response, got %v", res)
- }
- if err != keystore.ErrDecrypt {
- t.Errorf("Expected ErrLocked! %v", err)
- }
- control <- "No way"
- res, err = api.SignTransaction(context.Background(), tx, &methodSig)
- if res != nil {
- t.Errorf("Expected nil-response, got %v", res)
- }
- if err != ErrRequestDenied {
- t.Errorf("Expected ErrRequestDenied! %v", err)
- }
- control <- "Y"
- control <- "apassword"
- res, err = api.SignTransaction(context.Background(), tx, &methodSig)
- if err != nil {
- t.Fatal(err)
- }
- parsedTx := &types.Transaction{}
- rlp.Decode(bytes.NewReader(res.Raw), parsedTx)
- //The tx should NOT be modified by the UI
- if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 {
- t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value())
- }
- control <- "Y"
- control <- "apassword"
- res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(res.Raw, res2.Raw) {
- t.Error("Expected tx to be unmodified by UI")
- }
- //The tx is modified by the UI
- control <- "M"
- control <- "apassword"
- res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
- if err != nil {
- t.Fatal(err)
- }
- parsedTx2 := &types.Transaction{}
- rlp.Decode(bytes.NewReader(res.Raw), parsedTx2)
- //The tx should be modified by the UI
- if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 {
- t.Errorf("Expected value to be unchanged, got %v", parsedTx.Value())
- }
- if bytes.Equal(res.Raw, res2.Raw) {
- t.Error("Expected tx to be modified by UI")
- }
- }
- /*
- func TestAsyncronousResponses(t *testing.T){
- //Set up one account
- api, control := setup(t)
- createAccount(control, api, t)
- // Two transactions, the second one with larger value than the first
- tx1 := mkTestTx()
- newVal := big.NewInt(0).Add((*big.Int) (tx1.Value), big.NewInt(1))
- tx2 := mkTestTx()
- tx2.Value = (*hexutil.Big)(newVal)
- control <- "W" //wait
- control <- "Y" //
- control <- "apassword"
- control <- "Y" //
- control <- "apassword"
- var err error
- h1, err := api.SignTransaction(context.Background(), common.HexToAddress("1111"), tx1, nil)
- h2, err := api.SignTransaction(context.Background(), common.HexToAddress("2222"), tx2, nil)
- }
- */
|