| 
									
										
										
										
											2017-04-14 10:29:00 +02:00
										 |  |  | // Copyright 2017 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | // 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/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | package keystore | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-10-09 12:40:50 +02:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	"math/rand" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/cespare/cp" | 
					
						
							|  |  |  | 	"github.com/davecgh/go-spew/spew" | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/accounts" | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	cachetestDir, _   = filepath.Abs(filepath.Join("testdata", "keystore")) | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	cachetestAccounts = []accounts.Account{ | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8")}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "aaa")}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "zzz")}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestWatchNewFile(t *testing.T) { | 
					
						
							|  |  |  | 	t.Parallel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	dir, ks := tmpKeyStore(t, false) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	defer os.RemoveAll(dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Ensure the watcher is started before adding any files. | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	ks.Accounts() | 
					
						
							| 
									
										
										
										
											2017-11-16 14:17:28 +01:00
										 |  |  | 	time.Sleep(1000 * time.Millisecond) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Move in the files. | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	wantAccounts := make([]accounts.Account, len(cachetestAccounts)) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	for i := range cachetestAccounts { | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 		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 { | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	// ks should see the accounts. | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	var list []accounts.Account | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 { | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 		list = ks.Accounts() | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		if reflect.DeepEqual(list, wantAccounts) { | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 			// ks should have also received change notifications | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ks.changes: | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				t.Fatalf("wasn't notified of new accounts") | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		time.Sleep(d) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestWatchNoDir(t *testing.T) { | 
					
						
							|  |  |  | 	t.Parallel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	// Create ks but not the directory that it watches. | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	rand.Seed(time.Now().UnixNano()) | 
					
						
							| 
									
										
										
										
											2021-09-20 10:23:44 +02:00
										 |  |  | 	dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int())) | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	ks := NewKeyStore(dir, LightScryptN, LightScryptP) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	list := ks.Accounts() | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	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") | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 	if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil { | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	// ks should see the account. | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	wantAccounts := []accounts.Account{cachetestAccounts[0]} | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 { | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 		list = ks.Accounts() | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		if reflect.DeepEqual(list, wantAccounts) { | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 			// ks should have also received change notifications | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ks.changes: | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				t.Fatalf("wasn't notified of new accounts") | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		time.Sleep(d) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	t.Errorf("\ngot  %v\nwant %v", list, wantAccounts) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestCacheInitialReload(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	cache, _ := newAccountCache(cachetestDir) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	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) { | 
					
						
							| 
									
										
										
										
											2017-02-11 17:02:00 +02:00
										 |  |  | 	cache, _ := newAccountCache("testdata/no-such-dir") | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	cache.watcher.running = true // prevent unexpected reloads | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	accs := []accounts.Account{ | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "-309830980"}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "ggg"}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "zzzzzz-the-very-last-one.keyXXX"}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "SOMETHING.key"}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "aaa"}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: "zzz"}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	for _, a := range accs { | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		cache.add(a) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Add some of them twice to check that they don't get reinserted. | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	cache.add(accs[0]) | 
					
						
							|  |  |  | 	cache.add(accs[2]) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Check that the account list is sorted by filename. | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	wantAccounts := make([]accounts.Account, len(accs)) | 
					
						
							|  |  |  | 	copy(wantAccounts, accs) | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	sort.Sort(accountsByURL(wantAccounts)) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	list := cache.accounts() | 
					
						
							|  |  |  | 	if !reflect.DeepEqual(list, wantAccounts) { | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 		t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts)) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	for _, a := range accs { | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		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. | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	for i := 0; i < len(accs); i += 2 { | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		cache.delete(wantAccounts[i]) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 	cache.delete(accounts.Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), URL: accounts.URL{Scheme: KeyStoreScheme, Path: "something"}}) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Check content again after deletion. | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	wantAccountsAfterDelete := []accounts.Account{ | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		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") | 
					
						
							| 
									
										
										
										
											2017-02-07 12:47:34 +02:00
										 |  |  | 	cache, _ := newAccountCache(dir) | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	cache.watcher.running = true // prevent unexpected reloads | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	accs := []accounts.Account{ | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "a.key")}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "b.key")}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c.key")}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 			URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c2.key")}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	for _, a := range accs { | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		cache.add(a) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 	nomatchAccount := accounts.Account{ | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 		URL:     accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 		Query      accounts.Account | 
					
						
							|  |  |  | 		WantResult accounts.Account | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		WantError  error | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		// by address | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 		{Query: accounts.Account{Address: accs[0].Address}, WantResult: accs[0]}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		// by file | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 		{Query: accounts.Account{URL: accs[0].URL}, WantResult: accs[0]}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		// by basename | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 		{Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(accs[0].URL.Path)}}, WantResult: accs[0]}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		// by file and address | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 		{Query: accs[0], WantResult: accs[0]}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		// ambiguous address, tie resolved by file | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 		{Query: accs[2], WantResult: accs[2]}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 		// ambiguous address error | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 			Query: accounts.Account{Address: accs[2].Address}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 			WantError: &AmbiguousAddrError{ | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 				Addr:    accs[2].Address, | 
					
						
							|  |  |  | 				Matches: []accounts.Account{accs[2], accs[3]}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// no match error | 
					
						
							|  |  |  | 		{Query: nomatchAccount, WantError: ErrNoMatch}, | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 		{Query: accounts.Account{URL: nomatchAccount.URL}, WantError: ErrNoMatch}, | 
					
						
							| 
									
										
										
										
											2017-02-08 15:53:02 +02:00
										 |  |  | 		{Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(nomatchAccount.URL.Path)}}, WantError: ErrNoMatch}, | 
					
						
							| 
									
										
										
										
											2017-01-24 11:49:20 +02:00
										 |  |  | 		{Query: accounts.Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch}, | 
					
						
							| 
									
										
										
										
											2016-03-03 01:15:42 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-10-09 12:40:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error { | 
					
						
							|  |  |  | 	var list []accounts.Account | 
					
						
							|  |  |  | 	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: | 
					
						
							|  |  |  | 				return fmt.Errorf("wasn't notified of new accounts") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		time.Sleep(d) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fmt.Errorf("\ngot  %v\nwant %v", list, wantAccounts) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestUpdatedKeyfileContents tests that updating the contents of a keystore file | 
					
						
							|  |  |  | // is noticed by the watcher, and the account cache is updated accordingly | 
					
						
							|  |  |  | func TestUpdatedKeyfileContents(t *testing.T) { | 
					
						
							|  |  |  | 	t.Parallel() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a temporary kesytore to test with | 
					
						
							|  |  |  | 	rand.Seed(time.Now().UnixNano()) | 
					
						
							| 
									
										
										
										
											2021-09-20 10:23:44 +02:00
										 |  |  | 	dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int())) | 
					
						
							| 
									
										
										
										
											2017-10-09 12:40:50 +02:00
										 |  |  | 	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") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Place one of our testfiles in there | 
					
						
							|  |  |  | 	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} | 
					
						
							|  |  |  | 	if err := waitForAccounts(wantAccounts, ks); err != nil { | 
					
						
							|  |  |  | 		t.Error(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-16 15:01:02 +01:00
										 |  |  | 	// needed so that modTime of `file` is different to its current value after forceCopyFile | 
					
						
							| 
									
										
										
										
											2017-11-16 14:17:28 +01:00
										 |  |  | 	time.Sleep(1000 * time.Millisecond) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 12:40:50 +02:00
										 |  |  | 	// Now replace file contents | 
					
						
							|  |  |  | 	if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	wantAccounts = []accounts.Account{cachetestAccounts[1]} | 
					
						
							|  |  |  | 	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} | 
					
						
							|  |  |  | 	if err := waitForAccounts(wantAccounts, ks); err != nil { | 
					
						
							|  |  |  | 		t.Errorf("First replacement failed") | 
					
						
							|  |  |  | 		t.Error(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-16 15:01:02 +01:00
										 |  |  | 	// needed so that modTime of `file` is different to its current value after forceCopyFile | 
					
						
							| 
									
										
										
										
											2017-11-16 14:17:28 +01:00
										 |  |  | 	time.Sleep(1000 * time.Millisecond) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 12:40:50 +02:00
										 |  |  | 	// Now replace file contents again | 
					
						
							|  |  |  | 	if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	wantAccounts = []accounts.Account{cachetestAccounts[2]} | 
					
						
							|  |  |  | 	wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} | 
					
						
							|  |  |  | 	if err := waitForAccounts(wantAccounts, ks); err != nil { | 
					
						
							|  |  |  | 		t.Errorf("Second replacement failed") | 
					
						
							|  |  |  | 		t.Error(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-11-16 14:17:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-16 15:01:02 +01:00
										 |  |  | 	// needed so that modTime of `file` is different to its current value after ioutil.WriteFile | 
					
						
							| 
									
										
										
										
											2017-11-16 14:17:28 +01:00
										 |  |  | 	time.Sleep(1000 * time.Millisecond) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 12:40:50 +02:00
										 |  |  | 	// Now replace file contents with crap | 
					
						
							|  |  |  | 	if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := waitForAccounts([]accounts.Account{}, ks); err != nil { | 
					
						
							|  |  |  | 		t.Errorf("Emptying account file failed") | 
					
						
							|  |  |  | 		t.Error(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists. | 
					
						
							|  |  |  | func forceCopyFile(dst, src string) error { | 
					
						
							|  |  |  | 	data, err := ioutil.ReadFile(src) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ioutil.WriteFile(dst, data, 0644) | 
					
						
							|  |  |  | } |