aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/keystore/account_cache_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/keystore/account_cache_test.go')
-rw-r--r--accounts/keystore/account_cache_test.go297
1 files changed, 297 insertions, 0 deletions
diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go
new file mode 100644
index 000000000..554196321
--- /dev/null
+++ b/accounts/keystore/account_cache_test.go
@@ -0,0 +1,297 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package keystore
+
+import (
+ "fmt"
+ "math/rand"
+ "os"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "testing"
+ "time"
+
+ "github.com/cespare/cp"
+ "github.com/davecgh/go-spew/spew"
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+var (
+ cachetestDir, _ = filepath.Abs(filepath.Join("testdata", "keystore"))
+ cachetestAccounts = []accounts.Account{
+ {
+ Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8")},
+ },
+ {
+ Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "aaa")},
+ },
+ {
+ Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "zzz")},
+ },
+ }
+)
+
+func TestWatchNewFile(t *testing.T) {
+ t.Parallel()
+
+ dir, ks := tmpKeyStore(t, false)
+ defer os.RemoveAll(dir)
+
+ // Ensure the watcher is started before adding any files.
+ ks.Accounts()
+ time.Sleep(200 * time.Millisecond)
+
+ // Move in the files.
+ wantAccounts := make([]accounts.Account, len(cachetestAccounts))
+ for i := range cachetestAccounts {
+ wantAccounts[i] = accounts.Account{
+ Address: cachetestAccounts[i].Address,
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, filepath.Base(cachetestAccounts[i].URL.Path))},
+ }
+ if err := cp.CopyFile(wantAccounts[i].URL.Path, cachetestAccounts[i].URL.Path); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // ks should see the accounts.
+ var list []accounts.Account
+ for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
+ list = ks.Accounts()
+ if reflect.DeepEqual(list, wantAccounts) {
+ // ks should have also received change notifications
+ select {
+ case <-ks.changes:
+ default:
+ t.Fatalf("wasn't notified of new accounts")
+ }
+ return
+ }
+ time.Sleep(d)
+ }
+ t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts))
+}
+
+func TestWatchNoDir(t *testing.T) {
+ t.Parallel()
+
+ // Create ks but not the directory that it watches.
+ rand.Seed(time.Now().UnixNano())
+ dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
+ ks := NewKeyStore(dir, LightScryptN, LightScryptP)
+
+ list := ks.Accounts()
+ if len(list) > 0 {
+ t.Error("initial account list not empty:", list)
+ }
+ time.Sleep(100 * time.Millisecond)
+
+ // Create the directory and copy a key file into it.
+ os.MkdirAll(dir, 0700)
+ defer os.RemoveAll(dir)
+ file := filepath.Join(dir, "aaa")
+ if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
+ t.Fatal(err)
+ }
+
+ // ks should see the account.
+ wantAccounts := []accounts.Account{cachetestAccounts[0]}
+ wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
+ for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
+ list = ks.Accounts()
+ if reflect.DeepEqual(list, wantAccounts) {
+ // ks should have also received change notifications
+ select {
+ case <-ks.changes:
+ default:
+ t.Fatalf("wasn't notified of new accounts")
+ }
+ return
+ }
+ time.Sleep(d)
+ }
+ t.Errorf("\ngot %v\nwant %v", list, wantAccounts)
+}
+
+func TestCacheInitialReload(t *testing.T) {
+ cache, _ := newAccountCache(cachetestDir)
+ accounts := cache.accounts()
+ if !reflect.DeepEqual(accounts, cachetestAccounts) {
+ t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
+ }
+}
+
+func TestCacheAddDeleteOrder(t *testing.T) {
+ cache, _ := newAccountCache("testdata/no-such-dir")
+ cache.watcher.running = true // prevent unexpected reloads
+
+ accs := []accounts.Account{
+ {
+ Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "-309830980"},
+ },
+ {
+ Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "ggg"},
+ },
+ {
+ Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzzzzz-the-very-last-one.keyXXX"},
+ },
+ {
+ Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "SOMETHING.key"},
+ },
+ {
+ Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"},
+ },
+ {
+ Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "aaa"},
+ },
+ {
+ Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzz"},
+ },
+ }
+ for _, a := range accs {
+ cache.add(a)
+ }
+ // Add some of them twice to check that they don't get reinserted.
+ cache.add(accs[0])
+ cache.add(accs[2])
+
+ // Check that the account list is sorted by filename.
+ wantAccounts := make([]accounts.Account, len(accs))
+ copy(wantAccounts, accs)
+ sort.Sort(accountsByURL(wantAccounts))
+ list := cache.accounts()
+ if !reflect.DeepEqual(list, wantAccounts) {
+ t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
+ }
+ for _, a := range accs {
+ if !cache.hasAddress(a.Address) {
+ t.Errorf("expected hasAccount(%x) to return true", a.Address)
+ }
+ }
+ if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
+ t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
+ }
+
+ // Delete a few keys from the cache.
+ for i := 0; i < len(accs); i += 2 {
+ cache.delete(wantAccounts[i])
+ }
+ cache.delete(accounts.Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), URL: accounts.URL{Scheme: KeyStoreScheme, Path: "something"}})
+
+ // Check content again after deletion.
+ wantAccountsAfterDelete := []accounts.Account{
+ wantAccounts[1],
+ wantAccounts[3],
+ wantAccounts[5],
+ }
+ list = cache.accounts()
+ if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
+ t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
+ }
+ for _, a := range wantAccountsAfterDelete {
+ if !cache.hasAddress(a.Address) {
+ t.Errorf("expected hasAccount(%x) to return true", a.Address)
+ }
+ }
+ if cache.hasAddress(wantAccounts[0].Address) {
+ t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
+ }
+}
+
+func TestCacheFind(t *testing.T) {
+ dir := filepath.Join("testdata", "dir")
+ cache, _ := newAccountCache(dir)
+ cache.watcher.running = true // prevent unexpected reloads
+
+ accs := []accounts.Account{
+ {
+ Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "a.key")},
+ },
+ {
+ Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "b.key")},
+ },
+ {
+ Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c.key")},
+ },
+ {
+ Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c2.key")},
+ },
+ }
+ for _, a := range accs {
+ cache.add(a)
+ }
+
+ nomatchAccount := accounts.Account{
+ Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
+ }
+ tests := []struct {
+ Query accounts.Account
+ WantResult accounts.Account
+ WantError error
+ }{
+ // by address
+ {Query: accounts.Account{Address: accs[0].Address}, WantResult: accs[0]},
+ // by file
+ {Query: accounts.Account{URL: accs[0].URL}, WantResult: accs[0]},
+ // by basename
+ {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(accs[0].URL.Path)}}, WantResult: accs[0]},
+ // by file and address
+ {Query: accs[0], WantResult: accs[0]},
+ // ambiguous address, tie resolved by file
+ {Query: accs[2], WantResult: accs[2]},
+ // ambiguous address error
+ {
+ Query: accounts.Account{Address: accs[2].Address},
+ WantError: &AmbiguousAddrError{
+ Addr: accs[2].Address,
+ Matches: []accounts.Account{accs[2], accs[3]},
+ },
+ },
+ // no match error
+ {Query: nomatchAccount, WantError: ErrNoMatch},
+ {Query: accounts.Account{URL: nomatchAccount.URL}, WantError: ErrNoMatch},
+ {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(nomatchAccount.URL.Path)}}, WantError: ErrNoMatch},
+ {Query: accounts.Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch},
+ }
+ for i, test := range tests {
+ a, err := cache.find(test.Query)
+ if !reflect.DeepEqual(err, test.WantError) {
+ t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
+ continue
+ }
+ if a != test.WantResult {
+ t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
+ continue
+ }
+ }
+}