diff --git a/api/groups/nodeGroup.go b/api/groups/nodeGroup.go index 021ad389ed7..af87d97326f 100644 --- a/api/groups/nodeGroup.go +++ b/api/groups/nodeGroup.go @@ -28,6 +28,7 @@ const ( bootstrapStatusPath = "/bootstrapstatus" connectedPeersRatingsPath = "/connected-peers-ratings" managedKeys = "/managed-keys" + loadedKeys = "/loaded-keys" managedKeysCount = "/managed-keys/count" eligibleManagedKeys = "/managed-keys/eligible" waitingManagedKeys = "/managed-keys/waiting" @@ -43,6 +44,7 @@ type nodeFacadeHandler interface { GetConnectedPeersRatingsOnMainNetwork() (string, error) GetManagedKeysCount() int GetManagedKeys() []string + GetLoadedKeys() []string GetEligibleManagedKeys() ([]string, error) GetWaitingManagedKeys() ([]string, error) IsInterfaceNil() bool @@ -127,6 +129,11 @@ func NewNodeGroup(facade nodeFacadeHandler) (*nodeGroup, error) { Method: http.MethodGet, Handler: ng.managedKeys, }, + { + Path: loadedKeys, + Method: http.MethodGet, + Handler: ng.loadedKeys, + }, { Path: eligibleManagedKeys, Method: http.MethodGet, @@ -404,6 +411,19 @@ func (ng *nodeGroup) managedKeys(c *gin.Context) { ) } +// loadedKeys returns all keys loaded by the current node +func (ng *nodeGroup) loadedKeys(c *gin.Context) { + keys := ng.getFacade().GetLoadedKeys() + c.JSON( + http.StatusOK, + shared.GenericAPIResponse{ + Data: gin.H{"loadedKeys": keys}, + Error: "", + Code: shared.ReturnCodeSuccess, + }, + ) +} + // managedKeysEligible returns the node's eligible managed keys func (ng *nodeGroup) managedKeysEligible(c *gin.Context) { keys, err := ng.getFacade().GetEligibleManagedKeys() diff --git a/api/groups/nodeGroup_test.go b/api/groups/nodeGroup_test.go index a46d140e598..483f0139009 100644 --- a/api/groups/nodeGroup_test.go +++ b/api/groups/nodeGroup_test.go @@ -81,6 +81,13 @@ type managedKeysResponse struct { generalResponse } +type loadedKeysResponse struct { + Data struct { + LoadedKeys []string `json:"loadedKeys"` + } `json:"data"` + generalResponse +} + type managedEligibleKeysResponse struct { Data struct { Keys []string `json:"eligibleKeys"` @@ -733,6 +740,36 @@ func TestNodeGroup_ManagedKeys(t *testing.T) { assert.Equal(t, providedKeys, response.Data.ManagedKeys) } +func TestNodeGroup_LoadedKeys(t *testing.T) { + t.Parallel() + + providedKeys := []string{ + "pk1", + "pk2", + } + facade := mock.FacadeStub{ + GetLoadedKeysCalled: func() []string { + return providedKeys + }, + } + + nodeGroup, err := groups.NewNodeGroup(&facade) + require.NoError(t, err) + + ws := startWebServer(nodeGroup, "node", getNodeRoutesConfig()) + + req, _ := http.NewRequest("GET", "/node/loaded-keys", nil) + resp := httptest.NewRecorder() + ws.ServeHTTP(resp, req) + + response := &loadedKeysResponse{} + loadResponse(resp.Body, response) + + assert.Equal(t, http.StatusOK, resp.Code) + assert.Equal(t, "", response.Error) + assert.Equal(t, providedKeys, response.Data.LoadedKeys) +} + func TestNodeGroup_ManagedKeysEligible(t *testing.T) { t.Parallel() @@ -960,6 +997,7 @@ func getNodeRoutesConfig() config.ApiRoutesConfig { {Name: "/connected-peers-ratings", Open: true}, {Name: "/managed-keys/count", Open: true}, {Name: "/managed-keys", Open: true}, + {Name: "/loaded-keys", Open: true}, {Name: "/managed-keys/eligible", Open: true}, {Name: "/managed-keys/waiting", Open: true}, }, diff --git a/api/mock/facadeStub.go b/api/mock/facadeStub.go index 366af9dd218..e42534a1e57 100644 --- a/api/mock/facadeStub.go +++ b/api/mock/facadeStub.go @@ -91,6 +91,7 @@ type FacadeStub struct { IsDataTrieMigratedCalled func(address string, options api.AccountQueryOptions) (bool, error) GetManagedKeysCountCalled func() int GetManagedKeysCalled func() []string + GetLoadedKeysCalled func() []string GetEligibleManagedKeysCalled func() ([]string, error) GetWaitingManagedKeysCalled func() ([]string, error) } @@ -594,6 +595,14 @@ func (f *FacadeStub) GetManagedKeys() []string { return make([]string, 0) } +// GetLoadedKeys - +func (f *FacadeStub) GetLoadedKeys() []string { + if f.GetLoadedKeysCalled != nil { + return f.GetLoadedKeysCalled() + } + return make([]string, 0) +} + // GetEligibleManagedKeys - func (f *FacadeStub) GetEligibleManagedKeys() ([]string, error) { if f.GetEligibleManagedKeysCalled != nil { diff --git a/api/shared/interface.go b/api/shared/interface.go index 0b199393b96..0f278fbe95c 100644 --- a/api/shared/interface.go +++ b/api/shared/interface.go @@ -130,6 +130,7 @@ type FacadeHandler interface { IsDataTrieMigrated(address string, options api.AccountQueryOptions) (bool, error) GetManagedKeysCount() int GetManagedKeys() []string + GetLoadedKeys() []string GetEligibleManagedKeys() ([]string, error) GetWaitingManagedKeys() ([]string, error) IsInterfaceNil() bool diff --git a/cmd/node/config/api.toml b/cmd/node/config/api.toml index e444d9d5c65..f7d2d66cb8c 100644 --- a/cmd/node/config/api.toml +++ b/cmd/node/config/api.toml @@ -43,6 +43,9 @@ # /node/managed-keys will return the keys managed by the node { Name = "/managed-keys", Open = true }, + # /node/loaded-keys will return the keys loaded by the node + { Name = "/loaded-keys", Open = true }, + # /node/managed-keys/count will return the number of keys managed by the node { Name = "/managed-keys/count", Open = true }, diff --git a/cmd/node/config/config.toml b/cmd/node/config/config.toml index 184bf0db1ac..0e4bdf0c9fb 100644 --- a/cmd/node/config/config.toml +++ b/cmd/node/config/config.toml @@ -519,6 +519,7 @@ [Antiflood] Enabled = true NumConcurrentResolverJobs = 50 + NumConcurrentResolvingTrieNodesJobs = 3 [Antiflood.FastReacting] IntervalInSeconds = 1 ReservedPercent = 20.0 diff --git a/cmd/node/config/fullArchiveP2P.toml b/cmd/node/config/fullArchiveP2P.toml index ba6e76c4c01..6e9010a4c34 100644 --- a/cmd/node/config/fullArchiveP2P.toml +++ b/cmd/node/config/fullArchiveP2P.toml @@ -23,10 +23,11 @@ [Node.Transports.TCP] ListenAddress = "/ip4/0.0.0.0/tcp/%d" # TCP listen address PreventPortReuse = false - [Node.ResourceLimiter] - Type = "default autoscale" #available options "default autoscale", "infinite", "default with manual scale". - ManualSystemMemoryInMB = 0 # not taken into account if the type is not "default with manual scale" - ManualMaximumFD = 0 # not taken into account if the type is not "default with manual scale" + + [Node.ResourceLimiter] + Type = "default autoscale" #available options "default autoscale", "infinite", "default with manual scale". + ManualSystemMemoryInMB = 0 # not taken into account if the type is not "default with manual scale" + ManualMaximumFD = 0 # not taken into account if the type is not "default with manual scale" # P2P peer discovery section diff --git a/cmd/node/config/p2p.toml b/cmd/node/config/p2p.toml index e8df20bef59..519b7684b2c 100644 --- a/cmd/node/config/p2p.toml +++ b/cmd/node/config/p2p.toml @@ -23,10 +23,11 @@ [Node.Transports.TCP] ListenAddress = "/ip4/0.0.0.0/tcp/%d" # TCP listen address PreventPortReuse = false - [Node.ResourceLimiter] - Type = "default autoscale" #available options "default autoscale", "infinite", "default with manual scale". - ManualSystemMemoryInMB = 0 # not taken into account if the type is not "default with manual scale" - ManualMaximumFD = 0 # not taken into account if the type is not "default with manual scale" + + [Node.ResourceLimiter] + Type = "default autoscale" #available options "default autoscale", "infinite", "default with manual scale". + ManualSystemMemoryInMB = 0 # not taken into account if the type is not "default with manual scale" + ManualMaximumFD = 0 # not taken into account if the type is not "default with manual scale" # P2P peer discovery section diff --git a/cmd/node/flags.go b/cmd/node/flags.go index f40de41ef86..3f55c187060 100644 --- a/cmd/node/flags.go +++ b/cmd/node/flags.go @@ -632,7 +632,8 @@ func applyCompatibleConfigs(log logger.Logger, configs *config.Configs) error { isInHistoricalBalancesMode := operationmodes.SliceContainsElement(operationModes, operationmodes.OperationModeHistoricalBalances) if isInHistoricalBalancesMode { - processHistoricalBalancesMode(log, configs) + // TODO move all operation modes settings in the common/operationmodes package and add tests + operationmodes.ProcessHistoricalBalancesMode(log, configs) } isInDbLookupExtensionMode := operationmodes.SliceContainsElement(operationModes, operationmodes.OperationModeDbLookupExtension) @@ -648,28 +649,6 @@ func applyCompatibleConfigs(log logger.Logger, configs *config.Configs) error { return nil } -func processHistoricalBalancesMode(log logger.Logger, configs *config.Configs) { - configs.GeneralConfig.StoragePruning.Enabled = true - configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData = false - configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData = false - configs.GeneralConfig.GeneralSettings.StartInEpochEnabled = false - configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData = false - configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled = false - configs.GeneralConfig.DbLookupExtensions.Enabled = true - configs.PreferencesConfig.Preferences.FullArchive = true - - log.Warn("the node is in historical balances mode! Will auto-set some config values", - "StoragePruning.Enabled", configs.GeneralConfig.StoragePruning.Enabled, - "StoragePruning.ValidatorCleanOldEpochsData", configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData, - "StoragePruning.ObserverCleanOldEpochsData", configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData, - "StoragePruning.AccountsTrieCleanOldEpochsData", configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData, - "GeneralSettings.StartInEpochEnabled", configs.GeneralConfig.GeneralSettings.StartInEpochEnabled, - "StateTriesConfig.AccountsStatePruningEnabled", configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled, - "DbLookupExtensions.Enabled", configs.GeneralConfig.DbLookupExtensions.Enabled, - "Preferences.FullArchive", configs.PreferencesConfig.Preferences.FullArchive, - ) -} - func processDbLookupExtensionMode(log logger.Logger, configs *config.Configs) { configs.GeneralConfig.DbLookupExtensions.Enabled = true configs.GeneralConfig.StoragePruning.Enabled = true diff --git a/cmd/seednode/config/p2p.toml b/cmd/seednode/config/p2p.toml index 2c1a92717c9..8ddd4a72e4a 100644 --- a/cmd/seednode/config/p2p.toml +++ b/cmd/seednode/config/p2p.toml @@ -22,10 +22,11 @@ [Node.Transports.TCP] ListenAddress = "/ip4/0.0.0.0/tcp/%d" # TCP listen address PreventPortReuse = true # seeder nodes will need to enable this option - [Node.ResourceLimiter] - Type = "default with manual scale" - ManualSystemMemoryInMB = 65536 # pretend that the host running the seeder has more RAM so it can handle more connections - ManualMaximumFD = 1048576 + + [Node.ResourceLimiter] + Type = "default with manual scale" + ManualSystemMemoryInMB = 65536 # pretend that the host running the seeder has more RAM so it can handle more connections + ManualMaximumFD = 1048576 # P2P peer discovery section diff --git a/common/interface.go b/common/interface.go index 010d55e22d5..2e14c33730e 100644 --- a/common/interface.go +++ b/common/interface.go @@ -414,6 +414,7 @@ type ManagedPeersHolder interface { IncrementRoundsWithoutReceivedMessages(pkBytes []byte) ResetRoundsWithoutReceivedMessages(pkBytes []byte, pid core.PeerID) GetManagedKeysByCurrentNode() map[string]crypto.PrivateKey + GetLoadedKeysByCurrentNode() [][]byte IsKeyManagedByCurrentNode(pkBytes []byte) bool IsKeyRegistered(pkBytes []byte) bool IsPidManagedByCurrentNode(pid core.PeerID) bool @@ -443,6 +444,7 @@ type StateSyncNotifierSubscriber interface { type ManagedPeersMonitor interface { GetManagedKeysCount() int GetManagedKeys() [][]byte + GetLoadedKeys() [][]byte GetEligibleManagedKeys() ([][]byte, error) GetWaitingManagedKeys() ([][]byte, error) IsInterfaceNil() bool diff --git a/common/operationmodes/historicalBalances.go b/common/operationmodes/historicalBalances.go new file mode 100644 index 00000000000..da3cfe98dde --- /dev/null +++ b/common/operationmodes/historicalBalances.go @@ -0,0 +1,41 @@ +package operationmodes + +import ( + "github.com/multiversx/mx-chain-go/config" + logger "github.com/multiversx/mx-chain-logger-go" +) + +// ProcessHistoricalBalancesMode will process the provided flags for the historical balances +func ProcessHistoricalBalancesMode(log logger.Logger, configs *config.Configs) { + configs.GeneralConfig.StoragePruning.Enabled = true + configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData = false + configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData = false + configs.GeneralConfig.GeneralSettings.StartInEpochEnabled = false + configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData = false + configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled = false + configs.GeneralConfig.DbLookupExtensions.Enabled = true + configs.PreferencesConfig.Preferences.FullArchive = true + + log.Warn("the node is in historical balances mode! Will auto-set some config values", + "StoragePruning.Enabled", configs.GeneralConfig.StoragePruning.Enabled, + "StoragePruning.ValidatorCleanOldEpochsData", configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData, + "StoragePruning.ObserverCleanOldEpochsData", configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData, + "StoragePruning.AccountsTrieCleanOldEpochsData", configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData, + "GeneralSettings.StartInEpochEnabled", configs.GeneralConfig.GeneralSettings.StartInEpochEnabled, + "StateTriesConfig.AccountsStatePruningEnabled", configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled, + "DbLookupExtensions.Enabled", configs.GeneralConfig.DbLookupExtensions.Enabled, + "Preferences.FullArchive", configs.PreferencesConfig.Preferences.FullArchive, + ) +} + +// IsInHistoricalBalancesMode returns true if the configuration provided denotes a historical balances mode +func IsInHistoricalBalancesMode(configs *config.Configs) bool { + return configs.GeneralConfig.StoragePruning.Enabled && + !configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData && + !configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData && + !configs.GeneralConfig.GeneralSettings.StartInEpochEnabled && + !configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData && + !configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled && + configs.GeneralConfig.DbLookupExtensions.Enabled && + configs.PreferencesConfig.Preferences.FullArchive +} diff --git a/common/operationmodes/historicalBalances_test.go b/common/operationmodes/historicalBalances_test.go new file mode 100644 index 00000000000..d06061c3027 --- /dev/null +++ b/common/operationmodes/historicalBalances_test.go @@ -0,0 +1,141 @@ +package operationmodes + +import ( + "testing" + + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/stretchr/testify/assert" +) + +func TestProcessHistoricalBalancesMode(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + + assert.True(t, cfg.GeneralConfig.StoragePruning.Enabled) + assert.False(t, cfg.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData) + assert.False(t, cfg.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData) + assert.False(t, cfg.GeneralConfig.GeneralSettings.StartInEpochEnabled) + assert.False(t, cfg.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData) + assert.False(t, cfg.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled) + assert.True(t, cfg.GeneralConfig.DbLookupExtensions.Enabled) + assert.True(t, cfg.PreferencesConfig.Preferences.FullArchive) +} + +func TestIsInHistoricalBalancesMode(t *testing.T) { + t.Parallel() + + t.Run("empty configs should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("storage pruning disabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StoragePruning.Enabled = false + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("validator clean old epoch data enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("observer clean old epoch data enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("start in epoch enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.GeneralSettings.StartInEpochEnabled = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("accounts trie clean old epoch data enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("accounts state pruning enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("db lookup extension disabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.DbLookupExtensions.Enabled = false + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("not a full archive node should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.PreferencesConfig.Preferences.FullArchive = false + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("with historical balances config should return true", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + assert.True(t, IsInHistoricalBalancesMode(cfg)) + }) + +} diff --git a/common/operationmodes/operationmodes.go b/common/operationmodes/operationmodes.go index 70aed256f4b..1ae6a6fad70 100644 --- a/common/operationmodes/operationmodes.go +++ b/common/operationmodes/operationmodes.go @@ -5,6 +5,7 @@ import ( "strings" ) +// constants that define the operation mode of the node const ( OperationModeFullArchive = "full-archive" OperationModeDbLookupExtension = "db-lookup-extension" diff --git a/config/config.go b/config/config.go index b53e46a2201..366e288ee8e 100644 --- a/config/config.go +++ b/config/config.go @@ -362,15 +362,16 @@ type TxAccumulatorConfig struct { // AntifloodConfig will hold all p2p antiflood parameters type AntifloodConfig struct { - Enabled bool - NumConcurrentResolverJobs int32 - OutOfSpecs FloodPreventerConfig - FastReacting FloodPreventerConfig - SlowReacting FloodPreventerConfig - PeerMaxOutput AntifloodLimitsConfig - Cache CacheConfig - Topic TopicAntifloodConfig - TxAccumulator TxAccumulatorConfig + Enabled bool + NumConcurrentResolverJobs int32 + NumConcurrentResolvingTrieNodesJobs int32 + OutOfSpecs FloodPreventerConfig + FastReacting FloodPreventerConfig + SlowReacting FloodPreventerConfig + PeerMaxOutput AntifloodLimitsConfig + Cache CacheConfig + Topic TopicAntifloodConfig + TxAccumulator TxAccumulatorConfig } // FloodPreventerConfig will hold all flood preventer parameters diff --git a/config/tomlConfig_test.go b/config/tomlConfig_test.go index 5b8fa879f6e..d2edb2a4bbf 100644 --- a/config/tomlConfig_test.go +++ b/config/tomlConfig_test.go @@ -493,10 +493,11 @@ func TestP2pConfig(t *testing.T) { [Node.Transports.TCP] ListenAddress = "/ip4/0.0.0.0/tcp/%d" PreventPortReuse = true - [Node.ResourceLimiter] - Type = "default autoscale" #available options "default autoscale", "infinite", "default with manual scale". - ManualSystemMemoryInMB = 1 # not taken into account if the type is not "default with manual scale" - ManualMaximumFD = 2 # not taken into account if the type is not "default with manual scale" + + [Node.ResourceLimiter] + Type = "default autoscale" #available options "default autoscale", "infinite", "default with manual scale". + ManualSystemMemoryInMB = 1 # not taken into account if the type is not "default with manual scale" + ManualMaximumFD = 2 # not taken into account if the type is not "default with manual scale" [KadDhtPeerDiscovery] Enabled = false diff --git a/dataRetriever/factory/resolverscontainer/args.go b/dataRetriever/factory/resolverscontainer/args.go index 1446af01b97..d0001014a4d 100644 --- a/dataRetriever/factory/resolverscontainer/args.go +++ b/dataRetriever/factory/resolverscontainer/args.go @@ -11,21 +11,22 @@ import ( // FactoryArgs will hold the arguments for ResolversContainerFactory for both shard and meta type FactoryArgs struct { - NumConcurrentResolvingJobs int32 - ShardCoordinator sharding.Coordinator - MainMessenger p2p.Messenger - FullArchiveMessenger p2p.Messenger - Store dataRetriever.StorageService - Marshalizer marshal.Marshalizer - DataPools dataRetriever.PoolsHolder - Uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter - DataPacker dataRetriever.DataPacker - TriesContainer common.TriesHolder - InputAntifloodHandler dataRetriever.P2PAntifloodHandler - OutputAntifloodHandler dataRetriever.P2PAntifloodHandler - MainPreferredPeersHolder p2p.PreferredPeersHolderHandler - FullArchivePreferredPeersHolder p2p.PreferredPeersHolderHandler - SizeCheckDelta uint32 - IsFullHistoryNode bool - PayloadValidator dataRetriever.PeerAuthenticationPayloadValidator + NumConcurrentResolvingJobs int32 + NumConcurrentResolvingTrieNodesJobs int32 + ShardCoordinator sharding.Coordinator + MainMessenger p2p.Messenger + FullArchiveMessenger p2p.Messenger + Store dataRetriever.StorageService + Marshalizer marshal.Marshalizer + DataPools dataRetriever.PoolsHolder + Uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + DataPacker dataRetriever.DataPacker + TriesContainer common.TriesHolder + InputAntifloodHandler dataRetriever.P2PAntifloodHandler + OutputAntifloodHandler dataRetriever.P2PAntifloodHandler + MainPreferredPeersHolder p2p.PreferredPeersHolderHandler + FullArchivePreferredPeersHolder p2p.PreferredPeersHolderHandler + SizeCheckDelta uint32 + IsFullHistoryNode bool + PayloadValidator dataRetriever.PeerAuthenticationPayloadValidator } diff --git a/dataRetriever/factory/resolverscontainer/baseResolversContainerFactory.go b/dataRetriever/factory/resolverscontainer/baseResolversContainerFactory.go index c1fc1e3a16b..3d0eff8eaa9 100644 --- a/dataRetriever/factory/resolverscontainer/baseResolversContainerFactory.go +++ b/dataRetriever/factory/resolverscontainer/baseResolversContainerFactory.go @@ -36,6 +36,7 @@ type baseResolversContainerFactory struct { inputAntifloodHandler dataRetriever.P2PAntifloodHandler outputAntifloodHandler dataRetriever.P2PAntifloodHandler throttler dataRetriever.ResolverThrottler + trieNodesThrottler dataRetriever.ResolverThrottler intraShardTopic string isFullHistoryNode bool mainPreferredPeersHolder dataRetriever.PreferredPeersHolderHandler @@ -78,7 +79,10 @@ func (brcf *baseResolversContainerFactory) checkParams() error { return fmt.Errorf("%w for output", dataRetriever.ErrNilAntifloodHandler) } if check.IfNil(brcf.throttler) { - return dataRetriever.ErrNilThrottler + return fmt.Errorf("%w for the main throttler", dataRetriever.ErrNilThrottler) + } + if check.IfNil(brcf.trieNodesThrottler) { + return fmt.Errorf("%w for the trie nodes throttler", dataRetriever.ErrNilThrottler) } if check.IfNil(brcf.mainPreferredPeersHolder) { return fmt.Errorf("%w for main network", dataRetriever.ErrNilPreferredPeersHolder) @@ -351,7 +355,7 @@ func (brcf *baseResolversContainerFactory) createTrieNodesResolver( SenderResolver: resolverSender, Marshaller: brcf.marshalizer, AntifloodHandler: brcf.inputAntifloodHandler, - Throttler: brcf.throttler, + Throttler: brcf.trieNodesThrottler, }, TrieDataGetter: trie, } diff --git a/dataRetriever/factory/resolverscontainer/metaResolversContainerFactory.go b/dataRetriever/factory/resolverscontainer/metaResolversContainerFactory.go index 426a978ae20..b72f8c3154a 100644 --- a/dataRetriever/factory/resolverscontainer/metaResolversContainerFactory.go +++ b/dataRetriever/factory/resolverscontainer/metaResolversContainerFactory.go @@ -27,7 +27,12 @@ func NewMetaResolversContainerFactory( args.Marshalizer = marshal.NewSizeCheckUnmarshalizer(args.Marshalizer, args.SizeCheckDelta) } - thr, err := throttler.NewNumGoRoutinesThrottler(args.NumConcurrentResolvingJobs) + mainThrottler, err := throttler.NewNumGoRoutinesThrottler(args.NumConcurrentResolvingJobs) + if err != nil { + return nil, err + } + + trieNodesThrottler, err := throttler.NewNumGoRoutinesThrottler(args.NumConcurrentResolvingTrieNodesJobs) if err != nil { return nil, err } @@ -46,7 +51,8 @@ func NewMetaResolversContainerFactory( triesContainer: args.TriesContainer, inputAntifloodHandler: args.InputAntifloodHandler, outputAntifloodHandler: args.OutputAntifloodHandler, - throttler: thr, + throttler: mainThrottler, + trieNodesThrottler: trieNodesThrottler, isFullHistoryNode: args.IsFullHistoryNode, mainPreferredPeersHolder: args.MainPreferredPeersHolder, fullArchivePreferredPeersHolder: args.FullArchivePreferredPeersHolder, diff --git a/dataRetriever/factory/resolverscontainer/metaResolversContainerFactory_test.go b/dataRetriever/factory/resolverscontainer/metaResolversContainerFactory_test.go index c6659693d79..755672384cd 100644 --- a/dataRetriever/factory/resolverscontainer/metaResolversContainerFactory_test.go +++ b/dataRetriever/factory/resolverscontainer/metaResolversContainerFactory_test.go @@ -94,8 +94,15 @@ func TestNewMetaResolversContainerFactory_NewNumGoRoutinesThrottlerFailsShouldEr args := getArgumentsMeta() args.NumConcurrentResolvingJobs = 0 + rcf, err := resolverscontainer.NewMetaResolversContainerFactory(args) + assert.Nil(t, rcf) + assert.Equal(t, core.ErrNotPositiveValue, err) + + args.NumConcurrentResolvingJobs = 10 + args.NumConcurrentResolvingTrieNodesJobs = 0 + rcf, err = resolverscontainer.NewMetaResolversContainerFactory(args) assert.Nil(t, rcf) assert.Equal(t, core.ErrNotPositiveValue, err) } @@ -357,21 +364,22 @@ func TestMetaResolversContainerFactory_IsInterfaceNil(t *testing.T) { func getArgumentsMeta() resolverscontainer.FactoryArgs { return resolverscontainer.FactoryArgs{ - ShardCoordinator: mock.NewOneShardCoordinatorMock(), - MainMessenger: createStubMessengerForMeta("", ""), - FullArchiveMessenger: createStubMessengerForMeta("", ""), - Store: createStoreForMeta(), - Marshalizer: &mock.MarshalizerMock{}, - DataPools: createDataPoolsForMeta(), - Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, - DataPacker: &mock.DataPackerStub{}, - TriesContainer: createTriesHolderForMeta(), - SizeCheckDelta: 0, - InputAntifloodHandler: &mock.P2PAntifloodHandlerStub{}, - OutputAntifloodHandler: &mock.P2PAntifloodHandlerStub{}, - NumConcurrentResolvingJobs: 10, - MainPreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - FullArchivePreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - PayloadValidator: &testscommon.PeerAuthenticationPayloadValidatorStub{}, + ShardCoordinator: mock.NewOneShardCoordinatorMock(), + MainMessenger: createStubMessengerForMeta("", ""), + FullArchiveMessenger: createStubMessengerForMeta("", ""), + Store: createStoreForMeta(), + Marshalizer: &mock.MarshalizerMock{}, + DataPools: createDataPoolsForMeta(), + Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, + DataPacker: &mock.DataPackerStub{}, + TriesContainer: createTriesHolderForMeta(), + SizeCheckDelta: 0, + InputAntifloodHandler: &mock.P2PAntifloodHandlerStub{}, + OutputAntifloodHandler: &mock.P2PAntifloodHandlerStub{}, + NumConcurrentResolvingJobs: 10, + NumConcurrentResolvingTrieNodesJobs: 3, + MainPreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + FullArchivePreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + PayloadValidator: &testscommon.PeerAuthenticationPayloadValidatorStub{}, } } diff --git a/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory.go b/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory.go index 28582f03bc5..f24beaa4331 100644 --- a/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory.go +++ b/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory.go @@ -25,7 +25,12 @@ func NewShardResolversContainerFactory( args.Marshalizer = marshal.NewSizeCheckUnmarshalizer(args.Marshalizer, args.SizeCheckDelta) } - thr, err := throttler.NewNumGoRoutinesThrottler(args.NumConcurrentResolvingJobs) + mainThrottler, err := throttler.NewNumGoRoutinesThrottler(args.NumConcurrentResolvingJobs) + if err != nil { + return nil, err + } + + trieNodesThrottler, err := throttler.NewNumGoRoutinesThrottler(args.NumConcurrentResolvingTrieNodesJobs) if err != nil { return nil, err } @@ -44,7 +49,8 @@ func NewShardResolversContainerFactory( triesContainer: args.TriesContainer, inputAntifloodHandler: args.InputAntifloodHandler, outputAntifloodHandler: args.OutputAntifloodHandler, - throttler: thr, + throttler: mainThrottler, + trieNodesThrottler: trieNodesThrottler, isFullHistoryNode: args.IsFullHistoryNode, mainPreferredPeersHolder: args.MainPreferredPeersHolder, fullArchivePreferredPeersHolder: args.FullArchivePreferredPeersHolder, diff --git a/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory_test.go b/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory_test.go index 4d6ca351195..ca97015f3ae 100644 --- a/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory_test.go +++ b/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory_test.go @@ -100,8 +100,15 @@ func TestNewShardResolversContainerFactory_NewNumGoRoutinesThrottlerFailsShouldE args := getArgumentsShard() args.NumConcurrentResolvingJobs = 0 + rcf, err := resolverscontainer.NewShardResolversContainerFactory(args) + assert.Nil(t, rcf) + assert.Equal(t, core.ErrNotPositiveValue, err) + + args.NumConcurrentResolvingJobs = 10 + args.NumConcurrentResolvingTrieNodesJobs = 0 + rcf, err = resolverscontainer.NewShardResolversContainerFactory(args) assert.Nil(t, rcf) assert.Equal(t, core.ErrNotPositiveValue, err) } @@ -465,21 +472,22 @@ func TestShardResolversContainerFactory_IsInterfaceNil(t *testing.T) { func getArgumentsShard() resolverscontainer.FactoryArgs { return resolverscontainer.FactoryArgs{ - ShardCoordinator: mock.NewOneShardCoordinatorMock(), - MainMessenger: createMessengerStubForShard("", ""), - FullArchiveMessenger: createMessengerStubForShard("", ""), - Store: createStoreForShard(), - Marshalizer: &mock.MarshalizerMock{}, - DataPools: createDataPoolsForShard(), - Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, - DataPacker: &mock.DataPackerStub{}, - TriesContainer: createTriesHolderForShard(), - SizeCheckDelta: 0, - InputAntifloodHandler: &mock.P2PAntifloodHandlerStub{}, - OutputAntifloodHandler: &mock.P2PAntifloodHandlerStub{}, - NumConcurrentResolvingJobs: 10, - MainPreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - FullArchivePreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - PayloadValidator: &testscommon.PeerAuthenticationPayloadValidatorStub{}, + ShardCoordinator: mock.NewOneShardCoordinatorMock(), + MainMessenger: createMessengerStubForShard("", ""), + FullArchiveMessenger: createMessengerStubForShard("", ""), + Store: createStoreForShard(), + Marshalizer: &mock.MarshalizerMock{}, + DataPools: createDataPoolsForShard(), + Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, + DataPacker: &mock.DataPackerStub{}, + TriesContainer: createTriesHolderForShard(), + SizeCheckDelta: 0, + InputAntifloodHandler: &mock.P2PAntifloodHandlerStub{}, + OutputAntifloodHandler: &mock.P2PAntifloodHandlerStub{}, + NumConcurrentResolvingJobs: 10, + NumConcurrentResolvingTrieNodesJobs: 3, + MainPreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + FullArchivePreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + PayloadValidator: &testscommon.PeerAuthenticationPayloadValidatorStub{}, } } diff --git a/dataRetriever/resolvers/trieNodeResolver.go b/dataRetriever/resolvers/trieNodeResolver.go index 871ed85fee5..275327d44c6 100644 --- a/dataRetriever/resolvers/trieNodeResolver.go +++ b/dataRetriever/resolvers/trieNodeResolver.go @@ -1,6 +1,8 @@ package resolvers import ( + "sync" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/batch" @@ -20,6 +22,7 @@ type ArgTrieNodeResolver struct { // TrieNodeResolver is a wrapper over Resolver that is specialized in resolving trie node requests type TrieNodeResolver struct { + mutCriticalSection sync.Mutex *baseResolver messageProcessor trieDataGetter dataRetriever.TrieDataGetter @@ -104,6 +107,9 @@ func (tnRes *TrieNodeResolver) resolveMultipleHashes(hashesBuff []byte, message } func (tnRes *TrieNodeResolver) resolveOnlyRequestedHashes(hashes [][]byte, nodes map[string]struct{}) (int, bool) { + tnRes.mutCriticalSection.Lock() + defer tnRes.mutCriticalSection.Unlock() + spaceUsed := 0 usedAllSpace := false remainingSpace := core.MaxBufferSizeToSendTrieNodes @@ -129,6 +135,9 @@ func (tnRes *TrieNodeResolver) resolveOnlyRequestedHashes(hashes [][]byte, nodes } func (tnRes *TrieNodeResolver) resolveSubTries(hashes [][]byte, nodes map[string]struct{}, spaceUsedAlready int) { + tnRes.mutCriticalSection.Lock() + defer tnRes.mutCriticalSection.Unlock() + var serializedNodes [][]byte var err error var serializedNode []byte @@ -168,7 +177,10 @@ func convertMapToSlice(m map[string]struct{}) [][]byte { } func (tnRes *TrieNodeResolver) resolveOneHash(hash []byte, chunkIndex uint32, message p2p.MessageP2P, source p2p.MessageHandler) error { + tnRes.mutCriticalSection.Lock() serializedNode, err := tnRes.trieDataGetter.GetSerializedNode(hash) + tnRes.mutCriticalSection.Unlock() + if err != nil { return err } diff --git a/epochStart/bootstrap/process.go b/epochStart/bootstrap/process.go index d4f3f2a58d6..5dd718ea802 100644 --- a/epochStart/bootstrap/process.go +++ b/epochStart/bootstrap/process.go @@ -1210,22 +1210,23 @@ func (e *epochStartBootstrap) createResolversContainer() error { // this one should only be used before determining the correct shard where the node should reside log.Debug("epochStartBootstrap.createRequestHandler", "shard", e.shardCoordinator.SelfId()) resolversContainerArgs := resolverscontainer.FactoryArgs{ - ShardCoordinator: e.shardCoordinator, - MainMessenger: e.mainMessenger, - FullArchiveMessenger: e.fullArchiveMessenger, - Store: storageService, - Marshalizer: e.coreComponentsHolder.InternalMarshalizer(), - DataPools: e.dataPool, - Uint64ByteSliceConverter: uint64ByteSlice.NewBigEndianConverter(), - NumConcurrentResolvingJobs: 10, - DataPacker: dataPacker, - TriesContainer: e.trieContainer, - SizeCheckDelta: 0, - InputAntifloodHandler: disabled.NewAntiFloodHandler(), - OutputAntifloodHandler: disabled.NewAntiFloodHandler(), - MainPreferredPeersHolder: disabled.NewPreferredPeersHolder(), - FullArchivePreferredPeersHolder: disabled.NewPreferredPeersHolder(), - PayloadValidator: payloadValidator, + ShardCoordinator: e.shardCoordinator, + MainMessenger: e.mainMessenger, + FullArchiveMessenger: e.fullArchiveMessenger, + Store: storageService, + Marshalizer: e.coreComponentsHolder.InternalMarshalizer(), + DataPools: e.dataPool, + Uint64ByteSliceConverter: uint64ByteSlice.NewBigEndianConverter(), + NumConcurrentResolvingJobs: 10, + NumConcurrentResolvingTrieNodesJobs: 3, + DataPacker: dataPacker, + TriesContainer: e.trieContainer, + SizeCheckDelta: 0, + InputAntifloodHandler: disabled.NewAntiFloodHandler(), + OutputAntifloodHandler: disabled.NewAntiFloodHandler(), + MainPreferredPeersHolder: disabled.NewPreferredPeersHolder(), + FullArchivePreferredPeersHolder: disabled.NewPreferredPeersHolder(), + PayloadValidator: payloadValidator, } resolverFactory, err := resolverscontainer.NewMetaResolversContainerFactory(resolversContainerArgs) if err != nil { diff --git a/facade/initial/initialNodeFacade.go b/facade/initial/initialNodeFacade.go index a8e04f2c0bd..a2237f20805 100644 --- a/facade/initial/initialNodeFacade.go +++ b/facade/initial/initialNodeFacade.go @@ -76,7 +76,7 @@ func (inf *initialNodeFacade) SetSyncer(_ ntp.SyncTimer) { } // RestAPIServerDebugMode returns false -//TODO: remove in the future +// TODO: remove in the future func (inf *initialNodeFacade) RestAPIServerDebugMode() bool { return false } @@ -416,6 +416,11 @@ func (inf *initialNodeFacade) GetManagedKeys() []string { return nil } +// GetLoadedKeys returns nil +func (inf *initialNodeFacade) GetLoadedKeys() []string { + return nil +} + // GetEligibleManagedKeys returns nil and error func (inf *initialNodeFacade) GetEligibleManagedKeys() ([]string, error) { return nil, errNodeStarting diff --git a/facade/initial/initialNodeFacade_test.go b/facade/initial/initialNodeFacade_test.go index 7298b001ba3..3c13175b6e9 100644 --- a/facade/initial/initialNodeFacade_test.go +++ b/facade/initial/initialNodeFacade_test.go @@ -316,6 +316,23 @@ func TestInitialNodeFacade_AllMethodsShouldNotPanic(t *testing.T) { assert.Nil(t, txPoolGaps) assert.Equal(t, errNodeStarting, err) + count := inf.GetManagedKeysCount() + assert.Zero(t, count) + + keys := inf.GetManagedKeys() + assert.Nil(t, keys) + + keys = inf.GetLoadedKeys() + assert.Nil(t, keys) + + keys, err = inf.GetEligibleManagedKeys() + assert.Nil(t, keys) + assert.Equal(t, errNodeStarting, err) + + keys, err = inf.GetWaitingManagedKeys() + assert.Nil(t, keys) + assert.Equal(t, errNodeStarting, err) + assert.NotNil(t, inf) } diff --git a/facade/interface.go b/facade/interface.go index 910948b57a7..32ef8b01c94 100644 --- a/facade/interface.go +++ b/facade/interface.go @@ -142,6 +142,7 @@ type ApiResolver interface { GetGasConfigs() map[string]map[string]uint64 GetManagedKeysCount() int GetManagedKeys() []string + GetLoadedKeys() []string GetEligibleManagedKeys() ([]string, error) GetWaitingManagedKeys() ([]string, error) Close() error diff --git a/facade/mock/apiResolverStub.go b/facade/mock/apiResolverStub.go index ef71463c320..aed1ffb56bd 100644 --- a/facade/mock/apiResolverStub.go +++ b/facade/mock/apiResolverStub.go @@ -46,6 +46,7 @@ type ApiResolverStub struct { GetGasConfigsCalled func() map[string]map[string]uint64 GetManagedKeysCountCalled func() int GetManagedKeysCalled func() []string + GetLoadedKeysCalled func() []string GetEligibleManagedKeysCalled func() ([]string, error) GetWaitingManagedKeysCalled func() ([]string, error) } @@ -308,6 +309,14 @@ func (ars *ApiResolverStub) GetManagedKeys() []string { return make([]string, 0) } +// GetLoadedKeys - +func (ars *ApiResolverStub) GetLoadedKeys() []string { + if ars.GetLoadedKeysCalled != nil { + return ars.GetLoadedKeysCalled() + } + return make([]string, 0) +} + // GetEligibleManagedKeys - func (ars *ApiResolverStub) GetEligibleManagedKeys() ([]string, error) { if ars.GetEligibleManagedKeysCalled != nil { diff --git a/facade/nodeFacade.go b/facade/nodeFacade.go index 77ca17669a2..00902f8ed55 100644 --- a/facade/nodeFacade.go +++ b/facade/nodeFacade.go @@ -36,7 +36,7 @@ import ( const DefaultRestInterface = "localhost:8080" // DefaultRestPortOff is the default value that should be passed if it is desired -// to start the node without a REST endpoint available +// to start the node without a REST endpoint available const DefaultRestPortOff = "off" var log = logger.GetOrCreate("facade") @@ -163,7 +163,8 @@ func (nf *nodeFacade) RestAPIServerDebugMode() bool { // RestApiInterface returns the interface on which the rest API should start on, based on the config file provided. // The API will start on the DefaultRestInterface value unless a correct value is passed or -// the value is explicitly set to off, in which case it will not start at all +// +// the value is explicitly set to off, in which case it will not start at all func (nf *nodeFacade) RestApiInterface() string { if nf.config.RestApiInterface == "" { return DefaultRestInterface @@ -590,11 +591,16 @@ func (nf *nodeFacade) GetManagedKeysCount() int { return nf.apiResolver.GetManagedKeysCount() } -// GetManagedKeys returns all keys managed by the current node when running in multikey mode +// GetManagedKeys returns all keys that should act as validator(main or backup that took over) and will be managed by this node func (nf *nodeFacade) GetManagedKeys() []string { return nf.apiResolver.GetManagedKeys() } +// GetLoadedKeys returns all keys that were loaded by this node +func (nf *nodeFacade) GetLoadedKeys() []string { + return nf.apiResolver.GetLoadedKeys() +} + // GetEligibleManagedKeys returns the eligible managed keys when node is running in multikey mode func (nf *nodeFacade) GetEligibleManagedKeys() ([]string, error) { return nf.apiResolver.GetEligibleManagedKeys() diff --git a/facade/nodeFacade_test.go b/facade/nodeFacade_test.go index b2f069f673b..9082283d945 100644 --- a/facade/nodeFacade_test.go +++ b/facade/nodeFacade_test.go @@ -2225,6 +2225,52 @@ func TestNodeFacade_GetInternalStartOfEpochMetaBlock(t *testing.T) { require.Equal(t, providedResponse, response) } +func TestNodeFacade_GetManagedKeys(t *testing.T) { + t.Parallel() + + providedCount := 100 + providedManagedKeys := []string{"pk1", "pk2"} + providedLoadedKeys := []string{"pk3", "pk4"} + providedEligibleKeys := []string{"pk5", "pk6"} + providedWaitingKeys := []string{"pk7", "pk8"} + arg := createMockArguments() + arg.ApiResolver = &mock.ApiResolverStub{ + GetManagedKeysCountCalled: func() int { + return providedCount + }, + GetManagedKeysCalled: func() []string { + return providedManagedKeys + }, + GetLoadedKeysCalled: func() []string { + return providedLoadedKeys + }, + GetEligibleManagedKeysCalled: func() ([]string, error) { + return providedEligibleKeys, nil + }, + GetWaitingManagedKeysCalled: func() ([]string, error) { + return providedWaitingKeys, nil + }, + } + nf, _ := NewNodeFacade(arg) + + count := nf.GetManagedKeysCount() + require.Equal(t, providedCount, count) + + keys := nf.GetManagedKeys() + require.Equal(t, providedManagedKeys, keys) + + keys = nf.GetLoadedKeys() + require.Equal(t, providedLoadedKeys, keys) + + keys, err := nf.GetEligibleManagedKeys() + require.Equal(t, providedEligibleKeys, keys) + require.Nil(t, err) + + keys, err = nf.GetWaitingManagedKeys() + require.Equal(t, providedWaitingKeys, keys) + require.Nil(t, err) +} + func TestNodeFacade_Close(t *testing.T) { t.Parallel() diff --git a/factory/api/apiResolverFactory.go b/factory/api/apiResolverFactory.go index bd5c1d4abc9..dc015bad188 100644 --- a/factory/api/apiResolverFactory.go +++ b/factory/api/apiResolverFactory.go @@ -11,6 +11,7 @@ import ( "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/disabled" + "github.com/multiversx/mx-chain-go/common/operationmodes" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dataRetriever/blockchain" @@ -71,40 +72,42 @@ type ApiResolverArgs struct { } type scQueryServiceArgs struct { - generalConfig *config.Config - epochConfig *config.EpochConfig - coreComponents factory.CoreComponentsHolder - stateComponents factory.StateComponentsHolder - dataComponents factory.DataComponentsHolder - processComponents factory.ProcessComponentsHolder - statusCoreComponents factory.StatusCoreComponentsHolder - gasScheduleNotifier core.GasScheduleNotifier - messageSigVerifier vm.MessageSignVerifier - systemSCConfig *config.SystemSmartContractsConfig - bootstrapper process.Bootstrapper - guardedAccountHandler process.GuardedAccountHandler - allowVMQueriesChan chan struct{} - workingDir string - processingMode common.NodeProcessingMode + generalConfig *config.Config + epochConfig *config.EpochConfig + coreComponents factory.CoreComponentsHolder + stateComponents factory.StateComponentsHolder + dataComponents factory.DataComponentsHolder + processComponents factory.ProcessComponentsHolder + statusCoreComponents factory.StatusCoreComponentsHolder + gasScheduleNotifier core.GasScheduleNotifier + messageSigVerifier vm.MessageSignVerifier + systemSCConfig *config.SystemSmartContractsConfig + bootstrapper process.Bootstrapper + guardedAccountHandler process.GuardedAccountHandler + allowVMQueriesChan chan struct{} + workingDir string + processingMode common.NodeProcessingMode + isInHistoricalBalancesMode bool } type scQueryElementArgs struct { - generalConfig *config.Config - epochConfig *config.EpochConfig - coreComponents factory.CoreComponentsHolder - stateComponents factory.StateComponentsHolder - dataComponents factory.DataComponentsHolder - processComponents factory.ProcessComponentsHolder - statusCoreComponents factory.StatusCoreComponentsHolder - gasScheduleNotifier core.GasScheduleNotifier - messageSigVerifier vm.MessageSignVerifier - systemSCConfig *config.SystemSmartContractsConfig - bootstrapper process.Bootstrapper - guardedAccountHandler process.GuardedAccountHandler - allowVMQueriesChan chan struct{} - workingDir string - index int - processingMode common.NodeProcessingMode + generalConfig *config.Config + epochConfig *config.EpochConfig + coreComponents factory.CoreComponentsHolder + stateComponents factory.StateComponentsHolder + dataComponents factory.DataComponentsHolder + processComponents factory.ProcessComponentsHolder + statusCoreComponents factory.StatusCoreComponentsHolder + gasScheduleNotifier core.GasScheduleNotifier + messageSigVerifier vm.MessageSignVerifier + systemSCConfig *config.SystemSmartContractsConfig + bootstrapper process.Bootstrapper + guardedAccountHandler process.GuardedAccountHandler + allowVMQueriesChan chan struct{} + workingDir string + index int + processingMode common.NodeProcessingMode + isInHistoricalBalancesMode bool } // CreateApiResolver is able to create an ApiResolver instance that will solve the REST API requests through the node facade @@ -112,21 +115,22 @@ type scQueryElementArgs struct { func CreateApiResolver(args *ApiResolverArgs) (facade.ApiResolver, error) { apiWorkingDir := filepath.Join(args.Configs.FlagsConfig.WorkingDir, common.TemporaryPath) argsSCQuery := &scQueryServiceArgs{ - generalConfig: args.Configs.GeneralConfig, - epochConfig: args.Configs.EpochConfig, - coreComponents: args.CoreComponents, - dataComponents: args.DataComponents, - stateComponents: args.StateComponents, - processComponents: args.ProcessComponents, - statusCoreComponents: args.StatusCoreComponents, - gasScheduleNotifier: args.GasScheduleNotifier, - messageSigVerifier: args.CryptoComponents.MessageSignVerifier(), - systemSCConfig: args.Configs.SystemSCConfig, - bootstrapper: args.Bootstrapper, - guardedAccountHandler: args.BootstrapComponents.GuardedAccountHandler(), - allowVMQueriesChan: args.AllowVMQueriesChan, - workingDir: apiWorkingDir, - processingMode: args.ProcessingMode, + generalConfig: args.Configs.GeneralConfig, + epochConfig: args.Configs.EpochConfig, + coreComponents: args.CoreComponents, + dataComponents: args.DataComponents, + stateComponents: args.StateComponents, + processComponents: args.ProcessComponents, + statusCoreComponents: args.StatusCoreComponents, + gasScheduleNotifier: args.GasScheduleNotifier, + messageSigVerifier: args.CryptoComponents.MessageSignVerifier(), + systemSCConfig: args.Configs.SystemSCConfig, + bootstrapper: args.Bootstrapper, + guardedAccountHandler: args.BootstrapComponents.GuardedAccountHandler(), + allowVMQueriesChan: args.AllowVMQueriesChan, + workingDir: apiWorkingDir, + processingMode: args.ProcessingMode, + isInHistoricalBalancesMode: operationmodes.IsInHistoricalBalancesMode(args.Configs), } scQueryService, err := createScQueryService(argsSCQuery) @@ -284,6 +288,7 @@ func CreateApiResolver(args *ApiResolverArgs) (facade.ApiResolver, error) { AccountsParser: args.ProcessComponents.AccountsParser(), GasScheduleNotifier: args.GasScheduleNotifier, ManagedPeersMonitor: args.StatusComponents.ManagedPeersMonitor(), + PublicKey: args.CryptoComponents.PublicKeyString(), } return external.NewNodeApiResolver(argsApiResolver) @@ -298,22 +303,23 @@ func createScQueryService( } argsQueryElem := &scQueryElementArgs{ - generalConfig: args.generalConfig, - epochConfig: args.epochConfig, - coreComponents: args.coreComponents, - stateComponents: args.stateComponents, - dataComponents: args.dataComponents, - processComponents: args.processComponents, - statusCoreComponents: args.statusCoreComponents, - gasScheduleNotifier: args.gasScheduleNotifier, - messageSigVerifier: args.messageSigVerifier, - systemSCConfig: args.systemSCConfig, - bootstrapper: args.bootstrapper, - guardedAccountHandler: args.guardedAccountHandler, - allowVMQueriesChan: args.allowVMQueriesChan, - workingDir: args.workingDir, - index: 0, - processingMode: args.processingMode, + generalConfig: args.generalConfig, + epochConfig: args.epochConfig, + coreComponents: args.coreComponents, + stateComponents: args.stateComponents, + dataComponents: args.dataComponents, + processComponents: args.processComponents, + statusCoreComponents: args.statusCoreComponents, + gasScheduleNotifier: args.gasScheduleNotifier, + messageSigVerifier: args.messageSigVerifier, + systemSCConfig: args.systemSCConfig, + bootstrapper: args.bootstrapper, + guardedAccountHandler: args.guardedAccountHandler, + allowVMQueriesChan: args.allowVMQueriesChan, + workingDir: args.workingDir, + index: 0, + processingMode: args.processingMode, + isInHistoricalBalancesMode: args.isInHistoricalBalancesMode, } var err error @@ -322,7 +328,7 @@ func createScQueryService( list := make([]process.SCQueryService, 0, numConcurrentVms) for i := 0; i < numConcurrentVms; i++ { argsQueryElem.index = i - scQueryService, err = createScQueryElement(argsQueryElem) + scQueryService, err = createScQueryElement(*argsQueryElem) if err != nil { return nil, err } @@ -339,10 +345,12 @@ func createScQueryService( } func createScQueryElement( - args *scQueryElementArgs, + args scQueryElementArgs, ) (process.SCQueryService, error) { var err error + selfShardID := args.processComponents.ShardCoordinator().SelfId() + pkConverter := args.coreComponents.AddressPubKeyConverter() automaticCrawlerAddressesStrings := args.generalConfig.BuiltInFunctions.AutomaticCrawlerAddresses convertedAddresses, errDecode := factory.DecodeAddresses(pkConverter, automaticCrawlerAddressesStrings) @@ -356,10 +364,20 @@ func createScQueryElement( return nil, errDecode } + apiBlockchain, err := createBlockchainForScQuery(selfShardID) + if err != nil { + return nil, err + } + + accountsAdapterApi, err := createNewAccountsAdapterApi(args, apiBlockchain) + if err != nil { + return nil, err + } + builtInFuncFactory, err := createBuiltinFuncs( args.gasScheduleNotifier, args.coreComponents.InternalMarshalizer(), - args.stateComponents.AccountsAdapterAPI(), + accountsAdapterApi, args.processComponents.ShardCoordinator(), args.coreComponents.EpochNotifier(), args.coreComponents.EnableEpochsHandler(), @@ -399,16 +417,17 @@ func createScQueryElement( GasSchedule: args.gasScheduleNotifier, Counter: counters.NewDisabledCounter(), MissingTrieNodesNotifier: syncer.NewMissingTrieNodesNotifier(), + Accounts: accountsAdapterApi, + BlockChain: apiBlockchain, } - var apiBlockchain data.ChainHandler var vmFactory process.VirtualMachinesContainerFactory maxGasForVmQueries := args.generalConfig.VirtualMachine.GasConfig.ShardMaxGasPerVmQuery - if args.processComponents.ShardCoordinator().SelfId() == core.MetachainShardId { + if selfShardID == core.MetachainShardId { maxGasForVmQueries = args.generalConfig.VirtualMachine.GasConfig.MetaMaxGasPerVmQuery - apiBlockchain, vmFactory, err = createMetaVmContainerFactory(args, argsHook) + vmFactory, err = createMetaVmContainerFactory(args, argsHook) } else { - apiBlockchain, vmFactory, err = createShardVmContainerFactory(args, argsHook) + vmFactory, err = createShardVmContainerFactory(args, argsHook) } if err != nil { return nil, err @@ -432,43 +451,40 @@ func createScQueryElement( } argsNewSCQueryService := smartContract.ArgsNewSCQueryService{ - VmContainer: vmContainer, - EconomicsFee: args.coreComponents.EconomicsData(), - BlockChainHook: vmFactory.BlockChainHookImpl(), - MainBlockChain: args.dataComponents.Blockchain(), - APIBlockChain: apiBlockchain, - WasmVMChangeLocker: args.coreComponents.WasmVMChangeLocker(), - Bootstrapper: args.bootstrapper, - AllowExternalQueriesChan: args.allowVMQueriesChan, - MaxGasLimitPerQuery: maxGasForVmQueries, - HistoryRepository: args.processComponents.HistoryRepository(), - ShardCoordinator: args.processComponents.ShardCoordinator(), - StorageService: args.dataComponents.StorageService(), - Marshaller: args.coreComponents.InternalMarshalizer(), - Hasher: args.coreComponents.Hasher(), - Uint64ByteSliceConverter: args.coreComponents.Uint64ByteSliceConverter(), + VmContainer: vmContainer, + EconomicsFee: args.coreComponents.EconomicsData(), + BlockChainHook: vmFactory.BlockChainHookImpl(), + MainBlockChain: args.dataComponents.Blockchain(), + APIBlockChain: apiBlockchain, + WasmVMChangeLocker: args.coreComponents.WasmVMChangeLocker(), + Bootstrapper: args.bootstrapper, + AllowExternalQueriesChan: args.allowVMQueriesChan, + MaxGasLimitPerQuery: maxGasForVmQueries, + HistoryRepository: args.processComponents.HistoryRepository(), + ShardCoordinator: args.processComponents.ShardCoordinator(), + StorageService: args.dataComponents.StorageService(), + Marshaller: args.coreComponents.InternalMarshalizer(), + Hasher: args.coreComponents.Hasher(), + Uint64ByteSliceConverter: args.coreComponents.Uint64ByteSliceConverter(), + IsInHistoricalBalancesMode: args.isInHistoricalBalancesMode, } return smartContract.NewSCQueryService(argsNewSCQueryService) } -func createMetaVmContainerFactory(args *scQueryElementArgs, argsHook hooks.ArgBlockChainHook) (data.ChainHandler, process.VirtualMachinesContainerFactory, error) { - apiBlockchain, err := blockchain.NewMetaChain(disabled.NewAppStatusHandler()) - if err != nil { - return nil, nil, err +func createBlockchainForScQuery(selfShardID uint32) (data.ChainHandler, error) { + isMetachain := selfShardID == core.MetachainShardId + if isMetachain { + return blockchain.NewMetaChain(disabled.NewAppStatusHandler()) } - accountsAdapterApi, err := createNewAccountsAdapterApi(args, apiBlockchain) - if err != nil { - return nil, nil, err - } - - argsHook.BlockChain = apiBlockchain - argsHook.Accounts = accountsAdapterApi + return blockchain.NewBlockChain(disabled.NewAppStatusHandler()) +} +func createMetaVmContainerFactory(args scQueryElementArgs, argsHook hooks.ArgBlockChainHook) (process.VirtualMachinesContainerFactory, error) { blockChainHookImpl, errBlockChainHook := hooks.NewBlockChainHookImpl(argsHook) if errBlockChainHook != nil { - return nil, nil, errBlockChainHook + return nil, errBlockChainHook } argsNewVmFactory := metachain.ArgsNewVMContainerFactory{ @@ -489,35 +505,22 @@ func createMetaVmContainerFactory(args *scQueryElementArgs, argsHook hooks.ArgBl } vmFactory, err := metachain.NewVMContainerFactory(argsNewVmFactory) if err != nil { - return nil, nil, err + return nil, err } - return apiBlockchain, vmFactory, nil + return vmFactory, nil } -func createShardVmContainerFactory(args *scQueryElementArgs, argsHook hooks.ArgBlockChainHook) (data.ChainHandler, process.VirtualMachinesContainerFactory, error) { - apiBlockchain, err := blockchain.NewBlockChain(disabled.NewAppStatusHandler()) - if err != nil { - return nil, nil, err - } - - accountsAdapterApi, err := createNewAccountsAdapterApi(args, apiBlockchain) - if err != nil { - return nil, nil, err - } - - argsHook.BlockChain = apiBlockchain - argsHook.Accounts = accountsAdapterApi - +func createShardVmContainerFactory(args scQueryElementArgs, argsHook hooks.ArgBlockChainHook) (process.VirtualMachinesContainerFactory, error) { queryVirtualMachineConfig := args.generalConfig.VirtualMachine.Querying.VirtualMachineConfig esdtTransferParser, errParser := parsers.NewESDTTransferParser(args.coreComponents.InternalMarshalizer()) if errParser != nil { - return nil, nil, errParser + return nil, errParser } blockChainHookImpl, errBlockChainHook := hooks.NewBlockChainHookImpl(argsHook) if errBlockChainHook != nil { - return nil, nil, errBlockChainHook + return nil, errBlockChainHook } argsNewVMFactory := shard.ArgVMContainerFactory{ @@ -539,13 +542,13 @@ func createShardVmContainerFactory(args *scQueryElementArgs, argsHook hooks.ArgB vmFactory, err := shard.NewVMContainerFactory(argsNewVMFactory) if err != nil { - return nil, nil, err + return nil, err } - return apiBlockchain, vmFactory, nil + return vmFactory, nil } -func createNewAccountsAdapterApi(args *scQueryElementArgs, chainHandler data.ChainHandler) (state.AccountsAdapterAPI, error) { +func createNewAccountsAdapterApi(args scQueryElementArgs, chainHandler data.ChainHandler) (state.AccountsAdapterAPI, error) { argsAccCreator := factoryState.ArgsAccountCreator{ Hasher: args.coreComponents.Hasher(), Marshaller: args.coreComponents.InternalMarshalizer(), @@ -622,7 +625,7 @@ func createNewAccountsAdapterApi(args *scQueryElementArgs, chainHandler data.Cha return state.NewAccountsDBApi(accounts, provider) } -func newStoragePruningManager(args *scQueryElementArgs) (state.StoragePruningManager, error) { +func newStoragePruningManager(args scQueryElementArgs) (state.StoragePruningManager, error) { argsMemEviction := evictionWaitingList.MemoryEvictionWaitingListArgs{ RootHashesSize: args.generalConfig.EvictionWaitingList.RootHashesSize, HashesSize: args.generalConfig.EvictionWaitingList.HashesSize, diff --git a/factory/api/apiResolverFactory_test.go b/factory/api/apiResolverFactory_test.go index 591ea31b79f..ef1795d8a1a 100644 --- a/factory/api/apiResolverFactory_test.go +++ b/factory/api/apiResolverFactory_test.go @@ -1,6 +1,7 @@ package api_test import ( + "fmt" "strings" "sync" "testing" @@ -448,5 +449,24 @@ func TestCreateApiResolver_createScQueryElement(t *testing.T) { require.True(t, strings.Contains(strings.ToLower(err.Error()), "hasher")) require.Nil(t, scQueryService) }) +} + +func TestCreateApiResolver_createBlockchainForScQuery(t *testing.T) { + t.Parallel() + + t.Run("for metachain", func(t *testing.T) { + t.Parallel() + apiBlockchain, err := api.CreateBlockchainForScQuery(core.MetachainShardId) + require.NoError(t, err) + require.Equal(t, "*blockchain.metaChain", fmt.Sprintf("%T", apiBlockchain)) + }) + + t.Run("for shard", func(t *testing.T) { + t.Parallel() + + apiBlockchain, err := api.CreateBlockchainForScQuery(0) + require.NoError(t, err) + require.Equal(t, "*blockchain.blockChain", fmt.Sprintf("%T", apiBlockchain)) + }) } diff --git a/factory/api/export_test.go b/factory/api/export_test.go index 0164c0c2b10..5a7948c9acb 100644 --- a/factory/api/export_test.go +++ b/factory/api/export_test.go @@ -2,6 +2,7 @@ package api import ( "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/factory" "github.com/multiversx/mx-chain-go/process" @@ -29,7 +30,7 @@ type SCQueryElementArgs struct { // CreateScQueryElement - func CreateScQueryElement(args SCQueryElementArgs) (process.SCQueryService, error) { - return createScQueryElement(&scQueryElementArgs{ + return createScQueryElement(scQueryElementArgs{ generalConfig: args.GeneralConfig, epochConfig: args.EpochConfig, coreComponents: args.CoreComponents, @@ -47,3 +48,8 @@ func CreateScQueryElement(args SCQueryElementArgs) (process.SCQueryService, erro guardedAccountHandler: args.GuardedAccountHandler, }) } + +// CreateBlockchainForScQuery - +func CreateBlockchainForScQuery(selfShardID uint32) (data.ChainHandler, error) { + return createBlockchainForScQuery(selfShardID) +} diff --git a/factory/heartbeat/heartbeatV2Components.go b/factory/heartbeat/heartbeatV2Components.go index a551f22e869..97164a7240e 100644 --- a/factory/heartbeat/heartbeatV2Components.go +++ b/factory/heartbeat/heartbeatV2Components.go @@ -272,32 +272,6 @@ func (hcf *heartbeatV2ComponentsFactory) Create() (*heartbeatV2Components, error return nil, err } - argsMainCrossShardPeerTopicNotifier := monitor.ArgsCrossShardPeerTopicNotifier{ - ShardCoordinator: hcf.processComponents.ShardCoordinator(), - PeerShardMapper: hcf.processComponents.PeerShardMapper(), - } - mainCrossShardPeerTopicNotifier, err := monitor.NewCrossShardPeerTopicNotifier(argsMainCrossShardPeerTopicNotifier) - if err != nil { - return nil, err - } - err = hcf.networkComponents.NetworkMessenger().AddPeerTopicNotifier(mainCrossShardPeerTopicNotifier) - if err != nil { - return nil, err - } - - argsFullArchiveCrossShardPeerTopicNotifier := monitor.ArgsCrossShardPeerTopicNotifier{ - ShardCoordinator: hcf.processComponents.ShardCoordinator(), - PeerShardMapper: hcf.processComponents.FullArchivePeerShardMapper(), - } - fullArchiveCrossShardPeerTopicNotifier, err := monitor.NewCrossShardPeerTopicNotifier(argsFullArchiveCrossShardPeerTopicNotifier) - if err != nil { - return nil, err - } - err = hcf.networkComponents.FullArchiveNetworkMessenger().AddPeerTopicNotifier(fullArchiveCrossShardPeerTopicNotifier) - if err != nil { - return nil, err - } - return &heartbeatV2Components{ sender: heartbeatV2Sender, peerAuthRequestsProcessor: paRequestsProcessor, diff --git a/factory/heartbeat/heartbeatV2Components_test.go b/factory/heartbeat/heartbeatV2Components_test.go index f013294a7d1..6b5088cab5b 100644 --- a/factory/heartbeat/heartbeatV2Components_test.go +++ b/factory/heartbeat/heartbeatV2Components_test.go @@ -11,7 +11,6 @@ import ( errorsMx "github.com/multiversx/mx-chain-go/errors" heartbeatComp "github.com/multiversx/mx-chain-go/factory/heartbeat" testsMocks "github.com/multiversx/mx-chain-go/integrationTests/mock" - "github.com/multiversx/mx-chain-go/p2p" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/testscommon" @@ -504,26 +503,6 @@ func TestHeartbeatV2Components_Create(t *testing.T) { assert.Nil(t, hc) assert.Error(t, err) }) - t.Run("AddPeerTopicNotifier fails should error", func(t *testing.T) { - t.Parallel() - - args := createMockHeartbeatV2ComponentsFactoryArgs() - args.NetworkComponents = &testsMocks.NetworkComponentsStub{ - Messenger: &p2pmocks.MessengerStub{ - AddPeerTopicNotifierCalled: func(notifier p2p.PeerTopicNotifier) error { - return expectedErr - }, - }, - FullArchiveNetworkMessengerField: &p2pmocks.MessengerStub{}, - } - hcf, err := heartbeatComp.NewHeartbeatV2ComponentsFactory(args) - assert.NotNil(t, hcf) - assert.NoError(t, err) - - hc, err := hcf.Create() - assert.Nil(t, hc) - assert.Equal(t, expectedErr, err) - }) t.Run("should work", func(t *testing.T) { t.Parallel() diff --git a/factory/processing/processComponents.go b/factory/processing/processComponents.go index f36eee4e29e..d58c8d14e8e 100644 --- a/factory/processing/processComponents.go +++ b/factory/processing/processComponents.go @@ -1365,23 +1365,24 @@ func (pcf *processComponentsFactory) newShardResolverContainerFactory( } resolversContainerFactoryArgs := resolverscontainer.FactoryArgs{ - ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), - MainMessenger: pcf.network.NetworkMessenger(), - FullArchiveMessenger: pcf.network.FullArchiveNetworkMessenger(), - Store: pcf.data.StorageService(), - Marshalizer: pcf.coreData.InternalMarshalizer(), - DataPools: pcf.data.Datapool(), - Uint64ByteSliceConverter: pcf.coreData.Uint64ByteSliceConverter(), - DataPacker: dataPacker, - TriesContainer: pcf.state.TriesContainer(), - SizeCheckDelta: pcf.config.Marshalizer.SizeCheckDelta, - InputAntifloodHandler: pcf.network.InputAntiFloodHandler(), - OutputAntifloodHandler: pcf.network.OutputAntiFloodHandler(), - NumConcurrentResolvingJobs: pcf.config.Antiflood.NumConcurrentResolverJobs, - IsFullHistoryNode: pcf.prefConfigs.Preferences.FullArchive, - MainPreferredPeersHolder: pcf.network.PreferredPeersHolderHandler(), - FullArchivePreferredPeersHolder: pcf.network.FullArchivePreferredPeersHolderHandler(), - PayloadValidator: payloadValidator, + ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), + MainMessenger: pcf.network.NetworkMessenger(), + FullArchiveMessenger: pcf.network.FullArchiveNetworkMessenger(), + Store: pcf.data.StorageService(), + Marshalizer: pcf.coreData.InternalMarshalizer(), + DataPools: pcf.data.Datapool(), + Uint64ByteSliceConverter: pcf.coreData.Uint64ByteSliceConverter(), + DataPacker: dataPacker, + TriesContainer: pcf.state.TriesContainer(), + SizeCheckDelta: pcf.config.Marshalizer.SizeCheckDelta, + InputAntifloodHandler: pcf.network.InputAntiFloodHandler(), + OutputAntifloodHandler: pcf.network.OutputAntiFloodHandler(), + NumConcurrentResolvingJobs: pcf.config.Antiflood.NumConcurrentResolverJobs, + NumConcurrentResolvingTrieNodesJobs: pcf.config.Antiflood.NumConcurrentResolvingTrieNodesJobs, + IsFullHistoryNode: pcf.prefConfigs.Preferences.FullArchive, + MainPreferredPeersHolder: pcf.network.PreferredPeersHolderHandler(), + FullArchivePreferredPeersHolder: pcf.network.FullArchivePreferredPeersHolderHandler(), + PayloadValidator: payloadValidator, } resolversContainerFactory, err := resolverscontainer.NewShardResolversContainerFactory(resolversContainerFactoryArgs) if err != nil { @@ -1401,23 +1402,24 @@ func (pcf *processComponentsFactory) newMetaResolverContainerFactory( } resolversContainerFactoryArgs := resolverscontainer.FactoryArgs{ - ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), - MainMessenger: pcf.network.NetworkMessenger(), - FullArchiveMessenger: pcf.network.FullArchiveNetworkMessenger(), - Store: pcf.data.StorageService(), - Marshalizer: pcf.coreData.InternalMarshalizer(), - DataPools: pcf.data.Datapool(), - Uint64ByteSliceConverter: pcf.coreData.Uint64ByteSliceConverter(), - DataPacker: dataPacker, - TriesContainer: pcf.state.TriesContainer(), - SizeCheckDelta: pcf.config.Marshalizer.SizeCheckDelta, - InputAntifloodHandler: pcf.network.InputAntiFloodHandler(), - OutputAntifloodHandler: pcf.network.OutputAntiFloodHandler(), - NumConcurrentResolvingJobs: pcf.config.Antiflood.NumConcurrentResolverJobs, - IsFullHistoryNode: pcf.prefConfigs.Preferences.FullArchive, - MainPreferredPeersHolder: pcf.network.PreferredPeersHolderHandler(), - FullArchivePreferredPeersHolder: pcf.network.FullArchivePreferredPeersHolderHandler(), - PayloadValidator: payloadValidator, + ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), + MainMessenger: pcf.network.NetworkMessenger(), + FullArchiveMessenger: pcf.network.FullArchiveNetworkMessenger(), + Store: pcf.data.StorageService(), + Marshalizer: pcf.coreData.InternalMarshalizer(), + DataPools: pcf.data.Datapool(), + Uint64ByteSliceConverter: pcf.coreData.Uint64ByteSliceConverter(), + DataPacker: dataPacker, + TriesContainer: pcf.state.TriesContainer(), + SizeCheckDelta: pcf.config.Marshalizer.SizeCheckDelta, + InputAntifloodHandler: pcf.network.InputAntiFloodHandler(), + OutputAntifloodHandler: pcf.network.OutputAntiFloodHandler(), + NumConcurrentResolvingJobs: pcf.config.Antiflood.NumConcurrentResolverJobs, + NumConcurrentResolvingTrieNodesJobs: pcf.config.Antiflood.NumConcurrentResolvingTrieNodesJobs, + IsFullHistoryNode: pcf.prefConfigs.Preferences.FullArchive, + MainPreferredPeersHolder: pcf.network.PreferredPeersHolderHandler(), + FullArchivePreferredPeersHolder: pcf.network.FullArchivePreferredPeersHolderHandler(), + PayloadValidator: payloadValidator, } return resolverscontainer.NewMetaResolversContainerFactory(resolversContainerFactoryArgs) diff --git a/go.mod b/go.mod index fbd61b07d8d..84138c3ebc3 100644 --- a/go.mod +++ b/go.mod @@ -19,13 +19,13 @@ require ( github.com/multiversx/mx-chain-crypto-go v1.2.9 github.com/multiversx/mx-chain-es-indexer-go v1.4.18 github.com/multiversx/mx-chain-logger-go v1.0.13 - github.com/multiversx/mx-chain-scenario-go v1.3.0 + github.com/multiversx/mx-chain-scenario-go v1.4.2 github.com/multiversx/mx-chain-storage-go v1.0.14 github.com/multiversx/mx-chain-vm-common-go v1.5.11 - github.com/multiversx/mx-chain-vm-go v1.5.26 + github.com/multiversx/mx-chain-vm-go v1.5.27 github.com/multiversx/mx-chain-vm-v1_2-go v1.2.65 github.com/multiversx/mx-chain-vm-v1_3-go v1.3.66 - github.com/multiversx/mx-chain-vm-v1_4-go v1.4.94 + github.com/multiversx/mx-chain-vm-v1_4-go v1.4.95 github.com/pelletier/go-toml v1.9.3 github.com/pkg/errors v0.9.1 github.com/shirou/gopsutil v3.21.11+incompatible diff --git a/go.sum b/go.sum index b7cb342ed43..b7cd3036bc2 100644 --- a/go.sum +++ b/go.sum @@ -395,20 +395,20 @@ github.com/multiversx/mx-chain-es-indexer-go v1.4.18 h1:rCA+/mn/77MyB7c8FjtabdQe github.com/multiversx/mx-chain-es-indexer-go v1.4.18/go.mod h1:maraR9xXcfi0lLifhGMc+DVHpw1IOPX/c89HVckF1Js= github.com/multiversx/mx-chain-logger-go v1.0.13 h1:eru/TETo0MkO4ZTnXsQDKf4PBRpAXmqjT02klNT/JnY= github.com/multiversx/mx-chain-logger-go v1.0.13/go.mod h1:MZJhTAtZTJxT+yK2EHc4ZW3YOHUc1UdjCD0iahRNBZk= -github.com/multiversx/mx-chain-scenario-go v1.3.0 h1:Vm6jYipJuIcJt8kamgXkePtKkwXaF5Lv5DglZjE5o8I= -github.com/multiversx/mx-chain-scenario-go v1.3.0/go.mod h1:Sdgz8nzA9Ki/pdY/e2a4k90LALBKQB1Eo8HeCV3Bkzo= +github.com/multiversx/mx-chain-scenario-go v1.4.2 h1:iGgqMHup7DfMYFEynGjn2CX9ZNBfgPQLqzZx1AWHJzc= +github.com/multiversx/mx-chain-scenario-go v1.4.2/go.mod h1:Sdgz8nzA9Ki/pdY/e2a4k90LALBKQB1Eo8HeCV3Bkzo= github.com/multiversx/mx-chain-storage-go v1.0.14 h1:h0acoqPS3FKJ4S3cKBEriTU0OabSQnpxai5WKhi1YCs= github.com/multiversx/mx-chain-storage-go v1.0.14/go.mod h1:sJ2q49tgjxNpMpsHysjABqCAB0FLBmDblbjBkQ8XfmA= github.com/multiversx/mx-chain-vm-common-go v1.5.11 h1:rAQR00ALKOmWAuNA8XW8hR02M9gmrAE4WZwyJH4dMMU= github.com/multiversx/mx-chain-vm-common-go v1.5.11/go.mod h1:T04rZt/VdwjPXcXVxltMmDmrhxNXsqlByYbWNWcNLYA= -github.com/multiversx/mx-chain-vm-go v1.5.26 h1:ZjUJTG9cO2h5WNRIZ50ZSZNsTEPqXXPGS9Y/SAGyC2A= -github.com/multiversx/mx-chain-vm-go v1.5.26/go.mod h1:gNZ/s4Z6OHg6ZeBsW6aDxWQduXsRS0Bsv4pfkmHeRzs= +github.com/multiversx/mx-chain-vm-go v1.5.27 h1:80AdXyjAnN5w4hucPMtpsXnoWtcV47ZLcjECsTTccsA= +github.com/multiversx/mx-chain-vm-go v1.5.27/go.mod h1:Y1O50Z7+suo4D1hnSBA7n34KvgKs5W9jzoEGwpfAjks= github.com/multiversx/mx-chain-vm-v1_2-go v1.2.65 h1:TxFjQH0dXC/ACQxlIgJbO7pVoh00rcqeKSnIjWTDMxg= github.com/multiversx/mx-chain-vm-v1_2-go v1.2.65/go.mod h1:UUUxIU7mlRkz+Jz4GWV2GkgJt2mKd+j1kky++RNYc9s= github.com/multiversx/mx-chain-vm-v1_3-go v1.3.66 h1:xgrXfHKa0Za4xjFj5W0FcYEivjrQIhLvGEFXXa1uQZU= github.com/multiversx/mx-chain-vm-v1_3-go v1.3.66/go.mod h1:mx6IOAqo7zjSinYd8D2YqlpMWsuqFoYXJ6bntnTOeQg= -github.com/multiversx/mx-chain-vm-v1_4-go v1.4.94 h1:MZFEBjDmfwLGB0cZb/pvlLx+qRv/9tO83bEgHUk34is= -github.com/multiversx/mx-chain-vm-v1_4-go v1.4.94/go.mod h1:uuSbZGe0UwOWQyHA4EeJWhs8UeDdhtmMwlhNaX9ppx0= +github.com/multiversx/mx-chain-vm-v1_4-go v1.4.95 h1:zswK06SKd8VYjFTeC/4Jat5PhU9PT4pO5hw01U9ZjtE= +github.com/multiversx/mx-chain-vm-v1_4-go v1.4.95/go.mod h1:t4YcFK6VJkG1wGKx1JK4jyowo9zfGFpi8Jl3ycfqAxw= github.com/multiversx/mx-components-big-int v1.0.0 h1:Wkr8lSzK2nDqixOrrBa47VNuqdhV1m/aJhaP1EMaiS8= github.com/multiversx/mx-components-big-int v1.0.0/go.mod h1:maIEMgHlNE2u78JaDD0oLzri+ShgU4okHfzP3LWGdQM= github.com/multiversx/protobuf v1.3.2 h1:RaNkxvGTGbA0lMcnHAN24qE1G1i+Xs5yHA6MDvQ4mSM= diff --git a/heartbeat/interface.go b/heartbeat/interface.go index 12eb29a5d61..3652170d8ba 100644 --- a/heartbeat/interface.go +++ b/heartbeat/interface.go @@ -83,6 +83,7 @@ type ManagedPeersHolder interface { IncrementRoundsWithoutReceivedMessages(pkBytes []byte) ResetRoundsWithoutReceivedMessages(pkBytes []byte, pid core.PeerID) GetManagedKeysByCurrentNode() map[string]crypto.PrivateKey + GetLoadedKeysByCurrentNode() [][]byte IsKeyManagedByCurrentNode(pkBytes []byte) bool IsKeyRegistered(pkBytes []byte) bool IsPidManagedByCurrentNode(pid core.PeerID) bool diff --git a/heartbeat/monitor/crossShardPeerTopicNotifier.go b/heartbeat/monitor/crossShardPeerTopicNotifier.go deleted file mode 100644 index aa25995fc71..00000000000 --- a/heartbeat/monitor/crossShardPeerTopicNotifier.go +++ /dev/null @@ -1,111 +0,0 @@ -package monitor - -import ( - "fmt" - "strconv" - "strings" - - "github.com/multiversx/mx-chain-core-go/core" - "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/multiversx/mx-chain-go/common" - "github.com/multiversx/mx-chain-go/heartbeat" - "github.com/multiversx/mx-chain-go/sharding" -) - -const topicSeparator = "_" - -// ArgsCrossShardPeerTopicNotifier represents the arguments for the cross shard peer topic notifier -type ArgsCrossShardPeerTopicNotifier struct { - ShardCoordinator sharding.Coordinator - PeerShardMapper heartbeat.PeerShardMapper -} - -type crossShardPeerTopicNotifier struct { - shardCoordinator sharding.Coordinator - peerShardMapper heartbeat.PeerShardMapper -} - -// NewCrossShardPeerTopicNotifier create a new cross shard peer topic notifier instance -func NewCrossShardPeerTopicNotifier(args ArgsCrossShardPeerTopicNotifier) (*crossShardPeerTopicNotifier, error) { - err := checkArgsCrossShardPeerTopicNotifier(args) - if err != nil { - return nil, err - } - - notifier := &crossShardPeerTopicNotifier{ - shardCoordinator: args.ShardCoordinator, - peerShardMapper: args.PeerShardMapper, - } - - return notifier, nil -} - -func checkArgsCrossShardPeerTopicNotifier(args ArgsCrossShardPeerTopicNotifier) error { - if check.IfNil(args.PeerShardMapper) { - return heartbeat.ErrNilPeerShardMapper - } - if check.IfNil(args.ShardCoordinator) { - return heartbeat.ErrNilShardCoordinator - } - - return nil -} - -// NewPeerFound is called whenever a new peer was found -func (notifier *crossShardPeerTopicNotifier) NewPeerFound(pid core.PeerID, topic string) { - splt := strings.Split(topic, topicSeparator) - if len(splt) != 3 { - // not a cross shard peer or the topic is global - return - } - - shardID1, err := notifier.getShardID(splt[1]) - if err != nil { - log.Error("failed to extract first shard for topic", "topic", topic, "error", err.Error()) - return - } - - shardID2, err := notifier.getShardID(splt[2]) - if err != nil { - log.Error("failed to extract second shard for topic", "topic", topic, "error", err.Error()) - return - } - if shardID1 == shardID2 { - return - } - notifier.checkAndAddShardID(pid, shardID1, topic, shardID2) - notifier.checkAndAddShardID(pid, shardID2, topic, shardID1) -} - -// TODO make a standalone component out of this -func (notifier *crossShardPeerTopicNotifier) getShardID(data string) (uint32, error) { - if data == common.MetachainTopicIdentifier { - return common.MetachainShardId, nil - } - val, err := strconv.Atoi(data) - if err != nil { - return 0, err - } - if uint32(val) >= notifier.shardCoordinator.NumberOfShards() || val < 0 { - return 0, fmt.Errorf("invalid value in crossShardPeerTopicNotifier.getShardID %d", val) - } - - return uint32(val), nil -} - -func (notifier *crossShardPeerTopicNotifier) checkAndAddShardID(pid core.PeerID, shardID1 uint32, topic string, shardID2 uint32) { - if shardID1 != notifier.shardCoordinator.SelfId() { - return - } - - log.Trace("crossShardPeerTopicNotifier.NewPeerFound found a cross shard peer", - "topic", topic, - "pid", pid.Pretty(), - "shard", shardID2) - notifier.peerShardMapper.PutPeerIdShardId(pid, shardID2) -} - -// IsInterfaceNil returns true if there is no value under the interface -func (notifier *crossShardPeerTopicNotifier) IsInterfaceNil() bool { - return notifier == nil -} diff --git a/heartbeat/monitor/crossShardPeerTopicNotifier_test.go b/heartbeat/monitor/crossShardPeerTopicNotifier_test.go deleted file mode 100644 index e4951586852..00000000000 --- a/heartbeat/monitor/crossShardPeerTopicNotifier_test.go +++ /dev/null @@ -1,273 +0,0 @@ -package monitor - -import ( - "math" - "testing" - - "github.com/multiversx/mx-chain-core-go/core" - "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/multiversx/mx-chain-go/common" - "github.com/multiversx/mx-chain-go/heartbeat" - "github.com/multiversx/mx-chain-go/process/mock" - "github.com/multiversx/mx-chain-go/testscommon" - "github.com/stretchr/testify/assert" -) - -func createMockArgsCrossShardPeerTopicNotifier() ArgsCrossShardPeerTopicNotifier { - return ArgsCrossShardPeerTopicNotifier{ - ShardCoordinator: &testscommon.ShardsCoordinatorMock{ - NoShards: 3, - CurrentShard: 1, - }, - PeerShardMapper: &mock.PeerShardMapperStub{}, - } -} - -func TestNewCrossShardPeerTopicNotifier(t *testing.T) { - t.Parallel() - - t.Run("nil sharding coordinator should error", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.ShardCoordinator = nil - - notifier, err := NewCrossShardPeerTopicNotifier(args) - assert.True(t, check.IfNil(notifier)) - assert.Equal(t, heartbeat.ErrNilShardCoordinator, err) - }) - t.Run("nil peer shard mapper should error", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = nil - - notifier, err := NewCrossShardPeerTopicNotifier(args) - assert.True(t, check.IfNil(notifier)) - assert.Equal(t, heartbeat.ErrNilPeerShardMapper, err) - }) - t.Run("should work", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - - notifier, err := NewCrossShardPeerTopicNotifier(args) - assert.False(t, check.IfNil(notifier)) - assert.Nil(t, err) - }) -} - -func TestCrossShardPeerTopicNotifier_NewPeerFound(t *testing.T) { - t.Parallel() - - testTopic := "test" - t.Run("global topic should not notice", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Fail(t, "should have not called PutPeerIdShardId") - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - notifier.NewPeerFound("pid", "random topic") - }) - t.Run("intra-shard topic should not notice", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Fail(t, "should have not called PutPeerIdShardId") - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + core.CommunicationIdentifierBetweenShards(0, 0) - notifier.NewPeerFound("pid", topic) - }) - t.Run("cross-shard topic but not relevant to current node should not notice", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Fail(t, "should have not called PutPeerIdShardId") - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + core.CommunicationIdentifierBetweenShards(0, 2) - notifier.NewPeerFound("pid", topic) - }) - t.Run("first shard ID is a NaN should not notice", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Fail(t, "should have not called PutPeerIdShardId") - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + "_NaN_1" - notifier.NewPeerFound("pid", topic) - }) - t.Run("second shard ID is a NaN should not notice", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Fail(t, "should have not called PutPeerIdShardId") - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + "_1_NaN" - notifier.NewPeerFound("pid", topic) - }) - t.Run("second shard ID is a negative value should not notice", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Fail(t, "should have not called PutPeerIdShardId") - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + "_1_-1" - notifier.NewPeerFound("pid", topic) - }) - t.Run("second shard ID is an out of range value should not notice", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Fail(t, "should have not called PutPeerIdShardId") - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + "_1_4" - notifier.NewPeerFound("pid", topic) - }) - t.Run("same shard IDs should not notice", func(t *testing.T) { - t.Parallel() - - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Fail(t, "should have not called PutPeerIdShardId") - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + "_0_0" - notifier.NewPeerFound("pid", topic) - }) - t.Run("cross-shard between 0 and 1 should notice", func(t *testing.T) { - t.Parallel() - - expectedPid := core.PeerID("pid") - notifiedShardID := uint32(math.MaxUint32) - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Equal(t, pid, expectedPid) - notifiedShardID = shardId - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + core.CommunicationIdentifierBetweenShards(0, 1) - notifier.NewPeerFound("pid", topic) - assert.Equal(t, uint32(0), notifiedShardID) - }) - t.Run("cross-shard between 1 and 2 should notice", func(t *testing.T) { - t.Parallel() - - expectedPid := core.PeerID("pid") - notifiedShardID := uint32(math.MaxUint32) - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Equal(t, pid, expectedPid) - notifiedShardID = shardId - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + core.CommunicationIdentifierBetweenShards(1, 2) - notifier.NewPeerFound("pid", topic) - assert.Equal(t, uint32(2), notifiedShardID) - }) - t.Run("cross-shard between 1 and META should notice", func(t *testing.T) { - t.Parallel() - - expectedPid := core.PeerID("pid") - notifiedShardID := uint32(math.MaxUint32) - args := createMockArgsCrossShardPeerTopicNotifier() - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Equal(t, pid, expectedPid) - notifiedShardID = shardId - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + core.CommunicationIdentifierBetweenShards(1, common.MetachainShardId) - notifier.NewPeerFound("pid", topic) - assert.Equal(t, common.MetachainShardId, notifiedShardID) - }) - t.Run("cross-shard between META and 1 should notice", func(t *testing.T) { - t.Parallel() - - expectedPid := core.PeerID("pid") - notifiedShardID := uint32(math.MaxUint32) - args := createMockArgsCrossShardPeerTopicNotifier() - args.ShardCoordinator = &testscommon.ShardsCoordinatorMock{ - NoShards: 3, - CurrentShard: common.MetachainShardId, - } - args.PeerShardMapper = &mock.PeerShardMapperStub{ - PutPeerIdShardIdCalled: func(pid core.PeerID, shardId uint32) { - assert.Equal(t, pid, expectedPid) - notifiedShardID = shardId - }, - } - - notifier, _ := NewCrossShardPeerTopicNotifier(args) - topic := testTopic + core.CommunicationIdentifierBetweenShards(common.MetachainShardId, 1) - notifier.NewPeerFound("pid", topic) - assert.Equal(t, uint32(1), notifiedShardID) - }) -} - -func BenchmarkCrossShardPeerTopicNotifier_NewPeerFound(b *testing.B) { - args := createMockArgsCrossShardPeerTopicNotifier() - notifier, _ := NewCrossShardPeerTopicNotifier(args) - - for i := 0; i < b.N; i++ { - switch i % 6 { - case 0: - notifier.NewPeerFound("pid", "global") - case 2: - notifier.NewPeerFound("pid", "intrashard_1") - case 3: - notifier.NewPeerFound("pid", "crossshard_1_2") - case 4: - notifier.NewPeerFound("pid", "crossshard_1_META") - case 5: - notifier.NewPeerFound("pid", "crossshard_META_1") - case 6: - notifier.NewPeerFound("pid", "crossshard_2_META") - } - } -} diff --git a/integrationTests/interface.go b/integrationTests/interface.go index ddce1ebf3d4..373067f28b3 100644 --- a/integrationTests/interface.go +++ b/integrationTests/interface.go @@ -113,6 +113,7 @@ type Facade interface { IsDataTrieMigrated(address string, options api.AccountQueryOptions) (bool, error) GetManagedKeysCount() int GetManagedKeys() []string + GetLoadedKeys() []string GetEligibleManagedKeys() ([]string, error) GetWaitingManagedKeys() ([]string, error) IsInterfaceNil() bool diff --git a/integrationTests/miniNetwork.go b/integrationTests/miniNetwork.go new file mode 100644 index 00000000000..e9c64f5606d --- /dev/null +++ b/integrationTests/miniNetwork.go @@ -0,0 +1,113 @@ +package integrationTests + +import ( + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/multiversx/mx-chain-core-go/data/transaction" +) + +// MiniNetwork is a mini network, useful for some integration tests +type MiniNetwork struct { + Round uint64 + Nonce uint64 + + Nodes []*TestProcessorNode + ShardNode *TestProcessorNode + MetachainNode *TestProcessorNode + Users map[string]*TestWalletAccount +} + +// NewMiniNetwork creates a MiniNetwork +func NewMiniNetwork() *MiniNetwork { + n := &MiniNetwork{} + + nodes := CreateNodes( + 1, + 1, + 1, + ) + + n.Nodes = nodes + n.ShardNode = nodes[0] + n.MetachainNode = nodes[1] + n.Users = make(map[string]*TestWalletAccount) + + return n +} + +// Stop stops the mini network +func (n *MiniNetwork) Stop() { + n.ShardNode.Close() + n.MetachainNode.Close() +} + +// FundAccount funds an account +func (n *MiniNetwork) FundAccount(address []byte, value *big.Int) { + shard := n.MetachainNode.ShardCoordinator.ComputeId(address) + + if shard == n.MetachainNode.ShardCoordinator.SelfId() { + MintAddress(n.MetachainNode.AccntState, address, value) + } else { + MintAddress(n.ShardNode.AccntState, address, value) + } +} + +// AddUser adds a user (account) to the mini network +func (n *MiniNetwork) AddUser(balance *big.Int) *TestWalletAccount { + user := CreateTestWalletAccount(n.ShardNode.ShardCoordinator, 0) + n.Users[string(user.Address)] = user + n.FundAccount(user.Address, balance) + return user +} + +// Start starts the mini network +func (n *MiniNetwork) Start() { + n.Round = 1 + n.Nonce = 1 +} + +// Continue advances processing with a number of rounds +func (n *MiniNetwork) Continue(t *testing.T, numRounds int) { + idxProposers := []int{0, 1} + + for i := int64(0); i < int64(numRounds); i++ { + n.Nonce, n.Round = ProposeAndSyncOneBlock(t, n.Nodes, idxProposers, n.Round, n.Nonce) + } +} + +// SendTransaction sends a transaction +func (n *MiniNetwork) SendTransaction( + senderPubkey []byte, + receiverPubkey []byte, + value *big.Int, + data string, + additionalGasLimit uint64, +) (string, error) { + sender, ok := n.Users[string(senderPubkey)] + if !ok { + return "", fmt.Errorf("unknown sender: %s", hex.EncodeToString(senderPubkey)) + } + + tx := &transaction.Transaction{ + Nonce: sender.Nonce, + Value: new(big.Int).Set(value), + SndAddr: sender.Address, + RcvAddr: receiverPubkey, + Data: []byte(data), + GasPrice: MinTxGasPrice, + GasLimit: MinTxGasLimit + uint64(len(data)) + additionalGasLimit, + ChainID: ChainID, + Version: MinTransactionVersion, + } + + txBuff, _ := tx.GetDataForSigning(TestAddressPubkeyConverter, TestTxSignMarshalizer, TestTxSignHasher) + tx.Signature, _ = sender.SingleSigner.Sign(sender.SkTxSign, txBuff) + txHash, err := n.ShardNode.SendTransaction(tx) + + sender.Nonce++ + + return txHash, err +} diff --git a/integrationTests/oneNodeNetwork.go b/integrationTests/oneNodeNetwork.go deleted file mode 100644 index 720ff0529c6..00000000000 --- a/integrationTests/oneNodeNetwork.go +++ /dev/null @@ -1,70 +0,0 @@ -package integrationTests - -import ( - "math/big" - "testing" - - "github.com/multiversx/mx-chain-core-go/core" - "github.com/multiversx/mx-chain-core-go/data/transaction" - "github.com/multiversx/mx-chain-go/process" -) - -type oneNodeNetwork struct { - Round uint64 - Nonce uint64 - - Node *TestProcessorNode -} - -// NewOneNodeNetwork creates a one-node network, useful for some integration tests -func NewOneNodeNetwork() *oneNodeNetwork { - n := &oneNodeNetwork{} - - nodes := CreateNodes( - 1, - 1, - 0, - ) - - n.Node = nodes[0] - return n -} - -// Stop stops the test network -func (n *oneNodeNetwork) Stop() { - n.Node.Close() -} - -// Mint mints the given address -func (n *oneNodeNetwork) Mint(address []byte, value *big.Int) { - MintAddress(n.Node.AccntState, address, value) -} - -// GetMinGasPrice returns the min gas price -func (n *oneNodeNetwork) GetMinGasPrice() uint64 { - return n.Node.EconomicsData.GetMinGasPrice() -} - -// MaxGasLimitPerBlock returns the max gas per block -func (n *oneNodeNetwork) MaxGasLimitPerBlock() uint64 { - return n.Node.EconomicsData.MaxGasLimitPerBlock(0) - 1 -} - -// GoToRoundOne advances processing to block and round 1 -func (n *oneNodeNetwork) GoToRoundOne() { - n.Round = IncrementAndPrintRound(n.Round) - n.Nonce++ -} - -// Continue advances processing with a number of rounds -func (n *oneNodeNetwork) Continue(t *testing.T, numRounds int) { - n.Nonce, n.Round = WaitOperationToBeDone(t, []*TestProcessorNode{n.Node}, numRounds, n.Nonce, n.Round, []int{0}) -} - -// AddTxToPool adds a transaction to the pool (skips signature checks and interceptors) -func (n *oneNodeNetwork) AddTxToPool(tx *transaction.Transaction) { - txHash, _ := core.CalculateHash(TestMarshalizer, TestHasher, tx) - sourceShard := n.Node.ShardCoordinator.ComputeId(tx.SndAddr) - cacheIdentifier := process.ShardCacherIdentifier(sourceShard, sourceShard) - n.Node.DataPool.Transactions().AddData(txHash, tx, tx.Size(), cacheIdentifier) -} diff --git a/integrationTests/testHeartbeatNode.go b/integrationTests/testHeartbeatNode.go index 51c3091292c..bd40efbe667 100644 --- a/integrationTests/testHeartbeatNode.go +++ b/integrationTests/testHeartbeatNode.go @@ -29,7 +29,6 @@ import ( "github.com/multiversx/mx-chain-go/dataRetriever/factory/resolverscontainer" "github.com/multiversx/mx-chain-go/dataRetriever/requestHandlers" "github.com/multiversx/mx-chain-go/epochStart/notifier" - "github.com/multiversx/mx-chain-go/heartbeat/monitor" "github.com/multiversx/mx-chain-go/heartbeat/processor" "github.com/multiversx/mx-chain-go/heartbeat/sender" "github.com/multiversx/mx-chain-go/integrationTests/mock" @@ -445,7 +444,6 @@ func (thn *TestHeartbeatNode) InitTestHeartbeatNode(tb testing.TB, minPeersWaiti thn.initResolversAndRequesters() thn.initInterceptors() thn.initShardSender(tb) - thn.initCrossShardPeerTopicNotifier(tb) thn.initDirectConnectionProcessor(tb) for len(thn.MainMessenger.Peers()) < minPeersWaiting { @@ -525,13 +523,14 @@ func (thn *TestHeartbeatNode) initResolversAndRequesters() { return &trieMock.TrieStub{} }, }, - SizeCheckDelta: 100, - InputAntifloodHandler: &mock.NilAntifloodHandler{}, - OutputAntifloodHandler: &mock.NilAntifloodHandler{}, - NumConcurrentResolvingJobs: 10, - MainPreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - FullArchivePreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - PayloadValidator: payloadValidator, + SizeCheckDelta: 100, + InputAntifloodHandler: &mock.NilAntifloodHandler{}, + OutputAntifloodHandler: &mock.NilAntifloodHandler{}, + NumConcurrentResolvingJobs: 10, + NumConcurrentResolvingTrieNodesJobs: 3, + MainPreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + FullArchivePreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + PayloadValidator: payloadValidator, } requestersContainerFactoryArgs := requesterscontainer.FactoryArgs{ @@ -791,29 +790,6 @@ func (thn *TestHeartbeatNode) initDirectConnectionProcessor(tb testing.TB) { require.Nil(tb, err) } -func (thn *TestHeartbeatNode) initCrossShardPeerTopicNotifier(tb testing.TB) { - argsCrossShardPeerTopicNotifier := monitor.ArgsCrossShardPeerTopicNotifier{ - ShardCoordinator: thn.ShardCoordinator, - PeerShardMapper: thn.MainPeerShardMapper, - } - crossShardPeerTopicNotifier, err := monitor.NewCrossShardPeerTopicNotifier(argsCrossShardPeerTopicNotifier) - require.Nil(tb, err) - - err = thn.MainMessenger.AddPeerTopicNotifier(crossShardPeerTopicNotifier) - require.Nil(tb, err) - - argsCrossShardPeerTopicNotifier = monitor.ArgsCrossShardPeerTopicNotifier{ - ShardCoordinator: thn.ShardCoordinator, - PeerShardMapper: thn.FullArchivePeerShardMapper, - } - fullArchiveCrossShardPeerTopicNotifier, err := monitor.NewCrossShardPeerTopicNotifier(argsCrossShardPeerTopicNotifier) - require.Nil(tb, err) - - err = thn.FullArchiveMessenger.AddPeerTopicNotifier(fullArchiveCrossShardPeerTopicNotifier) - require.Nil(tb, err) - -} - // ConnectOnMain will try to initiate a connection to the provided parameter on the main messenger func (thn *TestHeartbeatNode) ConnectOnMain(connectable Connectable) error { if check.IfNil(connectable) { @@ -859,13 +835,19 @@ func MakeDisplayTableForHeartbeatNodes(nodes map[uint32][]*TestHeartbeatNode) st for _, n := range nodesList { buffPk, _ := n.NodeKeys.MainKey.Pk.ToByteArray() + validatorMarker := "" + v, _, _ := n.NodesCoordinator.GetValidatorWithPublicKey(buffPk) + if v != nil { + validatorMarker = "*" + } + peerInfo := n.MainMessenger.GetConnectedPeersInfo() pid := n.MainMessenger.ID().Pretty() lineData := display.NewLineData( false, []string{ - core.GetTrimmedPk(hex.EncodeToString(buffPk)), + core.GetTrimmedPk(hex.EncodeToString(buffPk)) + validatorMarker, pid[len(pid)-6:], fmt.Sprintf("%d", shardId), fmt.Sprintf("%d", n.CountGlobalMessages()), diff --git a/integrationTests/testNetwork.go b/integrationTests/testNetwork.go index e22222d41a7..a08b3aa85c7 100644 --- a/integrationTests/testNetwork.go +++ b/integrationTests/testNetwork.go @@ -34,7 +34,7 @@ type GasScheduleMap = map[string]map[string]uint64 // TestNetwork wraps a set of TestProcessorNodes along with a set of test // Wallets, instantiates them, controls them and provides operations with them; // designed to be used in integration tests. -// TODO combine TestNetwork with the preexisting TestContext and OneNodeNetwork +// TODO combine TestNetwork with the preexisting TestContext and MiniNetwork // into a single struct containing the functionality of all three type TestNetwork struct { NumShards int diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index cb23b90ca8c..29aba701c35 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -1441,22 +1441,23 @@ func (tpn *TestProcessorNode) initResolvers() { fullArchivePreferredPeersHolder, _ := p2pFactory.NewPeersHolder([]string{}) resolverContainerFactory := resolverscontainer.FactoryArgs{ - ShardCoordinator: tpn.ShardCoordinator, - MainMessenger: tpn.MainMessenger, - FullArchiveMessenger: tpn.FullArchiveMessenger, - Store: tpn.Storage, - Marshalizer: TestMarshalizer, - DataPools: tpn.DataPool, - Uint64ByteSliceConverter: TestUint64Converter, - DataPacker: dataPacker, - TriesContainer: tpn.TrieContainer, - SizeCheckDelta: 100, - InputAntifloodHandler: &mock.NilAntifloodHandler{}, - OutputAntifloodHandler: &mock.NilAntifloodHandler{}, - NumConcurrentResolvingJobs: 10, - MainPreferredPeersHolder: preferredPeersHolder, - FullArchivePreferredPeersHolder: fullArchivePreferredPeersHolder, - PayloadValidator: payloadValidator, + ShardCoordinator: tpn.ShardCoordinator, + MainMessenger: tpn.MainMessenger, + FullArchiveMessenger: tpn.FullArchiveMessenger, + Store: tpn.Storage, + Marshalizer: TestMarshalizer, + DataPools: tpn.DataPool, + Uint64ByteSliceConverter: TestUint64Converter, + DataPacker: dataPacker, + TriesContainer: tpn.TrieContainer, + SizeCheckDelta: 100, + InputAntifloodHandler: &mock.NilAntifloodHandler{}, + OutputAntifloodHandler: &mock.NilAntifloodHandler{}, + NumConcurrentResolvingJobs: 10, + NumConcurrentResolvingTrieNodesJobs: 3, + MainPreferredPeersHolder: preferredPeersHolder, + FullArchivePreferredPeersHolder: fullArchivePreferredPeersHolder, + PayloadValidator: payloadValidator, } var err error diff --git a/integrationTests/testProcessorNodeWithTestWebServer.go b/integrationTests/testProcessorNodeWithTestWebServer.go index f177c08cfd8..84428a770b2 100644 --- a/integrationTests/testProcessorNodeWithTestWebServer.go +++ b/integrationTests/testProcessorNodeWithTestWebServer.go @@ -101,7 +101,7 @@ func createFacadeArg(tpn *TestProcessorNode) nodeFacade.ArgNodeFacade { func createTestApiConfig() config.ApiRoutesConfig { routes := map[string][]string{ - "node": {"/status", "/metrics", "/heartbeatstatus", "/statistics", "/p2pstatus", "/debug", "/peerinfo", "/bootstrapstatus", "/connected-peers-ratings", "/managed-keys/count", "/managed-keys", "/managed-keys/eligible", "/managed-keys/waiting"}, + "node": {"/status", "/metrics", "/heartbeatstatus", "/statistics", "/p2pstatus", "/debug", "/peerinfo", "/bootstrapstatus", "/connected-peers-ratings", "/managed-keys/count", "/managed-keys", "/loaded-keys", "/managed-keys/eligible", "/managed-keys/waiting"}, "address": {"/:address", "/:address/balance", "/:address/username", "/:address/code-hash", "/:address/key/:key", "/:address/esdt", "/:address/esdt/:tokenIdentifier"}, "hardfork": {"/trigger"}, "network": {"/status", "/total-staked", "/economics", "/config"}, diff --git a/integrationTests/vm/wasm/queries/queries_test.go b/integrationTests/vm/wasm/queries/queries_test.go new file mode 100644 index 00000000000..7c51f04b325 --- /dev/null +++ b/integrationTests/vm/wasm/queries/queries_test.go @@ -0,0 +1,246 @@ +//go:build !race + +// TODO remove build condition above to allow -race -short, after Wasm VM fix + +package queries + +import ( + "context" + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-go/integrationTests" + "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/factory" + "github.com/multiversx/mx-chain-go/vm" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + + "github.com/stretchr/testify/require" +) + +type now struct { + blockNonce uint64 + stateRootHash []byte +} + +func TestQueries(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + snapshotsOfGetNow := make(map[uint64]now) + snapshotsOfGetState := make(map[uint64]int) + historyOfGetNow := make(map[uint64]now) + historyOfGetState := make(map[uint64]int) + + network := integrationTests.NewMiniNetwork() + defer network.Stop() + + scOwner := network.AddUser(big.NewInt(10000000000000)) + + network.Start() + + // Block 1 + + scAddress := deploy(t, network, scOwner.Address, "../testdata/history/output/history.wasm") + network.Continue(t, 1) + + // Block 2 + + now := queryHistoryGetNow(t, network.ShardNode, scAddress, core.OptionalUint64{}) + snapshotsOfGetNow[1] = now + network.Continue(t, 1) + + // Block 3 + + now = queryHistoryGetNow(t, network.ShardNode, scAddress, core.OptionalUint64{}) + snapshotsOfGetNow[2] = now + setState(t, network, scAddress, scOwner.Address, 42) + network.Continue(t, 1) + + // Block 4 + + state := getState(t, network.ShardNode, scAddress, core.OptionalUint64{}) + snapshotsOfGetState[3] = state + now = queryHistoryGetNow(t, network.ShardNode, scAddress, core.OptionalUint64{}) + snapshotsOfGetNow[3] = now + setState(t, network, scAddress, scOwner.Address, 43) + network.Continue(t, 1) + + // Block 4 + + state = getState(t, network.ShardNode, scAddress, core.OptionalUint64{}) + snapshotsOfGetState[4] = state + now = queryHistoryGetNow(t, network.ShardNode, scAddress, core.OptionalUint64{}) + snapshotsOfGetNow[4] = now + network.Continue(t, 1) + + // Check snapshots + block1, _ := network.ShardNode.GetShardHeader(1) + block2, _ := network.ShardNode.GetShardHeader(2) + block3, _ := network.ShardNode.GetShardHeader(3) + + require.Equal(t, uint64(1), snapshotsOfGetNow[1].blockNonce) + require.Equal(t, uint64(2), snapshotsOfGetNow[2].blockNonce) + require.Equal(t, uint64(3), snapshotsOfGetNow[3].blockNonce) + require.Equal(t, uint64(4), snapshotsOfGetNow[4].blockNonce) + + require.Equal(t, block1.GetRootHash(), snapshotsOfGetNow[1].stateRootHash) + require.Equal(t, block1.GetRootHash(), snapshotsOfGetNow[2].stateRootHash) + require.NotEqual(t, block2.GetRootHash(), snapshotsOfGetNow[3].stateRootHash) + require.NotEqual(t, block3.GetRootHash(), snapshotsOfGetNow[4].stateRootHash) + + require.Equal(t, 42, snapshotsOfGetState[3]) + require.Equal(t, 43, snapshotsOfGetState[4]) + + // Check history + historyOfGetState[1] = getState(t, network.ShardNode, scAddress, core.OptionalUint64{HasValue: true, Value: 1}) + historyOfGetNow[1] = queryHistoryGetNow(t, network.ShardNode, scAddress, core.OptionalUint64{HasValue: true, Value: 1}) + + historyOfGetState[2] = getState(t, network.ShardNode, scAddress, core.OptionalUint64{HasValue: true, Value: 2}) + historyOfGetNow[2] = queryHistoryGetNow(t, network.ShardNode, scAddress, core.OptionalUint64{HasValue: true, Value: 2}) + + historyOfGetState[3] = getState(t, network.ShardNode, scAddress, core.OptionalUint64{HasValue: true, Value: 3}) + historyOfGetNow[3] = queryHistoryGetNow(t, network.ShardNode, scAddress, core.OptionalUint64{HasValue: true, Value: 3}) + + historyOfGetState[4] = getState(t, network.ShardNode, scAddress, core.OptionalUint64{HasValue: true, Value: 4}) + historyOfGetNow[4] = queryHistoryGetNow(t, network.ShardNode, scAddress, core.OptionalUint64{HasValue: true, Value: 4}) + + require.Equal(t, snapshotsOfGetState[1], historyOfGetState[1]) + require.Equal(t, snapshotsOfGetNow[1].blockNonce, historyOfGetNow[1].blockNonce) + + require.Equal(t, snapshotsOfGetState[2], historyOfGetState[2]) + require.Equal(t, snapshotsOfGetNow[2].blockNonce, historyOfGetNow[2].blockNonce) + + require.Equal(t, snapshotsOfGetState[3], historyOfGetState[3]) + require.Equal(t, snapshotsOfGetNow[3].blockNonce, historyOfGetNow[3].blockNonce) + + require.Equal(t, snapshotsOfGetState[4], historyOfGetState[4]) + require.Equal(t, snapshotsOfGetNow[4].blockNonce, historyOfGetNow[4].blockNonce) +} + +func deploy(t *testing.T, network *integrationTests.MiniNetwork, sender []byte, codePath string) []byte { + code := wasm.GetSCCode(codePath) + data := fmt.Sprintf("%s@%s@0100", code, hex.EncodeToString(factory.WasmVirtualMachine)) + + _, err := network.SendTransaction( + sender, + make([]byte, 32), + big.NewInt(0), + data, + 1000, + ) + require.NoError(t, err) + + scAddress, _ := network.ShardNode.BlockchainHook.NewAddress(sender, 0, factory.WasmVirtualMachine) + return scAddress +} + +func setState(t *testing.T, network *integrationTests.MiniNetwork, scAddress []byte, sender []byte, value uint64) { + data := fmt.Sprintf("setState@%x", value) + + _, err := network.SendTransaction( + sender, + scAddress, + big.NewInt(0), + data, + 1000, + ) + + require.NoError(t, err) +} + +func getState(t *testing.T, node *integrationTests.TestProcessorNode, scAddress []byte, blockNonce core.OptionalUint64) int { + vmOutput, _, err := node.SCQueryService.ExecuteQuery(&process.SCQuery{ + ScAddress: scAddress, + FuncName: "getState", + Arguments: [][]byte{}, + BlockNonce: blockNonce, + }) + + require.Nil(t, err) + require.Equal(t, vmcommon.Ok, vmOutput.ReturnCode) + data := vmOutput.ReturnData + + return int(big.NewInt(0).SetBytes(data[0]).Uint64()) +} + +func queryHistoryGetNow(t *testing.T, node *integrationTests.TestProcessorNode, scAddress []byte, blockNonce core.OptionalUint64) now { + vmOutput, _, err := node.SCQueryService.ExecuteQuery(&process.SCQuery{ + ScAddress: scAddress, + FuncName: "getNow", + Arguments: [][]byte{}, + BlockNonce: blockNonce, + }) + + require.Nil(t, err) + require.Equal(t, vmcommon.Ok, vmOutput.ReturnCode) + data := vmOutput.ReturnData + + return now{ + blockNonce: big.NewInt(0).SetBytes(data[0]).Uint64(), + stateRootHash: data[1], + } +} + +func TestQueries_Metachain(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + network := integrationTests.NewMiniNetwork() + defer network.Stop() + + network.Start() + + alice := network.AddUser(big.NewInt(10000000000000)) + + // Issue fungible token + issueCost := big.NewInt(1000) + tokenNameHex := hex.EncodeToString([]byte("Test")) + tokenTickerHex := hex.EncodeToString([]byte("TEST")) + txData := fmt.Sprintf("issue@%s@%s@64@00", tokenNameHex, tokenTickerHex) + + _, err := network.SendTransaction( + alice.Address, + vm.ESDTSCAddress, + issueCost, + txData, + core.MinMetaTxExtraGasCost, + ) + + require.NoError(t, err) + network.Continue(t, 5) + + tokens, err := network.MetachainNode.Node.GetAllIssuedESDTs(core.FungibleESDT, context.Background()) + require.NoError(t, err) + require.Len(t, tokens, 1) + + // Query token on older block (should fail) + vmOutput, _, err := network.MetachainNode.SCQueryService.ExecuteQuery(&process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getTokenProperties", + Arguments: [][]byte{[]byte(tokens[0])}, + BlockNonce: core.OptionalUint64{HasValue: true, Value: 2}, + }) + + require.Nil(t, err) + require.Equal(t, vmcommon.UserError, vmOutput.ReturnCode) + require.Equal(t, "no ticker with given name", vmOutput.ReturnMessage) + + // Query token on newer block (should succeed) + vmOutput, _, err = network.MetachainNode.SCQueryService.ExecuteQuery(&process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getTokenProperties", + Arguments: [][]byte{[]byte(tokens[0])}, + BlockNonce: core.OptionalUint64{HasValue: true, Value: 4}, + }) + + require.Nil(t, err) + require.Equal(t, vmcommon.Ok, vmOutput.ReturnCode) + require.Equal(t, "Test", string(vmOutput.ReturnData[0])) +} diff --git a/integrationTests/vm/wasm/testdata/history/history.c b/integrationTests/vm/wasm/testdata/history/history.c new file mode 100644 index 00000000000..322e216aca8 --- /dev/null +++ b/integrationTests/vm/wasm/testdata/history/history.c @@ -0,0 +1,51 @@ +typedef unsigned char byte; +typedef unsigned int i32; +typedef unsigned long long i64; + +int getArgument(int argumentIndex, byte *argument); +long long int64getArgument(int argumentIndex); +long long getBlockNonce(); +long long getBlockEpoch(); +void getStateRootHash(byte *hash); + +int int64storageStore(byte *key, int keyLength, long long value); +long long int64storageLoad(byte *key, int keyLength); + +void finish(byte *data, int length); +void int64finish(long long value); + +byte zero32_buffer_a[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +byte zero32_buffer_b[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +byte zero32_buffer_c[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +byte storageKey[] = "state"; + +void init() +{ +} + +void upgrade() +{ +} + +void setState() +{ + i64 state = int64getArgument(0); + int64storageStore(storageKey, sizeof(storageKey) - 1, state); +} + +void getState() +{ + i64 state = int64storageLoad(storageKey, sizeof(storageKey) - 1); + int64finish(state); +} + +void getNow() +{ + i64 nonce = getBlockNonce(); + + byte *stateRootHash = zero32_buffer_a; + getStateRootHash(stateRootHash); + + int64finish(nonce); + finish(stateRootHash, 32); +} diff --git a/integrationTests/vm/wasm/testdata/history/history.export b/integrationTests/vm/wasm/testdata/history/history.export new file mode 100644 index 00000000000..b6646aa3aef --- /dev/null +++ b/integrationTests/vm/wasm/testdata/history/history.export @@ -0,0 +1,5 @@ +init +upgrade +getNow +setState +getState diff --git a/integrationTests/vm/wasm/testdata/history/output/history.wasm b/integrationTests/vm/wasm/testdata/history/output/history.wasm new file mode 100755 index 00000000000..5e34d9a0ab0 Binary files /dev/null and b/integrationTests/vm/wasm/testdata/history/output/history.wasm differ diff --git a/integrationTests/vm/wasm/testdata/transferValue/output/transferValue.wasm b/integrationTests/vm/wasm/testdata/transferValue/output/transferValue.wasm new file mode 100755 index 00000000000..cea133a3b2f Binary files /dev/null and b/integrationTests/vm/wasm/testdata/transferValue/output/transferValue.wasm differ diff --git a/integrationTests/vm/wasm/testdata/transferValue/transferValue.c b/integrationTests/vm/wasm/testdata/transferValue/transferValue.c new file mode 100644 index 00000000000..e82fc4054d8 --- /dev/null +++ b/integrationTests/vm/wasm/testdata/transferValue/transferValue.c @@ -0,0 +1,58 @@ +typedef unsigned char byte; +typedef unsigned int i32; +typedef unsigned long long i64; + +int getArgument(int argumentIndex, byte *argument); +int transferValueExecute(byte *destination, byte *value, long long gas, byte *function, int functionLength, int numArguments, byte *argumentsLengths, byte *arguments); +void getCaller(byte *callerAddress); +i32 createAsyncCall(byte *destination, byte *value, byte *data, int dataLength, byte *success, int successLength, byte *error, int errorLength, long long gas, long long extraGasForCallback); + +byte zero32_a[32] = {0}; +byte zero32_b[32] = {0}; +byte zero32_c[32] = {0}; + +byte oneAtomOfEGLD[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; +byte functionNameAskMoney[] = "askMoney"; +byte functionNameMyCallback[] = "myCallback"; + +void init() +{ +} + +void upgrade() +{ +} + +void fund() +{ +} + +void forwardAskMoney() +{ + byte *otherContract = zero32_a; + getArgument(0, otherContract); + + createAsyncCall( + otherContract, + 0, + functionNameAskMoney, + sizeof(functionNameAskMoney) - 1, + functionNameMyCallback, + sizeof(functionNameMyCallback) - 1, + functionNameMyCallback, + sizeof(functionNameMyCallback) - 1, + 15000000, + 0); +} + +void askMoney() +{ + byte *caller = zero32_a; + + getCaller(caller); + transferValueExecute(caller, oneAtomOfEGLD, 0, 0, 0, 0, 0, 0); +} + +void myCallback() +{ +} diff --git a/integrationTests/vm/wasm/testdata/transferValue/transferValue.export b/integrationTests/vm/wasm/testdata/transferValue/transferValue.export new file mode 100644 index 00000000000..c9613a09af3 --- /dev/null +++ b/integrationTests/vm/wasm/testdata/transferValue/transferValue.export @@ -0,0 +1,6 @@ +init +upgrade +fund +forwardAskMoney +askMoney +myCallback diff --git a/integrationTests/vm/wasm/transfers/transfers_test.go b/integrationTests/vm/wasm/transfers/transfers_test.go new file mode 100644 index 00000000000..98e0a416a89 --- /dev/null +++ b/integrationTests/vm/wasm/transfers/transfers_test.go @@ -0,0 +1,62 @@ +//go:build !race + +package transfers + +import ( + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" + "github.com/stretchr/testify/require" +) + +func TestTransfers_DuplicatedTransferValueEvents(t *testing.T) { + context := wasm.SetupTestContext(t) + defer context.Close() + + err := context.DeploySC("../testdata/transferValue/output/transferValue.wasm", "") + require.Nil(t, err) + vault := context.ScAddress + + err = context.DeploySC("../testdata/transferValue/output/transferValue.wasm", "") + require.Nil(t, err) + forwarder := context.ScAddress + + // Add money to the vault + context.ScAddress = vault + err = context.ExecuteSCWithValue(&context.Owner, "fund", big.NewInt(42)) + require.Nil(t, err) + + // Ask money from the vault, via the forwarder + context.ScAddress = forwarder + err = context.ExecuteSC(&context.Owner, fmt.Sprintf("forwardAskMoney@%s", hex.EncodeToString(vault))) + require.Nil(t, err) + require.Len(t, context.LastLogs, 1) + require.Len(t, context.LastLogs[0].GetLogEvents(), 5) + + events := context.LastLogs[0].GetLogEvents() + + require.Equal(t, "transferValueOnly", string(events[0].GetIdentifier())) + require.Equal(t, "AsyncCall", string(events[0].GetData())) + require.Equal(t, []byte{}, events[0].GetTopics()[0]) + require.Equal(t, forwarder, events[0].GetAddress()) + require.Equal(t, vault, events[0].GetTopics()[1]) + + require.Equal(t, "transferValueOnly", string(events[1].GetIdentifier())) + require.Equal(t, "BackTransfer", string(events[1].GetData())) + require.Equal(t, []byte{0x01}, events[1].GetTopics()[0]) + require.Equal(t, vault, events[1].GetAddress()) + require.Equal(t, forwarder, events[1].GetTopics()[1]) + + // Duplicated "transferValueOnly" events are fixed in #5936. + require.Equal(t, "transferValueOnly", string(events[2].GetIdentifier())) + require.Equal(t, "AsyncCallback", string(events[2].GetData())) + require.Equal(t, []byte{}, events[2].GetTopics()[0]) + require.Equal(t, vault, events[2].GetAddress()) + require.Equal(t, forwarder, events[2].GetTopics()[1]) + + require.Equal(t, "writeLog", string(events[3].GetIdentifier())) + require.Equal(t, "completedTxEvent", string(events[4].GetIdentifier())) +} diff --git a/integrationTests/vm/wasm/upgrades/upgrades_test.go b/integrationTests/vm/wasm/upgrades/upgrades_test.go index c989498c955..514507b0c04 100644 --- a/integrationTests/vm/wasm/upgrades/upgrades_test.go +++ b/integrationTests/vm/wasm/upgrades/upgrades_test.go @@ -10,9 +10,7 @@ import ( "math/big" "testing" - "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/integrationTests" - "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/factory" @@ -172,61 +170,56 @@ func TestUpgrades_HelloTrialAndError(t *testing.T) { t.Skip("this is not a short test") } - network := integrationTests.NewOneNodeNetwork() + network := integrationTests.NewMiniNetwork() defer network.Stop() - alice := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") - bob := []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") - network.Mint(alice, big.NewInt(10000000000000)) - network.Mint(bob, big.NewInt(10000000000000)) + alice := network.AddUser(big.NewInt(10000000000000)) + bob := network.AddUser(big.NewInt(10000000000000)) - network.GoToRoundOne() + network.Start() deployTxData := fmt.Sprintf("%s@%s@0100", wasm.GetSCCode("../testdata/hello-v1/output/answer.wasm"), hex.EncodeToString(factory.WasmVirtualMachine)) upgradeTxData := fmt.Sprintf("upgradeContract@%s@0100", wasm.GetSCCode("../testdata/hello-v2/output/answer.wasm")) // Deploy the smart contract. Alice is the owner - network.AddTxToPool(&transaction.Transaction{ - Nonce: 0, - Value: big.NewInt(0), - RcvAddr: vm.CreateEmptyAddress(), - SndAddr: alice, - GasPrice: network.GetMinGasPrice(), - GasLimit: network.MaxGasLimitPerBlock(), - Data: []byte(deployTxData), - }) + _, err := network.SendTransaction( + alice.Address, + make([]byte, 32), + big.NewInt(0), + deployTxData, + 1000, + ) + require.Nil(t, err) - scAddress, _ := network.Node.BlockchainHook.NewAddress(alice, 0, factory.WasmVirtualMachine) + scAddress, _ := network.ShardNode.BlockchainHook.NewAddress(alice.Address, 0, factory.WasmVirtualMachine) network.Continue(t, 1) - require.Equal(t, []byte{24}, query(t, network.Node, scAddress, "getUltimateAnswer")) + require.Equal(t, []byte{24}, query(t, network.ShardNode, scAddress, "getUltimateAnswer")) // Upgrade as Bob - upgrade should fail, since Alice is the owner - network.AddTxToPool(&transaction.Transaction{ - Nonce: 0, - Value: big.NewInt(0), - RcvAddr: scAddress, - SndAddr: bob, - GasPrice: network.GetMinGasPrice(), - GasLimit: network.MaxGasLimitPerBlock(), - Data: []byte(upgradeTxData), - }) + _, err = network.SendTransaction( + bob.Address, + scAddress, + big.NewInt(0), + upgradeTxData, + 1000, + ) + require.Nil(t, err) network.Continue(t, 1) - require.Equal(t, []byte{24}, query(t, network.Node, scAddress, "getUltimateAnswer")) + require.Equal(t, []byte{24}, query(t, network.ShardNode, scAddress, "getUltimateAnswer")) // Now upgrade as Alice, should work - network.AddTxToPool(&transaction.Transaction{ - Nonce: 1, - Value: big.NewInt(0), - RcvAddr: scAddress, - SndAddr: alice, - GasPrice: network.GetMinGasPrice(), - GasLimit: network.MaxGasLimitPerBlock(), - Data: []byte(upgradeTxData), - }) + _, err = network.SendTransaction( + alice.Address, + scAddress, + big.NewInt(0), + upgradeTxData, + 1000, + ) + require.Nil(t, err) network.Continue(t, 1) - require.Equal(t, []byte{42}, query(t, network.Node, scAddress, "getUltimateAnswer")) + require.Equal(t, []byte{42}, query(t, network.ShardNode, scAddress, "getUltimateAnswer")) } func TestUpgrades_CounterTrialAndError(t *testing.T) { @@ -234,75 +227,69 @@ func TestUpgrades_CounterTrialAndError(t *testing.T) { t.Skip("this is not a short test") } - network := integrationTests.NewOneNodeNetwork() + network := integrationTests.NewMiniNetwork() defer network.Stop() - alice := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") - bob := []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") - network.Mint(alice, big.NewInt(10000000000000)) - network.Mint(bob, big.NewInt(10000000000000)) + alice := network.AddUser(big.NewInt(10000000000000)) + bob := network.AddUser(big.NewInt(10000000000000)) - network.GoToRoundOne() + network.Start() deployTxData := fmt.Sprintf("%s@%s@0100", wasm.GetSCCode("../testdata/counter/output/counter.wasm"), hex.EncodeToString(factory.WasmVirtualMachine)) upgradeTxData := fmt.Sprintf("upgradeContract@%s@0100", wasm.GetSCCode("../testdata/counter/output/counter.wasm")) // Deploy the smart contract. Alice is the owner - network.AddTxToPool(&transaction.Transaction{ - Nonce: 0, - Value: big.NewInt(0), - RcvAddr: vm.CreateEmptyAddress(), - SndAddr: alice, - GasPrice: network.GetMinGasPrice(), - GasLimit: network.MaxGasLimitPerBlock(), - Data: []byte(deployTxData), - }) + _, err := network.SendTransaction( + alice.Address, + make([]byte, 32), + big.NewInt(0), + deployTxData, + 1000, + ) + require.Nil(t, err) - scAddress, _ := network.Node.BlockchainHook.NewAddress(alice, 0, factory.WasmVirtualMachine) + scAddress, _ := network.ShardNode.BlockchainHook.NewAddress(alice.Address, 0, factory.WasmVirtualMachine) network.Continue(t, 1) - require.Equal(t, []byte{1}, query(t, network.Node, scAddress, "get")) + require.Equal(t, []byte{1}, query(t, network.ShardNode, scAddress, "get")) // Increment the counter (could be either Bob or Alice) - network.AddTxToPool(&transaction.Transaction{ - Nonce: 1, - Value: big.NewInt(0), - RcvAddr: scAddress, - SndAddr: alice, - GasPrice: network.GetMinGasPrice(), - GasLimit: network.MaxGasLimitPerBlock(), - Data: []byte("increment"), - }) + _, err = network.SendTransaction( + alice.Address, + scAddress, + big.NewInt(0), + "increment", + 1000, + ) + require.Nil(t, err) network.Continue(t, 1) - require.Equal(t, []byte{2}, query(t, network.Node, scAddress, "get")) + require.Equal(t, []byte{2}, query(t, network.ShardNode, scAddress, "get")) // Upgrade as Bob - upgrade should fail, since Alice is the owner (counter.init() not executed, state not reset) - network.AddTxToPool(&transaction.Transaction{ - Nonce: 0, - Value: big.NewInt(0), - RcvAddr: scAddress, - SndAddr: bob, - GasPrice: network.GetMinGasPrice(), - GasLimit: network.MaxGasLimitPerBlock(), - Data: []byte(upgradeTxData), - }) + _, err = network.SendTransaction( + bob.Address, + scAddress, + big.NewInt(0), + upgradeTxData, + 1000, + ) + require.Nil(t, err) network.Continue(t, 1) - require.Equal(t, []byte{2}, query(t, network.Node, scAddress, "get")) + require.Equal(t, []byte{2}, query(t, network.ShardNode, scAddress, "get")) // Now upgrade as Alice, should work (state is reset by counter.init()) - network.AddTxToPool(&transaction.Transaction{ - Nonce: 2, - Value: big.NewInt(0), - RcvAddr: scAddress, - SndAddr: alice, - GasPrice: network.GetMinGasPrice(), - GasLimit: network.MaxGasLimitPerBlock(), - Data: []byte(upgradeTxData), - }) + _, err = network.SendTransaction( + alice.Address, + scAddress, + big.NewInt(0), + upgradeTxData, + 1000, + ) + require.Nil(t, err) network.Continue(t, 1) - require.Equal(t, []byte{1}, query(t, network.Node, scAddress, "get")) + require.Equal(t, []byte{1}, query(t, network.ShardNode, scAddress, "get")) } func query(t *testing.T, node *integrationTests.TestProcessorNode, scAddress []byte, function string) []byte { diff --git a/integrationTests/vm/wasm/utils.go b/integrationTests/vm/wasm/utils.go index e58d3e25c7b..e8987f24bd2 100644 --- a/integrationTests/vm/wasm/utils.go +++ b/integrationTests/vm/wasm/utils.go @@ -16,6 +16,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/pubkeyConverter" + "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/rewardTx" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" @@ -102,6 +103,7 @@ type TestContext struct { ScAddress []byte ScCodeMetadata vmcommon.CodeMetadata Accounts *state.AccountsDB + TxLogsProcessor process.TransactionLogProcessor TxProcessor process.TransactionProcessor ScProcessor scrCommon.TestSmartContractProcessor QueryService external.SCQueryService @@ -112,6 +114,7 @@ type TestContext struct { LastTxHash []byte SCRForwarder *mock.IntermediateTransactionHandlerMock LastSCResults []*smartContractResult.SmartContractResult + LastLogs []*data.LogData } type testParticipant struct { @@ -164,7 +167,7 @@ func SetupTestContextWithGasSchedule(t *testing.T, gasSchedule map[string]map[st context.initFeeHandlers() context.initVMAndBlockchainHook() context.initTxProcessorWithOneSCExecutorWithVMs() - context.ScAddress, _ = context.BlockchainHook.NewAddress(context.Owner.Address, context.Owner.Nonce, factory.WasmVirtualMachine) + argsNewSCQueryService := smartContract.ArgsNewSCQueryService{ VmContainer: context.VMContainer, EconomicsFee: context.EconomicsFee, @@ -364,8 +367,11 @@ func (context *TestContext) initTxProcessorWithOneSCExecutorWithVMs() { defaults.FillGasMapInternal(gasSchedule, 1) argsLogProcessor := transactionLog.ArgTxLogProcessor{Marshalizer: marshalizer} - logsProcessor, _ := transactionLog.NewTxLogProcessor(argsLogProcessor) + context.TxLogsProcessor, err = transactionLog.NewTxLogProcessor(argsLogProcessor) + require.Nil(context.T, err) + context.SCRForwarder = &mock.IntermediateTransactionHandlerMock{} + argsNewSCProcessor := scrCommon.ArgsNewSmartContractProcessor{ VmContainer: context.VMContainer, ArgsParser: smartContract.NewArgumentParser(), @@ -385,14 +391,14 @@ func (context *TestContext) initTxProcessorWithOneSCExecutorWithVMs() { SetGasRefundedCalled: func(gasRefunded uint64, hash []byte) {}, }, GasSchedule: mock.NewGasScheduleNotifierMock(gasSchedule), - TxLogsProcessor: logsProcessor, + TxLogsProcessor: context.TxLogsProcessor, EnableRoundsHandler: context.EnableRoundsHandler, EnableEpochsHandler: context.EnableEpochsHandler, WasmVMChangeLocker: context.WasmVMChangeLocker, VMOutputCacher: txcache.NewDisabledCache(), } - context.ScProcessor, _ = processProxy.NewTestSmartContractProcessorProxy(argsNewSCProcessor, context.EpochNotifier) + context.ScProcessor, err = processProxy.NewTestSmartContractProcessorProxy(argsNewSCProcessor, context.EpochNotifier) require.Nil(context.T, err) argsNewTxProcessor := processTransaction.ArgsNewTxProcessor{ @@ -414,7 +420,7 @@ func (context *TestContext) initTxProcessorWithOneSCExecutorWithVMs() { EnableEpochsHandler: context.EnableEpochsHandler, TxVersionChecker: &testscommon.TxVersionCheckerStub{}, GuardianChecker: &guardianMocks.GuardedAccountHandlerStub{}, - TxLogsProcessor: logsProcessor, + TxLogsProcessor: context.TxLogsProcessor, } context.TxProcessor, err = processTransaction.NewTxProcessor(argsNewTxProcessor) @@ -544,20 +550,20 @@ func (context *TestContext) DeploySC(wasmPath string, parametersString string) e return err } + context.ScAddress, _ = context.BlockchainHook.NewAddress(context.Owner.Address, context.Owner.Nonce, factory.WasmVirtualMachine) + owner.Nonce++ _, err = context.Accounts.Commit() if err != nil { return err } - err = context.GetCompositeTestError() + err = context.acquireOutcome() if err != nil { return err } - _ = context.UpdateLastSCResults() - - return nil + return context.GetCompositeTestError() } // UpgradeSC - @@ -604,14 +610,12 @@ func (context *TestContext) UpgradeSC(wasmPath string, parametersString string) return err } - err = context.GetCompositeTestError() + err = context.acquireOutcome() if err != nil { return err } - _ = context.UpdateLastSCResults() - - return nil + return context.GetCompositeTestError() } // GetSCCode - @@ -680,18 +684,16 @@ func (context *TestContext) ExecuteSCWithValue(sender *testParticipant, txData s return err } - err = context.GetCompositeTestError() + err = context.acquireOutcome() if err != nil { return err } - _ = context.UpdateLastSCResults() - - return nil + return context.GetCompositeTestError() } -// UpdateLastSCResults -- -func (context *TestContext) UpdateLastSCResults() error { +// acquireOutcome - +func (context *TestContext) acquireOutcome() error { transactions := context.SCRForwarder.GetIntermediateTransactions() context.LastSCResults = make([]*smartContractResult.SmartContractResult, len(transactions)) for i, tx := range transactions { @@ -703,6 +705,8 @@ func (context *TestContext) UpdateLastSCResults() error { } } + context.LastLogs = context.TxLogsProcessor.GetAllCurrentLogs() + return nil } diff --git a/keysManagement/managedPeersHolder.go b/keysManagement/managedPeersHolder.go index a347f4f2a53..8156b64c8eb 100644 --- a/keysManagement/managedPeersHolder.go +++ b/keysManagement/managedPeersHolder.go @@ -5,6 +5,7 @@ import ( "crypto/rand" "encoding/hex" "fmt" + "sort" "sync" "time" @@ -281,7 +282,7 @@ func (holder *managedPeersHolder) ResetRoundsWithoutReceivedMessages(pkBytes []b pInfo.resetRoundsWithoutReceivedMessages() } -// GetManagedKeysByCurrentNode returns all keys that will be managed by this node +// GetManagedKeysByCurrentNode returns all keys that should act as validator(main or backup that took over) and will be managed by this node func (holder *managedPeersHolder) GetManagedKeysByCurrentNode() map[string]crypto.PrivateKey { holder.mut.RLock() defer holder.mut.RUnlock() @@ -299,6 +300,23 @@ func (holder *managedPeersHolder) GetManagedKeysByCurrentNode() map[string]crypt return allManagedKeys } +// GetLoadedKeysByCurrentNode returns all keys that were loaded and will be managed by this node +func (holder *managedPeersHolder) GetLoadedKeysByCurrentNode() [][]byte { + holder.mut.RLock() + defer holder.mut.RUnlock() + + allLoadedKeys := make([][]byte, 0, len(holder.data)) + for pk := range holder.data { + allLoadedKeys = append(allLoadedKeys, []byte(pk)) + } + + sort.Slice(allLoadedKeys, func(i, j int) bool { + return string(allLoadedKeys[i]) < string(allLoadedKeys[j]) + }) + + return allLoadedKeys +} + // IsKeyManagedByCurrentNode returns true if the key is managed by the current node func (holder *managedPeersHolder) IsKeyManagedByCurrentNode(pkBytes []byte) bool { pInfo := holder.getPeerInfo(pkBytes) diff --git a/keysManagement/managedPeersHolder_test.go b/keysManagement/managedPeersHolder_test.go index 81f0dfff86b..fa7d84209a2 100644 --- a/keysManagement/managedPeersHolder_test.go +++ b/keysManagement/managedPeersHolder_test.go @@ -751,6 +751,24 @@ func TestManagedPeersHolder_GetManagedKeysByCurrentNode(t *testing.T) { }) } +func TestManagedPeersHolder_GetLoadedKeysByCurrentNode(t *testing.T) { + t.Parallel() + + args := createMockArgsManagedPeersHolder() + holder, _ := keysManagement.NewManagedPeersHolder(args) + _ = holder.AddManagedPeer(skBytes1) + _ = holder.AddManagedPeer(skBytes0) + + for i := 0; i < 10; i++ { + holder.IncrementRoundsWithoutReceivedMessages(pkBytes0) + } + + result := holder.GetLoadedKeysByCurrentNode() + assert.Equal(t, 2, len(result)) + assert.Equal(t, pkBytes0, result[0]) + assert.Equal(t, pkBytes1, result[1]) +} + func TestManagedPeersHolder_IsKeyManagedByCurrentNode(t *testing.T) { t.Parallel() diff --git a/keysManagement/managedPeersMonitor.go b/keysManagement/managedPeersMonitor.go index 2c2eef290b4..5f9f117cc2b 100644 --- a/keysManagement/managedPeersMonitor.go +++ b/keysManagement/managedPeersMonitor.go @@ -60,7 +60,7 @@ func (monitor *managedPeersMonitor) GetManagedKeysCount() int { return len(monitor.managedPeersHolder.GetManagedKeysByCurrentNode()) } -// GetManagedKeys returns all keys managed by the current node +// GetManagedKeys returns all keys that should act as validator(main or backup that took over) and will be managed by this node func (monitor *managedPeersMonitor) GetManagedKeys() [][]byte { managedKeysMap := monitor.managedPeersHolder.GetManagedKeysByCurrentNode() managedKeys := make([][]byte, 0, len(managedKeysMap)) @@ -75,6 +75,11 @@ func (monitor *managedPeersMonitor) GetManagedKeys() [][]byte { return managedKeys } +// GetLoadedKeys returns all keys that were loaded and will be managed by this node +func (monitor *managedPeersMonitor) GetLoadedKeys() [][]byte { + return monitor.managedPeersHolder.GetLoadedKeysByCurrentNode() +} + // GetEligibleManagedKeys returns eligible keys that are managed by the current node in the current epoch func (monitor *managedPeersMonitor) GetEligibleManagedKeys() ([][]byte, error) { epoch := monitor.epochProvider.CurrentEpoch() diff --git a/keysManagement/managedPeersMonitor_test.go b/keysManagement/managedPeersMonitor_test.go index 9ec9dbcd8ad..4be6a5282ca 100644 --- a/keysManagement/managedPeersMonitor_test.go +++ b/keysManagement/managedPeersMonitor_test.go @@ -281,3 +281,20 @@ func TestManagedPeersMonitor_GetManagedKeys(t *testing.T) { keys := monitor.GetManagedKeys() require.Equal(t, expectedManagedKeys, keys) } + +func TestManagedPeersMonitor_GetLoadedKeys(t *testing.T) { + t.Parallel() + + loadedKeys := [][]byte{[]byte("pk1"), []byte("pk2"), []byte("pk3")} + args := createMockArgManagedPeersMonitor() + args.ManagedPeersHolder = &testscommon.ManagedPeersHolderStub{ + GetLoadedKeysByCurrentNodeCalled: func() [][]byte { + return loadedKeys + }, + } + monitor, err := NewManagedPeersMonitor(args) + require.NoError(t, err) + + keys := monitor.GetLoadedKeys() + require.Equal(t, loadedKeys, keys) +} diff --git a/node/external/nodeApiResolver.go b/node/external/nodeApiResolver.go index 15d7f445962..ec1f414a286 100644 --- a/node/external/nodeApiResolver.go +++ b/node/external/nodeApiResolver.go @@ -40,6 +40,7 @@ type ArgNodeApiResolver struct { AccountsParser genesis.AccountsParser GasScheduleNotifier common.GasScheduleNotifierAPI ManagedPeersMonitor common.ManagedPeersMonitor + PublicKey string } // nodeApiResolver can resolve API requests @@ -58,6 +59,7 @@ type nodeApiResolver struct { accountsParser genesis.AccountsParser gasScheduleNotifier common.GasScheduleNotifierAPI managedPeersMonitor common.ManagedPeersMonitor + publicKey string } // NewNodeApiResolver creates a new nodeApiResolver instance @@ -120,6 +122,7 @@ func NewNodeApiResolver(arg ArgNodeApiResolver) (*nodeApiResolver, error) { accountsParser: arg.AccountsParser, gasScheduleNotifier: arg.GasScheduleNotifier, managedPeersMonitor: arg.ManagedPeersMonitor, + publicKey: arg.PublicKey, }, nil } @@ -339,12 +342,23 @@ func (nar *nodeApiResolver) GetManagedKeysCount() int { return nar.managedPeersMonitor.GetManagedKeysCount() } -// GetManagedKeys returns all keys managed by the current node when running in multikey mode +// GetManagedKeys returns all keys that should act as validator(main or backup that took over) and will be managed by this node func (nar *nodeApiResolver) GetManagedKeys() []string { managedKeys := nar.managedPeersMonitor.GetManagedKeys() return nar.parseKeys(managedKeys) } +// GetLoadedKeys returns all keys that were loaded by this node +func (nar *nodeApiResolver) GetLoadedKeys() []string { + loadedKeys := nar.managedPeersMonitor.GetLoadedKeys() + if len(loadedKeys) > 0 { + return nar.parseKeys(loadedKeys) + } + + // node is in single key mode, returning the main public key + return []string{nar.publicKey} +} + // GetEligibleManagedKeys returns the eligible managed keys when node is running in multikey mode func (nar *nodeApiResolver) GetEligibleManagedKeys() ([]string, error) { eligibleKeys, err := nar.managedPeersMonitor.GetEligibleManagedKeys() diff --git a/node/external/nodeApiResolver_test.go b/node/external/nodeApiResolver_test.go index 207ff020400..390e945bdab 100644 --- a/node/external/nodeApiResolver_test.go +++ b/node/external/nodeApiResolver_test.go @@ -726,6 +726,59 @@ func TestNodeApiResolver_GetManagedKeys(t *testing.T) { require.Equal(t, expectedKeys, keys) } +func TestNodeApiResolver_GetLoadedKeys(t *testing.T) { + t.Parallel() + + t.Run("multikey should work", func(t *testing.T) { + t.Parallel() + + providedKeys := [][]byte{ + []byte("pk1"), + []byte("pk2"), + } + expectedKeys := []string{ + "pk1", + "pk2", + } + args := createMockArgs() + args.ManagedPeersMonitor = &testscommon.ManagedPeersMonitorStub{ + GetLoadedKeysCalled: func() [][]byte { + return providedKeys + }, + } + args.ValidatorPubKeyConverter = &testscommon.PubkeyConverterStub{ + SilentEncodeCalled: func(pkBytes []byte, log core.Logger) string { + return string(pkBytes) + }, + } + nar, err := external.NewNodeApiResolver(args) + require.NoError(t, err) + + keys := nar.GetLoadedKeys() + require.Equal(t, expectedKeys, keys) + }) + t.Run("single key should work", func(t *testing.T) { + t.Parallel() + + providedKey := "pk1" + expectedKeys := []string{ + "pk1", + } + args := createMockArgs() + args.PublicKey = providedKey + args.ManagedPeersMonitor = &testscommon.ManagedPeersMonitorStub{ + GetLoadedKeysCalled: func() [][]byte { + return [][]byte{} + }, + } + nar, err := external.NewNodeApiResolver(args) + require.NoError(t, err) + + keys := nar.GetLoadedKeys() + require.Equal(t, expectedKeys, keys) + }) +} + func TestNodeApiResolver_GetEligibleManagedKeys(t *testing.T) { t.Parallel() diff --git a/process/smartContract/scQueryService.go b/process/smartContract/scQueryService.go index eb3d9b95e4e..ec6ad67e87c 100644 --- a/process/smartContract/scQueryService.go +++ b/process/smartContract/scQueryService.go @@ -22,53 +22,57 @@ import ( "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/smartContract/scrCommon" "github.com/multiversx/mx-chain-go/sharding" + logger "github.com/multiversx/mx-chain-logger-go" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-vm-common-go/parsers" ) var _ process.SCQueryService = (*SCQueryService)(nil) +var logQueryService = logger.GetOrCreate("process/smartcontract.queryService") + // MaxGasLimitPerQuery - each unit is the equivalent of 1 nanosecond processing time const MaxGasLimitPerQuery = 300_000_000_000 // SCQueryService can execute Get functions over SC to fetch stored values type SCQueryService struct { - vmContainer process.VirtualMachinesContainer - economicsFee process.FeeHandler - mutRunSc sync.Mutex - blockChainHook process.BlockChainHookWithAccountsAdapter - mainBlockChain data.ChainHandler - apiBlockChain data.ChainHandler - numQueries int - gasForQuery uint64 - wasmVMChangeLocker common.Locker - bootstrapper process.Bootstrapper - allowExternalQueriesChan chan struct{} - historyRepository dblookupext.HistoryRepository - shardCoordinator sharding.Coordinator - storageService dataRetriever.StorageService - marshaller marshal.Marshalizer - hasher hashing.Hasher - uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + vmContainer process.VirtualMachinesContainer + economicsFee process.FeeHandler + mutRunSc sync.Mutex + blockChainHook process.BlockChainHookWithAccountsAdapter + mainBlockChain data.ChainHandler + apiBlockChain data.ChainHandler + gasForQuery uint64 + wasmVMChangeLocker common.Locker + bootstrapper process.Bootstrapper + allowExternalQueriesChan chan struct{} + historyRepository dblookupext.HistoryRepository + shardCoordinator sharding.Coordinator + storageService dataRetriever.StorageService + marshaller marshal.Marshalizer + hasher hashing.Hasher + uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + isInHistoricalBalancesMode bool } // ArgsNewSCQueryService defines the arguments needed for the sc query service type ArgsNewSCQueryService struct { - VmContainer process.VirtualMachinesContainer - EconomicsFee process.FeeHandler - BlockChainHook process.BlockChainHookWithAccountsAdapter - MainBlockChain data.ChainHandler - APIBlockChain data.ChainHandler - WasmVMChangeLocker common.Locker - Bootstrapper process.Bootstrapper - AllowExternalQueriesChan chan struct{} - MaxGasLimitPerQuery uint64 - HistoryRepository dblookupext.HistoryRepository - ShardCoordinator sharding.Coordinator - StorageService dataRetriever.StorageService - Marshaller marshal.Marshalizer - Hasher hashing.Hasher - Uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + VmContainer process.VirtualMachinesContainer + EconomicsFee process.FeeHandler + BlockChainHook process.BlockChainHookWithAccountsAdapter + MainBlockChain data.ChainHandler + APIBlockChain data.ChainHandler + WasmVMChangeLocker common.Locker + Bootstrapper process.Bootstrapper + AllowExternalQueriesChan chan struct{} + MaxGasLimitPerQuery uint64 + HistoryRepository dblookupext.HistoryRepository + ShardCoordinator sharding.Coordinator + StorageService dataRetriever.StorageService + Marshaller marshal.Marshalizer + Hasher hashing.Hasher + Uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + IsInHistoricalBalancesMode bool } // NewSCQueryService returns a new instance of SCQueryService @@ -85,21 +89,22 @@ func NewSCQueryService( gasForQuery = args.MaxGasLimitPerQuery } return &SCQueryService{ - vmContainer: args.VmContainer, - economicsFee: args.EconomicsFee, - mainBlockChain: args.MainBlockChain, - apiBlockChain: args.APIBlockChain, - blockChainHook: args.BlockChainHook, - wasmVMChangeLocker: args.WasmVMChangeLocker, - bootstrapper: args.Bootstrapper, - gasForQuery: gasForQuery, - allowExternalQueriesChan: args.AllowExternalQueriesChan, - historyRepository: args.HistoryRepository, - shardCoordinator: args.ShardCoordinator, - storageService: args.StorageService, - marshaller: args.Marshaller, - hasher: args.Hasher, - uint64ByteSliceConverter: args.Uint64ByteSliceConverter, + vmContainer: args.VmContainer, + economicsFee: args.EconomicsFee, + mainBlockChain: args.MainBlockChain, + apiBlockChain: args.APIBlockChain, + blockChainHook: args.BlockChainHook, + wasmVMChangeLocker: args.WasmVMChangeLocker, + bootstrapper: args.Bootstrapper, + gasForQuery: gasForQuery, + allowExternalQueriesChan: args.AllowExternalQueriesChan, + historyRepository: args.HistoryRepository, + shardCoordinator: args.ShardCoordinator, + storageService: args.StorageService, + marshaller: args.Marshaller, + hasher: args.Hasher, + uint64ByteSliceConverter: args.Uint64ByteSliceConverter, + isInHistoricalBalancesMode: args.IsInHistoricalBalancesMode, }, nil } @@ -179,8 +184,7 @@ func (service *SCQueryService) shouldAllowQueriesExecution() bool { } func (service *SCQueryService) executeScCall(query *process.SCQuery, gasPrice uint64) (*vmcommon.VMOutput, common.BlockInfo, error) { - log.Trace("executeScCall", "function", query.FuncName, "numQueries", service.numQueries) - service.numQueries++ + logQueryService.Trace("executeScCall", "address", query.ScAddress, "function", query.FuncName, "blockNonce", query.BlockNonce.Value, "blockHash", query.BlockHash) shouldEarlyExitBecauseOfSyncState := query.ShouldBeSynced && service.bootstrapper.GetNodeState() == common.NsNotSynchronized if shouldEarlyExitBecauseOfSyncState { @@ -198,11 +202,11 @@ func (service *SCQueryService) executeScCall(query *process.SCQuery, gasPrice ui return nil, nil, err } - accountsAdapter := service.blockChainHook.GetAccountsAdapter() - err = accountsAdapter.RecreateTrie(blockRootHash) + err = service.recreateTrie(blockRootHash, blockHeader) if err != nil { return nil, nil, err } + service.blockChainHook.SetCurrentHeader(blockHeader) } shouldCheckRootHashChanges := query.SameScState @@ -212,8 +216,6 @@ func (service *SCQueryService) executeScCall(query *process.SCQuery, gasPrice ui rootHashBeforeExecution = service.apiBlockChain.GetCurrentBlockRootHash() } - service.blockChainHook.SetCurrentHeader(service.mainBlockChain.GetCurrentBlockHeader()) - service.wasmVMChangeLocker.RLock() vm, _, err := scrCommon.FindVMByScAddress(service.vmContainer, query.ScAddress) if err != nil { @@ -229,15 +231,6 @@ func (service *SCQueryService) executeScCall(query *process.SCQuery, gasPrice ui return nil, nil, err } - if service.hasRetriableExecutionError(vmOutput) { - log.Error("Retriable execution error detected. Will retry (once) executeScCall()", "returnCode", vmOutput.ReturnCode, "returnMessage", vmOutput.ReturnMessage) - - vmOutput, err = vm.RunSmartContractCall(vmInput) - if err != nil { - return nil, nil, err - } - } - if query.SameScState { err = service.checkForRootHashChanges(rootHashBeforeExecution) if err != nil { @@ -258,9 +251,26 @@ func (service *SCQueryService) executeScCall(query *process.SCQuery, gasPrice ui return vmOutput, blockInfo, nil } +func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader data.HeaderHandler) error { + if check.IfNil(blockHeader) { + return process.ErrNilBlockHeader + } + + accountsAdapter := service.blockChainHook.GetAccountsAdapter() + + if service.isInHistoricalBalancesMode { + logQueryService.Trace("calling RecreateTrieFromEpoch", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) + holder := holders.NewRootHashHolder(blockRootHash, core.OptionalUint32{Value: blockHeader.GetEpoch(), HasValue: true}) + + return accountsAdapter.RecreateTrieFromEpoch(holder) + } + + logQueryService.Trace("calling RecreateTrie", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) + return accountsAdapter.RecreateTrie(blockRootHash) +} + // TODO: extract duplicated code with nodeBlocks.go func (service *SCQueryService) extractBlockHeaderAndRootHash(query *process.SCQuery) (data.HeaderHandler, []byte, error) { - if len(query.BlockHash) > 0 { currentHeader, err := service.getBlockHeaderByHash(query.BlockHash) if err != nil { @@ -417,10 +427,6 @@ func (service *SCQueryService) createVMCallInput(query *process.SCQuery, gasPric return vmContractCallInput } -func (service *SCQueryService) hasRetriableExecutionError(vmOutput *vmcommon.VMOutput) bool { - return vmOutput.ReturnMessage == "allocation error" -} - // ComputeScCallGasLimit will estimate how many gas a transaction will consume func (service *SCQueryService) ComputeScCallGasLimit(tx *transaction.Transaction) (uint64, error) { argParser := parsers.NewCallArgsParser() diff --git a/process/smartContract/scQueryServiceDispatcher.go b/process/smartContract/scQueryServiceDispatcher.go index 2c51b47d55d..981f71f3dd9 100644 --- a/process/smartContract/scQueryServiceDispatcher.go +++ b/process/smartContract/scQueryServiceDispatcher.go @@ -78,7 +78,7 @@ func (sqsd *scQueryServiceDispatcher) Close() error { for _, scQueryService := range sqsd.list { err := scQueryService.Close() if err != nil { - log.Error("error while closing inner SC query service in scQueryServiceDispatcher.Close", "error", err) + logQueryService.Error("error while closing inner SC query service in scQueryServiceDispatcher.Close", "error", err) errFound = err } } diff --git a/process/smartContract/scQueryService_test.go b/process/smartContract/scQueryService_test.go index 0b76f3a739e..d71542a8aaa 100644 --- a/process/smartContract/scQueryService_test.go +++ b/process/smartContract/scQueryService_test.go @@ -11,7 +11,6 @@ import ( "testing" "time" - "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" @@ -41,7 +40,7 @@ func createMockArgumentsForSCQuery() ArgsNewSCQueryService { BlockChainHook: &testscommon.BlockChainHookStub{ GetAccountsAdapterCalled: func() state.AccountsAdapter { return &stateMocks.AccountsStub{ - RecreateTrieCalled: func(rootHash []byte) error { + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { return nil }, } @@ -59,9 +58,10 @@ func createMockArgumentsForSCQuery() ArgsNewSCQueryService { return &storageStubs.StorerStub{}, nil }, }, - Marshaller: &marshallerMock.MarshalizerStub{}, - Hasher: &testscommon.HasherStub{}, - Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, + Marshaller: &marshallerMock.MarshalizerStub{}, + Hasher: &testscommon.HasherStub{}, + Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, + IsInHistoricalBalancesMode: false, } } @@ -367,10 +367,11 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { _, _, _ = target.ExecuteQuery(&query) assert.True(t, runWasCalled) }) - t.Run("block hash should work", func(t *testing.T) { + t.Run("block hash should work - in deep history mode", func(t *testing.T) { t.Parallel() runWasCalled := false + epoch := uint32(37) mockVM := &mock.VMExecutionHandlerStub{ RunSmartContractCallCalled: func(input *vmcommon.ContractCallInput) (output *vmcommon.VMOutput, e error) { @@ -396,6 +397,13 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { return uint64(math.MaxUint64) }, } + argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &block.Header{ + Epoch: epoch, + } + }, + } providedHash := []byte("provided hash") providedRootHash := []byte("provided root hash") argsNewSCQuery.Marshaller = &marshallerMock.MarshalizerMock{} @@ -422,14 +430,21 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { return true }, GetEpochByHashCalled: func(hash []byte) (uint32, error) { - return 12, nil + return epoch, nil }, } - wasRecreateTrieCalled := false + + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + providedAccountsAdapter := &stateMocks.AccountsStub{ RecreateTrieCalled: func(rootHash []byte) error { - wasRecreateTrieCalled = true - assert.Equal(t, providedRootHash, rootHash) + recreateTrieWasCalled = true + return nil + }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieFromEpochWasCalled = true + assert.Equal(t, providedRootHash, options.GetRootHash()) return nil }, } @@ -438,6 +453,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { return providedAccountsAdapter }, } + argsNewSCQuery.IsInHistoricalBalancesMode = true target, _ := NewSCQueryService(argsNewSCQuery) @@ -452,13 +468,16 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { BlockHash: providedHash, } - _, _, _ = target.ExecuteQuery(&query) + _, _, err := target.ExecuteQuery(&query) assert.True(t, runWasCalled) - assert.True(t, wasRecreateTrieCalled) + assert.True(t, recreateTrieFromEpochWasCalled) + assert.False(t, recreateTrieWasCalled) + assert.Nil(t, err) }) - t.Run("block nonce should work", func(t *testing.T) { + t.Run("block hash should work - in normal mode", func(t *testing.T) { t.Parallel() + epoch := uint32(12) runWasCalled := false mockVM := &mock.VMExecutionHandlerStub{ @@ -487,22 +506,14 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { } providedHash := []byte("provided hash") providedRootHash := []byte("provided root hash") - providedNonce := uint64(123) argsNewSCQuery.Marshaller = &marshallerMock.MarshalizerMock{} - counter := 0 argsNewSCQuery.StorageService = &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ - GetCalled: func(key []byte) ([]byte, error) { - return providedHash, nil - }, GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { - counter++ - if counter > 2 { - return nil, fmt.Errorf("no scheduled") - } hdr := &block.Header{ RootHash: providedRootHash, + Epoch: epoch, } buff, _ := argsNewSCQuery.Marshaller.Marshal(hdr) return buff, nil @@ -515,23 +526,30 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { return true }, GetEpochByHashCalled: func(hash []byte) (uint32, error) { - require.Equal(t, providedHash, hash) - return 12, nil + return epoch, nil }, } - wasRecreateTrieCalled := false + + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + providedAccountsAdapter := &stateMocks.AccountsStub{ RecreateTrieCalled: func(rootHash []byte) error { - wasRecreateTrieCalled = true + recreateTrieWasCalled = true assert.Equal(t, providedRootHash, rootHash) return nil }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieFromEpochWasCalled = true + return nil + }, } argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ GetAccountsAdapterCalled: func() state.AccountsAdapter { return providedAccountsAdapter }, } + argsNewSCQuery.IsInHistoricalBalancesMode = false target, _ := NewSCQueryService(argsNewSCQuery) @@ -543,15 +561,123 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { ScAddress: scAddress, FuncName: funcName, Arguments: dataArgs, - BlockNonce: core.OptionalUint64{ - Value: providedNonce, - HasValue: true, - }, + BlockHash: providedHash, } - _, _, _ = target.ExecuteQuery(&query) + _, _, err := target.ExecuteQuery(&query) assert.True(t, runWasCalled) - assert.True(t, wasRecreateTrieCalled) + assert.True(t, recreateTrieWasCalled) + assert.False(t, recreateTrieFromEpochWasCalled) + assert.Nil(t, err) + }) +} + +func TestSCQueryService_RecreateTrie(t *testing.T) { + t.Parallel() + + testRootHash := []byte("test root hash") + t.Run("should not call RecreateTrie if block header is nil", func(t *testing.T) { + t.Parallel() + + argsNewSCQuery := createMockArgumentsForSCQuery() + argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ + GetAccountsAdapterCalled: func() state.AccountsAdapter { + return &stateMocks.AccountsStub{ + RecreateTrieCalled: func(rootHash []byte) error { + require.Fail(t, "should not be called") + return nil + }, + } + }, + } + + service, _ := NewSCQueryService(argsNewSCQuery) + err := service.recreateTrie(testRootHash, nil) + assert.ErrorIs(t, err, process.ErrNilBlockHeader) + }) + t.Run("should call RecreateTrieFromEpoch if in deep history mode", func(t *testing.T) { + t.Parallel() + + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + + argsNewSCQuery := createMockArgumentsForSCQuery() + argsNewSCQuery.IsInHistoricalBalancesMode = true + argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return nil // after the genesis we do not have a header as current block + }, + } + argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ + GetAccountsAdapterCalled: func() state.AccountsAdapter { + return &stateMocks.AccountsStub{ + RecreateTrieCalled: func(rootHash []byte) error { + recreateTrieWasCalled = true + recreateTrieFromEpochWasCalled = false + + assert.Equal(t, testRootHash, rootHash) + return nil + }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieWasCalled = false + recreateTrieFromEpochWasCalled = true + + assert.Equal(t, testRootHash, options.GetRootHash()) + return nil + }, + } + }, + } + + service, _ := NewSCQueryService(argsNewSCQuery) + + // For genesis block, RecreateTrieFromEpoch should be called + err := service.recreateTrie(testRootHash, &block.Header{}) + assert.Nil(t, err) + assert.True(t, recreateTrieFromEpochWasCalled) + assert.False(t, recreateTrieWasCalled) + }) + t.Run("should call RecreateTrie if not in deep history mode", func(t *testing.T) { + t.Parallel() + + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + + argsNewSCQuery := createMockArgumentsForSCQuery() + argsNewSCQuery.IsInHistoricalBalancesMode = false + argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return nil // after the genesis we do not have a header as current block + }, + } + argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ + GetAccountsAdapterCalled: func() state.AccountsAdapter { + return &stateMocks.AccountsStub{ + RecreateTrieCalled: func(rootHash []byte) error { + recreateTrieWasCalled = true + recreateTrieFromEpochWasCalled = false + + assert.Equal(t, testRootHash, rootHash) + return nil + }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieWasCalled = false + recreateTrieFromEpochWasCalled = true + + assert.Equal(t, testRootHash, options.GetRootHash()) + return nil + }, + } + }, + } + + service, _ := NewSCQueryService(argsNewSCQuery) + + // For genesis block, RecreateTrieFromEpoch should be called + err := service.recreateTrie(testRootHash, &block.Header{}) + assert.Nil(t, err) + assert.False(t, recreateTrieFromEpochWasCalled) + assert.True(t, recreateTrieWasCalled) }) } @@ -896,16 +1022,6 @@ func TestSCQueryService_ShouldFailIfStateChanged(t *testing.T) { t.Parallel() args := createMockArgumentsForSCQuery() - args.BlockChainHook = &testscommon.BlockChainHookStub{ - GetAccountsAdapterCalled: func() state.AccountsAdapter { - return &stateMocks.AccountsStub{ - RecreateTrieCalled: func(rootHash []byte) error { - return nil - }, - } - }, - } - rootHashCalledCounter := 0 args.APIBlockChain = &testscommon.ChainHandlerStub{ GetCurrentBlockRootHashCalled: func() []byte { @@ -927,7 +1043,7 @@ func TestSCQueryService_ShouldFailIfStateChanged(t *testing.T) { FuncName: "function", }) require.Nil(t, res) - require.True(t, errors.Is(err, process.ErrStateChangedWhileExecutingVmQuery)) + require.ErrorIs(t, err, process.ErrStateChangedWhileExecutingVmQuery) } func TestSCQueryService_ShouldWorkIfStateDidntChange(t *testing.T) { diff --git a/scripts/testnet/include/config.sh b/scripts/testnet/include/config.sh index 56d792dc7ed..5397f12e329 100644 --- a/scripts/testnet/include/config.sh +++ b/scripts/testnet/include/config.sh @@ -3,6 +3,7 @@ generateConfig() { TMP_SHARD_OBSERVERCOUNT=$SHARD_OBSERVERCOUNT TMP_META_OBSERVERCOUNT=$META_OBSERVERCOUNT + # set num of observers to 0, they will start with generated keys if [[ $MULTI_KEY_NODES -eq 1 ]]; then TMP_SHARD_OBSERVERCOUNT=0 TMP_META_OBSERVERCOUNT=0 diff --git a/scripts/testnet/include/observers.sh b/scripts/testnet/include/observers.sh index 6ba9ff9293a..50e7f5ade03 100644 --- a/scripts/testnet/include/observers.sh +++ b/scripts/testnet/include/observers.sh @@ -82,10 +82,18 @@ assembleCommand_startObserverNode() { let "KEY_INDEX=$TOTAL_NODECOUNT - $OBSERVER_INDEX - 1" WORKING_DIR=$TESTNETDIR/node_working_dirs/observer$OBSERVER_INDEX + KEYS_FLAGS="-validator-key-pem-file ./config/validatorKey.pem -sk-index $KEY_INDEX" + # if node is running in multi key mode, in order to avoid loading the common allValidatorKeys.pem file + # and force generating a new key for observers, simply provide an invalid path + if [[ $MULTI_KEY_NODES -eq 1 ]]; then + TMP_MISSING_PEM="missing-file.pem" + KEYS_FLAGS="-all-validator-keys-pem-file $TMP_MISSING_PEM -validator-key-pem-file $TMP_MISSING_PEM" + fi + local nodeCommand="./node \ -port $PORT --profile-mode -log-save -log-level $LOGLEVEL --log-logger-name --log-correlation --use-health-service -rest-api-interface localhost:$RESTAPIPORT \ -destination-shard-as-observer $SHARD \ - -sk-index $KEY_INDEX \ + $KEYS_FLAGS \ -working-directory $WORKING_DIR -config ./config/config_observer.toml $EXTRA_OBSERVERS_FLAGS" if [ -n "$NODE_NICENESS" ] diff --git a/scripts/testnet/variables.sh b/scripts/testnet/variables.sh index 1dc3c7cc65c..f3fb44c5866 100644 --- a/scripts/testnet/variables.sh +++ b/scripts/testnet/variables.sh @@ -170,10 +170,6 @@ export TOTAL_OBSERVERCOUNT=$total_observer_count # to enable the full archive feature on the observers, please use the --full-archive flag export EXTRA_OBSERVERS_FLAGS="-operation-mode db-lookup-extension" -if [[ $MULTI_KEY_NODES -eq 1 ]]; then - EXTRA_OBSERVERS_FLAGS="--no-key" -fi - # Leave unchanged. let "total_node_count = $SHARD_VALIDATORCOUNT * $SHARDCOUNT + $META_VALIDATORCOUNT + $TOTAL_OBSERVERCOUNT" export TOTAL_NODECOUNT=$total_node_count diff --git a/sharding/nodesCoordinator/consensusGroupProviderBench_test.go b/sharding/nodesCoordinator/consensusGroupProviderBench_test.go index c24f6f9549f..49731812213 100644 --- a/sharding/nodesCoordinator/consensusGroupProviderBench_test.go +++ b/sharding/nodesCoordinator/consensusGroupProviderBench_test.go @@ -1,11 +1,9 @@ package nodesCoordinator import ( - "math/rand" "testing" ) -const randSeed = 75 const numValidators = 63 const numValidatorsInEligibleList = 400 @@ -20,7 +18,6 @@ func getRandomness() []byte { func BenchmarkReslicingBasedProvider_Get(b *testing.B) { numVals := numValidators - rand.Seed(randSeed) expElList := getExpandedEligibleList(numValidatorsInEligibleList) randomness := getRandomness() @@ -32,7 +29,6 @@ func BenchmarkReslicingBasedProvider_Get(b *testing.B) { func BenchmarkSelectionBasedProvider_Get(b *testing.B) { numVals := numValidators - rand.Seed(randSeed) expElList := getExpandedEligibleList(numValidatorsInEligibleList) randomness := getRandomness() diff --git a/sharding/nodesCoordinator/indexHashedNodesCoordinatorWithRater_test.go b/sharding/nodesCoordinator/indexHashedNodesCoordinatorWithRater_test.go index 5d276deaaed..d74c38e9b0b 100644 --- a/sharding/nodesCoordinator/indexHashedNodesCoordinatorWithRater_test.go +++ b/sharding/nodesCoordinator/indexHashedNodesCoordinatorWithRater_test.go @@ -774,8 +774,8 @@ func BenchmarkIndexHashedGroupSelectorWithRater_TestExpandList(b *testing.B) { } //a := []int{1, 2, 3, 4, 5, 6, 7, 8} - rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(array), func(i, j int) { array[i], array[j] = array[j], array[i] }) + rnd := rand.New(rand.NewSource(time.Now().UnixNano())) + rnd.Shuffle(len(array), func(i, j int) { array[i], array[j] = array[j], array[i] }) m2 := runtime.MemStats{} runtime.ReadMemStats(&m2) diff --git a/state/accountsDBApi.go b/state/accountsDBApi.go index 89c2a27a636..791bfc658df 100644 --- a/state/accountsDBApi.go +++ b/state/accountsDBApi.go @@ -171,8 +171,28 @@ func (accountsDB *accountsDBApi) RecreateTrie(rootHash []byte) error { } // RecreateTrieFromEpoch is a not permitted operation in this implementation and thus, will return an error -func (accountsDB *accountsDBApi) RecreateTrieFromEpoch(_ common.RootHashHolder) error { - return ErrOperationNotPermitted +func (accountsDB *accountsDBApi) RecreateTrieFromEpoch(options common.RootHashHolder) error { + accountsDB.mutRecreatedTrieBlockInfo.Lock() + defer accountsDB.mutRecreatedTrieBlockInfo.Unlock() + + if check.IfNil(options) { + return ErrNilRootHashHolder + } + + newBlockInfo := holders.NewBlockInfo([]byte{}, 0, options.GetRootHash()) + if newBlockInfo.Equal(accountsDB.blockInfo) { + return nil + } + + err := accountsDB.innerAccountsAdapter.RecreateTrieFromEpoch(options) + if err != nil { + accountsDB.blockInfo = nil + return err + } + + accountsDB.blockInfo = newBlockInfo + + return nil } // PruneTrie is a not permitted operation in this implementation and thus, does nothing diff --git a/state/accountsDBApi_test.go b/state/accountsDBApi_test.go index 2792d18749a..1a22366ab06 100644 --- a/state/accountsDBApi_test.go +++ b/state/accountsDBApi_test.go @@ -16,7 +16,8 @@ import ( "github.com/multiversx/mx-chain-go/state/parsers" "github.com/multiversx/mx-chain-go/testscommon" mockState "github.com/multiversx/mx-chain-go/testscommon/state" - "github.com/multiversx/mx-chain-go/testscommon/trie" + testTrie "github.com/multiversx/mx-chain-go/testscommon/trie" + "github.com/multiversx/mx-chain-go/trie" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -195,7 +196,6 @@ func TestAccountsDBApi_NotPermittedOperations(t *testing.T) { assert.Equal(t, state.ErrOperationNotPermitted, accountsApi.SaveAccount(nil)) assert.Equal(t, state.ErrOperationNotPermitted, accountsApi.RemoveAccount(nil)) assert.Equal(t, state.ErrOperationNotPermitted, accountsApi.RevertToSnapshot(0)) - assert.Equal(t, state.ErrOperationNotPermitted, accountsApi.RecreateTrieFromEpoch(nil)) buff, err := accountsApi.CommitInEpoch(0, 0) assert.Nil(t, buff) @@ -226,6 +226,41 @@ func TestAccountsDBApi_RecreateTrie(t *testing.T) { assert.True(t, wasCalled) } +func TestAccountsDBApi_RecreateTrieFromEpoch(t *testing.T) { + t.Parallel() + + t.Run("should error if the roothash holder is nil", func(t *testing.T) { + accountsApi, _ := state.NewAccountsDBApi(&mockState.AccountsStub{ + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + assert.Fail(t, "should have not called accountsApi.RecreateTrieFromEpochCalled") + + return nil + }, + }, createBlockInfoProviderStub(dummyRootHash)) + + err := accountsApi.RecreateTrieFromEpoch(nil) + assert.Equal(t, trie.ErrNilRootHashHolder, err) + }) + t.Run("should work", func(t *testing.T) { + wasCalled := false + rootHash := []byte("root hash") + epoch := core.OptionalUint32{Value: 37, HasValue: true} + accountsApi, _ := state.NewAccountsDBApi(&mockState.AccountsStub{ + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + wasCalled = true + assert.Equal(t, rootHash, options.GetRootHash()) + assert.Equal(t, epoch, options.GetEpoch()) + return nil + }, + }, createBlockInfoProviderStub(dummyRootHash)) + + holder := holders.NewRootHashHolder(rootHash, epoch) + err := accountsApi.RecreateTrieFromEpoch(holder) + assert.NoError(t, err) + assert.True(t, wasCalled) + }) +} + func TestAccountsDBApi_EmptyMethodsShouldNotPanic(t *testing.T) { t.Parallel() @@ -273,7 +308,7 @@ func TestAccountsDBApi_SimpleProxyMethodsShouldWork(t *testing.T) { }, GetTrieCalled: func(i []byte) (common.Trie, error) { getTrieCalled = true - return &trie.TrieStub{}, nil + return &testTrie.TrieStub{}, nil }, } diff --git a/state/errors.go b/state/errors.go index 5a56aff40ff..893d65d7ec0 100644 --- a/state/errors.go +++ b/state/errors.go @@ -144,3 +144,6 @@ var ErrNilStateMetrics = errors.New("nil sstate metrics") // ErrNilChannelsProvider signals that a nil channels provider has been given var ErrNilChannelsProvider = errors.New("nil channels provider") + +// ErrNilRootHashHolder signals that a nil root hash holder was provided +var ErrNilRootHashHolder = errors.New("nil root hash holder provided") diff --git a/testscommon/generalConfig.go b/testscommon/generalConfig.go index 1a653313e0e..ac89501ee31 100644 --- a/testscommon/generalConfig.go +++ b/testscommon/generalConfig.go @@ -384,7 +384,8 @@ func GetGeneralConfig() config.Config { CheckNodesOnDisk: false, }, Antiflood: config.AntifloodConfig{ - NumConcurrentResolverJobs: 2, + NumConcurrentResolverJobs: 2, + NumConcurrentResolvingTrieNodesJobs: 1, TxAccumulator: config.TxAccumulatorConfig{ MaxAllowedTimeInMilliseconds: 10, MaxDeviationTimeInMilliseconds: 1, diff --git a/testscommon/managedPeersHolderStub.go b/testscommon/managedPeersHolderStub.go index 0bd1948d813..ef9a550fe2b 100644 --- a/testscommon/managedPeersHolderStub.go +++ b/testscommon/managedPeersHolderStub.go @@ -17,6 +17,7 @@ type ManagedPeersHolderStub struct { IncrementRoundsWithoutReceivedMessagesCalled func(pkBytes []byte) ResetRoundsWithoutReceivedMessagesCalled func(pkBytes []byte, pid core.PeerID) GetManagedKeysByCurrentNodeCalled func() map[string]crypto.PrivateKey + GetLoadedKeysByCurrentNodeCalled func() [][]byte IsKeyManagedByCurrentNodeCalled func(pkBytes []byte) bool IsKeyRegisteredCalled func(pkBytes []byte) bool IsPidManagedByCurrentNodeCalled func(pid core.PeerID) bool @@ -90,6 +91,14 @@ func (stub *ManagedPeersHolderStub) GetManagedKeysByCurrentNode() map[string]cry return nil } +// GetLoadedKeysByCurrentNode - +func (stub *ManagedPeersHolderStub) GetLoadedKeysByCurrentNode() [][]byte { + if stub.GetLoadedKeysByCurrentNodeCalled != nil { + return stub.GetLoadedKeysByCurrentNodeCalled() + } + return make([][]byte, 0) +} + // IsKeyManagedByCurrentNode - func (stub *ManagedPeersHolderStub) IsKeyManagedByCurrentNode(pkBytes []byte) bool { if stub.IsKeyManagedByCurrentNodeCalled != nil { diff --git a/testscommon/managedPeersMonitorStub.go b/testscommon/managedPeersMonitorStub.go index 2ae60ccc55e..43aea679c14 100644 --- a/testscommon/managedPeersMonitorStub.go +++ b/testscommon/managedPeersMonitorStub.go @@ -6,6 +6,7 @@ type ManagedPeersMonitorStub struct { GetEligibleManagedKeysCalled func() ([][]byte, error) GetWaitingManagedKeysCalled func() ([][]byte, error) GetManagedKeysCalled func() [][]byte + GetLoadedKeysCalled func() [][]byte } // GetManagedKeys - @@ -16,6 +17,14 @@ func (stub *ManagedPeersMonitorStub) GetManagedKeys() [][]byte { return make([][]byte, 0) } +// GetLoadedKeys - +func (stub *ManagedPeersMonitorStub) GetLoadedKeys() [][]byte { + if stub.GetLoadedKeysCalled != nil { + return stub.GetLoadedKeysCalled() + } + return make([][]byte, 0) +} + // GetManagedKeysCount - func (stub *ManagedPeersMonitorStub) GetManagedKeysCount() int { if stub.GetManagedKeysCountCalled != nil { diff --git a/trie/node_extension.go b/trie/node_extension.go index 4e7b38a6a7d..ffbdab699ad 100644 --- a/trie/node_extension.go +++ b/trie/node_extension.go @@ -26,8 +26,8 @@ func shouldTestNode(n node, key []byte) bool { } func snapshotGetTestPoint(key []byte, faultyChance int) error { - rand.Seed(time.Now().UnixNano()) - checkVal := rand.Intn(math.MaxInt) + rnd := rand.New(rand.NewSource(time.Now().UnixNano())) + checkVal := rnd.Intn(math.MaxInt) if checkVal%faultyChance == 0 { log.Debug("deliberately not returning hash", "hash", key) return fmt.Errorf("snapshot get error")