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
|
@ -24,6 +24,7 @@
|
|||
# used by the Makefile
|
||||
/build/_workspace/
|
||||
/build/bin/
|
||||
/vendor/github.com/karalabe/xgo
|
||||
|
||||
# travis
|
||||
profile.tmp
|
||||
|
|
126
geth/accounts.go
126
geth/accounts.go
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
@ -21,7 +22,7 @@ var (
|
|||
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
|
||||
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
||||
// sub-account derivations)
|
||||
|
@ -48,7 +49,7 @@ func CreateAccount(password string) (address, pubKey, mnemonic string, err error
|
|||
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 "").
|
||||
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
||||
func CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
||||
|
@ -58,20 +59,20 @@ func CreateChildAccount(parentAddress, password string) (address, pubKey string,
|
|||
return "", "", err
|
||||
}
|
||||
|
||||
if parentAddress == "" { // by default derive from currently selected account
|
||||
parentAddress = nodeManager.SelectedAddress
|
||||
if parentAddress == "" && nodeManager.SelectedAccount != nil { // derive from selected account by default
|
||||
parentAddress = string(nodeManager.SelectedAccount.Address.Hex())
|
||||
}
|
||||
|
||||
if parentAddress == "" {
|
||||
return "", "", ErrNoAccountSelected
|
||||
}
|
||||
|
||||
// make sure that given password can decrypt key associated with a given parent address
|
||||
account, err := utils.MakeAddress(accountManager, parentAddress)
|
||||
if err != nil {
|
||||
return "", "", ErrAddressToAccountMappingFailure
|
||||
}
|
||||
|
||||
// make sure that given password can decrypt key associated with a given parent address
|
||||
account, accountKey, err := accountManager.AccountDecryptedKey(account, password)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("%s: %v", ErrAccountToKeyMappingFailure.Error(), err)
|
||||
|
@ -88,6 +89,7 @@ func CreateChildAccount(parentAddress, password string) (address, pubKey string,
|
|||
return "", "", err
|
||||
}
|
||||
accountManager.IncSubAccountIndex(account, password)
|
||||
accountKey.SubAccountIndex++
|
||||
|
||||
// import derived key into account keystore
|
||||
address, pubKey, err = importExtendedKey(childKey, password)
|
||||
|
@ -95,10 +97,15 @@ func CreateChildAccount(parentAddress, password string) (address, pubKey string,
|
|||
return
|
||||
}
|
||||
|
||||
// update in-memory selected account
|
||||
if nodeManager.SelectedAccount != nil {
|
||||
nodeManager.SelectedAccount.AccountKey = accountKey
|
||||
}
|
||||
|
||||
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).
|
||||
func RecoverAccount(password, mnemonic string) (address, pubKey string, err error) {
|
||||
// re-create extended key (see BIP32)
|
||||
|
@ -117,7 +124,7 @@ func RecoverAccount(password, mnemonic string) (address, pubKey string, err erro
|
|||
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,
|
||||
// all previous identities are removed).
|
||||
func SelectAccount(address, password string) error {
|
||||
|
@ -146,13 +153,21 @@ func SelectAccount(address, password string) error {
|
|||
return ErrWhisperIdentityInjectionFailure
|
||||
}
|
||||
|
||||
// persist address for easier recovery of currently selected key (from Whisper)
|
||||
nodeManager.SelectedAddress = address
|
||||
// persist account key for easier recovery of currently selected key
|
||||
subAccounts, err := findSubAccounts(accountKey.ExtendedKey, accountKey.SubAccountIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nodeManager.SelectedAccount = &SelectedExtKey{
|
||||
Address: account.Address,
|
||||
AccountKey: accountKey,
|
||||
SubAccounts: subAccounts,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// logout clears whisper identities
|
||||
// Logout clears whisper identities
|
||||
func Logout() error {
|
||||
nodeManager := GetNodeManager()
|
||||
whisperService, err := nodeManager.WhisperService()
|
||||
|
@ -165,12 +180,12 @@ func Logout() error {
|
|||
return fmt.Errorf("%s: %v", ErrWhisperClearIdentitiesFailure, err)
|
||||
}
|
||||
|
||||
nodeManager.SelectedAddress = ""
|
||||
nodeManager.SelectedAccount = 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
|
||||
// a whisper enabled account
|
||||
func UnlockAccount(address, password string, seconds int) error {
|
||||
|
@ -201,3 +216,90 @@ func importExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, p
|
|||
|
||||
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"
|
||||
)
|
||||
|
||||
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) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
|
@ -21,6 +131,8 @@ func TestCreateChildAccount(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
geth.Logout() // to make sure that we start with empty account (which might get populated during previous tests)
|
||||
|
||||
accountManager, err := geth.GetNodeManager().AccountManager()
|
||||
if err != nil {
|
||||
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")
|
||||
)
|
||||
|
||||
type SelectedExtKey struct {
|
||||
Address common.Address
|
||||
AccountKey *accounts.Key
|
||||
SubAccounts []accounts.Account
|
||||
}
|
||||
|
||||
type NodeManager struct {
|
||||
currentNode *node.Node // currently running geth node
|
||||
ctx *cli.Context // the CLI context used to start the geth node
|
||||
lightEthereum *les.LightEthereum // LES service
|
||||
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
|
||||
client *rpc.ClientRestartWrapper // RPC client
|
||||
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 {
|
||||
glog.V(logger.Warn).Infoln("cannot get light ethereum service:", err)
|
||||
}
|
||||
|
||||
// setup handlers
|
||||
m.lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest)
|
||||
m.lightEthereum.StatusBackend.SetAccountsFilterHandler(onAccountsListRequest)
|
||||
|
||||
m.client = rpc.NewClientRestartWrapper(func() *rpc.Client {
|
||||
client, err := m.currentNode.Attach()
|
||||
|
|
|
@ -21,10 +21,18 @@ const (
|
|||
)
|
||||
|
||||
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
|
||||
signalRecieved := make(chan struct{}, 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) {
|
||||
if jsonEvent == `{"type":"node.started","event":{}}` {
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
var muPrepareTestNode sync.Mutex
|
||||
|
||||
const (
|
||||
testDataDir = "../.ethereumtest"
|
||||
testNodeSyncSeconds = 300
|
||||
TestDataDir = "../.ethereumtest"
|
||||
TestNodeSyncSeconds = 300
|
||||
)
|
||||
|
||||
type NodeNotificationHandler func(jsonEvent string)
|
||||
|
@ -90,19 +90,19 @@ func PrepareTestNode() (err error) {
|
|||
}
|
||||
|
||||
syncRequired := false
|
||||
if _, err := os.Stat(testDataDir); os.IsNotExist(err) {
|
||||
if _, err := os.Stat(TestDataDir); os.IsNotExist(err) {
|
||||
syncRequired = true
|
||||
}
|
||||
|
||||
// prepare node directory
|
||||
dataDir, err := PreprocessDataDir(testDataDir)
|
||||
dataDir, err := PreprocessDataDir(TestDataDir)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("make node failed:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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) {
|
||||
err = CopyFile(dst, filepath.Join("../data", "test-account.pk"))
|
||||
if err != nil {
|
||||
|
@ -132,8 +132,8 @@ func PrepareTestNode() (err error) {
|
|||
manager.AddPeer("enode://409772c7dea96fa59a912186ad5bcdb5e51b80556b3fe447d940f99d9eaadb51d4f0ffedb68efad232b52475dd7bd59b51cee99968b3cc79e2d5684b33c4090c@139.162.166.59:30303")
|
||||
|
||||
if syncRequired {
|
||||
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
|
||||
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
|
||||
} else {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ func PrepareTestNode() (err error) {
|
|||
}
|
||||
|
||||
func RemoveTestNode() {
|
||||
err := os.RemoveAll(testDataDir)
|
||||
err := os.RemoveAll(TestDataDir)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infof("could not clean up temporary datadir")
|
||||
}
|
||||
|
|
|
@ -199,6 +199,11 @@ func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI {
|
|||
|
||||
// Accounts returns the collection of accounts this node manages
|
||||
func (s *PublicAccountAPI) Accounts() []accounts.Account {
|
||||
backend := GetStatusBackend()
|
||||
if backend != nil {
|
||||
return statusBackend.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.
|
||||
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))
|
||||
for i, acc := range accounts {
|
||||
addresses[i] = acc.Address
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package ethapi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/les/status"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
|
@ -8,29 +10,40 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// StatusBackend implements les.StatusBackend with direct calls to Ethereum
|
||||
// internals to support calls from status-go bindings (to internal packages e.g. ethapi)
|
||||
// StatusBackend exposes Ethereum internals to support custom semantics in status-go bindings
|
||||
type StatusBackend struct {
|
||||
eapi *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
|
||||
bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data
|
||||
txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
|
||||
|
||||
txQueue *status.TxQueue
|
||||
am *status.AccountManager
|
||||
}
|
||||
|
||||
var statusBackend *StatusBackend
|
||||
var once sync.Once
|
||||
|
||||
// NewStatusBackend creates a new backend using an existing Ethereum object.
|
||||
func NewStatusBackend(apiBackend Backend) *StatusBackend {
|
||||
glog.V(logger.Info).Infof("Status backend service started")
|
||||
backend := &StatusBackend{
|
||||
eapi: NewPublicEthereumAPI(apiBackend),
|
||||
bcapi: NewPublicBlockChainAPI(apiBackend),
|
||||
txapi: NewPublicTransactionPoolAPI(apiBackend),
|
||||
txQueue: status.NewTransactionQueue(),
|
||||
}
|
||||
once.Do(func() {
|
||||
statusBackend = &StatusBackend{
|
||||
eapi: NewPublicEthereumAPI(apiBackend),
|
||||
bcapi: NewPublicBlockChainAPI(apiBackend),
|
||||
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) {
|
||||
|
@ -41,6 +54,14 @@ func (b *StatusBackend) TransactionQueue() *status.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
|
||||
func (b *StatusBackend) SendTransaction(ctx context.Context, args status.SendTxArgs) (common.Hash, error) {
|
||||
if ctx == nil {
|
||||
|
|
|
@ -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…
Reference in New Issue