github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/accounts/keystore/account_cache_test.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:26</date>
    10  //</624342584839376896>
    11  
    12  
    13  package keystore
    14  
    15  import (
    16  	"fmt"
    17  	"io/ioutil"
    18  	"math/rand"
    19  	"os"
    20  	"path/filepath"
    21  	"reflect"
    22  	"sort"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/cespare/cp"
    27  	"github.com/davecgh/go-spew/spew"
    28  	"github.com/ethereum/go-ethereum/accounts"
    29  	"github.com/ethereum/go-ethereum/common"
    30  )
    31  
    32  var (
    33  	cachetestDir, _   = filepath.Abs(filepath.Join("testdata", "keystore"))
    34  	cachetestAccounts = []accounts.Account{
    35  		{
    36  			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
    37  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8")},
    38  		},
    39  		{
    40  			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
    41  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "aaa")},
    42  		},
    43  		{
    44  			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
    45  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "zzz")},
    46  		},
    47  	}
    48  )
    49  
    50  func TestWatchNewFile(t *testing.T) {
    51  	t.Parallel()
    52  
    53  	dir, ks := tmpKeyStore(t, false)
    54  	defer os.RemoveAll(dir)
    55  
    56  //在添加任何文件之前,请确保监视程序已启动。
    57  	ks.Accounts()
    58  	time.Sleep(1000 * time.Millisecond)
    59  
    60  //移入文件。
    61  	wantAccounts := make([]accounts.Account, len(cachetestAccounts))
    62  	for i := range cachetestAccounts {
    63  		wantAccounts[i] = accounts.Account{
    64  			Address: cachetestAccounts[i].Address,
    65  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, filepath.Base(cachetestAccounts[i].URL.Path))},
    66  		}
    67  		if err := cp.CopyFile(wantAccounts[i].URL.Path, cachetestAccounts[i].URL.Path); err != nil {
    68  			t.Fatal(err)
    69  		}
    70  	}
    71  
    72  //Ks应该看看账目。
    73  	var list []accounts.Account
    74  	for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
    75  		list = ks.Accounts()
    76  		if reflect.DeepEqual(list, wantAccounts) {
    77  //KS也应该收到变更通知
    78  			select {
    79  			case <-ks.changes:
    80  			default:
    81  				t.Fatalf("wasn't notified of new accounts")
    82  			}
    83  			return
    84  		}
    85  		time.Sleep(d)
    86  	}
    87  	t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts))
    88  }
    89  
    90  func TestWatchNoDir(t *testing.T) {
    91  	t.Parallel()
    92  
    93  //创建ks,但不创建它监视的目录。
    94  	rand.Seed(time.Now().UnixNano())
    95  	dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
    96  	ks := NewKeyStore(dir, LightScryptN, LightScryptP)
    97  
    98  	list := ks.Accounts()
    99  	if len(list) > 0 {
   100  		t.Error("initial account list not empty:", list)
   101  	}
   102  	time.Sleep(100 * time.Millisecond)
   103  
   104  //创建目录并将密钥文件复制到其中。
   105  	os.MkdirAll(dir, 0700)
   106  	defer os.RemoveAll(dir)
   107  	file := filepath.Join(dir, "aaa")
   108  	if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
   109  		t.Fatal(err)
   110  	}
   111  
   112  //KS应该看到账户。
   113  	wantAccounts := []accounts.Account{cachetestAccounts[0]}
   114  	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
   115  	for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
   116  		list = ks.Accounts()
   117  		if reflect.DeepEqual(list, wantAccounts) {
   118  //KS也应该收到变更通知
   119  			select {
   120  			case <-ks.changes:
   121  			default:
   122  				t.Fatalf("wasn't notified of new accounts")
   123  			}
   124  			return
   125  		}
   126  		time.Sleep(d)
   127  	}
   128  	t.Errorf("\ngot  %v\nwant %v", list, wantAccounts)
   129  }
   130  
   131  func TestCacheInitialReload(t *testing.T) {
   132  	cache, _ := newAccountCache(cachetestDir)
   133  	accounts := cache.accounts()
   134  	if !reflect.DeepEqual(accounts, cachetestAccounts) {
   135  		t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
   136  	}
   137  }
   138  
   139  func TestCacheAddDeleteOrder(t *testing.T) {
   140  	cache, _ := newAccountCache("testdata/no-such-dir")
   141  cache.watcher.running = true //防止意外重新加载
   142  
   143  	accs := []accounts.Account{
   144  		{
   145  			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
   146  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "-309830980"},
   147  		},
   148  		{
   149  			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
   150  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "ggg"},
   151  		},
   152  		{
   153  			Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"),
   154  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "zzzzzz-the-very-last-one.keyXXX"},
   155  		},
   156  		{
   157  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   158  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "SOMETHING.key"},
   159  		},
   160  		{
   161  			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
   162  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"},
   163  		},
   164  		{
   165  			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
   166  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "aaa"},
   167  		},
   168  		{
   169  			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
   170  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "zzz"},
   171  		},
   172  	}
   173  	for _, a := range accs {
   174  		cache.add(a)
   175  	}
   176  //添加其中一些两次以检查它们是否没有重新插入。
   177  	cache.add(accs[0])
   178  	cache.add(accs[2])
   179  
   180  //检查帐户列表是否按文件名排序。
   181  	wantAccounts := make([]accounts.Account, len(accs))
   182  	copy(wantAccounts, accs)
   183  	sort.Sort(accountsByURL(wantAccounts))
   184  	list := cache.accounts()
   185  	if !reflect.DeepEqual(list, wantAccounts) {
   186  		t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
   187  	}
   188  	for _, a := range accs {
   189  		if !cache.hasAddress(a.Address) {
   190  			t.Errorf("expected hasAccount(%x) to return true", a.Address)
   191  		}
   192  	}
   193  	if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
   194  		t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
   195  	}
   196  
   197  //从缓存中删除一些键。
   198  	for i := 0; i < len(accs); i += 2 {
   199  		cache.delete(wantAccounts[i])
   200  	}
   201  	cache.delete(accounts.Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), URL: accounts.URL{Scheme: KeyStoreScheme, Path: "something"}})
   202  
   203  //删除后再次检查内容。
   204  	wantAccountsAfterDelete := []accounts.Account{
   205  		wantAccounts[1],
   206  		wantAccounts[3],
   207  		wantAccounts[5],
   208  	}
   209  	list = cache.accounts()
   210  	if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
   211  		t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
   212  	}
   213  	for _, a := range wantAccountsAfterDelete {
   214  		if !cache.hasAddress(a.Address) {
   215  			t.Errorf("expected hasAccount(%x) to return true", a.Address)
   216  		}
   217  	}
   218  	if cache.hasAddress(wantAccounts[0].Address) {
   219  		t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
   220  	}
   221  }
   222  
   223  func TestCacheFind(t *testing.T) {
   224  	dir := filepath.Join("testdata", "dir")
   225  	cache, _ := newAccountCache(dir)
   226  cache.watcher.running = true //防止意外重新加载
   227  
   228  	accs := []accounts.Account{
   229  		{
   230  			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
   231  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "a.key")},
   232  		},
   233  		{
   234  			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
   235  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "b.key")},
   236  		},
   237  		{
   238  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   239  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c.key")},
   240  		},
   241  		{
   242  			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
   243  			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c2.key")},
   244  		},
   245  	}
   246  	for _, a := range accs {
   247  		cache.add(a)
   248  	}
   249  
   250  	nomatchAccount := accounts.Account{
   251  		Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
   252  		URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
   253  	}
   254  	tests := []struct {
   255  		Query      accounts.Account
   256  		WantResult accounts.Account
   257  		WantError  error
   258  	}{
   259  //按地址
   260  		{Query: accounts.Account{Address: accs[0].Address}, WantResult: accs[0]},
   261  //按文件
   262  		{Query: accounts.Account{URL: accs[0].URL}, WantResult: accs[0]},
   263  //用BaseNeMe
   264  		{Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(accs[0].URL.Path)}}, WantResult: accs[0]},
   265  //按文件和地址
   266  		{Query: accs[0], WantResult: accs[0]},
   267  //地址不明确,由文件解析的领带
   268  		{Query: accs[2], WantResult: accs[2]},
   269  //地址不明确错误
   270  		{
   271  			Query: accounts.Account{Address: accs[2].Address},
   272  			WantError: &AmbiguousAddrError{
   273  				Addr:    accs[2].Address,
   274  				Matches: []accounts.Account{accs[2], accs[3]},
   275  			},
   276  		},
   277  //无匹配误差
   278  		{Query: nomatchAccount, WantError: ErrNoMatch},
   279  		{Query: accounts.Account{URL: nomatchAccount.URL}, WantError: ErrNoMatch},
   280  		{Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(nomatchAccount.URL.Path)}}, WantError: ErrNoMatch},
   281  		{Query: accounts.Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch},
   282  	}
   283  	for i, test := range tests {
   284  		a, err := cache.find(test.Query)
   285  		if !reflect.DeepEqual(err, test.WantError) {
   286  			t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
   287  			continue
   288  		}
   289  		if a != test.WantResult {
   290  			t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
   291  			continue
   292  		}
   293  	}
   294  }
   295  
   296  func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
   297  	var list []accounts.Account
   298  	for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
   299  		list = ks.Accounts()
   300  		if reflect.DeepEqual(list, wantAccounts) {
   301  //KS也应该收到变更通知
   302  			select {
   303  			case <-ks.changes:
   304  			default:
   305  				return fmt.Errorf("wasn't notified of new accounts")
   306  			}
   307  			return nil
   308  		}
   309  		time.Sleep(d)
   310  	}
   311  	return fmt.Errorf("\ngot  %v\nwant %v", list, wantAccounts)
   312  }
   313  
   314  //testUpdatedKeyFileContents测试更新密钥库文件的内容
   315  //被观察者注意到,帐户缓存会相应地更新
   316  func TestUpdatedKeyfileContents(t *testing.T) {
   317  	t.Parallel()
   318  
   319  //创建要测试的临时Kesytore
   320  	rand.Seed(time.Now().UnixNano())
   321  	dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
   322  	ks := NewKeyStore(dir, LightScryptN, LightScryptP)
   323  
   324  	list := ks.Accounts()
   325  	if len(list) > 0 {
   326  		t.Error("initial account list not empty:", list)
   327  	}
   328  	time.Sleep(100 * time.Millisecond)
   329  
   330  //创建目录并将密钥文件复制到其中。
   331  	os.MkdirAll(dir, 0700)
   332  	defer os.RemoveAll(dir)
   333  	file := filepath.Join(dir, "aaa")
   334  
   335  //把我们的一个测试文件放在那里
   336  	if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
   337  		t.Fatal(err)
   338  	}
   339  
   340  //KS应该看到账户。
   341  	wantAccounts := []accounts.Account{cachetestAccounts[0]}
   342  	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
   343  	if err := waitForAccounts(wantAccounts, ks); err != nil {
   344  		t.Error(err)
   345  		return
   346  	}
   347  
   348  //需要,以便“file”的modtime与forcecopyfile之后的当前值不同
   349  	time.Sleep(1000 * time.Millisecond)
   350  
   351  //现在替换文件内容
   352  	if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
   353  		t.Fatal(err)
   354  		return
   355  	}
   356  	wantAccounts = []accounts.Account{cachetestAccounts[1]}
   357  	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
   358  	if err := waitForAccounts(wantAccounts, ks); err != nil {
   359  		t.Errorf("First replacement failed")
   360  		t.Error(err)
   361  		return
   362  	}
   363  
   364  //需要,以便“file”的modtime与forcecopyfile之后的当前值不同
   365  	time.Sleep(1000 * time.Millisecond)
   366  
   367  //现在重新替换文件内容
   368  	if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
   369  		t.Fatal(err)
   370  		return
   371  	}
   372  	wantAccounts = []accounts.Account{cachetestAccounts[2]}
   373  	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
   374  	if err := waitForAccounts(wantAccounts, ks); err != nil {
   375  		t.Errorf("Second replacement failed")
   376  		t.Error(err)
   377  		return
   378  	}
   379  
   380  //需要,以便“file”的modtime与ioutil.writefile之后的当前值不同
   381  	time.Sleep(1000 * time.Millisecond)
   382  
   383  //现在用垃圾替换文件内容
   384  	if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil {
   385  		t.Fatal(err)
   386  		return
   387  	}
   388  	if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
   389  		t.Errorf("Emptying account file failed")
   390  		t.Error(err)
   391  		return
   392  	}
   393  }
   394  
   395  //forcecopyfile与cp.copyfile类似,但如果目标存在,则不会抱怨。
   396  func forceCopyFile(dst, src string) error {
   397  	data, err := ioutil.ReadFile(src)
   398  	if err != nil {
   399  		return err
   400  	}
   401  	return ioutil.WriteFile(dst, data, 0644)
   402  }
   403