github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/accounts/cache_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  // +build !deterministic
    18  
    19  package accounts
    20  
    21  import (
    22  	"fmt"
    23  	"io/ioutil"
    24  	"math/rand"
    25  	"os"
    26  	"path/filepath"
    27  	"reflect"
    28  	"sort"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/davecgh/go-spew/spew"
    33  	"github.com/ethereumproject/go-ethereum/common"
    34  )
    35  
    36  func TestWatchNewFile(t *testing.T) {
    37  	t.Parallel()
    38  
    39  	dir, am := tmpManager(t)
    40  	defer os.RemoveAll(dir)
    41  
    42  	// Ensure the watcher is started before adding any files.
    43  	am.Accounts()
    44  	time.Sleep(5 * time.Second)
    45  	if w := am.ac.getWatcher(); !w.running {
    46  		t.Fatalf("watcher not running after %v: %v", 5*time.Second, spew.Sdump(w))
    47  	}
    48  
    49  	// Move in the files.
    50  	wantAccounts := make([]Account, len(cachetestAccounts))
    51  	for i := range cachetestAccounts {
    52  		a := cachetestAccounts[i]
    53  		a.File = filepath.Join(dir, filepath.Base(a.File)) // rename test file to base from temp dir
    54  		wantAccounts[i] = a
    55  		data, err := ioutil.ReadFile(cachetestAccounts[i].File) // but we still have to read from original file path
    56  		if err != nil {
    57  			t.Fatal(err)
    58  		}
    59  		// write to temp dir
    60  		if err := ioutil.WriteFile(a.File, data, 0666); err != nil {
    61  			t.Fatal(err)
    62  		}
    63  	}
    64  	sort.Sort(accountsByFile(wantAccounts))
    65  
    66  	// am should see the accounts.
    67  	var list []Account
    68  	for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
    69  		list = am.Accounts()
    70  		if reflect.DeepEqual(list, wantAccounts) {
    71  			return
    72  		}
    73  		time.Sleep(d)
    74  	}
    75  	t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts))
    76  }
    77  
    78  func TestWatchNoDir(t *testing.T) {
    79  	t.Parallel()
    80  
    81  	// Create am but not the directory that it watches.
    82  	rand.Seed(time.Now().UnixNano())
    83  	rp := fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int())
    84  	dir, e := ioutil.TempDir("", rp)
    85  	if e != nil {
    86  		t.Fatal(e)
    87  	}
    88  	defer os.RemoveAll(dir)
    89  
    90  	am, err := NewManager(dir, LightScryptN, LightScryptP, false)
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	list := am.Accounts()
    96  	if len(list) > 0 {
    97  		t.Error("initial account list not empty:", list)
    98  	}
    99  	time.Sleep(5 * time.Second)
   100  	if w := am.ac.getWatcher(); !w.running {
   101  		t.Fatalf("watcher not running after %v: %v", 5*time.Second, spew.Sdump(w))
   102  	}
   103  
   104  	// Create the directory and copy a key file into it.
   105  	os.MkdirAll(dir, 0700)
   106  	defer os.RemoveAll(dir)
   107  
   108  	file := filepath.Join(dir, "aaa")
   109  	// This filepath-ing is redundant but ensure parallel tests don't fuck each other up... I think.
   110  	data, err := ioutil.ReadFile(filepath.Join(cachetestDir, filepath.Base(cachetestAccounts[0].File)))
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	ff, err := os.Create(file)
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  	_, err = ff.Write(data)
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	ff.Close()
   123  
   124  	// am should see the account.
   125  	a := cachetestAccounts[0]
   126  	a.File = file
   127  	wantAccounts := []Account{a}
   128  	var gotAccounts []Account
   129  	for d := 500 * time.Millisecond; d <= 2*minReloadInterval; d *= 2 {
   130  		gotAccounts = am.Accounts()
   131  		if reflect.DeepEqual(gotAccounts, wantAccounts) {
   132  			return
   133  		}
   134  		time.Sleep(d)
   135  	}
   136  	t.Errorf("account watcher never saw changes: got: %v, want: %v", spew.Sdump(gotAccounts), spew.Sdump(wantAccounts))
   137  }
   138  
   139  func TestCacheInitialReload(t *testing.T) {
   140  
   141  	cache := newAddrCache(cachetestDir)
   142  
   143  	accounts := cache.accounts()
   144  
   145  	if !reflect.DeepEqual(accounts, cachetestAccounts) {
   146  		t.Errorf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
   147  	}
   148  }
   149  
   150  func TestCacheAddDeleteOrder(t *testing.T) {
   151  	cache := newAddrCache("testdata/no-such-dir")
   152  	defer os.RemoveAll("testdata/no-such-dir")
   153  	cache.watcher.running = true // prevent unexpected reloads
   154  
   155  	accounts := []Account{
   156  		{
   157  			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
   158  			File:    "-309830980",
   159  		},
   160  		{
   161  			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
   162  			File:    "ggg",
   163  		},
   164  		{
   165  			Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"),
   166  			File:    "zzzzzz-the-very-last-one.keyXXX",
   167  		},
   168  		{
   169  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   170  			File:    "SOMETHING.key",
   171  		},
   172  		{
   173  			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
   174  			File:    "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
   175  		},
   176  		{
   177  			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
   178  			File:    "aaa",
   179  		},
   180  		{
   181  			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
   182  			File:    "zzz",
   183  		},
   184  	}
   185  	for _, a := range accounts {
   186  		cache.add(a)
   187  	}
   188  	// Add some of them twice to check that they don't get reinserted.
   189  	cache.add(accounts[0])
   190  	cache.add(accounts[2])
   191  
   192  	// Check that the account list is sorted by filename.
   193  	wantAccounts := make([]Account, len(accounts))
   194  	copy(wantAccounts, accounts)
   195  	sort.Sort(accountsByFile(wantAccounts))
   196  	list := cache.accounts()
   197  	if !reflect.DeepEqual(list, wantAccounts) {
   198  		t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accounts), spew.Sdump(wantAccounts))
   199  	}
   200  	for _, a := range accounts {
   201  		if !cache.hasAddress(a.Address) {
   202  			t.Errorf("expected hasAccount(%x) to return true", a.Address)
   203  		}
   204  	}
   205  	if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
   206  		t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
   207  	}
   208  
   209  	// Delete a few keys from the cache.
   210  	for i := 0; i < len(accounts); i += 2 {
   211  		cache.delete(wantAccounts[i])
   212  	}
   213  	cache.delete(Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), File: "something"})
   214  
   215  	// Check content again after deletion.
   216  	wantAccountsAfterDelete := []Account{
   217  		wantAccounts[1],
   218  		wantAccounts[3],
   219  		wantAccounts[5],
   220  	}
   221  	list = cache.accounts()
   222  	if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
   223  		t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
   224  	}
   225  	for _, a := range wantAccountsAfterDelete {
   226  		if !cache.hasAddress(a.Address) {
   227  			t.Errorf("expected hasAccount(%x) to return true", a.Address)
   228  		}
   229  	}
   230  	if cache.hasAddress(wantAccounts[0].Address) {
   231  		t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
   232  	}
   233  }
   234  
   235  func TestCacheFind(t *testing.T) {
   236  	dir := filepath.Join("testdata", "dir")
   237  	defer os.RemoveAll(dir)
   238  	cache := newAddrCache(dir)
   239  	cache.watcher.running = true // prevent unexpected reloads
   240  
   241  	accounts := []Account{
   242  		{
   243  			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
   244  			File:    filepath.Join(dir, "a.key"),
   245  		},
   246  		{
   247  			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
   248  			File:    filepath.Join(dir, "b.key"),
   249  		},
   250  		{
   251  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   252  			File:    filepath.Join(dir, "c.key"),
   253  		},
   254  		{
   255  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   256  			File:    filepath.Join(dir, "c2.key"),
   257  		},
   258  	}
   259  	for _, a := range accounts {
   260  		cache.add(a)
   261  	}
   262  
   263  	if lca := cache.accounts(); len(lca) != len(accounts) {
   264  		t.Fatalf("wrong number of accounts, got: %v, want: %v", len(lca), len(accounts))
   265  	}
   266  
   267  	if !reflect.DeepEqual(cache.accounts(), accounts) {
   268  		t.Fatalf("not matching initial accounts: got %v, want: %v", spew.Sdump(cache.accounts()), spew.Sdump(accounts))
   269  	}
   270  
   271  	nomatchAccount := Account{
   272  		Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
   273  		File:    filepath.Join(dir, "something"),
   274  	}
   275  	tests := []struct {
   276  		Query      Account
   277  		WantResult Account
   278  		WantError  error
   279  	}{
   280  		// by address
   281  		{Query: Account{Address: accounts[0].Address}, WantResult: accounts[0]},
   282  		// by file
   283  		{Query: Account{File: accounts[0].File}, WantResult: accounts[0]},
   284  		// by basename
   285  		{Query: Account{File: filepath.Base(accounts[0].File)}, WantResult: accounts[0]},
   286  		// by file and address
   287  		{Query: accounts[0], WantResult: accounts[0]},
   288  		// ambiguous address, tie resolved by file
   289  		{Query: accounts[2], WantResult: accounts[2]},
   290  		// ambiguous address error
   291  		{
   292  			Query: Account{Address: accounts[2].Address},
   293  			WantError: &AmbiguousAddrError{
   294  				Addr:    accounts[2].Address,
   295  				Matches: []Account{accounts[2], accounts[3]},
   296  			},
   297  		},
   298  		// no match error
   299  		{Query: nomatchAccount, WantError: ErrNoMatch},
   300  		{Query: Account{File: nomatchAccount.File}, WantError: ErrNoMatch},
   301  		{Query: Account{File: filepath.Base(nomatchAccount.File)}, WantError: ErrNoMatch},
   302  		{Query: Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch},
   303  	}
   304  	for i, test := range tests {
   305  		a, err := cache.find(test.Query)
   306  		if !reflect.DeepEqual(err, test.WantError) {
   307  			t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
   308  			continue
   309  		}
   310  		if a != test.WantResult {
   311  			t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
   312  			continue
   313  		}
   314  	}
   315  }
   316  
   317  func TestAccountCache_WatchRemove(t *testing.T) {
   318  	t.Parallel()
   319  
   320  	// setup temp dir
   321  	rp := fmt.Sprintf("eth-cacheremove-watch-test-%d-%d", os.Getpid(), rand.Int())
   322  	tmpDir, e := ioutil.TempDir("", rp)
   323  	if e != nil {
   324  		t.Fatalf("create temp dir: %v", e)
   325  	}
   326  	defer os.RemoveAll(tmpDir)
   327  
   328  	// copy 3 test account files into temp dir
   329  	wantAccounts := make([]Account, len(cachetestAccounts))
   330  	for i := range cachetestAccounts {
   331  		a := cachetestAccounts[i]
   332  		a.File = filepath.Join(tmpDir, filepath.Base(a.File))
   333  		wantAccounts[i] = a
   334  		data, err := ioutil.ReadFile(cachetestAccounts[i].File)
   335  		if err != nil {
   336  			t.Fatal(err)
   337  		}
   338  		ff, err := os.Create(a.File)
   339  		if err != nil {
   340  			t.Fatal(err)
   341  		}
   342  		_, err = ff.Write(data)
   343  		if err != nil {
   344  			t.Fatal(err)
   345  		}
   346  		ff.Close()
   347  	}
   348  
   349  	// make manager in temp dir
   350  	ma, e := NewManager(tmpDir, veryLightScryptN, veryLightScryptP, false)
   351  	if e != nil {
   352  		t.Errorf("create manager in temp dir: %v", e)
   353  	}
   354  
   355  	// test manager has all accounts
   356  	initAccs := ma.Accounts()
   357  	if !reflect.DeepEqual(initAccs, wantAccounts) {
   358  		t.Errorf("got %v, want: %v", spew.Sdump(initAccs), spew.Sdump(wantAccounts))
   359  	}
   360  	time.Sleep(minReloadInterval)
   361  
   362  	// test watcher is watching
   363  	if w := ma.ac.getWatcher(); !w.running {
   364  		t.Errorf("watcher not running")
   365  	}
   366  
   367  	// remove file
   368  	rmPath := filepath.Join(tmpDir, filepath.Base(wantAccounts[0].File))
   369  	if e := os.Remove(rmPath); e != nil {
   370  		t.Fatalf("removing key file: %v", e)
   371  	}
   372  	// ensure it's gone
   373  	if _, e := os.Stat(rmPath); e == nil {
   374  		t.Fatalf("removed file not actually rm'd")
   375  	}
   376  
   377  	// test manager does not have account
   378  	wantAccounts = wantAccounts[1:]
   379  	if len(wantAccounts) != 2 {
   380  		t.Errorf("dummy")
   381  	}
   382  
   383  	gotAccounts := []Account{}
   384  	for d := 500 * time.Millisecond; d < 5*time.Second; d *= 2 {
   385  		gotAccounts = ma.Accounts()
   386  		// If it's ever all the same, we're good. Exit with aplomb.
   387  		if reflect.DeepEqual(gotAccounts, wantAccounts) {
   388  			return
   389  		}
   390  		time.Sleep(d)
   391  	}
   392  	t.Errorf("got: %v, want: %v", spew.Sdump(gotAccounts), spew.Sdump(wantAccounts))
   393  }
   394  
   395  func TestCacheFilePath(t *testing.T) {
   396  	dir := filepath.Join("testdata", "keystore")
   397  	dir, _ = filepath.Abs(dir)
   398  	cache := newAddrCache(dir)
   399  
   400  	accs := cache.accounts()
   401  
   402  	for _, a := range accs {
   403  		if !filepath.IsAbs(a.File) {
   404  			t.Errorf("wanted absolute filepath, got: %v", a.File)
   405  		}
   406  	}
   407  
   408  	cache.close()
   409  }