diff options
Diffstat (limited to 'accounts/account_manager.go')
-rw-r--r-- | accounts/account_manager.go | 145 |
1 files changed, 74 insertions, 71 deletions
diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 13f16296a..17b128e9e 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -26,7 +26,7 @@ This abstracts part of a user's interaction with an account she controls. It's not an abstraction of core Ethereum accounts data type / logic - for that see the core processing code of blocks / txs. -Currently this is pretty much a passthrough to the KeyStore2 interface, +Currently this is pretty much a passthrough to the KeyStore interface, and accounts persistence is derived from stored keys' addresses */ @@ -36,6 +36,7 @@ import ( "crypto/ecdsa" crand "crypto/rand" "errors" + "fmt" "os" "sync" "time" @@ -49,17 +50,12 @@ var ( ErrNoKeys = errors.New("no keys in store") ) -const ( - // Default unlock duration (in seconds) when an account is unlocked from the console - DefaultAccountUnlockDuration = 300 -) - type Account struct { Address common.Address } type Manager struct { - keyStore crypto.KeyStore2 + keyStore crypto.KeyStore unlocked map[common.Address]*unlocked mutex sync.RWMutex } @@ -69,7 +65,7 @@ type unlocked struct { abort chan struct{} } -func NewManager(keyStore crypto.KeyStore2) *Manager { +func NewManager(keyStore crypto.KeyStore) *Manager { return &Manager{ keyStore: keyStore, unlocked: make(map[common.Address]*unlocked), @@ -86,19 +82,6 @@ func (am *Manager) HasAccount(addr common.Address) bool { return false } -func (am *Manager) Primary() (addr common.Address, err error) { - addrs, err := am.keyStore.GetKeyAddresses() - if os.IsNotExist(err) { - return common.Address{}, ErrNoKeys - } else if err != nil { - return common.Address{}, err - } - if len(addrs) == 0 { - return common.Address{}, ErrNoKeys - } - return addrs[0], nil -} - func (am *Manager) DeleteAccount(address common.Address, auth string) error { return am.keyStore.DeleteKey(address, auth) } @@ -114,28 +97,58 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) return signature, err } -// TimedUnlock unlocks the account with the given address. -// When timeout has passed, the account will be locked again. +// unlock indefinitely +func (am *Manager) Unlock(addr common.Address, keyAuth string) error { + return am.TimedUnlock(addr, keyAuth, 0) +} + +// Unlock unlocks the account with the given address. The account +// stays unlocked for the duration of timeout +// it timeout is 0 the account is unlocked for the entire session func (am *Manager) TimedUnlock(addr common.Address, keyAuth string, timeout time.Duration) error { key, err := am.keyStore.GetKey(addr, keyAuth) if err != nil { return err } - u := am.addUnlocked(addr, key) - go am.dropLater(addr, u, timeout) + var u *unlocked + am.mutex.Lock() + defer am.mutex.Unlock() + var found bool + u, found = am.unlocked[addr] + if found { + // terminate dropLater for this key to avoid unexpected drops. + if u.abort != nil { + close(u.abort) + } + } + if timeout > 0 { + u = &unlocked{Key: key, abort: make(chan struct{})} + go am.expire(addr, u, timeout) + } else { + u = &unlocked{Key: key} + } + am.unlocked[addr] = u return nil } -// Unlock unlocks the account with the given address. The account -// stays unlocked until the program exits or until a TimedUnlock -// timeout (started after the call to Unlock) expires. -func (am *Manager) Unlock(addr common.Address, keyAuth string) error { - key, err := am.keyStore.GetKey(addr, keyAuth) - if err != nil { - return err +func (am *Manager) expire(addr common.Address, u *unlocked, timeout time.Duration) { + t := time.NewTimer(timeout) + defer t.Stop() + select { + case <-u.abort: + // just quit + case <-t.C: + am.mutex.Lock() + // only drop if it's still the same key instance that dropLater + // was launched with. we can check that using pointer equality + // because the map stores a new pointer every time the key is + // unlocked. + if am.unlocked[addr] == u { + zeroKey(u.PrivateKey) + delete(am.unlocked, addr) + } + am.mutex.Unlock() } - am.addUnlocked(addr, key) - return nil } func (am *Manager) NewAccount(auth string) (Account, error) { @@ -146,6 +159,20 @@ func (am *Manager) NewAccount(auth string) (Account, error) { return Account{Address: key.Address}, nil } +func (am *Manager) AddressByIndex(index int) (addr string, err error) { + var addrs []common.Address + addrs, err = am.keyStore.GetKeyAddresses() + if err != nil { + return + } + if index < 0 || index >= len(addrs) { + err = fmt.Errorf("index out of range: %d (should be 0-%d)", index, len(addrs)-1) + } else { + addr = addrs[index].Hex() + } + return +} + func (am *Manager) Accounts() ([]Account, error) { addresses, err := am.keyStore.GetKeyAddresses() if os.IsNotExist(err) { @@ -162,43 +189,6 @@ func (am *Manager) Accounts() ([]Account, error) { return accounts, err } -func (am *Manager) addUnlocked(addr common.Address, key *crypto.Key) *unlocked { - u := &unlocked{Key: key, abort: make(chan struct{})} - am.mutex.Lock() - prev, found := am.unlocked[addr] - if found { - // terminate dropLater for this key to avoid unexpected drops. - close(prev.abort) - // the key is zeroed here instead of in dropLater because - // there might not actually be a dropLater running for this - // key, i.e. when Unlock was used. - zeroKey(prev.PrivateKey) - } - am.unlocked[addr] = u - am.mutex.Unlock() - return u -} - -func (am *Manager) dropLater(addr common.Address, u *unlocked, timeout time.Duration) { - t := time.NewTimer(timeout) - defer t.Stop() - select { - case <-u.abort: - // just quit - case <-t.C: - am.mutex.Lock() - // only drop if it's still the same key instance that dropLater - // was launched with. we can check that using pointer equality - // because the map stores a new pointer every time the key is - // unlocked. - if am.unlocked[addr] == u { - zeroKey(u.PrivateKey) - delete(am.unlocked, addr) - } - am.mutex.Unlock() - } -} - // zeroKey zeroes a private key in memory. func zeroKey(k *ecdsa.PrivateKey) { b := k.D.Bits() @@ -229,6 +219,19 @@ func (am *Manager) Import(path string, keyAuth string) (Account, error) { return Account{Address: key.Address}, nil } +func (am *Manager) Update(addr common.Address, authFrom, authTo string) (err error) { + var key *crypto.Key + key, err = am.keyStore.GetKey(addr, authFrom) + + if err == nil { + err = am.keyStore.StoreKey(key, authTo) + if err == nil { + am.keyStore.Cleanup(addr) + } + } + return +} + func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) { var key *crypto.Key key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password) |