mirror of
https://github.com/status-im/status-go.git
synced 2025-02-16 08:50:09 +00:00
Merge pull request #41 from farazdagi/feature/list-accounts
Updates eth.accounts and personal.listAccounts to rely on HD keys
This commit is contained in:
commit
6d59ebf0af
1
.gitignore
vendored
1
.gitignore
vendored
@ -24,6 +24,7 @@
|
|||||||
# used by the Makefile
|
# used by the Makefile
|
||||||
/build/_workspace/
|
/build/_workspace/
|
||||||
/build/bin/
|
/build/bin/
|
||||||
|
/vendor/github.com/karalabe/xgo
|
||||||
|
|
||||||
# travis
|
# travis
|
||||||
profile.tmp
|
profile.tmp
|
||||||
|
126
geth/accounts.go
126
geth/accounts.go
@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
@ -21,7 +22,7 @@ var (
|
|||||||
ErrInvalidMasterKeyCreated = errors.New("can not create master extended key")
|
ErrInvalidMasterKeyCreated = errors.New("can not create master extended key")
|
||||||
)
|
)
|
||||||
|
|
||||||
// createAccount creates an internal geth account
|
// CreateAccount creates an internal geth account
|
||||||
// BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
|
// BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
|
||||||
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
||||||
// sub-account derivations)
|
// sub-account derivations)
|
||||||
@ -48,7 +49,7 @@ func CreateAccount(password string) (address, pubKey, mnemonic string, err error
|
|||||||
return address, pubKey, mnemonic, nil
|
return address, pubKey, mnemonic, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createChildAccount creates sub-account for an account identified by parent address.
|
// CreateChildAccount creates sub-account for an account identified by parent address.
|
||||||
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
||||||
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
||||||
func CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
func CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
||||||
@ -58,20 +59,20 @@ func CreateChildAccount(parentAddress, password string) (address, pubKey string,
|
|||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if parentAddress == "" { // by default derive from currently selected account
|
if parentAddress == "" && nodeManager.SelectedAccount != nil { // derive from selected account by default
|
||||||
parentAddress = nodeManager.SelectedAddress
|
parentAddress = string(nodeManager.SelectedAccount.Address.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
if parentAddress == "" {
|
if parentAddress == "" {
|
||||||
return "", "", ErrNoAccountSelected
|
return "", "", ErrNoAccountSelected
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure that given password can decrypt key associated with a given parent address
|
|
||||||
account, err := utils.MakeAddress(accountManager, parentAddress)
|
account, err := utils.MakeAddress(accountManager, parentAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", ErrAddressToAccountMappingFailure
|
return "", "", ErrAddressToAccountMappingFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure that given password can decrypt key associated with a given parent address
|
||||||
account, accountKey, err := accountManager.AccountDecryptedKey(account, password)
|
account, accountKey, err := accountManager.AccountDecryptedKey(account, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("%s: %v", ErrAccountToKeyMappingFailure.Error(), err)
|
return "", "", fmt.Errorf("%s: %v", ErrAccountToKeyMappingFailure.Error(), err)
|
||||||
@ -88,6 +89,7 @@ func CreateChildAccount(parentAddress, password string) (address, pubKey string,
|
|||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
accountManager.IncSubAccountIndex(account, password)
|
accountManager.IncSubAccountIndex(account, password)
|
||||||
|
accountKey.SubAccountIndex++
|
||||||
|
|
||||||
// import derived key into account keystore
|
// import derived key into account keystore
|
||||||
address, pubKey, err = importExtendedKey(childKey, password)
|
address, pubKey, err = importExtendedKey(childKey, password)
|
||||||
@ -95,10 +97,15 @@ func CreateChildAccount(parentAddress, password string) (address, pubKey string,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update in-memory selected account
|
||||||
|
if nodeManager.SelectedAccount != nil {
|
||||||
|
nodeManager.SelectedAccount.AccountKey = accountKey
|
||||||
|
}
|
||||||
|
|
||||||
return address, pubKey, nil
|
return address, pubKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// recoverAccount re-creates master key using given details.
|
// RecoverAccount re-creates master key using given details.
|
||||||
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
||||||
func RecoverAccount(password, mnemonic string) (address, pubKey string, err error) {
|
func RecoverAccount(password, mnemonic string) (address, pubKey string, err error) {
|
||||||
// re-create extended key (see BIP32)
|
// re-create extended key (see BIP32)
|
||||||
@ -117,7 +124,7 @@ func RecoverAccount(password, mnemonic string) (address, pubKey string, err erro
|
|||||||
return address, pubKey, nil
|
return address, pubKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
||||||
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
||||||
// all previous identities are removed).
|
// all previous identities are removed).
|
||||||
func SelectAccount(address, password string) error {
|
func SelectAccount(address, password string) error {
|
||||||
@ -146,13 +153,21 @@ func SelectAccount(address, password string) error {
|
|||||||
return ErrWhisperIdentityInjectionFailure
|
return ErrWhisperIdentityInjectionFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
// persist address for easier recovery of currently selected key (from Whisper)
|
// persist account key for easier recovery of currently selected key
|
||||||
nodeManager.SelectedAddress = address
|
subAccounts, err := findSubAccounts(accountKey.ExtendedKey, accountKey.SubAccountIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nodeManager.SelectedAccount = &SelectedExtKey{
|
||||||
|
Address: account.Address,
|
||||||
|
AccountKey: accountKey,
|
||||||
|
SubAccounts: subAccounts,
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// logout clears whisper identities
|
// Logout clears whisper identities
|
||||||
func Logout() error {
|
func Logout() error {
|
||||||
nodeManager := GetNodeManager()
|
nodeManager := GetNodeManager()
|
||||||
whisperService, err := nodeManager.WhisperService()
|
whisperService, err := nodeManager.WhisperService()
|
||||||
@ -165,12 +180,12 @@ func Logout() error {
|
|||||||
return fmt.Errorf("%s: %v", ErrWhisperClearIdentitiesFailure, err)
|
return fmt.Errorf("%s: %v", ErrWhisperClearIdentitiesFailure, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeManager.SelectedAddress = ""
|
nodeManager.SelectedAccount = nil
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unlockAccount unlocks an existing account for a certain duration and
|
// UnlockAccount unlocks an existing account for a certain duration and
|
||||||
// inject the account as a whisper identity if the account was created as
|
// inject the account as a whisper identity if the account was created as
|
||||||
// a whisper enabled account
|
// a whisper enabled account
|
||||||
func UnlockAccount(address, password string, seconds int) error {
|
func UnlockAccount(address, password string, seconds int) error {
|
||||||
@ -201,3 +216,90 @@ func importExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, p
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onAccountsListRequest(entities []accounts.Account) []accounts.Account {
|
||||||
|
nodeManager := GetNodeManager()
|
||||||
|
|
||||||
|
if nodeManager.SelectedAccount == nil {
|
||||||
|
return []accounts.Account{}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshSelectedAccount()
|
||||||
|
|
||||||
|
filtered := make([]accounts.Account, 0)
|
||||||
|
for _, account := range entities {
|
||||||
|
// main account
|
||||||
|
if nodeManager.SelectedAccount.Address.Hex() == account.Address.Hex() {
|
||||||
|
filtered = append(filtered, account)
|
||||||
|
} else {
|
||||||
|
// sub accounts
|
||||||
|
for _, subAccount := range nodeManager.SelectedAccount.SubAccounts {
|
||||||
|
if subAccount.Address.Hex() == account.Address.Hex() {
|
||||||
|
filtered = append(filtered, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
// refreshSelectedAccount re-populates list of sub-accounts of the currently selected account (if any)
|
||||||
|
func refreshSelectedAccount() {
|
||||||
|
nodeManager := GetNodeManager()
|
||||||
|
|
||||||
|
if nodeManager.SelectedAccount == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accountKey := nodeManager.SelectedAccount.AccountKey
|
||||||
|
if accountKey == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-populate list of sub-accounts
|
||||||
|
subAccounts, err := findSubAccounts(accountKey.ExtendedKey, accountKey.SubAccountIndex)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeManager.SelectedAccount = &SelectedExtKey{
|
||||||
|
Address: nodeManager.SelectedAccount.Address,
|
||||||
|
AccountKey: nodeManager.SelectedAccount.AccountKey,
|
||||||
|
SubAccounts: subAccounts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// findSubAccounts traverses cached accounts and adds as a sub-accounts any
|
||||||
|
// that belong to the currently selected account.
|
||||||
|
// The extKey is CKD#2 := root of sub-accounts of the main account
|
||||||
|
func findSubAccounts(extKey *extkeys.ExtendedKey, subAccountIndex uint32) ([]accounts.Account, error) {
|
||||||
|
nodeManager := GetNodeManager()
|
||||||
|
accountManager, err := nodeManager.AccountManager()
|
||||||
|
if err != nil {
|
||||||
|
return []accounts.Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
subAccounts := make([]accounts.Account, 0)
|
||||||
|
if extKey.Depth == 5 { // CKD#2 level
|
||||||
|
// gather possible sub-account addresses
|
||||||
|
subAccountAddresses := make([]common.Address, 0)
|
||||||
|
for i := uint32(0); i < subAccountIndex; i++ {
|
||||||
|
childKey, err := extKey.Child(i)
|
||||||
|
if err != nil {
|
||||||
|
return []accounts.Account{}, err
|
||||||
|
}
|
||||||
|
subAccountAddresses = append(subAccountAddresses, crypto.PubkeyToAddress(childKey.ToECDSA().PublicKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if any of the gathered addresses actually exist in cached accounts list
|
||||||
|
for _, cachedAccount := range accountManager.Accounts() {
|
||||||
|
for _, possibleAddress := range subAccountAddresses {
|
||||||
|
if possibleAddress.Hex() == cachedAccount.Address.Hex() {
|
||||||
|
subAccounts = append(subAccounts, cachedAccount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return subAccounts, nil
|
||||||
|
}
|
||||||
|
@ -14,6 +14,116 @@ import (
|
|||||||
"github.com/status-im/status-go/geth"
|
"github.com/status-im/status-go/geth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestAccountsList(t *testing.T) {
|
||||||
|
err := geth.PrepareTestNode()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
les, err := geth.GetNodeManager().LightEthereumService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected LES service: %v", err)
|
||||||
|
}
|
||||||
|
accounts := les.StatusBackend.AccountManager().Accounts()
|
||||||
|
geth.Logout()
|
||||||
|
|
||||||
|
// make sure that we start with empty accounts list (nobody has logged in yet)
|
||||||
|
if len(accounts) != 0 {
|
||||||
|
t.Error("accounts returned, while there should be none (we haven't logged in yet)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an account
|
||||||
|
address, _, _, err := geth.CreateAccount(newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not create account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that there is still no accounts returned
|
||||||
|
accounts = les.StatusBackend.AccountManager().Accounts()
|
||||||
|
if len(accounts) != 0 {
|
||||||
|
t.Error("accounts returned, while there should be none (we haven't logged in yet)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// select account (sub-accounts will be created for this key)
|
||||||
|
err = geth.SelectAccount(address, newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed: could not select account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// at this point main account should show up
|
||||||
|
accounts = les.StatusBackend.AccountManager().Accounts()
|
||||||
|
if len(accounts) != 1 {
|
||||||
|
t.Error("exactly single account is expected (main account)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(accounts[0].Address.Hex()) != "0x"+address {
|
||||||
|
t.Errorf("main account is not retured as the first key: got %s, expected %s",
|
||||||
|
accounts[0].Address.Hex(), "0x"+address)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create sub-account 1
|
||||||
|
subAccount1, subPubKey1, err := geth.CreateChildAccount("", newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("cannot create sub-account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we expect to see both main account and sub-account 1
|
||||||
|
accounts = les.StatusBackend.AccountManager().Accounts()
|
||||||
|
if len(accounts) != 2 {
|
||||||
|
t.Error("exactly 2 accounts are expected (main + sub-account 1)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(accounts[0].Address.Hex()) != "0x"+address {
|
||||||
|
t.Errorf("main account is not retured as the first key: got %s, expected %s",
|
||||||
|
accounts[0].Address.Hex(), "0x"+address)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(accounts[1].Address.Hex()) != "0x"+subAccount1 {
|
||||||
|
t.Errorf("subAcount1 not returned: got %s, expected %s", accounts[1].Address.Hex(), "0x"+subAccount1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create sub-account 2, index automatically progresses
|
||||||
|
subAccount2, subPubKey2, err := geth.CreateChildAccount("", newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("cannot create sub-account: %v", err)
|
||||||
|
}
|
||||||
|
if subAccount1 == subAccount2 || subPubKey1 == subPubKey2 {
|
||||||
|
t.Error("sub-account index auto-increament failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally, all 3 accounts should show up (main account, sub-accounts 1 and 2)
|
||||||
|
accounts = les.StatusBackend.AccountManager().Accounts()
|
||||||
|
if len(accounts) != 3 {
|
||||||
|
t.Errorf("unexpected number of accounts: expected %d, got %d", 3, len(accounts))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(accounts[0].Address.Hex()) != "0x"+address {
|
||||||
|
t.Errorf("main account is not retured as the first key: got %s, expected %s",
|
||||||
|
accounts[0].Address.Hex(), "0x"+address)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
subAccount1MatchesKey1 := string(accounts[1].Address.Hex()) != "0x"+subAccount1
|
||||||
|
subAccount1MatchesKey2 := string(accounts[2].Address.Hex()) != "0x"+subAccount1
|
||||||
|
if !subAccount1MatchesKey1 && !subAccount1MatchesKey2 {
|
||||||
|
t.Errorf("subAcount1 not returned: got %s, expected %s", accounts[1].Address.Hex(), "0x"+subAccount1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
subAccount2MatchesKey1 := string(accounts[1].Address.Hex()) != "0x"+subAccount2
|
||||||
|
subAccount2MatchesKey2 := string(accounts[2].Address.Hex()) != "0x"+subAccount2
|
||||||
|
if !subAccount2MatchesKey1 && !subAccount2MatchesKey2 {
|
||||||
|
t.Errorf("subAcount2 not returned: got %s, expected %s", accounts[2].Address.Hex(), "0x"+subAccount1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateChildAccount(t *testing.T) {
|
func TestCreateChildAccount(t *testing.T) {
|
||||||
err := geth.PrepareTestNode()
|
err := geth.PrepareTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -21,6 +131,8 @@ func TestCreateChildAccount(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
geth.Logout() // to make sure that we start with empty account (which might get populated during previous tests)
|
||||||
|
|
||||||
accountManager, err := geth.GetNodeManager().AccountManager()
|
accountManager, err := geth.GetNodeManager().AccountManager()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
11
geth/node.go
11
geth/node.go
@ -54,12 +54,18 @@ var (
|
|||||||
ErrNodeStartFailure = errors.New("could not create the in-memory node object")
|
ErrNodeStartFailure = errors.New("could not create the in-memory node object")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SelectedExtKey struct {
|
||||||
|
Address common.Address
|
||||||
|
AccountKey *accounts.Key
|
||||||
|
SubAccounts []accounts.Account
|
||||||
|
}
|
||||||
|
|
||||||
type NodeManager struct {
|
type NodeManager struct {
|
||||||
currentNode *node.Node // currently running geth node
|
currentNode *node.Node // currently running geth node
|
||||||
ctx *cli.Context // the CLI context used to start the geth node
|
ctx *cli.Context // the CLI context used to start the geth node
|
||||||
lightEthereum *les.LightEthereum // LES service
|
lightEthereum *les.LightEthereum // LES service
|
||||||
accountManager *accounts.Manager // the account manager attached to the currentNode
|
accountManager *accounts.Manager // the account manager attached to the currentNode
|
||||||
SelectedAddress string // address of the account that was processed during the last call to SelectAccount()
|
SelectedAccount *SelectedExtKey // account that was processed during the last call to SelectAccount()
|
||||||
whisperService *whisper.Whisper // Whisper service
|
whisperService *whisper.Whisper // Whisper service
|
||||||
client *rpc.ClientRestartWrapper // RPC client
|
client *rpc.ClientRestartWrapper // RPC client
|
||||||
nodeStarted chan struct{} // channel to wait for node to start
|
nodeStarted chan struct{} // channel to wait for node to start
|
||||||
@ -160,7 +166,10 @@ func (m *NodeManager) RunNode() {
|
|||||||
if err := m.currentNode.Service(&m.lightEthereum); err != nil {
|
if err := m.currentNode.Service(&m.lightEthereum); err != nil {
|
||||||
glog.V(logger.Warn).Infoln("cannot get light ethereum service:", err)
|
glog.V(logger.Warn).Infoln("cannot get light ethereum service:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup handlers
|
||||||
m.lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest)
|
m.lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest)
|
||||||
|
m.lightEthereum.StatusBackend.SetAccountsFilterHandler(onAccountsListRequest)
|
||||||
|
|
||||||
m.client = rpc.NewClientRestartWrapper(func() *rpc.Client {
|
m.client = rpc.NewClientRestartWrapper(func() *rpc.Client {
|
||||||
client, err := m.currentNode.Attach()
|
client, err := m.currentNode.Attach()
|
||||||
|
@ -21,10 +21,18 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
syncRequired := false
|
||||||
|
if _, err := os.Stat(geth.TestDataDir); os.IsNotExist(err) {
|
||||||
|
syncRequired = true
|
||||||
|
}
|
||||||
// make sure you panic if node start signal is not received
|
// make sure you panic if node start signal is not received
|
||||||
signalRecieved := make(chan struct{}, 1)
|
signalRecieved := make(chan struct{}, 1)
|
||||||
abortPanic := make(chan bool, 1)
|
abortPanic := make(chan bool, 1)
|
||||||
geth.PanicAfter(10*time.Second, abortPanic, "TestNodeSetup")
|
if syncRequired {
|
||||||
|
geth.PanicAfter(geth.TestNodeSyncSeconds*time.Second, abortPanic, "TestNodeSetup")
|
||||||
|
} else {
|
||||||
|
geth.PanicAfter(10*time.Second, abortPanic, "TestNodeSetup")
|
||||||
|
}
|
||||||
|
|
||||||
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
if jsonEvent == `{"type":"node.started","event":{}}` {
|
if jsonEvent == `{"type":"node.started","event":{}}` {
|
||||||
|
@ -22,8 +22,8 @@ import (
|
|||||||
var muPrepareTestNode sync.Mutex
|
var muPrepareTestNode sync.Mutex
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testDataDir = "../.ethereumtest"
|
TestDataDir = "../.ethereumtest"
|
||||||
testNodeSyncSeconds = 300
|
TestNodeSyncSeconds = 300
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeNotificationHandler func(jsonEvent string)
|
type NodeNotificationHandler func(jsonEvent string)
|
||||||
@ -90,19 +90,19 @@ func PrepareTestNode() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
syncRequired := false
|
syncRequired := false
|
||||||
if _, err := os.Stat(testDataDir); os.IsNotExist(err) {
|
if _, err := os.Stat(TestDataDir); os.IsNotExist(err) {
|
||||||
syncRequired = true
|
syncRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare node directory
|
// prepare node directory
|
||||||
dataDir, err := PreprocessDataDir(testDataDir)
|
dataDir, err := PreprocessDataDir(TestDataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infoln("make node failed:", err)
|
glog.V(logger.Warn).Infoln("make node failed:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// import test account (with test ether on it)
|
// import test account (with test ether on it)
|
||||||
dst := filepath.Join(testDataDir, "testnet", "keystore", "test-account.pk")
|
dst := filepath.Join(TestDataDir, "testnet", "keystore", "test-account.pk")
|
||||||
if _, err := os.Stat(dst); os.IsNotExist(err) {
|
if _, err := os.Stat(dst); os.IsNotExist(err) {
|
||||||
err = CopyFile(dst, filepath.Join("../data", "test-account.pk"))
|
err = CopyFile(dst, filepath.Join("../data", "test-account.pk"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -132,8 +132,8 @@ func PrepareTestNode() (err error) {
|
|||||||
manager.AddPeer("enode://409772c7dea96fa59a912186ad5bcdb5e51b80556b3fe447d940f99d9eaadb51d4f0ffedb68efad232b52475dd7bd59b51cee99968b3cc79e2d5684b33c4090c@139.162.166.59:30303")
|
manager.AddPeer("enode://409772c7dea96fa59a912186ad5bcdb5e51b80556b3fe447d940f99d9eaadb51d4f0ffedb68efad232b52475dd7bd59b51cee99968b3cc79e2d5684b33c4090c@139.162.166.59:30303")
|
||||||
|
|
||||||
if syncRequired {
|
if syncRequired {
|
||||||
glog.V(logger.Warn).Infof("Sync is required, it will take %d seconds", testNodeSyncSeconds)
|
glog.V(logger.Warn).Infof("Sync is required, it will take %d seconds", TestNodeSyncSeconds)
|
||||||
time.Sleep(testNodeSyncSeconds * time.Second) // LES syncs headers, so that we are up do date when it is done
|
time.Sleep(TestNodeSyncSeconds * time.Second) // LES syncs headers, so that we are up do date when it is done
|
||||||
} else {
|
} else {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ func PrepareTestNode() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RemoveTestNode() {
|
func RemoveTestNode() {
|
||||||
err := os.RemoveAll(testDataDir)
|
err := os.RemoveAll(TestDataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infof("could not clean up temporary datadir")
|
glog.V(logger.Warn).Infof("could not clean up temporary datadir")
|
||||||
}
|
}
|
||||||
|
14
vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go
generated
vendored
14
vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go
generated
vendored
@ -199,6 +199,11 @@ func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI {
|
|||||||
|
|
||||||
// Accounts returns the collection of accounts this node manages
|
// Accounts returns the collection of accounts this node manages
|
||||||
func (s *PublicAccountAPI) Accounts() []accounts.Account {
|
func (s *PublicAccountAPI) Accounts() []accounts.Account {
|
||||||
|
backend := GetStatusBackend()
|
||||||
|
if backend != nil {
|
||||||
|
return statusBackend.am.Accounts()
|
||||||
|
}
|
||||||
|
|
||||||
return s.am.Accounts()
|
return s.am.Accounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +225,14 @@ func NewPrivateAccountAPI(b Backend) *PrivateAccountAPI {
|
|||||||
|
|
||||||
// ListAccounts will return a list of addresses for accounts this node manages.
|
// ListAccounts will return a list of addresses for accounts this node manages.
|
||||||
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
|
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
|
||||||
accounts := s.am.Accounts()
|
var accounts []accounts.Account
|
||||||
|
backend := GetStatusBackend()
|
||||||
|
if backend != nil {
|
||||||
|
accounts = statusBackend.am.Accounts()
|
||||||
|
} else {
|
||||||
|
accounts = s.am.Accounts()
|
||||||
|
}
|
||||||
|
|
||||||
addresses := make([]common.Address, len(accounts))
|
addresses := make([]common.Address, len(accounts))
|
||||||
for i, acc := range accounts {
|
for i, acc := range accounts {
|
||||||
addresses[i] = acc.Address
|
addresses[i] = acc.Address
|
||||||
|
41
vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go
generated
vendored
41
vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go
generated
vendored
@ -1,6 +1,8 @@
|
|||||||
package ethapi
|
package ethapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/les/status"
|
"github.com/ethereum/go-ethereum/les/status"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
@ -8,29 +10,40 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StatusBackend implements les.StatusBackend with direct calls to Ethereum
|
// StatusBackend exposes Ethereum internals to support custom semantics in status-go bindings
|
||||||
// internals to support calls from status-go bindings (to internal packages e.g. ethapi)
|
|
||||||
type StatusBackend struct {
|
type StatusBackend struct {
|
||||||
eapi *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
|
eapi *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
|
||||||
bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data
|
bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data
|
||||||
txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
|
txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
|
||||||
|
|
||||||
txQueue *status.TxQueue
|
txQueue *status.TxQueue
|
||||||
|
am *status.AccountManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var statusBackend *StatusBackend
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
// NewStatusBackend creates a new backend using an existing Ethereum object.
|
// NewStatusBackend creates a new backend using an existing Ethereum object.
|
||||||
func NewStatusBackend(apiBackend Backend) *StatusBackend {
|
func NewStatusBackend(apiBackend Backend) *StatusBackend {
|
||||||
glog.V(logger.Info).Infof("Status backend service started")
|
glog.V(logger.Info).Infof("Status backend service started")
|
||||||
backend := &StatusBackend{
|
once.Do(func() {
|
||||||
eapi: NewPublicEthereumAPI(apiBackend),
|
statusBackend = &StatusBackend{
|
||||||
bcapi: NewPublicBlockChainAPI(apiBackend),
|
eapi: NewPublicEthereumAPI(apiBackend),
|
||||||
txapi: NewPublicTransactionPoolAPI(apiBackend),
|
bcapi: NewPublicBlockChainAPI(apiBackend),
|
||||||
txQueue: status.NewTransactionQueue(),
|
txapi: NewPublicTransactionPoolAPI(apiBackend),
|
||||||
}
|
txQueue: status.NewTransactionQueue(),
|
||||||
|
am: status.NewAccountManager(apiBackend.AccountManager()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
go backend.transactionQueueForwardingLoop()
|
go statusBackend.transactionQueueForwardingLoop()
|
||||||
|
|
||||||
return backend
|
return statusBackend
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatusBackend exposes backend singleton instance
|
||||||
|
func GetStatusBackend() *StatusBackend {
|
||||||
|
return statusBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *StatusBackend) SetTransactionQueueHandler(fn status.EnqueuedTxHandler) {
|
func (b *StatusBackend) SetTransactionQueueHandler(fn status.EnqueuedTxHandler) {
|
||||||
@ -41,6 +54,14 @@ func (b *StatusBackend) TransactionQueue() *status.TxQueue {
|
|||||||
return b.txQueue
|
return b.txQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *StatusBackend) SetAccountsFilterHandler(fn status.AccountsFilterHandler) {
|
||||||
|
b.am.SetAccountsFilterHandler(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *StatusBackend) AccountManager() *status.AccountManager {
|
||||||
|
return b.am
|
||||||
|
}
|
||||||
|
|
||||||
// SendTransaction wraps call to PublicTransactionPoolAPI.SendTransaction
|
// SendTransaction wraps call to PublicTransactionPoolAPI.SendTransaction
|
||||||
func (b *StatusBackend) SendTransaction(ctx context.Context, args status.SendTxArgs) (common.Hash, error) {
|
func (b *StatusBackend) SendTransaction(ctx context.Context, args status.SendTxArgs) (common.Hash, error) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
35
vendor/github.com/ethereum/go-ethereum/les/status/accounts.go
generated
vendored
Normal file
35
vendor/github.com/ethereum/go-ethereum/les/status/accounts.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountManager struct {
|
||||||
|
am *accounts.Manager
|
||||||
|
accountsFilterHandler AccountsFilterHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAccountManager creates a new AccountManager
|
||||||
|
func NewAccountManager(am *accounts.Manager) *AccountManager {
|
||||||
|
return &AccountManager{
|
||||||
|
am: am,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountsFilterHandler func([]accounts.Account) []accounts.Account
|
||||||
|
|
||||||
|
// Accounts returns accounts 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AccountManager) SetAccountsFilterHandler(fn AccountsFilterHandler) {
|
||||||
|
d.accountsFilterHandler = fn
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user