diff --git a/vendor/github.com/ethereum/go-ethereum/.dockerignore b/vendor/github.com/ethereum/go-ethereum/.dockerignore
new file mode 100644
index 000000000..d1d79d53e
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/.dockerignore
@@ -0,0 +1,3 @@
+.git
+build/_workspace
+build/_bin
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go
index dbb235c14..e6bb0c3b5 100644
--- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go
@@ -22,7 +22,7 @@ import (
"io"
"io/ioutil"
- "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@@ -35,7 +35,7 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
if err != nil {
return nil, err
}
- key, err := accounts.DecryptKey(json, passphrase)
+ key, err := keystore.DecryptKey(json, passphrase)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go b/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go
deleted file mode 100644
index eb45f3bc0..000000000
--- a/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2015 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 .
-
-// Package accounts implements encrypted storage of secp256k1 private keys.
-//
-// Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification.
-// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information.
-package accounts
-
-import (
- "crypto/ecdsa"
- crand "crypto/rand"
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "runtime"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/status-im/status-go/extkeys"
-)
-
-var (
- ErrLocked = errors.New("account is locked")
- ErrNoMatch = errors.New("no key for given address or file")
- ErrDecrypt = errors.New("could not decrypt key with given passphrase")
-)
-
-// Account represents a stored key.
-// When used as an argument, it selects a unique key file to act on.
-type Account struct {
- Address common.Address // Ethereum account address derived from the key
-
- // File contains the key file name.
- // When Acccount is used as an argument to select a key, File can be left blank to
- // select just by address or set to the basename or absolute path of a file in the key
- // directory. Accounts returned by Manager will always contain an absolute path.
- File string
-}
-
-func (acc *Account) MarshalJSON() ([]byte, error) {
- return []byte(`"` + acc.Address.Hex() + `"`), nil
-}
-
-func (acc *Account) UnmarshalJSON(raw []byte) error {
- return json.Unmarshal(raw, &acc.Address)
-}
-
-// Manager manages a key storage directory on disk.
-type Manager struct {
- cache *addrCache
- keyStore keyStore
- mu sync.RWMutex
- unlocked map[common.Address]*unlocked
-}
-
-type unlocked struct {
- *Key
- abort chan struct{}
-}
-
-// NewManager creates a manager for the given directory.
-func NewManager(keydir string, scryptN, scryptP int) *Manager {
- keydir, _ = filepath.Abs(keydir)
- am := &Manager{keyStore: &keyStorePassphrase{keydir, scryptN, scryptP}}
- am.init(keydir)
- return am
-}
-
-// NewPlaintextManager creates a manager for the given directory.
-// Deprecated: Use NewManager.
-func NewPlaintextManager(keydir string) *Manager {
- keydir, _ = filepath.Abs(keydir)
- am := &Manager{keyStore: &keyStorePlain{keydir}}
- am.init(keydir)
- return am
-}
-
-func (am *Manager) init(keydir string) {
- am.unlocked = make(map[common.Address]*unlocked)
- am.cache = newAddrCache(keydir)
- // TODO: In order for this finalizer to work, there must be no references
- // to am. addrCache doesn't keep a reference but unlocked keys do,
- // so the finalizer will not trigger until all timed unlocks have expired.
- runtime.SetFinalizer(am, func(m *Manager) {
- m.cache.close()
- })
-}
-
-// HasAddress reports whether a key with the given address is present.
-func (am *Manager) HasAddress(addr common.Address) bool {
- return am.cache.hasAddress(addr)
-}
-
-// Accounts returns all key files present in the directory.
-func (am *Manager) Accounts() []Account {
- return am.cache.accounts()
-}
-
-// AccountDecryptedKey returns decrypted key for account (provided that password is correct).
-func (am *Manager) AccountDecryptedKey(a Account, auth string) (Account, *Key, error) {
- return am.getDecryptedKey(a, auth)
-}
-
-// Delete deletes the key matched by account if the passphrase is correct.
-// If the account contains no filename, the address must match a unique key.
-func (am *Manager) Delete(a Account, passphrase string) error {
- // Decrypting the key isn't really necessary, but we do
- // it anyway to check the password and zero out the key
- // immediately afterwards.
- a, key, err := am.getDecryptedKey(a, passphrase)
- if key != nil {
- zeroKey(key.PrivateKey)
- }
- if err != nil {
- return err
- }
- // The order is crucial here. The key is dropped from the
- // cache after the file is gone so that a reload happening in
- // between won't insert it into the cache again.
- err = os.Remove(a.File)
- if err == nil {
- am.cache.delete(a)
- }
- return err
-}
-
-// Sign calculates a ECDSA signature for the given hash. The produced signature
-// is in the [R || S || V] format where V is 0 or 1.
-func (am *Manager) Sign(addr common.Address, hash []byte) ([]byte, error) {
- am.mu.RLock()
- defer am.mu.RUnlock()
-
- unlockedKey, found := am.unlocked[addr]
- if !found {
- return nil, ErrLocked
- }
- return crypto.Sign(hash, unlockedKey.PrivateKey)
-}
-
-// SignWithPassphrase signs hash if the private key matching the given address
-// can be decrypted with the given passphrase. The produced signature is in the
-// [R || S || V] format where V is 0 or 1.
-func (am *Manager) SignWithPassphrase(a Account, passphrase string, hash []byte) (signature []byte, err error) {
- _, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return nil, err
- }
- defer zeroKey(key.PrivateKey)
- return crypto.Sign(hash, key.PrivateKey)
-}
-
-// Unlock unlocks the given account indefinitely.
-func (am *Manager) Unlock(a Account, passphrase string) error {
- return am.TimedUnlock(a, passphrase, 0)
-}
-
-// Lock removes the private key with the given address from memory.
-func (am *Manager) Lock(addr common.Address) error {
- am.mu.Lock()
- if unl, found := am.unlocked[addr]; found {
- am.mu.Unlock()
- am.expire(addr, unl, time.Duration(0)*time.Nanosecond)
- } else {
- am.mu.Unlock()
- }
- return nil
-}
-
-// TimedUnlock unlocks the given account with the passphrase. The account
-// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
-// until the program exits. The account must match a unique key file.
-//
-// If the account address is already unlocked for a duration, TimedUnlock extends or
-// shortens the active unlock timeout. If the address was previously unlocked
-// indefinitely the timeout is not altered.
-func (am *Manager) TimedUnlock(a Account, passphrase string, timeout time.Duration) error {
- a, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return err
- }
-
- am.mu.Lock()
- defer am.mu.Unlock()
- u, found := am.unlocked[a.Address]
- if found {
- if u.abort == nil {
- // The address was unlocked indefinitely, so unlocking
- // it with a timeout would be confusing.
- zeroKey(key.PrivateKey)
- return nil
- } else {
- // Terminate the expire goroutine and replace it below.
- close(u.abort)
- }
- }
- if timeout > 0 {
- u = &unlocked{Key: key, abort: make(chan struct{})}
- go am.expire(a.Address, u, timeout)
- } else {
- u = &unlocked{Key: key}
- }
- am.unlocked[a.Address] = u
- return nil
-}
-
-// Find resolves the given account into a unique entry in the keystore.
-func (am *Manager) Find(a Account) (Account, error) {
- am.cache.maybeReload()
- am.cache.mu.Lock()
- a, err := am.cache.find(a)
- am.cache.mu.Unlock()
- return a, err
-}
-
-func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) {
- a, err := am.Find(a)
- if err != nil {
- return a, nil, err
- }
- key, err := am.keyStore.GetKey(a.Address, a.File, auth)
- return a, key, 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.mu.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.mu.Unlock()
- }
-}
-
-// NewAccount generates a new key and stores it into the key directory,
-// encrypting it with the passphrase.
-func (am *Manager) NewAccount(passphrase string, w bool) (Account, error) {
- _, account, err := storeNewKey(am.keyStore, crand.Reader, passphrase, w)
- if err != nil {
- return Account{}, err
- }
- // Add the account to the cache immediately rather
- // than waiting for file system notifications to pick it up.
- am.cache.add(account)
- return account, nil
-}
-
-// AccountByIndex returns the ith account.
-func (am *Manager) AccountByIndex(i int) (Account, error) {
- accounts := am.Accounts()
- if i < 0 || i >= len(accounts) {
- return Account{}, fmt.Errorf("account index %d out of range [0, %d]", i, len(accounts)-1)
- }
- return accounts[i], nil
-}
-
-// Export exports as a JSON key, encrypted with newPassphrase.
-func (am *Manager) Export(a Account, passphrase, newPassphrase string) (keyJSON []byte, err error) {
- _, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return nil, err
- }
- var N, P int
- if store, ok := am.keyStore.(*keyStorePassphrase); ok {
- N, P = store.scryptN, store.scryptP
- } else {
- N, P = StandardScryptN, StandardScryptP
- }
- return EncryptKey(key, newPassphrase, N, P)
-}
-
-// Import stores the given encrypted JSON key into the key directory.
-func (am *Manager) Import(keyJSON []byte, passphrase, newPassphrase string) (Account, error) {
- key, err := DecryptKey(keyJSON, passphrase)
- if key != nil && key.PrivateKey != nil {
- defer zeroKey(key.PrivateKey)
- }
- if err != nil {
- return Account{}, err
- }
- return am.importKey(key, newPassphrase)
-}
-
-// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
-func (am *Manager) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (Account, error) {
- key := newKeyFromECDSA(priv)
- if am.cache.hasAddress(key.Address) {
- return Account{}, fmt.Errorf("account already exists")
- }
-
- return am.importKey(key, passphrase)
-}
-
-// ImportExtendedKey stores ECDSA key (obtained from extended key) along with CKD#2 (root for sub-accounts)
-// If key file is not found, it is created. Key is encrypted with the given passphrase.
-func (am *Manager) ImportExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (Account, error) {
- key, err := newKeyFromExtendedKey(extKey)
- if err != nil {
- zeroKey(key.PrivateKey)
- return Account{}, err
- }
-
- // if account is already imported, return cached version
- if am.cache.hasAddress(key.Address) {
- a := Account{
- Address: key.Address,
- }
- am.cache.maybeReload()
- am.cache.mu.Lock()
- a, err := am.cache.find(a)
- am.cache.mu.Unlock()
- if err != nil {
- zeroKey(key.PrivateKey)
- return a, err
- }
- return a, nil
- }
-
- return am.importKey(key, passphrase)
-}
-
-func (am *Manager) importKey(key *Key, passphrase string) (Account, error) {
- a := Account{Address: key.Address, File: am.keyStore.JoinPath(keyFileName(key.Address))}
- if err := am.keyStore.StoreKey(a.File, key, passphrase); err != nil {
- return Account{}, err
- }
- am.cache.add(a)
- return a, nil
-}
-
-// Update changes the passphrase of an existing account.
-func (am *Manager) Update(a Account, passphrase, newPassphrase string) error {
- a, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return err
- }
- return am.keyStore.StoreKey(a.File, key, newPassphrase)
-}
-
-func (am *Manager) IncSubAccountIndex(a Account, passphrase string) error {
- a, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return err
- }
- key.SubAccountIndex++
- return am.keyStore.StoreKey(a.File, key, passphrase)
-}
-
-// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
-// a key file in the key directory. The key file is encrypted with the same passphrase.
-func (am *Manager) ImportPreSaleKey(keyJSON []byte, passphrase string) (Account, error) {
- a, _, err := importPreSaleKey(am.keyStore, keyJSON, passphrase)
- if err != nil {
- return a, err
- }
- am.cache.add(a)
- return a, nil
-}
-
-// zeroKey zeroes a private key in memory.
-func zeroKey(k *ecdsa.PrivateKey) {
- if k == nil {
- return
- }
- b := k.D.Bits()
- for i := range b {
- b[i] = 0
- }
-}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/accounts.go b/vendor/github.com/ethereum/go-ethereum/accounts/accounts.go
new file mode 100644
index 000000000..640de5220
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/accounts.go
@@ -0,0 +1,155 @@
+// 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 .
+
+// Package accounts implements high level Ethereum account management.
+package accounts
+
+import (
+ "math/big"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+// Account represents an Ethereum account located at a specific location defined
+// by the optional URL field.
+type Account struct {
+ Address common.Address `json:"address"` // Ethereum account address derived from the key
+ URL URL `json:"url"` // Optional resource locator within a backend
+}
+
+// Wallet represents a software or hardware wallet that might contain one or more
+// accounts (derived from the same seed).
+type Wallet interface {
+ // URL retrieves the canonical path under which this wallet is reachable. It is
+ // user by upper layers to define a sorting order over all wallets from multiple
+ // backends.
+ URL() URL
+
+ // Status returns a textual status to aid the user in the current state of the
+ // wallet.
+ Status() string
+
+ // Open initializes access to a wallet instance. It is not meant to unlock or
+ // decrypt account keys, rather simply to establish a connection to hardware
+ // wallets and/or to access derivation seeds.
+ //
+ // The passphrase parameter may or may not be used by the implementation of a
+ // particular wallet instance. The reason there is no passwordless open method
+ // is to strive towards a uniform wallet handling, oblivious to the different
+ // backend providers.
+ //
+ // Please note, if you open a wallet, you must close it to release any allocated
+ // resources (especially important when working with hardware wallets).
+ Open(passphrase string) error
+
+ // Close releases any resources held by an open wallet instance.
+ Close() error
+
+ // Accounts retrieves the list of signing accounts the wallet is currently aware
+ // of. For hierarchical deterministic wallets, the list will not be exhaustive,
+ // rather only contain the accounts explicitly pinned during account derivation.
+ Accounts() []Account
+
+ // Contains returns whether an account is part of this particular wallet or not.
+ Contains(account Account) bool
+
+ // Derive attempts to explicitly derive a hierarchical deterministic account at
+ // the specified derivation path. If requested, the derived account will be added
+ // to the wallet's tracked account list.
+ Derive(path DerivationPath, pin bool) (Account, error)
+
+ // SelfDerive sets a base account derivation path from which the wallet attempts
+ // to discover non zero accounts and automatically add them to list of tracked
+ // accounts.
+ //
+ // Note, self derivaton will increment the last component of the specified path
+ // opposed to decending into a child path to allow discovering accounts starting
+ // from non zero components.
+ //
+ // You can disable automatic account discovery by calling SelfDerive with a nil
+ // chain state reader.
+ SelfDerive(base DerivationPath, chain ethereum.ChainStateReader)
+
+ // SignHash requests the wallet to sign the given hash.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code o verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignHash(account Account, hash []byte) ([]byte, error)
+
+ // SignTx requests the wallet to sign the given transaction.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code o verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
+
+ // SignHashWithPassphrase requests the wallet to sign the given hash with the
+ // given passphrase as extra authentication information.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ SignHashWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
+
+ // SignTxWithPassphrase requests the wallet to sign the given transaction, with the
+ // given passphrase as extra authentication information.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
+}
+
+// Backend is a "wallet provider" that may contain a batch of accounts they can
+// sign transactions with and upon request, do so.
+type Backend interface {
+ // Wallets retrieves the list of wallets the backend is currently aware of.
+ //
+ // The returned wallets are not opened by default. For software HD wallets this
+ // means that no base seeds are decrypted, and for hardware wallets that no actual
+ // connection is established.
+ //
+ // The resulting wallet list will be sorted alphabetically based on its internal
+ // URL assigned by the backend. Since wallets (especially hardware) may come and
+ // go, the same wallet might appear at a different positions in the list during
+ // subsequent retrievals.
+ Wallets() []Wallet
+
+ // Subscribe creates an async subscription to receive notifications when the
+ // backend detects the arrival or departure of a wallet.
+ Subscribe(sink chan<- WalletEvent) event.Subscription
+}
+
+// WalletEvent is an event fired by an account backend when a wallet arrival or
+// departure is detected.
+type WalletEvent struct {
+ Wallet Wallet // Wallet instance arrived or departed
+ Arrive bool // Whether the wallet was added or removed
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/errors.go b/vendor/github.com/ethereum/go-ethereum/accounts/errors.go
new file mode 100644
index 000000000..9ecc1eafd
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/errors.go
@@ -0,0 +1,68 @@
+// 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 .
+
+package accounts
+
+import (
+ "errors"
+ "fmt"
+)
+
+// ErrUnknownAccount is returned for any requested operation for which no backend
+// provides the specified account.
+var ErrUnknownAccount = errors.New("unknown account")
+
+// ErrUnknownWallet is returned for any requested operation for which no backend
+// provides the specified wallet.
+var ErrUnknownWallet = errors.New("unknown wallet")
+
+// ErrNotSupported is returned when an operation is requested from an account
+// backend that it does not support.
+var ErrNotSupported = errors.New("not supported")
+
+// ErrInvalidPassphrase is returned when a decryption operation receives a bad
+// passphrase.
+var ErrInvalidPassphrase = errors.New("invalid passphrase")
+
+// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
+// secodn time.
+var ErrWalletAlreadyOpen = errors.New("wallet already open")
+
+// ErrWalletClosed is returned if a wallet is attempted to be opened the
+// secodn time.
+var ErrWalletClosed = errors.New("wallet closed")
+
+// AuthNeededError is returned by backends for signing requests where the user
+// is required to provide further authentication before signing can succeed.
+//
+// This usually means either that a password needs to be supplied, or perhaps a
+// one time PIN code displayed by some hardware device.
+type AuthNeededError struct {
+ Needed string // Extra authentication the user needs to provide
+}
+
+// NewAuthNeededError creates a new authentication error with the extra details
+// about the needed fields set.
+func NewAuthNeededError(needed string) error {
+ return &AuthNeededError{
+ Needed: needed,
+ }
+}
+
+// Error implements the standard error interfacel.
+func (err *AuthNeededError) Error() string {
+ return fmt.Sprintf("authentication needed: %s", err.Needed)
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/hd.go b/vendor/github.com/ethereum/go-ethereum/accounts/hd.go
new file mode 100644
index 000000000..e8bc191af
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/hd.go
@@ -0,0 +1,130 @@
+// 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 .
+
+package accounts
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+ "strings"
+)
+
+// DefaultRootDerivationPath is the root path to which custom derivation endpoints
+// are appended. As such, the first account will be at m/44'/60'/0'/0, the second
+// at m/44'/60'/0'/1, etc.
+var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0}
+
+// DefaultBaseDerivationPath is the base path from which custom derivation endpoints
+// are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
+// at m/44'/60'/0'/1, etc.
+var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
+
+// DerivationPath represents the computer friendly version of a hierarchical
+// deterministic wallet account derivaion path.
+//
+// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
+// defines derivation paths to be of the form:
+//
+// m / purpose' / coin_type' / account' / change / address_index
+//
+// The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
+// defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and
+// SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns
+// the `coin_type` 60' (or 0x8000003C) to Ethereum.
+//
+// The root path for Ethereum is m/44'/60'/0'/0 according to the specification
+// from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone
+// yet whether accounts should increment the last component or the children of
+// that. We will go with the simpler approach of incrementing the last component.
+type DerivationPath []uint32
+
+// ParseDerivationPath converts a user specified derivation path string to the
+// internal binary representation.
+//
+// Full derivation paths need to start with the `m/` prefix, relative derivation
+// paths (which will get appended to the default root path) must not have prefixes
+// in front of the first element. Whitespace is ignored.
+func ParseDerivationPath(path string) (DerivationPath, error) {
+ var result DerivationPath
+
+ // Handle absolute or relative paths
+ components := strings.Split(path, "/")
+ switch {
+ case len(components) == 0:
+ return nil, errors.New("empty derivation path")
+
+ case strings.TrimSpace(components[0]) == "":
+ return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones")
+
+ case strings.TrimSpace(components[0]) == "m":
+ components = components[1:]
+
+ default:
+ result = append(result, DefaultRootDerivationPath...)
+ }
+ // All remaining components are relative, append one by one
+ if len(components) == 0 {
+ return nil, errors.New("empty derivation path") // Empty relative paths
+ }
+ for _, component := range components {
+ // Ignore any user added whitespace
+ component = strings.TrimSpace(component)
+ var value uint32
+
+ // Handle hardened paths
+ if strings.HasSuffix(component, "'") {
+ value = 0x80000000
+ component = strings.TrimSpace(strings.TrimSuffix(component, "'"))
+ }
+ // Handle the non hardened component
+ bigval, ok := new(big.Int).SetString(component, 0)
+ if !ok {
+ return nil, fmt.Errorf("invalid component: %s", component)
+ }
+ max := math.MaxUint32 - value
+ if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
+ if value == 0 {
+ return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max)
+ }
+ return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
+ }
+ value += uint32(bigval.Uint64())
+
+ // Append and repeat
+ result = append(result, value)
+ }
+ return result, nil
+}
+
+// String implements the stringer interface, converting a binary derivation path
+// to its canonical representation.
+func (path DerivationPath) String() string {
+ result := "m"
+ for _, component := range path {
+ var hardened bool
+ if component >= 0x80000000 {
+ component -= 0x80000000
+ hardened = true
+ }
+ result = fmt.Sprintf("%s/%d", result, component)
+ if hardened {
+ result += "'"
+ }
+ }
+ return result
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/addrcache.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache.go
similarity index 72%
rename from vendor/github.com/ethereum/go-ethereum/accounts/addrcache.go
rename to vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache.go
index a99f23606..3fae3ef5b 100644
--- a/vendor/github.com/ethereum/go-ethereum/accounts/addrcache.go
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package accounts
+package keystore
import (
"bufio"
@@ -28,6 +28,7 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -38,23 +39,23 @@ import (
// exist yet, the code will attempt to create a watcher at most this often.
const minReloadInterval = 2 * time.Second
-type accountsByFile []Account
+type accountsByURL []accounts.Account
-func (s accountsByFile) Len() int { return len(s) }
-func (s accountsByFile) Less(i, j int) bool { return s[i].File < s[j].File }
-func (s accountsByFile) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s accountsByURL) Len() int { return len(s) }
+func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 }
+func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// AmbiguousAddrError is returned when attempting to unlock
// an address for which more than one file exists.
type AmbiguousAddrError struct {
Addr common.Address
- Matches []Account
+ Matches []accounts.Account
}
func (err *AmbiguousAddrError) Error() string {
files := ""
for i, a := range err.Matches {
- files += a.File
+ files += a.URL.Path
if i < len(err.Matches)-1 {
files += ", "
}
@@ -62,60 +63,63 @@ func (err *AmbiguousAddrError) Error() string {
return fmt.Sprintf("multiple keys match address (%s)", files)
}
-// addrCache is a live index of all accounts in the keystore.
-type addrCache struct {
+// accountCache is a live index of all accounts in the keystore.
+type accountCache struct {
keydir string
watcher *watcher
mu sync.Mutex
- all accountsByFile
- byAddr map[common.Address][]Account
+ all accountsByURL
+ byAddr map[common.Address][]accounts.Account
throttle *time.Timer
+ notify chan struct{}
}
-func newAddrCache(keydir string) *addrCache {
- ac := &addrCache{
+func newAccountCache(keydir string) (*accountCache, chan struct{}) {
+ ac := &accountCache{
keydir: keydir,
- byAddr: make(map[common.Address][]Account),
+ byAddr: make(map[common.Address][]accounts.Account),
+ notify: make(chan struct{}, 1),
}
ac.watcher = newWatcher(ac)
- return ac
+ return ac, ac.notify
}
-func (ac *addrCache) accounts() []Account {
+func (ac *accountCache) accounts() []accounts.Account {
ac.maybeReload()
ac.mu.Lock()
defer ac.mu.Unlock()
- cpy := make([]Account, len(ac.all))
+ cpy := make([]accounts.Account, len(ac.all))
copy(cpy, ac.all)
return cpy
}
-func (ac *addrCache) hasAddress(addr common.Address) bool {
+func (ac *accountCache) hasAddress(addr common.Address) bool {
ac.maybeReload()
ac.mu.Lock()
defer ac.mu.Unlock()
return len(ac.byAddr[addr]) > 0
}
-func (ac *addrCache) add(newAccount Account) {
+func (ac *accountCache) add(newAccount accounts.Account) {
ac.mu.Lock()
defer ac.mu.Unlock()
- i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].File >= newAccount.File })
+ i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Cmp(newAccount.URL) >= 0 })
if i < len(ac.all) && ac.all[i] == newAccount {
return
}
// newAccount is not in the cache.
- ac.all = append(ac.all, Account{})
+ ac.all = append(ac.all, accounts.Account{})
copy(ac.all[i+1:], ac.all[i:])
ac.all[i] = newAccount
ac.byAddr[newAccount.Address] = append(ac.byAddr[newAccount.Address], newAccount)
}
// note: removed needs to be unique here (i.e. both File and Address must be set).
-func (ac *addrCache) delete(removed Account) {
+func (ac *accountCache) delete(removed accounts.Account) {
ac.mu.Lock()
defer ac.mu.Unlock()
+
ac.all = removeAccount(ac.all, removed)
if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
delete(ac.byAddr, removed.Address)
@@ -124,7 +128,7 @@ func (ac *addrCache) delete(removed Account) {
}
}
-func removeAccount(slice []Account, elem Account) []Account {
+func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
for i := range slice {
if slice[i] == elem {
return append(slice[:i], slice[i+1:]...)
@@ -134,43 +138,44 @@ func removeAccount(slice []Account, elem Account) []Account {
}
// find returns the cached account for address if there is a unique match.
-// The exact matching rules are explained by the documentation of Account.
+// The exact matching rules are explained by the documentation of accounts.Account.
// Callers must hold ac.mu.
-func (ac *addrCache) find(a Account) (Account, error) {
+func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
// Limit search to address candidates if possible.
matches := ac.all
if (a.Address != common.Address{}) {
matches = ac.byAddr[a.Address]
}
- if a.File != "" {
+ if a.URL.Path != "" {
// If only the basename is specified, complete the path.
- if !strings.ContainsRune(a.File, filepath.Separator) {
- a.File = filepath.Join(ac.keydir, a.File)
+ if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
+ a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
}
for i := range matches {
- if matches[i].File == a.File {
+ if matches[i].URL == a.URL {
return matches[i], nil
}
}
if (a.Address == common.Address{}) {
- return Account{}, ErrNoMatch
+ return accounts.Account{}, ErrNoMatch
}
}
switch len(matches) {
case 1:
return matches[0], nil
case 0:
- return Account{}, ErrNoMatch
+ return accounts.Account{}, ErrNoMatch
default:
- err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]Account, len(matches))}
+ err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
copy(err.Matches, matches)
- return Account{}, err
+ return accounts.Account{}, err
}
}
-func (ac *addrCache) maybeReload() {
+func (ac *accountCache) maybeReload() {
ac.mu.Lock()
defer ac.mu.Unlock()
+
if ac.watcher.running {
return // A watcher is running and will keep the cache up-to-date.
}
@@ -188,18 +193,22 @@ func (ac *addrCache) maybeReload() {
ac.throttle.Reset(minReloadInterval)
}
-func (ac *addrCache) close() {
+func (ac *accountCache) close() {
ac.mu.Lock()
ac.watcher.close()
if ac.throttle != nil {
ac.throttle.Stop()
}
+ if ac.notify != nil {
+ close(ac.notify)
+ ac.notify = nil
+ }
ac.mu.Unlock()
}
// reload caches addresses of existing accounts.
// Callers must hold ac.mu.
-func (ac *addrCache) reload() {
+func (ac *accountCache) reload() {
accounts, err := ac.scan()
if err != nil && glog.V(logger.Debug) {
glog.Errorf("can't load keys: %v", err)
@@ -212,10 +221,14 @@ func (ac *addrCache) reload() {
for _, a := range accounts {
ac.byAddr[a.Address] = append(ac.byAddr[a.Address], a)
}
+ select {
+ case ac.notify <- struct{}{}:
+ default:
+ }
glog.V(logger.Debug).Infof("reloaded keys, cache has %d accounts", len(ac.all))
}
-func (ac *addrCache) scan() ([]Account, error) {
+func (ac *accountCache) scan() ([]accounts.Account, error) {
files, err := ioutil.ReadDir(ac.keydir)
if err != nil {
return nil, err
@@ -223,7 +236,7 @@ func (ac *addrCache) scan() ([]Account, error) {
var (
buf = new(bufio.Reader)
- addrs []Account
+ addrs []accounts.Account
keyJSON struct {
Address string `json:"address"`
}
@@ -250,7 +263,7 @@ func (ac *addrCache) scan() ([]Account, error) {
case (addr == common.Address{}):
glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path)
default:
- addrs = append(addrs, Account{Address: addr, File: path})
+ addrs = append(addrs, accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}})
}
fd.Close()
}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/key.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/key.go
similarity index 93%
rename from vendor/github.com/ethereum/go-ethereum/accounts/key.go
rename to vendor/github.com/ethereum/go-ethereum/accounts/keystore/key.go
index f01154311..35cf47a04 100644
--- a/vendor/github.com/ethereum/go-ethereum/accounts/key.go
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/key.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package accounts
+package keystore
import (
"bytes"
@@ -29,6 +29,7 @@ import (
"strings"
"time"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
@@ -47,8 +48,7 @@ type Key struct {
// we only store privkey as pubkey/address can be derived from it
// privkey in this struct is always in plaintext
PrivateKey *ecdsa.PrivateKey
- // if whisper is enabled here, the address will be used as a whisper
- // identity upon creation of the account or unlocking of the account
+ // when enabled, the key will be used as a Whisper identity
WhisperEnabled bool
// extended key is the root node for new hardened children i.e. sub-accounts
ExtendedKey *extkeys.ExtendedKey
@@ -221,14 +221,14 @@ func newKey(rand io.Reader) (*Key, error) {
return newKeyFromECDSA(privateKeyECDSA), nil
}
-func storeNewKey(ks keyStore, rand io.Reader, auth string, w bool) (*Key, Account, error) {
+func storeNewKey(ks keyStore, rand io.Reader, auth string, whisperEnabled bool) (*Key, accounts.Account, error) {
key, err := newKey(rand)
if err != nil {
- return nil, Account{}, err
+ return nil, accounts.Account{}, err
}
- key.WhisperEnabled = w
- a := Account{Address: key.Address, File: ks.JoinPath(keyFileName(key.Address))}
- if err := ks.StoreKey(a.File, key, auth); err != nil {
+ key.WhisperEnabled = whisperEnabled
+ a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}}
+ if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
zeroKey(key.PrivateKey)
return nil, a, err
}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore.go
new file mode 100644
index 000000000..716685dbc
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore.go
@@ -0,0 +1,540 @@
+// Copyright 2015 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 .
+
+// Package keystore implements encrypted storage of secp256k1 private keys.
+//
+// Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification.
+// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information.
+package keystore
+
+import (
+ "crypto/ecdsa"
+ crand "crypto/rand"
+ "errors"
+ "fmt"
+ "math/big"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/status-im/status-go/extkeys"
+)
+
+var (
+ ErrLocked = accounts.NewAuthNeededError("password or unlock")
+ ErrNoMatch = errors.New("no key for given address or file")
+ ErrDecrypt = errors.New("could not decrypt key with given passphrase")
+)
+
+// KeyStoreType is the reflect type of a keystore backend.
+var KeyStoreType = reflect.TypeOf(&KeyStore{})
+
+// KeyStoreScheme is the protocol scheme prefixing account and wallet URLs.
+var KeyStoreScheme = "keystore"
+
+// Maximum time between wallet refreshes (if filesystem notifications don't work).
+const walletRefreshCycle = 3 * time.Second
+
+// KeyStore manages a key storage directory on disk.
+type KeyStore struct {
+ storage keyStore // Storage backend, might be cleartext or encrypted
+ cache *accountCache // In-memory account cache over the filesystem storage
+ changes chan struct{} // Channel receiving change notifications from the cache
+ unlocked map[common.Address]*unlocked // Currently unlocked account (decrypted private keys)
+
+ wallets []accounts.Wallet // Wallet wrappers around the individual key files
+ updateFeed event.Feed // Event feed to notify wallet additions/removals
+ updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
+ updating bool // Whether the event notification loop is running
+
+ mu sync.RWMutex
+}
+
+type unlocked struct {
+ *Key
+ abort chan struct{}
+}
+
+// NewKeyStore creates a keystore for the given directory.
+func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
+ keydir, _ = filepath.Abs(keydir)
+ ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP}}
+ ks.init(keydir)
+ return ks
+}
+
+// NewPlaintextKeyStore creates a keystore for the given directory.
+// Deprecated: Use NewKeyStore.
+func NewPlaintextKeyStore(keydir string) *KeyStore {
+ keydir, _ = filepath.Abs(keydir)
+ ks := &KeyStore{storage: &keyStorePlain{keydir}}
+ ks.init(keydir)
+ return ks
+}
+
+func (ks *KeyStore) init(keydir string) {
+ // Lock the mutex since the account cache might call back with events
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+
+ // Initialize the set of unlocked keys and the account cache
+ ks.unlocked = make(map[common.Address]*unlocked)
+ ks.cache, ks.changes = newAccountCache(keydir)
+
+ // 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,
+ // so the finalizer will not trigger until all timed unlocks have expired.
+ runtime.SetFinalizer(ks, func(m *KeyStore) {
+ m.cache.close()
+ })
+ // Create the initial list of wallets from the cache
+ accs := ks.cache.accounts()
+ ks.wallets = make([]accounts.Wallet, len(accs))
+ for i := 0; i < len(accs); i++ {
+ ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
+ }
+}
+
+// Wallets implements accounts.Backend, returning all single-key wallets from the
+// keystore directory.
+func (ks *KeyStore) Wallets() []accounts.Wallet {
+ // Make sure the list of wallets is in sync with the account cache
+ ks.refreshWallets()
+
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ cpy := make([]accounts.Wallet, len(ks.wallets))
+ copy(cpy, ks.wallets)
+ return cpy
+}
+
+// refreshWallets retrieves the current account list and based on that does any
+// necessary wallet refreshes.
+func (ks *KeyStore) refreshWallets() {
+ // Retrieve the current list of accounts
+ ks.mu.Lock()
+ accs := ks.cache.accounts()
+
+ // Transform the current list of wallets into the new one
+ wallets := make([]accounts.Wallet, 0, len(accs))
+ events := []accounts.WalletEvent{}
+
+ for _, account := range accs {
+ // Drop wallets while they were in front of the next account
+ for len(ks.wallets) > 0 && ks.wallets[0].URL().Cmp(account.URL) < 0 {
+ events = append(events, accounts.WalletEvent{Wallet: ks.wallets[0], Arrive: false})
+ ks.wallets = ks.wallets[1:]
+ }
+ // If there are no more wallets or the account is before the next, wrap new wallet
+ if len(ks.wallets) == 0 || ks.wallets[0].URL().Cmp(account.URL) > 0 {
+ wallet := &keystoreWallet{account: account, keystore: ks}
+
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true})
+ wallets = append(wallets, wallet)
+ continue
+ }
+ // If the account is the same as the first wallet, keep it
+ if ks.wallets[0].Accounts()[0] == account {
+ wallets = append(wallets, ks.wallets[0])
+ ks.wallets = ks.wallets[1:]
+ continue
+ }
+ }
+ // Drop any leftover wallets and set the new batch
+ for _, wallet := range ks.wallets {
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: false})
+ }
+ ks.wallets = wallets
+ ks.mu.Unlock()
+
+ // Fire all wallet events and return
+ for _, event := range events {
+ ks.updateFeed.Send(event)
+ }
+}
+
+// Subscribe implements accounts.Backend, creating an async subscription to
+// receive notifications on the addition or removal of keystore wallets.
+func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ // We need the mutex to reliably start/stop the update loop
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+
+ // Subscribe the caller and track the subscriber count
+ sub := ks.updateScope.Track(ks.updateFeed.Subscribe(sink))
+
+ // Subscribers require an active notification loop, start it
+ if !ks.updating {
+ ks.updating = true
+ go ks.updater()
+ }
+ return sub
+}
+
+// updater is responsible for maintaining an up-to-date list of wallets stored in
+// the keystore, and for firing wallet addition/removal events. It listens for
+// account change events from the underlying account cache, and also periodically
+// forces a manual refresh (only triggers for systems where the filesystem notifier
+// is not running).
+func (ks *KeyStore) updater() {
+ for {
+ // Wait for an account update or a refresh timeout
+ select {
+ case <-ks.changes:
+ case <-time.After(walletRefreshCycle):
+ }
+ // Run the wallet refresher
+ ks.refreshWallets()
+
+ // If all our subscribers left, stop the updater
+ ks.mu.Lock()
+ if ks.updateScope.Count() == 0 {
+ ks.updating = false
+ ks.mu.Unlock()
+ return
+ }
+ ks.mu.Unlock()
+ }
+}
+
+// HasAddress reports whether a key with the given address is present.
+func (ks *KeyStore) HasAddress(addr common.Address) bool {
+ return ks.cache.hasAddress(addr)
+}
+
+// Accounts returns all key files present in the directory.
+func (ks *KeyStore) Accounts() []accounts.Account {
+ return ks.cache.accounts()
+}
+
+// AccountDecryptedKey returns decrypted key for account (provided that password is correct).
+func (ks *KeyStore) AccountDecryptedKey(a accounts.Account, auth string) (accounts.Account, *Key, error) {
+ return ks.getDecryptedKey(a, auth)
+}
+
+// Delete deletes the key matched by account if the passphrase is correct.
+// If the account contains no filename, the address must match a unique key.
+func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
+ // Decrypting the key isn't really necessary, but we do
+ // it anyway to check the password and zero out the key
+ // immediately afterwards.
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if key != nil {
+ zeroKey(key.PrivateKey)
+ }
+ if err != nil {
+ return err
+ }
+ // The order is crucial here. The key is dropped from the
+ // cache after the file is gone so that a reload happening in
+ // between won't insert it into the cache again.
+ err = os.Remove(a.URL.Path)
+ if err == nil {
+ ks.cache.delete(a)
+ ks.refreshWallets()
+ }
+ return err
+}
+
+// SignHash calculates a ECDSA signature for the given hash. The produced
+// signature is in the [R || S || V] format where V is 0 or 1.
+func (ks *KeyStore) SignHash(a accounts.Account, hash []byte) ([]byte, error) {
+ // Look up the key to sign with and abort if it cannot be found
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ unlockedKey, found := ks.unlocked[a.Address]
+ if !found {
+ return nil, ErrLocked
+ }
+ // Sign the hash using plain ECDSA operations
+ return crypto.Sign(hash, unlockedKey.PrivateKey)
+}
+
+// SignTx signs the given transaction with the requested account.
+func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Look up the key to sign with and abort if it cannot be found
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ unlockedKey, found := ks.unlocked[a.Address]
+ if !found {
+ return nil, ErrLocked
+ }
+ // Depending on the presence of the chain ID, sign with EIP155 or homestead
+ if chainID != nil {
+ return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
+ }
+ return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
+}
+
+// SignHashWithPassphrase signs hash if the private key matching the given address
+// can be decrypted with the given passphrase. The produced signature is in the
+// [R || S || V] format where V is 0 or 1.
+func (ks *KeyStore) SignHashWithPassphrase(a accounts.Account, passphrase string, hash []byte) (signature []byte, err error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ defer zeroKey(key.PrivateKey)
+ return crypto.Sign(hash, key.PrivateKey)
+}
+
+// SignTxWithPassphrase signs the transaction if the private key matching the
+// given address can be decrypted with the given passphrase.
+func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ defer zeroKey(key.PrivateKey)
+
+ // Depending on the presence of the chain ID, sign with EIP155 or homestead
+ if chainID != nil {
+ return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey)
+ }
+ return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey)
+}
+
+// Unlock unlocks the given account indefinitely.
+func (ks *KeyStore) Unlock(a accounts.Account, passphrase string) error {
+ return ks.TimedUnlock(a, passphrase, 0)
+}
+
+// Lock removes the private key with the given address from memory.
+func (ks *KeyStore) Lock(addr common.Address) error {
+ ks.mu.Lock()
+ if unl, found := ks.unlocked[addr]; found {
+ ks.mu.Unlock()
+ ks.expire(addr, unl, time.Duration(0)*time.Nanosecond)
+ } else {
+ ks.mu.Unlock()
+ }
+ return nil
+}
+
+// TimedUnlock unlocks the given account with the passphrase. The account
+// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
+// until the program exits. The account must match a unique key file.
+//
+// If the account address is already unlocked for a duration, TimedUnlock extends or
+// shortens the active unlock timeout. If the address was previously unlocked
+// indefinitely the timeout is not altered.
+func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout time.Duration) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+ u, found := ks.unlocked[a.Address]
+ if found {
+ if u.abort == nil {
+ // The address was unlocked indefinitely, so unlocking
+ // it with a timeout would be confusing.
+ zeroKey(key.PrivateKey)
+ return nil
+ }
+ // Terminate the expire goroutine and replace it below.
+ close(u.abort)
+ }
+ if timeout > 0 {
+ u = &unlocked{Key: key, abort: make(chan struct{})}
+ go ks.expire(a.Address, u, timeout)
+ } else {
+ u = &unlocked{Key: key}
+ }
+ ks.unlocked[a.Address] = u
+ return nil
+}
+
+// Find resolves the given account into a unique entry in the keystore.
+func (ks *KeyStore) Find(a accounts.Account) (accounts.Account, error) {
+ ks.cache.maybeReload()
+ ks.cache.mu.Lock()
+ a, err := ks.cache.find(a)
+ ks.cache.mu.Unlock()
+ return a, err
+}
+
+func (ks *KeyStore) getDecryptedKey(a accounts.Account, auth string) (accounts.Account, *Key, error) {
+ a, err := ks.Find(a)
+ if err != nil {
+ return a, nil, err
+ }
+ key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth)
+ return a, key, err
+}
+
+func (ks *KeyStore) 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:
+ ks.mu.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 ks.unlocked[addr] == u {
+ zeroKey(u.PrivateKey)
+ delete(ks.unlocked, addr)
+ }
+ ks.mu.Unlock()
+ }
+}
+
+// NewAccount generates a new key and stores it into the key directory,
+// encrypting it with the passphrase.
+func (ks *KeyStore) NewAccount(passphrase string, whisperEnabled bool) (accounts.Account, error) {
+ _, account, err := storeNewKey(ks.storage, crand.Reader, passphrase, whisperEnabled)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ // Add the account to the cache immediately rather
+ // than waiting for file system notifications to pick it up.
+ ks.cache.add(account)
+ ks.refreshWallets()
+ return account, nil
+}
+
+// Export exports as a JSON key, encrypted with newPassphrase.
+func (ks *KeyStore) Export(a accounts.Account, passphrase, newPassphrase string) (keyJSON []byte, err error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ var N, P int
+ if store, ok := ks.storage.(*keyStorePassphrase); ok {
+ N, P = store.scryptN, store.scryptP
+ } else {
+ N, P = StandardScryptN, StandardScryptP
+ }
+ return EncryptKey(key, newPassphrase, N, P)
+}
+
+// Import stores the given encrypted JSON key into the key directory.
+func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (accounts.Account, error) {
+ key, err := DecryptKey(keyJSON, passphrase)
+ if key != nil && key.PrivateKey != nil {
+ defer zeroKey(key.PrivateKey)
+ }
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ return ks.importKey(key, newPassphrase)
+}
+
+// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
+func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
+ key := newKeyFromECDSA(priv)
+ if ks.cache.hasAddress(key.Address) {
+ return accounts.Account{}, fmt.Errorf("account already exists")
+ }
+
+ return ks.importKey(key, passphrase)
+}
+
+// ImportExtendedKey stores ECDSA key (obtained from extended key) along with CKD#2 (root for sub-accounts)
+// If key file is not found, it is created. Key is encrypted with the given passphrase.
+func (ks *KeyStore) ImportExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (accounts.Account, error) {
+ key, err := newKeyFromExtendedKey(extKey)
+ if err != nil {
+ zeroKey(key.PrivateKey)
+ return accounts.Account{}, err
+ }
+
+ // if account is already imported, return cached version
+ if ks.cache.hasAddress(key.Address) {
+ a := accounts.Account{
+ Address: key.Address,
+ }
+ ks.cache.maybeReload()
+ ks.cache.mu.Lock()
+ a, err := ks.cache.find(a)
+ ks.cache.mu.Unlock()
+ if err != nil {
+ zeroKey(key.PrivateKey)
+ return a, err
+ }
+ return a, nil
+ }
+
+ return ks.importKey(key, passphrase)
+}
+
+func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, error) {
+ a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.storage.JoinPath(keyFileName(key.Address))}}
+ if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
+ return accounts.Account{}, err
+ }
+ ks.cache.add(a)
+ ks.refreshWallets()
+ return a, nil
+}
+
+func (ks *KeyStore) IncSubAccountIndex(a accounts.Account, passphrase string) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+ key.SubAccountIndex++
+ return ks.storage.StoreKey(a.URL.Path, key, passphrase)
+}
+
+// Update changes the passphrase of an existing account.
+func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+ return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
+}
+
+// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
+// a key file in the key directory. The key file is encrypted with the same passphrase.
+func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (accounts.Account, error) {
+ a, _, err := importPreSaleKey(ks.storage, keyJSON, passphrase)
+ if err != nil {
+ return a, err
+ }
+ ks.cache.add(a)
+ ks.refreshWallets()
+ return a, nil
+}
+
+// zeroKey zeroes a private key in memory.
+func zeroKey(k *ecdsa.PrivateKey) {
+ if k == nil {
+ return
+ }
+ b := k.D.Bits()
+ for i := range b {
+ b[i] = 0
+ }
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase.go
similarity index 98%
rename from vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go
rename to vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase.go
index 493602352..a82202ebc 100644
--- a/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase.go
@@ -23,7 +23,7 @@ The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-St
*/
-package accounts
+package keystore
import (
"bytes"
@@ -212,6 +212,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
// Depending on the version try to parse one way or another
var (
keyBytes, keyId []byte
+ whisperEnabled bool
err error
extKeyBytes []byte
extKey *extkeys.ExtendedKey
@@ -249,6 +250,11 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
}
extKey, err = extkeys.NewKeyFromString(string(extKeyBytes))
}
+
+ whisperEnabled, ok = m["whisperenabled"].(bool)
+ if !ok {
+ whisperEnabled = false
+ }
// Handle any decryption errors and return the key
if err != nil {
return nil, err
@@ -258,7 +264,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
Id: uuid.UUID(keyId),
Address: crypto.PubkeyToAddress(key.PublicKey),
PrivateKey: key,
- WhisperEnabled: m["whisperenabled"].(bool),
+ WhisperEnabled: whisperEnabled,
ExtendedKey: extKey,
SubAccountIndex: uint32(subAccountIndex),
}, nil
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/key_store_plain.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain.go
similarity index 99%
rename from vendor/github.com/ethereum/go-ethereum/accounts/key_store_plain.go
rename to vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain.go
index 2cbaa94df..b490ca72b 100644
--- a/vendor/github.com/ethereum/go-ethereum/accounts/key_store_plain.go
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package accounts
+package keystore
import (
"encoding/json"
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_wallet.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_wallet.go
new file mode 100644
index 000000000..7165d2821
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_wallet.go
@@ -0,0 +1,139 @@
+// 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 .
+
+package keystore
+
+import (
+ "math/big"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// keystoreWallet implements the accounts.Wallet interface for the original
+// keystore.
+type keystoreWallet struct {
+ account accounts.Account // Single account contained in this wallet
+ keystore *KeyStore // Keystore where the account originates from
+}
+
+// URL implements accounts.Wallet, returning the URL of the account within.
+func (w *keystoreWallet) URL() accounts.URL {
+ return w.account.URL
+}
+
+// Status implements accounts.Wallet, always returning "open", since there is no
+// concept of open/close for plain keystore accounts.
+func (w *keystoreWallet) Status() string {
+ w.keystore.mu.RLock()
+ defer w.keystore.mu.RUnlock()
+
+ if _, ok := w.keystore.unlocked[w.account.Address]; ok {
+ return "Unlocked"
+ }
+ return "Locked"
+}
+
+// Open implements accounts.Wallet, but is a noop for plain wallets since there
+// is no connection or decryption step necessary to access the list of accounts.
+func (w *keystoreWallet) Open(passphrase string) error { return nil }
+
+// Close implements accounts.Wallet, but is a noop for plain wallets since is no
+// meaningful open operation.
+func (w *keystoreWallet) Close() error { return nil }
+
+// Accounts implements accounts.Wallet, returning an account list consisting of
+// a single account that the plain kestore wallet contains.
+func (w *keystoreWallet) Accounts() []accounts.Account {
+ return []accounts.Account{w.account}
+}
+
+// Contains implements accounts.Wallet, returning whether a particular account is
+// or is not wrapped by this wallet instance.
+func (w *keystoreWallet) Contains(account accounts.Account) bool {
+ return account.Address == w.account.Address && (account.URL == (accounts.URL{}) || account.URL == w.account.URL)
+}
+
+// Derive implements accounts.Wallet, but is a noop for plain wallets since there
+// is no notion of hierarchical account derivation for plain keystore accounts.
+func (w *keystoreWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ return accounts.Account{}, accounts.ErrNotSupported
+}
+
+// SelfDerive implements accounts.Wallet, but is a noop for plain wallets since
+// there is no notion of hierarchical account derivation for plain keystore accounts.
+func (w *keystoreWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {}
+
+// SignHash implements accounts.Wallet, attempting to sign the given hash with
+// the given account. If the wallet does not wrap this particular account, an
+// error is returned to avoid account leakage (even though in theory we may be
+// able to sign via our shared keystore backend).
+func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if account.Address != w.account.Address {
+ return nil, accounts.ErrUnknownAccount
+ }
+ if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHash(account, hash)
+}
+
+// SignTx implements accounts.Wallet, attempting to sign the given transaction
+// with the given account. If the wallet does not wrap this particular account,
+// an error is returned to avoid account leakage (even though in theory we may
+// be able to sign via our shared keystore backend).
+func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Make sure the requested account is contained within
+ if account.Address != w.account.Address {
+ return nil, accounts.ErrUnknownAccount
+ }
+ if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTx(account, tx, chainID)
+}
+
+// SignHashWithPassphrase implements accounts.Wallet, attempting to sign the
+// given hash with the given account using passphrase as extra authentication.
+func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if account.Address != w.account.Address {
+ return nil, accounts.ErrUnknownAccount
+ }
+ if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHashWithPassphrase(account, passphrase, hash)
+}
+
+// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
+// transaction with the given account using passphrase as extra authentication.
+func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Make sure the requested account is contained within
+ if account.Address != w.account.Address {
+ return nil, accounts.ErrUnknownAccount
+ }
+ if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTxWithPassphrase(account, passphrase, tx, chainID)
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/presale.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/presale.go
similarity index 92%
rename from vendor/github.com/ethereum/go-ethereum/accounts/presale.go
rename to vendor/github.com/ethereum/go-ethereum/accounts/keystore/presale.go
index f00b4f502..5b883c45f 100644
--- a/vendor/github.com/ethereum/go-ethereum/accounts/presale.go
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/presale.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package accounts
+package keystore
import (
"crypto/aes"
@@ -25,20 +25,21 @@ import (
"errors"
"fmt"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/crypto"
"github.com/pborman/uuid"
"golang.org/x/crypto/pbkdf2"
)
// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
-func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (Account, *Key, error) {
+func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accounts.Account, *Key, error) {
key, err := decryptPreSaleKey(keyJSON, password)
if err != nil {
- return Account{}, nil, err
+ return accounts.Account{}, nil, err
}
key.Id = uuid.NewRandom()
- a := Account{Address: key.Address, File: keyStore.JoinPath(keyFileName(key.Address))}
- err = keyStore.StoreKey(a.File, key, password)
+ a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: keyStore.JoinPath(keyFileName(key.Address))}}
+ err = keyStore.StoreKey(a.URL.Path, key, password)
return a, key, err
}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/watch.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch.go
similarity index 96%
rename from vendor/github.com/ethereum/go-ethereum/accounts/watch.go
rename to vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch.go
index 472be2df7..0b4401255 100644
--- a/vendor/github.com/ethereum/go-ethereum/accounts/watch.go
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch.go
@@ -16,7 +16,7 @@
// +build darwin,!ios freebsd linux,!arm64 netbsd solaris
-package accounts
+package keystore
import (
"time"
@@ -27,14 +27,14 @@ import (
)
type watcher struct {
- ac *addrCache
+ ac *accountCache
starting bool
running bool
ev chan notify.EventInfo
quit chan struct{}
}
-func newWatcher(ac *addrCache) *watcher {
+func newWatcher(ac *accountCache) *watcher {
return &watcher{
ac: ac,
ev: make(chan notify.EventInfo, 10),
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/watch_fallback.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch_fallback.go
similarity index 85%
rename from vendor/github.com/ethereum/go-ethereum/accounts/watch_fallback.go
rename to vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch_fallback.go
index bf971cb1b..7c5e9cb2e 100644
--- a/vendor/github.com/ethereum/go-ethereum/accounts/watch_fallback.go
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch_fallback.go
@@ -19,10 +19,10 @@
// This is the fallback implementation of directory watching.
// It is used on unsupported platforms.
-package accounts
+package keystore
type watcher struct{ running bool }
-func newWatcher(*addrCache) *watcher { return new(watcher) }
-func (*watcher) start() {}
-func (*watcher) close() {}
+func newWatcher(*accountCache) *watcher { return new(watcher) }
+func (*watcher) start() {}
+func (*watcher) close() {}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/manager.go b/vendor/github.com/ethereum/go-ethereum/accounts/manager.go
new file mode 100644
index 000000000..12a5bfcd9
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/manager.go
@@ -0,0 +1,198 @@
+// 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 .
+
+package accounts
+
+import (
+ "reflect"
+ "sort"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/event"
+)
+
+// Manager is an overarching account manager that can communicate with various
+// backends for signing transactions.
+type Manager struct {
+ backends map[reflect.Type][]Backend // Index of backends currently registered
+ updaters []event.Subscription // Wallet update subscriptions for all backends
+ updates chan WalletEvent // Subscription sink for backend wallet changes
+ wallets []Wallet // Cache of all wallets from all registered backends
+
+ feed event.Feed // Wallet feed notifying of arrivals/departures
+
+ quit chan chan error
+ lock sync.RWMutex
+}
+
+// NewManager creates a generic account manager to sign transaction via various
+// supported backends.
+func NewManager(backends ...Backend) *Manager {
+ // Subscribe to wallet notifications from all backends
+ updates := make(chan WalletEvent, 4*len(backends))
+
+ subs := make([]event.Subscription, len(backends))
+ 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),
+ updaters: subs,
+ updates: updates,
+ wallets: wallets,
+ quit: make(chan chan error),
+ }
+ for _, backend := range backends {
+ kind := reflect.TypeOf(backend)
+ am.backends[kind] = append(am.backends[kind], backend)
+ }
+ go am.update()
+
+ return am
+}
+
+// Close terminates the account manager's internal notification processes.
+func (am *Manager) Close() error {
+ errc := make(chan error)
+ am.quit <- errc
+ return <-errc
+}
+
+// update is the wallet event loop listening for notifications from the backends
+// and updating the cache of wallets.
+func (am *Manager) update() {
+ // Close all subscriptions when the manager terminates
+ defer func() {
+ am.lock.Lock()
+ for _, sub := range am.updaters {
+ sub.Unsubscribe()
+ }
+ am.updaters = nil
+ am.lock.Unlock()
+ }()
+
+ // Loop until termination
+ for {
+ select {
+ case event := <-am.updates:
+ // Wallet event arrived, update local cache
+ am.lock.Lock()
+ if event.Arrive {
+ am.wallets = merge(am.wallets, event.Wallet)
+ } else {
+ am.wallets = drop(am.wallets, event.Wallet)
+ }
+ am.lock.Unlock()
+
+ // Notify any listeners of the event
+ am.feed.Send(event)
+
+ case errc := <-am.quit:
+ // Manager terminating, return
+ errc <- nil
+ return
+ }
+ }
+}
+
+// Backends retrieves the backend(s) with the given type from the account manager.
+func (am *Manager) Backends(kind reflect.Type) []Backend {
+ return am.backends[kind]
+}
+
+// Wallets returns all signer accounts registered under this account manager.
+func (am *Manager) Wallets() []Wallet {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ cpy := make([]Wallet, len(am.wallets))
+ copy(cpy, am.wallets)
+ return cpy
+}
+
+// Wallet retrieves the wallet associated with a particular URL.
+func (am *Manager) Wallet(url string) (Wallet, error) {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ parsed, err := parseURL(url)
+ if err != nil {
+ return nil, err
+ }
+ for _, wallet := range am.Wallets() {
+ if wallet.URL() == parsed {
+ return wallet, nil
+ }
+ }
+ return nil, ErrUnknownWallet
+}
+
+// Find attempts to locate the wallet corresponding to a specific account. Since
+// accounts can be dynamically added to and removed from wallets, this method has
+// a linear runtime in the number of wallets.
+func (am *Manager) Find(account Account) (Wallet, error) {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ for _, wallet := range am.wallets {
+ if wallet.Contains(account) {
+ return wallet, nil
+ }
+ }
+ return nil, ErrUnknownAccount
+}
+
+// Subscribe creates an async subscription to receive notifications when the
+// manager detects the arrival or departure of a wallet from any of its backends.
+func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription {
+ return am.feed.Subscribe(sink)
+}
+
+// merge is a sorted analogue of append for wallets, where the ordering of the
+// origin list is preserved by inserting new wallets at the correct position.
+//
+// The original slice is assumed to be already sorted by URL.
+func merge(slice []Wallet, wallets ...Wallet) []Wallet {
+ for _, wallet := range wallets {
+ n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
+ if n == len(slice) {
+ slice = append(slice, wallet)
+ continue
+ }
+ slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
+ }
+ return slice
+}
+
+// drop is the couterpart of merge, which looks up wallets from within the sorted
+// cache and removes the ones specified.
+func drop(slice []Wallet, wallets ...Wallet) []Wallet {
+ for _, wallet := range wallets {
+ n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
+ if n == len(slice) {
+ // Wallet not found, may happen during startup
+ continue
+ }
+ slice = append(slice[:n], slice[n+1:]...)
+ }
+ return slice
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/url.go b/vendor/github.com/ethereum/go-ethereum/accounts/url.go
new file mode 100644
index 000000000..a2d00c1c6
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/url.go
@@ -0,0 +1,79 @@
+// 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 .
+
+package accounts
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+)
+
+// URL represents the canonical identification URL of a wallet or account.
+//
+// It is a simplified version of url.URL, with the important limitations (which
+// are considered features here) that it contains value-copyable components only,
+// as well as that it doesn't do any URL encoding/decoding of special characters.
+//
+// The former is important to allow an account to be copied without leaving live
+// references to the original version, whereas the latter is important to ensure
+// one single canonical form opposed to many allowed ones by the RFC 3986 spec.
+//
+// As such, these URLs should not be used outside of the scope of an Ethereum
+// wallet or account.
+type URL struct {
+ Scheme string // Protocol scheme to identify a capable account backend
+ Path string // Path for the backend to identify a unique entity
+}
+
+// parseURL converts a user supplied URL into the accounts specific structure.
+func parseURL(url string) (URL, error) {
+ parts := strings.Split(url, "://")
+ if len(parts) != 2 || parts[0] == "" {
+ return URL{}, errors.New("protocol scheme missing")
+ }
+ return URL{
+ Scheme: parts[0],
+ Path: parts[1],
+ }, nil
+}
+
+// String implements the stringer interface.
+func (u URL) String() string {
+ if u.Scheme != "" {
+ return fmt.Sprintf("%s://%s", u.Scheme, u.Path)
+ }
+ return u.Path
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (u URL) MarshalJSON() ([]byte, error) {
+ return json.Marshal(u.String())
+}
+
+// Cmp compares x and y and returns:
+//
+// -1 if x < y
+// 0 if x == y
+// +1 if x > y
+//
+func (u URL) Cmp(url URL) int {
+ if u.Scheme == url.Scheme {
+ return strings.Compare(u.Path, url.Path)
+ }
+ return strings.Compare(u.Scheme, url.Scheme)
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_hub.go b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_hub.go
new file mode 100644
index 000000000..ad5940cd4
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_hub.go
@@ -0,0 +1,209 @@
+// 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 .
+
+// This file contains the implementation for interacting with the Ledger hardware
+// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
+// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
+
+// +build !ios
+
+package usbwallet
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/karalabe/gousb/usb"
+)
+
+// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
+var LedgerScheme = "ledger"
+
+// ledgerDeviceIDs are the known device IDs that Ledger wallets use.
+var ledgerDeviceIDs = []deviceID{
+ {Vendor: 0x2c97, Product: 0x0000}, // Ledger Blue
+ {Vendor: 0x2c97, Product: 0x0001}, // Ledger Nano S
+}
+
+// Maximum time between wallet refreshes (if USB hotplug notifications don't work).
+const ledgerRefreshCycle = time.Second
+
+// Minimum time between wallet refreshes to avoid USB trashing.
+const ledgerRefreshThrottling = 500 * time.Millisecond
+
+// LedgerHub is a accounts.Backend that can find and handle Ledger hardware wallets.
+type LedgerHub struct {
+ ctx *usb.Context // Context interfacing with a libusb instance
+
+ refreshed time.Time // Time instance when the list of wallets was last refreshed
+ wallets []accounts.Wallet // List of Ledger devices currently tracking
+ updateFeed event.Feed // Event feed to notify wallet additions/removals
+ updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
+ updating bool // Whether the event notification loop is running
+
+ quit chan chan error
+ lock sync.RWMutex
+}
+
+// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
+func NewLedgerHub() (*LedgerHub, error) {
+ // Initialize the USB library to access Ledgers through
+ ctx, err := usb.NewContext()
+ if err != nil {
+ return nil, err
+ }
+ // Create the USB hub, start and return it
+ hub := &LedgerHub{
+ ctx: ctx,
+ quit: make(chan chan error),
+ }
+ hub.refreshWallets()
+
+ return hub, nil
+}
+
+// Wallets implements accounts.Backend, returning all the currently tracked USB
+// devices that appear to be Ledger hardware wallets.
+func (hub *LedgerHub) Wallets() []accounts.Wallet {
+ // Make sure the list of wallets is up to date
+ hub.refreshWallets()
+
+ hub.lock.RLock()
+ defer hub.lock.RUnlock()
+
+ cpy := make([]accounts.Wallet, len(hub.wallets))
+ copy(cpy, hub.wallets)
+ return cpy
+}
+
+// refreshWallets scans the USB devices attached to the machine and updates the
+// list of wallets based on the found devices.
+func (hub *LedgerHub) refreshWallets() {
+ // Don't scan the USB like crazy it the user fetches wallets in a loop
+ hub.lock.RLock()
+ elapsed := time.Since(hub.refreshed)
+ hub.lock.RUnlock()
+
+ if elapsed < ledgerRefreshThrottling {
+ return
+ }
+ // Retrieve the current list of Ledger devices
+ var devIDs []deviceID
+ var busIDs []uint16
+
+ hub.ctx.ListDevices(func(desc *usb.Descriptor) bool {
+ // Gather Ledger devices, don't connect any just yet
+ for _, id := range ledgerDeviceIDs {
+ if desc.Vendor == id.Vendor && desc.Product == id.Product {
+ devIDs = append(devIDs, deviceID{Vendor: desc.Vendor, Product: desc.Product})
+ busIDs = append(busIDs, uint16(desc.Bus)<<8+uint16(desc.Address))
+ return false
+ }
+ }
+ // Not ledger, ignore and don't connect either
+ return false
+ })
+ // Transform the current list of wallets into the new one
+ hub.lock.Lock()
+
+ wallets := make([]accounts.Wallet, 0, len(devIDs))
+ events := []accounts.WalletEvent{}
+
+ for i := 0; i < len(devIDs); i++ {
+ devID, busID := devIDs[i], busIDs[i]
+
+ url := accounts.URL{Scheme: LedgerScheme, Path: fmt.Sprintf("%03d:%03d", busID>>8, busID&0xff)}
+
+ // Drop wallets in front of the next device or those that failed for some reason
+ for len(hub.wallets) > 0 && (hub.wallets[0].URL().Cmp(url) < 0 || hub.wallets[0].(*ledgerWallet).failed()) {
+ events = append(events, accounts.WalletEvent{Wallet: hub.wallets[0], Arrive: false})
+ hub.wallets = hub.wallets[1:]
+ }
+ // If there are no more wallets or the device is before the next, wrap new wallet
+ if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 {
+ wallet := &ledgerWallet{context: hub.ctx, hardwareID: devID, locationID: busID, url: &url}
+
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true})
+ wallets = append(wallets, wallet)
+ continue
+ }
+ // If the device is the same as the first wallet, keep it
+ if hub.wallets[0].URL().Cmp(url) == 0 {
+ wallets = append(wallets, hub.wallets[0])
+ hub.wallets = hub.wallets[1:]
+ continue
+ }
+ }
+ // Drop any leftover wallets and set the new batch
+ for _, wallet := range hub.wallets {
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: false})
+ }
+ hub.refreshed = time.Now()
+ hub.wallets = wallets
+ hub.lock.Unlock()
+
+ // Fire all wallet events and return
+ for _, event := range events {
+ hub.updateFeed.Send(event)
+ }
+}
+
+// Subscribe implements accounts.Backend, creating an async subscription to
+// receive notifications on the addition or removal of Ledger wallets.
+func (hub *LedgerHub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ // We need the mutex to reliably start/stop the update loop
+ hub.lock.Lock()
+ defer hub.lock.Unlock()
+
+ // Subscribe the caller and track the subscriber count
+ sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink))
+
+ // Subscribers require an active notification loop, start it
+ if !hub.updating {
+ hub.updating = true
+ go hub.updater()
+ }
+ return sub
+}
+
+// updater is responsible for maintaining an up-to-date list of wallets stored in
+// the keystore, and for firing wallet addition/removal events. It listens for
+// account change events from the underlying account cache, and also periodically
+// forces a manual refresh (only triggers for systems where the filesystem notifier
+// is not running).
+func (hub *LedgerHub) updater() {
+ for {
+ // Wait for a USB hotplug event (not supported yet) or a refresh timeout
+ select {
+ //case <-hub.changes: // reenable on hutplug implementation
+ case <-time.After(ledgerRefreshCycle):
+ }
+ // Run the wallet refresher
+ hub.refreshWallets()
+
+ // If all our subscribers left, stop the updater
+ hub.lock.Lock()
+ if hub.updateScope.Count() == 0 {
+ hub.updating = false
+ hub.lock.Unlock()
+ return
+ }
+ hub.lock.Unlock()
+ }
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_wallet.go b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_wallet.go
new file mode 100644
index 000000000..a667f580a
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_wallet.go
@@ -0,0 +1,945 @@
+// 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 .
+
+// This file contains the implementation for interacting with the Ledger hardware
+// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
+// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
+
+// +build !ios
+
+package usbwallet
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "sync"
+ "time"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/karalabe/gousb/usb"
+ "golang.org/x/net/context"
+)
+
+// Maximum time between wallet health checks to detect USB unplugs.
+const ledgerHeartbeatCycle = time.Second
+
+// Minimum time to wait between self derivation attempts, even it the user is
+// requesting accounts like crazy.
+const ledgerSelfDeriveThrottling = time.Second
+
+// ledgerOpcode is an enumeration encoding the supported Ledger opcodes.
+type ledgerOpcode byte
+
+// ledgerParam1 is an enumeration encoding the supported Ledger parameters for
+// specific opcodes. The same parameter values may be reused between opcodes.
+type ledgerParam1 byte
+
+// ledgerParam2 is an enumeration encoding the supported Ledger parameters for
+// specific opcodes. The same parameter values may be reused between opcodes.
+type ledgerParam2 byte
+
+const (
+ ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path
+ ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters
+ ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration
+
+ ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet
+ ledgerP1ConfirmFetchAddress ledgerParam1 = 0x01 // Require a user confirmation before returning the address
+ ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
+ ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
+ ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
+ ledgerP2ReturnAddressChainCode ledgerParam2 = 0x01 // Require a user confirmation before returning the address
+)
+
+// errReplyInvalidHeader is the error message returned by a Ledfer data exchange
+// if the device replies with a mismatching header. This usually means the device
+// is in browser mode.
+var errReplyInvalidHeader = errors.New("invalid reply header")
+
+// ledgerWallet represents a live USB Ledger hardware wallet.
+type ledgerWallet struct {
+ context *usb.Context // USB context to interface libusb through
+ hardwareID deviceID // USB identifiers to identify this device type
+ locationID uint16 // USB bus and address to identify this device instance
+ url *accounts.URL // Textual URL uniquely identifying this wallet
+
+ device *usb.Device // USB device advertising itself as a Ledger wallet
+ input usb.Endpoint // Input endpoint to send data to this device
+ output usb.Endpoint // Output endpoint to receive data from this device
+ failure error // Any failure that would make the device unusable
+
+ version [3]byte // Current version of the Ledger Ethereum app (zero if app is offline)
+ browser bool // Flag whether the Ledger is in browser mode (reply channel mismatch)
+ accounts []accounts.Account // List of derive accounts pinned on the Ledger
+ paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
+
+ deriveNextPath accounts.DerivationPath // Next derivation path for account auto-discovery
+ deriveNextAddr common.Address // Next derived account address for auto-discovery
+ deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
+ deriveReq chan chan struct{} // Channel to request a self-derivation on
+ deriveQuit chan chan error // Channel to terminate the self-deriver with
+
+ healthQuit chan chan error
+
+ // Locking a hardware wallet is a bit special. Since hardware devices are lower
+ // performing, any communication with them might take a non negligible amount of
+ // time. Worse still, waiting for user confirmation can take arbitrarily long,
+ // but exclusive communication must be upheld during. Locking the entire wallet
+ // in the mean time however would stall any parts of the system that don't want
+ // to communicate, just read some state (e.g. list the accounts).
+ //
+ // As such, a hardware wallet needs two locks to function correctly. A state
+ // lock can be used to protect the wallet's software-side internal state, which
+ // must not be held exlusively during hardware communication. A communication
+ // lock can be used to achieve exclusive access to the device itself, this one
+ // however should allow "skipping" waiting for operations that might want to
+ // use the device, but can live without too (e.g. account self-derivation).
+ //
+ // Since we have two locks, it's important to know how to properly use them:
+ // - Communication requires the `device` to not change, so obtaining the
+ // commsLock should be done after having a stateLock.
+ // - Communication must not disable read access to the wallet state, so it
+ // must only ever hold a *read* lock to stateLock.
+ commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked
+ stateLock sync.RWMutex // Protects read and write access to the wallet struct fields
+}
+
+// URL implements accounts.Wallet, returning the URL of the Ledger device.
+func (w *ledgerWallet) URL() accounts.URL {
+ return *w.url // Immutable, no need for a lock
+}
+
+// Status implements accounts.Wallet, always whether the Ledger is opened, closed
+// or whether the Ethereum app was not started on it.
+func (w *ledgerWallet) Status() string {
+ w.stateLock.RLock() // No device communication, state lock is enough
+ defer w.stateLock.RUnlock()
+
+ if w.failure != nil {
+ return fmt.Sprintf("Failed: %v", w.failure)
+ }
+ if w.device == nil {
+ return "Closed"
+ }
+ if w.browser {
+ return "Ethereum app in browser mode"
+ }
+ if w.offline() {
+ return "Ethereum app offline"
+ }
+ return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2])
+}
+
+// offline returns whether the wallet and the Ethereum app is offline or not.
+//
+// The method assumes that the state lock is held!
+func (w *ledgerWallet) offline() bool {
+ return w.version == [3]byte{0, 0, 0}
+}
+
+// failed returns if the USB device wrapped by the wallet failed for some reason.
+// This is used by the device scanner to report failed wallets as departed.
+//
+// The method assumes that the state lock is *not* held!
+func (w *ledgerWallet) failed() bool {
+ w.stateLock.RLock() // No device communication, state lock is enough
+ defer w.stateLock.RUnlock()
+
+ return w.failure != nil
+}
+
+// Open implements accounts.Wallet, attempting to open a USB connection to the
+// Ledger hardware wallet. The Ledger does not require a user passphrase, so that
+// parameter is silently discarded.
+func (w *ledgerWallet) Open(passphrase string) error {
+ w.stateLock.Lock() // State lock is enough since there's no connection yet at this point
+ defer w.stateLock.Unlock()
+
+ // If the wallet was already opened, don't try to open again
+ if w.device != nil {
+ return accounts.ErrWalletAlreadyOpen
+ }
+ // Otherwise iterate over all USB devices and find this again (no way to directly do this)
+ // Iterate over all attached devices and fetch those seemingly Ledger
+ devices, err := w.context.ListDevices(func(desc *usb.Descriptor) bool {
+ // Only open this single specific device
+ return desc.Vendor == w.hardwareID.Vendor && desc.Product == w.hardwareID.Product &&
+ uint16(desc.Bus)<<8+uint16(desc.Address) == w.locationID
+ })
+ if err != nil {
+ return err
+ }
+ if len(devices) == 0 {
+ return accounts.ErrUnknownWallet
+ }
+ // Device opened, attach to the input and output endpoints
+ device := devices[0]
+
+ var invalid string
+ switch {
+ case len(device.Descriptor.Configs) == 0:
+ invalid = "no endpoint config available"
+ case len(device.Descriptor.Configs[0].Interfaces) == 0:
+ invalid = "no endpoint interface available"
+ case len(device.Descriptor.Configs[0].Interfaces[0].Setups) == 0:
+ invalid = "no endpoint setup available"
+ case len(device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints) < 2:
+ invalid = "not enough IO endpoints available"
+ }
+ if invalid != "" {
+ device.Close()
+ return fmt.Errorf("ledger wallet [%s] invalid: %s", w.url, invalid)
+ }
+ // Open the input and output endpoints to the device
+ input, err := device.OpenEndpoint(
+ device.Descriptor.Configs[0].Config,
+ device.Descriptor.Configs[0].Interfaces[0].Number,
+ device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number,
+ device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[1].Address,
+ )
+ if err != nil {
+ device.Close()
+ return fmt.Errorf("ledger wallet [%s] input open failed: %v", w.url, err)
+ }
+ output, err := device.OpenEndpoint(
+ device.Descriptor.Configs[0].Config,
+ device.Descriptor.Configs[0].Interfaces[0].Number,
+ device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number,
+ device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[0].Address,
+ )
+ if err != nil {
+ device.Close()
+ return fmt.Errorf("ledger wallet [%s] output open failed: %v", w.url, err)
+ }
+ // Wallet seems to be successfully opened, guess if the Ethereum app is running
+ w.device, w.input, w.output = device, input, output
+ w.commsLock = make(chan struct{}, 1)
+ w.commsLock <- struct{}{} // Enable lock
+
+ w.paths = make(map[common.Address]accounts.DerivationPath)
+
+ w.deriveReq = make(chan chan struct{})
+ w.deriveQuit = make(chan chan error)
+ w.healthQuit = make(chan chan error)
+
+ defer func() {
+ go w.heartbeat()
+ go w.selfDerive()
+ }()
+
+ if _, err = w.ledgerDerive(accounts.DefaultBaseDerivationPath); err != nil {
+ // Ethereum app is not running or in browser mode, nothing more to do, return
+ if err == errReplyInvalidHeader {
+ w.browser = true
+ }
+ return nil
+ }
+ // Try to resolve the Ethereum app's version, will fail prior to v1.0.2
+ if w.version, err = w.ledgerVersion(); err != nil {
+ w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1
+ }
+ return nil
+}
+
+// heartbeat is a health check loop for the Ledger wallets to periodically verify
+// whether they are still present or if they malfunctioned. It is needed because:
+// - libusb on Windows doesn't support hotplug, so we can't detect USB unplugs
+// - communication timeout on the Ledger requires a device power cycle to fix
+func (w *ledgerWallet) heartbeat() {
+ glog.V(logger.Debug).Infof("%s health-check started", w.url.String())
+ defer glog.V(logger.Debug).Infof("%s health-check stopped", w.url.String())
+
+ // Execute heartbeat checks until termination or error
+ var (
+ errc chan error
+ err error
+ )
+ for errc == nil && err == nil {
+ // Wait until termination is requested or the heartbeat cycle arrives
+ select {
+ case errc = <-w.healthQuit:
+ // Termination requested
+ continue
+ case <-time.After(ledgerHeartbeatCycle):
+ // Heartbeat time
+ }
+ // Execute a tiny data exchange to see responsiveness
+ w.stateLock.RLock()
+ if w.device == nil {
+ // Terminated while waiting for the lock
+ w.stateLock.RUnlock()
+ continue
+ }
+ <-w.commsLock // Don't lock state while resolving version
+ _, err = w.ledgerVersion()
+ w.commsLock <- struct{}{}
+ w.stateLock.RUnlock()
+
+ if err == usb.ERROR_IO || err == usb.ERROR_NO_DEVICE {
+ w.stateLock.Lock() // Lock state to tear the wallet down
+ w.failure = err
+ w.close()
+ w.stateLock.Unlock()
+ }
+ // Ignore uninteresting errors
+ err = nil
+ }
+ // In case of error, wait for termination
+ if err != nil {
+ glog.V(logger.Debug).Infof("%s health-check failed: %v", w.url.String(), err)
+ errc = <-w.healthQuit
+ }
+ errc <- err
+}
+
+// Close implements accounts.Wallet, closing the USB connection to the Ledger.
+func (w *ledgerWallet) Close() error {
+ // Ensure the wallet was opened
+ w.stateLock.RLock()
+ hQuit, dQuit := w.healthQuit, w.deriveQuit
+ w.stateLock.RUnlock()
+
+ // Terminate the health checks
+ var herr error
+ if hQuit != nil {
+ errc := make(chan error)
+ hQuit <- errc
+ herr = <-errc // Save for later, we *must* close the USB
+ }
+ // Terminate the self-derivations
+ var derr error
+ if dQuit != nil {
+ errc := make(chan error)
+ dQuit <- errc
+ derr = <-errc // Save for later, we *must* close the USB
+ }
+ // Terminate the device connection
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ w.healthQuit = nil
+ w.deriveQuit = nil
+ w.deriveReq = nil
+
+ if err := w.close(); err != nil {
+ return err
+ }
+ if herr != nil {
+ return herr
+ }
+ return derr
+}
+
+// close is the internal wallet closer that terminates the USB connection and
+// resets all the fields to their defaults.
+//
+// Note, close assumes the state lock is held!
+func (w *ledgerWallet) close() error {
+ // Allow duplicate closes, especially for health-check failures
+ if w.device == nil {
+ return nil
+ }
+ // Close the device, clear everything, then return
+ err := w.device.Close()
+
+ w.device, w.input, w.output = nil, nil, nil
+ w.browser, w.version = false, [3]byte{}
+ w.accounts, w.paths = nil, nil
+
+ return err
+}
+
+// Accounts implements accounts.Wallet, returning the list of accounts pinned to
+// the Ledger hardware wallet. If self-derivation was enabled, the account list
+// is periodically expanded based on current chain state.
+func (w *ledgerWallet) Accounts() []accounts.Account {
+ // Attempt self-derivation if it's running
+ reqc := make(chan struct{}, 1)
+ select {
+ case w.deriveReq <- reqc:
+ // Self-derivation request accepted, wait for it
+ <-reqc
+ default:
+ // Self-derivation offline, throttled or busy, skip
+ }
+ // Return whatever account list we ended up with
+ w.stateLock.RLock()
+ defer w.stateLock.RUnlock()
+
+ cpy := make([]accounts.Account, len(w.accounts))
+ copy(cpy, w.accounts)
+ return cpy
+}
+
+// selfDerive is an account derivation loop that upon request attempts to find
+// new non-zero accounts.
+func (w *ledgerWallet) selfDerive() {
+ glog.V(logger.Debug).Infof("%s self-derivation started", w.url.String())
+ defer glog.V(logger.Debug).Infof("%s self-derivation stopped", w.url.String())
+
+ // Execute self-derivations until termination or error
+ var (
+ reqc chan struct{}
+ errc chan error
+ err error
+ )
+ for errc == nil && err == nil {
+ // Wait until either derivation or termination is requested
+ select {
+ case errc = <-w.deriveQuit:
+ // Termination requested
+ continue
+ case reqc = <-w.deriveReq:
+ // Account discovery requested
+ }
+ // Derivation needs a chain and device access, skip if either unavailable
+ w.stateLock.RLock()
+ if w.device == nil || w.deriveChain == nil || w.offline() {
+ w.stateLock.RUnlock()
+ reqc <- struct{}{}
+ continue
+ }
+ select {
+ case <-w.commsLock:
+ default:
+ w.stateLock.RUnlock()
+ reqc <- struct{}{}
+ continue
+ }
+ // Device lock obtained, derive the next batch of accounts
+ var (
+ accs []accounts.Account
+ paths []accounts.DerivationPath
+
+ nextAddr = w.deriveNextAddr
+ nextPath = w.deriveNextPath
+
+ context = context.Background()
+ )
+ for empty := false; !empty; {
+ // Retrieve the next derived Ethereum account
+ if nextAddr == (common.Address{}) {
+ if nextAddr, err = w.ledgerDerive(nextPath); err != nil {
+ glog.V(logger.Warn).Infof("%s self-derivation failed: %v", w.url.String(), err)
+ break
+ }
+ }
+ // Check the account's status against the current chain state
+ var (
+ balance *big.Int
+ nonce uint64
+ )
+ balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil)
+ if err != nil {
+ glog.V(logger.Warn).Infof("%s self-derivation balance retrieval failed: %v", w.url.String(), err)
+ break
+ }
+ nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil)
+ if err != nil {
+ glog.V(logger.Warn).Infof("%s self-derivation nonce retrieval failed: %v", w.url.String(), err)
+ break
+ }
+ // If the next account is empty, stop self-derivation, but add it nonetheless
+ if balance.BitLen() == 0 && nonce == 0 {
+ empty = true
+ }
+ // We've just self-derived a new account, start tracking it locally
+ path := make(accounts.DerivationPath, len(nextPath))
+ copy(path[:], nextPath[:])
+ paths = append(paths, path)
+
+ account := accounts.Account{
+ Address: nextAddr,
+ URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
+ }
+ accs = append(accs, account)
+
+ // Display a log message to the user for new (or previously empty accounts)
+ if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) {
+ glog.V(logger.Info).Infof("%s discovered %s (balance %22v, nonce %4d) at %s", w.url.String(), nextAddr.Hex(), balance, nonce, path)
+ }
+ // Fetch the next potential account
+ if !empty {
+ nextAddr = common.Address{}
+ nextPath[len(nextPath)-1]++
+ }
+ }
+ // Self derivation complete, release device lock
+ w.commsLock <- struct{}{}
+ w.stateLock.RUnlock()
+
+ // Insert any accounts successfully derived
+ w.stateLock.Lock()
+ for i := 0; i < len(accs); i++ {
+ if _, ok := w.paths[accs[i].Address]; !ok {
+ w.accounts = append(w.accounts, accs[i])
+ w.paths[accs[i].Address] = paths[i]
+ }
+ }
+ // Shift the self-derivation forward
+ // TODO(karalabe): don't overwrite changes from wallet.SelfDerive
+ w.deriveNextAddr = nextAddr
+ w.deriveNextPath = nextPath
+ w.stateLock.Unlock()
+
+ // Notify the user of termination and loop after a bit of time (to avoid trashing)
+ reqc <- struct{}{}
+ if err == nil {
+ select {
+ case errc = <-w.deriveQuit:
+ // Termination requested, abort
+ case <-time.After(ledgerSelfDeriveThrottling):
+ // Waited enough, willing to self-derive again
+ }
+ }
+ }
+ // In case of error, wait for termination
+ if err != nil {
+ glog.V(logger.Debug).Infof("%s self-derivation failed: %s", w.url.String(), err)
+ errc = <-w.deriveQuit
+ }
+ errc <- err
+}
+
+// Contains implements accounts.Wallet, returning whether a particular account is
+// or is not pinned into this Ledger instance. Although we could attempt to resolve
+// unpinned accounts, that would be an non-negligible hardware operation.
+func (w *ledgerWallet) Contains(account accounts.Account) bool {
+ w.stateLock.RLock()
+ defer w.stateLock.RUnlock()
+
+ _, exists := w.paths[account.Address]
+ return exists
+}
+
+// Derive implements accounts.Wallet, deriving a new account at the specific
+// derivation path. If pin is set to true, the account will be added to the list
+// of tracked accounts.
+func (w *ledgerWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ // Try to derive the actual account and update its URL if successful
+ w.stateLock.RLock() // Avoid device disappearing during derivation
+
+ if w.device == nil || w.offline() {
+ w.stateLock.RUnlock()
+ return accounts.Account{}, accounts.ErrWalletClosed
+ }
+ <-w.commsLock // Avoid concurrent hardware access
+ address, err := w.ledgerDerive(path)
+ w.commsLock <- struct{}{}
+
+ w.stateLock.RUnlock()
+
+ // If an error occurred or no pinning was requested, return
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ account := accounts.Account{
+ Address: address,
+ URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
+ }
+ if !pin {
+ return account, nil
+ }
+ // Pinning needs to modify the state
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ if _, ok := w.paths[address]; !ok {
+ w.accounts = append(w.accounts, account)
+ w.paths[address] = path
+ }
+ return account, nil
+}
+
+// SelfDerive implements accounts.Wallet, trying to discover accounts that the
+// user used previously (based on the chain state), but ones that he/she did not
+// explicitly pin to the wallet manually. To avoid chain head monitoring, self
+// derivation only runs during account listing (and even then throttled).
+func (w *ledgerWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ w.deriveNextPath = make(accounts.DerivationPath, len(base))
+ copy(w.deriveNextPath[:], base[:])
+
+ w.deriveNextAddr = common.Address{}
+ w.deriveChain = chain
+}
+
+// SignHash implements accounts.Wallet, however signing arbitrary data is not
+// supported for Ledger wallets, so this method will always return an error.
+func (w *ledgerWallet) SignHash(acc accounts.Account, hash []byte) ([]byte, error) {
+ return nil, accounts.ErrNotSupported
+}
+
+// SignTx implements accounts.Wallet. It sends the transaction over to the Ledger
+// wallet to request a confirmation from the user. It returns either the signed
+// transaction or a failure if the user denied the transaction.
+//
+// Note, if the version of the Ethereum application running on the Ledger wallet is
+// too old to sign EIP-155 transactions, but such is requested nonetheless, an error
+// will be returned opposed to silently signing in Homestead mode.
+func (w *ledgerWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ w.stateLock.RLock() // Comms have own mutex, this is for the state fields
+ defer w.stateLock.RUnlock()
+
+ // If the wallet is closed, or the Ethereum app doesn't run, abort
+ if w.device == nil || w.offline() {
+ return nil, accounts.ErrWalletClosed
+ }
+ // Make sure the requested account is contained within
+ path, ok := w.paths[account.Address]
+ if !ok {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Ensure the wallet is capable of signing the given transaction
+ if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
+ return nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
+ }
+ // All infos gathered and metadata checks out, request signing
+ <-w.commsLock
+ defer func() { w.commsLock <- struct{}{} }()
+
+ return w.ledgerSign(path, account.Address, tx, chainID)
+}
+
+// SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary
+// data is not supported for Ledger wallets, so this method will always return
+// an error.
+func (w *ledgerWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
+ return nil, accounts.ErrNotSupported
+}
+
+// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
+// transaction with the given account using passphrase as extra authentication.
+// Since the Ledger does not support extra passphrases, it is silently ignored.
+func (w *ledgerWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ return w.SignTx(account, tx, chainID)
+}
+
+// ledgerVersion retrieves the current version of the Ethereum wallet app running
+// on the Ledger wallet.
+//
+// The version retrieval protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+----+---
+// E0 | 06 | 00 | 00 | 00 | 04
+//
+// With no input data, and the output data being:
+//
+// Description | Length
+// ---------------------------------------------------+--------
+// Flags 01: arbitrary data signature enabled by user | 1 byte
+// Application major version | 1 byte
+// Application minor version | 1 byte
+// Application patch version | 1 byte
+func (w *ledgerWallet) ledgerVersion() ([3]byte, error) {
+ // Send the request and wait for the response
+ reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil)
+ if err != nil {
+ return [3]byte{}, err
+ }
+ if len(reply) != 4 {
+ return [3]byte{}, errors.New("reply not of correct size")
+ }
+ // Cache the version for future reference
+ var version [3]byte
+ copy(version[:], reply[1:])
+ return version, nil
+}
+
+// ledgerDerive retrieves the currently active Ethereum address from a Ledger
+// wallet at the specified derivation path.
+//
+// The address derivation protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+-----+---
+// E0 | 02 | 00 return address
+// 01 display address and confirm before returning
+// | 00: do not return the chain code
+// | 01: return the chain code
+// | var | 00
+//
+// Where the input data is:
+//
+// Description | Length
+// -------------------------------------------------+--------
+// Number of BIP 32 derivations to perform (max 10) | 1 byte
+// First derivation index (big endian) | 4 bytes
+// ... | 4 bytes
+// Last derivation index (big endian) | 4 bytes
+//
+// And the output data is:
+//
+// Description | Length
+// ------------------------+-------------------
+// Public Key length | 1 byte
+// Uncompressed Public Key | arbitrary
+// Ethereum address length | 1 byte
+// Ethereum address | 40 bytes hex ascii
+// Chain code if requested | 32 bytes
+func (w *ledgerWallet) ledgerDerive(derivationPath []uint32) (common.Address, error) {
+ // Flatten the derivation path into the Ledger request
+ path := make([]byte, 1+4*len(derivationPath))
+ path[0] = byte(len(derivationPath))
+ for i, component := range derivationPath {
+ binary.BigEndian.PutUint32(path[1+4*i:], component)
+ }
+ // Send the request and wait for the response
+ reply, err := w.ledgerExchange(ledgerOpRetrieveAddress, ledgerP1DirectlyFetchAddress, ledgerP2DiscardAddressChainCode, path)
+ if err != nil {
+ return common.Address{}, err
+ }
+ // Discard the public key, we don't need that for now
+ if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
+ return common.Address{}, errors.New("reply lacks public key entry")
+ }
+ reply = reply[1+int(reply[0]):]
+
+ // Extract the Ethereum hex address string
+ if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
+ return common.Address{}, errors.New("reply lacks address entry")
+ }
+ hexstr := reply[1 : 1+int(reply[0])]
+
+ // Decode the hex sting into an Ethereum address and return
+ var address common.Address
+ hex.Decode(address[:], hexstr)
+ return address, nil
+}
+
+// ledgerSign sends the transaction to the Ledger wallet, and waits for the user
+// to confirm or deny the transaction.
+//
+// The transaction signing protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+-----+---
+// E0 | 04 | 00: first transaction data block
+// 80: subsequent transaction data block
+// | 00 | variable | variable
+//
+// Where the input for the first transaction block (first 255 bytes) is:
+//
+// Description | Length
+// -------------------------------------------------+----------
+// Number of BIP 32 derivations to perform (max 10) | 1 byte
+// First derivation index (big endian) | 4 bytes
+// ... | 4 bytes
+// Last derivation index (big endian) | 4 bytes
+// RLP transaction chunk | arbitrary
+//
+// And the input for subsequent transaction blocks (first 255 bytes) are:
+//
+// Description | Length
+// ----------------------+----------
+// RLP transaction chunk | arbitrary
+//
+// And the output data is:
+//
+// Description | Length
+// ------------+---------
+// signature V | 1 byte
+// signature R | 32 bytes
+// signature S | 32 bytes
+func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // We need to modify the timeouts to account for user feedback
+ defer func(old time.Duration) { w.device.ReadTimeout = old }(w.device.ReadTimeout)
+ w.device.ReadTimeout = time.Hour * 24 * 30 // Timeout requires a Ledger power cycle, only if you must
+
+ // Flatten the derivation path into the Ledger request
+ path := make([]byte, 1+4*len(derivationPath))
+ path[0] = byte(len(derivationPath))
+ for i, component := range derivationPath {
+ binary.BigEndian.PutUint32(path[1+4*i:], component)
+ }
+ // Create the transaction RLP based on whether legacy or EIP155 signing was requeste
+ var (
+ txrlp []byte
+ err error
+ )
+ if chainID == nil {
+ if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil {
+ return nil, err
+ }
+ } else {
+ if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil {
+ return nil, err
+ }
+ }
+ payload := append(path, txrlp...)
+
+ // Send the request and wait for the response
+ var (
+ op = ledgerP1InitTransactionData
+ reply []byte
+ )
+ for len(payload) > 0 {
+ // Calculate the size of the next data chunk
+ chunk := 255
+ if chunk > len(payload) {
+ chunk = len(payload)
+ }
+ // Send the chunk over, ensuring it's processed correctly
+ reply, err = w.ledgerExchange(ledgerOpSignTransaction, op, 0, payload[:chunk])
+ if err != nil {
+ return nil, err
+ }
+ // Shift the payload and ensure subsequent chunks are marked as such
+ payload = payload[chunk:]
+ op = ledgerP1ContTransactionData
+ }
+ // Extract the Ethereum signature and do a sanity validation
+ if len(reply) != 65 {
+ return nil, errors.New("reply lacks signature")
+ }
+ signature := append(reply[1:], reply[0])
+
+ // Create the correct signer and signature transform based on the chain ID
+ var signer types.Signer
+ if chainID == nil {
+ signer = new(types.HomesteadSigner)
+ } else {
+ signer = types.NewEIP155Signer(chainID)
+ signature[64] = signature[64] - byte(chainID.Uint64()*2+35)
+ }
+ // Inject the final signature into the transaction and sanity check the sender
+ signed, err := tx.WithSignature(signer, signature)
+ if err != nil {
+ return nil, err
+ }
+ sender, err := types.Sender(signer, signed)
+ if err != nil {
+ return nil, err
+ }
+ if sender != address {
+ return nil, fmt.Errorf("signer mismatch: expected %s, got %s", address.Hex(), sender.Hex())
+ }
+ return signed, nil
+}
+
+// ledgerExchange performs a data exchange with the Ledger wallet, sending it a
+// message and retrieving the response.
+//
+// The common transport header is defined as follows:
+//
+// Description | Length
+// --------------------------------------+----------
+// Communication channel ID (big endian) | 2 bytes
+// Command tag | 1 byte
+// Packet sequence index (big endian) | 2 bytes
+// Payload | arbitrary
+//
+// The Communication channel ID allows commands multiplexing over the same
+// physical link. It is not used for the time being, and should be set to 0101
+// to avoid compatibility issues with implementations ignoring a leading 00 byte.
+//
+// The Command tag describes the message content. Use TAG_APDU (0x05) for standard
+// APDU payloads, or TAG_PING (0x02) for a simple link test.
+//
+// The Packet sequence index describes the current sequence for fragmented payloads.
+// The first fragment index is 0x00.
+//
+// APDU Command payloads are encoded as follows:
+//
+// Description | Length
+// -----------------------------------
+// APDU length (big endian) | 2 bytes
+// APDU CLA | 1 byte
+// APDU INS | 1 byte
+// APDU P1 | 1 byte
+// APDU P2 | 1 byte
+// APDU length | 1 byte
+// Optional APDU data | arbitrary
+func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
+ // Construct the message payload, possibly split into multiple chunks
+ apdu := make([]byte, 2, 7+len(data))
+
+ binary.BigEndian.PutUint16(apdu, uint16(5+len(data)))
+ apdu = append(apdu, []byte{0xe0, byte(opcode), byte(p1), byte(p2), byte(len(data))}...)
+ apdu = append(apdu, data...)
+
+ // Stream all the chunks to the device
+ header := []byte{0x01, 0x01, 0x05, 0x00, 0x00} // Channel ID and command tag appended
+ chunk := make([]byte, 64)
+ space := len(chunk) - len(header)
+
+ for i := 0; len(apdu) > 0; i++ {
+ // Construct the new message to stream
+ chunk = append(chunk[:0], header...)
+ binary.BigEndian.PutUint16(chunk[3:], uint16(i))
+
+ if len(apdu) > space {
+ chunk = append(chunk, apdu[:space]...)
+ apdu = apdu[space:]
+ } else {
+ chunk = append(chunk, apdu...)
+ apdu = nil
+ }
+ // Send over to the device
+ if glog.V(logger.Detail) {
+ glog.Infof("-> %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
+ }
+ if _, err := w.input.Write(chunk); err != nil {
+ return nil, err
+ }
+ }
+ // Stream the reply back from the wallet in 64 byte chunks
+ var reply []byte
+ chunk = chunk[:64] // Yeah, we surely have enough space
+ for {
+ // Read the next chunk from the Ledger wallet
+ if _, err := io.ReadFull(w.output, chunk); err != nil {
+ return nil, err
+ }
+ if glog.V(logger.Detail) {
+ glog.Infof("<- %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
+ }
+ // Make sure the transport header matches
+ if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 {
+ return nil, errReplyInvalidHeader
+ }
+ // If it's the first chunk, retrieve the total message length
+ var payload []byte
+
+ if chunk[3] == 0x00 && chunk[4] == 0x00 {
+ reply = make([]byte, 0, int(binary.BigEndian.Uint16(chunk[5:7])))
+ payload = chunk[7:]
+ } else {
+ payload = chunk[5:]
+ }
+ // Append to the reply and stop when filled up
+ if left := cap(reply) - len(reply); left > len(payload) {
+ reply = append(reply, payload...)
+ } else {
+ reply = append(reply, payload[:left]...)
+ break
+ }
+ }
+ return reply[:len(reply)-2], nil
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet.go b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet.go
new file mode 100644
index 000000000..3989f3d02
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet.go
@@ -0,0 +1,29 @@
+// 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 .
+
+// +build !ios
+
+// Package usbwallet implements support for USB hardware wallets.
+package usbwallet
+
+import "github.com/karalabe/gousb/usb"
+
+// deviceID is a combined vendor/product identifier to uniquely identify a USB
+// hardware device.
+type deviceID struct {
+ Vendor usb.ID // The Vendor identifer
+ Product usb.ID // The Product identifier
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet_ios.go b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet_ios.go
new file mode 100644
index 000000000..17d342903
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet_ios.go
@@ -0,0 +1,38 @@
+// 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 .
+
+// This file contains the implementation for interacting with the Ledger hardware
+// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
+// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
+
+// +build ios
+
+package usbwallet
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/accounts"
+)
+
+// Here be dragons! There is no USB support on iOS.
+
+// ErrIOSNotSupported is returned for all USB hardware backends on iOS.
+var ErrIOSNotSupported = errors.New("no USB support on iOS")
+
+func NewLedgerHub() (accounts.Backend, error) {
+ return nil, ErrIOSNotSupported
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go
index dfbd025da..3a1ae6f4c 100644
--- a/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go
@@ -94,7 +94,9 @@ func main() {
abi, _ := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
abis = append(abis, string(abi))
bins = append(bins, contract.Code)
- types = append(types, name)
+
+ nameParts := strings.Split(name, ":")
+ types = append(types, nameParts[len(nameParts)-1])
}
} else {
// Otherwise load up the ABI, optional bytecode and type name from the parameters
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go
index ecb5b7239..1b470e161 100644
--- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go
@@ -21,6 +21,7 @@ import (
"io/ioutil"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/crypto"
@@ -180,31 +181,36 @@ nodes.
func accountList(ctx *cli.Context) error {
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
- for i, acct := range stack.AccountManager().Accounts() {
- fmt.Printf("Account #%d: {%x} %s\n", i, acct.Address, acct.File)
+
+ var index int
+ for _, wallet := range stack.AccountManager().Wallets() {
+ for _, account := range wallet.Accounts() {
+ fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL)
+ index++
+ }
}
return nil
}
// tries unlocking the specified account a few times.
-func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
- account, err := utils.MakeAddress(accman, address)
+func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
+ account, err := utils.MakeAddress(ks, address)
if err != nil {
utils.Fatalf("Could not list accounts: %v", err)
}
for trials := 0; trials < 3; trials++ {
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
password := getPassPhrase(prompt, false, i, passwords)
- err = accman.Unlock(account, password)
+ err = ks.Unlock(account, password)
if err == nil {
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
return account, password
}
- if err, ok := err.(*accounts.AmbiguousAddrError); ok {
+ if err, ok := err.(*keystore.AmbiguousAddrError); ok {
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
- return ambiguousAddrRecovery(accman, err, password), password
+ return ambiguousAddrRecovery(ks, err, password), password
}
- if err != accounts.ErrDecrypt {
+ if err != keystore.ErrDecrypt {
// No need to prompt again if the error is not decryption-related.
break
}
@@ -261,15 +267,15 @@ func getWhisperYesNo(prompt string) bool {
return shhRes
}
-func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrError, auth string) accounts.Account {
+func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
for _, a := range err.Matches {
- fmt.Println(" ", a.File)
+ fmt.Println(" ", a.URL)
}
fmt.Println("Testing your passphrase against all of them...")
var match *accounts.Account
for _, a := range err.Matches {
- if err := am.Unlock(a, auth); err == nil {
+ if err := ks.Unlock(a, auth); err == nil {
match = &a
break
}
@@ -277,11 +283,11 @@ func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrErro
if match == nil {
utils.Fatalf("None of the listed files could be unlocked.")
}
- fmt.Printf("Your passphrase unlocked %s\n", match.File)
+ fmt.Printf("Your passphrase unlocked %s\n", match.URL)
fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
for _, a := range err.Matches {
if a != *match {
- fmt.Println(" ", a.File)
+ fmt.Println(" ", a.URL)
}
}
return *match
@@ -291,13 +297,13 @@ func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrErro
func accountCreate(ctx *cli.Context) error {
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
- whisper := getWhisperYesNo("You can also choose to enable your new account as a Whisper identity.")
+ whisperEnabled := getWhisperYesNo("You can also choose to enable your new account as a Whisper identity.")
- account, err := stack.AccountManager().NewAccount(password, whisper)
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ account, err := ks.NewAccount(password, whisperEnabled)
if err != nil {
utils.Fatalf("Failed to create account: %v", err)
}
-
fmt.Printf("Address: {%x}\n", account.Address)
return nil
}
@@ -309,9 +315,11 @@ func accountUpdate(ctx *cli.Context) error {
utils.Fatalf("No accounts specified to update")
}
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
- account, oldPassword := unlockAccount(ctx, stack.AccountManager(), ctx.Args().First(), 0, nil)
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
+ account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil)
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
- if err := stack.AccountManager().Update(account, oldPassword, newPassword); err != nil {
+ if err := ks.Update(account, oldPassword, newPassword); err != nil {
utils.Fatalf("Could not update the account: %v", err)
}
return nil
@@ -329,7 +337,9 @@ func importWallet(ctx *cli.Context) error {
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
- acct, err := stack.AccountManager().ImportPreSaleKey(keyJson, passphrase)
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ acct, err := ks.ImportPreSaleKey(keyJson, passphrase)
if err != nil {
utils.Fatalf("%v", err)
}
@@ -348,7 +358,9 @@ func accountImport(ctx *cli.Context) error {
}
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
- acct, err := stack.AccountManager().ImportECDSA(key, passphrase)
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ acct, err := ks.ImportECDSA(key, passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go
index ff9d34b3c..1680a32e0 100644
--- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go
@@ -25,11 +25,14 @@ import (
"strings"
"time"
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/contracts/release"
"github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -245,14 +248,50 @@ func startNode(ctx *cli.Context, stack *node.Node) {
utils.StartNode(stack)
// Unlock any account specifically requested
- accman := stack.AccountManager()
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
passwords := utils.MakePasswordList(ctx)
- accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
- for i, account := range accounts {
+ unlocks := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
+ for i, account := range unlocks {
if trimmed := strings.TrimSpace(account); trimmed != "" {
- unlockAccount(ctx, accman, trimmed, i, passwords)
+ unlockAccount(ctx, ks, trimmed, i, passwords)
}
}
+ // Register wallet event handlers to open and auto-derive wallets
+ events := make(chan accounts.WalletEvent, 16)
+ stack.AccountManager().Subscribe(events)
+
+ go func() {
+ // Create an chain state reader for self-derivation
+ rpcClient, err := stack.Attach()
+ if err != nil {
+ utils.Fatalf("Failed to attach to self: %v", err)
+ }
+ stateReader := ethclient.NewClient(rpcClient)
+
+ // Open and self derive any wallets already attached
+ for _, wallet := range stack.AccountManager().Wallets() {
+ if err := wallet.Open(""); err != nil {
+ glog.V(logger.Warn).Infof("Failed to open wallet %s: %v", wallet.URL(), err)
+ } else {
+ wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
+ }
+ }
+ // Listen for wallet event till termination
+ for event := range events {
+ if event.Arrive {
+ if err := event.Wallet.Open(""); err != nil {
+ glog.V(logger.Info).Infof("New wallet appeared: %s, failed to open: %s", event.Wallet.URL(), err)
+ } else {
+ glog.V(logger.Info).Infof("New wallet appeared: %s, %s", event.Wallet.URL(), event.Wallet.Status())
+ event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
+ }
+ } else {
+ glog.V(logger.Info).Infof("Old wallet dropped: %s", event.Wallet.URL())
+ event.Wallet.Close()
+ }
+ }
+ }()
// Start auxiliary services if enabled
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
var ethereum *eth.Ethereum
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go
index 850bf8eb2..9e80ad05d 100644
--- a/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go
@@ -23,6 +23,7 @@ import (
"os"
"os/signal"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
@@ -99,17 +100,18 @@ func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) {
return nil, err
}
// Create the keystore and inject an unlocked account if requested
- accman := stack.AccountManager()
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
if len(privkey) > 0 {
key, err := crypto.HexToECDSA(privkey)
if err != nil {
return nil, err
}
- a, err := accman.ImportECDSA(key, "")
+ a, err := ks.ImportECDSA(key, "")
if err != nil {
return nil, err
}
- if err := accman.Unlock(a, ""); err != nil {
+ if err := ks.Unlock(a, ""); err != nil {
return nil, err
}
}
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/cleandb.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/cleandb.go
new file mode 100644
index 000000000..81636ada5
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/cleandb.go
@@ -0,0 +1,39 @@
+// Copyright 2016 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 .
+
+package main
+
+import (
+ "log"
+
+ "github.com/ethereum/go-ethereum/swarm/storage"
+ "gopkg.in/urfave/cli.v1"
+)
+
+func cleandb(ctx *cli.Context) {
+ args := ctx.Args()
+ if len(args) != 1 {
+ log.Fatal("need path to chunks database as the first and only argument")
+ }
+
+ chunkDbPath := args[0]
+ hash := storage.MakeHashFunc("SHA3")
+ dbStore, err := storage.NewDbStore(chunkDbPath, hash, 10000000, 0)
+ if err != nil {
+ log.Fatalf("cannot initialise dbstore: %v", err)
+ }
+ dbStore.Cleanup()
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/main.go
index 34b3f71c4..5661b3f6e 100644
--- a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/main.go
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/main.go
@@ -21,11 +21,14 @@ import (
"fmt"
"io/ioutil"
"os"
+ "os/signal"
"runtime"
"strconv"
"strings"
+ "syscall"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
@@ -152,6 +155,52 @@ The output of this command is supposed to be machine-readable.
ArgsUsage: " ",
Description: `
Prints the swarm hash of file or directory.
+`,
+ },
+ {
+ Name: "manifest",
+ Usage: "update a MANIFEST",
+ ArgsUsage: "manifest COMMAND",
+ Description: `
+Updates a MANIFEST by adding/removing/updating the hash of a path.
+`,
+ Subcommands: []cli.Command{
+ {
+ Action: add,
+ Name: "add",
+ Usage: "add a new path to the manifest",
+ ArgsUsage: " []",
+ Description: `
+Adds a new path to the manifest
+`,
+ },
+ {
+ Action: update,
+ Name: "update",
+ Usage: "update the hash for an already existing path in the manifest",
+ ArgsUsage: " []",
+ Description: `
+Update the hash for an already existing path in the manifest
+`,
+ },
+ {
+ Action: remove,
+ Name: "remove",
+ Usage: "removes a path from the manifest",
+ ArgsUsage: " ",
+ Description: `
+Removes a path from the manifest
+`,
+ },
+ },
+ },
+ {
+ Action: cleandb,
+ Name: "cleandb",
+ Usage: "Cleans database of corrupted entries",
+ ArgsUsage: " ",
+ Description: `
+Cleans database of corrupted entries.
`,
},
}
@@ -224,6 +273,14 @@ func bzzd(ctx *cli.Context) error {
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
registerBzzService(ctx, stack)
utils.StartNode(stack)
+ go func() {
+ sigc := make(chan os.Signal, 1)
+ signal.Notify(sigc, syscall.SIGTERM)
+ defer signal.Stop(sigc)
+ <-sigc
+ glog.V(logger.Info).Infoln("Got sigterm, shutting down...")
+ stack.Stop()
+ }()
networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
// Add bootnodes as initial peers.
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
@@ -290,29 +347,36 @@ func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
return key
}
// Otherwise try getting it from the keystore.
- return decryptStoreAccount(stack.AccountManager(), keyid)
+ am := stack.AccountManager()
+ ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
+ return decryptStoreAccount(ks, keyid)
}
-func decryptStoreAccount(accman *accounts.Manager, account string) *ecdsa.PrivateKey {
+func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKey {
var a accounts.Account
var err error
if common.IsHexAddress(account) {
- a, err = accman.Find(accounts.Account{Address: common.HexToAddress(account)})
- } else if ix, ixerr := strconv.Atoi(account); ixerr == nil {
- a, err = accman.AccountByIndex(ix)
+ a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
+ } else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
+ if accounts := ks.Accounts(); len(accounts) > ix {
+ a = accounts[ix]
+ } else {
+ err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
+ }
} else {
utils.Fatalf("Can't find swarm account key %s", account)
}
if err != nil {
utils.Fatalf("Can't find swarm account key: %v", err)
}
- keyjson, err := ioutil.ReadFile(a.File)
+ keyjson, err := ioutil.ReadFile(a.URL.Path)
if err != nil {
utils.Fatalf("Can't load swarm account key: %v", err)
}
for i := 1; i <= 3; i++ {
passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i))
- key, err := accounts.DecryptKey(keyjson, passphrase)
+ key, err := keystore.DecryptKey(keyjson, passphrase)
if err == nil {
return key.PrivateKey
}
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/manifest.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/manifest.go
new file mode 100644
index 000000000..0de0d69bb
--- /dev/null
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/manifest.go
@@ -0,0 +1,360 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+// Command MANIFEST update
+package main
+
+import (
+ "gopkg.in/urfave/cli.v1"
+ "log"
+ "mime"
+ "path/filepath"
+ "strings"
+ "fmt"
+ "encoding/json"
+)
+
+func add(ctx *cli.Context) {
+
+ args := ctx.Args()
+ if len(args) < 3 {
+ log.Fatal("need atleast three arguments []")
+ }
+
+ var (
+ mhash = args[0]
+ path = args[1]
+ hash = args[2]
+
+ ctype string
+ wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
+ mroot manifest
+ )
+
+
+ if len(args) > 3 {
+ ctype = args[3]
+ } else {
+ ctype = mime.TypeByExtension(filepath.Ext(path))
+ }
+
+ newManifest := addEntryToManifest (ctx, mhash, path, hash, ctype)
+ fmt.Println(newManifest)
+
+ if !wantManifest {
+ // Print the manifest. This is the only output to stdout.
+ mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
+ fmt.Println(string(mrootJSON))
+ return
+ }
+}
+
+func update(ctx *cli.Context) {
+
+ args := ctx.Args()
+ if len(args) < 3 {
+ log.Fatal("need atleast three arguments ")
+ }
+
+ var (
+ mhash = args[0]
+ path = args[1]
+ hash = args[2]
+
+ ctype string
+ wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
+ mroot manifest
+ )
+ if len(args) > 3 {
+ ctype = args[3]
+ } else {
+ ctype = mime.TypeByExtension(filepath.Ext(path))
+ }
+
+ newManifest := updateEntryInManifest (ctx, mhash, path, hash, ctype)
+ fmt.Println(newManifest)
+
+ if !wantManifest {
+ // Print the manifest. This is the only output to stdout.
+ mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
+ fmt.Println(string(mrootJSON))
+ return
+ }
+}
+
+func remove(ctx *cli.Context) {
+ args := ctx.Args()
+ if len(args) < 2 {
+ log.Fatal("need atleast two arguments ")
+ }
+
+ var (
+ mhash = args[0]
+ path = args[1]
+
+ wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
+ mroot manifest
+ )
+
+ newManifest := removeEntryFromManifest (ctx, mhash, path)
+ fmt.Println(newManifest)
+
+ if !wantManifest {
+ // Print the manifest. This is the only output to stdout.
+ mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
+ fmt.Println(string(mrootJSON))
+ return
+ }
+}
+
+func addEntryToManifest(ctx *cli.Context, mhash , path, hash , ctype string) string {
+
+ var (
+ bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
+ client = &client{api: bzzapi}
+ longestPathEntry = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ )
+
+ mroot, err := client.downloadManifest(mhash)
+ if err != nil {
+ log.Fatalln("manifest download failed:", err)
+ }
+
+ //TODO: check if the "hash" to add is valid and present in swarm
+ _, err = client.downloadManifest(hash)
+ if err != nil {
+ log.Fatalln("hash to add is not present:", err)
+ }
+
+
+ // See if we path is in this Manifest or do we have to dig deeper
+ for _, entry := range mroot.Entries {
+ if path == entry.Path {
+ log.Fatal(path, "Already present, not adding anything")
+ }else {
+ if entry.ContentType == "application/bzz-manifest+json" {
+ prfxlen := strings.HasPrefix(path, entry.Path)
+ if prfxlen && len(path) > len(longestPathEntry.Path) {
+ longestPathEntry = entry
+ }
+ }
+ }
+ }
+
+ if longestPathEntry.Path != "" {
+ // Load the child Manifest add the entry there
+ newPath := path[len(longestPathEntry.Path):]
+ newHash := addEntryToManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype)
+
+ // Replace the hash for parent Manifests
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if longestPathEntry.Path == entry.Path {
+ entry.Hash = newHash
+ }
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+ }
+ mroot = newMRoot
+ } else {
+ // Add the entry in the leaf Manifest
+ newEntry := manifestEntry{
+ Path: path,
+ Hash: hash,
+ ContentType: ctype,
+ }
+ mroot.Entries = append(mroot.Entries, newEntry)
+ }
+
+
+ newManifestHash, err := client.uploadManifest(mroot)
+ if err != nil {
+ log.Fatalln("manifest upload failed:", err)
+ }
+ return newManifestHash
+
+
+
+}
+
+func updateEntryInManifest(ctx *cli.Context, mhash , path, hash , ctype string) string {
+
+ var (
+ bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
+ client = &client{api: bzzapi}
+ newEntry = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ longestPathEntry = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ )
+
+ mroot, err := client.downloadManifest(mhash)
+ if err != nil {
+ log.Fatalln("manifest download failed:", err)
+ }
+
+ //TODO: check if the "hash" with which to update is valid and present in swarm
+
+
+ // See if we path is in this Manifest or do we have to dig deeper
+ for _, entry := range mroot.Entries {
+ if path == entry.Path {
+ newEntry = entry
+ }else {
+ if entry.ContentType == "application/bzz-manifest+json" {
+ prfxlen := strings.HasPrefix(path, entry.Path)
+ if prfxlen && len(path) > len(longestPathEntry.Path) {
+ longestPathEntry = entry
+ }
+ }
+ }
+ }
+
+ if longestPathEntry.Path == "" && newEntry.Path == "" {
+ log.Fatal(path, " Path not present in the Manifest, not setting anything")
+ }
+
+ if longestPathEntry.Path != "" {
+ // Load the child Manifest add the entry there
+ newPath := path[len(longestPathEntry.Path):]
+ newHash := updateEntryInManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype)
+
+ // Replace the hash for parent Manifests
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if longestPathEntry.Path == entry.Path {
+ entry.Hash = newHash
+ }
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+
+ }
+ mroot = newMRoot
+ }
+
+ if newEntry.Path != "" {
+ // Replace the hash for leaf Manifest
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if newEntry.Path == entry.Path {
+ myEntry := manifestEntry{
+ Path: entry.Path,
+ Hash: hash,
+ ContentType: ctype,
+ }
+ newMRoot.Entries = append(newMRoot.Entries, myEntry)
+ } else {
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+ }
+ }
+ mroot = newMRoot
+ }
+
+
+ newManifestHash, err := client.uploadManifest(mroot)
+ if err != nil {
+ log.Fatalln("manifest upload failed:", err)
+ }
+ return newManifestHash
+}
+
+func removeEntryFromManifest(ctx *cli.Context, mhash , path string) string {
+
+ var (
+ bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
+ client = &client{api: bzzapi}
+ entryToRemove = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ longestPathEntry = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ )
+
+ mroot, err := client.downloadManifest(mhash)
+ if err != nil {
+ log.Fatalln("manifest download failed:", err)
+ }
+
+
+
+ // See if we path is in this Manifest or do we have to dig deeper
+ for _, entry := range mroot.Entries {
+ if path == entry.Path {
+ entryToRemove = entry
+ }else {
+ if entry.ContentType == "application/bzz-manifest+json" {
+ prfxlen := strings.HasPrefix(path, entry.Path)
+ if prfxlen && len(path) > len(longestPathEntry.Path) {
+ longestPathEntry = entry
+ }
+ }
+ }
+ }
+
+ if longestPathEntry.Path == "" && entryToRemove.Path == "" {
+ log.Fatal(path, "Path not present in the Manifest, not removing anything")
+ }
+
+ if longestPathEntry.Path != "" {
+ // Load the child Manifest remove the entry there
+ newPath := path[len(longestPathEntry.Path):]
+ newHash := removeEntryFromManifest (ctx, longestPathEntry.Hash, newPath)
+
+ // Replace the hash for parent Manifests
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if longestPathEntry.Path == entry.Path {
+ entry.Hash = newHash
+ }
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+ }
+ mroot = newMRoot
+ }
+
+ if entryToRemove.Path != "" {
+ // remove the entry in this Manifest
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if entryToRemove.Path != entry.Path {
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+ }
+ }
+ mroot = newMRoot
+ }
+
+
+ newManifestHash, err := client.uploadManifest(mroot)
+ if err != nil {
+ log.Fatalln("manifest upload failed:", err)
+ }
+ return newManifestHash
+
+
+}
+
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/upload.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/upload.go
index d8039d45b..871713b2d 100644
--- a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/upload.go
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/upload.go
@@ -229,3 +229,29 @@ func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (strin
content, err := ioutil.ReadAll(resp.Body)
return string(content), err
}
+
+func (c *client) downloadManifest(mhash string) (manifest, error) {
+
+ mroot := manifest{}
+ req, err := http.NewRequest("GET", c.api + "/bzzr:/" + mhash, nil)
+ if err != nil {
+ return mroot, err
+ }
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return mroot, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode >= 400 {
+ return mroot, fmt.Errorf("bad status: %s", resp.Status)
+
+ }
+ content, err := ioutil.ReadAll(resp.Body)
+
+ err = json.Unmarshal(content, &mroot)
+ if err != nil {
+ return mroot, fmt.Errorf("Manifest %v is malformed: %v", mhash, err)
+ }
+ return mroot, err
+}
\ No newline at end of file
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go
index 4b76b8334..d6d7c3f82 100644
--- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go
@@ -30,6 +30,7 @@ import (
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
@@ -587,23 +588,27 @@ func MakeDatabaseHandles() int {
// MakeAddress converts an account specified directly as a hex encoded string or
// a key index in the key store to an internal account representation.
-func MakeAddress(accman *accounts.Manager, account string) (accounts.Account, error) {
+func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) {
// If the specified account is a valid address, return it
if common.IsHexAddress(account) {
return accounts.Account{Address: common.HexToAddress(account)}, nil
}
// Otherwise try to interpret the account as a keystore index
index, err := strconv.Atoi(account)
- if err != nil {
+ if err != nil || index < 0 {
return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account)
}
- return accman.AccountByIndex(index)
+ accs := ks.Accounts()
+ if len(accs) <= index {
+ return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs))
+ }
+ return accs[index], nil
}
// MakeEtherbase retrieves the etherbase either from the directly specified
// command line flags or from the keystore if CLI indexed.
-func MakeEtherbase(accman *accounts.Manager, ctx *cli.Context) common.Address {
- accounts := accman.Accounts()
+func MakeEtherbase(ks *keystore.KeyStore, ctx *cli.Context) common.Address {
+ accounts := ks.Accounts()
if !ctx.GlobalIsSet(EtherbaseFlag.Name) && len(accounts) == 0 {
glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default")
return common.Address{}
@@ -613,7 +618,7 @@ func MakeEtherbase(accman *accounts.Manager, ctx *cli.Context) common.Address {
return common.Address{}
}
// If the specified etherbase is a valid address, return it
- account, err := MakeAddress(accman, etherbase)
+ account, err := MakeAddress(ks, etherbase)
if err != nil {
Fatalf("Option %q: %v", EtherbaseFlag.Name, err)
}
@@ -717,9 +722,10 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
if networks > 1 {
Fatalf("The %v flags are mutually exclusive", netFlags)
}
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
ethConf := ð.Config{
- Etherbase: MakeEtherbase(stack.AccountManager(), ctx),
+ Etherbase: MakeEtherbase(ks, ctx),
ChainConfig: MakeChainConfig(ctx, stack),
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
LightMode: ctx.GlobalBool(LightModeFlag.Name),
diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/wnode/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/wnode/main.go
index cbf093aa7..d002497fb 100644
--- a/vendor/github.com/ethereum/go-ethereum/cmd/wnode/main.go
+++ b/vendor/github.com/ethereum/go-ethereum/cmd/wnode/main.go
@@ -22,8 +22,6 @@ package main
import (
"bufio"
"crypto/ecdsa"
- "crypto/sha1"
- "crypto/sha256"
"crypto/sha512"
"encoding/binary"
"encoding/hex"
@@ -49,6 +47,7 @@ import (
)
const quitCommand = "~Q"
+const symKeyName = "da919ea33001b04dfc630522e33078ec0df11"
// singletons
var (
@@ -67,7 +66,8 @@ var (
asymKey *ecdsa.PrivateKey
nodeid *ecdsa.PrivateKey
topic whisper.TopicType
- filterID uint32
+ filterID string
+ symPass string
msPassword string
)
@@ -82,13 +82,13 @@ var (
testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics")
generateKey = flag.Bool("k", false, "generate and show the private key")
+ argVerbosity = flag.Int("verbosity", logger.Warn, "log verbosity level")
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
argWorkTime = flag.Uint("work", 5, "work time in seconds")
argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request")
argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
- argSalt = flag.String("salt", "", "salt (for topic and key derivation)")
argPub = flag.String("pub", "", "public key for asymmetric encryption")
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
@@ -146,7 +146,6 @@ func echo() {
fmt.Printf("pow = %f \n", *argPoW)
fmt.Printf("mspow = %f \n", *argServerPoW)
fmt.Printf("ip = %s \n", *argIP)
- fmt.Printf("salt = %s \n", *argSalt)
fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub)))
fmt.Printf("idfile = %s \n", *argIDFile)
fmt.Printf("dbpath = %s \n", *argDBPath)
@@ -154,7 +153,7 @@ func echo() {
}
func initialize() {
- glog.SetV(logger.Warn)
+ glog.SetV(*argVerbosity)
glog.SetToStderr(true)
done = make(chan struct{})
@@ -172,10 +171,7 @@ func initialize() {
}
if *testMode {
- password := []byte("test password for symmetric encryption")
- salt := []byte("test salt for symmetric encryption")
- symKey = pbkdf2.Key(password, salt, 64, 32, sha256.New)
- topic = whisper.TopicType{0xFF, 0xFF, 0xFF, 0xFF}
+ symPass = "wwww" // ascii code: 0x77777777
msPassword = "mail server test password"
}
@@ -198,10 +194,11 @@ func initialize() {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
- shh = whisper.NewWhisper(&mailServer)
+ shh = whisper.New()
+ shh.RegisterServer(&mailServer)
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
} else {
- shh = whisper.NewWhisper(nil)
+ shh = whisper.New()
}
asymKey = shh.NewIdentity()
@@ -209,10 +206,15 @@ func initialize() {
nodeid = shh.NewIdentity()
}
+ maxPeers := 80
+ if *bootstrapMode {
+ maxPeers = 800
+ }
+
server = &p2p.Server{
Config: p2p.Config{
PrivateKey: nodeid,
- MaxPeers: 128,
+ MaxPeers: maxPeers,
Name: common.MakeName("whisper-go", "5.0"),
Protocols: shh.Protocols(),
ListenAddr: *argIP,
@@ -280,20 +282,18 @@ func configureNode() {
}
}
- if !*asymmetricMode && !*forwarderMode && !*testMode {
- pass, err := console.Stdin.PromptPassword("Please enter the password: ")
- if err != nil {
- utils.Fatalf("Failed to read passphrase: %v", err)
+ if !*asymmetricMode && !*forwarderMode {
+ if len(symPass) == 0 {
+ symPass, err = console.Stdin.PromptPassword("Please enter the password: ")
+ if err != nil {
+ utils.Fatalf("Failed to read passphrase: %v", err)
+ }
}
- if len(*argSalt) == 0 {
- argSalt = scanLineA("Please enter the salt: ")
- }
-
- symKey = pbkdf2.Key([]byte(pass), []byte(*argSalt), 65356, 32, sha256.New)
-
+ shh.AddSymKey(symKeyName, []byte(symPass))
+ symKey = shh.GetSymKey(symKeyName)
if len(*argTopic) == 0 {
- generateTopic([]byte(pass), []byte(*argSalt))
+ generateTopic([]byte(symPass))
}
}
@@ -309,19 +309,17 @@ func configureNode() {
Topics: []whisper.TopicType{topic},
AcceptP2P: p2pAccept,
}
- filterID = shh.Watch(&filter)
+ filterID, err = shh.Watch(&filter)
+ if err != nil {
+ utils.Fatalf("Failed to install filter: %s", err)
+ }
fmt.Printf("Filter is configured for the topic: %x \n", topic)
}
-func generateTopic(password, salt []byte) {
- const rounds = 4000
- const size = 128
- x1 := pbkdf2.Key(password, salt, rounds, size, sha512.New)
- x2 := pbkdf2.Key(password, salt, rounds, size, sha1.New)
- x3 := pbkdf2.Key(x1, x2, rounds, size, sha256.New)
-
- for i := 0; i < size; i++ {
- topic[i%whisper.TopicLength] ^= x3[i]
+func generateTopic(password []byte) {
+ x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
+ for i := 0; i < len(x); i++ {
+ topic[i%whisper.TopicLength] ^= x[i]
}
}
@@ -373,9 +371,9 @@ func sendLoop() {
if *asymmetricMode {
// print your own message for convenience,
// because in asymmetric mode it is impossible to decrypt it
- hour, min, sec := time.Now().Clock()
+ timestamp := time.Now().Unix()
from := crypto.PubkeyToAddress(asymKey.PublicKey)
- fmt.Printf("\n%02d:%02d:%02d <%x>: %s\n", hour, min, sec, from, s)
+ fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s)
}
}
}
diff --git a/vendor/github.com/ethereum/go-ethereum/core/genesis.go b/vendor/github.com/ethereum/go-ethereum/core/genesis.go
index a06c40408..b94b5af76 100644
--- a/vendor/github.com/ethereum/go-ethereum/core/genesis.go
+++ b/vendor/github.com/ethereum/go-ethereum/core/genesis.go
@@ -70,7 +70,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
for addr, account := range genesis.Alloc {
address := common.HexToAddress(addr)
statedb.AddBalance(address, common.String2Big(account.Balance))
- statedb.SetCode(address, common.Hex2Bytes(account.Code))
+ statedb.SetCode(address, common.FromHex(account.Code))
statedb.SetNonce(address, common.String2Big(account.Nonce).Uint64())
for key, value := range account.Storage {
statedb.SetState(address, common.HexToHash(key), common.HexToHash(value))
diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go
index ad73dc0dc..0d8f9dd28 100644
--- a/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go
+++ b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go
@@ -82,10 +82,12 @@ func (ms *ManagedState) NewNonce(addr common.Address) uint64 {
return uint64(len(account.nonces)-1) + account.nstart
}
-// GetNonce returns the canonical nonce for the managed or unmanaged account
+// GetNonce returns the canonical nonce for the managed or unmanaged account.
+//
+// Because GetNonce mutates the DB, we must take a write lock.
func (ms *ManagedState) GetNonce(addr common.Address) uint64 {
- ms.mu.RLock()
- defer ms.mu.RUnlock()
+ ms.mu.Lock()
+ defer ms.mu.Unlock()
if ms.hasAccount(addr) {
account := ms.getAccount(addr)
diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go b/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go
index fdbc0df7f..cb225b6ca 100644
--- a/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go
+++ b/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go
@@ -157,7 +157,7 @@ var _baseCheck = map[OpCode]req{
CALL: {7, Zero, 1},
CALLCODE: {7, Zero, 1},
DELEGATECALL: {6, Zero, 1},
- SUICIDE: {1, Zero, 0},
+ SELFDESTRUCT: {1, Zero, 0},
JUMPDEST: {0, params.JumpdestGas, 0},
RETURN: {2, Zero, 0},
PUSH1: {0, GasFastestStep, 1},
diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go
index f4ce81883..073798274 100644
--- a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go
+++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go
@@ -52,142 +52,149 @@ var defaultJumpTable = NewJumpTable()
func NewJumpTable() [256]operation {
return [256]operation{
+ STOP: {
+ execute: opStop,
+ gasCost: constGasFunc(new(big.Int)),
+ validateStack: makeStackFunc(0, 0),
+ halts: true,
+ valid: true,
+ },
ADD: {
execute: opAdd,
gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- SUB: {
- execute: opSub,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
+ validateStack: makeStackFunc(2, -1),
valid: true,
},
MUL: {
execute: opMul,
gasCost: constGasFunc(GasFastStep),
- validateStack: makeStackFunc(2, 1),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ SUB: {
+ execute: opSub,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
valid: true,
},
DIV: {
execute: opDiv,
gasCost: constGasFunc(GasFastStep),
- validateStack: makeStackFunc(2, 1),
+ validateStack: makeStackFunc(2, -1),
valid: true,
},
SDIV: {
execute: opSdiv,
gasCost: constGasFunc(GasFastStep),
- validateStack: makeStackFunc(2, 1),
+ validateStack: makeStackFunc(2, -1),
valid: true,
},
MOD: {
execute: opMod,
gasCost: constGasFunc(GasFastStep),
- validateStack: makeStackFunc(2, 1),
+ validateStack: makeStackFunc(2, -1),
valid: true,
},
SMOD: {
execute: opSmod,
gasCost: constGasFunc(GasFastStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- EXP: {
- execute: opExp,
- gasCost: gasExp,
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- SIGNEXTEND: {
- execute: opSignExtend,
- gasCost: constGasFunc(GasFastStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- NOT: {
- execute: opNot,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(1, 1),
- valid: true,
- },
- LT: {
- execute: opLt,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- GT: {
- execute: opGt,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- SLT: {
- execute: opSlt,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- SGT: {
- execute: opSgt,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- EQ: {
- execute: opEq,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- ISZERO: {
- execute: opIszero,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(1, 1),
- valid: true,
- },
- AND: {
- execute: opAnd,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- OR: {
- execute: opOr,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- XOR: {
- execute: opXor,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
- valid: true,
- },
- BYTE: {
- execute: opByte,
- gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, 1),
+ validateStack: makeStackFunc(2, -1),
valid: true,
},
ADDMOD: {
execute: opAddmod,
gasCost: constGasFunc(GasMidStep),
- validateStack: makeStackFunc(3, 1),
+ validateStack: makeStackFunc(3, -2),
valid: true,
},
MULMOD: {
execute: opMulmod,
gasCost: constGasFunc(GasMidStep),
- validateStack: makeStackFunc(3, 1),
+ validateStack: makeStackFunc(3, -2),
+ valid: true,
+ },
+ EXP: {
+ execute: opExp,
+ gasCost: gasExp,
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ SIGNEXTEND: {
+ execute: opSignExtend,
+ gasCost: constGasFunc(GasFastStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ LT: {
+ execute: opLt,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ GT: {
+ execute: opGt,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ SLT: {
+ execute: opSlt,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ SGT: {
+ execute: opSgt,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ EQ: {
+ execute: opEq,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ ISZERO: {
+ execute: opIszero,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(1, 0),
+ valid: true,
+ },
+ AND: {
+ execute: opAnd,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ XOR: {
+ execute: opXor,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ OR: {
+ execute: opOr,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
+ valid: true,
+ },
+ NOT: {
+ execute: opNot,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(1, 0),
+ valid: true,
+ },
+ BYTE: {
+ execute: opByte,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, -1),
valid: true,
},
SHA3: {
execute: opSha3,
gasCost: gasSha3,
- validateStack: makeStackFunc(2, 1),
+ validateStack: makeStackFunc(2, -1),
memorySize: memorySha3,
valid: true,
},
@@ -200,7 +207,7 @@ func NewJumpTable() [256]operation {
BALANCE: {
execute: opBalance,
gasCost: gasBalance,
- validateStack: makeStackFunc(0, 1),
+ validateStack: makeStackFunc(1, 0),
valid: true,
},
ORIGIN: {
@@ -224,7 +231,7 @@ func NewJumpTable() [256]operation {
CALLDATALOAD: {
execute: opCalldataLoad,
gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(1, 1),
+ validateStack: makeStackFunc(1, 0),
valid: true,
},
CALLDATASIZE: {
@@ -236,7 +243,7 @@ func NewJumpTable() [256]operation {
CALLDATACOPY: {
execute: opCalldataCopy,
gasCost: gasCalldataCopy,
- validateStack: makeStackFunc(3, 1),
+ validateStack: makeStackFunc(3, -3),
memorySize: memoryCalldataCopy,
valid: true,
},
@@ -246,36 +253,36 @@ func NewJumpTable() [256]operation {
validateStack: makeStackFunc(0, 1),
valid: true,
},
- EXTCODESIZE: {
- execute: opExtCodeSize,
- gasCost: gasExtCodeSize,
- validateStack: makeStackFunc(1, 1),
- valid: true,
- },
CODECOPY: {
execute: opCodeCopy,
gasCost: gasCodeCopy,
- validateStack: makeStackFunc(3, 0),
+ validateStack: makeStackFunc(3, -3),
memorySize: memoryCodeCopy,
valid: true,
},
- EXTCODECOPY: {
- execute: opExtCodeCopy,
- gasCost: gasExtCodeCopy,
- validateStack: makeStackFunc(4, 0),
- memorySize: memoryExtCodeCopy,
- valid: true,
- },
GASPRICE: {
execute: opGasprice,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
+ EXTCODESIZE: {
+ execute: opExtCodeSize,
+ gasCost: gasExtCodeSize,
+ validateStack: makeStackFunc(1, 0),
+ valid: true,
+ },
+ EXTCODECOPY: {
+ execute: opExtCodeCopy,
+ gasCost: gasExtCodeCopy,
+ validateStack: makeStackFunc(4, -4),
+ memorySize: memoryExtCodeCopy,
+ valid: true,
+ },
BLOCKHASH: {
execute: opBlockhash,
gasCost: constGasFunc(GasExtStep),
- validateStack: makeStackFunc(1, 1),
+ validateStack: makeStackFunc(1, 0),
valid: true,
},
COINBASE: {
@@ -311,20 +318,20 @@ func NewJumpTable() [256]operation {
POP: {
execute: opPop,
gasCost: constGasFunc(GasQuickStep),
- validateStack: makeStackFunc(1, 0),
+ validateStack: makeStackFunc(1, -1),
valid: true,
},
MLOAD: {
execute: opMload,
gasCost: gasMLoad,
- validateStack: makeStackFunc(1, 1),
+ validateStack: makeStackFunc(1, 0),
memorySize: memoryMLoad,
valid: true,
},
MSTORE: {
execute: opMstore,
gasCost: gasMStore,
- validateStack: makeStackFunc(2, 0),
+ validateStack: makeStackFunc(2, -2),
memorySize: memoryMStore,
valid: true,
},
@@ -332,26 +339,34 @@ func NewJumpTable() [256]operation {
execute: opMstore8,
gasCost: gasMStore8,
memorySize: memoryMStore8,
- validateStack: makeStackFunc(2, 0),
+ validateStack: makeStackFunc(2, -2),
valid: true,
},
SLOAD: {
execute: opSload,
gasCost: gasSLoad,
- validateStack: makeStackFunc(1, 1),
+ validateStack: makeStackFunc(1, 0),
valid: true,
},
SSTORE: {
execute: opSstore,
gasCost: gasSStore,
- validateStack: makeStackFunc(2, 0),
+ validateStack: makeStackFunc(2, -2),
valid: true,
},
- JUMPDEST: {
- execute: opJumpdest,
- gasCost: constGasFunc(params.JumpdestGas),
- validateStack: makeStackFunc(0, 0),
+ JUMP: {
+ execute: opJump,
+ gasCost: constGasFunc(GasMidStep),
+ validateStack: makeStackFunc(1, -1),
+ jumps: true,
+ valid: true,
+ },
+ JUMPI: {
+ execute: opJumpi,
+ gasCost: constGasFunc(GasSlowStep),
+ validateStack: makeStackFunc(2, -2),
+ jumps: true,
valid: true,
},
PC: {
@@ -372,199 +387,10 @@ func NewJumpTable() [256]operation {
validateStack: makeStackFunc(0, 1),
valid: true,
},
- CREATE: {
- execute: opCreate,
- gasCost: gasCreate,
- validateStack: makeStackFunc(3, 1),
- memorySize: memoryCreate,
- valid: true,
- },
- CALL: {
- execute: opCall,
- gasCost: gasCall,
- validateStack: makeStackFunc(7, 1),
- memorySize: memoryCall,
- valid: true,
- },
- CALLCODE: {
- execute: opCallCode,
- gasCost: gasCallCode,
- validateStack: makeStackFunc(7, 1),
- memorySize: memoryCall,
- valid: true,
- },
- DELEGATECALL: {
- execute: opDelegateCall,
- gasCost: gasDelegateCall,
- validateStack: makeStackFunc(6, 1),
- memorySize: memoryDelegateCall,
- valid: true,
- },
- RETURN: {
- execute: opReturn,
- gasCost: gasReturn,
- validateStack: makeStackFunc(2, 0),
- memorySize: memoryReturn,
- halts: true,
- valid: true,
- },
- SUICIDE: {
- execute: opSuicide,
- gasCost: gasSuicide,
- validateStack: makeStackFunc(1, 0),
- halts: true,
- valid: true,
- },
- JUMP: {
- execute: opJump,
- gasCost: constGasFunc(GasMidStep),
- validateStack: makeStackFunc(1, 0),
- jumps: true,
- valid: true,
- },
- JUMPI: {
- execute: opJumpi,
- gasCost: constGasFunc(GasSlowStep),
- validateStack: makeStackFunc(2, 0),
- jumps: true,
- valid: true,
- },
- STOP: {
- execute: opStop,
- gasCost: constGasFunc(Zero),
+ JUMPDEST: {
+ execute: opJumpdest,
+ gasCost: constGasFunc(params.JumpdestGas),
validateStack: makeStackFunc(0, 0),
- halts: true,
- valid: true,
- },
- LOG0: {
- execute: makeLog(0),
- gasCost: makeGasLog(0),
- validateStack: makeStackFunc(2, 0),
- memorySize: memoryLog,
- valid: true,
- },
- LOG1: {
- execute: makeLog(1),
- gasCost: makeGasLog(1),
- validateStack: makeStackFunc(3, 0),
- memorySize: memoryLog,
- valid: true,
- },
- LOG2: {
- execute: makeLog(2),
- gasCost: makeGasLog(2),
- validateStack: makeStackFunc(4, 0),
- memorySize: memoryLog,
- valid: true,
- },
- LOG3: {
- execute: makeLog(3),
- gasCost: makeGasLog(3),
- validateStack: makeStackFunc(5, 0),
- memorySize: memoryLog,
- valid: true,
- },
- LOG4: {
- execute: makeLog(4),
- gasCost: makeGasLog(4),
- validateStack: makeStackFunc(6, 0),
- memorySize: memoryLog,
- valid: true,
- },
- SWAP1: {
- execute: makeSwap(1),
- gasCost: gasSwap,
- validateStack: makeStackFunc(2, 0),
- valid: true,
- },
- SWAP2: {
- execute: makeSwap(2),
- gasCost: gasSwap,
- validateStack: makeStackFunc(3, 0),
- valid: true,
- },
- SWAP3: {
- execute: makeSwap(3),
- gasCost: gasSwap,
- validateStack: makeStackFunc(4, 0),
- valid: true,
- },
- SWAP4: {
- execute: makeSwap(4),
- gasCost: gasSwap,
- validateStack: makeStackFunc(5, 0),
- valid: true,
- },
- SWAP5: {
- execute: makeSwap(5),
- gasCost: gasSwap,
- validateStack: makeStackFunc(6, 0),
- valid: true,
- },
- SWAP6: {
- execute: makeSwap(6),
- gasCost: gasSwap,
- validateStack: makeStackFunc(7, 0),
- valid: true,
- },
- SWAP7: {
- execute: makeSwap(7),
- gasCost: gasSwap,
- validateStack: makeStackFunc(8, 0),
- valid: true,
- },
- SWAP8: {
- execute: makeSwap(8),
- gasCost: gasSwap,
- validateStack: makeStackFunc(9, 0),
- valid: true,
- },
- SWAP9: {
- execute: makeSwap(9),
- gasCost: gasSwap,
- validateStack: makeStackFunc(10, 0),
- valid: true,
- },
- SWAP10: {
- execute: makeSwap(10),
- gasCost: gasSwap,
- validateStack: makeStackFunc(11, 0),
- valid: true,
- },
- SWAP11: {
- execute: makeSwap(11),
- gasCost: gasSwap,
- validateStack: makeStackFunc(12, 0),
- valid: true,
- },
- SWAP12: {
- execute: makeSwap(12),
- gasCost: gasSwap,
- validateStack: makeStackFunc(13, 0),
- valid: true,
- },
- SWAP13: {
- execute: makeSwap(13),
- gasCost: gasSwap,
- validateStack: makeStackFunc(14, 0),
- valid: true,
- },
- SWAP14: {
- execute: makeSwap(14),
- gasCost: gasSwap,
- validateStack: makeStackFunc(15, 0),
- valid: true,
- },
- SWAP15: {
- execute: makeSwap(15),
- gasCost: gasSwap,
- validateStack: makeStackFunc(16, 0),
- valid: true,
- },
- SWAP16: {
- execute: makeSwap(16),
- gasCost: gasSwap,
- validateStack: makeStackFunc(17, 0),
valid: true,
},
PUSH1: {
@@ -762,97 +588,271 @@ func NewJumpTable() [256]operation {
DUP1: {
execute: makeDup(1),
gasCost: gasDup,
- validateStack: makeStackFunc(1, 1),
+ validateStack: makeDupStackFunc(1),
valid: true,
},
DUP2: {
execute: makeDup(2),
gasCost: gasDup,
- validateStack: makeStackFunc(2, 1),
+ validateStack: makeDupStackFunc(2),
valid: true,
},
DUP3: {
execute: makeDup(3),
gasCost: gasDup,
- validateStack: makeStackFunc(3, 1),
+ validateStack: makeDupStackFunc(3),
valid: true,
},
DUP4: {
execute: makeDup(4),
gasCost: gasDup,
- validateStack: makeStackFunc(4, 1),
+ validateStack: makeDupStackFunc(4),
valid: true,
},
DUP5: {
execute: makeDup(5),
gasCost: gasDup,
- validateStack: makeStackFunc(5, 1),
+ validateStack: makeDupStackFunc(5),
valid: true,
},
DUP6: {
execute: makeDup(6),
gasCost: gasDup,
- validateStack: makeStackFunc(6, 1),
+ validateStack: makeDupStackFunc(6),
valid: true,
},
DUP7: {
execute: makeDup(7),
gasCost: gasDup,
- validateStack: makeStackFunc(7, 1),
+ validateStack: makeDupStackFunc(7),
valid: true,
},
DUP8: {
execute: makeDup(8),
gasCost: gasDup,
- validateStack: makeStackFunc(8, 1),
+ validateStack: makeDupStackFunc(8),
valid: true,
},
DUP9: {
execute: makeDup(9),
gasCost: gasDup,
- validateStack: makeStackFunc(9, 1),
+ validateStack: makeDupStackFunc(9),
valid: true,
},
DUP10: {
execute: makeDup(10),
gasCost: gasDup,
- validateStack: makeStackFunc(10, 1),
+ validateStack: makeDupStackFunc(10),
valid: true,
},
DUP11: {
execute: makeDup(11),
gasCost: gasDup,
- validateStack: makeStackFunc(11, 1),
+ validateStack: makeDupStackFunc(11),
valid: true,
},
DUP12: {
execute: makeDup(12),
gasCost: gasDup,
- validateStack: makeStackFunc(12, 1),
+ validateStack: makeDupStackFunc(12),
valid: true,
},
DUP13: {
execute: makeDup(13),
gasCost: gasDup,
- validateStack: makeStackFunc(13, 1),
+ validateStack: makeDupStackFunc(13),
valid: true,
},
DUP14: {
execute: makeDup(14),
gasCost: gasDup,
- validateStack: makeStackFunc(14, 1),
+ validateStack: makeDupStackFunc(14),
valid: true,
},
DUP15: {
execute: makeDup(15),
gasCost: gasDup,
- validateStack: makeStackFunc(15, 1),
+ validateStack: makeDupStackFunc(15),
valid: true,
},
DUP16: {
execute: makeDup(16),
gasCost: gasDup,
- validateStack: makeStackFunc(16, 1),
+ validateStack: makeDupStackFunc(16),
+ valid: true,
+ },
+ SWAP1: {
+ execute: makeSwap(1),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(2),
+ valid: true,
+ },
+ SWAP2: {
+ execute: makeSwap(2),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(3),
+ valid: true,
+ },
+ SWAP3: {
+ execute: makeSwap(3),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(4),
+ valid: true,
+ },
+ SWAP4: {
+ execute: makeSwap(4),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(5),
+ valid: true,
+ },
+ SWAP5: {
+ execute: makeSwap(5),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(6),
+ valid: true,
+ },
+ SWAP6: {
+ execute: makeSwap(6),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(7),
+ valid: true,
+ },
+ SWAP7: {
+ execute: makeSwap(7),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(8),
+ valid: true,
+ },
+ SWAP8: {
+ execute: makeSwap(8),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(9),
+ valid: true,
+ },
+ SWAP9: {
+ execute: makeSwap(9),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(10),
+ valid: true,
+ },
+ SWAP10: {
+ execute: makeSwap(10),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(11),
+ valid: true,
+ },
+ SWAP11: {
+ execute: makeSwap(11),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(12),
+ valid: true,
+ },
+ SWAP12: {
+ execute: makeSwap(12),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(13),
+ valid: true,
+ },
+ SWAP13: {
+ execute: makeSwap(13),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(14),
+ valid: true,
+ },
+ SWAP14: {
+ execute: makeSwap(14),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(15),
+ valid: true,
+ },
+ SWAP15: {
+ execute: makeSwap(15),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(16),
+ valid: true,
+ },
+ SWAP16: {
+ execute: makeSwap(16),
+ gasCost: gasSwap,
+ validateStack: makeSwapStackFunc(17),
+ valid: true,
+ },
+ LOG0: {
+ execute: makeLog(0),
+ gasCost: makeGasLog(0),
+ validateStack: makeStackFunc(2, -2),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ LOG1: {
+ execute: makeLog(1),
+ gasCost: makeGasLog(1),
+ validateStack: makeStackFunc(3, -3),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ LOG2: {
+ execute: makeLog(2),
+ gasCost: makeGasLog(2),
+ validateStack: makeStackFunc(4, -4),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ LOG3: {
+ execute: makeLog(3),
+ gasCost: makeGasLog(3),
+ validateStack: makeStackFunc(5, -5),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ LOG4: {
+ execute: makeLog(4),
+ gasCost: makeGasLog(4),
+ validateStack: makeStackFunc(6, -6),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ CREATE: {
+ execute: opCreate,
+ gasCost: gasCreate,
+ validateStack: makeStackFunc(3, -2),
+ memorySize: memoryCreate,
+ valid: true,
+ },
+ CALL: {
+ execute: opCall,
+ gasCost: gasCall,
+ validateStack: makeStackFunc(7, -6),
+ memorySize: memoryCall,
+ valid: true,
+ },
+ CALLCODE: {
+ execute: opCallCode,
+ gasCost: gasCallCode,
+ validateStack: makeStackFunc(7, -6),
+ memorySize: memoryCall,
+ valid: true,
+ },
+ RETURN: {
+ execute: opReturn,
+ gasCost: gasReturn,
+ validateStack: makeStackFunc(2, -2),
+ memorySize: memoryReturn,
+ halts: true,
+ valid: true,
+ },
+ DELEGATECALL: {
+ execute: opDelegateCall,
+ gasCost: gasDelegateCall,
+ validateStack: makeStackFunc(6, -5),
+ memorySize: memoryDelegateCall,
+ valid: true,
+ },
+ SELFDESTRUCT: {
+ execute: opSuicide,
+ gasCost: gasSuicide,
+ validateStack: makeStackFunc(1, -1),
+ halts: true,
valid: true,
},
}
diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go b/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go
index 9d2b037a5..d4ba7f156 100644
--- a/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go
+++ b/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go
@@ -202,7 +202,7 @@ const (
RETURN
DELEGATECALL
- SUICIDE = 0xff
+ SELFDESTRUCT = 0xff
)
// Since the opcodes aren't all in order we can't use a regular slice
@@ -355,7 +355,7 @@ var opCodeToString = map[OpCode]string{
RETURN: "RETURN",
CALLCODE: "CALLCODE",
DELEGATECALL: "DELEGATECALL",
- SUICIDE: "SUICIDE",
+ SELFDESTRUCT: "SELFDESTRUCT",
PUSH: "PUSH",
DUP: "DUP",
@@ -501,7 +501,7 @@ var stringToOp = map[string]OpCode{
"CALL": CALL,
"RETURN": RETURN,
"CALLCODE": CALLCODE,
- "SUICIDE": SUICIDE,
+ "SELFDESTRUCT": SELFDESTRUCT,
}
func StringToOp(str string) OpCode {
diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/stack_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/stack_table.go
index ce4727a71..eed8805f2 100644
--- a/vendor/github.com/ethereum/go-ethereum/core/vm/stack_table.go
+++ b/vendor/github.com/ethereum/go-ethereum/core/vm/stack_table.go
@@ -6,15 +6,23 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-func makeStackFunc(pop, push int) stackValidationFunc {
+func makeStackFunc(pop, diff int) stackValidationFunc {
return func(stack *Stack) error {
if err := stack.require(pop); err != nil {
return err
}
- if push > 0 && int64(stack.len()-pop+push) > params.StackLimit.Int64() {
- return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
+ if int64(stack.len()+diff) > params.StackLimit.Int64() {
+ return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
}
return nil
}
}
+
+func makeDupStackFunc(n int) stackValidationFunc {
+ return makeStackFunc(n, 1)
+}
+
+func makeSwapStackFunc(n int) stackValidationFunc {
+ return makeStackFunc(n, 0)
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go
index a5f48750d..05886a863 100644
--- a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go
+++ b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go
@@ -126,7 +126,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
}
// The Interpreter main run loop (contextual). This loop runs until either an
- // explicit STOP, RETURN or SUICIDE is executed, an error accured during
+ // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the evm.done is set by
// the parent context.Context.
for atomic.LoadInt32(&evm.env.abort) == 0 {
diff --git a/vendor/github.com/ethereum/go-ethereum/eth/backend.go b/vendor/github.com/ethereum/go-ethereum/eth/backend.go
index 3090f84bf..eff578aa5 100644
--- a/vendor/github.com/ethereum/go-ethereum/eth/backend.go
+++ b/vendor/github.com/ethereum/go-ethereum/eth/backend.go
@@ -360,15 +360,15 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
}
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
- eb = s.etherbase
- if (eb == common.Address{}) {
- firstAccount, err := s.AccountManager().AccountByIndex(0)
- eb = firstAccount.Address
- if err != nil {
- return eb, fmt.Errorf("etherbase address must be explicitly specified")
+ if s.etherbase != (common.Address{}) {
+ return s.etherbase, nil
+ }
+ if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
+ if accounts := wallets[0].Accounts(); len(accounts) > 0 {
+ return accounts[0].Address, nil
}
}
- return eb, nil
+ return common.Address{}, fmt.Errorf("etherbase address must be explicitly specified")
}
// set in js console via admin interface or wrapper from cli flags
diff --git a/vendor/github.com/ethereum/go-ethereum/event/feed.go b/vendor/github.com/ethereum/go-ethereum/event/feed.go
index bd8e26321..b1b597f17 100644
--- a/vendor/github.com/ethereum/go-ethereum/event/feed.go
+++ b/vendor/github.com/ethereum/go-ethereum/event/feed.go
@@ -33,7 +33,8 @@ var errBadChannel = errors.New("event: Subscribe argument does not have sendable
//
// The zero value is ready to use.
type Feed struct {
- sendLock chan struct{} // one-element buffer, empty when held
+ once sync.Once // ensures that init only runs once
+ sendLock chan struct{} // sendLock has a one-element buffer and is empty when held.It protects sendCases.
removeSub chan interface{} // interrupts Send
sendCases caseList // the active set of select cases used by Send
@@ -44,6 +45,10 @@ type Feed struct {
closed bool
}
+// This is the index of the first actual subscription channel in sendCases.
+// sendCases[0] is a SelectRecv case for the removeSub channel.
+const firstSubSendCase = 1
+
type feedTypeError struct {
got, want reflect.Type
op string
@@ -54,9 +59,6 @@ func (e feedTypeError) Error() string {
}
func (f *Feed) init() {
- if f.sendLock != nil {
- return
- }
f.removeSub = make(chan interface{})
f.sendLock = make(chan struct{}, 1)
f.sendLock <- struct{}{}
@@ -67,7 +69,10 @@ func (f *Feed) init() {
// until the subscription is canceled. All channels added must have the same element type.
//
// The channel should have ample buffer space to avoid blocking other subscribers.
+// Slow subscribers are not dropped.
func (f *Feed) Subscribe(channel interface{}) Subscription {
+ f.once.Do(f.init)
+
chanval := reflect.ValueOf(channel)
chantyp := chanval.Type()
if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.SendDir == 0 {
@@ -77,7 +82,6 @@ func (f *Feed) Subscribe(channel interface{}) Subscription {
f.mu.Lock()
defer f.mu.Unlock()
- f.init()
if !f.typecheck(chantyp.Elem()) {
panic(feedTypeError{op: "Subscribe", got: chantyp, want: reflect.ChanOf(reflect.SendDir, f.etype)})
}
@@ -123,15 +127,13 @@ func (f *Feed) remove(sub *feedSub) {
// Send delivers to all subscribed channels simultaneously.
// It returns the number of subscribers that the value was sent to.
func (f *Feed) Send(value interface{}) (nsent int) {
- f.mu.Lock()
- f.init()
+ f.once.Do(f.init)
<-f.sendLock
- // Add new subscriptions from the inbox, then clear it.
+
+ // Add new cases from the inbox after taking the send lock.
+ f.mu.Lock()
f.sendCases = append(f.sendCases, f.inbox...)
- for i := range f.inbox {
- f.inbox[i] = reflect.SelectCase{}
- }
- f.inbox = f.inbox[:0]
+ f.inbox = nil
f.mu.Unlock()
// Set the sent value on all channels.
@@ -140,7 +142,7 @@ func (f *Feed) Send(value interface{}) (nsent int) {
f.sendLock <- struct{}{}
panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype})
}
- for i := 1; i < len(f.sendCases); i++ {
+ for i := firstSubSendCase; i < len(f.sendCases); i++ {
f.sendCases[i].Send = rvalue
}
@@ -150,13 +152,14 @@ func (f *Feed) Send(value interface{}) (nsent int) {
// Fast path: try sending without blocking before adding to the select set.
// This should usually succeed if subscribers are fast enough and have free
// buffer space.
- for i := 1; i < len(cases); i++ {
+ for i := firstSubSendCase; i < len(cases); i++ {
if cases[i].Chan.TrySend(rvalue) {
- cases = cases.deactivate(i)
nsent++
+ cases = cases.deactivate(i)
+ i--
}
}
- if len(cases) == 1 {
+ if len(cases) == firstSubSendCase {
break
}
// Select on all the receivers, waiting for them to unblock.
@@ -174,7 +177,7 @@ func (f *Feed) Send(value interface{}) (nsent int) {
}
// Forget about the sent value and hand off the send lock.
- for i := 1; i < len(f.sendCases); i++ {
+ for i := firstSubSendCase; i < len(f.sendCases); i++ {
f.sendCases[i].Send = reflect.Value{}
}
f.sendLock <- struct{}{}
diff --git a/vendor/github.com/ethereum/go-ethereum/event/subscription.go b/vendor/github.com/ethereum/go-ethereum/event/subscription.go
index 7f2619b2d..83bd21213 100644
--- a/vendor/github.com/ethereum/go-ethereum/event/subscription.go
+++ b/vendor/github.com/ethereum/go-ethereum/event/subscription.go
@@ -43,14 +43,14 @@ type Subscription interface {
Unsubscribe() // cancels sending of events, closing the error channel
}
-// NewSubscription runs fn as a subscription in a new goroutine. The channel given to fn
-// is closed when Unsubscribe is called. If fn returns an error, it is sent on the
-// subscription's error channel.
-func NewSubscription(fn func(<-chan struct{}) error) Subscription {
+// NewSubscription runs a producer function as a subscription in a new goroutine. The
+// channel given to the producer is closed when Unsubscribe is called. If fn returns an
+// error, it is sent on the subscription's error channel.
+func NewSubscription(producer func(<-chan struct{}) error) Subscription {
s := &funcSub{unsub: make(chan struct{}), err: make(chan error, 1)}
go func() {
defer close(s.err)
- err := fn(s.unsub)
+ err := producer(s.unsub)
s.mu.Lock()
defer s.mu.Unlock()
if !s.unsubscribed {
diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go
index 93ce7fffe..37800f159 100644
--- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go
+++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
@@ -190,13 +191,19 @@ func NewPublicAccountAPI(b Backend) *PublicAccountAPI {
}
// Accounts returns the collection of accounts this node manages
-func (s *PublicAccountAPI) Accounts() []accounts.Account {
+func (s *PublicAccountAPI) Accounts() []common.Address {
backend := s.b.GetStatusBackend()
if backend != nil {
return backend.am.Accounts()
}
- return s.am.Accounts()
+ var addresses []common.Address
+ for _, wallet := range s.am.Wallets() {
+ for _, account := range wallet.Accounts() {
+ addresses = append(addresses, account.Address)
+ }
+ }
+ return addresses
}
// PrivateAccountAPI provides an API to access accounts managed by this node.
@@ -217,30 +224,71 @@ func NewPrivateAccountAPI(b Backend) *PrivateAccountAPI {
// ListAccounts will return a list of addresses for accounts this node manages.
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
- var accounts []accounts.Account
backend := s.b.GetStatusBackend()
if backend != nil {
- accounts = backend.am.Accounts()
- } else {
- accounts = s.am.Accounts()
+ return backend.am.Accounts()
}
-
- addresses := make([]common.Address, len(accounts))
- for i, acc := range accounts {
- addresses[i] = acc.Address
+ var addresses []common.Address
+ for _, wallet := range s.am.Wallets() {
+ for _, account := range wallet.Accounts() {
+ addresses = append(addresses, account.Address)
+ }
}
return addresses
}
+// rawWallet is a JSON representation of an accounts.Wallet interface, with its
+// data contents extracted into plain fields.
+type rawWallet struct {
+ URL string `json:"url"`
+ Status string `json:"status"`
+ Accounts []accounts.Account `json:"accounts"`
+}
+
+// ListWallets will return a list of wallets this node manages.
+func (s *PrivateAccountAPI) ListWallets() []rawWallet {
+ var wallets []rawWallet
+ for _, wallet := range s.am.Wallets() {
+ wallets = append(wallets, rawWallet{
+ URL: wallet.URL().String(),
+ Status: wallet.Status(),
+ Accounts: wallet.Accounts(),
+ })
+ }
+ return wallets
+}
+
+// DeriveAccount requests a HD wallet to derive a new account, optionally pinning
+// it for later reuse.
+func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) {
+ wallet, err := s.am.Wallet(url)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ derivPath, err := accounts.ParseDerivationPath(path)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ if pin == nil {
+ pin = new(bool)
+ }
+ return wallet.Derive(derivPath, *pin)
+}
+
// NewAccount will create a new account and returns the address for the new account.
-func (s *PrivateAccountAPI) NewAccount(password string, w bool) (common.Address, error) {
- acc, err := s.am.NewAccount(password, w)
+func (s *PrivateAccountAPI) NewAccount(password string, whisperEnabled bool) (common.Address, error) {
+ acc, err := fetchKeystore(s.am).NewAccount(password, whisperEnabled)
if err == nil {
return acc.Address, nil
}
return common.Address{}, err
}
+// fetchKeystore retrives the encrypted keystore from the account manager.
+func fetchKeystore(am *accounts.Manager) *keystore.KeyStore {
+ return am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+}
+
// ImportRawKey stores the given hex encoded ECDSA key into the key directory,
// encrypting it with the passphrase.
func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) {
@@ -249,7 +297,7 @@ func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (commo
return common.Address{}, err
}
- acc, err := s.am.ImportECDSA(crypto.ToECDSA(hexkey), password)
+ acc, err := fetchKeystore(s.am).ImportECDSA(crypto.ToECDSA(hexkey), password)
return acc.Address, err
}
@@ -266,30 +314,42 @@ func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string,
} else {
d = time.Duration(*duration) * time.Second
}
- err := s.am.TimedUnlock(accounts.Account{Address: addr}, password, d)
+ err := fetchKeystore(s.am).TimedUnlock(accounts.Account{Address: addr}, password, d)
return err == nil, err
}
// LockAccount will lock the account associated with the given address when it's unlocked.
func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
- return s.am.Lock(addr) == nil
+ return fetchKeystore(s.am).Lock(addr) == nil
}
// SendTransaction will create a transaction from the given arguments and
// tries to sign it with the key associated with args.To. If the given passwd isn't
// able to decrypt the key it fails.
func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
+ // Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, s.b); err != nil {
return common.Hash{}, err
}
- tx := args.toTransaction()
- signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number())
- signature, err := s.am.SignWithPassphrase(accounts.Account{Address: args.From}, passwd, signer.Hash(tx).Bytes())
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: args.From}
+
+ wallet, err := s.am.Find(account)
if err != nil {
return common.Hash{}, err
}
+ // Assemble the transaction and sign with the wallet
+ tx := args.toTransaction()
- return submitTransaction(ctx, s.b, tx, signature)
+ var chainID *big.Int
+ if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
+ chainID = config.ChainId
+ }
+ signed, err := wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ return submitTransaction(ctx, s.b, signed)
}
// signHash is a helper function that calculates a hash for the given message that can be
@@ -314,7 +374,15 @@ func signHash(data []byte) []byte {
//
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
- signature, err := s.b.AccountManager().SignWithPassphrase(accounts.Account{Address: addr}, passwd, signHash(data))
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: addr}
+
+ wallet, err := s.b.AccountManager().Find(account)
+ if err != nil {
+ return nil, err
+ }
+ // Assemble sign the data with the wallet
+ signature, err := wallet.SignHashWithPassphrase(account, passwd, signHash(data))
if err != nil {
return nil, err
}
@@ -527,21 +595,18 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
if state == nil || err != nil {
return "0x", common.Big0, err
}
-
- // Set the account address to interact with
- var addr common.Address
- if args.From == (common.Address{}) {
- accounts := s.b.AccountManager().Accounts()
- if len(accounts) == 0 {
- addr = common.Address{}
- } else {
- addr = accounts[0].Address
+ // Set sender address or use a default if none specified
+ addr := args.From
+ if addr == (common.Address{}) {
+ if wallets := s.b.AccountManager().Wallets(); len(wallets) > 0 {
+ if accounts := wallets[0].Accounts(); len(accounts) > 0 {
+ addr = accounts[0].Address
+ }
}
} else {
addr = args.From
}
-
- // Assemble the CALL invocation
+ // Set default gas & gas price if none were set
gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt()
if gas.Cmp(common.Big0) == 0 {
gas = big.NewInt(50000000)
@@ -1020,13 +1085,19 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
// sign is a helper function that signs a transaction with the private key of the given address.
func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
- signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number())
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: addr}
- signature, err := s.b.AccountManager().Sign(addr, signer.Hash(tx).Bytes())
+ wallet, err := s.b.AccountManager().Find(account)
if err != nil {
return nil, err
}
- return tx.WithSignature(signer, signature)
+ // Request the wallet to sign the transaction
+ var chainID *big.Int
+ if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
+ chainID = config.ChainId
+ }
+ return wallet.SignTx(account, tx, chainID)
}
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
@@ -1073,27 +1144,19 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
}
// submitTransaction is a helper function that submits tx to txPool and logs a message.
-func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, signature []byte) (common.Hash, error) {
- signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
-
- signedTx, err := tx.WithSignature(signer, signature)
- if err != nil {
+func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
+ if err := b.SendTx(ctx, tx); err != nil {
return common.Hash{}, err
}
-
- if err := b.SendTx(ctx, signedTx); err != nil {
- return common.Hash{}, err
- }
-
- if signedTx.To() == nil {
- from, _ := types.Sender(signer, signedTx)
- addr := crypto.CreateAddress(from, signedTx.Nonce())
- glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex())
+ if tx.To() == nil {
+ signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
+ from, _ := types.Sender(signer, tx)
+ addr := crypto.CreateAddress(from, tx.Nonce())
+ glog.V(logger.Info).Infof("Tx(%s) created: %s\n", tx.Hash().Hex(), addr.Hex())
} else {
- glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex())
+ glog.V(logger.Info).Infof("Tx(%s) to: %s\n", tx.Hash().Hex(), tx.To().Hex())
}
-
- return signedTx.Hash(), nil
+ return tx.Hash(), nil
}
// SendTransaction queues transactions, to be fulfilled by CompleteQueuedTransaction()
@@ -1109,8 +1172,8 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
// CompleteQueuedTransaction creates a transaction by unpacking queued transaction, signs it and submits to the
// transaction pool.
func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(ctx context.Context, args SendTxArgs, passphrase string) (common.Hash, error) {
- err := args.setDefaults(ctx, s.b)
- if err != nil {
+ // Set some sanity defaults and terminate on failure
+ if err := args.setDefaults(ctx, s.b); err != nil {
return common.Hash{}, err
}
@@ -1124,25 +1187,25 @@ func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(ctx context.Context
return common.Hash{}, status.ErrInvalidCompleteTxSender
}
- nonce, err := s.b.GetPoolNonce(ctx, args.From)
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: args.From}
+
+ wallet, err := s.b.AccountManager().Find(account)
if err != nil {
return common.Hash{}, err
}
- args.Nonce = (*hexutil.Uint64)(&nonce)
+ // Assemble the transaction and sign with the wallet
+ tx := args.toTransaction()
- var tx *types.Transaction
- if args.To == nil {
- tx = types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data)
- } else {
- tx = types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data)
+ var chainID *big.Int
+ if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
+ chainID = config.ChainId
}
-
- signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number())
- signature, err := s.b.AccountManager().SignWithPassphrase(accounts.Account{Address: args.From}, passphrase, tx.SigHash(signer).Bytes())
+ signed, err := wallet.SignTxWithPassphrase(account, passphrase, tx, chainID)
if err != nil {
return common.Hash{}, err
}
- return submitTransaction(ctx, s.b, tx, signature)
+ return submitTransaction(ctx, s.b, signed)
}
// SendRawTransaction will add the signed transaction to the transaction pool.
@@ -1182,7 +1245,15 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
- signature, err := s.b.AccountManager().Sign(addr, signHash(data))
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: addr}
+
+ wallet, err := s.b.AccountManager().Find(account)
+ if err != nil {
+ return nil, err
+ }
+ // Sign the requested hash with the wallet
+ signature, err := wallet.SignHash(account, signHash(data))
if err == nil {
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
}
@@ -1228,7 +1299,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err
signer = types.NewEIP155Signer(tx.ChainId())
}
from, _ := types.Sender(signer, tx)
- if s.b.AccountManager().HasAddress(from) {
+ if _, err := s.b.AccountManager().Find(accounts.Account{Address: from}); err == nil {
transactions = append(transactions, newRPCPendingTransaction(tx))
}
}
diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go
index 8fa1353cd..c96b159c2 100644
--- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go
+++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go
@@ -5,7 +5,7 @@ import (
"math/big"
"time"
- "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/les/status"
@@ -132,7 +132,7 @@ func (b *StatusBackend) CompleteQueuedTransaction(ctx context.Context, id status
hash, err := b.txapi.CompleteQueuedTransaction(ctx, SendTxArgs(queuedTx.Args), passphrase)
// on password error, notify the app, and keep tx in queue (so that CompleteQueuedTransaction() can be resent)
- if err == accounts.ErrDecrypt {
+ if err == keystore.ErrDecrypt {
b.NotifyOnQueuedTxReturn(queuedTx, err)
return hash, err // SendTransaction is still blocked
}
diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bindata.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bindata.go
index 73732a4eb..5f6a2b873 100644
--- a/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bindata.go
+++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bindata.go
@@ -206,8 +206,8 @@ type bintree struct {
}
var _bintree = &bintree{nil, map[string]*bintree{
- "bignumber.js": &bintree{bignumberJs, map[string]*bintree{}},
- "web3.js": &bintree{web3Js, map[string]*bintree{}},
+ "bignumber.js": {bignumberJs, map[string]*bintree{}},
+ "web3.js": {web3Js, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory
diff --git a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go
index edbe45fa3..2012c2517 100644
--- a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go
+++ b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go
@@ -448,6 +448,18 @@ web3._extend({
name: 'ecRecover',
call: 'personal_ecRecover',
params: 2
+ }),
+ new web3._extend.Method({
+ name: 'deriveAccount',
+ call: 'personal_deriveAccount',
+ params: 3
+ })
+ ],
+ properties:
+ [
+ new web3._extend.Property({
+ name: 'listWallets',
+ getter: 'personal_listWallets'
})
]
})
diff --git a/vendor/github.com/ethereum/go-ethereum/les/status/accounts.go b/vendor/github.com/ethereum/go-ethereum/les/status/accounts.go
index f081180d5..4a1c76580 100644
--- a/vendor/github.com/ethereum/go-ethereum/les/status/accounts.go
+++ b/vendor/github.com/ethereum/go-ethereum/les/status/accounts.go
@@ -2,6 +2,7 @@ package status
import (
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
)
type AccountManager struct {
@@ -16,18 +17,24 @@ func NewAccountManager(am *accounts.Manager) *AccountManager {
}
}
-type AccountsFilterHandler func([]accounts.Account) []accounts.Account
+type AccountsFilterHandler func([]common.Address) []common.Address
-// Accounts returns accounts of currently logged in user.
+// Accounts returns accounts' addresses of currently logged in user.
// Since status supports HD keys, the following list is returned:
// [addressCDK#1, addressCKD#2->Child1, addressCKD#2->Child2, .. addressCKD#2->ChildN]
-func (d *AccountManager) Accounts() []accounts.Account {
- accounts := d.am.Accounts()
- if d.accountsFilterHandler != nil {
- accounts = d.accountsFilterHandler(accounts)
+func (d *AccountManager) Accounts() []common.Address {
+ var addresses []common.Address
+ for _, wallet := range d.am.Wallets() {
+ for _, account := range wallet.Accounts() {
+ addresses = append(addresses, account.Address)
+ }
}
- return accounts
+ if d.accountsFilterHandler != nil {
+ return d.accountsFilterHandler(addresses)
+ }
+
+ return addresses
}
func (d *AccountManager) SetAccountsFilterHandler(fn AccountsFilterHandler) {
diff --git a/vendor/github.com/ethereum/go-ethereum/les/status/txqueue.go b/vendor/github.com/ethereum/go-ethereum/les/status/txqueue.go
index cb4dfec02..3b5c41a4c 100644
--- a/vendor/github.com/ethereum/go-ethereum/les/status/txqueue.go
+++ b/vendor/github.com/ethereum/go-ethereum/les/status/txqueue.go
@@ -4,7 +4,7 @@ import (
"errors"
"sync"
- "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/logger"
@@ -230,7 +230,7 @@ func (q *TxQueue) NotifyOnQueuedTxReturn(queuedTx *QueuedTx, err error) {
// remove from queue on any error (except for transient ones) and propagate
transientErrs := map[error]bool{
- accounts.ErrDecrypt: true, // wrong password
+ keystore.ErrDecrypt: true, // wrong password
ErrInvalidCompleteTxSender: true, // completing tx create from another account
}
if !transientErrs[err] { // remove only on unrecoverable errors
diff --git a/vendor/github.com/ethereum/go-ethereum/miner/worker.go b/vendor/github.com/ethereum/go-ethereum/miner/worker.go
index 49ac60253..ef64c8fc9 100644
--- a/vendor/github.com/ethereum/go-ethereum/miner/worker.go
+++ b/vendor/github.com/ethereum/go-ethereum/miner/worker.go
@@ -386,8 +386,11 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
work.family.Add(ancestor.Hash())
work.ancestors.Add(ancestor.Hash())
}
- accounts := self.eth.AccountManager().Accounts()
-
+ wallets := self.eth.AccountManager().Wallets()
+ accounts := make([]accounts.Account, 0, len(wallets))
+ for _, wallet := range wallets {
+ accounts = append(accounts, wallet.Accounts()...)
+ }
// Keep track of transactions which return errors so they can be removed
work.tcount = 0
work.ownedAccounts = accountAddressesSet(accounts)
diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go b/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go
index 621be4d7a..fbaa3bf40 100644
--- a/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go
+++ b/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go
@@ -24,24 +24,25 @@ import (
"time"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
)
const (
// StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
// memory and taking approximately 1s CPU time on a modern processor.
- StandardScryptN = int(accounts.StandardScryptN)
+ StandardScryptN = int(keystore.StandardScryptN)
// StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
// memory and taking approximately 1s CPU time on a modern processor.
- StandardScryptP = int(accounts.StandardScryptP)
+ StandardScryptP = int(keystore.StandardScryptP)
// LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
// memory and taking approximately 100ms CPU time on a modern processor.
- LightScryptN = int(accounts.LightScryptN)
+ LightScryptN = int(keystore.LightScryptN)
// LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
// memory and taking approximately 100ms CPU time on a modern processor.
- LightScryptP = int(accounts.LightScryptP)
+ LightScryptP = int(keystore.LightScryptP)
)
// Account represents a stored key.
@@ -77,59 +78,75 @@ func (a *Account) GetAddress() *Address {
return &Address{a.account.Address}
}
-// GetFile retrieves the path of the file containing the account key.
-func (a *Account) GetFile() string {
- return a.account.File
+// GetURL retrieves the canonical URL of the account.
+func (a *Account) GetURL() string {
+ return a.account.URL.String()
}
-// AccountManager manages a key storage directory on disk.
-type AccountManager struct{ manager *accounts.Manager }
+// KeyStore manages a key storage directory on disk.
+type KeyStore struct{ keystore *keystore.KeyStore }
-// NewAccountManager creates a manager for the given directory.
-func NewAccountManager(keydir string, scryptN, scryptP int) *AccountManager {
- return &AccountManager{manager: accounts.NewManager(keydir, scryptN, scryptP)}
+// NewKeyStore creates a keystore for the given directory.
+func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
+ return &KeyStore{keystore: keystore.NewKeyStore(keydir, scryptN, scryptP)}
}
// HasAddress reports whether a key with the given address is present.
-func (am *AccountManager) HasAddress(address *Address) bool {
- return am.manager.HasAddress(address.address)
+func (ks *KeyStore) HasAddress(address *Address) bool {
+ return ks.keystore.HasAddress(address.address)
}
// GetAccounts returns all key files present in the directory.
-func (am *AccountManager) GetAccounts() *Accounts {
- return &Accounts{am.manager.Accounts()}
+func (ks *KeyStore) GetAccounts() *Accounts {
+ return &Accounts{ks.keystore.Accounts()}
}
// DeleteAccount deletes the key matched by account if the passphrase is correct.
// If a contains no filename, the address must match a unique key.
-func (am *AccountManager) DeleteAccount(account *Account, passphrase string) error {
- return am.manager.Delete(accounts.Account{
- Address: account.account.Address,
- File: account.account.File,
- }, passphrase)
+func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error {
+ return ks.keystore.Delete(account.account, passphrase)
}
-// Sign calculates a ECDSA signature for the given hash. The produced signature
+// SignHash calculates a ECDSA signature for the given hash. The produced signature
// is in the [R || S || V] format where V is 0 or 1.
-func (am *AccountManager) Sign(address *Address, hash []byte) (signature []byte, _ error) {
- return am.manager.Sign(address.address, hash)
+func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) {
+ return ks.keystore.SignHash(accounts.Account{Address: address.address}, hash)
}
-// SignPassphrase signs hash if the private key matching the given address can
+// SignTx signs the given transaction with the requested account.
+func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) {
+ signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint)
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{signed}, nil
+}
+
+// SignHashPassphrase signs hash if the private key matching the given address can
// be decrypted with the given passphrase. The produced signature is in the
// [R || S || V] format where V is 0 or 1.
-func (am *AccountManager) SignPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) {
- return am.manager.SignWithPassphrase(account.account, passphrase, hash)
+func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) {
+ return ks.keystore.SignHashWithPassphrase(account.account, passphrase, hash)
+}
+
+// SignTxPassphrase signs the transaction if the private key matching the
+// given address can be decrypted with the given passphrase.
+func (ks *KeyStore) SignTxPassphrase(account *Account, passphrase string, tx *Transaction, chainID *BigInt) (*Transaction, error) {
+ signed, err := ks.keystore.SignTxWithPassphrase(account.account, passphrase, tx.tx, chainID.bigint)
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{signed}, nil
}
// Unlock unlocks the given account indefinitely.
-func (am *AccountManager) Unlock(account *Account, passphrase string) error {
- return am.manager.TimedUnlock(account.account, passphrase, 0)
+func (ks *KeyStore) Unlock(account *Account, passphrase string) error {
+ return ks.keystore.TimedUnlock(account.account, passphrase, 0)
}
// Lock removes the private key with the given address from memory.
-func (am *AccountManager) Lock(address *Address) error {
- return am.manager.Lock(address.address)
+func (ks *KeyStore) Lock(address *Address) error {
+ return ks.keystore.Lock(address.address)
}
// TimedUnlock unlocks the given account with the passphrase. The account stays
@@ -139,14 +156,14 @@ func (am *AccountManager) Lock(address *Address) error {
// If the account address is already unlocked for a duration, TimedUnlock extends or
// shortens the active unlock timeout. If the address was previously unlocked
// indefinitely the timeout is not altered.
-func (am *AccountManager) TimedUnlock(account *Account, passphrase string, timeout int64) error {
- return am.manager.TimedUnlock(account.account, passphrase, time.Duration(timeout))
+func (ks *KeyStore) TimedUnlock(account *Account, passphrase string, timeout int64) error {
+ return ks.keystore.TimedUnlock(account.account, passphrase, time.Duration(timeout))
}
// NewAccount generates a new key and stores it into the key directory,
// encrypting it with the passphrase.
-func (am *AccountManager) NewAccount(passphrase string) (*Account, error) {
- account, err := am.manager.NewAccount(passphrase)
+func (ks *KeyStore) NewAccount(passphrase string) (*Account, error) {
+ account, err := ks.keystore.NewAccount(passphrase)
if err != nil {
return nil, err
}
@@ -154,13 +171,13 @@ func (am *AccountManager) NewAccount(passphrase string) (*Account, error) {
}
// ExportKey exports as a JSON key, encrypted with newPassphrase.
-func (am *AccountManager) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) {
- return am.manager.Export(account.account, passphrase, newPassphrase)
+func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) {
+ return ks.keystore.Export(account.account, passphrase, newPassphrase)
}
// ImportKey stores the given encrypted JSON key into the key directory.
-func (am *AccountManager) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) {
- acc, err := am.manager.Import(keyJSON, passphrase, newPassphrase)
+func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) {
+ acc, err := ks.keystore.Import(keyJSON, passphrase, newPassphrase)
if err != nil {
return nil, err
}
@@ -168,14 +185,14 @@ func (am *AccountManager) ImportKey(keyJSON []byte, passphrase, newPassphrase st
}
// UpdateAccount changes the passphrase of an existing account.
-func (am *AccountManager) UpdateAccount(account *Account, passphrase, newPassphrase string) error {
- return am.manager.Update(account.account, passphrase, newPassphrase)
+func (ks *KeyStore) UpdateAccount(account *Account, passphrase, newPassphrase string) error {
+ return ks.keystore.Update(account.account, passphrase, newPassphrase)
}
// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
// a key file in the key directory. The key file is encrypted with the same passphrase.
-func (am *AccountManager) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) {
- account, err := am.manager.ImportPreSaleKey(keyJSON, passphrase)
+func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) {
+ account, err := ks.keystore.ImportPreSaleKey(keyJSON, passphrase)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go
index af0054cdc..52c6986fb 100644
--- a/vendor/github.com/ethereum/go-ethereum/mobile/geth.go
+++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go
@@ -32,7 +32,7 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/whisper/whisperv2"
+ whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
)
// NodeConfig represents the collection of configuration values to fine tune the Geth
@@ -172,7 +172,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
}
// Register the Whisper protocol if requested
if config.WhisperEnabled {
- if err := rawStack.Register(func(*node.ServiceContext) (node.Service, error) { return whisperv2.New(), nil }); err != nil {
+ if err := rawStack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
return nil, fmt.Errorf("whisper init: %v", err)
}
}
diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/types.go b/vendor/github.com/ethereum/go-ethereum/mobile/types.go
index 9ea70ea9b..a9c8cf68c 100644
--- a/vendor/github.com/ethereum/go-ethereum/mobile/types.go
+++ b/vendor/github.com/ethereum/go-ethereum/mobile/types.go
@@ -132,6 +132,11 @@ type Transaction struct {
tx *types.Transaction
}
+// NewTransaction creates a new transaction with the given properties.
+func NewTransaction(nonce int64, to *Address, amount, gasLimit, gasPrice *BigInt, data []byte) *Transaction {
+ return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, gasLimit.bigint, gasPrice.bigint, data)}
+}
+
func (tx *Transaction) GetData() []byte { return tx.tx.Data() }
func (tx *Transaction) GetGas() int64 { return tx.tx.Gas().Int64() }
func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} }
diff --git a/vendor/github.com/ethereum/go-ethereum/node/config.go b/vendor/github.com/ethereum/go-ethereum/node/config.go
index 8d75e441b..c09f51747 100644
--- a/vendor/github.com/ethereum/go-ethereum/node/config.go
+++ b/vendor/github.com/ethereum/go-ethereum/node/config.go
@@ -27,6 +27,8 @@ import (
"strings"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
@@ -400,15 +402,19 @@ func (c *Config) parsePersistentNodes(path string) []*discover.Node {
return nodes
}
-func makeAccountManager(conf *Config) (am *accounts.Manager, ephemeralKeystore string, err error) {
- scryptN := accounts.StandardScryptN
- scryptP := accounts.StandardScryptP
+func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
+ scryptN := keystore.StandardScryptN
+ scryptP := keystore.StandardScryptP
if conf.UseLightweightKDF {
- scryptN = accounts.LightScryptN
- scryptP = accounts.LightScryptP
+ scryptN = keystore.LightScryptN
+ scryptP = keystore.LightScryptP
}
- var keydir string
+ var (
+ keydir string
+ ephemeral string
+ err error
+ )
switch {
case filepath.IsAbs(conf.KeyStoreDir):
keydir = conf.KeyStoreDir
@@ -423,7 +429,7 @@ func makeAccountManager(conf *Config) (am *accounts.Manager, ephemeralKeystore s
default:
// There is no datadir.
keydir, err = ioutil.TempDir("", "go-ethereum-keystore")
- ephemeralKeystore = keydir
+ ephemeral = keydir
}
if err != nil {
return nil, "", err
@@ -431,6 +437,14 @@ func makeAccountManager(conf *Config) (am *accounts.Manager, ephemeralKeystore s
if err := os.MkdirAll(keydir, 0700); err != nil {
return nil, "", err
}
-
- return accounts.NewManager(keydir, scryptN, scryptP), ephemeralKeystore, nil
+ // Assemble the account manager and supported backends
+ backends := []accounts.Backend{
+ keystore.NewKeyStore(keydir, scryptN, scryptP),
+ }
+ if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
+ glog.V(logger.Warn).Infof("Failed to start Ledger hub, disabling: %v", err)
+ } else {
+ backends = append(backends, ledgerhub)
+ }
+ return accounts.NewManager(backends...), ephemeral, nil
}
diff --git a/vendor/github.com/ethereum/go-ethereum/params/version.go b/vendor/github.com/ethereum/go-ethereum/params/version.go
index cc95f5de3..bc5756fc6 100644
--- a/vendor/github.com/ethereum/go-ethereum/params/version.go
+++ b/vendor/github.com/ethereum/go-ethereum/params/version.go
@@ -21,7 +21,7 @@ import "fmt"
const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 5 // Minor version component of the current release
- VersionPatch = 8 // Patch version component of the current release
+ VersionPatch = 9 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string
)
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go
index 79987cc6b..454a57270 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go
@@ -99,6 +99,7 @@ func (self *Depo) HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer)
// if key found locally, return. otherwise
// remote is untrusted, so hash is verified and chunk passed on to NetStore
func (self *Depo) HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) {
+ var islocal bool
req.from = p
chunk, err := self.localStore.Get(req.Key)
switch {
@@ -110,27 +111,32 @@ func (self *Depo) HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) {
case chunk.SData == nil:
// found chunk in memory store, needs the data, validate now
- hasher := self.hashfunc()
- hasher.Write(req.SData)
- if !bytes.Equal(hasher.Sum(nil), req.Key) {
- // data does not validate, ignore
- // TODO: peer should be penalised/dropped?
- glog.V(logger.Warn).Infof("Depo.HandleStoreRequest: chunk invalid. store request ignored: %v", req)
- return
- }
glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v. request entry found", req)
default:
// data is found, store request ignored
// this should update access count?
glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v found locally. ignore.", req)
+ islocal = true
+ //return
+ }
+
+ hasher := self.hashfunc()
+ hasher.Write(req.SData)
+ if !bytes.Equal(hasher.Sum(nil), req.Key) {
+ // data does not validate, ignore
+ // TODO: peer should be penalised/dropped?
+ glog.V(logger.Warn).Infof("Depo.HandleStoreRequest: chunk invalid. store request ignored: %v", req)
return
}
+ if islocal {
+ return
+ }
// update chunk with size and data
chunk.SData = req.SData // protocol validates that SData is minimum 9 bytes long (int64 size + at least one byte of data)
chunk.Size = int64(binary.LittleEndian.Uint64(req.SData[0:8]))
- glog.V(logger.Detail).Infof("delivery of %p from %v", chunk, p)
+ glog.V(logger.Detail).Infof("delivery of %v from %v", chunk, p)
chunk.Source = p
self.netStore.Put(chunk)
}
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go
index f81761b97..e5aef26b9 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go
@@ -20,6 +20,7 @@ import (
"fmt"
"math/rand"
"path/filepath"
+ "sync"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -51,6 +52,9 @@ type Hive struct {
toggle chan bool
more chan bool
+ lock sync.Mutex
+ running bool
+
// for testing only
swapEnabled bool
syncEnabled bool
@@ -122,6 +126,12 @@ func (self *Hive) Addr() kademlia.Address {
// connectPeer is a function to connect to a peer based on its NodeID or enode URL
// there are called on the p2p.Server which runs on the node
func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPeer func(string) error) (err error) {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ if self.running {
+ return
+ }
+
self.toggle = make(chan bool)
self.more = make(chan bool)
self.quit = make(chan bool)
@@ -211,6 +221,12 @@ func (self *Hive) keepAlive() {
}
func (self *Hive) Stop() error {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ if !self.running {
+ return nil
+ }
+
// closing toggle channel quits the updateloop
close(self.quit)
return self.kad.Save(self.path, saveSync)
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go
index e871666bd..b6b1ea3b6 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go
@@ -438,7 +438,7 @@ LOOP:
for priority = High; priority >= 0; priority-- {
// the first priority channel that is non-empty will be assigned to keys
if len(self.keys[priority]) > 0 {
- glog.V(logger.Detail).Infof("syncer[%v]: reading request with priority %v", self.key.Log(), priority)
+ glog.V(logger.Detail).Infof("syncer[%v]: reading request with priority %v", self.key.Log(), priority)
keys = self.keys[priority]
break PRIORITIES
}
@@ -551,10 +551,10 @@ LOOP:
}
if sreq, err := self.newSyncRequest(req, priority); err == nil {
// extract key from req
- glog.V(logger.Detail).Infof("syncer(priority %v): request %v (synced = %v)", self.key.Log(), priority, req, state.Synced)
+ glog.V(logger.Detail).Infof("syncer[%v]: (priority %v): request %v (synced = %v)", self.key.Log(), priority, req, state.Synced)
unsynced = append(unsynced, sreq)
} else {
- glog.V(logger.Warn).Infof("syncer(priority %v): error creating request for %v: %v)", self.key.Log(), priority, req, state.Synced, err)
+ glog.V(logger.Warn).Infof("syncer[%v]: (priority %v): error creating request for %v: %v)", self.key.Log(), priority, req, state.Synced, err)
}
}
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go
index 4ddebb021..e320cd327 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go
@@ -252,12 +252,7 @@ func (s *DbStore) collectGarbage(ratio float32) {
// actual gc
for i := 0; i < gcnt; i++ {
if s.gcArray[i].value <= cutval {
- batch := new(leveldb.Batch)
- batch.Delete(s.gcArray[i].idxKey)
- batch.Delete(getDataKey(s.gcArray[i].idx))
- s.entryCnt--
- batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt))
- s.db.Write(batch)
+ s.delete(s.gcArray[i].idx, s.gcArray[i].idxKey)
}
}
@@ -266,6 +261,52 @@ func (s *DbStore) collectGarbage(ratio float32) {
s.db.Put(keyGCPos, s.gcPos)
}
+func (s *DbStore) Cleanup() {
+ //Iterates over the database and checks that there are no faulty chunks
+ it := s.db.NewIterator()
+ startPosition := []byte{kpIndex}
+ it.Seek(startPosition)
+ var key []byte
+ var errorsFound, total int
+ for it.Valid() {
+ key = it.Key()
+ if (key == nil) || (key[0] != kpIndex) {
+ break
+ }
+ total++
+ var index dpaDBIndex
+ decodeIndex(it.Value(), &index)
+
+ data, err := s.db.Get(getDataKey(index.Idx))
+ if err != nil {
+ glog.V(logger.Warn).Infof("Chunk %x found but could not be accessed: %v", key[:], err)
+ s.delete(index.Idx, getIndexKey(key[1:]))
+ errorsFound++
+ } else {
+ hasher := s.hashfunc()
+ hasher.Write(data)
+ hash := hasher.Sum(nil)
+ if !bytes.Equal(hash, key[1:]) {
+ glog.V(logger.Warn).Infof("Found invalid chunk. Hash mismatch. hash=%x, key=%x", hash, key[:])
+ s.delete(index.Idx, getIndexKey(key[1:]))
+ errorsFound++
+ }
+ }
+ it.Next()
+ }
+ it.Release()
+ glog.V(logger.Warn).Infof("Found %v errors out of %v entries", errorsFound, total)
+}
+
+func (s *DbStore) delete(idx uint64, idxKey []byte) {
+ batch := new(leveldb.Batch)
+ batch.Delete(idxKey)
+ batch.Delete(getDataKey(idx))
+ s.entryCnt--
+ batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt))
+ s.db.Write(batch)
+}
+
func (s *DbStore) Counter() uint64 {
s.lock.Lock()
defer s.lock.Unlock()
@@ -283,6 +324,7 @@ func (s *DbStore) Put(chunk *Chunk) {
if chunk.dbStored != nil {
close(chunk.dbStored)
}
+ glog.V(logger.Detail).Infof("Storing to DB: chunk already exists, only update access")
return // already exists, only update access
}
@@ -348,6 +390,8 @@ func (s *DbStore) Get(key Key) (chunk *Chunk, err error) {
var data []byte
data, err = s.db.Get(getDataKey(index.Idx))
if err != nil {
+ glog.V(logger.Detail).Infof("DBStore: Chunk %v found but could not be accessed: %v", key.Log(), err)
+ s.delete(index.Idx, getIndexKey(key))
return
}
@@ -355,9 +399,8 @@ func (s *DbStore) Get(key Key) (chunk *Chunk, err error) {
hasher.Write(data)
hash := hasher.Sum(nil)
if !bytes.Equal(hash, key) {
- s.db.Delete(getDataKey(index.Idx))
- err = fmt.Errorf("invalid chunk. hash=%x, key=%v", hash, key[:])
- return
+ s.delete(index.Idx, getIndexKey(key))
+ panic("Invalid Chunk in Database. Please repair with command: 'swarm cleandb'")
}
chunk = &Chunk{
@@ -408,7 +451,7 @@ func (s *DbStore) getEntryCnt() uint64 {
return s.entryCnt
}
-func (s *DbStore) close() {
+func (s *DbStore) Close() {
s.db.Close()
}
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go
index 31b6c54ac..7b3e23cac 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go
@@ -237,3 +237,8 @@ func (self *dpaChunkStore) Put(entry *Chunk) {
self.n++
self.netStore.Put(chunk)
}
+
+// Close chunk store
+func (self *dpaChunkStore) Close() {
+ return
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go
index 541462f0c..14827e361 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go
@@ -72,3 +72,8 @@ func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) {
self.memStore.Put(chunk)
return
}
+
+// Close local store
+func (self *LocalStore) Close() {
+ return
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go
index f133bd7d3..7903d33e7 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go
@@ -20,6 +20,9 @@ package storage
import (
"sync"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
)
const (
@@ -284,7 +287,11 @@ func (s *MemStore) removeOldest() {
}
if node.entry.dbStored != nil {
+ glog.V(logger.Detail).Infof("Memstore Clean: Waiting for chunk %v to be saved", node.entry.Key.Log())
<-node.entry.dbStored
+ glog.V(logger.Detail).Infof("Memstore Clean: Chunk %v saved to DBStore. Ready to clear from mem.", node.entry.Key.Log())
+ } else {
+ glog.V(logger.Detail).Infof("Memstore Clean: Chunk %v already in DB. Ready to delete.", node.entry.Key.Log())
}
if node.entry.SData != nil {
@@ -314,3 +321,8 @@ func (s *MemStore) removeOldest() {
}
}
}
+
+// Close memstore
+func (s *MemStore) Close() {
+ return
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go
index f97862bbb..46479b58a 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go
@@ -132,3 +132,8 @@ func (self *NetStore) Get(key Key) (*Chunk, error) {
go self.cloud.Retrieve(chunk)
return chunk, nil
}
+
+// Close netstore
+func (self *NetStore) Close() {
+ return
+}
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go
index f3ab99c6c..cc5ded931 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go
@@ -167,6 +167,7 @@ The ChunkStore interface is implemented by :
type ChunkStore interface {
Put(*Chunk) // effectively there is no error even if there is an error
Get(Key) (*Chunk, error)
+ Close()
}
/*
diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/swarm.go b/vendor/github.com/ethereum/go-ethereum/swarm/swarm.go
index 4b3621aff..eab01f036 100644
--- a/vendor/github.com/ethereum/go-ethereum/swarm/swarm.go
+++ b/vendor/github.com/ethereum/go-ethereum/swarm/swarm.go
@@ -54,6 +54,7 @@ type Swarm struct {
privateKey *ecdsa.PrivateKey
corsString string
swapEnabled bool
+ lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped
}
type SwarmAPI struct {
@@ -90,7 +91,7 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
glog.V(logger.Debug).Infof("Setting up Swarm service components")
hash := storage.MakeHashFunc(config.ChunkerParams.Hash)
- lstore, err := storage.NewLocalStore(hash, config.StoreParams)
+ self.lstore, err = storage.NewLocalStore(hash, config.StoreParams)
if err != nil {
return
}
@@ -98,7 +99,7 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
// setup local store
glog.V(logger.Debug).Infof("Set up local storage")
- self.dbAccess = network.NewDbAccess(lstore)
+ self.dbAccess = network.NewDbAccess(self.lstore)
glog.V(logger.Debug).Infof("Set up local db access (iterator/counter)")
// set up the kademlia hive
@@ -115,15 +116,15 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
glog.V(logger.Debug).Infof("-> set swarm forwarder as cloud storage backend")
// setup cloud storage internal access layer
- self.storage = storage.NewNetStore(hash, lstore, cloud, config.StoreParams)
+ self.storage = storage.NewNetStore(hash, self.lstore, cloud, config.StoreParams)
glog.V(logger.Debug).Infof("-> swarm net store shared access layer to Swarm Chunk Store")
// set up Depo (storage handler = cloud storage access layer for incoming remote requests)
- self.depo = network.NewDepo(hash, lstore, self.storage)
+ self.depo = network.NewDepo(hash, self.lstore, self.storage)
glog.V(logger.Debug).Infof("-> REmote Access to CHunks")
// set up DPA, the cloud storage local access layer
- dpaChunkStore := storage.NewDpaChunkStore(lstore, self.storage)
+ dpaChunkStore := storage.NewDpaChunkStore(self.lstore, self.storage)
glog.V(logger.Debug).Infof("-> Local Access to Swarm")
// Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage
self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams)
@@ -212,6 +213,11 @@ func (self *Swarm) Stop() error {
ch.Stop()
ch.Save()
}
+
+ if self.lstore != nil {
+ self.lstore.DbStore.Close()
+ }
+
return self.config.Save()
}
diff --git a/vendor/github.com/ethereum/go-ethereum/vendor.conf b/vendor/github.com/ethereum/go-ethereum/vendor.conf
index 1428a1b59..92092a2ac 100644
--- a/vendor/github.com/ethereum/go-ethereum/vendor.conf
+++ b/vendor/github.com/ethereum/go-ethereum/vendor.conf
@@ -13,6 +13,7 @@ github.com/golang/snappy d9eb7a3
github.com/hashicorp/golang-lru 0a025b7
github.com/huin/goupnp 679507a
github.com/jackpal/go-nat-pmp v1.0.1-4-g1fa385a
+github.com/karalabe/gousb ffa821b
github.com/maruel/panicparse ad66119
github.com/mattn/go-colorable v0.0.6-9-gd228849
github.com/mattn/go-isatty 30a891c
diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/mailserver/mailserver.go b/vendor/github.com/ethereum/go-ethereum/whisper/mailserver/mailserver.go
index f7d6c3e5c..3e08a3b7e 100644
--- a/vendor/github.com/ethereum/go-ethereum/whisper/mailserver/mailserver.go
+++ b/vendor/github.com/ethereum/go-ethereum/whisper/mailserver/mailserver.go
@@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
@@ -101,11 +102,19 @@ func (s *WMailServer) Archive(env *whisper.Envelope) {
}
func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) {
- ok, lower, upper, topic := s.validate(peer, request)
- if !ok {
+ if peer == nil {
+ glog.V(logger.Error).Info("Whisper peer is nil")
return
}
+ ok, lower, upper, topic := s.validateRequest(peer.ID(), request)
+ if ok {
+ s.processRequest(peer, lower, upper, topic)
+ }
+}
+
+func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, topic whisper.TopicType) []*whisper.Envelope {
+ ret := make([]*whisper.Envelope, 0)
var err error
var zero common.Hash
var empty whisper.TopicType
@@ -122,10 +131,15 @@ func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope)
}
if topic == empty || envelope.Topic == topic {
- err = s.w.SendP2PDirect(peer, &envelope)
- if err != nil {
- glog.V(logger.Error).Infof("Failed to send direct message to peer: %s", err)
- return
+ if peer == nil {
+ // used for test purposes
+ ret = append(ret, &envelope)
+ } else {
+ err = s.w.SendP2PDirect(peer, &envelope)
+ if err != nil {
+ glog.V(logger.Error).Infof("Failed to send direct message to peer: %s", err)
+ return nil
+ }
}
}
}
@@ -134,9 +148,11 @@ func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope)
if err != nil {
glog.V(logger.Error).Infof("Level DB iterator error: %s", err)
}
+
+ return ret
}
-func (s *WMailServer) validate(peer *whisper.Peer, request *whisper.Envelope) (bool, uint32, uint32, whisper.TopicType) {
+func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, whisper.TopicType) {
var topic whisper.TopicType
if s.pow > 0.0 && request.PoW() < s.pow {
return false, 0, 0, topic
@@ -154,7 +170,11 @@ func (s *WMailServer) validate(peer *whisper.Peer, request *whisper.Envelope) (b
return false, 0, 0, topic
}
- if bytes.Equal(peer.ID(), decrypted.Signature) {
+ src := crypto.FromECDSAPub(decrypted.Src)
+ if len(src)-len(peerID) == 1 {
+ src = src[1:]
+ }
+ if !bytes.Equal(peerID, src) {
glog.V(logger.Warn).Infof("Wrong signature of p2p request")
return false, 0, 0, topic
}
diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/api.go
similarity index 79%
rename from vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go
rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/api.go
index b273053ec..4d33d2321 100644
--- a/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go
+++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/api.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package shhapi
+package whisperv5
import (
"encoding/json"
@@ -27,35 +27,20 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/whisper/whisperv5"
)
var whisperOffLineErr = errors.New("whisper is offline")
// PublicWhisperAPI provides the whisper RPC service.
type PublicWhisperAPI struct {
- whisper *whisperv5.Whisper
+ whisper *Whisper
}
// NewPublicWhisperAPI create a new RPC whisper service.
-func NewPublicWhisperAPI() *PublicWhisperAPI {
- w := whisperv5.NewWhisper(nil)
+func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI {
return &PublicWhisperAPI{whisper: w}
}
-// APIs returns the RPC descriptors the Whisper implementation offers
-func APIs() []rpc.API {
- return []rpc.API{
- {
- Namespace: whisperv5.ProtocolName,
- Version: whisperv5.ProtocolVersionStr,
- Service: NewPublicWhisperAPI(),
- Public: true,
- },
- }
-}
-
// Start starts the Whisper worker threads.
func (api *PublicWhisperAPI) Start() error {
if api.whisper == nil {
@@ -138,7 +123,7 @@ func (api *PublicWhisperAPI) GenerateSymKey(name string) error {
}
// AddSymKey stores the key under the 'name' id.
-func (api *PublicWhisperAPI) AddSymKey(name string, key []byte) error {
+func (api *PublicWhisperAPI) AddSymKey(name string, key hexutil.Bytes) error {
if api.whisper == nil {
return whisperOffLineErr
}
@@ -166,16 +151,16 @@ func (api *PublicWhisperAPI) DeleteSymKey(name string) error {
// NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages.
// Returns the ID of the newly created Filter.
-func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (uint32, error) {
+func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) {
if api.whisper == nil {
- return 0, whisperOffLineErr
+ return "", whisperOffLineErr
}
- filter := whisperv5.Filter{
+ filter := Filter{
Src: crypto.ToECDSAPub(common.FromHex(args.From)),
KeySym: api.whisper.GetSymKey(args.KeyName),
PoW: args.PoW,
- Messages: make(map[common.Hash]*whisperv5.ReceivedMessage),
+ Messages: make(map[common.Hash]*ReceivedMessage),
AcceptP2P: args.AcceptP2P,
}
if len(filter.KeySym) > 0 {
@@ -183,64 +168,63 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (uint32, error) {
}
filter.Topics = append(filter.Topics, args.Topics...)
- if len(args.Topics) == 0 {
+ if len(args.Topics) == 0 && len(args.KeyName) != 0 {
info := "NewFilter: at least one topic must be specified"
glog.V(logger.Error).Infof(info)
- return 0, errors.New(info)
+ return "", errors.New(info)
}
if len(args.KeyName) != 0 && len(filter.KeySym) == 0 {
info := "NewFilter: key was not found by name: " + args.KeyName
glog.V(logger.Error).Infof(info)
- return 0, errors.New(info)
+ return "", errors.New(info)
}
if len(args.To) == 0 && len(filter.KeySym) == 0 {
info := "NewFilter: filter must contain either symmetric or asymmetric key"
glog.V(logger.Error).Infof(info)
- return 0, errors.New(info)
+ return "", errors.New(info)
}
if len(args.To) != 0 && len(filter.KeySym) != 0 {
info := "NewFilter: filter must not contain both symmetric and asymmetric key"
glog.V(logger.Error).Infof(info)
- return 0, errors.New(info)
+ return "", errors.New(info)
}
if len(args.To) > 0 {
dst := crypto.ToECDSAPub(common.FromHex(args.To))
- if !whisperv5.ValidatePublicKey(dst) {
+ if !ValidatePublicKey(dst) {
info := "NewFilter: Invalid 'To' address"
glog.V(logger.Error).Infof(info)
- return 0, errors.New(info)
+ return "", errors.New(info)
}
filter.KeyAsym = api.whisper.GetIdentity(string(args.To))
if filter.KeyAsym == nil {
info := "NewFilter: non-existent identity provided"
glog.V(logger.Error).Infof(info)
- return 0, errors.New(info)
+ return "", errors.New(info)
}
}
if len(args.From) > 0 {
- if !whisperv5.ValidatePublicKey(filter.Src) {
+ if !ValidatePublicKey(filter.Src) {
info := "NewFilter: Invalid 'From' address"
glog.V(logger.Error).Infof(info)
- return 0, errors.New(info)
+ return "", errors.New(info)
}
}
- id := api.whisper.Watch(&filter)
- return id, nil
+ return api.whisper.Watch(&filter)
}
// UninstallFilter disables and removes an existing filter.
-func (api *PublicWhisperAPI) UninstallFilter(filterId uint32) {
+func (api *PublicWhisperAPI) UninstallFilter(filterId string) {
api.whisper.Unwatch(filterId)
}
// GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval.
-func (api *PublicWhisperAPI) GetFilterChanges(filterId uint32) []WhisperMessage {
+func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage {
f := api.whisper.GetFilter(filterId)
if f != nil {
newMail := f.Retrieve()
@@ -250,14 +234,14 @@ func (api *PublicWhisperAPI) GetFilterChanges(filterId uint32) []WhisperMessage
}
// GetMessages retrieves all the known messages that match a specific filter.
-func (api *PublicWhisperAPI) GetMessages(filterId uint32) []WhisperMessage {
+func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage {
all := api.whisper.Messages(filterId)
return toWhisperMessages(all)
}
// toWhisperMessages converts a Whisper message to a RPC whisper message.
-func toWhisperMessages(messages []*whisperv5.ReceivedMessage) []WhisperMessage {
- msgs := make([]WhisperMessage, len(messages))
+func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage {
+ msgs := make([]*WhisperMessage, len(messages))
for i, msg := range messages {
msgs[i] = NewWhisperMessage(msg)
}
@@ -270,7 +254,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
return whisperOffLineErr
}
- params := whisperv5.MessageParams{
+ params := MessageParams{
TTL: args.TTL,
Dst: crypto.ToECDSAPub(common.FromHex(args.To)),
KeySym: api.whisper.GetSymKey(args.KeyName),
@@ -283,7 +267,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
if len(args.From) > 0 {
pub := crypto.ToECDSAPub(common.FromHex(args.From))
- if !whisperv5.ValidatePublicKey(pub) {
+ if !ValidatePublicKey(pub) {
info := "Post: Invalid 'From' address"
glog.V(logger.Error).Infof(info)
return errors.New(info)
@@ -297,8 +281,8 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
}
filter := api.whisper.GetFilter(args.FilterID)
- if filter == nil && args.FilterID > 0 {
- info := fmt.Sprintf("Post: wrong filter id %d", args.FilterID)
+ if filter == nil && len(args.FilterID) > 0 {
+ info := fmt.Sprintf("Post: wrong filter id %s", args.FilterID)
glog.V(logger.Error).Infof(info)
return errors.New(info)
}
@@ -311,10 +295,10 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
if params.Src == nil && filter.Src != nil {
params.Src = filter.KeyAsym
}
- if (params.Topic == whisperv5.TopicType{}) {
+ if (params.Topic == TopicType{}) {
sz := len(filter.Topics)
if sz < 1 {
- info := fmt.Sprintf("Post: no topics in filter # %d", args.FilterID)
+ info := fmt.Sprintf("Post: no topics in filter # %s", args.FilterID)
glog.V(logger.Error).Infof(info)
return errors.New(info)
} else if sz == 1 {
@@ -347,7 +331,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
}
if len(args.To) > 0 {
- if !whisperv5.ValidatePublicKey(params.Dst) {
+ if !ValidatePublicKey(params.Dst) {
info := "Post: Invalid 'To' address"
glog.V(logger.Error).Infof(info)
return errors.New(info)
@@ -355,18 +339,18 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
}
// encrypt and send
- message := whisperv5.NewSentMessage(¶ms)
+ message := NewSentMessage(¶ms)
envelope, err := message.Wrap(¶ms)
if err != nil {
glog.V(logger.Error).Infof(err.Error())
return err
}
- if len(envelope.Data) > whisperv5.MaxMessageLength {
+ if len(envelope.Data) > MaxMessageLength {
info := "Post: message is too big"
glog.V(logger.Error).Infof(info)
return errors.New(info)
}
- if (envelope.Topic == whisperv5.TopicType{} && envelope.IsSymmetric()) {
+ if (envelope.Topic == TopicType{} && envelope.IsSymmetric()) {
info := "Post: topic is missing for symmetric encryption"
glog.V(logger.Error).Infof(info)
return errors.New(info)
@@ -380,26 +364,26 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
}
type PostArgs struct {
- TTL uint32 `json:"ttl"`
- From string `json:"from"`
- To string `json:"to"`
- KeyName string `json:"keyname"`
- Topic whisperv5.TopicType `json:"topic"`
- Padding hexutil.Bytes `json:"padding"`
- Payload hexutil.Bytes `json:"payload"`
- WorkTime uint32 `json:"worktime"`
- PoW float64 `json:"pow"`
- FilterID uint32 `json:"filterID"`
- PeerID hexutil.Bytes `json:"peerID"`
+ TTL uint32 `json:"ttl"`
+ From string `json:"from"`
+ To string `json:"to"`
+ KeyName string `json:"keyname"`
+ Topic TopicType `json:"topic"`
+ Padding hexutil.Bytes `json:"padding"`
+ Payload hexutil.Bytes `json:"payload"`
+ WorkTime uint32 `json:"worktime"`
+ PoW float64 `json:"pow"`
+ FilterID string `json:"filterID"`
+ PeerID hexutil.Bytes `json:"peerID"`
}
type WhisperFilterArgs struct {
- To string
- From string
- KeyName string
- PoW float64
- Topics []whisperv5.TopicType
- AcceptP2P bool
+ To string `json:"to"`
+ From string `json:"from"`
+ KeyName string `json:"keyname"`
+ PoW float64 `json:"pow"`
+ Topics []TopicType `json:"topics"`
+ AcceptP2P bool `json:"p2p"`
}
// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
@@ -412,7 +396,7 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
KeyName string `json:"keyname"`
PoW float64 `json:"pow"`
Topics []interface{} `json:"topics"`
- AcceptP2P bool `json:"acceptP2P"`
+ AcceptP2P bool `json:"p2p"`
}
if err := json.Unmarshal(b, &obj); err != nil {
return err
@@ -437,13 +421,13 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
return fmt.Errorf("topic[%d] is not a string", i)
}
}
- topicsDecoded := make([]whisperv5.TopicType, len(topics))
+ topicsDecoded := make([]TopicType, len(topics))
for j, s := range topics {
x := common.FromHex(s)
- if x == nil || len(x) != whisperv5.TopicLength {
+ if x == nil || len(x) != TopicLength {
return fmt.Errorf("topic[%d] is invalid", j)
}
- topicsDecoded[j] = whisperv5.BytesToTopic(x)
+ topicsDecoded[j] = BytesToTopic(x)
}
args.Topics = topicsDecoded
}
@@ -453,6 +437,7 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
// WhisperMessage is the RPC representation of a whisper message.
type WhisperMessage struct {
+ Topic string `json:"topic"`
Payload string `json:"payload"`
Padding string `json:"padding"`
From string `json:"from"`
@@ -464,15 +449,22 @@ type WhisperMessage struct {
}
// NewWhisperMessage converts an internal message into an API version.
-func NewWhisperMessage(message *whisperv5.ReceivedMessage) WhisperMessage {
- return WhisperMessage{
+func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
+ msg := WhisperMessage{
+ Topic: common.ToHex(message.Topic[:]),
Payload: common.ToHex(message.Payload),
Padding: common.ToHex(message.Padding),
- From: common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())),
- To: common.ToHex(crypto.FromECDSAPub(message.Dst)),
Sent: message.Sent,
TTL: message.TTL,
PoW: message.PoW,
Hash: common.ToHex(message.EnvelopeHash.Bytes()),
}
+
+ if message.Dst != nil {
+ msg.To = common.ToHex(crypto.FromECDSAPub(message.Dst))
+ }
+ if isMessageSigned(message.Raw[0]) {
+ msg.From = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey()))
+ }
+ return &msg
}
diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go
index b82a82468..70c7008a7 100644
--- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go
+++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go
@@ -55,8 +55,8 @@ const (
saltLength = 12
AESNonceMaxLength = 12
- MaxMessageLength = 0xFFFF // todo: remove this restriction after testing. this should be regulated by PoW.
- MinimumPoW = 1.0 // todo: review after testing.
+ MaxMessageLength = 0x0FFFFF // todo: remove this restriction after testing. this should be regulated by PoW.
+ MinimumPoW = 10.0 // todo: review after testing.
padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature
padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility
diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go
index 1b976705d..8812ae207 100644
--- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go
+++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go
@@ -116,12 +116,16 @@ func (e *Envelope) Seal(options *MessageParams) error {
}
if target > 0 && bestBit < target {
- return errors.New("Failed to reach the PoW target")
+ return errors.New("Failed to reach the PoW target, insufficient work time")
}
return nil
}
+func (e *Envelope) size() int {
+ return len(e.Data) + len(e.Version) + len(e.AESNonce) + len(e.Salt) + 20
+}
+
func (e *Envelope) PoW() float64 {
if e.pow == 0 {
e.calculatePoW(0)
@@ -137,14 +141,14 @@ func (e *Envelope) calculatePoW(diff uint32) {
h = crypto.Keccak256(buf)
firstBit := common.FirstBitSet(common.BigD(h))
x := math.Pow(2, float64(firstBit))
- x /= float64(len(e.Data)) // we only count e.Data, other variable-sized members are checked in Whisper.add()
+ x /= float64(e.size())
x /= float64(e.TTL + diff)
e.pow = x
}
func (e *Envelope) powToFirstBit(pow float64) int {
x := pow
- x *= float64(len(e.Data))
+ x *= float64(e.size())
x *= float64(e.TTL)
bits := math.Log2(x)
bits = math.Ceil(bits)
diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go
index a386aa164..832ebe3f6 100644
--- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go
+++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go
@@ -18,6 +18,8 @@ package whisperv5
import (
"crypto/ecdsa"
+ crand "crypto/rand"
+ "fmt"
"sync"
"github.com/ethereum/go-ethereum/common"
@@ -39,20 +41,41 @@ type Filter struct {
}
type Filters struct {
- id uint32 // can contain any value except zero
- watchers map[uint32]*Filter
+ watchers map[string]*Filter
whisper *Whisper
mutex sync.RWMutex
}
func NewFilters(w *Whisper) *Filters {
return &Filters{
- watchers: make(map[uint32]*Filter),
+ watchers: make(map[string]*Filter),
whisper: w,
}
}
-func (fs *Filters) Install(watcher *Filter) uint32 {
+func (fs *Filters) generateRandomID() (id string, err error) {
+ buf := make([]byte, 20)
+ for i := 0; i < 3; i++ {
+ _, err = crand.Read(buf)
+ if err != nil {
+ continue
+ }
+ if !validateSymmetricKey(buf) {
+ err = fmt.Errorf("error in generateRandomID: crypto/rand failed to generate random data")
+ continue
+ }
+ id = common.Bytes2Hex(buf)
+ if fs.watchers[id] != nil {
+ err = fmt.Errorf("error in generateRandomID: generated same ID twice")
+ continue
+ }
+ return id, err
+ }
+
+ return "", err
+}
+
+func (fs *Filters) Install(watcher *Filter) (string, error) {
if watcher.Messages == nil {
watcher.Messages = make(map[common.Hash]*ReceivedMessage)
}
@@ -60,21 +83,23 @@ func (fs *Filters) Install(watcher *Filter) uint32 {
fs.mutex.Lock()
defer fs.mutex.Unlock()
- fs.id++
- fs.watchers[fs.id] = watcher
- return fs.id
+ id, err := fs.generateRandomID()
+ if err == nil {
+ fs.watchers[id] = watcher
+ }
+ return id, err
}
-func (fs *Filters) Uninstall(id uint32) {
+func (fs *Filters) Uninstall(id string) {
fs.mutex.Lock()
defer fs.mutex.Unlock()
delete(fs.watchers, id)
}
-func (fs *Filters) Get(i uint32) *Filter {
+func (fs *Filters) Get(id string) *Filter {
fs.mutex.RLock()
defer fs.mutex.RUnlock()
- return fs.watchers[i]
+ return fs.watchers[id]
}
func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go
index ff31aab2d..2a6ff5f40 100644
--- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go
+++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go
@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/crypto/pbkdf2"
set "gopkg.in/fatih/set.v0"
)
@@ -65,7 +66,7 @@ type Whisper struct {
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
// Param s should be passed if you want to implement mail server, otherwise nil.
-func NewWhisper(server MailServer) *Whisper {
+func New() *Whisper {
whisper := &Whisper{
privateKeys: make(map[string]*ecdsa.PrivateKey),
symKeys: make(map[string][]byte),
@@ -73,7 +74,6 @@ func NewWhisper(server MailServer) *Whisper {
messages: make(map[common.Hash]*ReceivedMessage),
expirations: make(map[uint32]*set.SetNonTS),
peers: make(map[*Peer]struct{}),
- mailServer: server,
messageQueue: make(chan *Envelope, messageQueueLimit),
p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
quit: make(chan struct{}),
@@ -91,6 +91,22 @@ func NewWhisper(server MailServer) *Whisper {
return whisper
}
+// APIs returns the RPC descriptors the Whisper implementation offers
+func (w *Whisper) APIs() []rpc.API {
+ return []rpc.API{
+ {
+ Namespace: ProtocolName,
+ Version: ProtocolVersionStr,
+ Service: NewPublicWhisperAPI(w),
+ Public: true,
+ },
+ }
+}
+
+func (w *Whisper) RegisterServer(server MailServer) {
+ w.mailServer = server
+}
+
// Protocols returns the whisper sub-protocols ran by this particular client.
func (w *Whisper) Protocols() []p2p.Protocol {
return []p2p.Protocol{w.protocol}
@@ -256,16 +272,16 @@ func (w *Whisper) GetSymKey(name string) []byte {
// Watch installs a new message handler to run in case a matching packet arrives
// from the whisper network.
-func (w *Whisper) Watch(f *Filter) uint32 {
+func (w *Whisper) Watch(f *Filter) (string, error) {
return w.filters.Install(f)
}
-func (w *Whisper) GetFilter(id uint32) *Filter {
+func (w *Whisper) GetFilter(id string) *Filter {
return w.filters.Get(id)
}
// Unwatch removes an installed message handler.
-func (w *Whisper) Unwatch(id uint32) {
+func (w *Whisper) Unwatch(id string) {
w.filters.Uninstall(id)
}
@@ -559,7 +575,7 @@ func (w *Whisper) Envelopes() []*Envelope {
}
// Messages retrieves all the decrypted messages matching a filter id.
-func (w *Whisper) Messages(id uint32) []*ReceivedMessage {
+func (w *Whisper) Messages(id string) []*ReceivedMessage {
result := make([]*ReceivedMessage, 0)
w.poolMu.RLock()
defer w.poolMu.RUnlock()