mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-16 17:03:46 +00:00
Merge a2e62140ac into cb5235eb07
This commit is contained in:
commit
74c5494b1b
3 changed files with 99 additions and 62 deletions
|
|
@ -209,6 +209,12 @@ func (ac *accountCache) close() {
|
||||||
// Callers must hold ac.mu.
|
// Callers must hold ac.mu.
|
||||||
func (ac *accountCache) reload() {
|
func (ac *accountCache) reload() {
|
||||||
accounts, err := ac.scan()
|
accounts, err := ac.scan()
|
||||||
|
ac.handleScanResult(accounts, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleScanResult uses the result of a fs scan to update the account lists
|
||||||
|
// This can be used to perform un-mutexed fs scans
|
||||||
|
func (ac *accountCache) handleScanResult(accounts []accounts.Account, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to reload keystore contents", "err", err)
|
log.Debug("Failed to reload keystore contents", "err", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -272,82 +272,104 @@ func TestWalletNotifierLifecycle(t *testing.T) {
|
||||||
t.Errorf("wallet notifier didn't terminate after unsubscribe")
|
t.Errorf("wallet notifier didn't terminate after unsubscribe")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type walletEvent struct {
|
||||||
|
accounts.WalletEvent
|
||||||
|
a accounts.Account
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that wallet notifications and correctly fired when accounts are added
|
// Tests that wallet notifications and correctly fired when accounts are added
|
||||||
// or deleted from the keystore.
|
// or deleted from the keystore.
|
||||||
func TestWalletNotifications(t *testing.T) {
|
func TestWalletNotifications(t *testing.T) {
|
||||||
// Create a temporary kesytore to test with
|
|
||||||
dir, ks := tmpKeyStore(t, false)
|
dir, ks := tmpKeyStore(t, false)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
// Subscribe to the wallet feed
|
// Subscribe to the wallet feed and collect events.
|
||||||
updates := make(chan accounts.WalletEvent, 1)
|
var (
|
||||||
sub := ks.Subscribe(updates)
|
events []walletEvent
|
||||||
|
updates = make(chan accounts.WalletEvent)
|
||||||
|
sub = ks.Subscribe(updates)
|
||||||
|
)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ev := <-updates:
|
||||||
|
events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
|
||||||
|
case <-sub.Err():
|
||||||
|
close(updates)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Randomly add and remove account and make sure events and wallets are in sync
|
// Randomly add and remove accounts.
|
||||||
live := make(map[common.Address]accounts.Account)
|
var (
|
||||||
|
live = make(map[common.Address]accounts.Account)
|
||||||
|
wantEvents []walletEvent
|
||||||
|
)
|
||||||
for i := 0; i < 1024; i++ {
|
for i := 0; i < 1024; i++ {
|
||||||
// Execute a creation or deletion and ensure event arrival
|
|
||||||
if create := len(live) == 0 || rand.Int()%4 > 0; create {
|
if create := len(live) == 0 || rand.Int()%4 > 0; create {
|
||||||
// Add a new account and ensure wallet notifications arrives
|
// Add a new account and ensure wallet notifications arrives
|
||||||
account, err := ks.NewAccount("")
|
account, err := ks.NewAccount("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create test account: %v", err)
|
t.Fatalf("failed to create test account: %v", err)
|
||||||
}
|
}
|
||||||
select {
|
|
||||||
case event := <-updates:
|
|
||||||
if event.Kind != accounts.WalletArrived {
|
|
||||||
t.Errorf("non-arrival event on account creation")
|
|
||||||
}
|
|
||||||
if event.Wallet.Accounts()[0] != account {
|
|
||||||
t.Errorf("account mismatch on created wallet: have %v, want %v", event.Wallet.Accounts()[0], account)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
t.Errorf("wallet arrival event not fired on account creation")
|
|
||||||
}
|
|
||||||
live[account.Address] = account
|
live[account.Address] = account
|
||||||
|
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account})
|
||||||
} else {
|
} else {
|
||||||
// Select a random account to delete (crude, but works)
|
// Delete a random account.
|
||||||
var account accounts.Account
|
var account accounts.Account
|
||||||
for _, a := range live {
|
for _, a := range live {
|
||||||
account = a
|
account = a
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Remove an account and ensure wallet notifiaction arrives
|
|
||||||
if err := ks.Delete(account, ""); err != nil {
|
if err := ks.Delete(account, ""); err != nil {
|
||||||
t.Fatalf("failed to delete test account: %v", err)
|
t.Fatalf("failed to delete test account: %v", err)
|
||||||
}
|
}
|
||||||
select {
|
|
||||||
case event := <-updates:
|
|
||||||
if event.Kind != accounts.WalletDropped {
|
|
||||||
t.Errorf("non-drop event on account deletion")
|
|
||||||
}
|
|
||||||
if event.Wallet.Accounts()[0] != account {
|
|
||||||
t.Errorf("account mismatch on deleted wallet: have %v, want %v", event.Wallet.Accounts()[0], account)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
t.Errorf("wallet departure event not fired on account creation")
|
|
||||||
}
|
|
||||||
delete(live, account.Address)
|
delete(live, account.Address)
|
||||||
|
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account})
|
||||||
}
|
}
|
||||||
// Retrieve the list of wallets and ensure it matches with our required live set
|
}
|
||||||
liveList := make([]accounts.Account, 0, len(live))
|
|
||||||
for _, account := range live {
|
|
||||||
liveList = append(liveList, account)
|
|
||||||
}
|
|
||||||
sort.Sort(accountsByURL(liveList))
|
|
||||||
|
|
||||||
wallets := ks.Wallets()
|
// Shut down the event collector and check events.
|
||||||
if len(liveList) != len(wallets) {
|
sub.Unsubscribe()
|
||||||
t.Errorf("wallet list doesn't match required accounts: have %v, want %v", wallets, liveList)
|
<-updates
|
||||||
} else {
|
checkAccounts(t, live, ks.Wallets())
|
||||||
for j, wallet := range wallets {
|
checkEvents(t, wantEvents, events)
|
||||||
if accs := wallet.Accounts(); len(accs) != 1 {
|
}
|
||||||
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
|
|
||||||
} else if accs[0] != liveList[j] {
|
// checkAccounts checks that all known live accounts are present in the wallet list.
|
||||||
t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j])
|
func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) {
|
||||||
}
|
if len(live) != len(wallets) {
|
||||||
|
t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
liveList := make([]accounts.Account, 0, len(live))
|
||||||
|
for _, account := range live {
|
||||||
|
liveList = append(liveList, account)
|
||||||
|
}
|
||||||
|
sort.Sort(accountsByURL(liveList))
|
||||||
|
for j, wallet := range wallets {
|
||||||
|
if accs := wallet.Accounts(); len(accs) != 1 {
|
||||||
|
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
|
||||||
|
} else if accs[0] != liveList[j] {
|
||||||
|
t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkEvents checks that all events in 'want' are present in 'have'. Events may be present multiple times.
|
||||||
|
func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
|
||||||
|
for _, wantEv := range want {
|
||||||
|
nmatch := 0
|
||||||
|
for ; len(have) > 0; nmatch++ {
|
||||||
|
if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
have = have[1:]
|
||||||
|
}
|
||||||
|
if nmatch == 0 {
|
||||||
|
t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ package keystore
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/rjeczalik/notify"
|
"github.com/rjeczalik/notify"
|
||||||
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
|
|
@ -82,9 +84,10 @@ func (w *watcher) loop() {
|
||||||
// When an event occurs, the reload call is delayed a bit so that
|
// When an event occurs, the reload call is delayed a bit so that
|
||||||
// multiple events arriving quickly only cause a single reload.
|
// multiple events arriving quickly only cause a single reload.
|
||||||
var (
|
var (
|
||||||
debounce = time.NewTimer(0)
|
debounce = time.NewTimer(0)
|
||||||
debounceDuration = 500 * time.Millisecond
|
debounceDuration = 500 * time.Millisecond
|
||||||
inCycle, hadEvent bool
|
unHandledEvents = uint64(0)
|
||||||
|
inCycle = uint64(0)
|
||||||
)
|
)
|
||||||
defer debounce.Stop()
|
defer debounce.Stop()
|
||||||
for {
|
for {
|
||||||
|
|
@ -92,22 +95,28 @@ func (w *watcher) loop() {
|
||||||
case <-w.quit:
|
case <-w.quit:
|
||||||
return
|
return
|
||||||
case <-w.ev:
|
case <-w.ev:
|
||||||
if !inCycle {
|
// Count up the unhandled events
|
||||||
|
atomic.AddUint64(&unHandledEvents, 1)
|
||||||
|
// Trigger the scan (with delay), if not already triggered
|
||||||
|
if atomic.SwapUint64(&inCycle, 1) == 0 {
|
||||||
debounce.Reset(debounceDuration)
|
debounce.Reset(debounceDuration)
|
||||||
inCycle = true
|
|
||||||
} else {
|
|
||||||
hadEvent = true
|
|
||||||
}
|
}
|
||||||
case <-debounce.C:
|
case <-debounce.C:
|
||||||
w.ac.mu.Lock()
|
//We're now handling the events, scan again as long as new
|
||||||
w.ac.reload()
|
// events keep coming during our fs-scan
|
||||||
w.ac.mu.Unlock()
|
var (
|
||||||
if hadEvent {
|
accs []accounts.Account
|
||||||
debounce.Reset(debounceDuration)
|
err error
|
||||||
inCycle, hadEvent = true, false
|
)
|
||||||
} else {
|
// Scan again if more events occurred during scan
|
||||||
inCycle, hadEvent = false, false
|
for atomic.SwapUint64(&unHandledEvents, 0) > 0 {
|
||||||
|
accs, err = w.ac.scan()
|
||||||
}
|
}
|
||||||
|
w.ac.mu.Lock()
|
||||||
|
w.ac.handleScanResult(accs, err)
|
||||||
|
w.ac.mu.Unlock()
|
||||||
|
// Signal we're finished with cycle
|
||||||
|
atomic.SwapUint64(&inCycle, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue