feat: Add hasActivity param to derived addresses (#2663)

Added functionality to find target address when 6th param in path is added
for ex: "m'/44'/60'/0'/0/500" reperents the Address at the 500th index

Added a api to get the Address derived from a private key
This commit is contained in:
Khushboo-dev-cpp 2022-05-18 13:31:45 +02:00 committed by GitHub
parent 714c03c635
commit 15e5584ed2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 158 additions and 72 deletions

View File

@ -443,7 +443,7 @@ func (b *StatusNode) appmetricsService() common.StatusService {
func (b *StatusNode) walletService(accountsDB *accounts.Database, accountsFeed *event.Feed, openseaAPIKey string) common.StatusService {
if b.walletSrvc == nil {
b.walletSrvc = wallet.NewService(b.appDB, accountsDB, b.rpcClient, accountsFeed, openseaAPIKey)
b.walletSrvc = wallet.NewService(b.appDB, accountsDB, b.rpcClient, accountsFeed, openseaAPIKey, b.gethAccountManager)
}
return b.walletSrvc
}

View File

@ -32,13 +32,6 @@ type API struct {
feed *event.Feed
}
type DerivedAddress struct {
Address common.Address `json:"address"`
Path string `json:"path"`
HasActivity bool `json:"hasActivity"`
AlreadyCreated bool `json:"alreadyCreated"`
}
func (api *API) SaveAccounts(ctx context.Context, accounts []accounts.Account) error {
log.Info("[AccountsAPI::SaveAccounts]")
err := api.db.SaveAccounts(accounts)
@ -201,26 +194,6 @@ func (api *API) GenerateAccountWithDerivedPath(
return api.generateAccount(ctx, password, name, color, emoji, path, derivedFrom)
}
func (api *API) GetDerivedAddressesForPath(password string, derivedFrom string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
info, err := api.manager.AccountsGenerator().LoadAccount(derivedFrom, password)
if err != nil {
return nil, err
}
return api.getDerivedAddresses(info.ID, path, pageSize, pageNumber)
}
func (api *API) GetDerivedAddressesForMenominicWithPath(mnemonic string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ")
info, err := api.manager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "")
if err != nil {
return nil, err
}
return api.getDerivedAddresses(info.ID, path, pageSize, pageNumber)
}
func (api *API) verifyPassword(password string) error {
address, err := api.db.GetChatAddress()
if err != nil {
@ -230,49 +203,6 @@ func (api *API) verifyPassword(password string) error {
return err
}
func (api *API) getDerivedAddresses(id string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
addedAccounts, err := api.db.GetAccounts()
if err != nil {
return nil, err
}
derivedAddresses := make([]*DerivedAddress, 0)
if pageNumber <= 0 || pageSize <= 0 {
return nil, fmt.Errorf("pageSize and pageNumber should be greater than 0")
}
var startIndex = ((pageNumber - 1) * pageSize)
var endIndex = (pageNumber * pageSize)
for i := startIndex; i < endIndex; i++ {
derivedPath := fmt.Sprint(path, "/", i)
info, err := api.manager.AccountsGenerator().DeriveAddresses(id, []string{derivedPath})
if err != nil {
return nil, err
}
alreadyExists := false
for _, account := range addedAccounts {
if types.Address(common.HexToAddress(info[derivedPath].Address)) == account.Address {
alreadyExists = true
break
}
}
address := &DerivedAddress{
Address: common.HexToAddress(info[derivedPath].Address),
Path: derivedPath,
HasActivity: false,
AlreadyCreated: alreadyExists,
}
derivedAddresses = append(derivedAddresses, address)
}
return derivedAddresses, nil
}
func (api *API) addAccountWithMnemonic(
ctx context.Context,
mnemonic string,

View File

@ -2,11 +2,16 @@ package wallet
import (
"context"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/services/wallet/transfer"
)
@ -30,6 +35,13 @@ func (api *API) GetWallet(ctx context.Context, chainIDs []uint64) (*Wallet, erro
return api.r.GetWallet(ctx, chainIDs)
}
type DerivedAddress struct {
Address common.Address `json:"address"`
Path string `json:"path"`
HasActivity bool `json:"hasActivity"`
AlreadyCreated bool `json:"alreadyCreated"`
}
// SetInitialBlocksRange sets initial blocks range
func (api *API) SetInitialBlocksRange(ctx context.Context) error {
return api.s.transferController.SetInitialBlocksRange([]uint64{api.s.rpcClient.UpstreamChainID})
@ -303,3 +315,144 @@ func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*Suggeste
log.Debug("call to GetSuggestedFees")
return api.s.feesManager.suggestedFees(ctx, chainID)
}
func (api *API) GetDerivedAddressesForPath(ctx context.Context, password string, derivedFrom string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
info, err := api.s.gethManager.AccountsGenerator().LoadAccount(derivedFrom, password)
if err != nil {
return nil, err
}
return api.getDerivedAddresses(ctx, info.ID, path, pageSize, pageNumber)
}
func (api *API) GetDerivedAddressesForMenominicWithPath(ctx context.Context, mnemonic string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ")
info, err := api.s.gethManager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "")
if err != nil {
return nil, err
}
return api.getDerivedAddresses(ctx, info.ID, path, pageSize, pageNumber)
}
func (api *API) GetDerivedAddressForPrivateKey(ctx context.Context, privateKey string) ([]*DerivedAddress, error) {
var derivedAddresses = make([]*DerivedAddress, 0)
info, err := api.s.gethManager.AccountsGenerator().ImportPrivateKey(privateKey)
if err != nil {
return derivedAddresses, err
}
addressExists, err := api.s.accountsDB.AddressExists(types.Address(common.HexToAddress(info.Address)))
if err != nil {
return derivedAddresses, err
}
if addressExists {
return derivedAddresses, fmt.Errorf("account already exists")
}
transactions, err := api.s.transferController.GetTransfersByAddress(ctx, api.s.rpcClient.UpstreamChainID, common.HexToAddress(info.Address), nil, (*hexutil.Big)(big.NewInt(1)), false)
if err != nil {
return derivedAddresses, err
}
hasActivity := int64(len(transactions)) > 0
derivedAddress := &DerivedAddress{
Address: common.HexToAddress(info.Address),
Path: "",
HasActivity: hasActivity,
AlreadyCreated: addressExists,
}
derivedAddresses = append(derivedAddresses, derivedAddress)
return derivedAddresses, nil
}
func (api *API) getDerivedAddresses(ctx context.Context, id string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
var (
group = async.NewAtomicGroup(ctx)
derivedAddresses = make([]*DerivedAddress, 0)
unorderedDerivedAddresses = map[int]*DerivedAddress{}
err error
)
splitPathValues := strings.Split(path, "/")
if len(splitPathValues) == 6 {
derivedAddress, err := api.getDerivedAddress(id, path)
if err != nil {
return nil, err
}
derivedAddresses = append(derivedAddresses, derivedAddress)
} else {
if pageNumber <= 0 || pageSize <= 0 {
return nil, fmt.Errorf("pageSize and pageNumber should be greater than 0")
}
var startIndex = ((pageNumber - 1) * pageSize)
var endIndex = (pageNumber * pageSize)
for i := startIndex; i < endIndex; i++ {
derivedPath := fmt.Sprint(path, "/", i)
index := i
group.Add(func(parent context.Context) error {
derivedAddress, err := api.getDerivedAddress(id, derivedPath)
if err != nil {
return err
}
unorderedDerivedAddresses[index] = derivedAddress
return nil
})
}
select {
case <-group.WaitAsync():
case <-ctx.Done():
return nil, ctx.Err()
}
for i := startIndex; i < endIndex; i++ {
derivedAddresses = append(derivedAddresses, unorderedDerivedAddresses[i])
}
err = group.Error()
}
return derivedAddresses, err
}
func (api *API) getDerivedAddress(id string, derivedPath string) (*DerivedAddress, error) {
addedAccounts, err := api.s.accountsDB.GetAccounts()
if err != nil {
return nil, err
}
info, err := api.s.gethManager.AccountsGenerator().DeriveAddresses(id, []string{derivedPath})
if err != nil {
return nil, err
}
alreadyExists := false
for _, account := range addedAccounts {
if types.Address(common.HexToAddress(info[derivedPath].Address)) == account.Address {
alreadyExists = true
break
}
}
var ctx context.Context
transactions, err := api.s.transferController.GetTransfersByAddress(ctx, api.s.rpcClient.UpstreamChainID, common.HexToAddress(info[derivedPath].Address), nil, (*hexutil.Big)(big.NewInt(1)), false)
if err != nil {
return nil, err
}
hasActivity := int64(len(transactions)) > 0
address := &DerivedAddress{
Address: common.HexToAddress(info[derivedPath].Address),
Path: derivedPath,
HasActivity: hasActivity,
AlreadyCreated: alreadyExists,
}
return address, nil
}

View File

@ -8,13 +8,14 @@ import (
"github.com/ethereum/go-ethereum/p2p"
gethrpc "github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/wallet/transfer"
)
// NewService initializes service instance.
func NewService(db *sql.DB, accountsDB *accounts.Database, rpcClient *rpc.Client, accountFeed *event.Feed, openseaAPIKey string) *Service {
func NewService(db *sql.DB, accountsDB *accounts.Database, rpcClient *rpc.Client, accountFeed *event.Feed, openseaAPIKey string, gethManager *account.GethManager) *Service {
cryptoOnRampManager := NewCryptoOnRampManager(&CryptoOnRampOptions{
dataSourceType: DataSourceStatic,
})
@ -36,6 +37,7 @@ func NewService(db *sql.DB, accountsDB *accounts.Database, rpcClient *rpc.Client
cryptoOnRampManager: cryptoOnRampManager,
openseaAPIKey: openseaAPIKey,
feesManager: &FeeManager{rpcClient},
gethManager: gethManager,
}
}
@ -53,6 +55,7 @@ type Service struct {
feesManager *FeeManager
started bool
openseaAPIKey string
gethManager *account.GethManager
}
// Start signals transmitter.