diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go index df3dda60b6..e8bc132919 100644 --- a/accounts/keystore/keystore.go +++ b/accounts/keystore/keystore.go @@ -72,6 +72,8 @@ type KeyStore struct { mu sync.RWMutex importMu sync.Mutex // Import Mutex locks the import to prevent two insertions from racing + + addedWallets map[string]struct{} // Track addedWallets wallet URLs to prevent duplicates } type unlocked struct { @@ -95,6 +97,7 @@ func (ks *KeyStore) init(keydir string) { // Initialize the set of unlocked keys and the account cache ks.unlocked = make(map[common.Address]*unlocked) ks.cache, ks.changes = newAccountCache(keydir) + ks.addedWallets = make(map[string]struct{}) // TODO: In order for this finalizer to work, there must be no references // to ks. addressCache doesn't keep a reference but unlocked keys do, @@ -147,6 +150,13 @@ func (ks *KeyStore) refreshWallets() { if len(ks.wallets) == 0 || ks.wallets[0].URL().Cmp(account.URL) > 0 { wallet := &keystoreWallet{account: account, keystore: ks} + // Only add a wallet once + if _, ok := ks.addedWallets[wallet.URL().String()]; !ok { + ks.addedWallets[wallet.URL().String()] = struct{}{} + } else { + continue + } + events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived}) wallets = append(wallets, wallet) continue diff --git a/accounts/keystore/keystore_test.go b/accounts/keystore/keystore_test.go index f8922a3f3f..1856d2248b 100644 --- a/accounts/keystore/keystore_test.go +++ b/accounts/keystore/keystore_test.go @@ -17,6 +17,7 @@ package keystore import ( + "github.com/ethereum/go-ethereum/log" "math/rand" "os" "runtime" @@ -283,6 +284,8 @@ type walletEvent struct { // Tests that wallet notifications and correctly fired when accounts are added // or deleted from the keystore. func TestWalletNotifications(t *testing.T) { + log.SetDefault(log.NewLogger(log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false)))) + t.Parallel() _, ks := tmpKeyStore(t) @@ -292,16 +295,14 @@ func TestWalletNotifications(t *testing.T) { updates = make(chan accounts.WalletEvent) sub = ks.Subscribe(updates) ) + defer sub.Unsubscribe() + var wg sync.WaitGroup + wg.Add(1) go func() { - for { - select { - case ev := <-updates: - events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) - case <-sub.Err(): - close(updates) - return - } + defer wg.Done() + for ev := range updates { + events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) } }() @@ -336,9 +337,13 @@ func TestWalletNotifications(t *testing.T) { // Shut down the event collector and check events. sub.Unsubscribe() - for ev := range updates { - events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) + if !waitForKsUpdating(t, ks, false, 4*time.Second) { + t.Errorf("wallet notifier didn't terminate after unsubscribe") } + // Wait for the event collection goroutine to finish + close(updates) + wg.Wait() + checkAccounts(t, live, ks.Wallets()) checkEvents(t, wantEvents, events) }