github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/accounts/addrcache_test.go (about)

     1  // Copyright 2016 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 accounts
    18  
    19  import (
    20  	"fmt"
    21  	"math/rand"
    22  	"os"
    23  	"path/filepath"
    24  	"reflect"
    25  	"sort"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/cespare/cp"
    30  	"github.com/davecgh/go-spew/spew"
    31  	"github.com/ethereum/go-ethereum/common"
    32  )
    33  
    34  var (
    35  	cachetestDir, _   = filepath.Abs(filepath.Join("testdata", "keystore"))
    36  	cachetestAccounts = []Account{
    37  		{
    38  			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
    39  			File:    filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
    40  		},
    41  		{
    42  			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
    43  			File:    filepath.Join(cachetestDir, "aaa"),
    44  		},
    45  		{
    46  			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
    47  			File:    filepath.Join(cachetestDir, "zzz"),
    48  		},
    49  	}
    50  )
    51  
    52  func TestWatchNewFile(t *testing.T) {
    53  	t.Parallel()
    54  
    55  	dir, am := tmpManager(t, false)
    56  	defer os.RemoveAll(dir)
    57  
    58  	// Ensure the watcher is started before adding any files.
    59  	am.Accounts()
    60  	time.Sleep(200 * time.Millisecond)
    61  
    62  	// Move in the files.
    63  	wantAccounts := make([]Account, len(cachetestAccounts))
    64  	for i := range cachetestAccounts {
    65  		a := cachetestAccounts[i]
    66  		a.File = filepath.Join(dir, filepath.Base(a.File))
    67  		wantAccounts[i] = a
    68  		if err := cp.CopyFile(a.File, cachetestAccounts[i].File); err != nil {
    69  			t.Fatal(err)
    70  		}
    71  	}
    72  
    73  	// am should see the accounts.
    74  	var list []Account
    75  	for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
    76  		list = am.Accounts()
    77  		if reflect.DeepEqual(list, wantAccounts) {
    78  			return
    79  		}
    80  		time.Sleep(d)
    81  	}
    82  	t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts))
    83  }
    84  
    85  func TestWatchNoDir(t *testing.T) {
    86  	t.Parallel()
    87  
    88  	// Create am but not the directory that it watches.
    89  	rand.Seed(time.Now().UnixNano())
    90  	dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
    91  	am := NewManager(dir, LightScryptN, LightScryptP)
    92  
    93  	list := am.Accounts()
    94  	if len(list) > 0 {
    95  		t.Error("initial account list not empty:", list)
    96  	}
    97  	time.Sleep(100 * time.Millisecond)
    98  
    99  	// Create the directory and copy a key file into it.
   100  	os.MkdirAll(dir, 0700)
   101  	defer os.RemoveAll(dir)
   102  	file := filepath.Join(dir, "aaa")
   103  	if err := cp.CopyFile(file, cachetestAccounts[0].File); err != nil {
   104  		t.Fatal(err)
   105  	}
   106  
   107  	// am should see the account.
   108  	wantAccounts := []Account{cachetestAccounts[0]}
   109  	wantAccounts[0].File = file
   110  	for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
   111  		list = am.Accounts()
   112  		if reflect.DeepEqual(list, wantAccounts) {
   113  			return
   114  		}
   115  		time.Sleep(d)
   116  	}
   117  	t.Errorf("\ngot  %v\nwant %v", list, wantAccounts)
   118  }
   119  
   120  func TestCacheInitialReload(t *testing.T) {
   121  	cache := newAddrCache(cachetestDir)
   122  	accounts := cache.accounts()
   123  	if !reflect.DeepEqual(accounts, cachetestAccounts) {
   124  		t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
   125  	}
   126  }
   127  
   128  func TestCacheAddDeleteOrder(t *testing.T) {
   129  	cache := newAddrCache("testdata/no-such-dir")
   130  	cache.watcher.running = true // prevent unexpected reloads
   131  
   132  	accounts := []Account{
   133  		{
   134  			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
   135  			File:    "-309830980",
   136  		},
   137  		{
   138  			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
   139  			File:    "ggg",
   140  		},
   141  		{
   142  			Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"),
   143  			File:    "zzzzzz-the-very-last-one.keyXXX",
   144  		},
   145  		{
   146  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   147  			File:    "SOMETHING.key",
   148  		},
   149  		{
   150  			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
   151  			File:    "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
   152  		},
   153  		{
   154  			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
   155  			File:    "aaa",
   156  		},
   157  		{
   158  			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
   159  			File:    "zzz",
   160  		},
   161  	}
   162  	for _, a := range accounts {
   163  		cache.add(a)
   164  	}
   165  	// Add some of them twice to check that they don't get reinserted.
   166  	cache.add(accounts[0])
   167  	cache.add(accounts[2])
   168  
   169  	// Check that the account list is sorted by filename.
   170  	wantAccounts := make([]Account, len(accounts))
   171  	copy(wantAccounts, accounts)
   172  	sort.Sort(accountsByFile(wantAccounts))
   173  	list := cache.accounts()
   174  	if !reflect.DeepEqual(list, wantAccounts) {
   175  		t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accounts), spew.Sdump(wantAccounts))
   176  	}
   177  	for _, a := range accounts {
   178  		if !cache.hasAddress(a.Address) {
   179  			t.Errorf("expected hasAccount(%x) to return true", a.Address)
   180  		}
   181  	}
   182  	if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
   183  		t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
   184  	}
   185  
   186  	// Delete a few keys from the cache.
   187  	for i := 0; i < len(accounts); i += 2 {
   188  		cache.delete(wantAccounts[i])
   189  	}
   190  	cache.delete(Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), File: "something"})
   191  
   192  	// Check content again after deletion.
   193  	wantAccountsAfterDelete := []Account{
   194  		wantAccounts[1],
   195  		wantAccounts[3],
   196  		wantAccounts[5],
   197  	}
   198  	list = cache.accounts()
   199  	if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
   200  		t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
   201  	}
   202  	for _, a := range wantAccountsAfterDelete {
   203  		if !cache.hasAddress(a.Address) {
   204  			t.Errorf("expected hasAccount(%x) to return true", a.Address)
   205  		}
   206  	}
   207  	if cache.hasAddress(wantAccounts[0].Address) {
   208  		t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
   209  	}
   210  }
   211  
   212  func TestCacheFind(t *testing.T) {
   213  	dir := filepath.Join("testdata", "dir")
   214  	cache := newAddrCache(dir)
   215  	cache.watcher.running = true // prevent unexpected reloads
   216  
   217  	accounts := []Account{
   218  		{
   219  			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
   220  			File:    filepath.Join(dir, "a.key"),
   221  		},
   222  		{
   223  			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
   224  			File:    filepath.Join(dir, "b.key"),
   225  		},
   226  		{
   227  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   228  			File:    filepath.Join(dir, "c.key"),
   229  		},
   230  		{
   231  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   232  			File:    filepath.Join(dir, "c2.key"),
   233  		},
   234  	}
   235  	for _, a := range accounts {
   236  		cache.add(a)
   237  	}
   238  
   239  	nomatchAccount := Account{
   240  		Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
   241  		File:    filepath.Join(dir, "something"),
   242  	}
   243  	tests := []struct {
   244  		Query      Account
   245  		WantResult Account
   246  		WantError  error
   247  	}{
   248  		// by address
   249  		{Query: Account{Address: accounts[0].Address}, WantResult: accounts[0]},
   250  		// by file
   251  		{Query: Account{File: accounts[0].File}, WantResult: accounts[0]},
   252  		// by basename
   253  		{Query: Account{File: filepath.Base(accounts[0].File)}, WantResult: accounts[0]},
   254  		// by file and address
   255  		{Query: accounts[0], WantResult: accounts[0]},
   256  		// ambiguous address, tie resolved by file
   257  		{Query: accounts[2], WantResult: accounts[2]},
   258  		// ambiguous address error
   259  		{
   260  			Query: Account{Address: accounts[2].Address},
   261  			WantError: &AmbiguousAddrError{
   262  				Addr:    accounts[2].Address,
   263  				Matches: []Account{accounts[2], accounts[3]},
   264  			},
   265  		},
   266  		// no match error
   267  		{Query: nomatchAccount, WantError: ErrNoMatch},
   268  		{Query: Account{File: nomatchAccount.File}, WantError: ErrNoMatch},
   269  		{Query: Account{File: filepath.Base(nomatchAccount.File)}, WantError: ErrNoMatch},
   270  		{Query: Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch},
   271  	}
   272  	for i, test := range tests {
   273  		a, err := cache.find(test.Query)
   274  		if !reflect.DeepEqual(err, test.WantError) {
   275  			t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
   276  			continue
   277  		}
   278  		if a != test.WantResult {
   279  			t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
   280  			continue
   281  		}
   282  	}
   283  }