From adff3fc66b69048d86a0ffa450f75dbddb2572de Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Fri, 22 Sep 2023 15:30:23 +0300 Subject: [PATCH] fixes after second review --- common/options_test.go | 38 +++++ .../availabilityProvider.go | 48 ++++++ .../availabilityProvider_test.go | 66 ++++++++ observer/holder/nodesHolder.go | 82 +++++---- observer/holder/nodesHolder_test.go | 155 ++++++++++++++---- process/accountProcessor.go | 47 +++--- process/scQueryProcessor.go | 21 +-- 7 files changed, 356 insertions(+), 101 deletions(-) create mode 100644 observer/availabilityCommon/availabilityProvider.go create mode 100644 observer/availabilityCommon/availabilityProvider_test.go diff --git a/common/options_test.go b/common/options_test.go index 6ca12eab..81c459ec 100644 --- a/common/options_test.go +++ b/common/options_test.go @@ -9,6 +9,8 @@ import ( ) func TestBuildUrlWithBlockQueryOptions_ShouldWork(t *testing.T) { + t.Parallel() + builtUrl := BuildUrlWithBlockQueryOptions("/block/by-nonce/15", BlockQueryOptions{}) require.Equal(t, "/block/by-nonce/15", builtUrl) @@ -29,6 +31,8 @@ func TestBuildUrlWithBlockQueryOptions_ShouldWork(t *testing.T) { } func TestBuildUrlWithAccountQueryOptions_ShouldWork(t *testing.T) { + t.Parallel() + builtUrl := BuildUrlWithAccountQueryOptions("/address/erd1alice", AccountQueryOptions{}) require.Equal(t, "/address/erd1alice", builtUrl) @@ -65,6 +69,8 @@ func TestBuildUrlWithAccountQueryOptions_ShouldWork(t *testing.T) { } func TestBuildUrlWithAlteredAccountsQueryOptions(t *testing.T) { + t.Parallel() + resultedUrl := BuildUrlWithAlteredAccountsQueryOptions("path", GetAlteredAccountsForBlockOptions{}) require.Equal(t, "path", resultedUrl) @@ -74,3 +80,35 @@ func TestBuildUrlWithAlteredAccountsQueryOptions(t *testing.T) { // 2C is the ascii hex encoding of (,) require.Equal(t, "path?tokens=token1%2Ctoken2%2Ctoken3", resultedUrl) } + +func TestAccountQueryOptions_AreHistoricalCoordinatesSet(t *testing.T) { + t.Parallel() + + emptyQuery := AccountQueryOptions{} + require.False(t, emptyQuery.AreHistoricalCoordinatesSet()) + + queryWithNonce := AccountQueryOptions{ + BlockNonce: core.OptionalUint64{HasValue: true, Value: 37}, + } + require.True(t, queryWithNonce.AreHistoricalCoordinatesSet()) + + queryWithBlockHash := AccountQueryOptions{ + BlockHash: []byte("hash"), + } + require.True(t, queryWithBlockHash.AreHistoricalCoordinatesSet()) + + queryWithBlockRootHash := AccountQueryOptions{ + BlockRootHash: []byte("rootHash"), + } + require.True(t, queryWithBlockRootHash.AreHistoricalCoordinatesSet()) + + queryWithEpochStart := AccountQueryOptions{ + OnStartOfEpoch: core.OptionalUint32{HasValue: true, Value: 37}, + } + require.True(t, queryWithEpochStart.AreHistoricalCoordinatesSet()) + + queryWithHintEpoch := AccountQueryOptions{ + HintEpoch: core.OptionalUint32{HasValue: false, Value: 37}, + } + require.True(t, queryWithHintEpoch.AreHistoricalCoordinatesSet()) +} diff --git a/observer/availabilityCommon/availabilityProvider.go b/observer/availabilityCommon/availabilityProvider.go new file mode 100644 index 00000000..1d6e41db --- /dev/null +++ b/observer/availabilityCommon/availabilityProvider.go @@ -0,0 +1,48 @@ +package availabilityCommon + +import ( + "github.com/multiversx/mx-chain-proxy-go/common" + "github.com/multiversx/mx-chain-proxy-go/data" +) + +// AvailabilityProvider is a stateless component that aims to group common operations regarding observers' data availability +type AvailabilityProvider struct { +} + +// AvailabilityForAccountQueryOptions returns the availability needed for the provided query options +func (ap *AvailabilityProvider) AvailabilityForAccountQueryOptions(options common.AccountQueryOptions) data.ObserverDataAvailabilityType { + availability := data.AvailabilityRecent + if options.AreHistoricalCoordinatesSet() { + availability = data.AvailabilityAll + } + return availability +} + +// AvailabilityForVmQuery returns the availability needed for the provided query options +func (ap *AvailabilityProvider) AvailabilityForVmQuery(query *data.SCQuery) data.ObserverDataAvailabilityType { + availability := data.AvailabilityRecent + if query.BlockNonce.HasValue || len(query.BlockHash) > 0 { + availability = data.AvailabilityAll + } + return availability +} + +// IsNodeValid returns true if the provided node is valid based on the availability +func (ap *AvailabilityProvider) IsNodeValid(node *data.NodeData, availability data.ObserverDataAvailabilityType) bool { + isInvalidSnapshotlessNode := availability == data.AvailabilityRecent && !node.IsSnapshotless + isInvalidRegularNode := availability == data.AvailabilityAll && node.IsSnapshotless + isInvalidNode := isInvalidSnapshotlessNode || isInvalidRegularNode + return !isInvalidNode +} + +// GetDescriptionForAvailability returns a short description string about the provided availability +func (ap *AvailabilityProvider) GetDescriptionForAvailability(availability data.ObserverDataAvailabilityType) string { + switch availability { + case data.AvailabilityAll: + return "regular nodes" + case data.AvailabilityRecent: + return "snapshotless nodes" + default: + return "N/A" + } +} diff --git a/observer/availabilityCommon/availabilityProvider_test.go b/observer/availabilityCommon/availabilityProvider_test.go new file mode 100644 index 00000000..c1e7f725 --- /dev/null +++ b/observer/availabilityCommon/availabilityProvider_test.go @@ -0,0 +1,66 @@ +package availabilityCommon + +import ( + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-proxy-go/common" + "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/stretchr/testify/require" +) + +func TestAvailabilityForAccountQueryOptions(t *testing.T) { + ap := &AvailabilityProvider{} + + // Test with historical coordinates set + options := common.AccountQueryOptions{BlockHash: []byte("hash")} + require.Equal(t, data.AvailabilityAll, ap.AvailabilityForAccountQueryOptions(options)) + + // Test without historical coordinates set + options = common.AccountQueryOptions{} + require.Equal(t, data.AvailabilityRecent, ap.AvailabilityForAccountQueryOptions(options)) +} + +func TestAvailabilityForVmQuery(t *testing.T) { + ap := &AvailabilityProvider{} + + // Test with BlockNonce set + query := &data.SCQuery{BlockNonce: core.OptionalUint64{HasValue: true, Value: 37}} + require.Equal(t, data.AvailabilityAll, ap.AvailabilityForVmQuery(query)) + + // Test without BlockNonce set but with BlockHash + query = &data.SCQuery{BlockHash: []byte("hash")} + require.Equal(t, data.AvailabilityAll, ap.AvailabilityForVmQuery(query)) + + // Test without BlockNonce and BlockHash + query = &data.SCQuery{} + require.Equal(t, data.AvailabilityRecent, ap.AvailabilityForVmQuery(query)) +} + +func TestIsNodeValid(t *testing.T) { + ap := &AvailabilityProvider{} + + // Test with AvailabilityRecent and snapshotless node + node := &data.NodeData{IsSnapshotless: true} + require.True(t, ap.IsNodeValid(node, data.AvailabilityRecent)) + + // Test with AvailabilityRecent and regular node + node = &data.NodeData{} + require.False(t, ap.IsNodeValid(node, data.AvailabilityRecent)) + + // Test with AvailabilityAll and regular node + node = &data.NodeData{} + require.True(t, ap.IsNodeValid(node, data.AvailabilityAll)) + + // Test with AvailabilityAll and Snapshotless node + node = &data.NodeData{IsSnapshotless: true} + require.False(t, ap.IsNodeValid(node, data.AvailabilityAll)) +} + +func TestGetDescriptionForAvailability(t *testing.T) { + ap := &AvailabilityProvider{} + + require.Equal(t, "regular nodes", ap.GetDescriptionForAvailability(data.AvailabilityAll)) + require.Equal(t, "snapshotless nodes", ap.GetDescriptionForAvailability(data.AvailabilityRecent)) + require.Equal(t, "N/A", ap.GetDescriptionForAvailability("invalid")) // Invalid value +} diff --git a/observer/holder/nodesHolder.go b/observer/holder/nodesHolder.go index 04443c82..ea28e3d5 100644 --- a/observer/holder/nodesHolder.go +++ b/observer/holder/nodesHolder.go @@ -8,6 +8,7 @@ import ( logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/multiversx/mx-chain-proxy-go/observer/availabilityCommon" ) type cacheType string @@ -25,10 +26,11 @@ var ( ) type nodesHolder struct { - mut sync.RWMutex - allNodes map[uint32][]*data.NodeData - cache map[string][]*data.NodeData - availability data.ObserverDataAvailabilityType + mut sync.RWMutex + allNodes map[uint32][]*data.NodeData + cache map[string][]*data.NodeData + availability data.ObserverDataAvailabilityType + availabilityProvider availabilityCommon.AvailabilityProvider } // NewNodesHolder will return a new instance of a nodesHolder @@ -37,9 +39,10 @@ func NewNodesHolder(syncedNodes []*data.NodeData, fallbackNodes []*data.NodeData return nil, errEmptyNodesList } return &nodesHolder{ - allNodes: computeInitialNodeList(syncedNodes, fallbackNodes), - cache: make(map[string][]*data.NodeData), - availability: availability, + allNodes: computeInitialNodeList(syncedNodes, fallbackNodes), + cache: make(map[string][]*data.NodeData), + availability: availability, + availabilityProvider: availabilityCommon.AvailabilityProvider{}, }, nil } @@ -55,9 +58,7 @@ func (nh *nodesHolder) UpdateNodes(nodesWithSyncStatus []*data.NodeData) { nh.allNodes = make(map[uint32][]*data.NodeData) nh.cache = make(map[string][]*data.NodeData) for _, node := range nodesWithSyncStatus { - shouldSkipNode := nh.availability == data.AvailabilityRecent && !node.IsSnapshotless || - nh.availability == data.AvailabilityAll && node.IsSnapshotless - if shouldSkipNode { + if !nh.availabilityProvider.IsNodeValid(node, nh.availability) { continue } nh.allNodes[node.ShardId] = append(nh.allNodes[node.ShardId], node) @@ -112,6 +113,8 @@ func (nh *nodesHolder) getObservers(cache cacheType, shardID uint32) []*data.Nod // nodes not cached, compute the list and update the cache recomputedList := make([]*data.NodeData, 0) nh.mut.Lock() + defer nh.mut.Unlock() + cachedValues, exists = nh.cache[cacheKey] if exists { return cachedValues @@ -122,7 +125,6 @@ func (nh *nodesHolder) getObservers(cache cacheType, shardID uint32) []*data.Nod } } nh.cache[cacheKey] = recomputedList - nh.mut.Unlock() return recomputedList } @@ -157,20 +159,6 @@ func (nh *nodesHolder) IsInterfaceNil() bool { func (nh *nodesHolder) printNodesInShardsUnprotected() { nodesByType := make(map[uint32]map[cacheType][]*data.NodeData) - // define a function to get the cache type for a node - getCacheType := func(node *data.NodeData) cacheType { - if node.IsFallback { - if node.IsSynced { - return syncedFallbackNodesCache - } - return outOfSyncFallbackNodesCache - } - if node.IsSynced { - return syncedNodesCache - } - return outOfSyncNodesCache - } - // populate nodesByType map for shard, nodes := range nh.allNodes { if nodesByType[shard] == nil { @@ -183,11 +171,7 @@ func (nh *nodesHolder) printNodesInShardsUnprotected() { } } - printHeader := "regular nodes" - if nh.availability == data.AvailabilityRecent { - printHeader = "snapshotless nodes" - } - + printHeader := nh.availabilityProvider.GetDescriptionForAvailability(nh.availability) for shard, nodesByCache := range nodesByType { log.Info(fmt.Sprintf("shard %d %s", shard, printHeader), "synced observers", getNodesListAsString(nodesByCache[syncedNodesCache]), @@ -197,6 +181,19 @@ func (nh *nodesHolder) printNodesInShardsUnprotected() { } } +func getCacheType(node *data.NodeData) cacheType { + if node.IsFallback { + if node.IsSynced { + return syncedFallbackNodesCache + } + return outOfSyncFallbackNodesCache + } + if node.IsSynced { + return syncedNodesCache + } + return outOfSyncNodesCache +} + func getNodesListAsString(nodes []*data.NodeData) string { addressesString := "" for _, node := range nodes { @@ -206,14 +203,33 @@ func getNodesListAsString(nodes []*data.NodeData) string { return strings.TrimSuffix(addressesString, ", ") } +func cloneNodesSlice(input []*data.NodeData) []*data.NodeData { + clonedSlice := make([]*data.NodeData, len(input)) + for idx, node := range input { + clonedSlice[idx] = &data.NodeData{ + ShardId: node.ShardId, + Address: node.Address, + IsFallback: node.IsFallback, + IsSynced: node.IsSynced, + IsSnapshotless: node.IsSnapshotless, + } + } + + return clonedSlice +} + func computeInitialNodeList(regularNodes []*data.NodeData, fallbackNodes []*data.NodeData) map[uint32][]*data.NodeData { + // clone the original maps as not to affect the input + clonedRegularNodes := cloneNodesSlice(regularNodes) + clonedFallbackNodes := cloneNodesSlice(fallbackNodes) + mapToReturn := make(map[uint32][]*data.NodeData) - // in the first step, consider all the nodes to be active - for _, node := range regularNodes { + // since this function is called at constructor level, consider that all the nodes are active + for _, node := range clonedRegularNodes { node.IsSynced = true mapToReturn[node.ShardId] = append(mapToReturn[node.ShardId], node) } - for _, node := range fallbackNodes { + for _, node := range clonedFallbackNodes { node.IsSynced = true mapToReturn[node.ShardId] = append(mapToReturn[node.ShardId], node) } diff --git a/observer/holder/nodesHolder_test.go b/observer/holder/nodesHolder_test.go index 6e296867..807c5710 100644 --- a/observer/holder/nodesHolder_test.go +++ b/observer/holder/nodesHolder_test.go @@ -10,43 +10,138 @@ import ( "github.com/stretchr/testify/require" ) -func TestNodesHolder_ConstructorAndGetters(t *testing.T) { +func TestNewNodesHolder(t *testing.T) { t.Parallel() - nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityAll) - require.Equal(t, errEmptyNodesList, err) - require.Nil(t, nh) + t.Run("empty regular nodes slice - should error", func(t *testing.T) { + t.Parallel() - syncedNodes := createTestNodes(3) - setPropertyToNodes(syncedNodes, "synced", true, 0, 1, 2) + nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityAll) + require.Equal(t, errEmptyNodesList, err) + require.Nil(t, nh) + }) - fallbackNodes := createTestNodes(3) - setPropertyToNodes(fallbackNodes, "synced", true, 0, 1, 2) - setPropertyToNodes(fallbackNodes, "fallback", true, 0, 1, 2) + t.Run("empty snapshotless nodes slice - should not error", func(t *testing.T) { + t.Parallel() + + nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityRecent) + require.NoError(t, err) + require.NotNil(t, nh) + }) + + t.Run("should work for regular nodes", func(t *testing.T) { + t.Parallel() + + nh, err := NewNodesHolder([]*data.NodeData{{Address: "addr"}}, []*data.NodeData{}, data.AvailabilityAll) + require.NoError(t, err) + require.NotNil(t, nh) + }) + + t.Run("should work for snapshotless nodes", func(t *testing.T) { + t.Parallel() + + nh, err := NewNodesHolder([]*data.NodeData{{Address: "addr"}}, []*data.NodeData{}, data.AvailabilityRecent) + require.NoError(t, err) + require.NotNil(t, nh) + }) +} - nh, err = NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityAll) +func TestNodesHolder_Getters(t *testing.T) { + t.Parallel() + + shardIDs := []uint32{0, 1, core.MetachainShardId} + syncedNodes := createTestNodes(6) + fallbackNodes := createTestNodes(6) + setPropertyToNodes(fallbackNodes, "fallback", true, 0, 1, 2, 3, 4, 5) + + nh, err := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityAll) require.NoError(t, err) require.NotNil(t, nh) - require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetSyncedNodes(0)) - require.Equal(t, []*data.NodeData{syncedNodes[1]}, nh.GetSyncedNodes(1)) - require.Equal(t, []*data.NodeData{syncedNodes[2]}, nh.GetSyncedNodes(core.MetachainShardId)) + t.Run("test getters before updating the nodes", func(t *testing.T) { + for _, shardID := range shardIDs { + indices := getIndicesOfNodesInShard(syncedNodes, shardID) + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(shardID), syncedNodes, indices) + } + for _, shardID := range shardIDs { + require.Empty(t, nh.GetOutOfSyncNodes(shardID)) + } + for _, shardID := range shardIDs { + indices := getIndicesOfNodesInShard(fallbackNodes, shardID) + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(shardID), fallbackNodes, indices) + } + for _, shardID := range shardIDs { + require.Empty(t, nh.GetOutOfSyncFallbackNodes(shardID)) + } + }) + + t.Run("test getters after updating the nodes", func(t *testing.T) { + setPropertyToNodes(syncedNodes, "synced", true, 3, 4, 5) + setPropertyToNodes(syncedNodes, "synced", false, 0, 1, 2) + + setPropertyToNodes(fallbackNodes, "synced", true, 0, 2, 3, 4, 5) + setPropertyToNodes(fallbackNodes, "synced", false, 1) + nh.UpdateNodes(append(syncedNodes, fallbackNodes...)) + + // check synced regular nodes + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(0), syncedNodes, []int{3}) + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(1), syncedNodes, []int{4}) + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(core.MetachainShardId), syncedNodes, []int{5}) + + // check out of sync regular nodes + compareNodesBasedOnIndices(t, nh.GetOutOfSyncNodes(0), syncedNodes, []int{0}) + compareNodesBasedOnIndices(t, nh.GetOutOfSyncNodes(1), syncedNodes, []int{1}) + compareNodesBasedOnIndices(t, nh.GetOutOfSyncNodes(core.MetachainShardId), syncedNodes, []int{2}) + + // check synced fallback nodes + compareNodesBasedOnIndices(t, nh.GetSyncedFallbackNodes(0), syncedNodes, []int{0, 3}) + compareNodesBasedOnIndices(t, nh.GetSyncedFallbackNodes(1), syncedNodes, []int{4}) + compareNodesBasedOnIndices(t, nh.GetSyncedFallbackNodes(core.MetachainShardId), syncedNodes, []int{2, 5}) + + // check out of sync fallback nodes + require.Empty(t, nh.GetOutOfSyncFallbackNodes(0)) + compareNodesBasedOnIndices(t, nh.GetOutOfSyncFallbackNodes(1), syncedNodes, []int{1}) + require.Empty(t, nh.GetOutOfSyncFallbackNodes(core.MetachainShardId)) + }) +} - require.Equal(t, []*data.NodeData{fallbackNodes[0]}, nh.GetSyncedFallbackNodes(0)) - require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetSyncedFallbackNodes(1)) - require.Equal(t, []*data.NodeData{fallbackNodes[2]}, nh.GetSyncedFallbackNodes(core.MetachainShardId)) +func compareNodesBasedOnIndices(t *testing.T, firstSlice []*data.NodeData, secondSlice []*data.NodeData, indices []int) { + if len(firstSlice) > len(indices) { + t.Fail() + } - setPropertyToNodes(syncedNodes, "synced", false, 0, 2) - setPropertyToNodes(fallbackNodes, "synced", false, 1) - nh.UpdateNodes(append(syncedNodes, fallbackNodes...)) - require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetOutOfSyncNodes(0)) - require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncNodes(1)) - require.Equal(t, []*data.NodeData{syncedNodes[2]}, nh.GetOutOfSyncNodes(core.MetachainShardId)) - - require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncFallbackNodes(0)) - require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetOutOfSyncFallbackNodes(1)) - require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncFallbackNodes(core.MetachainShardId)) - require.Equal(t, 6, nh.Count()) + if len(firstSlice) == 0 { + t.Fail() + } + + for i, node := range firstSlice { + indexInSecondSlice := indices[i] + if indexInSecondSlice > len(secondSlice) { + t.Fail() + } + require.Equal(t, node.Address, secondSlice[indexInSecondSlice].Address) + } +} + +func getIndicesOfNodesInShard(nodes []*data.NodeData, shardID uint32) []int { + intSlice := make([]int, 0) + for idx, node := range nodes { + if node.ShardId != shardID { + continue + } + + intSlice = append(intSlice, idx) + } + + return intSlice +} + +func TestNodesHolder_Count(t *testing.T) { + t.Parallel() + + syncedNodes := createTestNodes(3) + nh, _ := NewNodesHolder(syncedNodes, syncedNodes, data.AvailabilityAll) + require.Equal(t, 2*len(syncedNodes), nh.Count()) } func TestNodesHolder_IsInterfaceNil(t *testing.T) { @@ -153,7 +248,7 @@ func TestNodesHolder_ConcurrentOperations(t *testing.T) { fallbackNodes := createTestNodes(100) nh, _ := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityRecent) - numOperations := 100_000 + numOperations := 1_000 wg := sync.WaitGroup{} wg.Add(numOperations) for i := 0; i < numOperations; i++ { @@ -173,7 +268,7 @@ func TestNodesHolder_ConcurrentOperations(t *testing.T) { _ = nh.GetOutOfSyncNodes(uint32(index % 3)) } wg.Done() - }(i) + }(i % 6) } wg.Wait() } @@ -181,8 +276,6 @@ func TestNodesHolder_ConcurrentOperations(t *testing.T) { func createTestNodes(numNodes int) []*data.NodeData { getShard := func(index int) uint32 { switch index % 3 { - case 0: - return 0 case 1: return 1 case 2: diff --git a/process/accountProcessor.go b/process/accountProcessor.go index a916af85..5a1161a1 100644 --- a/process/accountProcessor.go +++ b/process/accountProcessor.go @@ -9,6 +9,7 @@ import ( "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" + "github.com/multiversx/mx-chain-proxy-go/observer/availabilityCommon" ) // addressPath defines the address path at which the nodes answer @@ -16,9 +17,10 @@ const addressPath = "/address/" // AccountProcessor is able to process account requests type AccountProcessor struct { - connector ExternalStorageConnector - proc Processor - pubKeyConverter core.PubkeyConverter + connector ExternalStorageConnector + proc Processor + pubKeyConverter core.PubkeyConverter + availabilityProvider availabilityCommon.AvailabilityProvider } // NewAccountProcessor creates a new instance of AccountProcessor @@ -34,9 +36,10 @@ func NewAccountProcessor(proc Processor, pubKeyConverter core.PubkeyConverter, c } return &AccountProcessor{ - proc: proc, - pubKeyConverter: pubKeyConverter, - connector: connector, + proc: proc, + pubKeyConverter: pubKeyConverter, + connector: connector, + availabilityProvider: availabilityCommon.AvailabilityProvider{}, }, nil } @@ -50,9 +53,9 @@ func (ap *AccountProcessor) GetShardIDForAddress(address string) (uint32, error) return ap.proc.ComputeShardId(addressBytes) } -// GetAccount resolves the request by sending the request to the right observer and replies back the answer +// GetAccount resolves the request by sending the request to the right observer and returns the response func (ap *AccountProcessor) GetAccount(address string, options common.AccountQueryOptions) (*data.AccountModel, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -76,7 +79,7 @@ func (ap *AccountProcessor) GetAccount(address string, options common.AccountQue // GetValueForKey returns the value for the given address and key func (ap *AccountProcessor) GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return "", err @@ -108,7 +111,7 @@ func (ap *AccountProcessor) GetValueForKey(address string, key string, options c // GetESDTTokenData returns the token data for a token with the given name func (ap *AccountProcessor) GetESDTTokenData(address string, key string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -141,7 +144,7 @@ func (ap *AccountProcessor) GetESDTTokenData(address string, key string, options // GetESDTsWithRole returns the token identifiers where the given address has the given role assigned func (ap *AccountProcessor) GetESDTsWithRole(address string, role string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err @@ -174,7 +177,7 @@ func (ap *AccountProcessor) GetESDTsWithRole(address string, role string, option // GetESDTsRoles returns all the tokens and their roles for a given address func (ap *AccountProcessor) GetESDTsRoles(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err @@ -208,7 +211,7 @@ func (ap *AccountProcessor) GetESDTsRoles(address string, options common.Account func (ap *AccountProcessor) GetNFTTokenIDsRegisteredByAddress(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { //TODO: refactor the entire proxy so endpoints like this which simply forward the response will use a common // component, as described in task EN-9857. - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err @@ -240,7 +243,7 @@ func (ap *AccountProcessor) GetNFTTokenIDsRegisteredByAddress(address string, op // GetESDTNftTokenData returns the nft token data for a token with the given identifier and nonce func (ap *AccountProcessor) GetESDTNftTokenData(address string, key string, nonce uint64, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -274,7 +277,7 @@ func (ap *AccountProcessor) GetESDTNftTokenData(address string, key string, nonc // GetAllESDTTokens returns all the tokens for a given address func (ap *AccountProcessor) GetAllESDTTokens(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -306,7 +309,7 @@ func (ap *AccountProcessor) GetAllESDTTokens(address string, options common.Acco // GetKeyValuePairs returns all the key-value pairs for a given address func (ap *AccountProcessor) GetKeyValuePairs(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -338,7 +341,7 @@ func (ap *AccountProcessor) GetKeyValuePairs(address string, options common.Acco // GetGuardianData returns the guardian data for the given address func (ap *AccountProcessor) GetGuardianData(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -379,7 +382,7 @@ func (ap *AccountProcessor) GetTransactions(address string) ([]data.DatabaseTran // GetCodeHash returns the code hash for a given address func (ap *AccountProcessor) GetCodeHash(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -464,10 +467,6 @@ func (ap *AccountProcessor) IsDataTrieMigrated(address string, options common.Ac return nil, ErrSendingRequest } -func getAvailabilityBasedOnAccountQueryOptions(options common.AccountQueryOptions) data.ObserverDataAvailabilityType { - availability := data.AvailabilityRecent - if options.AreHistoricalCoordinatesSet() { - availability = data.AvailabilityAll - } - return availability +func (ap *AccountProcessor) getAvailabilityBasedOnAccountQueryOptions(options common.AccountQueryOptions) data.ObserverDataAvailabilityType { + return ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) } diff --git a/process/scQueryProcessor.go b/process/scQueryProcessor.go index 24508ac5..0ba01ead 100644 --- a/process/scQueryProcessor.go +++ b/process/scQueryProcessor.go @@ -10,6 +10,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/vm" "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/multiversx/mx-chain-proxy-go/observer/availabilityCommon" ) // scQueryServicePath defines the get values path at which the nodes answer @@ -19,8 +20,9 @@ const blockHash = "blockHash" // SCQueryProcessor is able to process smart contract queries type SCQueryProcessor struct { - proc Processor - pubKeyConverter core.PubkeyConverter + proc Processor + pubKeyConverter core.PubkeyConverter + availabilityProvider availabilityCommon.AvailabilityProvider } // NewSCQueryProcessor creates a new instance of SCQueryProcessor @@ -33,8 +35,9 @@ func NewSCQueryProcessor(proc Processor, pubKeyConverter core.PubkeyConverter) ( } return &SCQueryProcessor{ - proc: proc, - pubKeyConverter: pubKeyConverter, + proc: proc, + pubKeyConverter: pubKeyConverter, + availabilityProvider: availabilityCommon.AvailabilityProvider{}, }, nil } @@ -50,7 +53,7 @@ func (scQueryProcessor *SCQueryProcessor) ExecuteQuery(query *data.SCQuery) (*vm return nil, data.BlockInfo{}, err } - availability := getAvailabilityBasedOnVmQueryOptions(query) + availability := scQueryProcessor.availabilityProvider.AvailabilityForVmQuery(query) observers, err := scQueryProcessor.proc.GetObservers(shardID, availability) if err != nil { return nil, data.BlockInfo{}, err @@ -118,11 +121,3 @@ func (scQueryProcessor *SCQueryProcessor) createRequestFromQuery(query *data.SCQ func (scQueryProcessor *SCQueryProcessor) IsInterfaceNil() bool { return scQueryProcessor == nil } - -func getAvailabilityBasedOnVmQueryOptions(query *data.SCQuery) data.ObserverDataAvailabilityType { - availability := data.AvailabilityRecent - if query.BlockNonce.HasValue || len(query.BlockHash) > 0 { - availability = data.AvailabilityAll - } - return availability -}