github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/chain/accounts/keystore/account_cache_test.go (about)

     1  package keystore
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"math/rand"
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  	"sort"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/cespare/cp"
    15  	"github.com/davecgh/go-spew/spew"
    16  	"github.com/neatlab/neatio/chain/accounts"
    17  	"github.com/neatlab/neatio/utilities/common"
    18  )
    19  
    20  var (
    21  	cachetestDir, _   = filepath.Abs(filepath.Join("testdata", "keystore"))
    22  	cachetestAccounts = []accounts.Account{
    23  		{
    24  			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
    25  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8")},
    26  		},
    27  		{
    28  			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
    29  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "aaa")},
    30  		},
    31  		{
    32  			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
    33  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "zzz")},
    34  		},
    35  	}
    36  )
    37  
    38  func TestWatchNewFile(t *testing.T) {
    39  	t.Parallel()
    40  
    41  	dir, ks := tmpKeyStore(t, false)
    42  	defer os.RemoveAll(dir)
    43  
    44  	ks.Accounts()
    45  	time.Sleep(1000 * time.Millisecond)
    46  
    47  	wantAccounts := make([]accounts.Account, len(cachetestAccounts))
    48  	for i := range cachetestAccounts {
    49  		wantAccounts[i] = accounts.Account{
    50  			Address: cachetestAccounts[i].Address,
    51  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, filepath.Base(cachetestAccounts[i].URL.Path))},
    52  		}
    53  		if err := cp.CopyFile(wantAccounts[i].URL.Path, cachetestAccounts[i].URL.Path); err != nil {
    54  			t.Fatal(err)
    55  		}
    56  	}
    57  
    58  	var list []accounts.Account
    59  	for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
    60  		list = ks.Accounts()
    61  		if reflect.DeepEqual(list, wantAccounts) {
    62  
    63  			select {
    64  			case <-ks.changes:
    65  			default:
    66  				t.Fatalf("wasn't notified of new accounts")
    67  			}
    68  			return
    69  		}
    70  		time.Sleep(d)
    71  	}
    72  	t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts))
    73  }
    74  
    75  func TestWatchNoDir(t *testing.T) {
    76  	t.Parallel()
    77  
    78  	rand.Seed(time.Now().UnixNano())
    79  	dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
    80  	ks := NewKeyStore(dir, LightScryptN, LightScryptP)
    81  
    82  	list := ks.Accounts()
    83  	if len(list) > 0 {
    84  		t.Error("initial account list not empty:", list)
    85  	}
    86  	time.Sleep(100 * time.Millisecond)
    87  
    88  	os.MkdirAll(dir, 0700)
    89  	defer os.RemoveAll(dir)
    90  	file := filepath.Join(dir, "aaa")
    91  	if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	wantAccounts := []accounts.Account{cachetestAccounts[0]}
    96  	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
    97  	for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
    98  		list = ks.Accounts()
    99  		if reflect.DeepEqual(list, wantAccounts) {
   100  
   101  			select {
   102  			case <-ks.changes:
   103  			default:
   104  				t.Fatalf("wasn't notified of new accounts")
   105  			}
   106  			return
   107  		}
   108  		time.Sleep(d)
   109  	}
   110  	t.Errorf("\ngot  %v\nwant %v", list, wantAccounts)
   111  }
   112  
   113  func TestCacheInitialReload(t *testing.T) {
   114  	cache, _ := newAccountCache(cachetestDir)
   115  	accounts := cache.accounts()
   116  	for _, account := range accounts {
   117  		fmt.Printf("TestCacheInitialReload account=%v\n", account)
   118  	}
   119  	if !reflect.DeepEqual(accounts, cachetestAccounts) {
   120  		t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
   121  	}
   122  }
   123  
   124  func TestCacheAddDeleteOrder(t *testing.T) {
   125  	cache, _ := newAccountCache("testdata/no-such-dir")
   126  	cache.watcher.running = true
   127  
   128  	accs := []accounts.Account{
   129  		{
   130  			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
   131  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "-309830980"},
   132  		},
   133  		{
   134  			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
   135  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "ggg"},
   136  		},
   137  		{
   138  			Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"),
   139  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "zzzzzz-the-very-last-one.keyXXX"},
   140  		},
   141  		{
   142  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   143  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "SOMETHING.key"},
   144  		},
   145  		{
   146  			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
   147  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"},
   148  		},
   149  		{
   150  			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
   151  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "aaa"},
   152  		},
   153  		{
   154  			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
   155  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "zzz"},
   156  		},
   157  	}
   158  	for _, a := range accs {
   159  		cache.add(a)
   160  	}
   161  
   162  	cache.add(accs[0])
   163  	cache.add(accs[2])
   164  
   165  	wantAccounts := make([]accounts.Account, len(accs))
   166  	copy(wantAccounts, accs)
   167  	sort.Sort(accountsByURL(wantAccounts))
   168  	list := cache.accounts()
   169  	if !reflect.DeepEqual(list, wantAccounts) {
   170  		t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
   171  	}
   172  	for _, a := range accs {
   173  		if !cache.hasAddress(a.Address) {
   174  			t.Errorf("expected hasAccount(%x) to return true", a.Address)
   175  		}
   176  	}
   177  	if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
   178  		t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
   179  	}
   180  
   181  	for i := 0; i < len(accs); i += 2 {
   182  		cache.delete(wantAccounts[i])
   183  	}
   184  	cache.delete(accounts.Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), URL: accounts.URL{Scheme: KeyStoreScheme, Path: "something"}})
   185  
   186  	wantAccountsAfterDelete := []accounts.Account{
   187  		wantAccounts[1],
   188  		wantAccounts[3],
   189  		wantAccounts[5],
   190  	}
   191  	list = cache.accounts()
   192  	if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
   193  		t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
   194  	}
   195  	for _, a := range wantAccountsAfterDelete {
   196  		if !cache.hasAddress(a.Address) {
   197  			t.Errorf("expected hasAccount(%x) to return true", a.Address)
   198  		}
   199  	}
   200  	if cache.hasAddress(wantAccounts[0].Address) {
   201  		t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
   202  	}
   203  }
   204  
   205  func TestCacheFind(t *testing.T) {
   206  	dir := filepath.Join("testdata", "dir")
   207  	cache, _ := newAccountCache(dir)
   208  	cache.watcher.running = true
   209  
   210  	accs := []accounts.Account{
   211  		{
   212  			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
   213  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "a.key")},
   214  		},
   215  		{
   216  			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
   217  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "b.key")},
   218  		},
   219  		{
   220  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   221  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c.key")},
   222  		},
   223  		{
   224  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   225  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c2.key")},
   226  		},
   227  		{
   228  			Address: common.HexToAddress("3Nm7pC9jsTZyLLiTtbX8aRiYWC3HHjW9SU"),
   229  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "d.key")},
   230  		},
   231  	}
   232  	for _, a := range accs {
   233  		cache.add(a)
   234  	}
   235  
   236  	nomatchAccount := accounts.Account{
   237  		Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
   238  		URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
   239  	}
   240  	tests := []struct {
   241  		Query      accounts.Account
   242  		WantResult accounts.Account
   243  		WantError  error
   244  	}{
   245  
   246  		{Query: accounts.Account{Address: accs[0].Address}, WantResult: accs[0]},
   247  
   248  		{Query: accounts.Account{URL: accs[0].URL}, WantResult: accs[0]},
   249  
   250  		{Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(accs[0].URL.Path)}}, WantResult: accs[0]},
   251  
   252  		{Query: accs[0], WantResult: accs[0]},
   253  
   254  		{Query: accs[2], WantResult: accs[2]},
   255  
   256  		{
   257  			Query: accounts.Account{Address: accs[2].Address},
   258  			WantError: &AmbiguousAddrError{
   259  				Addr:    accs[2].Address,
   260  				Matches: []accounts.Account{accs[2], accs[3]},
   261  			},
   262  		},
   263  
   264  		{Query: nomatchAccount, WantError: ErrNoMatch},
   265  		{Query: accounts.Account{URL: nomatchAccount.URL}, WantError: ErrNoMatch},
   266  		{Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(nomatchAccount.URL.Path)}}, WantError: ErrNoMatch},
   267  		{Query: accounts.Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch},
   268  	}
   269  	for i, test := range tests {
   270  		a, err := cache.find(test.Query)
   271  		if !reflect.DeepEqual(err, test.WantError) {
   272  			t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
   273  			continue
   274  		}
   275  		if a != test.WantResult {
   276  			t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
   277  			continue
   278  		}
   279  	}
   280  }
   281  
   282  func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
   283  	var list []accounts.Account
   284  	for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
   285  		list = ks.Accounts()
   286  		if reflect.DeepEqual(list, wantAccounts) {
   287  
   288  			select {
   289  			case <-ks.changes:
   290  			default:
   291  				return fmt.Errorf("wasn't notified of new accounts")
   292  			}
   293  			return nil
   294  		}
   295  		time.Sleep(d)
   296  	}
   297  	return fmt.Errorf("\ngot  %v\nwant %v", list, wantAccounts)
   298  }
   299  
   300  func TestUpdatedKeyfileContents(t *testing.T) {
   301  	t.Parallel()
   302  
   303  	rand.Seed(time.Now().UnixNano())
   304  	dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
   305  	ks := NewKeyStore(dir, LightScryptN, LightScryptP)
   306  
   307  	list := ks.Accounts()
   308  	if len(list) > 0 {
   309  		t.Error("initial account list not empty:", list)
   310  	}
   311  	time.Sleep(100 * time.Millisecond)
   312  
   313  	os.MkdirAll(dir, 0700)
   314  	defer os.RemoveAll(dir)
   315  	file := filepath.Join(dir, "aaa")
   316  
   317  	if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
   318  		t.Fatal(err)
   319  	}
   320  
   321  	wantAccounts := []accounts.Account{cachetestAccounts[0]}
   322  	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
   323  	if err := waitForAccounts(wantAccounts, ks); err != nil {
   324  		t.Error(err)
   325  		return
   326  	}
   327  
   328  	time.Sleep(1000 * time.Millisecond)
   329  
   330  	if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
   331  		t.Fatal(err)
   332  		return
   333  	}
   334  	wantAccounts = []accounts.Account{cachetestAccounts[1]}
   335  	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
   336  	if err := waitForAccounts(wantAccounts, ks); err != nil {
   337  		t.Errorf("First replacement failed")
   338  		t.Error(err)
   339  		return
   340  	}
   341  
   342  	time.Sleep(1000 * time.Millisecond)
   343  
   344  	if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
   345  		t.Fatal(err)
   346  		return
   347  	}
   348  	wantAccounts = []accounts.Account{cachetestAccounts[2]}
   349  	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
   350  	if err := waitForAccounts(wantAccounts, ks); err != nil {
   351  		t.Errorf("Second replacement failed")
   352  		t.Error(err)
   353  		return
   354  	}
   355  
   356  	time.Sleep(1000 * time.Millisecond)
   357  
   358  	if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil {
   359  		t.Fatal(err)
   360  		return
   361  	}
   362  	if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
   363  		t.Errorf("Emptying account file failed")
   364  		t.Error(err)
   365  		return
   366  	}
   367  }
   368  
   369  func forceCopyFile(dst, src string) error {
   370  	data, err := ioutil.ReadFile(src)
   371  	if err != nil {
   372  		return err
   373  	}
   374  	return ioutil.WriteFile(dst, data, 0644)
   375  }