diff options
-rw-r--r-- | .github/CODEOWNERS | 9 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | accounts/keystore/account_cache.go | 113 | ||||
-rw-r--r-- | accounts/keystore/file_cache.go | 102 | ||||
-rw-r--r-- | accounts/keystore/keystore_passphrase.go | 7 | ||||
-rw-r--r-- | accounts/keystore/watch.go | 6 | ||||
-rw-r--r-- | accounts/manager.go | 10 | ||||
-rw-r--r-- | cmd/geth/accountcmd.go | 21 | ||||
-rw-r--r-- | eth/api.go | 83 | ||||
-rw-r--r-- | internal/web3ext/web3ext.go | 12 | ||||
-rw-r--r-- | node/config.go | 36 | ||||
-rw-r--r-- | params/version.go | 4 |
12 files changed, 287 insertions, 118 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..6076fe46a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,9 @@ +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners. + +accounts/usbwallet @karalabe +consensus @karalabe +core/ @karalabe @holiman +eth/ @karalabe +mobile/ @karalabe +p2p/ @fjl @zsfelfoldi @@ -1 +1 @@ -1.7.3 +1.8.0 diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go index 4b08cc202..71f698ece 100644 --- a/accounts/keystore/account_cache.go +++ b/accounts/keystore/account_cache.go @@ -20,7 +20,6 @@ import ( "bufio" "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" "sort" @@ -75,13 +74,6 @@ type accountCache struct { fileC fileCache } -// fileCache is a cache of files seen during scan of keystore -type fileCache struct { - all *set.SetNonTS // list of all files - mtime time.Time // latest mtime seen - mu sync.RWMutex -} - func newAccountCache(keydir string) (*accountCache, chan struct{}) { ac := &accountCache{ keydir: keydir, @@ -236,66 +228,22 @@ func (ac *accountCache) close() { ac.mu.Unlock() } -// scanFiles performs a new scan on the given directory, compares against the already -// cached filenames, and returns file sets: new, missing , modified -func (fc *fileCache) scanFiles(keyDir string) (set.Interface, set.Interface, set.Interface, error) { - t0 := time.Now() - files, err := ioutil.ReadDir(keyDir) - t1 := time.Now() - if err != nil { - return nil, nil, nil, err - } - fc.mu.RLock() - prevMtime := fc.mtime - fc.mu.RUnlock() - - filesNow := set.NewNonTS() - moddedFiles := set.NewNonTS() - var newMtime time.Time - for _, fi := range files { - modTime := fi.ModTime() - path := filepath.Join(keyDir, fi.Name()) - if skipKeyFile(fi) { - log.Trace("Ignoring file on account scan", "path", path) - continue - } - filesNow.Add(path) - if modTime.After(prevMtime) { - moddedFiles.Add(path) - } - if modTime.After(newMtime) { - newMtime = modTime - } - } - t2 := time.Now() - - fc.mu.Lock() - // Missing = previous - current - missing := set.Difference(fc.all, filesNow) - // New = current - previous - newFiles := set.Difference(filesNow, fc.all) - // Modified = modified - new - modified := set.Difference(moddedFiles, newFiles) - fc.all = filesNow - fc.mtime = newMtime - fc.mu.Unlock() - t3 := time.Now() - log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2)) - return newFiles, missing, modified, nil -} - // scanAccounts checks if any changes have occurred on the filesystem, and // updates the account cache accordingly func (ac *accountCache) scanAccounts() error { - newFiles, missingFiles, modified, err := ac.fileC.scanFiles(ac.keydir) - t1 := time.Now() + // Scan the entire folder metadata for file changes + creates, deletes, updates, err := ac.fileC.scan(ac.keydir) if err != nil { log.Debug("Failed to reload keystore contents", "err", err) return err } + if creates.Size() == 0 && deletes.Size() == 0 && updates.Size() == 0 { + return nil + } + // Create a helper method to scan the contents of the key files var ( - buf = new(bufio.Reader) - keyJSON struct { + buf = new(bufio.Reader) + key struct { Address string `json:"address"` } ) @@ -308,9 +256,9 @@ func (ac *accountCache) scanAccounts() error { defer fd.Close() buf.Reset(fd) // Parse the address. - keyJSON.Address = "" - err = json.NewDecoder(buf).Decode(&keyJSON) - addr := common.HexToAddress(keyJSON.Address) + key.Address = "" + err = json.NewDecoder(buf).Decode(&key) + addr := common.HexToAddress(key.Address) switch { case err != nil: log.Debug("Failed to decode keystore key", "path", path, "err", err) @@ -321,47 +269,30 @@ func (ac *accountCache) scanAccounts() error { } return nil } + // Process all the file diffs + start := time.Now() - for _, p := range newFiles.List() { - path, _ := p.(string) - a := readAccount(path) - if a != nil { + for _, p := range creates.List() { + if a := readAccount(p.(string)); a != nil { ac.add(*a) } } - for _, p := range missingFiles.List() { - path, _ := p.(string) - ac.deleteByFile(path) + for _, p := range deletes.List() { + ac.deleteByFile(p.(string)) } - - for _, p := range modified.List() { - path, _ := p.(string) - a := readAccount(path) + for _, p := range updates.List() { + path := p.(string) ac.deleteByFile(path) - if a != nil { + if a := readAccount(path); a != nil { ac.add(*a) } } - - t2 := time.Now() + end := time.Now() select { case ac.notify <- struct{}{}: default: } - log.Trace("Handled keystore changes", "time", t2.Sub(t1)) - + log.Trace("Handled keystore changes", "time", end.Sub(start)) return nil } - -func skipKeyFile(fi os.FileInfo) bool { - // Skip editor backups and UNIX-style hidden files. - if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") { - return true - } - // Skip misc special files, directories (yes, symlinks too). - if fi.IsDir() || fi.Mode()&os.ModeType != 0 { - return true - } - return false -} diff --git a/accounts/keystore/file_cache.go b/accounts/keystore/file_cache.go new file mode 100644 index 000000000..c91b7b7b6 --- /dev/null +++ b/accounts/keystore/file_cache.go @@ -0,0 +1,102 @@ +// Copyright 2017 The go-ethereum Authors +// 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/>. + +package keystore + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/log" + set "gopkg.in/fatih/set.v0" +) + +// fileCache is a cache of files seen during scan of keystore. +type fileCache struct { + all *set.SetNonTS // Set of all files from the keystore folder + lastMod time.Time // Last time instance when a file was modified + mu sync.RWMutex +} + +// scan performs a new scan on the given directory, compares against the already +// cached filenames, and returns file sets: creates, deletes, updates. +func (fc *fileCache) scan(keyDir string) (set.Interface, set.Interface, set.Interface, error) { + t0 := time.Now() + + // List all the failes from the keystore folder + files, err := ioutil.ReadDir(keyDir) + if err != nil { + return nil, nil, nil, err + } + t1 := time.Now() + + fc.mu.Lock() + defer fc.mu.Unlock() + + // Iterate all the files and gather their metadata + all := set.NewNonTS() + mods := set.NewNonTS() + + var newLastMod time.Time + for _, fi := range files { + // Skip any non-key files from the folder + path := filepath.Join(keyDir, fi.Name()) + if skipKeyFile(fi) { + log.Trace("Ignoring file on account scan", "path", path) + continue + } + // Gather the set of all and fresly modified files + all.Add(path) + + modified := fi.ModTime() + if modified.After(fc.lastMod) { + mods.Add(path) + } + if modified.After(newLastMod) { + newLastMod = modified + } + } + t2 := time.Now() + + // Update the tracked files and return the three sets + deletes := set.Difference(fc.all, all) // Deletes = previous - current + creates := set.Difference(all, fc.all) // Creates = current - previous + updates := set.Difference(mods, creates) // Updates = modified - creates + + fc.all, fc.lastMod = all, newLastMod + t3 := time.Now() + + // Report on the scanning stats and return + log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2)) + return creates, deletes, updates, nil +} + +// skipKeyFile ignores editor backups, hidden files and folders/symlinks. +func skipKeyFile(fi os.FileInfo) bool { + // Skip editor backups and UNIX-style hidden files. + if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") { + return true + } + // Skip misc special files, directories (yes, symlinks too). + if fi.IsDir() || fi.Mode()&os.ModeType != 0 { + return true + } + return false +} diff --git a/accounts/keystore/keystore_passphrase.go b/accounts/keystore/keystore_passphrase.go index 535608a60..eaec39f7d 100644 --- a/accounts/keystore/keystore_passphrase.go +++ b/accounts/keystore/keystore_passphrase.go @@ -28,6 +28,7 @@ package keystore import ( "bytes" "crypto/aes" + crand "crypto/rand" "crypto/sha256" "encoding/hex" "encoding/json" @@ -90,6 +91,12 @@ func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) return key, nil } +// StoreKey generates a key, encrypts with 'auth' and stores in the given directory +func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) { + _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP}, crand.Reader, auth) + return a.Address, err +} + func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error { keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) if err != nil { diff --git a/accounts/keystore/watch.go b/accounts/keystore/watch.go index 602300b10..bbcfb9925 100644 --- a/accounts/keystore/watch.go +++ b/accounts/keystore/watch.go @@ -81,10 +81,14 @@ func (w *watcher) loop() { // When an event occurs, the reload call is delayed a bit so that // multiple events arriving quickly only cause a single reload. var ( - debounce = time.NewTimer(0) debounceDuration = 500 * time.Millisecond rescanTriggered = false + debounce = time.NewTimer(0) ) + // Ignore initial trigger + if !debounce.Stop() { + <-debounce.C + } defer debounce.Stop() for { select { diff --git a/accounts/manager.go b/accounts/manager.go index 78ddb1368..96ca298fc 100644 --- a/accounts/manager.go +++ b/accounts/manager.go @@ -41,6 +41,11 @@ type Manager struct { // NewManager creates a generic account manager to sign transaction via various // supported backends. func NewManager(backends ...Backend) *Manager { + // Retrieve the initial list of wallets from the backends and sort by URL + var wallets []Wallet + for _, backend := range backends { + wallets = merge(wallets, backend.Wallets()...) + } // Subscribe to wallet notifications from all backends updates := make(chan WalletEvent, 4*len(backends)) @@ -48,11 +53,6 @@ func NewManager(backends ...Backend) *Manager { for i, backend := range backends { subs[i] = backend.Subscribe(updates) } - // Retrieve the initial list of wallets from the backends and sort by URL - var wallets []Wallet - for _, backend := range backends { - wallets = merge(wallets, backend.Wallets()...) - } // Assemble the account manager and return am := &Manager{ backends: make(map[reflect.Type][]Backend), diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go index 0f53c92b0..0db5c4ce0 100644 --- a/cmd/geth/accountcmd.go +++ b/cmd/geth/accountcmd.go @@ -291,15 +291,28 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr // accountCreate creates a new account into the keystore defined by the CLI flags. func accountCreate(ctx *cli.Context) error { - stack, _ := makeConfigNode(ctx) + cfg := gethConfig{Node: defaultNodeConfig()} + // Load config file. + if file := ctx.GlobalString(configFileFlag.Name); file != "" { + if err := loadConfig(file, &cfg); err != nil { + utils.Fatalf("%v", err) + } + } + utils.SetNodeConfig(ctx, &cfg.Node) + scryptN, scryptP, keydir, err := cfg.Node.AccountConfig() + + if err != nil { + utils.Fatalf("Failed to read configuration: %v", err) + } + password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) - ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - account, err := ks.NewAccount(password) + address, err := keystore.StoreKey(keydir, password, scryptN, scryptP) + if err != nil { utils.Fatalf("Failed to create account: %v", err) } - fmt.Printf("Address: {%x}\n", account.Address) + fmt.Printf("Address: {%x}\n", address) return nil } diff --git a/eth/api.go b/eth/api.go index e91f51bb9..12448a6a1 100644 --- a/eth/api.go +++ b/eth/api.go @@ -636,3 +636,86 @@ func storageRangeAt(st state.Trie, start []byte, maxResult int) StorageRangeResu } return result } + +// GetModifiedAccountsByumber returns all accounts that have changed between the +// two blocks specified. A change is defined as a difference in nonce, balance, +// code hash, or storage hash. +// +// With one parameter, returns the list of accounts modified in the specified block. +func (api *PrivateDebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) { + var startBlock, endBlock *types.Block + + startBlock = api.eth.blockchain.GetBlockByNumber(startNum) + if startBlock == nil { + return nil, fmt.Errorf("start block %x not found", startNum) + } + + if endNum == nil { + endBlock = startBlock + startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash()) + if startBlock == nil { + return nil, fmt.Errorf("block %x has no parent", endBlock.Number()) + } + } else { + endBlock = api.eth.blockchain.GetBlockByNumber(*endNum) + if endBlock == nil { + return nil, fmt.Errorf("end block %d not found", *endNum) + } + } + return api.getModifiedAccounts(startBlock, endBlock) +} + +// GetModifiedAccountsByHash returns all accounts that have changed between the +// two blocks specified. A change is defined as a difference in nonce, balance, +// code hash, or storage hash. +// +// With one parameter, returns the list of accounts modified in the specified block. +func (api *PrivateDebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) { + var startBlock, endBlock *types.Block + startBlock = api.eth.blockchain.GetBlockByHash(startHash) + if startBlock == nil { + return nil, fmt.Errorf("start block %x not found", startHash) + } + + if endHash == nil { + endBlock = startBlock + startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash()) + if startBlock == nil { + return nil, fmt.Errorf("block %x has no parent", endBlock.Number()) + } + } else { + endBlock = api.eth.blockchain.GetBlockByHash(*endHash) + if endBlock == nil { + return nil, fmt.Errorf("end block %x not found", *endHash) + } + } + return api.getModifiedAccounts(startBlock, endBlock) +} + +func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) { + if startBlock.Number().Uint64() >= endBlock.Number().Uint64() { + return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64()) + } + + oldTrie, err := trie.NewSecure(startBlock.Root(), api.eth.chainDb, 0) + if err != nil { + return nil, err + } + newTrie, err := trie.NewSecure(endBlock.Root(), api.eth.chainDb, 0) + if err != nil { + return nil, err + } + + diff, _ := trie.NewDifferenceIterator(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{})) + iter := trie.NewIterator(diff) + + var dirty []common.Address + for iter.Next() { + key := newTrie.GetKey(iter.Key) + if key == nil { + return nil, fmt.Errorf("no preimage found for hash %x", iter.Key) + } + dirty = append(dirty, common.BytesToAddress(key)) + } + return dirty, nil +} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 1ae6e2d73..ef0d2b4e6 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -354,6 +354,18 @@ web3._extend({ call: 'debug_storageRangeAt', params: 5, }), + new web3._extend.Method({ + name: 'getModifiedAccountsByNumber', + call: 'debug_getModifiedAccountsByNumber', + params: 2, + inputFormatter: [null, null], + }), + new web3._extend.Method({ + name: 'getModifiedAccountsByHash', + call: 'debug_getModifiedAccountsByHash', + params: 2, + inputFormatter:[null, null], + }), ], properties: [] }); diff --git a/node/config.go b/node/config.go index be9e21b4f..1ee02d896 100644 --- a/node/config.go +++ b/node/config.go @@ -360,35 +360,43 @@ func (c *Config) parsePersistentNodes(path string) []*discover.Node { return nodes } -func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { +// AccountConfig determines the settings for scrypt and keydirectory +func (c *Config) AccountConfig() (int, int, string, error) { scryptN := keystore.StandardScryptN scryptP := keystore.StandardScryptP - if conf.UseLightweightKDF { + if c.UseLightweightKDF { scryptN = keystore.LightScryptN scryptP = keystore.LightScryptP } var ( - keydir string - ephemeral string - err error + keydir string + err error ) switch { - case filepath.IsAbs(conf.KeyStoreDir): - keydir = conf.KeyStoreDir - case conf.DataDir != "": - if conf.KeyStoreDir == "" { - keydir = filepath.Join(conf.DataDir, datadirDefaultKeyStore) + case filepath.IsAbs(c.KeyStoreDir): + keydir = c.KeyStoreDir + case c.DataDir != "": + if c.KeyStoreDir == "" { + keydir = filepath.Join(c.DataDir, datadirDefaultKeyStore) } else { - keydir, err = filepath.Abs(conf.KeyStoreDir) + keydir, err = filepath.Abs(c.KeyStoreDir) } - case conf.KeyStoreDir != "": - keydir, err = filepath.Abs(conf.KeyStoreDir) - default: + case c.KeyStoreDir != "": + keydir, err = filepath.Abs(c.KeyStoreDir) + } + return scryptN, scryptP, keydir, err +} + +func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { + scryptN, scryptP, keydir, err := conf.AccountConfig() + var ephemeral string + if keydir == "" { // There is no datadir. keydir, err = ioutil.TempDir("", "go-ethereum-keystore") ephemeral = keydir } + if err != nil { return nil, "", err } diff --git a/params/version.go b/params/version.go index 40d459162..32d4a2e23 100644 --- a/params/version.go +++ b/params/version.go @@ -22,8 +22,8 @@ import ( const ( VersionMajor = 1 // Major version component of the current release - VersionMinor = 7 // Minor version component of the current release - VersionPatch = 3 // Patch version component of the current release + VersionMinor = 8 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release VersionMeta = "unstable" // Version metadata to append to the version string ) |