From 81345fd5b8409d0df011f3d35657ee40a1d12ff7 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Tue, 5 Dec 2023 13:36:15 +0200 Subject: [PATCH 1/6] SERVICES-1983: bulk accounts requests support --- api/errors/errors.go | 6 + api/groups/baseAccountsGroup.go | 25 ++ api/groups/baseAccountsGroup_test.go | 99 ++++++++ api/groups/interface.go | 1 + api/mock/facadeStub.go | 6 + cmd/proxy/config/apiConfig/v1_0.toml | 1 + cmd/proxy/config/apiConfig/v_next.toml | 1 + cmd/proxy/config/config.toml | 12 +- data/account.go | 12 + facade/baseFacade.go | 305 +++++++++++++------------ facade/interface.go | 1 + facade/mock/accountProccessorStub.go | 6 + process/accountProcessor.go | 69 ++++++ process/accountProcessor_test.go | 104 +++++++++ 14 files changed, 494 insertions(+), 154 deletions(-) diff --git a/api/errors/errors.go b/api/errors/errors.go index 61da22d1..d3499a73 100644 --- a/api/errors/errors.go +++ b/api/errors/errors.go @@ -14,6 +14,12 @@ var ErrGetValueForKey = errors.New("get value for key error") // ErrGetKeyValuePairs signals an error in getting the key-value pairs for a given address var ErrGetKeyValuePairs = errors.New("get key value pairs error") +// ErrInvalidAddressesArray signals that an invalid input has been provided +var ErrInvalidAddressesArray = errors.New("invalid addresses array") + +// ErrCannotGetAddresses signals an error when trying to fetch a bulk of accounts +var ErrCannotGetAddresses = errors.New("error while fetching a bulk of accounts") + // ErrComputeShardForAddress signals an error in computing the shard ID for a given address var ErrComputeShardForAddress = errors.New("compute shard ID for address error") diff --git a/api/groups/baseAccountsGroup.go b/api/groups/baseAccountsGroup.go index 023df6e5..69ffed69 100644 --- a/api/groups/baseAccountsGroup.go +++ b/api/groups/baseAccountsGroup.go @@ -45,6 +45,7 @@ func NewAccountsGroup(facadeHandler data.FacadeHandler) (*accountsGroup, error) {Path: "/:address/nft/:tokenIdentifier/nonce/:nonce", Handler: ag.getESDTNftTokenData, Method: http.MethodGet}, {Path: "/:address/guardian-data", Handler: ag.getGuardianData, Method: http.MethodGet}, {Path: "/:address/is-data-trie-migrated", Handler: ag.isDataTrieMigrated, Method: http.MethodGet}, + {Path: "/bulk", Handler: ag.getAccounts, Method: http.MethodPost}, } ag.baseGroup.endpoints = baseRoutesHandlers @@ -127,6 +128,30 @@ func (group *accountsGroup) getCodeHash(c *gin.Context) { c.JSON(http.StatusOK, codeHashResponse) } +// getAccounts will handle the request for a bulk of addresses data +func (group *accountsGroup) getAccounts(c *gin.Context) { + var addresses []string + err := c.ShouldBindJSON(&addresses) + if err != nil { + shared.RespondWithBadRequest(c, errors.ErrInvalidAddressesArray.Error()) + return + } + + options, err := parseAccountQueryOptions(c) + if err != nil { + shared.RespondWithValidationError(c, errors.ErrInvalidFields, err) + return + } + + response, err := group.facade.GetAccounts(addresses, options) + if err != nil { + shared.RespondWithInternalError(c, errors.ErrCannotGetAddresses, err) + return + } + + shared.RespondWith(c, http.StatusOK, response, "", data.ReturnCodeSuccess) +} + // getTransactions returns the transactions for the address parameter func (group *accountsGroup) getTransactions(c *gin.Context) { transactions, status, err := group.getTransactionsFromFacade(c) diff --git a/api/groups/baseAccountsGroup_test.go b/api/groups/baseAccountsGroup_test.go index 6830aac8..cd3529fc 100644 --- a/api/groups/baseAccountsGroup_test.go +++ b/api/groups/baseAccountsGroup_test.go @@ -1,6 +1,8 @@ package groups_test import ( + "bytes" + "encoding/json" "errors" "fmt" "net/http" @@ -45,6 +47,15 @@ type balanceResponse struct { Data balanceResponseData } +type accountsResponseData struct { + Accounts map[string]*data.Account `json:"accounts"` +} + +type accountsResponse struct { + GeneralResponse + Data accountsResponseData `json:"data"` +} + type usernameResponseData struct { Username string `json:"username"` } @@ -240,6 +251,94 @@ func TestGetAccount_ReturnsSuccessfully(t *testing.T) { assert.Empty(t, accountResponse.Error) } +//------- GetAccounts + +func TestGetAccount_FailsWhenInvalidRequest(t *testing.T) { + t.Parallel() + + facade := &mock.FacadeStub{} + addressGroup, err := groups.NewAccountsGroup(facade) + require.NoError(t, err) + ws := startProxyServer(addressGroup, addressPath) + + req, _ := http.NewRequest("POST", "/address/bulk", bytes.NewBuffer([]byte(`invalid request`))) + resp := httptest.NewRecorder() + ws.ServeHTTP(resp, req) + + accountsResponse := accountsResponse{} + loadResponse(resp.Body, &accountsResponse) + + assert.Equal(t, http.StatusBadRequest, resp.Code) + assert.Empty(t, accountsResponse.Data) + assert.Equal(t, accountsResponse.Error, apiErrors.ErrInvalidAddressesArray.Error()) +} + +func TestGetAccount_FailWhenFacadeGetAccountsFails(t *testing.T) { + t.Parallel() + + returnedError := "i am an error" + facade := &mock.FacadeStub{ + GetAccountsHandler: func(addresses []string, _ common.AccountQueryOptions) (*data.AccountsModel, error) { + return nil, errors.New(returnedError) + }, + } + addressGroup, err := groups.NewAccountsGroup(facade) + require.NoError(t, err) + ws := startProxyServer(addressGroup, addressPath) + + req, _ := http.NewRequest("POST", "/address/bulk", bytes.NewBuffer([]byte(`["test", "test"]`))) + resp := httptest.NewRecorder() + ws.ServeHTTP(resp, req) + + accountsResponse := accountsResponse{} + loadResponse(resp.Body, &accountsResponse) + + assert.Equal(t, http.StatusInternalServerError, resp.Code) + assert.Empty(t, accountsResponse.Data) + assert.Contains(t, accountsResponse.Error, returnedError) +} + +func TestGetAccounts_ReturnsSuccessfully(t *testing.T) { + t.Parallel() + + accounts := map[string]*data.Account{ + "erd1alice": { + Address: "erd1alice", + Nonce: 1, + Balance: "100", + }, + "erd1bob": { + Address: "erd1bob", + Nonce: 1, + Balance: "101", + }, + } + facade := &mock.FacadeStub{ + GetAccountsHandler: func(addresses []string, _ common.AccountQueryOptions) (*data.AccountsModel, error) { + return &data.AccountsModel{ + Accounts: accounts, + }, nil + }, + } + addressGroup, err := groups.NewAccountsGroup(facade) + require.NoError(t, err) + ws := startProxyServer(addressGroup, addressPath) + + reqAddresses := []string{"erd1alice", "erd1bob"} + addressBytes, _ := json.Marshal(reqAddresses) + fmt.Println(string(addressBytes)) + req, _ := http.NewRequest("POST", "/address/bulk", bytes.NewBuffer(addressBytes)) + resp := httptest.NewRecorder() + ws.ServeHTTP(resp, req) + + accountsResponse := accountsResponse{} + loadResponse(resp.Body, &accountsResponse) + + assert.Equal(t, http.StatusOK, resp.Code) + assert.Equal(t, accountsResponse.Data.Accounts, accounts) + assert.Empty(t, accountsResponse.Error) +} + //------- GetBalance func TestGetBalance_ReturnsSuccessfully(t *testing.T) { diff --git a/api/groups/interface.go b/api/groups/interface.go index 253a3327..d84d6d38 100644 --- a/api/groups/interface.go +++ b/api/groups/interface.go @@ -18,6 +18,7 @@ type AccountsFacadeHandler interface { GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) GetAllESDTTokens(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) GetKeyValuePairs(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) + GetAccounts(addresses []string, options common.AccountQueryOptions) (*data.AccountsModel, error) GetESDTTokenData(address string, key string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) GetESDTsWithRole(address string, role string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) GetESDTsRoles(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) diff --git a/api/mock/facadeStub.go b/api/mock/facadeStub.go index abf744a8..0bc1e828 100644 --- a/api/mock/facadeStub.go +++ b/api/mock/facadeStub.go @@ -14,6 +14,7 @@ import ( type FacadeStub struct { IsFaucetEnabledHandler func() bool GetAccountHandler func(address string, options common.AccountQueryOptions) (*data.AccountModel, error) + GetAccountsHandler func(addresses []string, options common.AccountQueryOptions) (*data.AccountsModel, error) GetShardIDForAddressHandler func(address string) (uint32, error) GetValueForKeyHandler func(address string, key string, options common.AccountQueryOptions) (string, error) GetKeyValuePairsHandler func(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) @@ -256,6 +257,11 @@ func (f *FacadeStub) GetAccount(address string, options common.AccountQueryOptio return f.GetAccountHandler(address, options) } +// GetAccounts - +func (f *FacadeStub) GetAccounts(addresses []string, options common.AccountQueryOptions) (*data.AccountsModel, error) { + return f.GetAccountsHandler(addresses, options) +} + // GetKeyValuePairs - func (f *FacadeStub) GetKeyValuePairs(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { return f.GetKeyValuePairsHandler(address, options) diff --git a/cmd/proxy/config/apiConfig/v1_0.toml b/cmd/proxy/config/apiConfig/v1_0.toml index b8cea0ed..9c3e0d40 100644 --- a/cmd/proxy/config/apiConfig/v1_0.toml +++ b/cmd/proxy/config/apiConfig/v1_0.toml @@ -30,6 +30,7 @@ Routes = [ [APIPackages.address] Routes = [ { Name = "/:address", Open = true, Secured = false, RateLimit = 0 }, + { Name = "/bulk", Open = true, Secured = false, RateLimit = 0 }, { Name = "/:address/balance", Open = true, Secured = false, RateLimit = 0 }, { Name = "/:address/nonce", Open = true, Secured = false, RateLimit = 0 }, { Name = "/:address/username", Open = true, Secured = false, RateLimit = 0 }, diff --git a/cmd/proxy/config/apiConfig/v_next.toml b/cmd/proxy/config/apiConfig/v_next.toml index c1e6a0e6..f4af12ea 100644 --- a/cmd/proxy/config/apiConfig/v_next.toml +++ b/cmd/proxy/config/apiConfig/v_next.toml @@ -30,6 +30,7 @@ Routes = [ [APIPackages.address] Routes = [ { Name = "/:address", Open = true, Secured = false, RateLimit = 0 }, + { Name = "/bulk", Open = true, Secured = false, RateLimit = 0 }, { Name = "/:address/balance", Open = true, Secured = false, RateLimit = 0 }, { Name = "/:address/nonce", Open = true, Secured = false, RateLimit = 0 }, { Name = "/:address/username", Open = true, Secured = false, RateLimit = 0 }, diff --git a/cmd/proxy/config/config.toml b/cmd/proxy/config/config.toml index da7b6c19..943726db 100644 --- a/cmd/proxy/config/config.toml +++ b/cmd/proxy/config/config.toml @@ -66,14 +66,18 @@ # shard id 4294967295 # Fallback observers which are only used when regular ones are offline should have IsFallback = true [[Observers]] - ShardId = 0 - Address = "http://127.0.0.1:8081" + ShardId = 2 + Address = "http://5.22.217.171:8080" [[Observers]] ShardId = 1 - Address = "http://127.0.0.1:8082" + Address = "http://5.22.217.170:8080" + +[[Observers]] + ShardId = 0 + Address = "http://5.22.217.169:8080" [[Observers]] ShardId = 4294967295 - Address = "http://127.0.0.1:8083" + Address = "http://5.22.217.168:8080" IsFallback = false diff --git a/data/account.go b/data/account.go index 42db810d..47d78f40 100644 --- a/data/account.go +++ b/data/account.go @@ -6,6 +6,11 @@ type AccountModel struct { BlockInfo BlockInfo `json:"blockInfo"` } +// AccountsModel defines the model of the accounts response +type AccountsModel struct { + Accounts map[string]*Account `json:"accounts"` +} + // Account defines the data structure for an account type Account struct { Address string `json:"address"` @@ -58,6 +63,13 @@ type AccountApiResponse struct { Code string `json:"code"` } +// AccountsApiResponse defines the response that will be returned by the node when requesting multiple accounts +type AccountsApiResponse struct { + Data AccountsModel `json:"data"` + Error string `json:"error"` + Code string `json:"code"` +} + // AccountKeyValueResponseData follows the format of the data field on an account key-value response type AccountKeyValueResponseData struct { Value string `json:"value"` diff --git a/facade/baseFacade.go b/facade/baseFacade.go index 3a72611a..cb138c9a 100644 --- a/facade/baseFacade.go +++ b/facade/baseFacade.go @@ -127,143 +127,148 @@ func NewProxyFacade( } // GetAccount returns an account based on the input address -func (epf *ProxyFacade) GetAccount(address string, options common.AccountQueryOptions) (*data.AccountModel, error) { - return epf.accountProc.GetAccount(address, options) +func (pf *ProxyFacade) GetAccount(address string, options common.AccountQueryOptions) (*data.AccountModel, error) { + return pf.accountProc.GetAccount(address, options) } // GetCodeHash returns the code hash for the given address -func (epf *ProxyFacade) GetCodeHash(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetCodeHash(address, options) +func (pf *ProxyFacade) GetCodeHash(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetCodeHash(address, options) } // GetKeyValuePairs returns the key-value pairs for the given address -func (epf *ProxyFacade) GetKeyValuePairs(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetKeyValuePairs(address, options) +func (pf *ProxyFacade) GetKeyValuePairs(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetKeyValuePairs(address, options) +} + +// GetAccounts returns data about the provided addresses +func (pf *ProxyFacade) GetAccounts(addresses []string, options common.AccountQueryOptions) (*data.AccountsModel, error) { + return pf.accountProc.GetAccounts(addresses, options) } // GetValueForKey returns the value for the given address and key -func (epf *ProxyFacade) GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) { - return epf.accountProc.GetValueForKey(address, key, options) +func (pf *ProxyFacade) GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) { + return pf.accountProc.GetValueForKey(address, key, options) } // GetGuardianData returns the guardian data for the given address -func (epf *ProxyFacade) GetGuardianData(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetGuardianData(address, options) +func (pf *ProxyFacade) GetGuardianData(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetGuardianData(address, options) } // GetShardIDForAddress returns the computed shard ID for the given address based on the current proxy's configuration -func (epf *ProxyFacade) GetShardIDForAddress(address string) (uint32, error) { - return epf.accountProc.GetShardIDForAddress(address) +func (pf *ProxyFacade) GetShardIDForAddress(address string) (uint32, error) { + return pf.accountProc.GetShardIDForAddress(address) } // GetTransactions returns transactions by address -func (epf *ProxyFacade) GetTransactions(address string) ([]data.DatabaseTransaction, error) { - return epf.accountProc.GetTransactions(address) +func (pf *ProxyFacade) GetTransactions(address string) ([]data.DatabaseTransaction, error) { + return pf.accountProc.GetTransactions(address) } // GetESDTTokenData returns the token data for a given token name -func (epf *ProxyFacade) GetESDTTokenData(address string, key string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetESDTTokenData(address, key, options) +func (pf *ProxyFacade) GetESDTTokenData(address string, key string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetESDTTokenData(address, key, options) } // GetESDTNftTokenData returns the token data for a given token name -func (epf *ProxyFacade) GetESDTNftTokenData(address string, key string, nonce uint64, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetESDTNftTokenData(address, key, nonce, options) +func (pf *ProxyFacade) GetESDTNftTokenData(address string, key string, nonce uint64, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetESDTNftTokenData(address, key, nonce, options) } // GetESDTsWithRole returns the tokens where the given address has the assigned role -func (epf *ProxyFacade) GetESDTsWithRole(address string, role string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetESDTsWithRole(address, role, options) +func (pf *ProxyFacade) GetESDTsWithRole(address string, role string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetESDTsWithRole(address, role, options) } // GetESDTsRoles returns the tokens and roles for the given address -func (epf *ProxyFacade) GetESDTsRoles(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetESDTsRoles(address, options) +func (pf *ProxyFacade) GetESDTsRoles(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetESDTsRoles(address, options) } // GetNFTTokenIDsRegisteredByAddress returns the token identifiers of the NFTs registered by the address -func (epf *ProxyFacade) GetNFTTokenIDsRegisteredByAddress(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetNFTTokenIDsRegisteredByAddress(address, options) +func (pf *ProxyFacade) GetNFTTokenIDsRegisteredByAddress(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetNFTTokenIDsRegisteredByAddress(address, options) } // GetAllESDTTokens returns all the ESDT tokens for a given address -func (epf *ProxyFacade) GetAllESDTTokens(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.GetAllESDTTokens(address, options) +func (pf *ProxyFacade) GetAllESDTTokens(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.GetAllESDTTokens(address, options) } // SendTransaction should send the transaction to the correct observer -func (epf *ProxyFacade) SendTransaction(tx *data.Transaction) (int, string, error) { - return epf.txProc.SendTransaction(tx) +func (pf *ProxyFacade) SendTransaction(tx *data.Transaction) (int, string, error) { + return pf.txProc.SendTransaction(tx) } // SendMultipleTransactions should send the transactions to the correct observers -func (epf *ProxyFacade) SendMultipleTransactions(txs []*data.Transaction) (data.MultipleTransactionsResponseData, error) { - return epf.txProc.SendMultipleTransactions(txs) +func (pf *ProxyFacade) SendMultipleTransactions(txs []*data.Transaction) (data.MultipleTransactionsResponseData, error) { + return pf.txProc.SendMultipleTransactions(txs) } // SimulateTransaction should send the transaction to the correct observer for simulation -func (epf *ProxyFacade) SimulateTransaction(tx *data.Transaction, checkSignature bool) (*data.GenericAPIResponse, error) { - return epf.txProc.SimulateTransaction(tx, checkSignature) +func (pf *ProxyFacade) SimulateTransaction(tx *data.Transaction, checkSignature bool) (*data.GenericAPIResponse, error) { + return pf.txProc.SimulateTransaction(tx, checkSignature) } // TransactionCostRequest should return how many gas units a transaction will cost -func (epf *ProxyFacade) TransactionCostRequest(tx *data.Transaction) (*data.TxCostResponseData, error) { - return epf.txProc.TransactionCostRequest(tx) +func (pf *ProxyFacade) TransactionCostRequest(tx *data.Transaction) (*data.TxCostResponseData, error) { + return pf.txProc.TransactionCostRequest(tx) } // GetTransactionStatus should return transaction status -func (epf *ProxyFacade) GetTransactionStatus(txHash string, sender string) (string, error) { - return epf.txProc.GetTransactionStatus(txHash, sender) +func (pf *ProxyFacade) GetTransactionStatus(txHash string, sender string) (string, error) { + return pf.txProc.GetTransactionStatus(txHash, sender) } // GetProcessedTransactionStatus should return transaction status after internal processing of the transaction results -func (epf *ProxyFacade) GetProcessedTransactionStatus(txHash string) (string, error) { - return epf.txProc.GetProcessedTransactionStatus(txHash) +func (pf *ProxyFacade) GetProcessedTransactionStatus(txHash string) (string, error) { + return pf.txProc.GetProcessedTransactionStatus(txHash) } // GetTransaction should return a transaction by hash -func (epf *ProxyFacade) GetTransaction(txHash string, withResults bool) (*transaction.ApiTransactionResult, error) { - return epf.txProc.GetTransaction(txHash, withResults) +func (pf *ProxyFacade) GetTransaction(txHash string, withResults bool) (*transaction.ApiTransactionResult, error) { + return pf.txProc.GetTransaction(txHash, withResults) } // ReloadObservers will try to reload the observers -func (epf *ProxyFacade) ReloadObservers() data.NodesReloadResponse { - return epf.actionsProc.ReloadObservers() +func (pf *ProxyFacade) ReloadObservers() data.NodesReloadResponse { + return pf.actionsProc.ReloadObservers() } // ReloadFullHistoryObservers will try to reload the full history observers -func (epf *ProxyFacade) ReloadFullHistoryObservers() data.NodesReloadResponse { - return epf.actionsProc.ReloadFullHistoryObservers() +func (pf *ProxyFacade) ReloadFullHistoryObservers() data.NodesReloadResponse { + return pf.actionsProc.ReloadFullHistoryObservers() } // GetTransactionByHashAndSenderAddress should return a transaction by hash and sender address -func (epf *ProxyFacade) GetTransactionByHashAndSenderAddress(txHash string, sndAddr string, withEvents bool) (*transaction.ApiTransactionResult, int, error) { - return epf.txProc.GetTransactionByHashAndSenderAddress(txHash, sndAddr, withEvents) +func (pf *ProxyFacade) GetTransactionByHashAndSenderAddress(txHash string, sndAddr string, withEvents bool) (*transaction.ApiTransactionResult, int, error) { + return pf.txProc.GetTransactionByHashAndSenderAddress(txHash, sndAddr, withEvents) } // IsFaucetEnabled returns true if the faucet mechanism is enabled or false otherwise -func (epf *ProxyFacade) IsFaucetEnabled() bool { - return epf.faucetProc.IsEnabled() +func (pf *ProxyFacade) IsFaucetEnabled() bool { + return pf.faucetProc.IsEnabled() } // SendUserFunds should send a transaction to load one user's account with extra funds from an account in the pem file -func (epf *ProxyFacade) SendUserFunds(receiver string, value *big.Int) error { - senderSk, senderPk, err := epf.faucetProc.SenderDetailsFromPem(receiver) +func (pf *ProxyFacade) SendUserFunds(receiver string, value *big.Int) error { + senderSk, senderPk, err := pf.faucetProc.SenderDetailsFromPem(receiver) if err != nil { return err } - senderAccount, err := epf.accountProc.GetAccount(senderPk, common.AccountQueryOptions{}) + senderAccount, err := pf.accountProc.GetAccount(senderPk, common.AccountQueryOptions{}) if err != nil { return err } - networkCfg, err := epf.getNetworkConfig() + networkCfg, err := pf.getNetworkConfig() if err != nil { return err } - tx, err := epf.faucetProc.GenerateTxForSendUserFunds( + tx, err := pf.faucetProc.GenerateTxForSendUserFunds( senderSk, senderPk, senderAccount.Account.Nonce, @@ -275,12 +280,12 @@ func (epf *ProxyFacade) SendUserFunds(receiver string, value *big.Int) error { return err } - _, _, err = epf.txProc.SendTransaction(tx) + _, _, err = pf.txProc.SendTransaction(tx) return err } -func (epf *ProxyFacade) getNetworkConfig() (*data.NetworkConfig, error) { - genericResponse, err := epf.nodeStatusProc.GetNetworkConfigMetrics() +func (pf *ProxyFacade) getNetworkConfig() (*data.NetworkConfig, error) { + genericResponse, err := pf.nodeStatusProc.GetNetworkConfigMetrics() if err != nil { return nil, err } @@ -297,108 +302,108 @@ func (epf *ProxyFacade) getNetworkConfig() (*data.NetworkConfig, error) { } // ExecuteSCQuery retrieves data from existing SC trie through the use of a VM -func (epf *ProxyFacade) ExecuteSCQuery(query *data.SCQuery) (*vm.VMOutputApi, data.BlockInfo, error) { - return epf.scQueryService.ExecuteQuery(query) +func (pf *ProxyFacade) ExecuteSCQuery(query *data.SCQuery) (*vm.VMOutputApi, data.BlockInfo, error) { + return pf.scQueryService.ExecuteQuery(query) } // GetHeartbeatData retrieves the heartbeat status from one observer -func (epf *ProxyFacade) GetHeartbeatData() (*data.HeartbeatResponse, error) { - return epf.nodeGroupProc.GetHeartbeatData() +func (pf *ProxyFacade) GetHeartbeatData() (*data.HeartbeatResponse, error) { + return pf.nodeGroupProc.GetHeartbeatData() } // GetNetworkConfigMetrics retrieves the node's configuration's metrics -func (epf *ProxyFacade) GetNetworkConfigMetrics() (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetNetworkConfigMetrics() +func (pf *ProxyFacade) GetNetworkConfigMetrics() (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetNetworkConfigMetrics() } // GetNetworkStatusMetrics retrieves the node's network metrics for a given shard -func (epf *ProxyFacade) GetNetworkStatusMetrics(shardID uint32) (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetNetworkStatusMetrics(shardID) +func (pf *ProxyFacade) GetNetworkStatusMetrics(shardID uint32) (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetNetworkStatusMetrics(shardID) } // GetESDTSupply retrieves the supply for the provided token -func (epf *ProxyFacade) GetESDTSupply(token string) (*data.ESDTSupplyResponse, error) { - return epf.esdtSuppliesProc.GetESDTSupply(token) +func (pf *ProxyFacade) GetESDTSupply(token string) (*data.ESDTSupplyResponse, error) { + return pf.esdtSuppliesProc.GetESDTSupply(token) } // GetEconomicsDataMetrics retrieves the node's network metrics for a given shard -func (epf *ProxyFacade) GetEconomicsDataMetrics() (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetEconomicsDataMetrics() +func (pf *ProxyFacade) GetEconomicsDataMetrics() (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetEconomicsDataMetrics() } // GetDelegatedInfo retrieves the node's network delegated info -func (epf *ProxyFacade) GetDelegatedInfo() (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetDelegatedInfo() +func (pf *ProxyFacade) GetDelegatedInfo() (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetDelegatedInfo() } // GetDirectStakedInfo retrieves the node's direct staked values -func (epf *ProxyFacade) GetDirectStakedInfo() (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetDirectStakedInfo() +func (pf *ProxyFacade) GetDirectStakedInfo() (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetDirectStakedInfo() } // GetAllIssuedESDTs retrieves all the issued ESDTs from the node -func (epf *ProxyFacade) GetAllIssuedESDTs(tokenType string) (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetAllIssuedESDTs(tokenType) +func (pf *ProxyFacade) GetAllIssuedESDTs(tokenType string) (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetAllIssuedESDTs(tokenType) } // GetEnableEpochsMetrics retrieves the activation epochs -func (epf *ProxyFacade) GetEnableEpochsMetrics() (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetEnableEpochsMetrics() +func (pf *ProxyFacade) GetEnableEpochsMetrics() (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetEnableEpochsMetrics() } // GetRatingsConfig retrieves the node's configuration's metrics -func (epf *ProxyFacade) GetRatingsConfig() (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetRatingsConfig() +func (pf *ProxyFacade) GetRatingsConfig() (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetRatingsConfig() } // GetBlockByHash retrieves the block by hash for a given shard -func (epf *ProxyFacade) GetBlockByHash(shardID uint32, hash string, options common.BlockQueryOptions) (*data.BlockApiResponse, error) { - return epf.blockProc.GetBlockByHash(shardID, hash, options) +func (pf *ProxyFacade) GetBlockByHash(shardID uint32, hash string, options common.BlockQueryOptions) (*data.BlockApiResponse, error) { + return pf.blockProc.GetBlockByHash(shardID, hash, options) } // GetBlockByNonce retrieves the block by nonce for a given shard -func (epf *ProxyFacade) GetBlockByNonce(shardID uint32, nonce uint64, options common.BlockQueryOptions) (*data.BlockApiResponse, error) { - return epf.blockProc.GetBlockByNonce(shardID, nonce, options) +func (pf *ProxyFacade) GetBlockByNonce(shardID uint32, nonce uint64, options common.BlockQueryOptions) (*data.BlockApiResponse, error) { + return pf.blockProc.GetBlockByNonce(shardID, nonce, options) } // GetBlocksByRound retrieves the blocks for a given round -func (epf *ProxyFacade) GetBlocksByRound(round uint64, options common.BlockQueryOptions) (*data.BlocksApiResponse, error) { - return epf.blocksProc.GetBlocksByRound(round, options) +func (pf *ProxyFacade) GetBlocksByRound(round uint64, options common.BlockQueryOptions) (*data.BlocksApiResponse, error) { + return pf.blocksProc.GetBlocksByRound(round, options) } // GetInternalBlockByHash retrieves the internal block by hash for a given shard -func (epf *ProxyFacade) GetInternalBlockByHash(shardID uint32, hash string, format common.OutputFormat) (*data.InternalBlockApiResponse, error) { - return epf.blockProc.GetInternalBlockByHash(shardID, hash, format) +func (pf *ProxyFacade) GetInternalBlockByHash(shardID uint32, hash string, format common.OutputFormat) (*data.InternalBlockApiResponse, error) { + return pf.blockProc.GetInternalBlockByHash(shardID, hash, format) } // GetInternalBlockByNonce retrieves the internal block by nonce for a given shard -func (epf *ProxyFacade) GetInternalBlockByNonce(shardID uint32, nonce uint64, format common.OutputFormat) (*data.InternalBlockApiResponse, error) { - return epf.blockProc.GetInternalBlockByNonce(shardID, nonce, format) +func (pf *ProxyFacade) GetInternalBlockByNonce(shardID uint32, nonce uint64, format common.OutputFormat) (*data.InternalBlockApiResponse, error) { + return pf.blockProc.GetInternalBlockByNonce(shardID, nonce, format) } // GetInternalStartOfEpochMetaBlock retrieves the internal block by nonce for a given shard -func (epf *ProxyFacade) GetInternalStartOfEpochMetaBlock(epoch uint32, format common.OutputFormat) (*data.InternalBlockApiResponse, error) { - return epf.blockProc.GetInternalStartOfEpochMetaBlock(epoch, format) +func (pf *ProxyFacade) GetInternalStartOfEpochMetaBlock(epoch uint32, format common.OutputFormat) (*data.InternalBlockApiResponse, error) { + return pf.blockProc.GetInternalStartOfEpochMetaBlock(epoch, format) } // GetInternalMiniBlockByHash retrieves the internal miniblock by hash for a given shard -func (epf *ProxyFacade) GetInternalMiniBlockByHash(shardID uint32, hash string, epoch uint32, format common.OutputFormat) (*data.InternalMiniBlockApiResponse, error) { - return epf.blockProc.GetInternalMiniBlockByHash(shardID, hash, epoch, format) +func (pf *ProxyFacade) GetInternalMiniBlockByHash(shardID uint32, hash string, epoch uint32, format common.OutputFormat) (*data.InternalMiniBlockApiResponse, error) { + return pf.blockProc.GetInternalMiniBlockByHash(shardID, hash, epoch, format) } // GetHyperBlockByHash retrieves the hyperblock by hash -func (epf *ProxyFacade) GetHyperBlockByHash(hash string, options common.HyperblockQueryOptions) (*data.HyperblockApiResponse, error) { - return epf.blockProc.GetHyperBlockByHash(hash, options) +func (pf *ProxyFacade) GetHyperBlockByHash(hash string, options common.HyperblockQueryOptions) (*data.HyperblockApiResponse, error) { + return pf.blockProc.GetHyperBlockByHash(hash, options) } // GetHyperBlockByNonce retrieves the block by nonce -func (epf *ProxyFacade) GetHyperBlockByNonce(nonce uint64, options common.HyperblockQueryOptions) (*data.HyperblockApiResponse, error) { - return epf.blockProc.GetHyperBlockByNonce(nonce, options) +func (pf *ProxyFacade) GetHyperBlockByNonce(nonce uint64, options common.HyperblockQueryOptions) (*data.HyperblockApiResponse, error) { + return pf.blockProc.GetHyperBlockByNonce(nonce, options) } // ValidatorStatistics will return the statistics from an observer -func (epf *ProxyFacade) ValidatorStatistics() (map[string]*data.ValidatorApiResponse, error) { - valStats, err := epf.valStatsProc.GetValidatorStatistics() +func (pf *ProxyFacade) ValidatorStatistics() (map[string]*data.ValidatorApiResponse, error) { + valStats, err := pf.valStatsProc.GetValidatorStatistics() if err != nil { return nil, err } @@ -407,131 +412,131 @@ func (epf *ProxyFacade) ValidatorStatistics() (map[string]*data.ValidatorApiResp } // GetAtlasBlockByShardIDAndNonce returns block by shardID and nonce in a BlockAtlas-friendly-format -func (epf *ProxyFacade) GetAtlasBlockByShardIDAndNonce(shardID uint32, nonce uint64) (data.AtlasBlock, error) { - return epf.blockProc.GetAtlasBlockByShardIDAndNonce(shardID, nonce) +func (pf *ProxyFacade) GetAtlasBlockByShardIDAndNonce(shardID uint32, nonce uint64) (data.AtlasBlock, error) { + return pf.blockProc.GetAtlasBlockByShardIDAndNonce(shardID, nonce) } // GetAddressConverter returns the address converter -func (epf *ProxyFacade) GetAddressConverter() (core.PubkeyConverter, error) { - return epf.pubKeyConverter, nil +func (pf *ProxyFacade) GetAddressConverter() (core.PubkeyConverter, error) { + return pf.pubKeyConverter, nil } // GetLatestFullySynchronizedHyperblockNonce returns the latest fully synchronized hyperblock nonce -func (epf *ProxyFacade) GetLatestFullySynchronizedHyperblockNonce() (uint64, error) { - return epf.nodeStatusProc.GetLatestFullySynchronizedHyperblockNonce() +func (pf *ProxyFacade) GetLatestFullySynchronizedHyperblockNonce() (uint64, error) { + return pf.nodeStatusProc.GetLatestFullySynchronizedHyperblockNonce() } // ComputeTransactionHash will compute hash of a given transaction -func (epf *ProxyFacade) ComputeTransactionHash(tx *data.Transaction) (string, error) { - return epf.txProc.ComputeTransactionHash(tx) +func (pf *ProxyFacade) ComputeTransactionHash(tx *data.Transaction) (string, error) { + return pf.txProc.ComputeTransactionHash(tx) } // GetTransactionsPool returns all txs from pool -func (epf *ProxyFacade) GetTransactionsPool(fields string) (*data.TransactionsPool, error) { - return epf.txProc.GetTransactionsPool(fields) +func (pf *ProxyFacade) GetTransactionsPool(fields string) (*data.TransactionsPool, error) { + return pf.txProc.GetTransactionsPool(fields) } // GetTransactionsPoolForShard returns all txs from shard's pool -func (epf *ProxyFacade) GetTransactionsPoolForShard(shardID uint32, fields string) (*data.TransactionsPool, error) { - return epf.txProc.GetTransactionsPoolForShard(shardID, fields) +func (pf *ProxyFacade) GetTransactionsPoolForShard(shardID uint32, fields string) (*data.TransactionsPool, error) { + return pf.txProc.GetTransactionsPoolForShard(shardID, fields) } // GetTransactionsPoolForSender returns tx pool for sender -func (epf *ProxyFacade) GetTransactionsPoolForSender(sender, fields string) (*data.TransactionsPoolForSender, error) { - return epf.txProc.GetTransactionsPoolForSender(sender, fields) +func (pf *ProxyFacade) GetTransactionsPoolForSender(sender, fields string) (*data.TransactionsPoolForSender, error) { + return pf.txProc.GetTransactionsPoolForSender(sender, fields) } // GetLastPoolNonceForSender returns last nonce from tx pool for sender -func (epf *ProxyFacade) GetLastPoolNonceForSender(sender string) (uint64, error) { - return epf.txProc.GetLastPoolNonceForSender(sender) +func (pf *ProxyFacade) GetLastPoolNonceForSender(sender string) (uint64, error) { + return pf.txProc.GetLastPoolNonceForSender(sender) } // IsOldStorageForToken returns true is the storage for a given token is old -func (epf *ProxyFacade) IsOldStorageForToken(tokenID string, nonce uint64) (bool, error) { - return epf.nodeGroupProc.IsOldStorageForToken(tokenID, nonce) +func (pf *ProxyFacade) IsOldStorageForToken(tokenID string, nonce uint64) (bool, error) { + return pf.nodeGroupProc.IsOldStorageForToken(tokenID, nonce) } // GetTransactionsPoolNonceGapsForSender returns all nonce gaps from tx pool for sender -func (epf *ProxyFacade) GetTransactionsPoolNonceGapsForSender(sender string) (*data.TransactionsPoolNonceGaps, error) { - return epf.txProc.GetTransactionsPoolNonceGapsForSender(sender) +func (pf *ProxyFacade) GetTransactionsPoolNonceGapsForSender(sender string) (*data.TransactionsPoolNonceGaps, error) { + return pf.txProc.GetTransactionsPoolNonceGapsForSender(sender) } // GetProof returns the Merkle proof for the given address -func (epf *ProxyFacade) GetProof(rootHash string, address string) (*data.GenericAPIResponse, error) { - return epf.proofProc.GetProof(rootHash, address) +func (pf *ProxyFacade) GetProof(rootHash string, address string) (*data.GenericAPIResponse, error) { + return pf.proofProc.GetProof(rootHash, address) } // GetProofDataTrie returns a Merkle proof for the given address and a Merkle proof for the given key -func (epf *ProxyFacade) GetProofDataTrie(rootHash string, address string, key string) (*data.GenericAPIResponse, error) { - return epf.proofProc.GetProofDataTrie(rootHash, address, key) +func (pf *ProxyFacade) GetProofDataTrie(rootHash string, address string, key string) (*data.GenericAPIResponse, error) { + return pf.proofProc.GetProofDataTrie(rootHash, address, key) } // GetProofCurrentRootHash returns the Merkle proof for the given address -func (epf *ProxyFacade) GetProofCurrentRootHash(address string) (*data.GenericAPIResponse, error) { - return epf.proofProc.GetProofCurrentRootHash(address) +func (pf *ProxyFacade) GetProofCurrentRootHash(address string) (*data.GenericAPIResponse, error) { + return pf.proofProc.GetProofCurrentRootHash(address) } // VerifyProof verifies the given Merkle proof -func (epf *ProxyFacade) VerifyProof(rootHash string, address string, proof []string) (*data.GenericAPIResponse, error) { - return epf.proofProc.VerifyProof(rootHash, address, proof) +func (pf *ProxyFacade) VerifyProof(rootHash string, address string, proof []string) (*data.GenericAPIResponse, error) { + return pf.proofProc.VerifyProof(rootHash, address, proof) } // GetMetrics will return the status metrics -func (epf *ProxyFacade) GetMetrics() map[string]*data.EndpointMetrics { - return epf.statusProc.GetMetrics() +func (pf *ProxyFacade) GetMetrics() map[string]*data.EndpointMetrics { + return pf.statusProc.GetMetrics() } // GetMetricsForPrometheus will return the status metrics in a prometheus format -func (epf *ProxyFacade) GetMetricsForPrometheus() string { - return epf.statusProc.GetMetricsForPrometheus() +func (pf *ProxyFacade) GetMetricsForPrometheus() string { + return pf.statusProc.GetMetricsForPrometheus() } // GetGenesisNodesPubKeys retrieves the node's configuration public keys -func (epf *ProxyFacade) GetGenesisNodesPubKeys() (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetGenesisNodesPubKeys() +func (pf *ProxyFacade) GetGenesisNodesPubKeys() (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetGenesisNodesPubKeys() } // GetGasConfigs retrieves the current gas schedule configs -func (epf *ProxyFacade) GetGasConfigs() (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetGasConfigs() +func (pf *ProxyFacade) GetGasConfigs() (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetGasConfigs() } // GetAboutInfo will return the app info -func (epf *ProxyFacade) GetAboutInfo() (*data.GenericAPIResponse, error) { - return epf.aboutInfoProc.GetAboutInfo(), nil +func (pf *ProxyFacade) GetAboutInfo() (*data.GenericAPIResponse, error) { + return pf.aboutInfoProc.GetAboutInfo(), nil } // GetNodesVersions will return the version of the nodes -func (epf *ProxyFacade) GetNodesVersions() (*data.GenericAPIResponse, error) { - return epf.aboutInfoProc.GetNodesVersions() +func (pf *ProxyFacade) GetNodesVersions() (*data.GenericAPIResponse, error) { + return pf.aboutInfoProc.GetNodesVersions() } // GetAlteredAccountsByNonce returns altered accounts by nonce in block -func (epf *ProxyFacade) GetAlteredAccountsByNonce(shardID uint32, nonce uint64, options common.GetAlteredAccountsForBlockOptions) (*data.AlteredAccountsApiResponse, error) { - return epf.blockProc.GetAlteredAccountsByNonce(shardID, nonce, options) +func (pf *ProxyFacade) GetAlteredAccountsByNonce(shardID uint32, nonce uint64, options common.GetAlteredAccountsForBlockOptions) (*data.AlteredAccountsApiResponse, error) { + return pf.blockProc.GetAlteredAccountsByNonce(shardID, nonce, options) } // GetAlteredAccountsByHash returns altered accounts by hash in block -func (epf *ProxyFacade) GetAlteredAccountsByHash(shardID uint32, hash string, options common.GetAlteredAccountsForBlockOptions) (*data.AlteredAccountsApiResponse, error) { - return epf.blockProc.GetAlteredAccountsByHash(shardID, hash, options) +func (pf *ProxyFacade) GetAlteredAccountsByHash(shardID uint32, hash string, options common.GetAlteredAccountsForBlockOptions) (*data.AlteredAccountsApiResponse, error) { + return pf.blockProc.GetAlteredAccountsByHash(shardID, hash, options) } // GetTriesStatistics will return trie statistics -func (epf *ProxyFacade) GetTriesStatistics(shardID uint32) (*data.TrieStatisticsAPIResponse, error) { - return epf.nodeStatusProc.GetTriesStatistics(shardID) +func (pf *ProxyFacade) GetTriesStatistics(shardID uint32) (*data.TrieStatisticsAPIResponse, error) { + return pf.nodeStatusProc.GetTriesStatistics(shardID) } // GetEpochStartData retrieves epoch start data for the provides epoch and shard ID -func (epf *ProxyFacade) GetEpochStartData(epoch uint32, shardID uint32) (*data.GenericAPIResponse, error) { - return epf.nodeStatusProc.GetEpochStartData(epoch, shardID) +func (pf *ProxyFacade) GetEpochStartData(epoch uint32, shardID uint32) (*data.GenericAPIResponse, error) { + return pf.nodeStatusProc.GetEpochStartData(epoch, shardID) } // GetInternalStartOfEpochValidatorsInfo retrieves the validators info by epoch -func (epf *ProxyFacade) GetInternalStartOfEpochValidatorsInfo(epoch uint32) (*data.ValidatorsInfoApiResponse, error) { - return epf.blockProc.GetInternalStartOfEpochValidatorsInfo(epoch) +func (pf *ProxyFacade) GetInternalStartOfEpochValidatorsInfo(epoch uint32) (*data.ValidatorsInfoApiResponse, error) { + return pf.blockProc.GetInternalStartOfEpochValidatorsInfo(epoch) } // IsDataTrieMigrated returns true if the data trie for the given address is migrated -func (epf *ProxyFacade) IsDataTrieMigrated(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - return epf.accountProc.IsDataTrieMigrated(address, options) +func (pf *ProxyFacade) IsDataTrieMigrated(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { + return pf.accountProc.IsDataTrieMigrated(address, options) } diff --git a/facade/interface.go b/facade/interface.go index ceb04241..29bdb2a3 100644 --- a/facade/interface.go +++ b/facade/interface.go @@ -19,6 +19,7 @@ type ActionsProcessor interface { // AccountProcessor defines what an account request processor should do type AccountProcessor interface { GetAccount(address string, options common.AccountQueryOptions) (*data.AccountModel, error) + GetAccounts(addresses []string, options common.AccountQueryOptions) (*data.AccountsModel, error) GetShardIDForAddress(address string) (uint32, error) GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) GetTransactions(address string) ([]data.DatabaseTransaction, error) diff --git a/facade/mock/accountProccessorStub.go b/facade/mock/accountProccessorStub.go index 5c18f007..9ed6019e 100644 --- a/facade/mock/accountProccessorStub.go +++ b/facade/mock/accountProccessorStub.go @@ -8,6 +8,7 @@ import ( // AccountProcessorStub - type AccountProcessorStub struct { GetAccountCalled func(address string, options common.AccountQueryOptions) (*data.AccountModel, error) + GetAccountsCalled func(addresses []string, options common.AccountQueryOptions) (*data.AccountsModel, error) GetValueForKeyCalled func(address string, key string, options common.AccountQueryOptions) (string, error) GetShardIDForAddressCalled func(address string) (uint32, error) GetTransactionsCalled func(address string) ([]data.DatabaseTransaction, error) @@ -68,6 +69,11 @@ func (aps *AccountProcessorStub) GetAccount(address string, options common.Accou return aps.GetAccountCalled(address, options) } +// GetAccounts - +func (aps *AccountProcessorStub) GetAccounts(addresses []string, options common.AccountQueryOptions) (*data.AccountsModel, error) { + return aps.GetAccountsCalled(addresses, options) +} + // GetValueForKey - func (aps *AccountProcessorStub) GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) { return aps.GetValueForKeyCalled(address, key, options) diff --git a/process/accountProcessor.go b/process/accountProcessor.go index 75cf7b52..73838cab 100644 --- a/process/accountProcessor.go +++ b/process/accountProcessor.go @@ -73,6 +73,66 @@ func (ap *AccountProcessor) GetAccount(address string, options common.AccountQue return nil, ErrSendingRequest } +// GetAccounts will return data about the provided accounts +func (ap *AccountProcessor) GetAccounts(addresses []string, options common.AccountQueryOptions) (*data.AccountsModel, error) { + addressesInShards := make(map[uint32][]string) + var shardID uint32 + var err error + for _, address := range addresses { + shardID, err = ap.GetShardIDForAddress(address) + if err != nil { + return nil, fmt.Errorf("%w while trying to compute shard ID of address %s", err, address) + } + + addressesInShards[shardID] = append(addressesInShards[shardID], address) + } + + accountsResponse := make(map[string]*data.Account) + for shardID, accounts := range addressesInShards { + accounts, err := ap.getAccountsInShard(accounts, shardID, options) + if err != nil { + return nil, err + } + + for address, account := range accounts { + accountsResponse[address] = account + } + } + + return &data.AccountsModel{ + Accounts: accountsResponse, + }, nil +} + +func (ap *AccountProcessor) getAccountsInShard(addresses []string, shardID uint32, options common.AccountQueryOptions) (map[string]*data.Account, error) { + observers, err := ap.proc.GetObservers(shardID) + if err != nil { + return nil, err + } + + apiResponse := data.AccountsApiResponse{} + apiPath := addressPath + "bulk" + apiPath = common.BuildUrlWithAccountQueryOptions(apiPath, options) + for _, observer := range observers { + respCode, err := ap.proc.CallPostRestEndPoint(observer.Address, apiPath, addresses, &apiResponse) + if err == nil || respCode == http.StatusBadRequest || respCode == http.StatusInternalServerError { + log.Info("bulk accounts request", + "shard ID", observer.ShardId, + "observer", observer.Address, + "http code", respCode) + if apiResponse.Error != "" { + return nil, errors.New(apiResponse.Error) + } + + return apiResponse.Data.Accounts, nil + } + + log.Error("bulk accounts request", "observer", observer.Address, "error", err.Error()) + } + + return nil, ErrSendingRequest +} + // GetValueForKey returns the value for the given address and key func (ap *AccountProcessor) GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) { observers, err := ap.getObserversForAddress(address) @@ -398,6 +458,15 @@ func (ap *AccountProcessor) GetCodeHash(address string, options common.AccountQu return nil, ErrSendingRequest } +func (ap *AccountProcessor) getShardIfOdAddress(address string) (uint32, error) { + addressBytes, err := ap.pubKeyConverter.Decode(address) + if err != nil { + return 0, err + } + + return ap.proc.ComputeShardId(addressBytes) +} + func (ap *AccountProcessor) getObserversForAddress(address string) ([]*data.NodeData, error) { addressBytes, err := ap.pubKeyConverter.Decode(address) if err != nil { diff --git a/process/accountProcessor_test.go b/process/accountProcessor_test.go index 61f0f13a..231a9c73 100644 --- a/process/accountProcessor_test.go +++ b/process/accountProcessor_test.go @@ -1,6 +1,7 @@ package process_test import ( + "encoding/hex" "errors" "strings" "testing" @@ -562,3 +563,106 @@ func TestAccountProcessor_IsDataTrieMigrated(t *testing.T) { require.True(t, result.Data.(bool)) }) } + +func TestAccountProcessor_GetAccounts(t *testing.T) { + t.Parallel() + + t.Run("should return error if a shard returns error", func(t *testing.T) { + t.Parallel() + + expectedError := "expected error message" + ap, _ := process.NewAccountProcessor( + &mock.ProcessorStub{ + GetObserversCalled: func(shardID uint32) ([]*data.NodeData, error) { + address := "observer0" + if shardID == 1 { + address = "observer1" + } + return []*data.NodeData{ + { + Address: address, + ShardId: shardID, + }, + }, nil + }, + + CallPostRestEndPointCalled: func(obsAddr string, _ string, _ interface{}, value interface{}) (int, error) { + response := value.(*data.AccountsApiResponse) + if obsAddr == "observer1" { + response.Error = expectedError + } + return 0, nil + }, + ComputeShardIdCalled: func(addr []byte) (uint32, error) { + if hex.EncodeToString(addr) == "aabb" { + return 0, nil + } + + return 1, nil + }, + }, + &mock.PubKeyConverterMock{}, + &mock.ElasticSearchConnectorMock{}, + ) + + result, err := ap.GetAccounts([]string{"aabb", "bbaa"}, common.AccountQueryOptions{}) + require.Equal(t, expectedError, err.Error()) + require.Empty(t, result) + }) + + t.Run("should work", func(t *testing.T) { + t.Parallel() + + ap, _ := process.NewAccountProcessor( + &mock.ProcessorStub{ + GetObserversCalled: func(shardID uint32) ([]*data.NodeData, error) { + address := "observer0" + if shardID == 1 { + address = "observer1" + } + return []*data.NodeData{ + { + Address: address, + ShardId: shardID, + }, + }, nil + }, + + CallPostRestEndPointCalled: func(obsAddr string, _ string, _ interface{}, value interface{}) (int, error) { + address := "shard0Address" + if obsAddr == "observer1" { + address = "shard1Address" + } + response := value.(*data.AccountsApiResponse) + response.Data.Accounts = map[string]*data.Account{ + address: {Address: address, Balance: "37"}, + } + return 0, nil + }, + ComputeShardIdCalled: func(addr []byte) (uint32, error) { + if hex.EncodeToString(addr) == "aabb" { + return 0, nil + } + + return 1, nil + }, + }, + &mock.PubKeyConverterMock{}, + &mock.ElasticSearchConnectorMock{}, + ) + + result, err := ap.GetAccounts([]string{"aabb", "bbaa"}, common.AccountQueryOptions{}) + require.NoError(t, err) + + require.Equal(t, map[string]*data.Account{ + "shard0Address": { + Address: "shard0Address", + Balance: "37", + }, + "shard1Address": { + Address: "shard1Address", + Balance: "37", + }, + }, result.Accounts) + }) +} From e6fb694168fe28bc551fe60e1bdca2e6dfed2a66 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Tue, 5 Dec 2023 14:05:16 +0200 Subject: [PATCH 2/6] revert config file --- cmd/proxy/config/config.toml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/cmd/proxy/config/config.toml b/cmd/proxy/config/config.toml index 943726db..da99aa4d 100644 --- a/cmd/proxy/config/config.toml +++ b/cmd/proxy/config/config.toml @@ -66,18 +66,14 @@ # shard id 4294967295 # Fallback observers which are only used when regular ones are offline should have IsFallback = true [[Observers]] - ShardId = 2 - Address = "http://5.22.217.171:8080" + ShardId = 0 + Address = "http://127.0.0.1:8081" [[Observers]] ShardId = 1 - Address = "http://5.22.217.170:8080" - -[[Observers]] - ShardId = 0 - Address = "http://5.22.217.169:8080" + Address = "http://127.0.0.1:8082" [[Observers]] ShardId = 4294967295 - Address = "http://5.22.217.168:8080" - IsFallback = false + Address = "http://127.0.0.1:8083" + IsFallback = false \ No newline at end of file From c6840557e713c92412e51b5576bb286fddc5aea5 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Tue, 5 Dec 2023 14:14:49 +0200 Subject: [PATCH 3/6] added empty line --- cmd/proxy/config/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/proxy/config/config.toml b/cmd/proxy/config/config.toml index da99aa4d..da7b6c19 100644 --- a/cmd/proxy/config/config.toml +++ b/cmd/proxy/config/config.toml @@ -76,4 +76,4 @@ [[Observers]] ShardId = 4294967295 Address = "http://127.0.0.1:8083" - IsFallback = false \ No newline at end of file + IsFallback = false From dab44d1c380a0db8f7bef0b65249a752299bb4c9 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Wed, 6 Dec 2023 14:33:55 +0200 Subject: [PATCH 4/6] parallel requests --- process/accountProcessor.go | 42 ++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/process/accountProcessor.go b/process/accountProcessor.go index 73838cab..31feff63 100644 --- a/process/accountProcessor.go +++ b/process/accountProcessor.go @@ -3,12 +3,12 @@ package process import ( "errors" "fmt" - "net/http" - "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-proxy-go/common" "github.com/multiversx/mx-chain-proxy-go/data" + "net/http" + "sync" ) // addressPath defines the address path at which the nodes answer @@ -87,14 +87,38 @@ func (ap *AccountProcessor) GetAccounts(addresses []string, options common.Accou addressesInShards[shardID] = append(addressesInShards[shardID], address) } + var wg sync.WaitGroup + resultsCh := make(chan map[string]*data.Account, len(addressesInShards)) + errCh := make(chan error, len(addressesInShards)) + + for shID, accounts := range addressesInShards { + wg.Add(1) + go func(shID uint32, accounts []string) { + defer wg.Done() + accountsInShard, err := ap.getAccountsInShard(accounts, shID, options) + if err != nil { + errCh <- err + return + } + resultsCh <- accountsInShard + }(shID, accounts) + } + + go func() { + wg.Wait() + close(resultsCh) + close(errCh) + }() + + for err := range errCh { + // early exit if the request to at least one shard failed + return nil, err + } + accountsResponse := make(map[string]*data.Account) - for shardID, accounts := range addressesInShards { - accounts, err := ap.getAccountsInShard(accounts, shardID, options) - if err != nil { - return nil, err - } - for address, account := range accounts { + for accountsInShard := range resultsCh { + for address, account := range accountsInShard { accountsResponse[address] = account } } @@ -114,6 +138,7 @@ func (ap *AccountProcessor) getAccountsInShard(addresses []string, shardID uint3 apiPath := addressPath + "bulk" apiPath = common.BuildUrlWithAccountQueryOptions(apiPath, options) for _, observer := range observers { + fmt.Printf("getting accounts for shard %d for observer with address %s \n", shardID, observer.Address) respCode, err := ap.proc.CallPostRestEndPoint(observer.Address, apiPath, addresses, &apiResponse) if err == nil || respCode == http.StatusBadRequest || respCode == http.StatusInternalServerError { log.Info("bulk accounts request", @@ -121,6 +146,7 @@ func (ap *AccountProcessor) getAccountsInShard(addresses []string, shardID uint3 "observer", observer.Address, "http code", respCode) if apiResponse.Error != "" { + fmt.Println("returned error here") return nil, errors.New(apiResponse.Error) } From 4e47df9d36c84a2646007acde9d89992c31ba5dc Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Wed, 6 Dec 2023 15:19:08 +0200 Subject: [PATCH 5/6] refactoring --- process/accountProcessor.go | 44 ++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/process/accountProcessor.go b/process/accountProcessor.go index 31feff63..aa9300ef 100644 --- a/process/accountProcessor.go +++ b/process/accountProcessor.go @@ -88,39 +88,35 @@ func (ap *AccountProcessor) GetAccounts(addresses []string, options common.Accou } var wg sync.WaitGroup - resultsCh := make(chan map[string]*data.Account, len(addressesInShards)) - errCh := make(chan error, len(addressesInShards)) + wg.Add(len(addressesInShards)) + + var shardErr error + var mut sync.Mutex // Mutex to protect the shared map and error + accountsResponse := make(map[string]*data.Account) for shID, accounts := range addressesInShards { - wg.Add(1) go func(shID uint32, accounts []string) { defer wg.Done() - accountsInShard, err := ap.getAccountsInShard(accounts, shID, options) - if err != nil { - errCh <- err + accountsInShard, errGetAccounts := ap.getAccountsInShard(accounts, shID, options) + + mut.Lock() + defer mut.Unlock() + + if errGetAccounts != nil { + shardErr = errGetAccounts return } - resultsCh <- accountsInShard - }(shID, accounts) - } - go func() { - wg.Wait() - close(resultsCh) - close(errCh) - }() - - for err := range errCh { - // early exit if the request to at least one shard failed - return nil, err + for address, account := range accountsInShard { + accountsResponse[address] = account + } + }(shID, accounts) } - accountsResponse := make(map[string]*data.Account) + wg.Wait() - for accountsInShard := range resultsCh { - for address, account := range accountsInShard { - accountsResponse[address] = account - } + if shardErr != nil { + return nil, shardErr } return &data.AccountsModel{ @@ -138,7 +134,6 @@ func (ap *AccountProcessor) getAccountsInShard(addresses []string, shardID uint3 apiPath := addressPath + "bulk" apiPath = common.BuildUrlWithAccountQueryOptions(apiPath, options) for _, observer := range observers { - fmt.Printf("getting accounts for shard %d for observer with address %s \n", shardID, observer.Address) respCode, err := ap.proc.CallPostRestEndPoint(observer.Address, apiPath, addresses, &apiResponse) if err == nil || respCode == http.StatusBadRequest || respCode == http.StatusInternalServerError { log.Info("bulk accounts request", @@ -146,7 +141,6 @@ func (ap *AccountProcessor) getAccountsInShard(addresses []string, shardID uint3 "observer", observer.Address, "http code", respCode) if apiResponse.Error != "" { - fmt.Println("returned error here") return nil, errors.New(apiResponse.Error) } From 55b9dd1382732864e9eeb576818a3db4f3dbb4b8 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Wed, 6 Dec 2023 16:30:49 +0200 Subject: [PATCH 6/6] fix imports --- process/accountProcessor.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/process/accountProcessor.go b/process/accountProcessor.go index aa9300ef..48af3e1d 100644 --- a/process/accountProcessor.go +++ b/process/accountProcessor.go @@ -3,12 +3,13 @@ package process import ( "errors" "fmt" + "net/http" + "sync" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-proxy-go/common" "github.com/multiversx/mx-chain-proxy-go/data" - "net/http" - "sync" ) // addressPath defines the address path at which the nodes answer